Added Calculated field arguments autocomplete

This commit is contained in:
mpetrov 2025-02-14 18:39:45 +02:00
parent aa2e277c45
commit 4bd0833eed
5 changed files with 110 additions and 6 deletions

View File

@ -36,9 +36,14 @@ import { EntityDebugSettingsPanelComponent } from '@home/components/entity/debug
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { catchError, filter, switchMap, tap } from 'rxjs/operators';
import {
ArgumentType,
CalculatedField,
CalculatedFieldArgument,
CalculatedFieldEventArguments,
CalculatedFieldDebugDialogData,
CalculatedFieldDialogData,
CalculatedFieldRollingValueArgumentAutocomplete,
CalculatedFieldSingleValueArgumentAutocomplete,
CalculatedFieldTestScriptDialogData,
} from '@shared/models/calculated-field.models';
import {
@ -48,6 +53,7 @@ import {
} from './components/public-api';
import { ImportExportService } from '@shared/import-export/import-export.service';
import { isObject } from '@core/utils';
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedField, PageLink> {
@ -58,7 +64,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
readonly tenantId = getCurrentAuthUser(this.store).tenantId;
additionalDebugActionConfig = {
title: this.translate.instant('calculated-fields.see-debug-events'),
action: (calculatedField: CalculatedField) => this.openDebugDialog.call(this, calculatedField),
action: (calculatedField: CalculatedField) => this.openDebugEventsDialog.call(this, calculatedField),
};
constructor(private calculatedFieldsService: CalculatedFieldsService,
@ -122,7 +128,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
name: this.translate.instant('entity-view.events'),
icon: 'history',
isEnabled: () => true,
onAction: (_, entity) => this.openDebugDialog(entity),
onAction: (_, entity) => this.openDebugEventsDialog(entity),
},
{
name: '',
@ -149,7 +155,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
const { debugSettings = {}, id } = calculatedField;
const additionalActionConfig = {
...this.additionalDebugActionConfig,
action: () => this.openDebugDialog(calculatedField)
action: () => this.openDebugEventsDialog(calculatedField)
};
const { viewContainerRef } = this.getTable();
if ($event) {
@ -211,14 +217,15 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
entityName: this.entityName,
additionalDebugActionConfig: this.additionalDebugActionConfig,
getTestScriptDialogFn: this.getTestScriptDialog.bind(this),
isDirty
isDirty,
getArgumentsEditorCompleterFn: this.getArgumentsEditorCompleter,
},
enterAnimationDuration: isDirty ? 0 : null,
})
.afterClosed();
}
private openDebugDialog(calculatedField: CalculatedField): void {
private openDebugEventsDialog(calculatedField: CalculatedField): void {
this.dialog.open<CalculatedFieldDebugDialogComponent, CalculatedFieldDebugDialogData, null>(CalculatedFieldDebugDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
@ -267,7 +274,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
).subscribe(() => this.updateData());
}
private getTestScriptDialog(calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>, openCalculatedFieldEdit = true): Observable<string> {
private getTestScriptDialog(calculatedField: CalculatedField, argumentsObj?: CalculatedFieldEventArguments, openCalculatedFieldEdit = true): Observable<string> {
const resultArguments = Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => {
acc[key] = isObject(argumentsObj) && argumentsObj.hasOwnProperty(key) ? argumentsObj[key] : '';
return acc;
@ -279,6 +286,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
data: {
arguments: resultArguments,
expression: calculatedField.configuration.expression,
argumentsEditorCompleter: this.getArgumentsEditorCompleter(calculatedField.configuration.arguments),
openCalculatedFieldEdit
}
}).afterClosed()
@ -291,4 +299,13 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
}),
);
}
private getArgumentsEditorCompleter(argumentsObj: Record<string, CalculatedFieldArgument>): TbEditorCompleter {
return new TbEditorCompleter(Object.keys(argumentsObj).reduce((acc, key) => {
acc[key] = argumentsObj[key].refEntityKey.type === ArgumentType.Rolling
? CalculatedFieldRollingValueArgumentAutocomplete
: CalculatedFieldSingleValueArgumentAutocomplete;
return acc;
}, {}))
}
}

View File

@ -99,6 +99,7 @@
[functionArgs]="functionArgs$ | async"
[disableUndefinedCheck]="true"
[scriptLanguage]="ScriptLanguage.TBEL"
[editorCompleter]="argumentsEditorCompleter$ | async"
helpId="calculated-field/expression_fn"
>
<button toolbarSuffixButton

View File

@ -66,6 +66,12 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
map(argumentsObj => Object.keys(argumentsObj))
);
argumentsEditorCompleter$ = this.configFormGroup.get('arguments').valueChanges
.pipe(
startWith(this.data.value?.configuration?.arguments ?? {}),
map(argumentsObj => this.data.getArgumentsEditorCompleterFn(argumentsObj))
);
additionalDebugActionConfig = this.data.value?.id ? {
...this.data.additionalDebugActionConfig,
action: () => this.data.additionalDebugActionConfig.action({ id: this.data.value.id, ...this.fromGroupValue }),

View File

@ -40,6 +40,7 @@
[disableUndefinedCheck]="true"
[fillHeight]="true"
[scriptLanguage]="ScriptLanguage.TBEL"
[editorCompleter]="data.argumentsEditorCompleter"
resultType="object"
helpId="calculated-field/test-expression_fn"
/>

View File

@ -27,6 +27,7 @@ import { AttributeScope } from '@shared/models/telemetry/telemetry.models';
import { EntityType } from '@shared/models/entity-type.models';
import { AliasFilterType } from '@shared/models/alias.models';
import { Observable } from 'rxjs';
import { TbEditorCompleter } from '@shared/models/ace/completion.models';
export interface CalculatedField extends Omit<BaseData<CalculatedFieldId>, 'label'>, HasVersion, HasTenantId, ExportableEntity<CalculatedFieldId> {
debugSettings?: EntityDebugSettings;
@ -129,6 +130,8 @@ export interface CalculatedFieldArgumentValue extends CalculatedFieldArgument {
export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>, closeAllOnSave?: boolean) => Observable<string>;
export type CalculatedFieldArgumentsEditorCompleterFn = (argumentsObj: Record<string, CalculatedFieldArgument>) => TbEditorCompleter;
export interface CalculatedFieldDialogData {
value?: CalculatedField;
buttonTitle: string;
@ -139,6 +142,7 @@ export interface CalculatedFieldDialogData {
additionalDebugActionConfig: AdditionalDebugActionConfig<(calculatedField: CalculatedField) => void>;
getTestScriptDialogFn: CalculatedFieldTestScriptFn;
isDirty?: boolean;
getArgumentsEditorCompleterFn: CalculatedFieldArgumentsEditorCompleterFn;
}
export interface CalculatedFieldDebugDialogData {
@ -153,6 +157,7 @@ export interface CalculatedFieldTestScriptInputParams {
}
export interface CalculatedFieldTestScriptDialogData extends CalculatedFieldTestScriptInputParams {
argumentsEditorCompleter: TbEditorCompleter
openCalculatedFieldEdit?: boolean;
}
@ -186,3 +191,77 @@ export const getCalculatedFieldCurrentEntityFilter = (entityName: string, entity
};
}
}
export interface CalculatedFieldAttributeArgumentValue<ValueType = unknown> {
ts: number;
value: ValueType;
}
export interface CalculatedFieldLatestTelemetryArgumentValue<ValueType = unknown> {
ts: number;
value: ValueType;
}
export interface CalculatedFieldRollingTelemetryArgumentValue<ValueType = unknown> {
timewindow: { startTs: number; endTs: number; limit: number };
values: CalculatedFieldSingleArgumentValue<ValueType>[];
}
export type CalculatedFieldSingleArgumentValue<ValueType = unknown> = CalculatedFieldAttributeArgumentValue<ValueType> & CalculatedFieldLatestTelemetryArgumentValue<ValueType>;
export type CalculatedFieldArgumentEventValue<ValueType = unknown> = CalculatedFieldAttributeArgumentValue<ValueType> | CalculatedFieldLatestTelemetryArgumentValue<ValueType> | CalculatedFieldRollingTelemetryArgumentValue<ValueType>;
export type CalculatedFieldEventArguments<ValueType = unknown> = Record<string, CalculatedFieldArgumentEventValue<ValueType>>;
export const CalculatedFieldSingleValueArgumentAutocomplete = {
meta: 'object',
type: '{ ts: number; value: any; }',
description: 'Calculated field single value argument.',
children: {
ts: {
meta: 'number',
type: 'number',
description: 'Time stamp',
},
value: {
meta: 'any',
type: 'any',
description: 'Value',
}
},
};
export const CalculatedFieldRollingValueArgumentAutocomplete = {
meta: 'object',
type: '{ values: { ts: number; value: any; }[]; timewindow: { startTs: number; endTs: number; limit: number } }; }',
description: 'Calculated field rolling value argument.',
children: {
values: {
meta: 'array',
type: '{ ts: number; value: any; }[]',
description: 'Values array',
},
timewindow: {
meta: 'object',
type: '{ startTs: number; endTs: number; limit: number }',
description: 'Time window configuration',
children: {
startTs: {
meta: 'number',
type: 'number',
description: 'Start time stamp',
},
endTs: {
meta: 'number',
type: 'number',
description: 'End time stamp',
},
limit: {
meta: 'number',
type: 'number',
description: 'Limit',
}
}
}
},
};