Browse Source

Added debug settings service

pull/12811/head
mpetrov 2 years ago
parent
commit
3d3a8ffc05
  1. 61
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
  2. 11
      ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts
  3. 2
      ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html
  4. 46
      ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts
  5. 68
      ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts
  6. 4
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  7. 11
      ui-ngx/src/app/shared/models/entity.models.ts
  8. 2
      ui-ngx/src/app/shared/shared.module.ts

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

@ -29,10 +29,7 @@ import { AppState } from '@core/core.state';
import { getCurrentAuthState, getCurrentAuthUser } from '@core/auth/auth.selectors'; import { getCurrentAuthState, getCurrentAuthUser } from '@core/auth/auth.selectors';
import { DestroyRef, Renderer2 } from '@angular/core'; import { DestroyRef, Renderer2 } from '@angular/core';
import { EntityDebugSettings } from '@shared/models/entity.models'; import { EntityDebugSettings } from '@shared/models/entity.models';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; 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 { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { catchError, filter, switchMap, tap } from 'rxjs/operators'; import { catchError, filter, switchMap, tap } from 'rxjs/operators';
import { import {
@ -53,6 +50,7 @@ import {
} from './components/public-api'; } from './components/public-api';
import { ImportExportService } from '@shared/import-export/import-export.service'; import { ImportExportService } from '@shared/import-export/import-export.service';
import { isObject } from '@core/utils'; import { isObject } from '@core/utils';
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedField, PageLink> { export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedField, PageLink> {
@ -71,12 +69,11 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
private dialog: MatDialog, private dialog: MatDialog,
public entityId: EntityId = null, public entityId: EntityId = null,
private store: Store<AppState>, private store: Store<AppState>,
private durationLeft: DurationLeftPipe,
private popoverService: TbPopoverService,
private destroyRef: DestroyRef, private destroyRef: DestroyRef,
private renderer: Renderer2, private renderer: Renderer2,
public entityName: string, public entityName: string,
private importExportService: ImportExportService private importExportService: ImportExportService,
private entityDebugSettingsService: EntityDebugSettingsService,
) { ) {
super(); super();
this.tableTitle = this.translate.instant('entity.type-calculated-fields'); this.tableTitle = this.translate.instant('entity.type-calculated-fields');
@ -131,10 +128,10 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
}, },
{ {
name: '', name: '',
nameFunction: entity => this.getDebugConfigLabel(entity?.debugSettings), nameFunction: entity => this.entityDebugSettingsService.getDebugConfigLabel(entity?.debugSettings),
icon: 'mdi:bug', icon: 'mdi:bug',
isEnabled: () => true, isEnabled: () => true,
iconFunction: ({ debugSettings }) => this.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline', iconFunction: ({ debugSettings }) => this.entityDebugSettingsService.isDebugActive(debugSettings?.allEnabledUntil) || debugSettings?.failuresEnabled ? 'mdi:bug' : 'mdi:bug-outline',
onAction: ($event, entity) => this.onOpenDebugConfig($event, entity), onAction: ($event, entity) => this.onOpenDebugConfig($event, entity),
}, },
{ {
@ -160,26 +157,20 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
const trigger = $event.target as Element;
if (this.popoverService.hasPopover(trigger)) { this.entityDebugSettingsService.viewContainerRef = viewContainerRef;
this.popoverService.hidePopover(trigger); this.entityDebugSettingsService.renderer = this.renderer;
} else {
const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer, this.entityDebugSettingsService.openDebugStrategyPanel({
viewContainerRef, EntityDebugSettingsPanelComponent, 'bottom', true, null, debugSettings,
{ debugConfig: {
debugLimitsConfiguration: this.calculatedFieldsDebugPerTenantLimitsConfiguration, debugLimitsConfiguration: this.calculatedFieldsDebugPerTenantLimitsConfiguration,
maxDebugModeDuration: this.maxDebugModeDuration, maxDebugModeDuration: this.maxDebugModeDuration,
entityLabel: this.translate.instant('debug-settings.calculated-field'), entityLabel: this.translate.instant('debug-settings.calculated-field'),
additionalActionConfig, additionalActionConfig,
...debugSettings },
}, onSettingsAppliedFn: settings => this.onDebugConfigChanged(id.id, settings)
{}, }, $event.target as Element);
{}, {}, true);
debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((settings: EntityDebugSettings) => {
this.onDebugConfigChanged(id.id, settings);
debugStrategyPopover.hide();
});
}
} }
private editCalculatedField(calculatedField: CalculatedField, isDirty = false): void { private editCalculatedField(calculatedField: CalculatedField, isDirty = false): void {
@ -239,20 +230,6 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig<CalculatedFie
.subscribe(() => this.updateData()); .subscribe(() => this.updateData());
} }
private getDebugConfigLabel(debugSettings: EntityDebugSettings): string {
const isDebugActive = this.isDebugActive(debugSettings?.allEnabledUntil);
if (!isDebugActive) {
return debugSettings?.failuresEnabled ? this.translate.instant('debug-settings.failures') : this.translate.instant('common.disabled');
} else {
return this.durationLeft.transform(debugSettings?.allEnabledUntil);
}
}
private isDebugActive(allEnabledUntil: number): boolean {
return allEnabledUntil > new Date().getTime();
}
private onDebugConfigChanged(id: string, debugSettings: EntityDebugSettings): void { private onDebugConfigChanged(id: string, debugSettings: EntityDebugSettings): void {
this.calculatedFieldsService.getCalculatedFieldById(id).pipe( this.calculatedFieldsService.getCalculatedFieldById(id).pipe(
switchMap(field => this.calculatedFieldsService.saveCalculatedField({ ...field, debugSettings })), switchMap(field => this.calculatedFieldsService.saveCalculatedField({ ...field, debugSettings })),

11
ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table.component.ts

@ -31,10 +31,9 @@ import { MatDialog } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { CalculatedFieldsTableConfig } from '@home/components/calculated-fields/calculated-fields-table-config'; import { CalculatedFieldsTableConfig } from '@home/components/calculated-fields/calculated-fields-table-config';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
import { TbPopoverService } from '@shared/components/popover.service';
import { CalculatedFieldsService } from '@core/http/calculated-fields.service'; import { CalculatedFieldsService } from '@core/http/calculated-fields.service';
import { ImportExportService } from '@shared/import-export/import-export.service'; import { ImportExportService } from '@shared/import-export/import-export.service';
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
@Component({ @Component({
selector: 'tb-calculated-fields-table', selector: 'tb-calculated-fields-table',
@ -56,11 +55,10 @@ export class CalculatedFieldsTableComponent {
private translate: TranslateService, private translate: TranslateService,
private dialog: MatDialog, private dialog: MatDialog,
private store: Store<AppState>, private store: Store<AppState>,
private durationLeft: DurationLeftPipe,
private popoverService: TbPopoverService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private renderer: Renderer2, private renderer: Renderer2,
private importExportService: ImportExportService, private importExportService: ImportExportService,
private entityDebugSettingsService: EntityDebugSettingsService,
private destroyRef: DestroyRef) { private destroyRef: DestroyRef) {
effect(() => { effect(() => {
@ -71,12 +69,11 @@ export class CalculatedFieldsTableComponent {
this.dialog, this.dialog,
this.entityId(), this.entityId(),
this.store, this.store,
this.durationLeft,
this.popoverService,
this.destroyRef, this.destroyRef,
this.renderer, this.renderer,
this.entityName(), this.entityName(),
this.importExportService this.importExportService,
this.entityDebugSettingsService,
); );
this.cd.markForCheck(); this.cd.markForCheck();
} }

2
ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.html

@ -21,7 +21,7 @@
#matButton #matButton
[class.active]="((isDebugAllActive$ | async) || failuresEnabled) && !disabled" [class.active]="((isDebugAllActive$ | async) || failuresEnabled) && !disabled"
[disabled]="disabled" [disabled]="disabled"
(click)="openDebugStrategyPanel($event, matButton)"> (click)="onOpenDebugStrategyPanel($event, matButton)">
<mat-icon [color]="debugSettingsFormGroup.disabled ? 'inherit' : 'primary'">bug_report</mat-icon> <mat-icon [color]="debugSettingsFormGroup.disabled ? 'inherit' : 'primary'">bug_report</mat-icon>
@if (isDebugAllActive$ | async) { @if (isDebugAllActive$ | async) {
{{ (allEnabled$ | async) === false ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }} {{ (allEnabled$ | async) === false ? (allEnabledUntil | durationLeft) : (maxDebugModeDuration | milliSecondsToTimeString: true : true) }}

46
ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings-button.component.ts

@ -20,15 +20,11 @@ import {
Component, Component,
forwardRef, forwardRef,
Input, Input,
Renderer2,
ViewContainerRef
} from '@angular/core'; } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module'; import { SharedModule } from '@shared/shared.module';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
import { TbPopoverService } from '@shared/components/popover.service';
import { MatButton } from '@angular/material/button'; import { MatButton } from '@angular/material/button';
import { EntityDebugSettingsPanelComponent } from './entity-debug-settings-panel.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { BehaviorSubject, of, shareReplay, timer } from 'rxjs'; import { BehaviorSubject, of, shareReplay, timer } from 'rxjs';
import { SECOND, MINUTE } from '@shared/models/time/time.models'; import { SECOND, MINUTE } from '@shared/models/time/time.models';
@ -38,6 +34,7 @@ import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms';
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
@Component({ @Component({
selector: 'tb-entity-debug-settings-button', selector: 'tb-entity-debug-settings-button',
@ -54,6 +51,7 @@ import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/f
useExisting: forwardRef(() => EntityDebugSettingsButtonComponent), useExisting: forwardRef(() => EntityDebugSettingsButtonComponent),
multi: true multi: true
}, },
EntityDebugSettingsService
], ],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
@ -92,11 +90,9 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor
private propagateChange: (settings: EntityDebugSettings) => void = () => {}; private propagateChange: (settings: EntityDebugSettings) => void = () => {};
constructor(private popoverService: TbPopoverService, constructor(private store: Store<AppState>,
private renderer: Renderer2,
private store: Store<AppState>,
private viewContainerRef: ViewContainerRef,
private fb: FormBuilder, private fb: FormBuilder,
private entityDebugSettingsService: EntityDebugSettingsService,
private cd : ChangeDetectorRef, private cd : ChangeDetectorRef,
) { ) {
this.debugSettingsFormGroup.valueChanges.pipe( this.debugSettingsFormGroup.valueChanges.pipe(
@ -118,33 +114,23 @@ export class EntityDebugSettingsButtonComponent implements ControlValueAccessor
return this.debugSettingsFormGroup.get('allEnabledUntil').value; return this.debugSettingsFormGroup.get('allEnabledUntil').value;
} }
openDebugStrategyPanel($event: Event, matButton: MatButton): void { onOpenDebugStrategyPanel($event: Event, matButton: MatButton): void {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
const trigger = matButton._elementRef.nativeElement; this.entityDebugSettingsService.openDebugStrategyPanel({
const debugSettings = this.debugSettingsFormGroup.value; debugSettings: this.debugSettingsFormGroup.value,
debugConfig: {
if (this.popoverService.hasPopover(trigger)) { maxDebugModeDuration: this.maxDebugModeDuration,
this.popoverService.hidePopover(trigger); debugLimitsConfiguration: this.debugLimitsConfiguration,
} else { entityLabel: this.entityLabel,
const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer, additionalActionConfig: this.additionalActionConfig,
this.viewContainerRef, EntityDebugSettingsPanelComponent, 'bottom', true, null, },
{ onSettingsAppliedFn: settings => {
...debugSettings,
maxDebugModeDuration: this.maxDebugModeDuration,
debugLimitsConfiguration: this.debugLimitsConfiguration,
entityLabel: this.entityLabel,
additionalActionConfig: this.additionalActionConfig,
},
{},
{}, {}, true);
debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: EntityDebugSettings) => {
this.debugSettingsFormGroup.patchValue(settings); this.debugSettingsFormGroup.patchValue(settings);
this.cd.markForCheck(); this.cd.markForCheck();
debugStrategyPopover.hide(); }
}); }, matButton._elementRef.nativeElement);
}
} }
registerOnChange(fn: (settings: EntityDebugSettings) => void): void { registerOnChange(fn: (settings: EntityDebugSettings) => void): void {

68
ui-ngx/src/app/modules/home/components/entity/debug/entity-debug-settings.service.ts

@ -0,0 +1,68 @@
///
/// 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 { Injectable, Optional, Renderer2, ViewContainerRef } from '@angular/core';
import { EntityDebugSettingsPanelComponent } from '@home/components/entity/debug/entity-debug-settings-panel.component';
import { EntityDebugSettingPanelConfig, EntityDebugSettings } from '@shared/models/entity.models';
import { TbPopoverService } from '@shared/components/popover.service';
import { TranslateService } from '@ngx-translate/core';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
@Injectable()
export class EntityDebugSettingsService {
constructor(
private popoverService: TbPopoverService,
@Optional() public renderer: Renderer2,
@Optional() public viewContainerRef: ViewContainerRef,
private translate: TranslateService,
private durationLeft: DurationLeftPipe,
) {}
openDebugStrategyPanel(panelConfig: EntityDebugSettingPanelConfig, trigger: Element): void {
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, EntityDebugSettingsPanelComponent, 'bottom', true, null,
{
...panelConfig.debugSettings,
...panelConfig.debugConfig,
},
{},
{}, {}, true);
debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe(settings => {
panelConfig.onSettingsAppliedFn(settings);
debugStrategyPopover.hide();
});
}
}
getDebugConfigLabel(debugSettings: EntityDebugSettings): string {
const isDebugActive = this.isDebugActive(debugSettings?.allEnabledUntil);
if (!isDebugActive) {
return debugSettings?.failuresEnabled ? this.translate.instant('debug-settings.failures') : this.translate.instant('common.disabled');
} else {
return this.durationLeft.transform(debugSettings?.allEnabledUntil);
}
}
isDebugActive(allEnabledUntil: number): boolean {
return allEnabledUntil > new Date().getTime();
}
}

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

@ -184,7 +184,6 @@ import {
import { EntityChipsComponent } from '@home/components/entity/entity-chips.component'; import { EntityChipsComponent } from '@home/components/entity/entity-chips.component';
import { DashboardViewComponent } from '@home/components/dashboard-view/dashboard-view.component'; import { DashboardViewComponent } from '@home/components/dashboard-view/dashboard-view.component';
import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component'; import { CalculatedFieldsTableComponent } from '@home/components/calculated-fields/calculated-fields-table.component';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
import { CalculatedFieldDialogComponent } from '@home/components/calculated-fields/components/dialog/calculated-field-dialog.component'; import { CalculatedFieldDialogComponent } from '@home/components/calculated-fields/components/dialog/calculated-field-dialog.component';
import { import {
EntityDebugSettingsButtonComponent EntityDebugSettingsButtonComponent
@ -204,6 +203,7 @@ import {
import { import {
CalculatedFieldTestArgumentsComponent CalculatedFieldTestArgumentsComponent
} from '@home/components/calculated-fields/components/test-arguments/calculated-field-test-arguments.component'; } from '@home/components/calculated-fields/components/test-arguments/calculated-field-test-arguments.component';
import { EntityDebugSettingsService } from '@home/components/entity/debug/entity-debug-settings.service';
@NgModule({ @NgModule({
declarations: declarations:
@ -504,7 +504,7 @@ import {
providers: [ providers: [
WidgetComponentService, WidgetComponentService,
CustomDialogService, CustomDialogService,
DurationLeftPipe, EntityDebugSettingsService,
{provide: EMBED_DASHBOARD_DIALOG_TOKEN, useValue: EmbedDashboardDialogComponent}, {provide: EMBED_DASHBOARD_DIALOG_TOKEN, useValue: EmbedDashboardDialogComponent},
{provide: COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN, useValue: ComplexFilterPredicateDialogComponent}, {provide: COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN, useValue: ComplexFilterPredicateDialogComponent},
{provide: DASHBOARD_PAGE_COMPONENT_TOKEN, useValue: DashboardPageComponent}, {provide: DASHBOARD_PAGE_COMPONENT_TOKEN, useValue: DashboardPageComponent},

11
ui-ngx/src/app/shared/models/entity.models.ts

@ -203,6 +203,17 @@ export interface EntityDebugSettings {
allEnabledUntil?: number; allEnabledUntil?: number;
} }
export interface EntityDebugSettingPanelConfig {
debugSettings: EntityDebugSettings;
debugConfig: {
maxDebugModeDuration: number;
debugLimitsConfiguration: string;
entityLabel?: string;
additionalActionConfig?: AdditionalDebugActionConfig;
}
onSettingsAppliedFn: (settings: EntityDebugSettings) => void;
}
export interface EntityTestScriptResult { export interface EntityTestScriptResult {
output: string; output: string;
error: string; error: string;

2
ui-ngx/src/app/shared/shared.module.ts

@ -225,6 +225,7 @@ import { GroupingIntervalOptionsComponent } from '@shared/components/time/aggreg
import { JsFuncModulesComponent } from '@shared/components/js-func-modules.component'; import { JsFuncModulesComponent } from '@shared/components/js-func-modules.component';
import { JsFuncModuleRowComponent } from '@shared/components/js-func-module-row.component'; import { JsFuncModuleRowComponent } from '@shared/components/js-func-module-row.component';
import { EntityKeyAutocompleteComponent } from '@shared/components/entity/entity-key-autocomplete.component'; import { EntityKeyAutocompleteComponent } from '@shared/components/entity/entity-key-autocomplete.component';
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
return markedOptionsService; return markedOptionsService;
@ -244,6 +245,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
ShortNumberPipe, ShortNumberPipe,
ImagePipe, ImagePipe,
CustomTranslatePipe, CustomTranslatePipe,
DurationLeftPipe,
{ {
provide: FlowInjectionToken, provide: FlowInjectionToken,
useValue: Flow useValue: Flow

Loading…
Cancel
Save