Debug settings changed approach to ControlValueAccessor and refactoring
This commit is contained in:
parent
04074b8baa
commit
02016487c7
@ -1,46 +0,0 @@
|
|||||||
<!--
|
|
||||||
|
|
||||||
Copyright © 2016-2024 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<button mat-stroked-button
|
|
||||||
*ngIf="!minifyMode else minifiedButton"
|
|
||||||
class="tb-rounded-btn flex-1 w-36"
|
|
||||||
color="primary"
|
|
||||||
#matButton
|
|
||||||
[class.active]="((isDebugAllActive$ | async) || debugFailures) && !disabled"
|
|
||||||
[disabled]="disabled"
|
|
||||||
(click)="openDebugStrategyPanel($event, matButton)">
|
|
||||||
<mat-icon [color]="disabled ? 'inherit' : 'primary'">bug_report</mat-icon>
|
|
||||||
<span *ngIf="!debugFailures && !(isDebugAllActive$ | async)" translate>common.disabled</span>
|
|
||||||
<span *ngIf="(isDebugAllActive$ | async) && debugFailures" translate>debug-config.all</span>
|
|
||||||
<span *ngIf="(isDebugAllActive$ | async) && !debugFailures">
|
|
||||||
{{ !debugAll ? (debugAllUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) }}
|
|
||||||
</span>
|
|
||||||
<span *ngIf="!(isDebugAllActive$ | async) && debugFailures" translate>debug-config.failures</span>
|
|
||||||
</button>
|
|
||||||
<ng-template #minifiedButton>
|
|
||||||
<button mat-icon-button
|
|
||||||
class="mini-debug-btn"
|
|
||||||
[disabled]="disabled"
|
|
||||||
#matButton
|
|
||||||
[matTooltip]="(isDebugAllActive$ | async) ? (debugAllUntil | durationLeft) : null"
|
|
||||||
matTooltipPosition="above"
|
|
||||||
(click)="openDebugStrategyPanel($event, matButton)">
|
|
||||||
<mat-icon class="material-icons" [color]="((isDebugAllActive$ | async) || debugFailures) && !disabled ? 'primary' : 'inherit'">
|
|
||||||
bug_report
|
|
||||||
</mat-icon>
|
|
||||||
</button>
|
|
||||||
</ng-template>
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright © 2016-2024 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.
|
|
||||||
*/
|
|
||||||
:host {
|
|
||||||
.mini-debug-btn {
|
|
||||||
color: rgba(0, 0, 0, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016-2024 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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<button mat-stroked-button
|
||||||
|
class="tb-rounded-btn flex-1 w-36"
|
||||||
|
color="primary"
|
||||||
|
#matButton
|
||||||
|
[class.active]="((isDebugAllActive$ | async) || failuresEnabled) && !disabled"
|
||||||
|
[disabled]="disabled"
|
||||||
|
(click)="openDebugStrategyPanel($event, matButton)">
|
||||||
|
<mat-icon [color]="debugSettingsFormGroup.disabled ? 'inherit' : 'primary'">bug_report</mat-icon>
|
||||||
|
@if (failuresEnabled) {
|
||||||
|
@if (isDebugAllActive$ | async) {
|
||||||
|
{{ 'debug-config.all' | translate }}
|
||||||
|
} @else {
|
||||||
|
{{ 'debug-config.failures' | translate }}
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
@if (isDebugAllActive$ | async) {
|
||||||
|
{{ !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) }}
|
||||||
|
} @else {
|
||||||
|
{{ 'common.disabled' | translate }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</button>
|
||||||
@ -21,82 +21,128 @@ import {
|
|||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
DestroyRef,
|
DestroyRef,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
EventEmitter,
|
forwardRef
|
||||||
Output
|
|
||||||
} 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 { TbPopoverService } from '@shared/components/popover.service';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { DebugConfigPanelComponent } from './debug-config-panel.component';
|
import { DebugSettingsPanelComponent } from './debug-settings-panel.component';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { shareReplay, timer } from 'rxjs';
|
import { shareReplay, timer } from 'rxjs';
|
||||||
import { SECOND } from '@shared/models/time/time.models';
|
import { SECOND } from '@shared/models/time/time.models';
|
||||||
import { HasDebugConfig } from '@shared/models/entity.models';
|
import { DebugSettings } from '@shared/models/entity.models';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { getCurrentAuthState } from '@core/auth/auth.selectors';
|
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,
|
||||||
|
UntypedFormGroup,
|
||||||
|
} from '@angular/forms';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-debug-config-button',
|
selector: 'tb-debug-settings-button',
|
||||||
templateUrl: './debug-config-button.component.html',
|
templateUrl: './debug-settings-button.component.html',
|
||||||
styleUrls: ['./debug-config-button.component.scss'],
|
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
DurationLeftPipe,
|
DurationLeftPipe,
|
||||||
],
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => DebugSettingsButtonComponent),
|
||||||
|
multi: true
|
||||||
|
},
|
||||||
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class DebugConfigButtonComponent {
|
export class DebugSettingsButtonComponent implements ControlValueAccessor {
|
||||||
|
|
||||||
@Input() debugFailures = false;
|
|
||||||
@Input() debugAll = false;
|
|
||||||
@Input() debugAllUntil = 0;
|
|
||||||
@Input() disabled = false;
|
|
||||||
@Input() minifyMode = false;
|
|
||||||
@Input() debugLimitsConfiguration: string;
|
@Input() debugLimitsConfiguration: string;
|
||||||
|
|
||||||
@Output() onDebugConfigChanged = new EventEmitter<HasDebugConfig>();
|
debugSettingsFormGroup: UntypedFormGroup;
|
||||||
|
disabled = false;
|
||||||
isDebugAllActive$ = timer(0, SECOND).pipe(map(() => this.debugAllUntil > new Date().getTime() || this.debugAll), shareReplay(1));
|
isDebugAllActive$ = timer(0, SECOND).pipe(map(() => this.allEnabledUntil > new Date().getTime() || this.allEnabled), shareReplay(1));
|
||||||
|
|
||||||
readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes;
|
readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes;
|
||||||
|
|
||||||
|
private onChange: (settings: DebugSettings) => void;
|
||||||
|
|
||||||
constructor(private popoverService: TbPopoverService,
|
constructor(private popoverService: TbPopoverService,
|
||||||
private renderer: Renderer2,
|
private renderer: Renderer2,
|
||||||
private store: Store<AppState>,
|
private store: Store<AppState>,
|
||||||
private viewContainerRef: ViewContainerRef,
|
private viewContainerRef: ViewContainerRef,
|
||||||
private destroyRef: DestroyRef,
|
private destroyRef: DestroyRef,
|
||||||
) {}
|
private fb: FormBuilder,
|
||||||
|
) {
|
||||||
|
this.debugSettingsFormGroup = this.fb.group({
|
||||||
|
failuresEnabled: [false],
|
||||||
|
allEnabled: [false],
|
||||||
|
allEnabledUntil: []
|
||||||
|
});
|
||||||
|
|
||||||
|
this.debugSettingsFormGroup.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||||
|
this.onChange(value);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get failuresEnabled(): boolean {
|
||||||
|
return this.debugSettingsFormGroup.get('failuresEnabled').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get allEnabled(): boolean {
|
||||||
|
return this.debugSettingsFormGroup.get('allEnabled').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get allEnabledUntil(): number {
|
||||||
|
return this.debugSettingsFormGroup.get('allEnabledUntil').value;
|
||||||
|
}
|
||||||
|
|
||||||
openDebugStrategyPanel($event: Event, matButton: MatButton): void {
|
openDebugStrategyPanel($event: Event, matButton: MatButton): void {
|
||||||
if ($event) {
|
if ($event) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
}
|
}
|
||||||
const trigger = matButton._elementRef.nativeElement;
|
const trigger = matButton._elementRef.nativeElement;
|
||||||
|
const debugSettings = this.debugSettingsFormGroup.value;
|
||||||
|
|
||||||
if (this.popoverService.hasPopover(trigger)) {
|
if (this.popoverService.hasPopover(trigger)) {
|
||||||
this.popoverService.hidePopover(trigger);
|
this.popoverService.hidePopover(trigger);
|
||||||
} else {
|
} else {
|
||||||
const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer,
|
||||||
this.viewContainerRef, DebugConfigPanelComponent, 'bottom', true, null,
|
this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null,
|
||||||
{
|
{
|
||||||
debugFailures: this.debugFailures,
|
...debugSettings,
|
||||||
debugAll: this.debugAll,
|
|
||||||
debugAllUntil: this.debugAllUntil,
|
|
||||||
maxDebugModeDurationMinutes: this.maxDebugModeDurationMinutes,
|
maxDebugModeDurationMinutes: this.maxDebugModeDurationMinutes,
|
||||||
debugLimitsConfiguration: this.debugLimitsConfiguration
|
debugLimitsConfiguration: this.debugLimitsConfiguration
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
{}, {}, true);
|
{}, {}, true);
|
||||||
debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover;
|
debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover;
|
||||||
debugStrategyPopover.tbComponentRef.instance.onConfigApplied.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((config: HasDebugConfig) => {
|
debugStrategyPopover.tbComponentRef.instance.onConfigApplied.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((settings: DebugSettings) => {
|
||||||
this.onDebugConfigChanged.emit(config);
|
this.debugSettingsFormGroup.patchValue(settings);
|
||||||
debugStrategyPopover.hide();
|
debugStrategyPopover.hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: (settings: DebugSettings) => void): void {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_: () => void): void {}
|
||||||
|
|
||||||
|
writeValue(settings: DebugSettings): void {
|
||||||
|
this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState(isDisabled: boolean): void {
|
||||||
|
this.disabled = isDisabled;
|
||||||
|
this.debugSettingsFormGroup[isDisabled ? 'disable' : 'enable']({emitEvent: false});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -19,10 +19,11 @@
|
|||||||
<div class="tb-form-panel-title" translate>debug-config.label</div>
|
<div class="tb-form-panel-title" translate>debug-config.label</div>
|
||||||
<div class="hint-container">
|
<div class="hint-container">
|
||||||
<div class="tb-form-hint tb-primary-fill tb-flex center">
|
<div class="tb-form-hint tb-primary-fill tb-flex center">
|
||||||
<span *ngIf="debugLimitsConfiguration else noLimitHint">
|
@if (debugLimitsConfiguration) {
|
||||||
{{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, sec: maxTimeFrameSec } }}
|
{{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, sec: maxTimeFrameSec } }}
|
||||||
</span>
|
} @else {
|
||||||
<ng-template #noLimitHint>{{ 'debug-config.hint.main' | translate }}</ng-template>
|
{{ 'debug-config.hint.main' | translate }}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-3">
|
<div class="flex flex-col gap-3">
|
||||||
@ -34,17 +35,19 @@
|
|||||||
<div class="flex justify-between align-center">
|
<div class="flex justify-between align-center">
|
||||||
<mat-slide-toggle class="mat-slide" [formControl]="debugAllControl">
|
<mat-slide-toggle class="mat-slide" [formControl]="debugAllControl">
|
||||||
<div tb-hint-tooltip-icon="{{ 'debug-config.hint.all-messages' | translate }}">
|
<div tb-hint-tooltip-icon="{{ 'debug-config.hint.all-messages' | translate }}">
|
||||||
{{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !debugAll ? (debugAllUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) } }}
|
{{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) } }}
|
||||||
</div>
|
</div>
|
||||||
</mat-slide-toggle>
|
</mat-slide-toggle>
|
||||||
<button mat-icon-button *ngIf="(isDebugAllActive$ | async) && !debugAll"
|
@if ((isDebugAllActive$ | async) && !allEnabled) {
|
||||||
class="tb-mat-20"
|
<button mat-icon-button
|
||||||
matTooltip="{{ 'action.reset' | translate }}"
|
class="tb-mat-20"
|
||||||
matTooltipPosition="above"
|
matTooltip="{{ 'action.reset' | translate }}"
|
||||||
color="primary"
|
matTooltipPosition="above"
|
||||||
(click)="onReset()">
|
color="primary"
|
||||||
<mat-icon class="material-icons">refresh</mat-icon>
|
(click)="onReset()">
|
||||||
</button>
|
<mat-icon class="material-icons">refresh</mat-icon>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
@ -31,12 +31,13 @@ import { MINUTE, SECOND } from '@shared/models/time/time.models';
|
|||||||
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
|
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
|
||||||
import { shareReplay, timer } from 'rxjs';
|
import { shareReplay, timer } from 'rxjs';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { HasDebugConfig } from '@shared/models/entity.models';
|
import { DebugSettings } from '@shared/models/entity.models';
|
||||||
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
|
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
|
||||||
|
import { coerceBoolean } from '@shared/decorators/coercion';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-debug-config-panel',
|
selector: 'tb-debug-settings-panel',
|
||||||
templateUrl: './debug-config-panel.component.html',
|
templateUrl: './debug-settings-panel.component.html',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
@ -45,12 +46,12 @@ import { distinctUntilChanged, map, tap } from 'rxjs/operators';
|
|||||||
],
|
],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class DebugConfigPanelComponent extends PageComponent implements OnInit {
|
export class DebugSettingsPanelComponent extends PageComponent implements OnInit {
|
||||||
|
|
||||||
@Input() popover: TbPopoverComponent<DebugConfigPanelComponent>;
|
@Input() popover: TbPopoverComponent<DebugSettingsPanelComponent>;
|
||||||
@Input() debugFailures = false;
|
@Input() @coerceBoolean() failuresEnabled = false;
|
||||||
@Input() debugAll = false;
|
@Input() @coerceBoolean() allEnabled = false;
|
||||||
@Input() debugAllUntil = 0;
|
@Input() allEnabledUntil = 0;
|
||||||
@Input() maxDebugModeDurationMinutes: number;
|
@Input() maxDebugModeDurationMinutes: number;
|
||||||
@Input() debugLimitsConfiguration: string;
|
@Input() debugLimitsConfiguration: string;
|
||||||
|
|
||||||
@ -63,14 +64,14 @@ export class DebugConfigPanelComponent extends PageComponent implements OnInit {
|
|||||||
isDebugAllActive$ = timer(0, SECOND).pipe(
|
isDebugAllActive$ = timer(0, SECOND).pipe(
|
||||||
map(() => {
|
map(() => {
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
return this.debugAllUntil > new Date().getTime() || this.debugAll;
|
return this.allEnabledUntil > new Date().getTime() || this.allEnabled;
|
||||||
}),
|
}),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
tap(isDebugOn => this.debugAllControl.patchValue(isDebugOn, { emitEvent: false })),
|
tap(isDebugOn => this.debugAllControl.patchValue(isDebugOn, { emitEvent: false })),
|
||||||
shareReplay(1),
|
shareReplay(1),
|
||||||
);
|
);
|
||||||
|
|
||||||
onConfigApplied = new EventEmitter<HasDebugConfig>();
|
onConfigApplied = new EventEmitter<DebugSettings>();
|
||||||
|
|
||||||
constructor(private fb: UntypedFormBuilder, private cd: ChangeDetectorRef) {
|
constructor(private fb: UntypedFormBuilder, private cd: ChangeDetectorRef) {
|
||||||
super();
|
super();
|
||||||
@ -81,7 +82,7 @@ export class DebugConfigPanelComponent extends PageComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.maxMessagesCount = this.debugLimitsConfiguration?.split(':')[0];
|
this.maxMessagesCount = this.debugLimitsConfiguration?.split(':')[0];
|
||||||
this.maxTimeFrameSec = this.debugLimitsConfiguration?.split(':')[1];
|
this.maxTimeFrameSec = this.debugLimitsConfiguration?.split(':')[1];
|
||||||
this.onFailuresControl.patchValue(this.debugFailures);
|
this.onFailuresControl.patchValue(this.failuresEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCancel(): void {
|
onCancel(): void {
|
||||||
@ -90,22 +91,22 @@ export class DebugConfigPanelComponent extends PageComponent implements OnInit {
|
|||||||
|
|
||||||
onApply(): void {
|
onApply(): void {
|
||||||
this.onConfigApplied.emit({
|
this.onConfigApplied.emit({
|
||||||
debugAll: this.debugAll,
|
allEnabled: this.allEnabled,
|
||||||
debugFailures: this.onFailuresControl.value,
|
failuresEnabled: this.onFailuresControl.value,
|
||||||
debugAllUntil: this.debugAllUntil
|
allEnabledUntil: this.allEnabledUntil
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset(): void {
|
onReset(): void {
|
||||||
this.debugAll = true;
|
this.allEnabled = true;
|
||||||
this.debugAllUntil = new Date().getTime() + this.maxDebugModeDurationMinutes * MINUTE;
|
this.allEnabledUntil = new Date().getTime() + this.maxDebugModeDurationMinutes * MINUTE;
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private observeDebugAllChange(): void {
|
private observeDebugAllChange(): void {
|
||||||
this.debugAllControl.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => {
|
this.debugAllControl.valueChanges.pipe(takeUntilDestroyed()).subscribe(value => {
|
||||||
this.debugAllUntil = value? new Date().getTime() + this.maxDebugModeDurationMinutes * MINUTE : 0;
|
this.allEnabledUntil = value? new Date().getTime() + this.maxDebugModeDurationMinutes * MINUTE : 0;
|
||||||
this.debugAll = value;
|
this.allEnabled = value;
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -76,22 +76,6 @@
|
|||||||
<mat-hint></mat-hint>
|
<mat-hint></mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="fields-element flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
|
|
||||||
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
|
|
||||||
<mat-label translate>tenant-profile.maximum-debug-duration-min</mat-label>
|
|
||||||
<input matInput required min="0" step="1"
|
|
||||||
formControlName="maxDebugModeDurationMinutes"
|
|
||||||
type="number">
|
|
||||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDebugModeDurationMinutes').hasError('required')">
|
|
||||||
{{ 'tenant-profile.maximum-debug-duration-min-range' | translate }}
|
|
||||||
</mat-error>
|
|
||||||
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDebugModeDurationMinutes').hasError('min')">
|
|
||||||
{{ 'tenant-profile.maximum-debug-duration-min-required' | translate }}
|
|
||||||
</mat-error>
|
|
||||||
<mat-hint></mat-hint>
|
|
||||||
</mat-form-field>
|
|
||||||
<div class="flex-1"></div>
|
|
||||||
</div>
|
|
||||||
<mat-expansion-panel class="configuration-panel">
|
<mat-expansion-panel class="configuration-panel">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-description class="flex items-stretch justify-end" translate>
|
<mat-panel-description class="flex items-stretch justify-end" translate>
|
||||||
@ -370,6 +354,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="fields-group">
|
||||||
|
<legend class="group-title">
|
||||||
|
{{ 'tenant-profile.debug' | translate }} <span translate>tenant-profile.unlimited</span>
|
||||||
|
</legend>
|
||||||
|
<div class="fields-element flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
|
||||||
|
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
|
||||||
|
<mat-label translate>tenant-profile.maximum-debug-duration-min</mat-label>
|
||||||
|
<input matInput min="0" step="1"
|
||||||
|
formControlName="maxDebugModeDurationMinutes"
|
||||||
|
type="number">
|
||||||
|
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxDebugModeDurationMinutes').hasError('min')">
|
||||||
|
{{ 'tenant-profile.maximum-debug-duration-min-range' | translate }}
|
||||||
|
</mat-error>
|
||||||
|
<mat-hint></mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<div class="flex-1"></div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset class="fields-group">
|
<fieldset class="fields-group">
|
||||||
<legend class="group-title">
|
<legend class="group-title">
|
||||||
{{ 'tenant-profile.ota-files-in-bytes' | translate }} <span translate>tenant-profile.unlimited</span>
|
{{ 'tenant-profile.ota-files-in-bytes' | translate }} <span translate>tenant-profile.unlimited</span>
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
|
|||||||
tenantNotificationRequestsRateLimit: [null, []],
|
tenantNotificationRequestsRateLimit: [null, []],
|
||||||
tenantNotificationRequestsPerRuleRateLimit: [null, []],
|
tenantNotificationRequestsPerRuleRateLimit: [null, []],
|
||||||
maxTransportMessages: [null, [Validators.required, Validators.min(0)]],
|
maxTransportMessages: [null, [Validators.required, Validators.min(0)]],
|
||||||
maxDebugModeDurationMinutes: [null, [Validators.required, Validators.min(0)]],
|
maxDebugModeDurationMinutes: [null, [Validators.min(0)]],
|
||||||
maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]],
|
maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]],
|
||||||
maxREExecutions: [null, [Validators.required, Validators.min(0)]],
|
maxREExecutions: [null, [Validators.required, Validators.min(0)]],
|
||||||
maxJSExecutions: [null, [Validators.required, Validators.min(0)]],
|
maxJSExecutions: [null, [Validators.required, Validators.min(0)]],
|
||||||
|
|||||||
@ -35,13 +35,10 @@
|
|||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<section class="flex flex-row max-w-xs mb-5">
|
<section class="flex flex-row max-w-xs mb-5">
|
||||||
<tb-debug-config-button
|
<tb-debug-settings-button
|
||||||
class="mr-2"
|
class="mr-2"
|
||||||
[debugAll]="ruleNode.debugAll"
|
formControlName="debugSettings"
|
||||||
[debugFailures]="ruleNode.debugFailures"
|
|
||||||
[debugAllUntil]="ruleNode.debugAllUntil"
|
|
||||||
[debugLimitsConfiguration]="ruleChainDebugPerTenantLimitsConfiguration"
|
[debugLimitsConfiguration]="ruleChainDebugPerTenantLimitsConfiguration"
|
||||||
(onDebugConfigChanged)="onDebugConfigChanged($event)"
|
|
||||||
/>
|
/>
|
||||||
<button mat-stroked-button
|
<button mat-stroked-button
|
||||||
class="tb-rounded-btn flex-1"
|
class="tb-rounded-btn flex-1"
|
||||||
|
|||||||
@ -40,7 +40,6 @@ import { coerceBoolean } from '@shared/decorators/coercion';
|
|||||||
import { ServiceType } from '@shared/models/queue.models';
|
import { ServiceType } from '@shared/models/queue.models';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { getCurrentAuthState } from '@core/auth/auth.selectors';
|
import { getCurrentAuthState } from '@core/auth/auth.selectors';
|
||||||
import { HasDebugConfig } from '@shared/models/entity.models';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-rule-node',
|
selector: 'tb-rule-node',
|
||||||
@ -96,9 +95,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
|
|||||||
if (this.ruleNode) {
|
if (this.ruleNode) {
|
||||||
this.ruleNodeFormGroup = this.fb.group({
|
this.ruleNodeFormGroup = this.fb.group({
|
||||||
name: [this.ruleNode.name, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*'), Validators.maxLength(255)]],
|
name: [this.ruleNode.name, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*'), Validators.maxLength(255)]],
|
||||||
debugAll: [this.ruleNode.debugAll],
|
debugSettings: [this.ruleNode.debugSettings],
|
||||||
debugFailures: [this.ruleNode.debugFailures],
|
|
||||||
debugAllUntil: [this.ruleNode.debugAllUntil],
|
|
||||||
singletonMode: [this.ruleNode.singletonMode, []],
|
singletonMode: [this.ruleNode.singletonMode, []],
|
||||||
configuration: [this.ruleNode.configuration, [Validators.required]],
|
configuration: [this.ruleNode.configuration, [Validators.required]],
|
||||||
additionalInfo: this.fb.group(
|
additionalInfo: this.fb.group(
|
||||||
@ -208,11 +205,4 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
|
|||||||
isSingletonEditAllowed() {
|
isSingletonEditAllowed() {
|
||||||
return this.ruleNode.component.clusteringMode === ComponentClusteringMode.USER_PREFERENCE;
|
return this.ruleNode.component.clusteringMode === ComponentClusteringMode.USER_PREFERENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDebugConfigChanged(config: HasDebugConfig): void {
|
|
||||||
this.ruleNodeFormGroup.get('debugAllUntil').setValue(config.debugAllUntil);
|
|
||||||
this.ruleNodeFormGroup.get('debugAll').setValue(config.debugAll);
|
|
||||||
this.ruleNodeFormGroup.get('debugFailures').setValue(config.debugFailures);
|
|
||||||
this.ruleNodeFormGroup.markAsDirty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -575,9 +575,7 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
additionalInfo: ruleNode.additionalInfo,
|
additionalInfo: ruleNode.additionalInfo,
|
||||||
configuration: ruleNode.configuration,
|
configuration: ruleNode.configuration,
|
||||||
configurationVersion: isDefinedAndNotNull(ruleNode.configurationVersion) ? ruleNode.configurationVersion : 0,
|
configurationVersion: isDefinedAndNotNull(ruleNode.configurationVersion) ? ruleNode.configurationVersion : 0,
|
||||||
debugAll: ruleNode.debugAll,
|
debugSettings: ruleNode.debugSettings,
|
||||||
debugFailures: ruleNode.debugFailures,
|
|
||||||
debugAllUntil: ruleNode.debugAllUntil,
|
|
||||||
singletonMode: ruleNode.singletonMode,
|
singletonMode: ruleNode.singletonMode,
|
||||||
queueName: ruleNode.queueName,
|
queueName: ruleNode.queueName,
|
||||||
x: Math.round(ruleNode.additionalInfo.layoutX),
|
x: Math.round(ruleNode.additionalInfo.layoutX),
|
||||||
@ -938,9 +936,7 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
name: node.name,
|
name: node.name,
|
||||||
configuration: deepClone(node.configuration),
|
configuration: deepClone(node.configuration),
|
||||||
additionalInfo: node.additionalInfo ? deepClone(node.additionalInfo) : {},
|
additionalInfo: node.additionalInfo ? deepClone(node.additionalInfo) : {},
|
||||||
debugFailures: node.debugFailures,
|
debugSettings: node.debugSettings,
|
||||||
debugAllUntil: node.debugAllUntil,
|
|
||||||
debugAll: node.debugAll,
|
|
||||||
singletonMode: node.singletonMode,
|
singletonMode: node.singletonMode,
|
||||||
queueName: node.queueName
|
queueName: node.queueName
|
||||||
};
|
};
|
||||||
@ -1013,9 +1009,7 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
name: outputEdge.label,
|
name: outputEdge.label,
|
||||||
configuration: {},
|
configuration: {},
|
||||||
additionalInfo: {},
|
additionalInfo: {},
|
||||||
debugFailures: false,
|
debugSettings: {},
|
||||||
debugAllUntil: 0,
|
|
||||||
debugAll: false,
|
|
||||||
singletonMode: false
|
singletonMode: false
|
||||||
};
|
};
|
||||||
outputNode.additionalInfo.layoutX = Math.round(destNode.x);
|
outputNode.additionalInfo.layoutX = Math.round(destNode.x);
|
||||||
@ -1061,9 +1055,7 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
configuration: {
|
configuration: {
|
||||||
ruleChainId: ruleChain.id.id
|
ruleChainId: ruleChain.id.id
|
||||||
},
|
},
|
||||||
debugFailures: false,
|
debugSettings: {},
|
||||||
debugAllUntil: 0,
|
|
||||||
debugAll: false,
|
|
||||||
singletonMode: false,
|
singletonMode: false,
|
||||||
x: Math.round(ruleChainNodeX),
|
x: Math.round(ruleChainNodeX),
|
||||||
y: Math.round(ruleChainNodeY),
|
y: Math.round(ruleChainNodeY),
|
||||||
@ -1483,9 +1475,7 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
: node.component.configurationVersion,
|
: node.component.configurationVersion,
|
||||||
configuration: node.configuration,
|
configuration: node.configuration,
|
||||||
additionalInfo: node.additionalInfo ? node.additionalInfo : {},
|
additionalInfo: node.additionalInfo ? node.additionalInfo : {},
|
||||||
debugFailures: node.debugFailures,
|
debugSettings: node.debugSettings,
|
||||||
debugAllUntil: node.debugAllUntil,
|
|
||||||
debugAll: node.debugAll,
|
|
||||||
singletonMode: node.singletonMode,
|
singletonMode: node.singletonMode,
|
||||||
queueName: node.queueName
|
queueName: node.queueName
|
||||||
};
|
};
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import { RuleNodeLinkComponent } from './rule-node-link.component';
|
|||||||
import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component';
|
import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component';
|
||||||
import { RuleNodeConfigComponent } from './rule-node-config.component';
|
import { RuleNodeConfigComponent } from './rule-node-config.component';
|
||||||
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
|
import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe';
|
||||||
import { DebugConfigButtonComponent } from '@home/components/debug-config/debug-config-button.component';
|
import { DebugSettingsButtonComponent } from '@home/components/debug-settings/debug-settings-button.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -63,7 +63,7 @@ import { DebugConfigButtonComponent } from '@home/components/debug-config/debug-
|
|||||||
HomeComponentsModule,
|
HomeComponentsModule,
|
||||||
RuleChainRoutingModule,
|
RuleChainRoutingModule,
|
||||||
DurationLeftPipe,
|
DurationLeftPipe,
|
||||||
DebugConfigButtonComponent
|
DebugSettingsButtonComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class RuleChainModule { }
|
export class RuleChainModule { }
|
||||||
|
|||||||
@ -649,18 +649,20 @@ export class ImportExportService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private processOldRuleChainConnections(ruleChainImport: RuleChainImport): Observable<RuleChainImport> {
|
private processOldRuleChainConnections({ruleChain, metadata}: RuleChainImport): Observable<RuleChainImport> {
|
||||||
ruleChainImport.ruleChain = this.prepareImport(ruleChainImport.ruleChain);
|
ruleChain = this.prepareImport(ruleChain);
|
||||||
const metadata = ruleChainImport.metadata;
|
metadata = {
|
||||||
|
...metadata,
|
||||||
|
nodes: metadata.nodes.map(({ debugMode, ...node }: RuleNode & { debugMode: boolean }) => {
|
||||||
|
return debugMode ? { ...node, debugSettings: { failuresEnabled: true, allEnabled: true} } : node
|
||||||
|
})
|
||||||
|
};
|
||||||
if ((metadata as any).ruleChainConnections) {
|
if ((metadata as any).ruleChainConnections) {
|
||||||
const ruleChainNameResolveObservables: Observable<void>[] = [];
|
const ruleChainNameResolveObservables: Observable<void>[] = [];
|
||||||
for (const ruleChainConnection of (metadata as any).ruleChainConnections) {
|
for (const ruleChainConnection of (metadata as any).ruleChainConnections) {
|
||||||
if (ruleChainConnection.targetRuleChainId && ruleChainConnection.targetRuleChainId.id) {
|
if (ruleChainConnection.targetRuleChainId && ruleChainConnection.targetRuleChainId.id) {
|
||||||
const ruleChainNode: RuleNode = {
|
const ruleChainNode: RuleNode = {
|
||||||
name: '',
|
name: '',
|
||||||
debugFailures: false,
|
|
||||||
debugAllUntil: 0,
|
|
||||||
debugAll: false,
|
|
||||||
singletonMode: false,
|
singletonMode: false,
|
||||||
type: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode',
|
type: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode',
|
||||||
configuration: {
|
configuration: {
|
||||||
@ -688,13 +690,13 @@ export class ImportExportService {
|
|||||||
}
|
}
|
||||||
if (ruleChainNameResolveObservables.length) {
|
if (ruleChainNameResolveObservables.length) {
|
||||||
return forkJoin(ruleChainNameResolveObservables).pipe(
|
return forkJoin(ruleChainNameResolveObservables).pipe(
|
||||||
map(() => ruleChainImport)
|
map(() => ({ruleChain, metadata}))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return of(ruleChainImport);
|
return of({ruleChain, metadata});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return of(ruleChainImport);
|
return of({ruleChain, metadata});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -193,10 +193,14 @@ export interface HasVersion {
|
|||||||
version?: number;
|
version?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HasDebugConfig {
|
export interface HasDebugSettings {
|
||||||
debugAll?: boolean;
|
debugSettings?: DebugSettings;
|
||||||
debugFailures?: boolean;
|
|
||||||
debugAllUntil?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VersionedEntity = EntityInfoData & HasVersion | RuleChainMetaData;
|
export interface DebugSettings {
|
||||||
|
failuresEnabled?: boolean;
|
||||||
|
allEnabled?: boolean;
|
||||||
|
allEnabledUntil?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VersionedEntity = EntityInfoData & HasVersion | RuleChainMetaData;
|
||||||
|
|||||||
@ -27,13 +27,13 @@ import { AppState } from '@core/core.state';
|
|||||||
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
|
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
|
||||||
import { RuleChainType } from '@shared/models/rule-chain.models';
|
import { RuleChainType } from '@shared/models/rule-chain.models';
|
||||||
import { DebugRuleNodeEventBody } from '@shared/models/event.models';
|
import { DebugRuleNodeEventBody } from '@shared/models/event.models';
|
||||||
import { HasDebugConfig } from '@shared/models/entity.models';
|
import { HasDebugSettings } from '@shared/models/entity.models';
|
||||||
|
|
||||||
export interface RuleNodeConfiguration {
|
export interface RuleNodeConfiguration {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuleNode extends BaseData<RuleNodeId>, HasDebugConfig {
|
export interface RuleNode extends BaseData<RuleNodeId>, HasDebugSettings {
|
||||||
ruleChainId?: RuleChainId;
|
ruleChainId?: RuleChainId;
|
||||||
type: string;
|
type: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -331,7 +331,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor {
|
|||||||
configurationDescriptor?: RuleNodeConfigurationDescriptor;
|
configurationDescriptor?: RuleNodeConfigurationDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FcRuleNodeType extends FcNode, HasDebugConfig {
|
export interface FcRuleNodeType extends FcNode, HasDebugSettings {
|
||||||
component?: RuleNodeComponentDescriptor;
|
component?: RuleNodeComponentDescriptor;
|
||||||
singletonMode?: boolean;
|
singletonMode?: boolean;
|
||||||
queueName?: string;
|
queueName?: string;
|
||||||
|
|||||||
@ -4402,6 +4402,7 @@
|
|||||||
"tenant-profiles": "Tenant profiles",
|
"tenant-profiles": "Tenant profiles",
|
||||||
"add": "Add tenant profile",
|
"add": "Add tenant profile",
|
||||||
"add-profile": "Add profile",
|
"add-profile": "Add profile",
|
||||||
|
"debug": "Debug",
|
||||||
"edit": "Edit tenant profile",
|
"edit": "Edit tenant profile",
|
||||||
"tenant-profile-details": "Tenant profile details",
|
"tenant-profile-details": "Tenant profile details",
|
||||||
"no-tenant-profiles-text": "No tenant profiles found",
|
"no-tenant-profiles-text": "No tenant profiles found",
|
||||||
@ -4470,7 +4471,6 @@
|
|||||||
"maximum-ota-package-sum-data-size-required": "Maximum total size of OTA package files is required.",
|
"maximum-ota-package-sum-data-size-required": "Maximum total size of OTA package files is required.",
|
||||||
"maximum-ota-package-sum-data-size-range": "Maximum total size of OTA package files can't be negative",
|
"maximum-ota-package-sum-data-size-range": "Maximum total size of OTA package files can't be negative",
|
||||||
"maximum-debug-duration-min": "Maximum debug duration (min)",
|
"maximum-debug-duration-min": "Maximum debug duration (min)",
|
||||||
"maximum-debug-duration-min-required": "Maximum debug duration is required.",
|
|
||||||
"maximum-debug-duration-min-range": "Maximum debug duration can't be negative",
|
"maximum-debug-duration-min-range": "Maximum debug duration can't be negative",
|
||||||
"rest-requests-for-tenant": "REST requests for tenant",
|
"rest-requests-for-tenant": "REST requests for tenant",
|
||||||
"transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages",
|
"transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user