UI: [Rule node save ts] Rename Persistence settings to Processing settings

This commit is contained in:
Vladyslav_Prykhodko 2025-02-07 12:15:32 +02:00
parent 12a8c070a6
commit fc3bc52247
11 changed files with 100 additions and 124 deletions

View File

@ -24,11 +24,11 @@ import {
Validator
} from '@angular/forms';
import {
AdvancedPersistenceConfig,
defaultAdvancedPersistenceConfig,
AdvancedProcessingConfig,
defaultAdvancedProcessingConfig,
maxDeduplicateTimeSecs,
PersistenceType,
PersistenceTypeTranslationMap
ProcessingType,
ProcessingTypeTranslationMap
} from '@home/components/rule-node/action/timeseries-config.models';
import { isDefinedAndNotNull } from '@core/utils';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@ -52,13 +52,13 @@ export class AdvancedPersistenceSettingRowComponent implements ControlValueAcces
title: string;
persistenceSettingRowForm = this.fb.group({
type: [defaultAdvancedPersistenceConfig.type],
type: [defaultAdvancedProcessingConfig.type],
deduplicationIntervalSecs: [{value: 60, disabled: true}]
});
PersistenceType = PersistenceType;
persistenceStrategies = [PersistenceType.ON_EVERY_MESSAGE, PersistenceType.DEDUPLICATE, PersistenceType.SKIP];
PersistenceTypeTranslationMap = PersistenceTypeTranslationMap;
PersistenceType = ProcessingType;
persistenceStrategies = [ProcessingType.ON_EVERY_MESSAGE, ProcessingType.DEDUPLICATE, ProcessingType.SKIP];
PersistenceTypeTranslationMap = ProcessingTypeTranslationMap;
maxDeduplicateTime = maxDeduplicateTimeSecs;
@ -96,16 +96,16 @@ export class AdvancedPersistenceSettingRowComponent implements ControlValueAcces
};
}
writeValue(value: AdvancedPersistenceConfig) {
writeValue(value: AdvancedProcessingConfig) {
if (isDefinedAndNotNull(value)) {
this.persistenceSettingRowForm.patchValue(value, {emitEvent: false});
} else {
this.persistenceSettingRowForm.patchValue(defaultAdvancedPersistenceConfig);
this.persistenceSettingRowForm.patchValue(defaultAdvancedProcessingConfig);
}
}
private updatedValidation() {
if (this.persistenceSettingRowForm.get('type').value === PersistenceType.DEDUPLICATE) {
if (this.persistenceSettingRowForm.get('type').value === ProcessingType.DEDUPLICATE) {
this.persistenceSettingRowForm.get('deduplicationIntervalSecs').enable({emitEvent: false});
} else {
this.persistenceSettingRowForm.get('deduplicationIntervalSecs').disable({emitEvent: false})

View File

@ -16,6 +16,11 @@
-->
<section [formGroup]="persistenceForm" class="tb-form-panel no-border no-padding">
<tb-example-hint
[hintText]="'rule-node-config.save-time-series.advanced-settings-hint'"
[popupHelpLink]="'rulenode/save_timeseries_node_advanced'"
>
</tb-example-hint>
<tb-advanced-persistence-setting-row
formControlName="timeseries"
title="{{ 'rule-node-config.save-time-series.time-series' | translate }}"

View File

@ -24,7 +24,7 @@ import {
} from '@angular/forms';
import { Component, forwardRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { AdvancedPersistenceStrategy } from '@home/components/rule-node/action/timeseries-config.models';
import { AdvancedProcessingStrategy } from '@home/components/rule-node/action/timeseries-config.models';
@Component({
selector: 'tb-advanced-persistence-settings',
@ -76,8 +76,7 @@ export class AdvancedPersistenceSettingComponent implements ControlValueAccessor
};
}
writeValue(value: AdvancedPersistenceStrategy) {
writeValue(value: AdvancedProcessingStrategy) {
this.persistenceForm.patchValue(value, {emitEvent: false});
}
}

View File

@ -16,10 +16,10 @@
-->
<section [formGroup]="timeseriesConfigForm" class="tb-form-panel no-border no-padding">
<div class="tb-form-panel stroked no-padding-bottom no-gap" formGroupName="persistenceSettings">
<div class="tb-form-panel stroked no-padding-bottom no-gap" formGroupName="processingSettings">
<div class="mb-4 flex flex-row items-center justify-between">
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'rule-node-config.save-time-series.persistence-settings-hint' | translate}}" translate>
rule-node-config.save-time-series.persistence-settings
<div class="tb-form-panel-title" tb-hint-tooltip-icon="{{ 'rule-node-config.save-time-series.processing-settings-hint' | translate}}" translate>
rule-node-config.save-time-series.processing-settings
</div>
<tb-toggle-select appearance="fill" selectMediaBreakpoint="xs"
formControlName="isAdvanced">
@ -27,7 +27,7 @@
<tb-toggle-option [value]=true>{{ 'rule-node-config.advanced-mode' | translate }}</tb-toggle-option>
</tb-toggle-select>
</div>
@if(!timeseriesConfigForm.get('persistenceSettings.isAdvanced').value) {
@if(!timeseriesConfigForm.get('processingSettings.isAdvanced').value) {
<mat-form-field>
<mat-label translate>rule-node-config.save-time-series.strategy</mat-label>
<mat-select formControlName="type">
@ -37,7 +37,7 @@
</mat-select>
</mat-form-field>
@if(timeseriesConfigForm.get('persistenceSettings.type').value === PersistenceType.DEDUPLICATE) {
@if(timeseriesConfigForm.get('processingSettings.type').value === PersistenceType.DEDUPLICATE) {
<tb-time-unit-input
required
labelText="{{ 'rule-node-config.save-time-series.deduplication-interval' | translate }}"
@ -49,8 +49,7 @@
formControlName="deduplicationIntervalSecs">
</tb-time-unit-input>
}
}
@else {
} @else {
<tb-advanced-persistence-settings
class="mb-4"
formControlName="advanced"

View File

@ -20,10 +20,10 @@ import { RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'
import {
defaultAdvancedPersistenceStrategy,
maxDeduplicateTimeSecs,
PersistenceSettings,
PersistenceSettingsForm,
PersistenceType,
PersistenceTypeTranslationMap,
ProcessingSettings,
ProcessingSettingsForm,
ProcessingType,
ProcessingTypeTranslationMap,
TimeseriesNodeConfiguration,
TimeseriesNodeConfigurationForm
} from '@home/components/rule-node/action/timeseries-config.models';
@ -37,9 +37,9 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
timeseriesConfigForm: FormGroup;
PersistenceType = PersistenceType;
persistenceStrategies = [PersistenceType.ON_EVERY_MESSAGE, PersistenceType.DEDUPLICATE, PersistenceType.WEBSOCKETS_ONLY];
PersistenceTypeTranslationMap = PersistenceTypeTranslationMap;
PersistenceType = ProcessingType;
persistenceStrategies = [ProcessingType.ON_EVERY_MESSAGE, ProcessingType.DEDUPLICATE, ProcessingType.WEBSOCKETS_ONLY];
PersistenceTypeTranslationMap = ProcessingTypeTranslationMap;
maxDeduplicateTime = maxDeduplicateTimeSecs
@ -52,22 +52,22 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
}
protected validatorTriggers(): string[] {
return ['persistenceSettings.isAdvanced', 'persistenceSettings.type'];
return ['processingSettings.isAdvanced', 'processingSettings.type'];
}
protected prepareInputConfig(config: TimeseriesNodeConfiguration): TimeseriesNodeConfigurationForm {
let persistenceSettings: PersistenceSettingsForm;
if (config?.persistenceSettings) {
const isAdvanced = config?.persistenceSettings?.type === PersistenceType.ADVANCED;
persistenceSettings = {
type: isAdvanced ? PersistenceType.ON_EVERY_MESSAGE : config.persistenceSettings.type,
let processingSettings: ProcessingSettingsForm;
if (config?.processingSettings) {
const isAdvanced = config?.processingSettings?.type === ProcessingType.ADVANCED;
processingSettings = {
type: isAdvanced ? ProcessingType.ON_EVERY_MESSAGE : config.processingSettings.type,
isAdvanced: isAdvanced,
deduplicationIntervalSecs: config.persistenceSettings?.deduplicationIntervalSecs ?? 60,
advanced: isAdvanced ? config.persistenceSettings : defaultAdvancedPersistenceStrategy
deduplicationIntervalSecs: config.processingSettings?.deduplicationIntervalSecs ?? 60,
advanced: isAdvanced ? config.processingSettings : defaultAdvancedPersistenceStrategy
}
} else {
persistenceSettings = {
type: PersistenceType.ON_EVERY_MESSAGE,
processingSettings = {
type: ProcessingType.ON_EVERY_MESSAGE,
isAdvanced: false,
deduplicationIntervalSecs: 60,
advanced: defaultAdvancedPersistenceStrategy
@ -75,36 +75,36 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
}
return {
...config,
persistenceSettings: persistenceSettings
processingSettings: processingSettings
}
}
protected prepareOutputConfig(config: TimeseriesNodeConfigurationForm): TimeseriesNodeConfiguration {
let persistenceSettings: PersistenceSettings;
if (config.persistenceSettings.isAdvanced) {
persistenceSettings = {
...config.persistenceSettings.advanced,
type: PersistenceType.ADVANCED
let processingSettings: ProcessingSettings;
if (config.processingSettings.isAdvanced) {
processingSettings = {
...config.processingSettings.advanced,
type: ProcessingType.ADVANCED
};
} else {
persistenceSettings = {
type: config.persistenceSettings.type,
deduplicationIntervalSecs: config.persistenceSettings?.deduplicationIntervalSecs
processingSettings = {
type: config.processingSettings.type,
deduplicationIntervalSecs: config.processingSettings?.deduplicationIntervalSecs
};
}
return {
...config,
persistenceSettings
processingSettings
};
}
protected onConfigurationSet(config: TimeseriesNodeConfigurationForm) {
this.timeseriesConfigForm = this.fb.group({
persistenceSettings: this.fb.group({
isAdvanced: [config?.persistenceSettings?.isAdvanced ?? false],
type: [config?.persistenceSettings?.type ?? PersistenceType.ON_EVERY_MESSAGE],
processingSettings: this.fb.group({
isAdvanced: [config?.processingSettings?.isAdvanced ?? false],
type: [config?.processingSettings?.type ?? ProcessingType.ON_EVERY_MESSAGE],
deduplicationIntervalSecs: [
{value: config?.persistenceSettings?.deduplicationIntervalSecs ?? 60, disabled: true},
{value: config?.processingSettings?.deduplicationIntervalSecs ?? 60, disabled: true},
[Validators.required, Validators.max(maxDeduplicateTimeSecs)]
],
advanced: [{value: null, disabled: true}]
@ -115,18 +115,18 @@ export class TimeseriesConfigComponent extends RuleNodeConfigurationComponent {
}
protected updateValidators(emitEvent: boolean, _trigger?: string) {
const persistenceForm = this.timeseriesConfigForm.get('persistenceSettings') as FormGroup;
const isAdvanced: boolean = persistenceForm.get('isAdvanced').value;
const type: PersistenceType = persistenceForm.get('type').value;
if (!isAdvanced && type === PersistenceType.DEDUPLICATE) {
persistenceForm.get('deduplicationIntervalSecs').enable({emitEvent});
const processingForm = this.timeseriesConfigForm.get('processingSettings') as FormGroup;
const isAdvanced: boolean = processingForm.get('isAdvanced').value;
const type: ProcessingType = processingForm.get('type').value;
if (!isAdvanced && type === ProcessingType.DEDUPLICATE) {
processingForm.get('deduplicationIntervalSecs').enable({emitEvent});
} else {
persistenceForm.get('deduplicationIntervalSecs').disable({emitEvent});
processingForm.get('deduplicationIntervalSecs').disable({emitEvent});
}
if (isAdvanced) {
persistenceForm.get('advanced').enable({emitEvent});
processingForm.get('advanced').enable({emitEvent});
} else {
persistenceForm.get('advanced').disable({emitEvent});
processingForm.get('advanced').disable({emitEvent});
}
}
}

View File

@ -19,24 +19,24 @@ import { DAY, SECOND } from '@shared/models/time/time.models';
export const maxDeduplicateTimeSecs = DAY / SECOND;
export interface TimeseriesNodeConfiguration {
persistenceSettings: PersistenceSettings;
processingSettings: ProcessingSettings;
defaultTTL: number;
useServerTs: boolean;
}
export interface TimeseriesNodeConfigurationForm extends Omit<TimeseriesNodeConfiguration, 'persistenceSettings'> {
persistenceSettings: PersistenceSettingsForm
export interface TimeseriesNodeConfigurationForm extends Omit<TimeseriesNodeConfiguration, 'processingSettings'> {
processingSettings: ProcessingSettingsForm
}
export type PersistenceSettings = BasicPersistenceSettings & Partial<DeduplicatePersistenceStrategy> & Partial<AdvancedPersistenceStrategy>;
export type ProcessingSettings = ProcessingSettingsSettings & Partial<DeduplicateProcessingStrategy> & Partial<AdvancedProcessingStrategy>;
export type PersistenceSettingsForm = Omit<PersistenceSettings, keyof AdvancedPersistenceStrategy> & {
export type ProcessingSettingsForm = Omit<ProcessingSettings, keyof AdvancedProcessingStrategy> & {
isAdvanced: boolean;
advanced?: Partial<AdvancedPersistenceStrategy>;
type: PersistenceType;
advanced?: Partial<AdvancedProcessingStrategy>;
type: ProcessingType;
};
export enum PersistenceType {
export enum ProcessingType {
ON_EVERY_MESSAGE = 'ON_EVERY_MESSAGE',
DEDUPLICATE = 'DEDUPLICATE',
WEBSOCKETS_ONLY = 'WEBSOCKETS_ONLY',
@ -44,35 +44,35 @@ export enum PersistenceType {
SKIP = 'SKIP'
}
export const PersistenceTypeTranslationMap = new Map<PersistenceType, string>([
[PersistenceType.ON_EVERY_MESSAGE, 'rule-node-config.save-time-series.strategy-type.every-message'],
[PersistenceType.DEDUPLICATE, 'rule-node-config.save-time-series.strategy-type.deduplicate'],
[PersistenceType.WEBSOCKETS_ONLY, 'rule-node-config.save-time-series.strategy-type.web-sockets-only'],
[PersistenceType.SKIP, 'rule-node-config.save-time-series.strategy-type.skip'],
export const ProcessingTypeTranslationMap = new Map<ProcessingType, string>([
[ProcessingType.ON_EVERY_MESSAGE, 'rule-node-config.save-time-series.strategy-type.every-message'],
[ProcessingType.DEDUPLICATE, 'rule-node-config.save-time-series.strategy-type.deduplicate'],
[ProcessingType.WEBSOCKETS_ONLY, 'rule-node-config.save-time-series.strategy-type.web-sockets-only'],
[ProcessingType.SKIP, 'rule-node-config.save-time-series.strategy-type.skip'],
])
export interface BasicPersistenceSettings {
type: PersistenceType;
export interface ProcessingSettingsSettings {
type: ProcessingType;
}
export interface DeduplicatePersistenceStrategy extends BasicPersistenceSettings{
export interface DeduplicateProcessingStrategy extends ProcessingSettingsSettings{
deduplicationIntervalSecs: number;
}
export interface AdvancedPersistenceStrategy extends BasicPersistenceSettings{
timeseries: AdvancedPersistenceConfig;
latest: AdvancedPersistenceConfig;
webSockets: AdvancedPersistenceConfig;
export interface AdvancedProcessingStrategy extends ProcessingSettingsSettings{
timeseries: AdvancedProcessingConfig;
latest: AdvancedProcessingConfig;
webSockets: AdvancedProcessingConfig;
}
export type AdvancedPersistenceConfig = WithOptional<DeduplicatePersistenceStrategy, 'deduplicationIntervalSecs'>;
export type AdvancedProcessingConfig = WithOptional<DeduplicateProcessingStrategy, 'deduplicationIntervalSecs'>;
export const defaultAdvancedPersistenceConfig: AdvancedPersistenceConfig = {
type: PersistenceType.ON_EVERY_MESSAGE
export const defaultAdvancedProcessingConfig: AdvancedProcessingConfig = {
type: ProcessingType.ON_EVERY_MESSAGE
}
export const defaultAdvancedPersistenceStrategy: Omit<AdvancedPersistenceStrategy, 'type'> = {
timeseries: defaultAdvancedPersistenceConfig,
latest: defaultAdvancedPersistenceConfig,
webSockets: defaultAdvancedPersistenceConfig,
export const defaultAdvancedPersistenceStrategy: Omit<AdvancedProcessingStrategy, 'type'> = {
timeseries: defaultAdvancedProcessingConfig,
latest: defaultAdvancedProcessingConfig,
webSockets: defaultAdvancedProcessingConfig,
}

View File

@ -15,11 +15,11 @@
limitations under the License.
-->
<div [hidden]="!hintText" class="tb-form-hint tb-primary-fill space-between">
<div [hidden]="!hintText" class="tb-form-hint tb-primary-fill flex justify-between gap-5">
<div [innerHTML]=" hintText | translate | safe: 'html'"
[style.text-align]="textAlign"
class="hint-text"></div>
<div *ngIf="popupHelpLink" class="see-example" tb-help-popup="{{ popupHelpLink }}"
class="w-full"></div>
<div *ngIf="popupHelpLink" class="flex shrink-0" tb-help-popup="{{ popupHelpLink }}"
hintMode
tb-help-popup-placement="right"
trigger-style="letter-spacing:0.25px; font-size:12px"

View File

@ -1,31 +0,0 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.space-between {
display: flex;
justify-content: space-between;
gap: 20px;
.see-example {
display: flex;
flex-shrink: 0;
}
}
.hint-text {
width: 100%;
}
}

View File

@ -19,7 +19,7 @@ import { Component, Input } from '@angular/core';
@Component({
selector: 'tb-example-hint',
templateUrl: './example-hint.component.html',
styleUrls: ['./example-hint.component.scss']
styleUrls: []
})
export class ExampleHintComponent {
@Input() hintText: string;

View File

@ -0,0 +1,3 @@
**TO DO;**
Advanced mode doc

View File

@ -5134,8 +5134,9 @@
"basic-mode": "Basic",
"advanced-mode": "Advanced",
"save-time-series": {
"persistence-settings": "Persistence settings",
"persistence-settings-hint": "Define how and when time series data is saved. In Basic mode, apply a single persistence strategy to all actions or enable only WebSockets updates. Advanced mode allows you to configure individual persistence strategies for each action.",
"processing-settings": "Processing settings",
"processing-settings-hint": "Define how time series data is processed. In Basic mode, select a preconfigured processing strategy or enable only WebSocket updates. Advanced mode allows you to select individual processing strategies for each action.",
"advanced-settings-hint": "Advanced settings mode is dangerous, and if you do not understand it, you may lose telemetry.\n\n\n\n\n\n\n\n",
"strategy": "Strategy",
"deduplication-interval": "Deduplication interval",
"deduplication-interval-required": "Deduplication interval is required",