Review comments resolving and refactoring

This commit is contained in:
mpetrov 2025-02-11 15:43:13 +02:00
parent 84b9bde577
commit 9e19fab11d
15 changed files with 109 additions and 73 deletions

View File

@ -22,7 +22,7 @@ import { PageData } from '@shared/models/page/page-data';
import { CalculatedField, CalculatedFieldTestScriptInputParams } from '@shared/models/calculated-field.models';
import { PageLink } from '@shared/models/page/page-link';
import { EntityId } from '@shared/models/id/entity-id';
import { TestScriptResult } from '@shared/models/entity.models';
import { EntityTestScriptResult } from '@shared/models/entity.models';
@Injectable({
providedIn: 'root'
@ -50,7 +50,7 @@ export class CalculatedFieldsService {
defaultHttpOptionsFromConfig(config));
}
public testScript(inputParams: CalculatedFieldTestScriptInputParams, config?: RequestConfig): Observable<TestScriptResult> {
return this.http.post<TestScriptResult>('/api/calculatedField/testScript', inputParams, defaultHttpOptionsFromConfig(config));
public testScript(inputParams: CalculatedFieldTestScriptInputParams, config?: RequestConfig): Observable<EntityTestScriptResult> {
return this.http.post<EntityTestScriptResult>('/api/calculatedField/testScript', inputParams, defaultHttpOptionsFromConfig(config));
}
}

View File

@ -35,6 +35,7 @@ import {
RuleNodeConfiguration,
ScriptLanguage,
TestScriptInputParams,
TestScriptResult
} from '@app/shared/models/rule-node.models';
import { componentTypeBySelector, ResourcesService } from '../services/resources.service';
import { catchError, map, mergeMap } from 'rxjs/operators';
@ -43,7 +44,6 @@ import { deepClone, snakeCase } from '@core/utils';
import { DebugRuleNodeEventBody } from '@app/shared/models/event.models';
import { Edge } from '@shared/models/edge.models';
import { IModulesMap } from '@modules/common/modules-map.models';
import { TestScriptResult } from '@shared/models/entity.models';
@Injectable({
providedIn: 'root'

View File

@ -34,12 +34,12 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TbPopoverService } from '@shared/components/popover.service';
import { EntityDebugSettingsPanelComponent } from '@home/components/entity/debug/entity-debug-settings-panel.component';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { catchError, filter, switchMap } from 'rxjs/operators';
import { catchError, filter, switchMap, tap } from 'rxjs/operators';
import {
CalculatedField,
CalculatedFieldDebugDialogData,
CalculatedFieldDialogData,
CalculatedFieldScriptTestDialogData
CalculatedFieldTestScriptInputParams,
} from '@shared/models/calculated-field.models';
import {
CalculatedFieldDebugDialogComponent,
@ -47,7 +47,6 @@ import {
CalculatedFieldScriptTestDialogComponent
} from './components/public-api';
import { ImportExportService } from '@shared/import-export/import-export.service';
import { CalculatedFieldId } from '@shared/models/id/calculated-field-id';
export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedField, PageLink> {
@ -58,7 +57,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
readonly tenantId = getCurrentAuthUser(this.store).tenantId;
additionalDebugActionConfig = {
title: this.translate.instant('calculated-fields.see-debug-events'),
action: (id?: CalculatedFieldId, expression?: string) => this.openDebugDialog.call(this, id, expression),
action: (calculatedField: CalculatedField) => this.openDebugDialog.call(this, calculatedField),
};
constructor(private calculatedFieldsService: CalculatedFieldsService,
@ -139,10 +138,11 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
return this.calculatedFieldsService.getCalculatedFields(this.entityId, pageLink);
}
onOpenDebugConfig($event: Event, { debugSettings = {}, configuration, id }: CalculatedField): void {
onOpenDebugConfig($event: Event, calculatedField: CalculatedField): void {
const { debugSettings = {}, id } = calculatedField;
const additionalActionConfig = {
...this.additionalDebugActionConfig,
action: () => this.openDebugDialog(id, configuration?.expression)
action: () => this.openDebugDialog(calculatedField)
};
const { viewContainerRef } = this.getTable();
if ($event) {
@ -178,8 +178,8 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
)
}
private editCalculatedField(calculatedField: CalculatedField): void {
this.getCalculatedFieldDialog(calculatedField, 'action.apply')
private editCalculatedField(calculatedField: CalculatedField, isDirty = false): void {
this.getCalculatedFieldDialog(calculatedField, 'action.apply', isDirty)
.pipe(
filter(Boolean),
switchMap((updatedCalculatedField) => this.calculatedFieldsService.saveCalculatedField({ ...calculatedField, ...updatedCalculatedField })),
@ -191,7 +191,7 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
});
}
private getCalculatedFieldDialog(value?: CalculatedField, buttonTitle = 'action.add'): Observable<CalculatedField> {
private getCalculatedFieldDialog(value?: CalculatedField, buttonTitle = 'action.add', isDirty = false): Observable<CalculatedField> {
return this.dialog.open<CalculatedFieldDialogComponent, CalculatedFieldDialogData, CalculatedField>(CalculatedFieldDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
@ -204,21 +204,20 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
entityName: this.entityName,
additionalDebugActionConfig: this.additionalDebugActionConfig,
testScriptFn: this.getTestScriptDialog.bind(this),
isDirty
}
})
.afterClosed();
}
private openDebugDialog(id: CalculatedFieldId, expression: string): void {
private openDebugDialog(calculatedField: CalculatedField): void {
this.dialog.open<CalculatedFieldDebugDialogComponent, CalculatedFieldDebugDialogData, null>(CalculatedFieldDebugDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
tenantId: this.tenantId,
entityId: this.entityId,
id,
expression,
testScriptFn: this.getTestScriptDialog.bind(this),
value: calculatedField,
getTestScriptDialogFn: this.getTestScriptDialog.bind(this),
}
})
.afterClosed()
@ -260,16 +259,21 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
).subscribe(() => this.updateData());
}
private getTestScriptDialog(argumentsObj: Record<string, unknown>, expression: string, withApply = false): Observable<string> {
return this.dialog.open<CalculatedFieldScriptTestDialogComponent, CalculatedFieldScriptTestDialogData, string>(CalculatedFieldScriptTestDialogComponent,
private getTestScriptDialog(calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>): Observable<string> {
return this.dialog.open<CalculatedFieldScriptTestDialogComponent, CalculatedFieldTestScriptInputParams, string>(CalculatedFieldScriptTestDialogComponent,
{
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog', 'tb-fullscreen-dialog-gt-xs'],
data: {
arguments: argumentsObj,
expression,
withApply,
arguments: argumentsObj ?? Object.keys(calculatedField.configuration.arguments).reduce((acc, key) => ({...acc, [key]: '' }), {}),
expression: calculatedField.configuration.expression,
}
}).afterClosed();
}).afterClosed()
.pipe(
filter(Boolean),
tap(expression =>
this.editCalculatedField({...calculatedField, configuration: {...calculatedField.configuration, expression } }, true)
),
);
}
}

View File

@ -32,7 +32,7 @@
[disabledEventTypes]="[EventType.LC_EVENT, EventType.ERROR, EventType.STATS]"
[defaultEventType]="DebugEventType.DEBUG_CALCULATED_FIELD"
[active]="true"
[entityId]="data.id"
[entityId]="data.value.id"
[functionTestButtonLabel]="'common.test-function' | translate"
(debugEventSelected)="onDebugEventSelected($event)"
/>

View File

@ -22,14 +22,14 @@ import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component';
import { CalculatedFieldEventBody, DebugEventType, EventType } from '@shared/models/event.models';
import { EventTableComponent } from '@home/components/event/event-table.component';
import { CalculatedFieldDebugDialogData } from '@shared/models/calculated-field.models';
import { CalculatedFieldDebugDialogData, CalculatedFieldType } from '@shared/models/calculated-field.models';
@Component({
selector: 'tb-calculated-field-debug-dialog',
styleUrls: ['calculated-field-debug-dialog.component.scss'],
templateUrl: './calculated-field-debug-dialog.component.html',
})
export class CalculatedFieldDebugDialogComponent extends DialogComponent<CalculatedFieldDebugDialogComponent, null> implements AfterViewInit {
export class CalculatedFieldDebugDialogComponent extends DialogComponent<CalculatedFieldDebugDialogComponent, string> implements AfterViewInit {
@ViewChild(EventTableComponent, {static: true}) eventsTable: EventTableComponent;
@ -40,12 +40,13 @@ export class CalculatedFieldDebugDialogComponent extends DialogComponent<Calcula
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: CalculatedFieldDebugDialogData,
protected dialogRef: MatDialogRef<CalculatedFieldDebugDialogComponent, null>) {
protected dialogRef: MatDialogRef<CalculatedFieldDebugDialogComponent, string>) {
super(store, router, dialogRef);
}
ngAfterViewInit(): void {
this.eventsTable.entitiesTable.updateData();
this.eventsTable.entitiesTable.cellActionDescriptors[0].isEnabled = () => this.data.value.type === CalculatedFieldType.SCRIPT;
}
cancel(): void {
@ -53,6 +54,7 @@ export class CalculatedFieldDebugDialogComponent extends DialogComponent<Calcula
}
onDebugEventSelected(event: CalculatedFieldEventBody): void {
this.data.testScriptFn(JSON.parse(event.arguments), this.data.expression);
this.data.getTestScriptDialogFn(this.data.value, JSON.parse(event.arguments))
.subscribe(expression => this.dialogRef.close(expression));
}
}

View File

@ -99,7 +99,7 @@
[functionArgs]="functionArgs$ | async"
[disableUndefinedCheck]="true"
[scriptLanguage]="ScriptLanguage.TBEL"
helpId="[TODO]: [Calculated Fields] add valid link"
helpId="calculated-field/expression_fn"
/>
<div>
<button mat-button mat-raised-button color="primary"

View File

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, Inject } from '@angular/core';
import { AfterViewInit, 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';
@ -41,7 +41,7 @@ import { ScriptLanguage } from '@shared/models/rule-node.models';
selector: 'tb-calculated-field-dialog',
templateUrl: './calculated-field-dialog.component.html',
})
export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFieldDialogComponent, CalculatedField> {
export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFieldDialogComponent, CalculatedField> implements AfterViewInit {
fieldFormGroup = this.fb.group({
name: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex), Validators.maxLength(255)]],
@ -67,7 +67,7 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
additionalDebugActionConfig = this.data.value?.id ? {
...this.data.additionalDebugActionConfig,
action: () => this.data.additionalDebugActionConfig.action(this.data.value.id, this.data.value.configuration.expression)
action: () => this.data.additionalDebugActionConfig.action({ id: this.data.value.id, ...this.fromGroupValue }),
} : null;
readonly OutputTypeTranslations = OutputTypeTranslations;
@ -98,24 +98,31 @@ export class CalculatedFieldDialogComponent extends DialogComponent<CalculatedFi
return this.fieldFormGroup.get('configuration').get('output') as FormGroup;
}
get fromGroupValue(): CalculatedField {
const { configuration, type, ...rest } = this.fieldFormGroup.value;
const { expressionSIMPLE, expressionSCRIPT, ...restConfig } = configuration;
return { configuration: { ...restConfig, type, expression: configuration['expression'+type] }, ...rest, type } as CalculatedField;
}
ngAfterViewInit(): void {
if (this.data.isDirty) {
this.fieldFormGroup.markAsDirty();
}
}
cancel(): void {
this.dialogRef.close(null);
}
add(): void {
if (this.fieldFormGroup.valid) {
const { configuration, type, ...rest } = this.fieldFormGroup.value;
const { expressionSIMPLE, expressionSCRIPT, ...restConfig } = configuration;
this.dialogRef.close({ configuration: { ...restConfig, type, expression: configuration['expression'+type] }, ...rest, type } as CalculatedField);
this.dialogRef.close(this.fromGroupValue);
}
}
onTestScript(): void {
this.data.testScriptFn(
Object.fromEntries(Object.keys(this.configFormGroup.get('arguments').value).map(k => [k, ''])),
this.configFormGroup.get('expressionSCRIPT').value,
true
).pipe(filter(Boolean)).subscribe((expression: string) => {
this.data.testScriptFn(this.fromGroupValue)
.pipe(filter(Boolean)).subscribe((expression: string) => {
this.configFormGroup.get('expressionSCRIPT').setValue(expression);
this.configFormGroup.get('expressionSCRIPT').markAsDirty();
});

View File

@ -17,7 +17,7 @@
-->
<form class="test-dialog-container size-full" [formGroup]="calculatedFieldScriptTestFormGroup">
<mat-toolbar class="flex justify-between" color="primary">
<h2>{{ 'calculated-fields.test-script-function' | translate }} ({{ 'TBEL' }})</h2>
<h2>{{ 'calculated-fields.test-script-function' | translate }} ({{ 'api-usage.tbel' | translate }})</h2>
<button mat-icon-button
(click)="cancel()"
type="button">
@ -29,7 +29,7 @@
<div class="tb-fullscreen-panel flex size-full flex-row">
<div #leftPanel class="test-block-content overflow-hidden">
<div class="relative size-full min-w-64">
<div class="absolute right-28 top-[9px] z-10 text-[12px] font-bold">
<div class="block-label-container left">
<span class="block-label">{{ 'calculated-fields.expression' | translate }}</span>
</div>
<tb-js-func
@ -41,14 +41,14 @@
[fillHeight]="true"
[scriptLanguage]="ScriptLanguage.TBEL"
resultType="object"
helpId="[TODO]: [Calculated Fields] add valid link"
helpId="calculated-field/test-expression_fn"
/>
</div>
</div>
<div #rightPanel>
<div #topRightPanel class="test-block-content">
<div class="relative flex size-full min-w-96 gap-2">
<div class="absolute right-2 top-[6px] z-10 text-[12px] font-bold">
<div class="block-label-container right-top">
<span class="block-label">{{ 'calculated-fields.arguments' | translate }}</span>
</div>
<tb-calculated-field-test-arguments class="size-full" formControlName="arguments"/>
@ -56,7 +56,7 @@
</div>
<div #bottomRightPanel class="test-block-content">
<div class="relative size-full">
<div class="absolute right-10 top-[6px] z-10 text-[12px] font-bold">
<div class="block-label-container right-bottom">
<span class="block-label" translate>common.output</span>
</div>
<tb-json-content
@ -89,13 +89,11 @@
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
@if (data.withApply) {
<button mat-button mat-raised-button color="primary"
type="submit"
(click)="save()"
[disabled]="(isLoading$ | async) || calculatedFieldScriptTestFormGroup.get('expression').invalid || !calculatedFieldScriptTestFormGroup.get('expression').dirty">
{{ 'action.save' | translate }}
</button>
}
<button mat-button mat-raised-button color="primary"
type="submit"
(click)="save()"
[disabled]="(isLoading$ | async) || calculatedFieldScriptTestFormGroup.get('expression').invalid || !calculatedFieldScriptTestFormGroup.get('expression').dirty">
{{ 'action.save' | translate }}
</button>
</div>
</form>

View File

@ -27,6 +27,28 @@
padding-left: 5px;
border: 1px solid #c0c0c0;
}
.block-label-container {
position: absolute;
z-index: 10;
font-size: 12px;
font-weight: bold;
&.left {
right: 112px;
top: 9px;
}
&.right-bottom {
right: 40px;
top: 6px;
}
&.right-top {
right: 8px;
top: 2px;
}
}
}
}

View File

@ -22,7 +22,7 @@ import {
Inject,
ViewChild,
} from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { FormBuilder } from '@angular/forms';
@ -36,8 +36,8 @@ import { ActionNotificationShow } from '@core/notification/notification.actions'
import { beautifyJs } from '@shared/models/beautify.models';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { CalculatedFieldScriptTestDialogData } from '@shared/models/calculated-field.models';
import { filter } from 'rxjs/operators';
import { CalculatedFieldTestScriptInputParams } from '@shared/models/calculated-field.models';
@Component({
selector: 'tb-calculated-field-script-test-dialog',
@ -66,8 +66,9 @@ export class CalculatedFieldScriptTestDialogComponent extends DialogComponent<Ca
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: CalculatedFieldScriptTestDialogData,
@Inject(MAT_DIALOG_DATA) public data: CalculatedFieldTestScriptInputParams,
protected dialogRef: MatDialogRef<CalculatedFieldScriptTestDialogComponent, string>,
private dialog: MatDialog,
private fb: FormBuilder,
private destroyRef: DestroyRef,
private calculatedFieldService: CalculatedFieldsService) {
@ -101,13 +102,13 @@ export class CalculatedFieldScriptTestDialogComponent extends DialogComponent<Ca
}
save(): void {
this.testScript().pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.testScript(true).pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
this.calculatedFieldScriptTestFormGroup.get('expression').markAsPristine();
this.dialogRef.close(this.calculatedFieldScriptTestFormGroup.get('expression').value);
});
}
private testScript(): Observable<string> {
private testScript(onSave = false): Observable<string> {
if (this.checkInputParamErrors()) {
return this.calculatedFieldService.testScript({
expression: this.calculatedFieldScriptTestFormGroup.get('expression').value,
@ -122,6 +123,9 @@ export class CalculatedFieldScriptTestDialogComponent extends DialogComponent<Ca
}));
return NEVER;
} else {
if (onSave) {
this.dialog.closeAll();
}
return of(result.output);
}
}),

View File

@ -127,7 +127,7 @@ export interface CalculatedFieldArgumentValue extends CalculatedFieldArgument {
argumentName: string;
}
export type CalculatedFieldTestScriptFn = (argumentsObj: Record<string, unknown>, expression: string, withApply?: boolean) => Observable<string>;
export type CalculatedFieldTestScriptFn = (calculatedField: CalculatedField, argumentsObj?: Record<string, unknown>) => Observable<string>;
export interface CalculatedFieldDialogData {
value?: CalculatedField;
@ -136,20 +136,15 @@ export interface CalculatedFieldDialogData {
debugLimitsConfiguration: string;
tenantId: string;
entityName?: string;
additionalDebugActionConfig: AdditionalDebugActionConfig;
additionalDebugActionConfig: AdditionalDebugActionConfig<(calculatedField: CalculatedField) => void>;
testScriptFn: CalculatedFieldTestScriptFn;
isDirty?: boolean;
}
export interface CalculatedFieldDebugDialogData {
id?: CalculatedFieldId;
entityId: EntityId;
tenantId: string;
expression?: string;
testScriptFn: CalculatedFieldTestScriptFn;
}
export interface CalculatedFieldScriptTestDialogData extends CalculatedFieldTestScriptInputParams {
withApply: boolean;
value: CalculatedField;
getTestScriptDialogFn: CalculatedFieldTestScriptFn;
}
export interface CalculatedFieldTestScriptInputParams {

View File

@ -203,13 +203,13 @@ export interface EntityDebugSettings {
allEnabledUntil?: number;
}
export interface TestScriptResult {
export interface EntityTestScriptResult {
output: string;
error: string;
}
export interface AdditionalDebugActionConfig {
action?: (id?: EntityId, ...restArguments: unknown[]) => void;
export interface AdditionalDebugActionConfig<Action = (...args: unknown[]) => void> {
action?: Action;
title: string;
}

View File

@ -27,7 +27,7 @@ import { AppState } from '@core/core.state';
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
import { RuleChainType } from '@shared/models/rule-chain.models';
import { DebugRuleNodeEventBody } from '@shared/models/event.models';
import { HasEntityDebugSettings } from '@shared/models/entity.models';
import { EntityTestScriptResult, HasEntityDebugSettings } from '@shared/models/entity.models';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export interface RuleNodeConfiguration {
@ -374,6 +374,8 @@ export interface TestScriptInputParams {
msgType: string;
}
export type TestScriptResult = EntityTestScriptResult;
export enum MessageType {
POST_ATTRIBUTES_REQUEST = 'POST_ATTRIBUTES_REQUEST',
POST_TELEMETRY_REQUEST = 'POST_TELEMETRY_REQUEST',

View File

@ -0,0 +1 @@
<!-- [TODO]: [Calculated Fields] add content -->

View File

@ -0,0 +1 @@
<!-- [TODO]: [Calculated Fields] add content -->