Browse Source

UI: Fixed debug event function dialog

pull/14423/head
ArtemDzhereleiko 9 months ago
parent
commit
361c9c8593
  1. 7
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html
  2. 25
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts
  3. 50
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts
  4. 63
      ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.ts
  5. 9
      ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.ts
  6. 2
      ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html
  7. 7
      ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.ts
  8. 5
      ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.html
  9. 7
      ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts
  10. 3
      ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.html
  11. 6
      ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.html
  12. 5
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
  13. 29
      ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts
  14. 9
      ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/propagate-arguments-table.component.ts
  15. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html
  16. 6
      ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts
  17. 1
      ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.html
  18. 4
      ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts
  19. 10
      ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html
  20. 69
      ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts
  21. 8
      ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts
  22. 2
      ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.html
  23. 5
      ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.ts
  24. 4
      ui-ngx/src/app/shared/models/alarm-rule.models.ts
  25. 2
      ui-ngx/src/app/shared/models/calculated-field.models.ts

7
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.html

@ -91,7 +91,7 @@
<div class="tb-form-panel">
<div class="tb-form-panel-title">{{ 'alarm-rule.create-conditions' | translate }}</div>
<div class="flex flex-1 flex-col">
<tb-create-cf-alarm-rules formControlName="createRules" [arguments]="arguments" [value]="data.value">
<tb-create-cf-alarm-rules formControlName="createRules" [arguments]="arguments" [testScript]="onTestScript.bind(this)">
</tb-create-cf-alarm-rules>
</div>
</div>
@ -100,7 +100,7 @@
<div class="flex flex-row items-center justify-start gap-2 pb-2"
[class.!hidden]="!configFormGroup.get('clearRule').value">
<div class="clear-alarm-rule flex flex-1 flex-row">
<tb-cf-alarm-rule formControlName="clearRule" class="flex-1" [arguments]="arguments" [value]="data.value" isClearCondition>
<tb-cf-alarm-rule formControlName="clearRule" class="flex-1" [arguments]="arguments" [testScript]="onTestScript.bind(this)" isClearCondition>
</tb-cf-alarm-rule>
</div>
<button mat-icon-button
@ -119,8 +119,7 @@
<div [class.!hidden]="configFormGroup.get('clearRule').value">
<button mat-stroked-button color="primary"
type="button"
(click)="addClearAlarmRule()"
matTooltipPosition="above">
(click)="addClearAlarmRule()">
{{ 'alarm-rule.add-clear-alarm-rule' | translate }}
</button>
</div>

25
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rule-dialog.component.ts

@ -31,8 +31,15 @@ import { EntityId } from '@shared/models/id/entity-id';
import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model';
import { COMMA, ENTER, SEMICOLON } from "@angular/cdk/keycodes";
import { MatChipInputEvent } from "@angular/material/chips";
import { AlarmRule, AlarmRuleConditionType, AlarmRuleExpressionType } from "@shared/models/alarm-rule.models";
import {
AlarmRule,
AlarmRuleConditionType,
AlarmRuleExpressionType,
AlarmRuleTestScriptFn
} from "@shared/models/alarm-rule.models";
import { deepTrim } from "@core/utils";
import { Observable } from "rxjs";
import { switchMap } from "rxjs/operators";
export interface AlarmRuleDialogData {
value?: CalculatedField;
@ -43,6 +50,7 @@ export interface AlarmRuleDialogData {
ownerId: EntityId;
additionalDebugActionConfig: AdditionalDebugActionConfig<(calculatedField: CalculatedField) => void>;
isDirty?: boolean;
getTestScriptDialogFn: AlarmRuleTestScriptFn,
}
@Component({
@ -182,4 +190,19 @@ export class AlarmRuleDialogComponent extends DialogComponent<AlarmRuleDialogCom
}
});
}
onTestScript(expression: string): Observable<string> {
const calculatedFieldId = this.data.value?.id?.id;
if (calculatedFieldId) {
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true})
.pipe(
switchMap(event => {
const args = event?.arguments ? JSON.parse(event.arguments) : null;
return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, args, false);
}),
takeUntilDestroyed(this.destroyRef)
)
}
return this.data.getTestScriptDialogFn(this.fromGroupValue, expression, null, false);
}
}

50
ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts

@ -36,13 +36,17 @@ import { DestroyRef, Renderer2 } from '@angular/core';
import { EntityDebugSettings } from '@shared/models/entity.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { catchError, filter, switchMap } from 'rxjs/operators';
import { catchError, filter, switchMap, tap } from 'rxjs/operators';
import {
ArgumentEntityType,
ArgumentType,
CalculatedField,
CalculatedFieldAlarmRule,
CalculatedFieldEventArguments,
CalculatedFieldsQuery,
CalculatedFieldType,
getCalculatedFieldArgumentsEditorCompleter,
getCalculatedFieldArgumentsHighlights,
} from '@shared/models/calculated-field.models';
import { ImportExportService } from '@shared/import-export/import-export.service';
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
@ -57,9 +61,13 @@ import {
} from "@home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component";
import { AlarmSeverity, alarmSeverityTranslations } from "@shared/models/alarm.models";
import { UtilsService } from "@core/services/utils.service";
import { deepClone, getEntityDetailsPageURL } from "@core/utils";
import { deepClone, getEntityDetailsPageURL, isObject } from "@core/utils";
import { AlarmRuleTableHeaderComponent } from "@home/components/alarm-rules/alarm-rule-table-header.component";
import { ActionNotificationShow } from "@core/notification/notification.actions";
import {
CalculatedFieldScriptTestDialogComponent,
CalculatedFieldTestScriptDialogData
} from "@home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component";
export class AlarmRulesTableConfig extends EntityTableConfig<any> {
@ -249,6 +257,7 @@ export class AlarmRulesTableConfig extends EntityTableConfig<any> {
ownerId: this.ownerId ?? {entityType: EntityType.TENANT, id: this.tenantId},
additionalDebugActionConfig: this.additionalDebugActionConfig,
isDirty,
getTestScriptDialogFn: this.getTestScriptDialog.bind(this),
},
enterAnimationDuration: isDirty ? 0 : null,
})
@ -324,4 +333,41 @@ export class AlarmRulesTableConfig extends EntityTableConfig<any> {
takeUntilDestroyed(this.destroyRef),
).subscribe(() => this.updateData());
}
private getTestScriptDialog(calculatedField: CalculatedField, expression: string, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true): Observable<string> {
if (calculatedField.type === CalculatedFieldType.ALARM) {
const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => {
const type = calculatedField.configuration.arguments[key].refEntityKey.type;
acc[key] = isObject(argumentsObj) && argumentsObj.hasOwnProperty(key)
? {...argumentsObj[key], type}
: type === ArgumentType.Rolling ? {values: [], type} : {value: '', type, ts: new Date().getTime()};
return acc;
}, {});
return this.dialog.open<CalculatedFieldScriptTestDialogComponent, CalculatedFieldTestScriptDialogData, string>(CalculatedFieldScriptTestDialogComponent,
{
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'],
data: {
arguments: resultArguments,
expression,
argumentsEditorCompleter: getCalculatedFieldArgumentsEditorCompleter(calculatedField.configuration.arguments),
argumentsHighlightRules: getCalculatedFieldArgumentsHighlights(calculatedField.configuration.arguments),
openCalculatedFieldEdit
}
}).afterClosed()
.pipe(
filter(Boolean),
tap(expression => {
if (openCalculatedFieldEdit) {
this.editCalculatedField({
entityId: this.entityId, ...calculatedField,
configuration: {...calculatedField.configuration, expression} as any
}, true)
}
}),
);
} else {
return of(null);
}
}
}

63
ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition-dialog.component.ts

@ -14,8 +14,8 @@
/// limitations under the License.
///
import { Component, DestroyRef, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { FormBuilder, Validators } from '@angular/forms';
@ -32,10 +32,7 @@ import {
AlarmRuleExpressionType
} from "@shared/models/alarm-rule.models";
import {
ArgumentType,
CalculatedField,
CalculatedFieldArgument,
CalculatedFieldEventArguments,
getCalculatedFieldArgumentsEditorCompleter,
getCalculatedFieldArgumentsHighlights
} from "@shared/models/calculated-field.models";
@ -43,19 +40,12 @@ import { TbEditorCompleter } from "@shared/models/ace/completion.models";
import { AceHighlightRules } from "@shared/models/ace/ace.models";
import { ComplexOperation, complexOperationTranslationMap } from "@shared/models/query/query.models";
import { Observable } from "rxjs";
import { filter, switchMap, tap } from "rxjs/operators";
import { isObject } from "@core/utils";
import {
CalculatedFieldScriptTestDialogComponent,
CalculatedFieldTestScriptDialogData
} from "@home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component";
import { CalculatedFieldsService } from "@core/http/calculated-fields.service";
export interface CfAlarmRuleConditionDialogData {
readonly: boolean;
condition: AlarmRuleCondition;
arguments?: Record<string, CalculatedFieldArgument>;
value?: CalculatedField;
testScript: (expression: string) => Observable<string>;
}
@Component({
@ -119,10 +109,7 @@ export class CfAlarmRuleConditionDialogComponent extends DialogComponent<CfAlarm
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: CfAlarmRuleConditionDialogData,
public dialogRef: MatDialogRef<CfAlarmRuleConditionDialogComponent, AlarmRuleCondition>,
private fb: FormBuilder,
private calculatedFieldsService: CalculatedFieldsService,
private destroyRef: DestroyRef,
private dialog: MatDialog) {
private fb: FormBuilder) {
super(store, router, dialogRef);
this.functionArgs = ['ctx', ...Object.keys(this.data.arguments)];
@ -240,50 +227,10 @@ export class CfAlarmRuleConditionDialogComponent extends DialogComponent<CfAlarm
}
onTestScript() {
this.testScript().subscribe(
this.data.testScript(this.conditionFormGroup.get('expression.expression').value).subscribe(
(expression) => {
this.conditionFormGroup.get('expression.expression').setValue(expression);
this.conditionFormGroup.get('expression.expression').markAsDirty();
})
}
testScript(): Observable<string> {
if (this.data.value?.id?.id) {
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(this.data.value?.id?.id, {ignoreLoading: true})
.pipe(
switchMap(event => {
const args = event?.arguments ? JSON.parse(event.arguments) : null;
return this.getTestScriptDialog(this.arguments, this.conditionFormGroup.get('expression.expression').value, args);
}),
takeUntilDestroyed(this.destroyRef)
)
}
return this.getTestScriptDialog(this.arguments, this.conditionFormGroup.get('expression.expression').value, null);
}
getTestScriptDialog(argumentsList: Record<string, CalculatedFieldArgument>, expression: string, argumentsObj?: CalculatedFieldEventArguments): Observable<string> {
const resultArguments = Object.keys(argumentsList).reduce((acc, key) => {
const type = argumentsList[key].refEntityKey.type;
acc[key] = isObject(argumentsObj) && argumentsObj.hasOwnProperty(key)
? {...argumentsObj[key], type}
: type === ArgumentType.Rolling ? {values: [], type} : {value: '', type, ts: new Date().getTime()};
return acc;
}, {});
return this.dialog.open<CalculatedFieldScriptTestDialogComponent, CalculatedFieldTestScriptDialogData, string>(CalculatedFieldScriptTestDialogComponent,
{
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'],
data: {
arguments: resultArguments,
expression: expression,
argumentsEditorCompleter: getCalculatedFieldArgumentsEditorCompleter(argumentsList),
argumentsHighlightRules: getCalculatedFieldArgumentsHighlights(argumentsList)
}
}).afterClosed()
.pipe(
filter(Boolean),
tap(expression => expression)
);
}
}

9
ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule-condition.component.ts

@ -43,12 +43,13 @@ import {
AlarmRuleSchedule,
AlarmRuleScheduleType
} from "@shared/models/alarm-rule.models";
import { CalculatedField, CalculatedFieldArgument } from "@shared/models/calculated-field.models";
import { CalculatedFieldArgument } from "@shared/models/calculated-field.models";
import {
AlarmRuleScheduleDialogData,
CfAlarmScheduleDialogComponent
} from "@home/components/alarm-rules/cf-alarm-schedule-dialog.component";
import { coerceBoolean } from "@shared/decorators/coercion";
import { Observable } from "rxjs";
@Component({
selector: 'tb-cf-alarm-rule-condition',
@ -84,8 +85,8 @@ export class CfAlarmRuleConditionComponent implements ControlValueAccessor, Vali
@coerceBoolean()
isClearCondition = false;
@Input()
value: CalculatedField;
@Input({required: true})
testScript: (expression: string) => Observable<string>;
alarmRuleConditionFormGroup = this.fb.group({
type: ['SIMPLE'],
@ -152,7 +153,7 @@ export class CfAlarmRuleConditionComponent implements ControlValueAccessor, Vali
readonly: this.disabled,
condition: this.disabled ? this.modelValue : deepClone(this.modelValue),
arguments: this.arguments,
value: this.value,
testScript: this.testScript
}
}).afterClosed().subscribe((result) => {
if (result) {

2
ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.html

@ -16,7 +16,7 @@
-->
<div class="tb-form-panel no-border no-padding" [formGroup]="alarmRuleFormGroup">
<tb-cf-alarm-rule-condition formControlName="condition" [arguments]="arguments" [required]="required" [value]="value" [isClearCondition]="isClearCondition">
<tb-cf-alarm-rule-condition formControlName="condition" [arguments]="arguments" [required]="required" [testScript]="testScript" [isClearCondition]="isClearCondition">
</tb-cf-alarm-rule-condition>
@if (!disabled || alarmRuleFormGroup.get('alarmDetails').value) {
<div class="tb-form-row space-between column-xs">

7
ui-ngx/src/app/modules/home/components/alarm-rules/cf-alarm-rule.component.ts

@ -28,12 +28,13 @@ import { isDefinedAndNotNull } from '@core/utils';
import { DashboardId } from '@shared/models/id/dashboard-id';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { AlarmRule, AlarmRuleCondition } from "@shared/models/alarm-rule.models";
import { CalculatedField, CalculatedFieldArgument } from "@shared/models/calculated-field.models";
import { CalculatedFieldArgument } from "@shared/models/calculated-field.models";
import {
AlarmRuleDetailsDialogComponent,
AlarmRuleDetailsDialogData
} from "@home/components/alarm-rules/alarm-rule-details-dialog.component";
import { coerceBoolean } from "@shared/decorators/coercion";
import { Observable } from "rxjs";
@Component({
selector: 'tb-cf-alarm-rule',
@ -69,8 +70,8 @@ export class CfAlarmRuleComponent implements ControlValueAccessor, OnInit, Valid
@coerceBoolean()
isClearCondition = false;
@Input()
value: CalculatedField;
@Input({required: true})
testScript: (expression: string) => Observable<string>;
private modelValue: AlarmRule;

5
ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.html

@ -34,7 +34,7 @@
</mat-select>
</mat-form-field>
</div>
<tb-cf-alarm-rule formControlName="alarmRule" [arguments]="arguments" [value]="value" required class="flex-1">
<tb-cf-alarm-rule formControlName="alarmRule" [arguments]="arguments" [testScript]="testScript" required class="flex-1">
</tb-cf-alarm-rule>
</div>
<button *ngIf="!disabled"
@ -57,8 +57,7 @@
<button mat-stroked-button color="primary"
[disabled]="createAlarmRulesFormArray().controls.length > 4"
type="button"
(click)="addCreateAlarmRule()"
matTooltipPosition="above">
(click)="addCreateAlarmRule()">
{{ 'alarm-rule.add-create-alarm-rule' | translate }}
</button>
</div>

7
ui-ngx/src/app/modules/home/components/alarm-rules/create-cf-alarm-rules.component.ts

@ -29,10 +29,11 @@ import {
} from '@angular/forms';
import { AlarmSeverity, alarmSeverityTranslations } from '@shared/models/alarm.models';
import { AlarmRule } from "@shared/models/alarm-rule.models";
import { CalculatedField, CalculatedFieldArgument } from "@shared/models/calculated-field.models";
import { CalculatedFieldArgument } from "@shared/models/calculated-field.models";
import { AlarmSeverityNotificationColors } from "@shared/models/notification.models";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { coerceBoolean } from "@shared/decorators/coercion";
import { Observable } from "rxjs";
@Component({
selector: 'tb-create-cf-alarm-rules',
@ -60,8 +61,8 @@ export class CreateCfAlarmRulesComponent implements ControlValueAccessor, Valida
@Input()
arguments: Record<string, CalculatedFieldArgument>;
@Input()
value: CalculatedField;
@Input({required: true})
testScript: (expression: string) => Observable<string>;
alarmSeverities = Object.keys(AlarmSeverity);
alarmSeverityEnum = AlarmSeverity;

3
ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-list.component.html

@ -71,8 +71,7 @@
</section>
<button mat-button mat-raised-button color="primary"
(click)="addFilter()"
type="button"
matTooltipPosition="above">
type="button">
{{ 'alarm-rule.add-filter' | translate }}
</button>

6
ui-ngx/src/app/modules/home/components/alarm-rules/filter/alarm-rule-filter-predicate-list.component.html

@ -69,15 +69,13 @@
<button mat-button mat-raised-button color="primary"
[class.!hidden]="disabled"
(click)="addPredicate(false)"
type="button"
matTooltipPosition="above">
type="button">
{{ 'action.add' | translate }}
</button>
<button mat-button mat-raised-button color="primary"
[class.!hidden]="disabled"
(click)="addPredicate(true)"
type="button"
matTooltipPosition="above">
type="button">
{{ 'filter.add-complex' | translate }}
</button>
</div>

5
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts

@ -309,9 +309,10 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
).subscribe(() => this.updateData());
}
private getTestScriptDialog(calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true): Observable<string> {
private getTestScriptDialog(calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true, expression?: string): Observable<string> {
if (
calculatedField.type === CalculatedFieldType.SCRIPT ||
calculatedField.type === CalculatedFieldType.RELATED_ENTITIES_AGGREGATION ||
(calculatedField.type === CalculatedFieldType.PROPAGATION && calculatedField.configuration.applyExpressionToResolvedArguments === true)
) {
const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => {
@ -327,7 +328,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'],
data: {
arguments: resultArguments,
expression: (calculatedField.configuration as CalculatedFieldScriptConfiguration | PropagationWithExpression).expression,
expression: expression ?? (calculatedField.configuration as CalculatedFieldScriptConfiguration | PropagationWithExpression).expression,
argumentsEditorCompleter: getCalculatedFieldArgumentsEditorCompleter(calculatedField.configuration.arguments),
argumentsHighlightRules: getCalculatedFieldArgumentsHighlights(calculatedField.configuration.arguments),
openCalculatedFieldEdit

29
ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/calculated-field-argument-panel.component.ts

@ -71,7 +71,6 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
@Input() ownerId: EntityId;
@Input() isScript: boolean;
@Input() usedArgumentNames: string[];
@Input() isOutputKey = false;
@Input() watchKeyChange = false;
@Input() hiddenEntityTypes = false;
@Input() hiddenEntityKeyTypes = false;
@ -81,6 +80,14 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
@Input() predefinedEntityFilter: EntityFilter;
@Input() forbiddenNames = FORBIDDEN_NAMES;
@Input() argumentEntityTypes = Object.values(ArgumentEntityType).filter(value => value !== ArgumentEntityType.RelationQuery) as ArgumentEntityType[];
@Input() argumentNameContext: {[key: string]: string} = {
label: 'calculated-fields.argument-name',
required: 'calculated-fields.hint.argument-name-required',
duplicate: 'calculated-fields.hint.argument-name-duplicate',
pattern: 'calculated-fields.hint.argument-name-pattern',
maxlength: 'calculated-fields.hint.argument-name-max-length',
forbidden: 'calculated-fields.hint.argument-name-forbidden'
};
@ViewChild('entityAutocomplete') entityAutocomplete: EntityAutocompleteComponent;
@ -110,15 +117,6 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
enableAutocomplete = false;
argumentNameContext = {
label: 'calculated-fields.argument-name',
required: 'calculated-fields.hint.argument-name-required',
duplicate: 'calculated-fields.hint.argument-name-duplicate',
pattern: 'calculated-fields.hint.argument-name-pattern',
maxlength: 'calculated-fields.hint.argument-name-max-length',
forbidden: 'calculated-fields.hint.argument-name-forbidden'
}
readonly ArgumentEntityTypeTranslations = ArgumentEntityTypeTranslations;
readonly ArgumentType = ArgumentType;
readonly DataKeyType = DataKeyType;
@ -171,17 +169,6 @@ export class CalculatedFieldArgumentPanelComponent implements OnInit, AfterViewI
this.setWatchKeyChange();
}
if (this.isOutputKey) {
this.argumentNameContext = {
label: 'calculated-fields.output-key',
required: 'calculated-fields.hint.output-key-required',
duplicate: 'calculated-fields.hint.output-key-duplicate',
pattern: 'calculated-fields.hint.output-key-pattern',
maxlength: 'calculated-fields.hint.output-key-max-length',
forbidden: 'calculated-fields.hint.output-key-forbidden'
}
}
if (this.defaultValueRequired) {
this.argumentFormGroup.get('defaultValue').addValidators(Validators.required);
this.argumentFormGroup.get('defaultValue').updateValueAndValidity({onlySelf: true});

9
ui-ngx/src/app/modules/home/components/calculated-fields/components/calculated-field-arguments/propagate-arguments-table.component.ts

@ -93,7 +93,14 @@ export class PropagateArgumentsTableComponent extends CalculatedFieldArgumentsTa
this.displayColumns = ['name', 'type', 'key', 'actions'];
this.panelAdditionalCtx = {
argumentEntityTypes: [ArgumentEntityType.Current],
isOutputKey: true,
argumentNameContext: {
label: 'calculated-fields.output-key',
required: 'calculated-fields.hint.output-key-required',
duplicate: 'calculated-fields.hint.output-key-duplicate',
pattern: 'calculated-fields.hint.output-key-pattern',
maxlength: 'calculated-fields.hint.output-key-max-length',
forbidden: 'calculated-fields.hint.output-key-forbidden'
},
watchKeyChange: true,
forbiddenNames: [...FORBIDDEN_NAMES, 'propagationCtx'],
};

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.html

@ -88,7 +88,7 @@
[entityId]="data.entityId"
[entityName]="data.entityName"
[tenantId]="data.tenantId"
[calculatedFieldId]="data.value?.id?.id"
[testScript]="onTestScript.bind(this)"
></tb-related-entities-aggregation-component>
}
@case (CalculatedFieldType.ENTITY_AGGREGATION) {

6
ui-ngx/src/app/modules/home/components/calculated-fields/components/dialog/calculated-field-dialog.component.ts

@ -105,19 +105,19 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
}
}
onTestScript(): Observable<string> {
onTestScript(expression?: string): Observable<string> {
const calculatedFieldId = this.data.value?.id?.id;
if (calculatedFieldId) {
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(calculatedFieldId, {ignoreLoading: true})
.pipe(
switchMap(event => {
const args = event?.arguments ? JSON.parse(event.arguments) : null;
return this.data.getTestScriptDialogFn(this.fromGroupValue, args, false);
return this.data.getTestScriptDialogFn(this.fromGroupValue, args, false, expression);
}),
takeUntilDestroyed(this.destroyRef)
)
}
return this.data.getTestScriptDialogFn(this.fromGroupValue, null, false);
return this.data.getTestScriptDialogFn(this.fromGroupValue, null, false, expression);
}
private applyDialogData(): void {

1
ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.html

@ -34,6 +34,7 @@
</div>
<tb-calculated-field-metrics-table formControlName="metrics"
simpleMode
[testScript]="testScript"
[arguments]="arguments$ | async"
></tb-calculated-field-metrics-table>
</div>

4
ui-ngx/src/app/modules/home/components/calculated-fields/components/entity-aggregation-configuration/entity-aggregation-component.component.ts

@ -42,7 +42,7 @@ import { deepClone, isDefinedAndNotNull } from '@core/utils';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { merge } from 'rxjs';
import { merge, Observable } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import _moment from 'moment';
@ -85,6 +85,8 @@ export class EntityAggregationComponentComponent implements ControlValueAccessor
@Input({required: true})
entityName: string;
@Input() testScript: (expression?: string) => Observable<string>;
readonly minAllowedAggregationIntervalInSecForCF = getCurrentAuthState(this.store).minAllowedAggregationIntervalInSecForCF;
readonly DayInSec = DAY / SECOND;

10
ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.html

@ -110,7 +110,7 @@
matTooltip="{{ 'calculated-fields.test-script-function' | translate }}"
matTooltipPosition="above"
class="tb-mat-32"
[disabled]="!argumentsList.length"
[disabled]="!arguments.length"
(click)="onTestScript('filter')">
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
</button>
@ -119,7 +119,7 @@
<button mat-button mat-raised-button color="primary"
type="button"
(click)="onTestScript('filter')"
[disabled]="!argumentsList.length">
[disabled]="!arguments.length">
{{ 'calculated-fields.test-script-function' | translate }}
</button>
</div>
@ -145,7 +145,7 @@
<div class="fixed-title-width tb-required">{{ 'calculated-fields.argument-name' | translate }}</div>
<mat-form-field class="flex-1" appearance="outline" subscriptSizing="dynamic">
<mat-select formControlName="key" placeholder="{{ 'action.set' | translate }}">
@for (argument of argumentsList; track argument) {
@for (argument of arguments; track argument) {
<mat-option [value]="argument">{{ argument }}</mat-option>
}
</mat-select>
@ -179,7 +179,7 @@
matTooltip="{{ 'calculated-fields.test-script-function' | translate }}"
matTooltipPosition="above"
class="tb-mat-32"
[disabled]="!argumentsList.length"
[disabled]="!arguments.length"
(click)="onTestScript('input.function')">
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
</button>
@ -188,7 +188,7 @@
<button mat-button mat-raised-button color="primary"
type="button"
(click)="onTestScript('input.function')"
[disabled]="!argumentsList.length">
[disabled]="!arguments.length">
{{ 'calculated-fields.test-script-function' | translate }}
</button>
</div>

69
ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, DestroyRef, Input, OnInit, output } from '@angular/core';
import { Component, Input, OnInit, output } from '@angular/core';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { FormBuilder, Validators } from '@angular/forms';
import { charsWithNumRegex } from '@shared/models/regex.constants';
@ -23,14 +23,9 @@ import {
AggFunctionTranslations,
AggInputType,
AggInputTypeTranslations,
ArgumentType,
CalculatedFieldAggMetricValue,
CalculatedFieldArgument,
CalculatedFieldEventArguments,
FORBIDDEN_NAMES,
forbiddenNamesValidator,
getCalculatedFieldArgumentsEditorCompleter,
getCalculatedFieldArgumentsHighlights,
uniqueNameValidator
} from '@shared/models/calculated-field.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@ -38,15 +33,7 @@ import { EntityFilter } from '@shared/models/query/query.models';
import { ScriptLanguage } from '@shared/models/rule-node.models';
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
import { AceHighlightRules } from '@shared/models/ace/ace.models';
import { MatDialog } from "@angular/material/dialog";
import { Observable } from "rxjs";
import { isObject } from "@core/utils";
import {
CalculatedFieldScriptTestDialogComponent,
CalculatedFieldTestScriptDialogData
} from "@home/components/calculated-fields/components/test-dialog/calculated-field-script-test-dialog.component";
import { filter, switchMap, tap } from "rxjs/operators";
import { CalculatedFieldsService } from "@core/http/calculated-fields.service";
interface CalculatedFieldAggMetricValuePanel extends CalculatedFieldAggMetricValue {
allowFilter: boolean;
@ -62,15 +49,14 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit {
@Input() buttonTitle: string;
@Input() metric: CalculatedFieldAggMetricValue;
@Input() usedNames: string[];
@Input() arguments: Record<string, CalculatedFieldArgument>;
@Input() arguments: Array<string>;
@Input() simpleMode: boolean;
@Input() editorCompleter: TbEditorCompleter;
@Input() highlightRules: AceHighlightRules;
@Input() calculatedFieldId: string;
@Input({required: true}) testScript: (expression?: string) => Observable<string>;
metricDataApplied = output<CalculatedFieldAggMetricValue>();
filterExpanded = false;
argumentsList: Array<string>
functionArgs: Array<string>
metricForm = this.fb.group({
@ -98,9 +84,6 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit {
constructor(
private fb: FormBuilder,
private popover: TbPopoverComponent<CalculatedFieldMetricsPanelComponent>,
private dialog: MatDialog,
private calculatedFieldsService: CalculatedFieldsService,
private destroyRef: DestroyRef
) {
this.observeFilterAllowChange();
this.observeInputTypeChange();
@ -119,8 +102,7 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit {
this.validateInputTypeFilter(data.input?.type ?? AggInputType.key);
this.validateInputKey();
this.argumentsList = Object.keys(this.arguments);
this.functionArgs = ['ctx', ...this.argumentsList];
this.functionArgs = ['ctx', ...this.arguments];
}
saveMetric(): void {
@ -178,55 +160,16 @@ export class CalculatedFieldMetricsPanelComponent implements OnInit {
}
private validateInputKey() {
if (this.metric.input?.type === AggInputType.key && !Object.keys(this.arguments).includes(this.metric.input.key)) {
if (this.metric.input?.type === AggInputType.key && !this.arguments.includes(this.metric.input.key)) {
this.metricForm.get('input.key').setValue(null);
this.metricForm.get('input.key').markAsTouched();
}
}
onTestScript(scriptFunc: 'filter' | 'input.function') {
this.testScript(scriptFunc).subscribe(expression => {
this.testScript(this.metricForm.get(scriptFunc).value).subscribe(expression => {
this.metricForm.get(scriptFunc).setValue(expression);
this.metricForm.get(scriptFunc).markAsDirty();
});
}
testScript(scriptFunc: 'filter' | 'input.function'): Observable<string> {
if (this.calculatedFieldId) {
return this.calculatedFieldsService.getLatestCalculatedFieldDebugEvent(this.calculatedFieldId, {ignoreLoading: true})
.pipe(
switchMap(event => {
const args = event?.arguments ? JSON.parse(event.arguments) : null;
return this.getTestScriptDialog(this.arguments, this.metricForm.get(scriptFunc).value, args);
}),
takeUntilDestroyed(this.destroyRef)
)
}
return this.getTestScriptDialog(this.arguments, this.metricForm.get(scriptFunc).value, null);
}
getTestScriptDialog(argumentsList: Record<string, CalculatedFieldArgument>, expression: string, argumentsObj?: CalculatedFieldEventArguments): Observable<string> {
const resultArguments = Object.keys(argumentsList).reduce((acc, key) => {
const type = argumentsList[key].refEntityKey.type;
acc[key] = isObject(argumentsObj) && argumentsObj.hasOwnProperty(key)
? {...argumentsObj[key], type}
: type === ArgumentType.Rolling ? {values: [], type} : {value: '', type, ts: new Date().getTime()};
return acc;
}, {});
return this.dialog.open<CalculatedFieldScriptTestDialogComponent, CalculatedFieldTestScriptDialogData, string>(CalculatedFieldScriptTestDialogComponent,
{
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'],
data: {
arguments: resultArguments,
expression: expression,
argumentsEditorCompleter: getCalculatedFieldArgumentsEditorCompleter(argumentsList),
argumentsHighlightRules: getCalculatedFieldArgumentsHighlights(argumentsList)
}
}).afterClosed()
.pipe(
filter(Boolean),
tap(expression => expression)
);
}
}

8
ui-ngx/src/app/modules/home/components/calculated-fields/components/metrics/calculated-field-metrics-table.component.ts

@ -40,7 +40,6 @@ import {
AggInputTypeTranslations,
CalculatedFieldAggMetric,
CalculatedFieldAggMetricValue,
CalculatedFieldArgument,
} from '@shared/models/calculated-field.models';
import { MatButton } from '@angular/material/button';
import { TbPopoverService } from '@shared/components/popover.service';
@ -57,6 +56,7 @@ import {
} from '@home/components/calculated-fields/components/metrics/calculated-field-metrics-panel.component';
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
import { AceHighlightRules } from '@shared/models/ace/ace.models';
import { Observable } from "rxjs";
@Component({
selector: 'tb-calculated-field-metrics-table',
@ -77,11 +77,11 @@ import { AceHighlightRules } from '@shared/models/ace/ace.models';
})
export class CalculatedFieldMetricsTableComponent implements OnInit, ControlValueAccessor, Validator, AfterViewInit {
@Input() arguments: Record<string, CalculatedFieldArgument>;
@Input() arguments: Array<string>;
@Input() editorCompleter: TbEditorCompleter;
@Input() highlightRules: AceHighlightRules;
@Input({transform: booleanAttribute}) simpleMode: boolean = false;
@Input() calculatedFieldId: string;
@Input({required: true}) testScript: (expression?: string) => Observable<string>;
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ -168,7 +168,7 @@ export class CalculatedFieldMetricsTableComponent implements OnInit, ControlValu
editorCompleter: this.editorCompleter,
highlightRules: this.highlightRules,
simpleMode: this.simpleMode,
calculatedFieldId: this.calculatedFieldId
testScript: this.testScript
};
this.popoverComponent = this.popoverService.displayPopover({
trigger,

2
ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.html

@ -55,7 +55,7 @@
{{ 'calculated-fields.metrics.metrics' | translate }}
</div>
<tb-calculated-field-metrics-table formControlName="metrics"
[calculatedFieldId]="calculatedFieldId"
[testScript]="testScript"
[arguments]="arguments$ | async"
[highlightRules]="argumentsHighlightRules$ | async"
[editorCompleter]="argumentsEditorCompleter$ | async"

5
ui-ngx/src/app/modules/home/components/calculated-fields/components/related-entities-aggregation-configuration/related-entities-aggregation-component.component.ts

@ -73,7 +73,8 @@ export class RelatedEntitiesAggregationComponentComponent implements ControlValu
@Input({required: true})
entityName: string;
@Input() calculatedFieldId: string;
@Input({required: true})
testScript: (expression?: string) => Observable<string>;
readonly ScriptLanguage = ScriptLanguage;
readonly CalculatedFieldType = CalculatedFieldType;
@ -95,7 +96,7 @@ export class RelatedEntitiesAggregationComponentComponent implements ControlValu
});
arguments$ = this.relatedAggregationConfiguration.get('arguments').valueChanges.pipe(
map(argumentsObj => argumentsObj)
map(argumentsObj => Object.keys(argumentsObj))
);
argumentsEditorCompleter$ = this.relatedAggregationConfiguration.get('arguments').valueChanges.pipe(

4
ui-ngx/src/app/shared/models/alarm-rule.models.ts

@ -27,6 +27,8 @@ import {
StringOperation
} from "@shared/models/query/query.models";
import { EntityType } from "@shared/models/entity-type.models";
import { Observable } from "rxjs";
import { CalculatedField } from "@shared/models/calculated-field.models";
export enum AlarmRuleScheduleType {
ANY_TIME = 'ANY_TIME',
@ -161,3 +163,5 @@ export interface AlarmRuleFilterConfig {
export const alarmRuleDefaultScript =
'// Sample expression for an alarm rule: triggers when temperature is above 20 degree\n' +
'return temperature > 20;'
export type AlarmRuleTestScriptFn = (calculatedField: CalculatedField, expression: string, argumentsObj?: Record<string, unknown>, closeAllOnSave?: boolean) => Observable<string>;

2
ui-ngx/src/app/shared/models/calculated-field.models.ts

@ -496,7 +496,7 @@ export interface CalculatedFieldArgumentValue extends CalculatedFieldArgument {
entityName?: string;
}
export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>, closeAllOnSave?: boolean) => Observable<string>;
export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>, closeAllOnSave?: boolean, expression?: string) => Observable<string>;
export interface CalculatedFieldTestScriptInputParams {
arguments: CalculatedFieldEventArguments;

Loading…
Cancel
Save