committed by
GitHub
273 changed files with 15599 additions and 109 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,105 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { CommonModule } from '@angular/common'; |
||||
|
import { SharedModule } from '@shared/public-api'; |
||||
|
import { HomeComponentsModule } from '@home/components/public-api'; |
||||
|
import { AttributesConfigComponent } from './attributes-config.component'; |
||||
|
import { TimeseriesConfigComponent } from './timeseries-config.component'; |
||||
|
import { RpcRequestConfigComponent } from './rpc-request-config.component'; |
||||
|
import { LogConfigComponent } from './log-config.component'; |
||||
|
import { AssignCustomerConfigComponent } from './assign-customer-config.component'; |
||||
|
import { ClearAlarmConfigComponent } from './clear-alarm-config.component'; |
||||
|
import { CreateAlarmConfigComponent } from './create-alarm-config.component'; |
||||
|
import { CreateRelationConfigComponent } from './create-relation-config.component'; |
||||
|
import { MsgDelayConfigComponent } from './msg-delay-config.component'; |
||||
|
import { DeleteRelationConfigComponent } from './delete-relation-config.component'; |
||||
|
import { GeneratorConfigComponent } from './generator-config.component'; |
||||
|
import { GpsGeoActionConfigComponent } from './gps-geo-action-config.component'; |
||||
|
import { MsgCountConfigComponent } from './msg-count-config.component'; |
||||
|
import { RpcReplyConfigComponent } from './rpc-reply-config.component'; |
||||
|
import { SaveToCustomTableConfigComponent } from './save-to-custom-table-config.component'; |
||||
|
import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module'; |
||||
|
import { UnassignCustomerConfigComponent } from './unassign-customer-config.component'; |
||||
|
import { DeviceProfileConfigComponent } from './device-profile-config.component'; |
||||
|
import { PushToEdgeConfigComponent } from './push-to-edge-config.component'; |
||||
|
import { PushToCloudConfigComponent } from './push-to-cloud-config.component'; |
||||
|
import { DeleteAttributesConfigComponent } from './delete-attributes-config.component'; |
||||
|
import { MathFunctionConfigComponent } from './math-function-config.component'; |
||||
|
import { DeviceStateConfigComponent } from './device-state-config.component'; |
||||
|
import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply-config.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [ |
||||
|
DeleteAttributesConfigComponent, |
||||
|
AttributesConfigComponent, |
||||
|
TimeseriesConfigComponent, |
||||
|
RpcRequestConfigComponent, |
||||
|
LogConfigComponent, |
||||
|
AssignCustomerConfigComponent, |
||||
|
ClearAlarmConfigComponent, |
||||
|
CreateAlarmConfigComponent, |
||||
|
CreateRelationConfigComponent, |
||||
|
MsgDelayConfigComponent, |
||||
|
DeleteRelationConfigComponent, |
||||
|
GeneratorConfigComponent, |
||||
|
GpsGeoActionConfigComponent, |
||||
|
MsgCountConfigComponent, |
||||
|
RpcReplyConfigComponent, |
||||
|
SaveToCustomTableConfigComponent, |
||||
|
UnassignCustomerConfigComponent, |
||||
|
SendRestApiCallReplyConfigComponent, |
||||
|
DeviceProfileConfigComponent, |
||||
|
PushToEdgeConfigComponent, |
||||
|
PushToCloudConfigComponent, |
||||
|
MathFunctionConfigComponent, |
||||
|
DeviceStateConfigComponent |
||||
|
], |
||||
|
imports: [ |
||||
|
CommonModule, |
||||
|
SharedModule, |
||||
|
HomeComponentsModule, |
||||
|
CommonRuleNodeConfigModule |
||||
|
], |
||||
|
exports: [ |
||||
|
DeleteAttributesConfigComponent, |
||||
|
AttributesConfigComponent, |
||||
|
TimeseriesConfigComponent, |
||||
|
RpcRequestConfigComponent, |
||||
|
LogConfigComponent, |
||||
|
AssignCustomerConfigComponent, |
||||
|
ClearAlarmConfigComponent, |
||||
|
CreateAlarmConfigComponent, |
||||
|
CreateRelationConfigComponent, |
||||
|
MsgDelayConfigComponent, |
||||
|
DeleteRelationConfigComponent, |
||||
|
GeneratorConfigComponent, |
||||
|
GpsGeoActionConfigComponent, |
||||
|
MsgCountConfigComponent, |
||||
|
RpcReplyConfigComponent, |
||||
|
SaveToCustomTableConfigComponent, |
||||
|
UnassignCustomerConfigComponent, |
||||
|
SendRestApiCallReplyConfigComponent, |
||||
|
DeviceProfileConfigComponent, |
||||
|
PushToEdgeConfigComponent, |
||||
|
PushToCloudConfigComponent, |
||||
|
MathFunctionConfigComponent, |
||||
|
DeviceStateConfigComponent |
||||
|
] |
||||
|
}) |
||||
|
export class ActionRuleNodeConfigModule { |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="assignCustomerConfigForm" class="flex flex-col"> |
||||
|
<div class="tb-form-panel no-padding no-border"> |
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>rule-node-config.customer-name-pattern</mat-label> |
||||
|
<input required matInput formControlName="customerNamePattern"> |
||||
|
<mat-error *ngIf="assignCustomerConfigForm.get('customerNamePattern').hasError('required') || |
||||
|
assignCustomerConfigForm.get('customerNamePattern').hasError('pattern')"> |
||||
|
{{ 'rule-node-config.customer-name-pattern-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint translate>rule-node-config.customer-name-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
<div class="tb-form-row"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="createCustomerIfNotExists"> |
||||
|
{{ 'rule-node-config.create-customer-if-not-exists' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,49 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-assign-to-customer-config', |
||||
|
templateUrl: './assign-customer-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class AssignCustomerConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
assignCustomerConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.assignCustomerConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.assignCustomerConfigForm = this.fb.group({ |
||||
|
customerNamePattern: [configuration ? configuration.customerNamePattern : null, [Validators.required, Validators.pattern(/.*\S.*/)]], |
||||
|
createCustomerIfNotExists: [configuration ? configuration.createCustomerIfNotExists : false, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
configuration.customerNamePattern = configuration.customerNamePattern.trim(); |
||||
|
return configuration; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="attributesConfigForm" class="tb-form-panel no-border no-padding"> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<tb-example-hint [hintText]="'rule-node-config.attributes-scope-hint'"> |
||||
|
</tb-example-hint> |
||||
|
<div class="tb-form-row no-border no-padding tb-standard-fields"> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'rule-node-config.attributes-scope' | translate }}</mat-label> |
||||
|
<mat-select required |
||||
|
class="tb-entity-type-select" matInput formControlName="scope"> |
||||
|
<mat-option *ngFor="let scope of attributeScopes" [value]="scope"> |
||||
|
{{ telemetryTypeTranslationsMap.get(scope) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'rule-node-config.attributes-scope-value' | translate }}</mat-label> |
||||
|
<input type="text" matInput readonly disabled [ngModel]="attributesConfigForm.get('scope').value" [ngModelOptions]="{standalone: true}"> |
||||
|
<button type="button" |
||||
|
matSuffix |
||||
|
mat-icon-button |
||||
|
aria-label="Copy" |
||||
|
ngxClipboard |
||||
|
[cbContent]="attributesConfigForm.get('scope').value" |
||||
|
matTooltip="{{ 'rule-node-config.attributes-scope-value-copy' | translate }}"> |
||||
|
<mat-icon aria-hidden="false" |
||||
|
aria-label="help-icon">content_copy |
||||
|
</mat-icon> |
||||
|
</button> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<section class="tb-form-panel stroked"> |
||||
|
<mat-expansion-panel class="tb-settings"> |
||||
|
<mat-expansion-panel-header> |
||||
|
<mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<div tb-hint-tooltip-icon="{{ (attributesConfigForm.get('updateAttributesOnlyOnValueChange').value |
||||
|
? 'rule-node-config.update-attributes-only-on-value-change-hint-enabled' |
||||
|
: 'rule-node-config.update-attributes-only-on-value-change-hint') | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="updateAttributesOnlyOnValueChange"> |
||||
|
{{ 'rule-node-config.update-attributes-only-on-value-change' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.send-attributes-updated-notification-hint' | translate }}" |
||||
|
*ngIf="attributesConfigForm.get('scope').value !== attributeScopeMap.CLIENT_SCOPE" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sendAttributesUpdatedNotification"> |
||||
|
{{ 'rule-node-config.send-attributes-updated-notification' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.notify-device-on-update-hint' | translate }}" |
||||
|
*ngIf="attributesConfigForm.get('scope').value === attributeScopeMap.SHARED_SCOPE" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="notifyDevice"> |
||||
|
{{ 'rule-node-config.notify-device' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,65 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
import { AttributeScope, telemetryTypeTranslations } from '@app/shared/models/telemetry/telemetry.models'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-attributes-config', |
||||
|
templateUrl: './attributes-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class AttributesConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
attributeScopeMap = AttributeScope; |
||||
|
attributeScopes = Object.keys(AttributeScope); |
||||
|
telemetryTypeTranslationsMap = telemetryTypeTranslations; |
||||
|
|
||||
|
attributesConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.attributesConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.attributesConfigForm = this.fb.group({ |
||||
|
scope: [configuration ? configuration.scope : null, [Validators.required]], |
||||
|
notifyDevice: [configuration ? configuration.notifyDevice : true, []], |
||||
|
sendAttributesUpdatedNotification: [configuration ? configuration.sendAttributesUpdatedNotification : false, []], |
||||
|
updateAttributesOnlyOnValueChange: [configuration ? configuration.updateAttributesOnlyOnValueChange : false, []] |
||||
|
}); |
||||
|
|
||||
|
this.attributesConfigForm.get('scope').valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe((value) => { |
||||
|
if (value !== AttributeScope.SHARED_SCOPE) { |
||||
|
this.attributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false}); |
||||
|
} |
||||
|
if (value === AttributeScope.CLIENT_SCOPE) { |
||||
|
this.attributesConfigForm.get('sendAttributesUpdatedNotification').patchValue(false, {emitEvent: false}); |
||||
|
} |
||||
|
this.attributesConfigForm.get('updateAttributesOnlyOnValueChange').patchValue(false, {emitEvent: false}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="clearAlarmConfigForm" class="flex flex-col"> |
||||
|
<tb-script-lang formControlName="scriptLang" *ngIf="tbelEnabled"></tb-script-lang> |
||||
|
<tb-js-func *ngIf="clearAlarmConfigForm.get('scriptLang').value === scriptLanguage.JS" |
||||
|
#jsFuncComponent |
||||
|
formControlName="alarmDetailsBuildJs" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
helpId="rulenode/clear_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<tb-js-func *ngIf="clearAlarmConfigForm.get('scriptLang').value === scriptLanguage.TBEL" |
||||
|
#tbelFuncComponent |
||||
|
formControlName="alarmDetailsBuildTbel" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
[disableUndefinedCheck]="true" |
||||
|
[scriptLanguage]="scriptLanguage.TBEL" |
||||
|
helpId="rulenode/tbel/clear_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<div class="flex flex-row" style="padding-bottom: 16px;"> |
||||
|
<button mat-button mat-raised-button color="primary" (click)="testScript()"> |
||||
|
{{ testScriptLabel | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>rule-node-config.alarm-type</mat-label> |
||||
|
<input required matInput formControlName="alarmType"> |
||||
|
<mat-error *ngIf="clearAlarmConfigForm.get('alarmType').hasError('required')"> |
||||
|
{{ 'rule-node-config.alarm-type-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint translate>rule-node-config.general-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
@ -0,0 +1,127 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, EventEmitter, ViewChild } from '@angular/core'; |
||||
|
import { AppState, getCurrentAuthState, NodeScriptTestService } from '@core/public-api'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { |
||||
|
RuleNodeConfiguration, |
||||
|
RuleNodeConfigurationComponent, |
||||
|
ScriptLanguage |
||||
|
} from '@app/shared/models/rule-node.models'; |
||||
|
import type { JsFuncComponent } from '@app/shared/components/js-func.component'; |
||||
|
import { DebugRuleNodeEventBody } from '@shared/models/event.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-clear-alarm-config', |
||||
|
templateUrl: './clear-alarm-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class ClearAlarmConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; |
||||
|
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; |
||||
|
|
||||
|
clearAlarmConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; |
||||
|
|
||||
|
scriptLanguage = ScriptLanguage; |
||||
|
|
||||
|
changeScript: EventEmitter<void> = new EventEmitter<void>(); |
||||
|
|
||||
|
readonly hasScript = true; |
||||
|
|
||||
|
readonly testScriptLabel = 'rule-node-config.test-details-function'; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: UntypedFormBuilder, |
||||
|
private nodeScriptTestService: NodeScriptTestService, |
||||
|
private translate: TranslateService) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.clearAlarmConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.clearAlarmConfigForm = this.fb.group({ |
||||
|
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]], |
||||
|
alarmDetailsBuildJs: [configuration ? configuration.alarmDetailsBuildJs : null, []], |
||||
|
alarmDetailsBuildTbel: [configuration ? configuration.alarmDetailsBuildTbel : null, []], |
||||
|
alarmType: [configuration ? configuration.alarmType : null, [Validators.required]] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['scriptLang']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
let scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { |
||||
|
scriptLang = ScriptLanguage.JS; |
||||
|
this.clearAlarmConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); |
||||
|
setTimeout(() => {this.clearAlarmConfigForm.updateValueAndValidity({emitEvent: true});}); |
||||
|
} |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildJs').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildJs').updateValueAndValidity({emitEvent}); |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildTbel').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildTbel').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
if (configuration) { |
||||
|
if (!configuration.scriptLang) { |
||||
|
configuration.scriptLang = ScriptLanguage.JS; |
||||
|
} |
||||
|
} |
||||
|
return configuration; |
||||
|
} |
||||
|
|
||||
|
testScript(debugEventBody?: DebugRuleNodeEventBody) { |
||||
|
const scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value; |
||||
|
const scriptField = scriptLang === ScriptLanguage.JS ? 'alarmDetailsBuildJs' : 'alarmDetailsBuildTbel'; |
||||
|
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/clear_alarm_node_script_fn' : 'rulenode/tbel/clear_alarm_node_script_fn'; |
||||
|
const script: string = this.clearAlarmConfigForm.get(scriptField).value; |
||||
|
this.nodeScriptTestService.testNodeScript( |
||||
|
script, |
||||
|
'json', |
||||
|
this.translate.instant('rule-node-config.details'), |
||||
|
'Details', |
||||
|
['msg', 'metadata', 'msgType'], |
||||
|
this.ruleNodeId, |
||||
|
helpId, |
||||
|
scriptLang, |
||||
|
debugEventBody |
||||
|
).subscribe((theScript) => { |
||||
|
if (theScript) { |
||||
|
this.clearAlarmConfigForm.get(scriptField).setValue(theScript); |
||||
|
this.changeScript.emit(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected onValidate() { |
||||
|
const scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.JS) { |
||||
|
this.jsFuncComponent.validateOnSubmit(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,128 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="createAlarmConfigForm" class="flex flex-col"> |
||||
|
<mat-checkbox formControlName="useMessageAlarmData"> |
||||
|
{{ 'rule-node-config.use-message-alarm-data' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<mat-checkbox formControlName="overwriteAlarmDetails" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === true"> |
||||
|
{{ 'rule-node-config.overwrite-alarm-details' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<section class="flex flex-col" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === false || |
||||
|
createAlarmConfigForm.get('overwriteAlarmDetails').value === true"> |
||||
|
<tb-script-lang formControlName="scriptLang" *ngIf="tbelEnabled"></tb-script-lang> |
||||
|
<tb-js-func *ngIf="createAlarmConfigForm.get('scriptLang').value === scriptLanguage.JS" |
||||
|
#jsFuncComponent |
||||
|
formControlName="alarmDetailsBuildJs" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
helpId="rulenode/create_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<tb-js-func *ngIf="createAlarmConfigForm.get('scriptLang').value === scriptLanguage.TBEL" |
||||
|
#tbelFuncComponent |
||||
|
formControlName="alarmDetailsBuildTbel" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
[disableUndefinedCheck]="true" |
||||
|
[scriptLanguage]="scriptLanguage.TBEL" |
||||
|
helpId="rulenode/tbel/create_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<div class="flex flex-row" style="padding-bottom: 16px;"> |
||||
|
<button mat-button mat-raised-button color="primary" (click)="testScript()"> |
||||
|
{{ testScriptLabel | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</section> |
||||
|
<section class="flex flex-col" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === false"> |
||||
|
<mat-form-field class="flex-1" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>rule-node-config.alarm-type</mat-label> |
||||
|
<input required matInput formControlName="alarmType"> |
||||
|
<mat-error *ngIf="createAlarmConfigForm.get('alarmType').hasError('required')"> |
||||
|
{{ 'rule-node-config.alarm-type-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint translate>rule-node-config.general-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
<mat-checkbox formControlName="dynamicSeverity"> |
||||
|
{{ 'rule-node-config.use-alarm-severity-pattern' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<mat-form-field class="flex-1" *ngIf="!createAlarmConfigForm.get('dynamicSeverity').value"> |
||||
|
<mat-label translate>rule-node-config.alarm-severity</mat-label> |
||||
|
<mat-select formControlName="severity" required> |
||||
|
<mat-option *ngFor="let severity of alarmSeverities" [value]="severity"> |
||||
|
{{ alarmSeverityTranslationMap.get(severity) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
<mat-error *ngIf="createAlarmConfigForm.get('severity').hasError('required')"> |
||||
|
{{ 'rule-node-config.alarm-severity-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex-1" *ngIf="createAlarmConfigForm.get('dynamicSeverity').value" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>rule-node-config.alarm-severity-pattern</mat-label> |
||||
|
<input matInput formControlName="severity" required> |
||||
|
<mat-error *ngIf="createAlarmConfigForm.get('severity').hasError('required')"> |
||||
|
{{ 'rule-node-config.alarm-severity-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint [innerHTML]="'rule-node-config.alarm-severity-pattern-hint' | translate | safe: 'html'"></mat-hint> |
||||
|
</mat-form-field> |
||||
|
<mat-checkbox formControlName="propagate"> |
||||
|
{{ 'rule-node-config.propagate' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<section *ngIf="createAlarmConfigForm.get('propagate').value === true"> |
||||
|
<mat-form-field floatLabel="always" class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>rule-node-config.relation-types-list</mat-label> |
||||
|
<mat-chip-grid #relationTypesChipList> |
||||
|
<mat-chip-row |
||||
|
*ngFor="let key of createAlarmConfigForm.get('relationTypes').value;" |
||||
|
(removed)="removeKey(key, 'relationTypes')"> |
||||
|
{{key}} |
||||
|
<mat-icon matChipRemove>close</mat-icon> |
||||
|
</mat-chip-row> |
||||
|
<input matInput type="text" placeholder="{{'rule-node-config.relation-types-list' | translate}}" |
||||
|
[matChipInputFor]="relationTypesChipList" |
||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" |
||||
|
(matChipInputTokenEnd)="addKey($event, 'relationTypes')" |
||||
|
[matChipInputAddOnBlur]="true"> |
||||
|
</mat-chip-grid> |
||||
|
<mat-hint translate>rule-node-config.relation-types-list-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
|
<mat-checkbox formControlName="propagateToOwner"> |
||||
|
{{ 'rule-node-config.propagate-to-owner' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<mat-checkbox formControlName="propagateToTenant"> |
||||
|
{{ 'rule-node-config.propagate-to-tenant' | translate }} |
||||
|
</mat-checkbox> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,202 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, EventEmitter, ViewChild } from '@angular/core'; |
||||
|
import { AppState, getCurrentAuthState, NodeScriptTestService } from '@core/public-api'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; |
||||
|
import { MatChipInputEvent } from '@angular/material/chips'; |
||||
|
import { |
||||
|
RuleNodeConfiguration, |
||||
|
RuleNodeConfigurationComponent, |
||||
|
ScriptLanguage |
||||
|
} from '@app/shared/models/rule-node.models'; |
||||
|
import type { JsFuncComponent } from '@app/shared/components/js-func.component'; |
||||
|
import { AlarmSeverity, alarmSeverityTranslations } from '@app/shared/models/alarm.models'; |
||||
|
import { DebugRuleNodeEventBody } from '@shared/models/event.models'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-create-alarm-config', |
||||
|
templateUrl: './create-alarm-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class CreateAlarmConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; |
||||
|
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; |
||||
|
|
||||
|
alarmSeverities = Object.keys(AlarmSeverity); |
||||
|
alarmSeverityTranslationMap = alarmSeverityTranslations; |
||||
|
createAlarmConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; |
||||
|
|
||||
|
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; |
||||
|
|
||||
|
scriptLanguage = ScriptLanguage; |
||||
|
|
||||
|
changeScript: EventEmitter<void> = new EventEmitter<void>(); |
||||
|
|
||||
|
readonly hasScript = true; |
||||
|
|
||||
|
readonly testScriptLabel = 'rule-node-config.test-details-function'; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: UntypedFormBuilder, |
||||
|
private nodeScriptTestService: NodeScriptTestService, |
||||
|
private translate: TranslateService) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.createAlarmConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.createAlarmConfigForm = this.fb.group({ |
||||
|
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]], |
||||
|
alarmDetailsBuildJs: [configuration ? configuration.alarmDetailsBuildJs : null, []], |
||||
|
alarmDetailsBuildTbel: [configuration ? configuration.alarmDetailsBuildTbel : null, []], |
||||
|
useMessageAlarmData: [configuration ? configuration.useMessageAlarmData : false, []], |
||||
|
overwriteAlarmDetails: [configuration ? configuration.overwriteAlarmDetails : false, []], |
||||
|
alarmType: [configuration ? configuration.alarmType : null, []], |
||||
|
severity: [configuration ? configuration.severity : null, []], |
||||
|
propagate: [configuration ? configuration.propagate : false, []], |
||||
|
relationTypes: [configuration ? configuration.relationTypes : null, []], |
||||
|
propagateToOwner: [configuration ? configuration.propagateToOwner : false, []], |
||||
|
propagateToTenant: [configuration ? configuration.propagateToTenant : false, []], |
||||
|
dynamicSeverity: false |
||||
|
}); |
||||
|
|
||||
|
this.createAlarmConfigForm.get('dynamicSeverity').valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe((dynamicSeverity) => { |
||||
|
if(dynamicSeverity){ |
||||
|
this.createAlarmConfigForm.get('severity').patchValue('',{emitEvent:false}); |
||||
|
} else { |
||||
|
this.createAlarmConfigForm.get('severity').patchValue(this.alarmSeverities[0],{emitEvent:false}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['useMessageAlarmData', 'overwriteAlarmDetails', 'scriptLang']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const useMessageAlarmData: boolean = this.createAlarmConfigForm.get('useMessageAlarmData').value; |
||||
|
const overwriteAlarmDetails: boolean = this.createAlarmConfigForm.get('overwriteAlarmDetails').value; |
||||
|
if (useMessageAlarmData) { |
||||
|
this.createAlarmConfigForm.get('alarmType').setValidators([]); |
||||
|
this.createAlarmConfigForm.get('severity').setValidators([]); |
||||
|
} else { |
||||
|
this.createAlarmConfigForm.get('alarmType').setValidators([Validators.required]); |
||||
|
this.createAlarmConfigForm.get('severity').setValidators([Validators.required]); |
||||
|
} |
||||
|
this.createAlarmConfigForm.get('alarmType').updateValueAndValidity({emitEvent}); |
||||
|
this.createAlarmConfigForm.get('severity').updateValueAndValidity({emitEvent}); |
||||
|
|
||||
|
let scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { |
||||
|
scriptLang = ScriptLanguage.JS; |
||||
|
this.createAlarmConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); |
||||
|
setTimeout(() => {this.createAlarmConfigForm.updateValueAndValidity({emitEvent: true});}); |
||||
|
} |
||||
|
const useAlarmDetailsBuildScript = useMessageAlarmData === false || overwriteAlarmDetails === true; |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildJs') |
||||
|
.setValidators(useAlarmDetailsBuildScript && scriptLang === ScriptLanguage.JS ? [Validators.required] : []); |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildTbel') |
||||
|
.setValidators(useAlarmDetailsBuildScript && scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildJs').updateValueAndValidity({emitEvent}); |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildTbel').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
if (configuration) { |
||||
|
if (!configuration.scriptLang) { |
||||
|
configuration.scriptLang = ScriptLanguage.JS; |
||||
|
} |
||||
|
} |
||||
|
return configuration; |
||||
|
} |
||||
|
|
||||
|
testScript(debugEventBody?: DebugRuleNodeEventBody) { |
||||
|
const scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value; |
||||
|
const scriptField = scriptLang === ScriptLanguage.JS ? 'alarmDetailsBuildJs' : 'alarmDetailsBuildTbel'; |
||||
|
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/create_alarm_node_script_fn' : 'rulenode/tbel/create_alarm_node_script_fn'; |
||||
|
const script: string = this.createAlarmConfigForm.get(scriptField).value; |
||||
|
this.nodeScriptTestService.testNodeScript( |
||||
|
script, |
||||
|
'json', |
||||
|
this.translate.instant('rule-node-config.details'), |
||||
|
'Details', |
||||
|
['msg', 'metadata', 'msgType'], |
||||
|
this.ruleNodeId, |
||||
|
helpId, |
||||
|
scriptLang, |
||||
|
debugEventBody |
||||
|
).subscribe((theScript) => { |
||||
|
if (theScript) { |
||||
|
this.createAlarmConfigForm.get(scriptField).setValue(theScript); |
||||
|
this.changeScript.emit(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeKey(key: string, keysField: string): void { |
||||
|
const keys: string[] = this.createAlarmConfigForm.get(keysField).value; |
||||
|
const index = keys.indexOf(key); |
||||
|
if (index >= 0) { |
||||
|
keys.splice(index, 1); |
||||
|
this.createAlarmConfigForm.get(keysField).setValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addKey(event: MatChipInputEvent, keysField: string): void { |
||||
|
const input = event.input; |
||||
|
let value = event.value; |
||||
|
if ((value || '').trim()) { |
||||
|
value = value.trim(); |
||||
|
let keys: string[] = this.createAlarmConfigForm.get(keysField).value; |
||||
|
if (!keys || keys.indexOf(value) === -1) { |
||||
|
if (!keys) { |
||||
|
keys = []; |
||||
|
} |
||||
|
keys.push(value); |
||||
|
this.createAlarmConfigForm.get(keysField).setValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
if (input) { |
||||
|
input.value = ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected onValidate() { |
||||
|
const useMessageAlarmData: boolean = this.createAlarmConfigForm.get('useMessageAlarmData').value; |
||||
|
const overwriteAlarmDetails: boolean = this.createAlarmConfigForm.get('overwriteAlarmDetails').value; |
||||
|
if (!useMessageAlarmData || overwriteAlarmDetails) { |
||||
|
const scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.JS) { |
||||
|
this.jsFuncComponent.validateOnSubmit(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,100 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="createRelationConfigForm" class="tb-form-panel no-padding no-border"> |
||||
|
<div class="tb-form-panel stroked no-padding-bottom"> |
||||
|
<div class="tb-form-panel-title" translate>rule-node-config.relation-parameters</div> |
||||
|
<div class="flex flex-col"> |
||||
|
<mat-form-field class="mat-block" hideRequiredMarker> |
||||
|
<mat-label translate>relation.direction</mat-label> |
||||
|
<mat-select required matInput formControlName="direction"> |
||||
|
<mat-option *ngFor="let type of directionTypes" [value]="type"> |
||||
|
{{ directionTypeTranslations.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<tb-relation-type-autocomplete |
||||
|
required |
||||
|
formControlName="relationType"> |
||||
|
</tb-relation-type-autocomplete> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="tb-form-panel stroked no-padding-bottom"> |
||||
|
<div class="tb-form-panel-title" translate>rule-node-config.target-entity</div> |
||||
|
<div class="flex flex-row gap-4"> |
||||
|
<tb-entity-type-select |
||||
|
class="flex-1" |
||||
|
showLabel |
||||
|
required |
||||
|
[allowedEntityTypes]="allowedEntityTypes" |
||||
|
formControlName="entityType"> |
||||
|
</tb-entity-type-select> |
||||
|
|
||||
|
<mat-form-field *ngIf="createRelationConfigForm.get('entityType').value && |
||||
|
createRelationConfigForm.get('entityType').value !== entityType.TENANT" |
||||
|
class="mat-block flex-1"> |
||||
|
<mat-label>{{ entityTypeNamePatternTranslation.get(createRelationConfigForm.get('entityType').value) | translate }}</mat-label> |
||||
|
<input required matInput formControlName="entityNamePattern"> |
||||
|
</mat-form-field> |
||||
|
|
||||
|
<mat-form-field *ngIf="createRelationConfigForm.get('entityType').value === entityType.DEVICE || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.ASSET" |
||||
|
class="mat-block flex-1"> |
||||
|
<mat-label translate>rule-node-config.profile-name</mat-label> |
||||
|
<input matInput formControlName="entityTypePattern"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
|
||||
|
<tb-example-hint [hintText]="'rule-node-config.kv-map-pattern-hint'" |
||||
|
*ngIf="createRelationConfigForm.get('entityType').value === entityType.CUSTOMER || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.DEVICE || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.ASSET"></tb-example-hint> |
||||
|
|
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.create-entity-if-not-exists-hint' | translate }}" |
||||
|
*ngIf="createRelationConfigForm.get('entityType').value === entityType.CUSTOMER || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.DEVICE || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.ASSET" |
||||
|
style="margin-bottom: 18px" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="createEntityIfNotExists"> |
||||
|
{{ 'rule-node-config.create-entity-if-not-exists' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</div> |
||||
|
<section class="tb-form-panel stroked no-padding"> |
||||
|
<mat-expansion-panel class="tb-settings"> |
||||
|
<mat-expansion-panel-header style="padding: 16px"> |
||||
|
<mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<div class="tb-form-panel no-border no-padding-top"> |
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.remove-current-relations-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="removeCurrentRelations"> |
||||
|
{{ 'rule-node-config.remove-current-relations' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.change-originator-to-related-entity-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="changeOriginatorToRelatedEntity"> |
||||
|
{{ 'rule-node-config.change-originator-to-related-entity' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,107 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; |
||||
|
import { EntitySearchDirection } from '@app/shared/models/relation.models'; |
||||
|
import { EntityType } from '@app/shared/models/entity-type.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-create-relation-config', |
||||
|
templateUrl: './create-relation-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class CreateRelationConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
directionTypes = Object.keys(EntitySearchDirection); |
||||
|
directionTypeTranslations = new Map<EntitySearchDirection, string>( |
||||
|
[ |
||||
|
[EntitySearchDirection.FROM, 'rule-node-config.search-direction-from'], |
||||
|
[EntitySearchDirection.TO, 'rule-node-config.search-direction-to'], |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
entityType = EntityType; |
||||
|
|
||||
|
entityTypeNamePatternTranslation = new Map<EntityType, string>( |
||||
|
[ |
||||
|
[EntityType.DEVICE, 'rule-node-config.device-name-pattern'], |
||||
|
[EntityType.ASSET, 'rule-node-config.asset-name-pattern'], |
||||
|
[EntityType.ENTITY_VIEW, 'rule-node-config.entity-view-name-pattern'], |
||||
|
[EntityType.CUSTOMER, 'rule-node-config.customer-title-pattern'], |
||||
|
[EntityType.USER, 'rule-node-config.user-name-pattern'], |
||||
|
[EntityType.DASHBOARD, 'rule-node-config.dashboard-name-pattern'], |
||||
|
[EntityType.EDGE, 'rule-node-config.edge-name-pattern'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
allowedEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.TENANT, |
||||
|
EntityType.CUSTOMER, EntityType.USER, EntityType.DASHBOARD, EntityType.EDGE]; |
||||
|
|
||||
|
createRelationConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.createRelationConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.createRelationConfigForm = this.fb.group({ |
||||
|
direction: [configuration ? configuration.direction : null, [Validators.required]], |
||||
|
entityType: [configuration ? configuration.entityType : null, [Validators.required]], |
||||
|
entityNamePattern: [configuration ? configuration.entityNamePattern : null, []], |
||||
|
entityTypePattern: [configuration ? configuration.entityTypePattern : null, []], |
||||
|
relationType: [configuration ? configuration.relationType : null, [Validators.required]], |
||||
|
createEntityIfNotExists: [configuration ? configuration.createEntityIfNotExists : false, []], |
||||
|
removeCurrentRelations: [configuration ? configuration.removeCurrentRelations : false, []], |
||||
|
changeOriginatorToRelatedEntity: [configuration ? configuration.changeOriginatorToRelatedEntity : false, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['entityType', 'createEntityIfNotExists']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const entityType: EntityType = this.createRelationConfigForm.get('entityType').value; |
||||
|
if (entityType) { |
||||
|
this.createRelationConfigForm.get('entityNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]); |
||||
|
} else { |
||||
|
this.createRelationConfigForm.get('entityNamePattern').setValidators([]); |
||||
|
} |
||||
|
if (entityType && (entityType === EntityType.DEVICE || entityType === EntityType.ASSET)) { |
||||
|
const validators = [Validators.pattern(/.*\S.*/)] |
||||
|
if (this.createRelationConfigForm.get('createEntityIfNotExists').value) { |
||||
|
validators.push(Validators.required); |
||||
|
} |
||||
|
this.createRelationConfigForm.get('entityTypePattern').setValidators(validators); |
||||
|
} else { |
||||
|
this.createRelationConfigForm.get('entityTypePattern').setValidators([]); |
||||
|
} |
||||
|
this.createRelationConfigForm.get('entityNamePattern').updateValueAndValidity({emitEvent}); |
||||
|
this.createRelationConfigForm.get('entityTypePattern').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
configuration.entityNamePattern = configuration.entityNamePattern ? configuration.entityNamePattern.trim() : null; |
||||
|
configuration.entityTypePattern = configuration.entityTypePattern ? configuration.entityTypePattern.trim() : null; |
||||
|
return configuration; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,89 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deleteAttributesConfigForm" class="tb-form-panel no-border no-padding"> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<tb-example-hint [hintText]="'rule-node-config.attributes-scope-hint'"> |
||||
|
</tb-example-hint> |
||||
|
<div class="tb-form-row no-border no-padding tb-standard-fields"> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'rule-node-config.attributes-scope' | translate }}</mat-label> |
||||
|
<mat-select required |
||||
|
class="tb-entity-type-select" matInput formControlName="scope"> |
||||
|
<mat-option *ngFor="let scope of attributeScopes" [value]="scope"> |
||||
|
{{ telemetryTypeTranslationsMap.get(scope) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'rule-node-config.attributes-scope-value' | translate }}</mat-label> |
||||
|
<input type="text" matInput readonly disabled [ngModel]="deleteAttributesConfigForm.get('scope').value" [ngModelOptions]="{standalone: true}"> |
||||
|
<button type="button" |
||||
|
matSuffix |
||||
|
mat-icon-button |
||||
|
aria-label="Copy" |
||||
|
ngxClipboard |
||||
|
[cbContent]="deleteAttributesConfigForm.get('scope').value" |
||||
|
matTooltip="{{ 'rule-node-config.attributes-scope-value-copy' | translate }}"> |
||||
|
<mat-icon aria-hidden="false" |
||||
|
aria-label="help-icon">content_copy |
||||
|
</mat-icon> |
||||
|
</button> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label>{{ 'rule-node-config.attributes-keys' | translate }}</mat-label> |
||||
|
<mat-chip-grid formControlName="keys" #attributeChipList> |
||||
|
<mat-chip-row |
||||
|
*ngFor="let key of deleteAttributesConfigForm.get('keys').value;" |
||||
|
(removed)="removeKey(key)"> |
||||
|
{{key}} |
||||
|
<mat-icon matChipRemove>close</mat-icon> |
||||
|
</mat-chip-row> |
||||
|
<input matInput type="text" |
||||
|
[matChipInputFor]="attributeChipList" |
||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" |
||||
|
(matChipInputTokenEnd)="addKey($event)" |
||||
|
[matChipInputAddOnBlur]="true"> |
||||
|
</mat-chip-grid> |
||||
|
<mat-error *ngIf="deleteAttributesConfigForm.get('keys').hasError('required')">{{ 'rule-node-config.attributes-keys-required' | translate }}</mat-error> |
||||
|
<mat-hint translate>rule-node-config.general-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
|
||||
|
<section class="tb-form-panel stroked"> |
||||
|
<mat-expansion-panel class="tb-settings"> |
||||
|
<mat-expansion-panel-header> |
||||
|
<mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.send-attributes-deleted-notification-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sendAttributesDeletedNotification"> |
||||
|
{{ 'rule-node-config.send-attributes-deleted-notification' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.notify-device-on-delete-hint' | translate }}" |
||||
|
*ngIf="deleteAttributesConfigForm.get('scope').value === attributeScopeMap.SHARED_SCOPE" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="notifyDevice"> |
||||
|
{{ 'rule-node-config.notify-device' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,91 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, ViewChild } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { MatChipGrid, MatChipInputEvent } from '@angular/material/chips'; |
||||
|
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; |
||||
|
import { AttributeScope, telemetryTypeTranslations } from '@shared/models/telemetry/telemetry.models'; |
||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-delete-attributes-config', |
||||
|
templateUrl: './delete-attributes-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeleteAttributesConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
@ViewChild('attributeChipList') attributeChipList: MatChipGrid; |
||||
|
|
||||
|
deleteAttributesConfigForm: UntypedFormGroup; |
||||
|
attributeScopeMap = AttributeScope; |
||||
|
attributeScopes = Object.keys(AttributeScope); |
||||
|
telemetryTypeTranslationsMap = telemetryTypeTranslations; |
||||
|
separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.deleteAttributesConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deleteAttributesConfigForm = this.fb.group({ |
||||
|
scope: [configuration ? configuration.scope : null, [Validators.required]], |
||||
|
keys: [configuration ? configuration.keys : null, [Validators.required]], |
||||
|
sendAttributesDeletedNotification: [configuration ? configuration.sendAttributesDeletedNotification : false, []], |
||||
|
notifyDevice: [configuration ? configuration.notifyDevice : false, []] |
||||
|
}); |
||||
|
|
||||
|
this.deleteAttributesConfigForm.get('scope').valueChanges.pipe( |
||||
|
takeUntilDestroyed(this.destroyRef) |
||||
|
).subscribe((value) => { |
||||
|
if (value !== AttributeScope.SHARED_SCOPE) { |
||||
|
this.deleteAttributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeKey(key: string): void { |
||||
|
const keys: string[] = this.deleteAttributesConfigForm.get('keys').value; |
||||
|
const index = keys.indexOf(key); |
||||
|
if (index >= 0) { |
||||
|
keys.splice(index, 1); |
||||
|
this.deleteAttributesConfigForm.get('keys').patchValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addKey(event: MatChipInputEvent): void { |
||||
|
const input = event.input; |
||||
|
let value = event.value; |
||||
|
if ((value || '').trim()) { |
||||
|
value = value.trim(); |
||||
|
let keys: string[] = this.deleteAttributesConfigForm.get('keys').value; |
||||
|
if (!keys || keys.indexOf(value) === -1) { |
||||
|
if (!keys) { |
||||
|
keys = []; |
||||
|
} |
||||
|
keys.push(value); |
||||
|
this.deleteAttributesConfigForm.get('keys').patchValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
if (input) { |
||||
|
input.value = ''; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deleteRelationConfigForm" class="tb-form-panel no-padding no-border"> |
||||
|
<div class="tb-form-panel stroked no-padding-bottom"> |
||||
|
<div class="tb-form-panel-title" translate>rule-node-config.relation-parameters</div> |
||||
|
<div class="flex flex-col"> |
||||
|
<mat-form-field class="mat-block" hideRequiredMarker> |
||||
|
<mat-label translate>relation.direction</mat-label> |
||||
|
<mat-select required matInput formControlName="direction"> |
||||
|
<mat-option *ngFor="let type of directionTypes" [value]="type"> |
||||
|
{{ directionTypeTranslations.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<tb-relation-type-autocomplete |
||||
|
required |
||||
|
formControlName="relationType"> |
||||
|
</tb-relation-type-autocomplete> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<div tb-hint-tooltip-icon="{{ 'rule-node-config.delete-relation-with-specific-entity-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="deleteForSingleEntity"> |
||||
|
{{ 'rule-node-config.delete-relation-with-specific-entity' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div *ngIf="deleteRelationConfigForm.get('deleteForSingleEntity').value"> |
||||
|
<div class="flex flex-row gap-2.5"> |
||||
|
<tb-entity-type-select |
||||
|
class="flex-1" |
||||
|
showLabel |
||||
|
required |
||||
|
[allowedEntityTypes]="allowedEntityTypes" |
||||
|
formControlName="entityType"> |
||||
|
</tb-entity-type-select> |
||||
|
<mat-form-field *ngIf="deleteRelationConfigForm.get('entityType').value && deleteRelationConfigForm.get('entityType').value !== entityType.TENANT" |
||||
|
class="mat-block flex-1"> |
||||
|
<mat-label>{{ entityTypeNamePatternTranslation.get(deleteRelationConfigForm.get('entityType').value) | translate }}</mat-label> |
||||
|
<input required matInput formControlName="entityNamePattern"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<tb-example-hint *ngIf="deleteRelationConfigForm.get('entityType').value && deleteRelationConfigForm.get('entityType').value !== entityType.TENANT" |
||||
|
[hintText]="'rule-node-config.kv-map-single-pattern-hint'"></tb-example-hint> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,101 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { EntityType } from '@app/shared/models/entity-type.models'; |
||||
|
import { EntitySearchDirection } from '@app/shared/models/relation.models'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-delete-relation-config', |
||||
|
templateUrl: './delete-relation-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeleteRelationConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
directionTypes = Object.keys(EntitySearchDirection); |
||||
|
|
||||
|
directionTypeTranslations = new Map<EntitySearchDirection, string>( |
||||
|
[ |
||||
|
[EntitySearchDirection.FROM, 'rule-node-config.del-relation-direction-from'], |
||||
|
[EntitySearchDirection.TO, 'rule-node-config.del-relation-direction-to'], |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
entityTypeNamePatternTranslation = new Map<EntityType, string>( |
||||
|
[ |
||||
|
[EntityType.DEVICE, 'rule-node-config.device-name-pattern'], |
||||
|
[EntityType.ASSET, 'rule-node-config.asset-name-pattern'], |
||||
|
[EntityType.ENTITY_VIEW, 'rule-node-config.entity-view-name-pattern'], |
||||
|
[EntityType.CUSTOMER, 'rule-node-config.customer-title-pattern'], |
||||
|
[EntityType.USER, 'rule-node-config.user-name-pattern'], |
||||
|
[EntityType.DASHBOARD, 'rule-node-config.dashboard-name-pattern'], |
||||
|
[EntityType.EDGE, 'rule-node-config.edge-name-pattern'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
entityType = EntityType; |
||||
|
|
||||
|
allowedEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.TENANT, |
||||
|
EntityType.CUSTOMER, EntityType.USER, EntityType.DASHBOARD, EntityType.EDGE]; |
||||
|
|
||||
|
deleteRelationConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.deleteRelationConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deleteRelationConfigForm = this.fb.group({ |
||||
|
deleteForSingleEntity: [configuration ? configuration.deleteForSingleEntity : false, []], |
||||
|
direction: [configuration ? configuration.direction : null, [Validators.required]], |
||||
|
entityType: [configuration ? configuration.entityType : null, []], |
||||
|
entityNamePattern: [configuration ? configuration.entityNamePattern : null, []], |
||||
|
relationType: [configuration ? configuration.relationType : null, [Validators.required]] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['deleteForSingleEntity', 'entityType']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const deleteForSingleEntity: boolean = this.deleteRelationConfigForm.get('deleteForSingleEntity').value; |
||||
|
const entityType: EntityType = this.deleteRelationConfigForm.get('entityType').value; |
||||
|
if (deleteForSingleEntity) { |
||||
|
this.deleteRelationConfigForm.get('entityType').setValidators([Validators.required]); |
||||
|
} else { |
||||
|
this.deleteRelationConfigForm.get('entityType').setValidators([]); |
||||
|
} |
||||
|
if (deleteForSingleEntity && entityType && entityType !== EntityType.TENANT) { |
||||
|
this.deleteRelationConfigForm.get('entityNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]); |
||||
|
} else { |
||||
|
this.deleteRelationConfigForm.get('entityNamePattern').setValidators([]); |
||||
|
} |
||||
|
this.deleteRelationConfigForm.get('entityType').updateValueAndValidity({emitEvent: false}); |
||||
|
this.deleteRelationConfigForm.get('entityNamePattern').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
configuration.entityNamePattern = configuration.entityNamePattern ? configuration.entityNamePattern.trim() : null; |
||||
|
return configuration; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deviceProfile" class="tb-form-panel stroked"> |
||||
|
<div class="tb-form-hint tb-primary-fill" translate>rule-node-config.device-profile-node-hint</div> |
||||
|
<div tb-hint-tooltip-icon="{{'rule-node-config.persist-alarm-rules-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding slide-toggle"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="persistAlarmRulesState"> |
||||
|
{{ 'rule-node-config.persist-alarm-rules' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{'rule-node-config.fetch-alarm-rules-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding slide-toggle"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="fetchAlarmRulesStateOnStart"> |
||||
|
{{ 'rule-node-config.fetch-alarm-rules' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,59 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-device-profile-config', |
||||
|
templateUrl: './device-profile-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeviceProfileConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
deviceProfile: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.deviceProfile; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deviceProfile = this.fb.group({ |
||||
|
persistAlarmRulesState: [configuration ? configuration.persistAlarmRulesState : false], |
||||
|
fetchAlarmRulesStateOnStart: [configuration ? configuration.fetchAlarmRulesStateOnStart : false] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['persistAlarmRulesState']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
if (this.deviceProfile.get('persistAlarmRulesState').value) { |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').enable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').setValue(false, {emitEvent: false}); |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').disable({emitEvent: false}); |
||||
|
} |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deviceState"> |
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label>{{ 'rule-node-config.select-device-connectivity-event' | translate }}</mat-label> |
||||
|
<mat-select formControlName="event"> |
||||
|
<mat-option *ngFor="let eventOption of eventOptions" [value]="eventOption"> |
||||
|
{{ messageTypeNames.get(eventOption) }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
@ -0,0 +1,64 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
import { isDefinedAndNotNull } from '@core/public-api'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { |
||||
|
MessageType, |
||||
|
messageTypeNames, |
||||
|
RuleNodeConfiguration, |
||||
|
RuleNodeConfigurationComponent |
||||
|
} from '@shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-device-state-config', |
||||
|
templateUrl: './device-state-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeviceStateConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
deviceState: FormGroup; |
||||
|
|
||||
|
public messageTypeNames = messageTypeNames; |
||||
|
public eventOptions: MessageType[] = [ |
||||
|
MessageType.CONNECT_EVENT, |
||||
|
MessageType.ACTIVITY_EVENT, |
||||
|
MessageType.DISCONNECT_EVENT, |
||||
|
MessageType.INACTIVITY_EVENT |
||||
|
]; |
||||
|
|
||||
|
constructor(private fb: FormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): FormGroup { |
||||
|
return this.deviceState; |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
return { |
||||
|
event: isDefinedAndNotNull(configuration?.event) ? configuration.event : MessageType.ACTIVITY_EVENT |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deviceState = this.fb.group({ |
||||
|
event: [configuration.event, [Validators.required]] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="generatorConfigForm" class="tb-form-panel no-border no-padding"> |
||||
|
<div class="tb-form-panel no-padding-bottom stroked"> |
||||
|
<div class="tb-form-panel-title" translate>rule-node-config.generation-parameters</div> |
||||
|
<div class="tb-form-row no-border no-padding tb-standard-fields column-xs"> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label translate>rule-node-config.message-count</mat-label> |
||||
|
<input required type="number" min="0" step="1" matInput formControlName="msgCount"> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('msgCount').hasError('required')"> |
||||
|
{{ 'rule-node-config.message-count-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('msgCount').hasError('min')"> |
||||
|
{{ 'rule-node-config.min-message-count-message' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label translate>rule-node-config.generation-frequency-seconds</mat-label> |
||||
|
<input required type="number" min="1" step="1" matInput formControlName="periodInSeconds"> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('periodInSeconds').hasError('required')"> |
||||
|
{{ 'rule-node-config.generation-frequency-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('periodInSeconds').hasError('min')"> |
||||
|
{{ 'rule-node-config.min-generation-frequency-message' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="tb-form-panel no-padding-bottom stroked"> |
||||
|
<div class="tb-form-panel-title" translate>rule-node-config.originator</div> |
||||
|
<tb-entity-select class="flex-1" |
||||
|
required="true" |
||||
|
useAliasEntityTypes="true" |
||||
|
[allowedEntityTypes]="allowedEntityTypes" |
||||
|
[additionEntityTypes]="additionEntityTypes" |
||||
|
formControlName="originator"> |
||||
|
</tb-entity-select> |
||||
|
</div> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<mat-expansion-panel class="tb-settings" expanded> |
||||
|
<mat-expansion-panel-header> |
||||
|
<mat-panel-title translate>rule-node-config.generator-function</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<tb-js-func *ngIf="generatorConfigForm.get('scriptLang').value === scriptLanguage.JS" |
||||
|
#jsFuncComponent |
||||
|
formControlName="jsScript" |
||||
|
functionName="Generate" |
||||
|
[functionArgs]="['prevMsg', 'prevMetadata', 'prevMsgType']" |
||||
|
helpId="rulenode/generator_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<tb-toggle-select toolbarPrefixButton formControlName="scriptLang" appearance="fill" *ngIf="tbelEnabled"> |
||||
|
<tb-toggle-option [value]="scriptLanguage.TBEL"> |
||||
|
{{ 'rule-node-config.script-lang-tbel' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
<tb-toggle-option [value]="scriptLanguage.JS"> |
||||
|
{{ 'rule-node-config.script-lang-js' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
</tb-toggle-select> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<tb-js-func *ngIf="generatorConfigForm.get('scriptLang').value === scriptLanguage.TBEL" |
||||
|
#tbelFuncComponent |
||||
|
formControlName="tbelScript" |
||||
|
functionName="Generate" |
||||
|
[functionArgs]="['prevMsg', 'prevMetadata', 'prevMsgType']" |
||||
|
[disableUndefinedCheck]="true" |
||||
|
[scriptLanguage]="scriptLanguage.TBEL" |
||||
|
helpId="rulenode/tbel/generator_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<tb-toggle-select toolbarPrefixButton formControlName="scriptLang" appearance="fill"> |
||||
|
<tb-toggle-option [value]="scriptLanguage.TBEL"> |
||||
|
{{ 'rule-node-config.script-lang-tbel' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
<tb-toggle-option [value]="scriptLanguage.JS"> |
||||
|
{{ 'rule-node-config.script-lang-js' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
</tb-toggle-select> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<div class="flex flex-row" style="padding-bottom: 16px;"> |
||||
|
<button mat-button mat-raised-button color="primary" (click)="testScript()"> |
||||
|
{{ testScriptLabel | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,51 @@ |
|||||
|
/** |
||||
|
* 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 { |
||||
|
::ng-deep { |
||||
|
.mat-button-toggle-group { |
||||
|
min-width: 120px; |
||||
|
height: 24px !important; |
||||
|
.mat-button-toggle { |
||||
|
font-size: 0; |
||||
|
.mat-button-toggle-button { |
||||
|
height: 20px!important; |
||||
|
line-height: 20px !important; |
||||
|
border: none !important; |
||||
|
.mat-button-toggle-label-content { |
||||
|
font-size: 14px !important; |
||||
|
line-height: 20px !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.tb-entity-select { |
||||
|
@media screen and (min-width: 599px) { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
gap: 16px; |
||||
|
} |
||||
|
tb-entity-type-select { |
||||
|
flex: 1; |
||||
|
} |
||||
|
tb-entity-autocomplete { |
||||
|
flex: 1; |
||||
|
mat-form-field { |
||||
|
width: 100% !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, EventEmitter, ViewChild } from '@angular/core'; |
||||
|
import { getCurrentAuthState, isDefinedAndNotNull, NodeScriptTestService } from '@core/public-api'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { |
||||
|
RuleNodeConfiguration, |
||||
|
RuleNodeConfigurationComponent, |
||||
|
ScriptLanguage |
||||
|
} from '@app/shared/models/rule-node.models'; |
||||
|
import type { JsFuncComponent } from '@app/shared/components/js-func.component'; |
||||
|
import { EntityType } from '@app/shared/models/entity-type.models'; |
||||
|
import { DebugRuleNodeEventBody } from '@shared/models/event.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-generator-config', |
||||
|
templateUrl: './generator-config.component.html', |
||||
|
styleUrls: ['generator-config.component.scss'] |
||||
|
}) |
||||
|
export class GeneratorConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; |
||||
|
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; |
||||
|
|
||||
|
generatorConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; |
||||
|
|
||||
|
scriptLanguage = ScriptLanguage; |
||||
|
|
||||
|
changeScript: EventEmitter<void> = new EventEmitter<void>(); |
||||
|
|
||||
|
allowedEntityTypes = [ |
||||
|
EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.CUSTOMER, |
||||
|
EntityType.USER, EntityType.DASHBOARD |
||||
|
]; |
||||
|
|
||||
|
additionEntityTypes = { |
||||
|
TENANT: this.translate.instant('rule-node-config.current-tenant'), |
||||
|
RULE_NODE: this.translate.instant('rule-node-config.current-rule-node') |
||||
|
}; |
||||
|
|
||||
|
readonly hasScript = true; |
||||
|
|
||||
|
readonly testScriptLabel = 'rule-node-config.test-generator-function'; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder, |
||||
|
private nodeScriptTestService: NodeScriptTestService, |
||||
|
private translate: TranslateService) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.generatorConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.generatorConfigForm = this.fb.group({ |
||||
|
msgCount: [configuration ? configuration.msgCount : null, [Validators.required, Validators.min(0)]], |
||||
|
periodInSeconds: [configuration ? configuration.periodInSeconds : null, [Validators.required, Validators.min(1)]], |
||||
|
originator: [configuration ? configuration.originator : {id: null, entityType: EntityType.RULE_NODE}, []], |
||||
|
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]], |
||||
|
jsScript: [configuration ? configuration.jsScript : null, []], |
||||
|
tbelScript: [configuration ? configuration.tbelScript : null, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['scriptLang']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
let scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { |
||||
|
scriptLang = ScriptLanguage.JS; |
||||
|
this.generatorConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); |
||||
|
setTimeout(() => {this.generatorConfigForm.updateValueAndValidity({emitEvent: true});}); |
||||
|
} |
||||
|
this.generatorConfigForm.get('jsScript').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); |
||||
|
this.generatorConfigForm.get('jsScript').updateValueAndValidity({emitEvent}); |
||||
|
this.generatorConfigForm.get('tbelScript').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); |
||||
|
this.generatorConfigForm.get('tbelScript').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
return { |
||||
|
msgCount: isDefinedAndNotNull(configuration?.msgCount) ? configuration?.msgCount : 0, |
||||
|
periodInSeconds: isDefinedAndNotNull(configuration?.periodInSeconds) ? configuration?.periodInSeconds : 1, |
||||
|
originator: { |
||||
|
id: isDefinedAndNotNull(configuration?.originatorId) ? configuration?.originatorId : null, |
||||
|
entityType: isDefinedAndNotNull(configuration?.originatorType) ? configuration?.originatorType : EntityType.RULE_NODE |
||||
|
}, |
||||
|
scriptLang: isDefinedAndNotNull(configuration?.scriptLang) ? configuration?.scriptLang : ScriptLanguage.JS, |
||||
|
tbelScript: isDefinedAndNotNull(configuration?.tbelScript) ? configuration?.tbelScript : null, |
||||
|
jsScript: isDefinedAndNotNull(configuration?.jsScript) ? configuration?.jsScript : null, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
if (configuration.originator) { |
||||
|
configuration.originatorId = configuration.originator.id; |
||||
|
configuration.originatorType = configuration.originator.entityType; |
||||
|
} else { |
||||
|
configuration.originatorId = null; |
||||
|
configuration.originatorType = null; |
||||
|
} |
||||
|
delete configuration.originator; |
||||
|
return configuration; |
||||
|
} |
||||
|
|
||||
|
testScript(debugEventBody?: DebugRuleNodeEventBody) { |
||||
|
const scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value; |
||||
|
const scriptField = scriptLang === ScriptLanguage.JS ? 'jsScript' : 'tbelScript'; |
||||
|
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/generator_node_script_fn' : 'rulenode/tbel/generator_node_script_fn'; |
||||
|
const script: string = this.generatorConfigForm.get(scriptField).value; |
||||
|
this.nodeScriptTestService.testNodeScript( |
||||
|
script, |
||||
|
'generate', |
||||
|
this.translate.instant('rule-node-config.generator'), |
||||
|
'Generate', |
||||
|
['prevMsg', 'prevMetadata', 'prevMsgType'], |
||||
|
this.ruleNodeId, |
||||
|
helpId, |
||||
|
scriptLang, |
||||
|
debugEventBody |
||||
|
).subscribe((theScript) => { |
||||
|
if (theScript) { |
||||
|
this.generatorConfigForm.get(scriptField).setValue(theScript); |
||||
|
this.changeScript.emit(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected onValidate() { |
||||
|
const scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.JS) { |
||||
|
this.jsFuncComponent.validateOnSubmit(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue