Browse Source

Merge pull request #14513 from mtsymbarov-del/feat/debug-events-modals

Added debug events modal to rule chain elements
pull/14543/head
Vladyslav Prykhodko 9 months ago
committed by GitHub
parent
commit
e3ff5eb8a5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 16
      ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts
  2. 29
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
  3. 77
      ui-ngx/src/app/modules/home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component.ts
  4. 1
      ui-ngx/src/app/modules/home/components/calculated-fields/components/public-api.ts
  5. 9
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  6. 15
      ui-ngx/src/app/modules/home/dialogs/events-dialog.component.html
  7. 0
      ui-ngx/src/app/modules/home/dialogs/events-dialog.component.scss
  8. 72
      ui-ngx/src/app/modules/home/dialogs/events-dialog.component.ts
  9. 1
      ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html
  10. 4
      ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts
  11. 1
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html
  12. 37
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts

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

@ -55,14 +55,12 @@ import {
AlarmRuleDialogComponent,
AlarmRuleDialogData
} from "@home/components/alarm-rules/alarm-rule-dialog.component";
import {
CalculatedFieldDebugDialogComponent,
CalculatedFieldDebugDialogData
} 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, isObject } from "@core/utils";
import { AlarmRuleTableHeaderComponent } from "@home/components/alarm-rules/alarm-rule-table-header.component";
import { EventsDialogComponent, EventsDialogData } from '@home/dialogs/events-dialog.component';
import { DebugEventType, Event as DebugEvent, EventType } from '@shared/models/event.models';
import { ActionNotificationShow } from "@core/notification/notification.actions";
import {
CalculatedFieldScriptTestDialogComponent,
@ -266,13 +264,17 @@ export class AlarmRulesTableConfig extends EntityTableConfig<any> {
}
private openDebugEventsDialog(calculatedField: CalculatedField): void {
this.dialog.open<CalculatedFieldDebugDialogComponent, CalculatedFieldDebugDialogData, null>(CalculatedFieldDebugDialogComponent, {
this.dialog.open<EventsDialogComponent, EventsDialogData, null>(EventsDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
title: 'alarm-rule.debugging',
tenantId: this.tenantId,
value: calculatedField,
getTestScriptDialogFn: null,
entityId: calculatedField.id,
debugEventTypes:[DebugEventType.DEBUG_CALCULATED_FIELD],
disabledEventTypes:[EventType.LC_EVENT, EventType.ERROR, EventType.STATS],
defaultEventType: DebugEventType.DEBUG_CALCULATED_FIELD,
debugActionEnabledFn: () => false
}
})
.afterClosed()

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

@ -22,7 +22,7 @@ import {
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { TranslateService } from '@ngx-translate/core';
import { Direction } from '@shared/models/page/sort-order';
import { MatDialog } from '@angular/material/dialog';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { PageLink } from '@shared/models/page/page-link';
import { EMPTY, Observable, of } from 'rxjs';
import { PageData } from '@shared/models/page/page-data';
@ -48,8 +48,6 @@ import {
PropagationWithExpression,
} from '@shared/models/calculated-field.models';
import {
CalculatedFieldDebugDialogComponent,
CalculatedFieldDebugDialogData,
CalculatedFieldDialogComponent,
CalculatedFieldDialogData,
CalculatedFieldScriptTestDialogComponent,
@ -61,6 +59,8 @@ import { EntityDebugSettingsService } from '@home/components/entity/debug/entity
import { DatePipe } from '@angular/common';
import { UtilsService } from "@core/services/utils.service";
import { ActionNotificationShow } from "@core/notification/notification.actions";
import { CalculatedFieldEventBody, DebugEventType, Event as DebugEvent, EventType } from '@shared/models/event.models';
import { EventsDialogComponent, EventsDialogData } from '@home/dialogs/events-dialog.component';
export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedField> {
@ -233,13 +233,30 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
}
private openDebugEventsDialog(calculatedField: CalculatedField): void {
this.dialog.open<CalculatedFieldDebugDialogComponent, CalculatedFieldDebugDialogData, null>(CalculatedFieldDebugDialogComponent, {
const debugActionEnabledFn = (event: DebugEvent) => {
return (calculatedField.type === CalculatedFieldType.SCRIPT ||
(calculatedField.type === CalculatedFieldType.PROPAGATION &&
calculatedField.configuration.applyExpressionToResolvedArguments)
) && !!(event as DebugEvent).body.arguments;
};
const onDebugEventSelected = (event: CalculatedFieldEventBody, dialogRef: MatDialogRef<EventsDialogComponent, string>) => {
this.getTestScriptDialog(calculatedField, JSON.parse(event.arguments))
.subscribe(expression => dialogRef.close(expression));
};
this.dialog.open<EventsDialogComponent, EventsDialogData, null>(EventsDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
title: 'calculated-fields.debugging',
tenantId: this.tenantId,
value: calculatedField,
getTestScriptDialogFn: this.getTestScriptDialog.bind(this),
entityId: calculatedField.id,
debugEventTypes:[DebugEventType.DEBUG_CALCULATED_FIELD],
disabledEventTypes:[EventType.LC_EVENT, EventType.ERROR, EventType.STATS],
defaultEventType: DebugEventType.DEBUG_CALCULATED_FIELD,
onDebugEventSelected,
debugActionEnabledFn
}
})
.afterClosed()

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

@ -1,77 +0,0 @@
///
/// Copyright © 2016-2025 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.
///
import { AfterViewInit, Component, Inject, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component';
import { CalculatedFieldEventBody, DebugEventType, Event, EventType } from '@shared/models/event.models';
import { EventTableComponent } from '@home/components/event/event-table.component';
import {
CalculatedField,
CalculatedFieldTestScriptFn,
CalculatedFieldType
} from '@shared/models/calculated-field.models';
export interface CalculatedFieldDebugDialogData {
tenantId: string;
value: CalculatedField;
getTestScriptDialogFn: CalculatedFieldTestScriptFn;
}
@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, string> implements AfterViewInit {
@ViewChild(EventTableComponent, {static: true}) eventsTable: EventTableComponent;
readonly DebugEventType = DebugEventType;
readonly debugEventTypes = DebugEventType;
readonly EventType = EventType;
dialogTitle: string;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: CalculatedFieldDebugDialogData,
protected dialogRef: MatDialogRef<CalculatedFieldDebugDialogComponent, string>) {
super(store, router, dialogRef);
this.dialogTitle = this.data.value.type === CalculatedFieldType.ALARM ? 'alarm-rule.debugging' : 'calculated-fields.debugging';
}
ngAfterViewInit(): void {
this.eventsTable.entitiesTable.cellActionDescriptors[0].isEnabled = (event => {
return (this.data.value.type === CalculatedFieldType.SCRIPT ||
(this.data.value.type === CalculatedFieldType.PROPAGATION && this.data.value.configuration.applyExpressionToResolvedArguments)
) && !!(event as Event).body.arguments
});
this.eventsTable.entitiesTable.updateData();
}
cancel(): void {
this.dialogRef.close(null);
}
onDebugEventSelected(event: CalculatedFieldEventBody): void {
this.data.getTestScriptDialogFn(this.data.value, JSON.parse(event.arguments))
.subscribe(expression => this.dialogRef.close(expression));
}
}

1
ui-ngx/src/app/modules/home/components/calculated-fields/components/public-api.ts

@ -15,5 +15,4 @@
///
export * from './dialog/calculated-field-dialog.component';
export * from './debug-dialog/calculated-field-debug-dialog.component';
export * from './test-dialog/calculated-field-script-test-dialog.component';

9
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -192,9 +192,6 @@ import { AIModelDialogComponent } from '@home/components/ai-model/ai-model-dialo
import { ResourcesDialogComponent } from "@home/components/resources/resources-dialog.component";
import { ResourcesLibraryComponent } from "@home/components/resources/resources-library.component";
import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component';
import {
CalculatedFieldDebugDialogComponent
} from '@home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component';
import { CalculatedFieldsModule } from '@home/components/calculated-fields/calculated-field.module';
import { AlarmRuleModule } from "@home/components/alarm-rules/alarm-rule.module";
import { AlarmRulesTableComponent } from "@home/components/alarm-rules/alarm-rules-table.component";
@ -204,6 +201,7 @@ import { EditApiKeyDescriptionPanelComponent } from '@home/components/api-key/ed
import { ApiKeyGeneratedDialogComponent } from '@home/components/api-key/api-key-generated-dialog.component';
import { ApiKeysTableDialogComponent } from '@home/components/api-key/api-keys-table-dialog.component';
import { AuditLogFilterComponent } from "@home/components/audit-log/audit-log-filter.component";
import { EventsDialogComponent } from '@home/dialogs/events-dialog.component';
@NgModule({
declarations:
@ -217,7 +215,6 @@ import { AuditLogFilterComponent } from "@home/components/audit-log/audit-log-fi
AuditLogTableComponent,
AuditLogDetailsDialogComponent,
CalculatedFieldsTableComponent,
CalculatedFieldDebugDialogComponent,
AlarmRulesTableComponent,
EventContentDialogComponent,
EventTableHeaderComponent,
@ -361,7 +358,8 @@ import { AuditLogFilterComponent } from "@home/components/audit-log/audit-log-fi
EditApiKeyDescriptionPanelComponent,
ApiKeyGeneratedDialogComponent,
AuditLogHeaderComponent,
AuditLogFilterComponent
AuditLogFilterComponent,
EventsDialogComponent
],
imports: [
CommonModule,
@ -510,6 +508,7 @@ import { AuditLogFilterComponent } from "@home/components/audit-log/audit-log-fi
ResourcesLibraryComponent,
ApiKeysTableComponent,
ApiKeysTableDialogComponent,
EventsDialogComponent
],
providers: [
WidgetComponentService,

15
ui-ngx/src/app/modules/home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component.html → ui-ngx/src/app/modules/home/dialogs/events-dialog.component.html

@ -17,7 +17,7 @@
-->
<div class="debug-dialog-container flex flex-col xs:h-full">
<mat-toolbar color="primary">
<h2>{{ dialogTitle | translate}}</h2>
<h2>{{ data.title | translate}}</h2>
<span class="flex-1"></span>
<button mat-icon-button
(click)="cancel()"
@ -27,13 +27,14 @@
</mat-toolbar>
<div mat-dialog-content class="tb-form-panel stroked debug-dialog-content xs:flex-1">
<tb-event-table
[tenantId]="data.tenantId"
[debugEventTypes]="[debugEventTypes.DEBUG_CALCULATED_FIELD]"
[disabledEventTypes]="[EventType.LC_EVENT, EventType.ERROR, EventType.STATS]"
[defaultEventType]="DebugEventType.DEBUG_CALCULATED_FIELD"
[debugEventTypes]="data.debugEventTypes"
[defaultEventType]="data.defaultEventType"
[disabledEventTypes]="data.disabledEventTypes ?? []"
[active]="true"
[entityId]="data.value.id"
(debugEventSelected)="onDebugEventSelected($event)"
[tenantId]="data.tenantId"
[entityId]="data.entityId"
[functionTestButtonLabel]="data.functionTestButtonLabel ?? ''"
(debugEventSelected)="this.onDebugEventSelected($event)"
/>
</div>
<div mat-dialog-actions class="justify-end">

0
ui-ngx/src/app/modules/home/components/calculated-fields/components/debug-dialog/calculated-field-debug-dialog.component.scss → ui-ngx/src/app/modules/home/dialogs/events-dialog.component.scss

72
ui-ngx/src/app/modules/home/dialogs/events-dialog.component.ts

@ -0,0 +1,72 @@
///
/// Copyright © 2016-2025 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.
///
import { AfterViewInit, Component, Inject, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DialogComponent } from '@shared/components/dialog.component';
import { EventTableComponent } from '@home/components/event/event-table.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router';
import { DebugEventType, EventType } from '@shared/models/event.models';
import { EntityId } from '@shared/models/id/entity-id';
import { BaseData, HasId } from '@shared/models/base-data';
export interface EventsDialogData {
title: string;
tenantId: string;
entityId: EntityId;
debugEventTypes: Array<DebugEventType>;
defaultEventType: DebugEventType;
disabledEventTypes?: Array<EventType | DebugEventType>;
functionTestButtonLabel?: string;
onDebugEventSelected?: (event: any, dialogRef: MatDialogRef<EventsDialogComponent, string>) => void;
debugActionEnabledFn?: (event: BaseData<HasId>) => boolean;
}
@Component({
selector: 'tb-debug-dialog',
templateUrl: './events-dialog.component.html',
styleUrl: './events-dialog.component.scss'
})
export class EventsDialogComponent extends DialogComponent<EventsDialogComponent, string> implements AfterViewInit{
@ViewChild(EventTableComponent, {static: true}) eventsTable: EventTableComponent;
constructor(protected store: Store<AppState>,
protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: EventsDialogData,
protected dialogRef: MatDialogRef<EventsDialogComponent, string>) {
super(store, router, dialogRef);
}
ngAfterViewInit() {
if (this.data.debugActionEnabledFn && this.eventsTable.entitiesTable.cellActionDescriptors?.length > 0) {
this.eventsTable.entitiesTable.cellActionDescriptors[0].isEnabled = this.data.debugActionEnabledFn;
}
this.eventsTable.entitiesTable.updateData();
}
cancel(): void {
this.dialogRef.close(null);
}
onDebugEventSelected(event: any): void {
if(this.data.onDebugEventSelected) {
this.data.onDebugEventSelected(event, this.dialogRef);
}
}
}

1
ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html

@ -36,6 +36,7 @@
</mat-form-field>
<section class="mb-5 flex max-w-xs flex-row">
<tb-entity-debug-settings-button
[additionalActionConfig]="additionalActionConfig"
class="mr-2"
formControlName="debugSettings"
[entityType]="entityType.RULE_NODE"

4
ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts

@ -37,6 +37,7 @@ import { ComponentClusteringMode } from '@shared/models/component-descriptor.mod
import { coerceBoolean } from '@shared/decorators/coercion';
import { ServiceType } from '@shared/models/queue.models';
import { takeUntil } from 'rxjs/operators';
import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model';
@Component({
selector: 'tb-rule-node',
@ -64,6 +65,9 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
@coerceBoolean()
isAdd = false;
@Input()
additionalActionConfig: AdditionalDebugActionConfig;
@Output()
initRuleNode = new EventEmitter<void>();

1
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html

@ -106,6 +106,7 @@
<mat-tab-group mat-stretch-tabs="false" class="tb-absolute-fill tb-rulenode-details flex-1" [(selectedIndex)]="selectedRuleNodeTabIndex">
<mat-tab label="{{ 'rulenode.details' | translate }}">
<tb-rule-node #tbRuleNode
[additionalActionConfig]="additionalActionConfig"
[ruleNode]="editingRuleNode"
[ruleChainId]="ruleChain.id?.id"
[ruleChainType]="ruleChainType"

37
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts

@ -99,6 +99,8 @@ import { TbContextMenuEvent } from '@shared/models/jquery-event.models';
import { EntityDebugSettings } from '@shared/models/entity.models';
import Timeout = NodeJS.Timeout;
import { DomSanitizer } from '@angular/platform-browser';
import { AdditionalDebugActionConfig } from '@home/components/entity/debug/entity-debug-settings.model';
import { EventsDialogComponent } from '@home/dialogs/events-dialog.component';
@Component({
selector: 'tb-rulechain-page',
@ -1366,7 +1368,7 @@ export class RuleChainPageComponent extends PageComponent
details = this.sanitizer.sanitize(SecurityContext.HTML, node.additionalInfo.description);
}
}
name = this.sanitizer.sanitize(SecurityContext.HTML, name);
desc = this.sanitizer.sanitize(SecurityContext.HTML, desc);
@ -1656,6 +1658,39 @@ export class RuleChainPageComponent extends PageComponent
}
}
get additionalActionConfig (): AdditionalDebugActionConfig {
return {
title: this.translate.instant('rulenode.rule-node-events'),
action: this.switchToEventsTab.bind(this)
}
}
private switchToEventsTab() {
if(!this.ruleNodeComponent.ruleNodeFormGroup.dirty) {
this.selectedRuleNodeTabIndex = 1;
} else {
this.openDebugEventsDialog();
}
}
private openDebugEventsDialog(): void {
this.dialog.open<EventsDialogComponent>(EventsDialogComponent, {
disableClose: true,
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
data: {
title: 'rulenode.events',
debugEventTypes: [DebugEventType.DEBUG_RULE_NODE],
defaultEventType: DebugEventType.DEBUG_RULE_NODE,
tenantId: this.ruleChain.tenantId.id,
entityId: this.editingRuleNode.ruleNodeId,
functionTestButtonLabel: this.ruleNodeTestButtonLabel,
onDebugEventSelected: this.onDebugEventSelected.bind(this)
}
})
.afterClosed()
.subscribe();
}
private updateNodeErrorTooltip(node: FcRuleNode) {
if (node.error) {
const element = $('#' + node.id);

Loading…
Cancel
Save