Rule node test script dialog
This commit is contained in:
		
							parent
							
								
									c6c53fc77c
								
							
						
					
					
						commit
						642c9fabe6
					
				@ -15,7 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const ruleNodeUiforwardHost = 'localhost';
 | 
			
		||||
const ruleNodeUiforwardPort = 8080;
 | 
			
		||||
const ruleNodeUiforwardPort = 5000;
 | 
			
		||||
 | 
			
		||||
const PROXY_CONFIG = {
 | 
			
		||||
  '/api': {
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,7 @@ import { catchError, map, mergeMap } from 'rxjs/operators';
 | 
			
		||||
import { TranslateService } from '@ngx-translate/core';
 | 
			
		||||
import { EntityType } from '@shared/models/entity-type.models';
 | 
			
		||||
import { deepClone, snakeCase } from '@core/utils';
 | 
			
		||||
import { DebugRuleNodeEventBody } from '@app/shared/models/event.models';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -170,6 +171,10 @@ export class RuleChainService {
 | 
			
		||||
    return component.configurationDescriptor.nodeDefinition.customRelations;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getLatestRuleNodeDebugInput(ruleNodeId: string, config?: RequestConfig): Observable<DebugRuleNodeEventBody> {
 | 
			
		||||
    return this.http.get<DebugRuleNodeEventBody>(`/api/ruleNode/${ruleNodeId}/debugIn`, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private resolveTargetRuleChains(ruleChainConnections: Array<RuleChainConnectionInfo>): Observable<{[ruleChainId: string]: RuleChain}> {
 | 
			
		||||
    if (ruleChainConnections && ruleChainConnections.length) {
 | 
			
		||||
      const tasks: Observable<RuleChain>[] = [];
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,93 @@
 | 
			
		||||
<!--
 | 
			
		||||
 | 
			
		||||
    Copyright © 2016-2019 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.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<form #nodeScriptTestForm="ngForm" [formGroup]="nodeScriptTestFormGroup" (ngSubmit)="save()" style="width: 800px;">
 | 
			
		||||
  <mat-toolbar fxLayout="row" color="primary">
 | 
			
		||||
    <h2>{{ 'rulenode.test-script-function' | translate }}</h2>
 | 
			
		||||
    <span fxFlex></span>
 | 
			
		||||
    <button mat-button mat-icon-button
 | 
			
		||||
            (click)="cancel()"
 | 
			
		||||
            type="button">
 | 
			
		||||
      <mat-icon class="material-icons">close</mat-icon>
 | 
			
		||||
    </button>
 | 
			
		||||
  </mat-toolbar>
 | 
			
		||||
  <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
 | 
			
		||||
  </mat-progress-bar>
 | 
			
		||||
  <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
 | 
			
		||||
  <div mat-dialog-content fxFlex style="position: relative;">
 | 
			
		||||
    <div class="tb-absolute-fill">
 | 
			
		||||
      <div #topPanel class="tb-split tb-split-vertical">
 | 
			
		||||
        <div #topLeftPanel class="tb-split tb-content">
 | 
			
		||||
          <div class="tb-resize-container">
 | 
			
		||||
            <div class="tb-editor-area-title-panel">
 | 
			
		||||
              <label translate>rulenode.message</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            TODO: payloadForm
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div #topRightPanel class="tb-split tb-content">
 | 
			
		||||
          <div tb-toast toastTarget="metadataPanel" class="tb-resize-container">
 | 
			
		||||
            <div class="tb-editor-area-title-panel">
 | 
			
		||||
              <label translate>rulenode.metadata</label>
 | 
			
		||||
              TODO: metadataForm
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div #bottomPanel class="tb-split tb-split-vertical">
 | 
			
		||||
        <div #bottomLeftPanel class="tb-split tb-content">
 | 
			
		||||
          <div class="tb-resize-container">
 | 
			
		||||
            <div class="tb-editor-area-title-panel tb-js-function">
 | 
			
		||||
              <label>{{ functionTitle }}</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            TODO: funcBodyForm
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div #bottomRightPanel class="tb-split tb-content">
 | 
			
		||||
          <div class="tb-resize-container">
 | 
			
		||||
            <div class="tb-editor-area-title-panel">
 | 
			
		||||
              <label translate>rulenode.output</label>
 | 
			
		||||
            </div>
 | 
			
		||||
            TODO: output
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div mat-dialog-actions fxLayout="row">
 | 
			
		||||
    <button mat-button mat-raised-button color="primary"
 | 
			
		||||
            type="button"
 | 
			
		||||
            (click)="test()"
 | 
			
		||||
            cdkFocusInitial
 | 
			
		||||
            [disabled]="(isLoading$ | async) || nodeScriptTestFormGroup.invalid">
 | 
			
		||||
      {{ 'rulenode.test' | translate }}
 | 
			
		||||
    </button>
 | 
			
		||||
    <span fxFlex></span>
 | 
			
		||||
    <button mat-button mat-raised-button color="primary"
 | 
			
		||||
            type="submit"
 | 
			
		||||
            [disabled]="(isLoading$ | async) || nodeScriptTestFormGroup.get('funcBody').invalid || !nodeScriptTestFormGroup.get('funcBody').dirty">
 | 
			
		||||
      {{ 'action.save' | translate }}
 | 
			
		||||
    </button>
 | 
			
		||||
    <button mat-button color="primary"
 | 
			
		||||
            style="margin-right: 20px;"
 | 
			
		||||
            type="button"
 | 
			
		||||
            [disabled]="(isLoading$ | async)"
 | 
			
		||||
            (click)="cancel()">
 | 
			
		||||
      {{ 'action.cancel' | translate }}
 | 
			
		||||
    </button>
 | 
			
		||||
  </div>
 | 
			
		||||
</form>
 | 
			
		||||
@ -0,0 +1,93 @@
 | 
			
		||||
:host {
 | 
			
		||||
  .tb-split {
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-content {
 | 
			
		||||
    padding-top: 5px;
 | 
			
		||||
    padding-left: 5px;
 | 
			
		||||
    border: 1px solid #c0c0c0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .gutter {
 | 
			
		||||
    background-color: #eee;
 | 
			
		||||
 | 
			
		||||
    background-repeat: no-repeat;
 | 
			
		||||
    background-position: 50%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .gutter {
 | 
			
		||||
    background-color: #eee;
 | 
			
		||||
    background-repeat: no-repeat;
 | 
			
		||||
    background-position: 50%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .gutter.gutter-horizontal {
 | 
			
		||||
    cursor: col-resize;
 | 
			
		||||
    background-image: url("../../../../assets/split.js/grips/vertical.png");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .gutter.gutter-vertical {
 | 
			
		||||
    cursor: row-resize;
 | 
			
		||||
    background-image: url("../../../../assets/split.js/grips/horizontal.png");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-split.tb-split-horizontal,
 | 
			
		||||
  .gutter.gutter-horizontal {
 | 
			
		||||
    float: left;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-split.tb-split-vertical {
 | 
			
		||||
    display: flex;
 | 
			
		||||
 | 
			
		||||
    .tb-split.tb-content {
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  div.tb-editor-area-title-panel {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 13px;
 | 
			
		||||
    right: 40px;
 | 
			
		||||
    z-index: 5;
 | 
			
		||||
    font-size: .8rem;
 | 
			
		||||
    font-weight: 500;
 | 
			
		||||
 | 
			
		||||
    &.tb-js-function {
 | 
			
		||||
      right: 80px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    label {
 | 
			
		||||
      padding: 4px;
 | 
			
		||||
      color: #00acc1;
 | 
			
		||||
      text-transform: uppercase;
 | 
			
		||||
      background: rgba(220, 220, 220, .35);
 | 
			
		||||
      border-radius: 5px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .mat-button {
 | 
			
		||||
      min-width: 32px;
 | 
			
		||||
      min-height: 15px;
 | 
			
		||||
      padding: 4px;
 | 
			
		||||
      margin: 0;
 | 
			
		||||
      font-size: .8rem;
 | 
			
		||||
      line-height: 15px;
 | 
			
		||||
      color: #7b7b7b;
 | 
			
		||||
      background: rgba(220, 220, 220, .35);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .tb-resize-container {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
 | 
			
		||||
    .ace_editor {
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,176 @@
 | 
			
		||||
///
 | 
			
		||||
/// Copyright © 2016-2019 The Thingsboard Authors
 | 
			
		||||
///
 | 
			
		||||
/// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
/// you may not use this file except in compliance with the License.
 | 
			
		||||
/// You may obtain a copy of the License at
 | 
			
		||||
///
 | 
			
		||||
///     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
///
 | 
			
		||||
/// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
/// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
/// See the License for the specific language governing permissions and
 | 
			
		||||
/// limitations under the License.
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  AfterViewInit,
 | 
			
		||||
  Component,
 | 
			
		||||
  ElementRef,
 | 
			
		||||
  Inject,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  QueryList,
 | 
			
		||||
  SkipSelf,
 | 
			
		||||
  ViewChild,
 | 
			
		||||
  ViewChildren
 | 
			
		||||
} from '@angular/core';
 | 
			
		||||
import { ErrorStateMatcher, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
 | 
			
		||||
import { Store } from '@ngrx/store';
 | 
			
		||||
import { AppState } from '@core/core.state';
 | 
			
		||||
import {
 | 
			
		||||
  FormBuilder,
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormGroupDirective,
 | 
			
		||||
  NgForm,
 | 
			
		||||
  ValidatorFn,
 | 
			
		||||
  Validators
 | 
			
		||||
} from '@angular/forms';
 | 
			
		||||
import { combineLatest, Observable, of } from 'rxjs';
 | 
			
		||||
import { Router } from '@angular/router';
 | 
			
		||||
import { DialogComponent } from '@app/shared/components/dialog.component';
 | 
			
		||||
import {
 | 
			
		||||
  toCustomAction,
 | 
			
		||||
  WidgetActionCallbacks,
 | 
			
		||||
  WidgetActionDescriptorInfo,
 | 
			
		||||
  WidgetActionsData
 | 
			
		||||
} from '@home/components/widget/action/manage-widget-actions.component.models';
 | 
			
		||||
import { UtilsService } from '@core/services/utils.service';
 | 
			
		||||
import { WidgetActionSource, WidgetActionType, widgetActionTypeTranslationMap } from '@shared/models/widget.models';
 | 
			
		||||
import { map, mergeMap, startWith, tap } from 'rxjs/operators';
 | 
			
		||||
import { DashboardService } from '@core/http/dashboard.service';
 | 
			
		||||
import { Dashboard } from '@shared/models/dashboard.models';
 | 
			
		||||
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
 | 
			
		||||
 | 
			
		||||
export interface NodeScriptTestDialogData {
 | 
			
		||||
  script: string;
 | 
			
		||||
  scriptType: string;
 | 
			
		||||
  functionTitle: string;
 | 
			
		||||
  functionName: string;
 | 
			
		||||
  argNames: string[];
 | 
			
		||||
  msg?: any;
 | 
			
		||||
  metadata?: any;
 | 
			
		||||
  msgType?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-node-script-test-dialog',
 | 
			
		||||
  templateUrl: './node-script-test-dialog.component.html',
 | 
			
		||||
  providers: [{provide: ErrorStateMatcher, useExisting: NodeScriptTestDialogComponent}],
 | 
			
		||||
  styleUrls: ['./node-script-test-dialog.component.scss']
 | 
			
		||||
})
 | 
			
		||||
export class NodeScriptTestDialogComponent extends DialogComponent<NodeScriptTestDialogComponent,
 | 
			
		||||
  string> implements OnInit, AfterViewInit, ErrorStateMatcher {
 | 
			
		||||
 | 
			
		||||
  @ViewChildren('topPanel')
 | 
			
		||||
  topPanelElmRef: QueryList<ElementRef<HTMLElement>>;
 | 
			
		||||
 | 
			
		||||
  @ViewChildren('topLeftPanel')
 | 
			
		||||
  topLeftPanelElmRef: QueryList<ElementRef<HTMLElement>>;
 | 
			
		||||
 | 
			
		||||
  @ViewChildren('topRightPanel')
 | 
			
		||||
  topRightPanelElmRef: QueryList<ElementRef<HTMLElement>>;
 | 
			
		||||
 | 
			
		||||
  @ViewChildren('bottomPanel')
 | 
			
		||||
  bottomPanelElmRef: QueryList<ElementRef<HTMLElement>>;
 | 
			
		||||
 | 
			
		||||
  @ViewChildren('bottomLeftPanel')
 | 
			
		||||
  bottomLeftPanelElmRef: QueryList<ElementRef<HTMLElement>>;
 | 
			
		||||
 | 
			
		||||
  @ViewChildren('bottomLeftPanel')
 | 
			
		||||
  bottomRightPanelElmRef: QueryList<ElementRef<HTMLElement>>;
 | 
			
		||||
 | 
			
		||||
  nodeScriptTestFormGroup: FormGroup;
 | 
			
		||||
 | 
			
		||||
  functionTitle: string;
 | 
			
		||||
 | 
			
		||||
  submitted = false;
 | 
			
		||||
 | 
			
		||||
  constructor(protected store: Store<AppState>,
 | 
			
		||||
              protected router: Router,
 | 
			
		||||
              @Inject(MAT_DIALOG_DATA) public data: NodeScriptTestDialogData,
 | 
			
		||||
              @SkipSelf() private errorStateMatcher: ErrorStateMatcher,
 | 
			
		||||
              public dialogRef: MatDialogRef<NodeScriptTestDialogComponent, string>,
 | 
			
		||||
              public fb: FormBuilder) {
 | 
			
		||||
    super(store, router, dialogRef);
 | 
			
		||||
    this.functionTitle = this.data.functionTitle;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.nodeScriptTestFormGroup = this.fb.group({});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngAfterViewInit(): void {
 | 
			
		||||
    combineLatest(this.topPanelElmRef.changes,
 | 
			
		||||
                  this.topLeftPanelElmRef.changes,
 | 
			
		||||
                  this.topRightPanelElmRef.changes,
 | 
			
		||||
                  this.bottomPanelElmRef.changes,
 | 
			
		||||
                  this.bottomLeftPanelElmRef.changes,
 | 
			
		||||
                  this.bottomRightPanelElmRef.changes).subscribe(() => {
 | 
			
		||||
      if (this.topPanelElmRef.length && this.topLeftPanelElmRef.length &&
 | 
			
		||||
          this.topRightPanelElmRef.length && this.bottomPanelElmRef.length &&
 | 
			
		||||
          this.bottomLeftPanelElmRef.length && this.bottomRightPanelElmRef.length) {
 | 
			
		||||
        this.initSplitLayout(this.topPanelElmRef.first.nativeElement,
 | 
			
		||||
                             this.topLeftPanelElmRef.first.nativeElement,
 | 
			
		||||
                             this.topRightPanelElmRef.first.nativeElement,
 | 
			
		||||
                             this.bottomPanelElmRef.first.nativeElement,
 | 
			
		||||
                             this.bottomLeftPanelElmRef.first.nativeElement,
 | 
			
		||||
                             this.bottomRightPanelElmRef.first.nativeElement);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private initSplitLayout(topPanel: any,
 | 
			
		||||
                          topLeftPanel: any,
 | 
			
		||||
                          topRightPanel: any,
 | 
			
		||||
                          bottomPanel: any,
 | 
			
		||||
                          bottomLeftPanel: any,
 | 
			
		||||
                          bottomRightPanel: any) {
 | 
			
		||||
 | 
			
		||||
    Split([topPanel, bottomPanel], {
 | 
			
		||||
      sizes: [35, 65],
 | 
			
		||||
      gutterSize: 8,
 | 
			
		||||
      cursor: 'row-resize',
 | 
			
		||||
      direction: 'vertical'
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    Split([topLeftPanel, topRightPanel], {
 | 
			
		||||
      sizes: [50, 50],
 | 
			
		||||
      gutterSize: 8,
 | 
			
		||||
      cursor: 'col-resize'
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    Split([bottomLeftPanel, bottomRightPanel], {
 | 
			
		||||
      sizes: [50, 50],
 | 
			
		||||
      gutterSize: 8,
 | 
			
		||||
      cursor: 'col-resize'
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
 | 
			
		||||
    const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
 | 
			
		||||
    const customErrorState = !!(control && control.invalid && this.submitted);
 | 
			
		||||
    return originalErrorState || customErrorState;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cancel(): void {
 | 
			
		||||
    this.dialogRef.close(null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  save(): void {
 | 
			
		||||
    this.submitted = true;
 | 
			
		||||
    const script: string = this.nodeScriptTestFormGroup.get('funcBody').value;
 | 
			
		||||
    this.dialogRef.close(script);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -16,14 +16,63 @@
 | 
			
		||||
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { Observable, of } from 'rxjs';
 | 
			
		||||
import { RuleChainService } from '@core/http/rule-chain.service';
 | 
			
		||||
import { map, switchMap } from 'rxjs/operators';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
})
 | 
			
		||||
export class NodeScriptTestService {
 | 
			
		||||
 | 
			
		||||
  testNodeScript(script: string, scriptType: any, functionTitle: string,
 | 
			
		||||
  constructor(private ruleChainService: RuleChainService) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  testNodeScript(script: string, scriptType: string, functionTitle: string,
 | 
			
		||||
                 functionName: string, argNames: string[], ruleNodeId: string): Observable<string> {
 | 
			
		||||
    if (ruleNodeId) {
 | 
			
		||||
      return this.ruleChainService.getLatestRuleNodeDebugInput(ruleNodeId).pipe(
 | 
			
		||||
        switchMap((debugIn) => {
 | 
			
		||||
          let msg: any;
 | 
			
		||||
          let metadata: any;
 | 
			
		||||
          let msgType: string;
 | 
			
		||||
          if (debugIn) {
 | 
			
		||||
            if (debugIn.data) {
 | 
			
		||||
              msg = JSON.parse(debugIn.data);
 | 
			
		||||
            }
 | 
			
		||||
            if (debugIn.metadata) {
 | 
			
		||||
              metadata = JSON.parse(debugIn.metadata);
 | 
			
		||||
            }
 | 
			
		||||
            msgType = debugIn.msgType;
 | 
			
		||||
          }
 | 
			
		||||
          return this.openTestScriptDialog(script, scriptType, functionTitle,
 | 
			
		||||
            functionName, argNames, msg, metadata, msgType);
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      return this.openTestScriptDialog(script, scriptType, functionTitle,
 | 
			
		||||
        functionName, argNames);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private openTestScriptDialog(script: string, scriptType: string,
 | 
			
		||||
                               functionTitle: string, functionName: string, argNames: string[],
 | 
			
		||||
                               msg?: any, metadata?: any, msgType?: string): Observable<string> {
 | 
			
		||||
    if (!msg) {
 | 
			
		||||
      msg = {
 | 
			
		||||
        temperature: 22.4,
 | 
			
		||||
        humidity: 78
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    if (!metadata) {
 | 
			
		||||
      metadata = {
 | 
			
		||||
        deviceType: 'default',
 | 
			
		||||
        deviceName: 'Test Device',
 | 
			
		||||
        ts: new Date().getTime() + ''
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    if (!msgType) {
 | 
			
		||||
      msgType = 'POST_TELEMETRY_REQUEST';
 | 
			
		||||
    }
 | 
			
		||||
    console.log(`testNodeScript TODO: ${script}`);
 | 
			
		||||
    return of(script);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -205,4 +205,10 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate() {
 | 
			
		||||
    if (this.useDefinedDirective()) {
 | 
			
		||||
      this.definedConfigComponent.validate();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@
 | 
			
		||||
            {{ 'rulenode.debug-mode' | translate }}
 | 
			
		||||
          </mat-checkbox>
 | 
			
		||||
        </section>
 | 
			
		||||
        <tb-rule-node-config
 | 
			
		||||
        <tb-rule-node-config #ruleNodeConfigComponent
 | 
			
		||||
          formControlName="configuration"
 | 
			
		||||
          [ruleNodeId]="ruleNode.ruleNodeId?.id"
 | 
			
		||||
          [nodeDefinition]="ruleNode.component.configurationDescriptor.nodeDefinition">
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
/// limitations under the License.
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
 | 
			
		||||
import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core';
 | 
			
		||||
import { PageComponent } from '@shared/components/page.component';
 | 
			
		||||
import { Store } from '@ngrx/store';
 | 
			
		||||
import { AppState } from '@core/core.state';
 | 
			
		||||
@ -24,6 +24,8 @@ import { RuleNodeType } from '@shared/models/rule-node.models';
 | 
			
		||||
import { EntityType } from '@shared/models/entity-type.models';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
import { RuleChainService } from '@core/http/rule-chain.service';
 | 
			
		||||
import { JsonObjectEditComponent } from '@shared/components/json-object-edit.component';
 | 
			
		||||
import { RuleNodeConfigComponent } from './rule-node-config.component';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-rule-node',
 | 
			
		||||
@ -34,6 +36,8 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
 | 
			
		||||
 | 
			
		||||
  @ViewChild('ruleNodeForm', {static: true}) ruleNodeForm: NgForm;
 | 
			
		||||
 | 
			
		||||
  @ViewChild('ruleNodeConfigComponent', {static: false}) ruleNodeConfigComponent: RuleNodeConfigComponent;
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  ruleNode: FcRuleNode;
 | 
			
		||||
 | 
			
		||||
@ -127,4 +131,10 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate() {
 | 
			
		||||
    if (this.ruleNode.component.type !== RuleNodeType.RULE_CHAIN) {
 | 
			
		||||
      this.ruleNodeConfigComponent.validate();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -67,6 +67,7 @@ import { DialogComponent } from '@shared/components/dialog.component';
 | 
			
		||||
import { UtilsService } from '@core/services/utils.service';
 | 
			
		||||
import { EntityService } from '@core/http/entity.service';
 | 
			
		||||
import { AddWidgetDialogComponent, AddWidgetDialogData } from '@home/pages/dashboard/add-widget-dialog.component';
 | 
			
		||||
import { RuleNodeConfigComponent } from '@home/pages/rulechain/rule-node-config.component';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-rulechain-page',
 | 
			
		||||
@ -585,14 +586,17 @@ export class RuleChainPageComponent extends PageComponent
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  saveRuleNode() {
 | 
			
		||||
    this.ruleNodeComponent.ruleNodeFormGroup.markAsPristine();
 | 
			
		||||
    if (this.editingRuleNode.error) {
 | 
			
		||||
      delete this.editingRuleNode.error;
 | 
			
		||||
    this.ruleNodeComponent.validate();
 | 
			
		||||
    if (this.ruleNodeComponent.ruleNodeFormGroup.valid) {
 | 
			
		||||
      this.ruleNodeComponent.ruleNodeFormGroup.markAsPristine();
 | 
			
		||||
      if (this.editingRuleNode.error) {
 | 
			
		||||
        delete this.editingRuleNode.error;
 | 
			
		||||
      }
 | 
			
		||||
      this.ruleChainModel.nodes[this.editingRuleNodeIndex] = this.editingRuleNode;
 | 
			
		||||
      this.editingRuleNode = deepClone(this.editingRuleNode);
 | 
			
		||||
      this.onModelChanged();
 | 
			
		||||
      this.updateRuleNodesHighlight();
 | 
			
		||||
    }
 | 
			
		||||
    this.ruleChainModel.nodes[this.editingRuleNodeIndex] = this.editingRuleNode;
 | 
			
		||||
    this.editingRuleNode = deepClone(this.editingRuleNode);
 | 
			
		||||
    this.onModelChanged();
 | 
			
		||||
    this.updateRuleNodesHighlight();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  saveRuleNodeLink() {
 | 
			
		||||
@ -938,6 +942,8 @@ export interface AddRuleNodeDialogData {
 | 
			
		||||
export class AddRuleNodeDialogComponent extends DialogComponent<AddRuleNodeDialogComponent, FcRuleNode>
 | 
			
		||||
  implements OnInit, ErrorStateMatcher {
 | 
			
		||||
 | 
			
		||||
  @ViewChild('tbRuleNode', {static: true}) ruleNodeDetailsComponent: RuleNodeDetailsComponent;
 | 
			
		||||
 | 
			
		||||
  ruleNode: FcRuleNode;
 | 
			
		||||
  ruleChainId: string;
 | 
			
		||||
 | 
			
		||||
@ -973,6 +979,9 @@ export class AddRuleNodeDialogComponent extends DialogComponent<AddRuleNodeDialo
 | 
			
		||||
 | 
			
		||||
  add(): void {
 | 
			
		||||
    this.submitted = true;
 | 
			
		||||
    this.dialogRef.close(this.ruleNode);
 | 
			
		||||
    this.ruleNodeDetailsComponent.validate();
 | 
			
		||||
    if (this.ruleNodeDetailsComponent.ruleNodeFormGroup.valid) {
 | 
			
		||||
      this.dialogRef.close(this.ruleNode);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -15,3 +15,4 @@
 | 
			
		||||
///
 | 
			
		||||
 | 
			
		||||
export * from './page.component';
 | 
			
		||||
export * from './js-func.component';
 | 
			
		||||
 | 
			
		||||
@ -78,6 +78,7 @@ export interface IRuleNodeConfigurationComponent {
 | 
			
		||||
  ruleNodeId: string;
 | 
			
		||||
  configuration: RuleNodeConfiguration;
 | 
			
		||||
  configurationChanged: Observable<RuleNodeConfiguration>;
 | 
			
		||||
  validate();
 | 
			
		||||
  [key: string]: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -97,11 +98,17 @@ export abstract class RuleNodeConfigurationComponent extends PageComponent imple
 | 
			
		||||
    this.onConfigurationSet(this.configuration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validate() {
 | 
			
		||||
    this.onValidate();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected abstract onConfigurationSet(configuration: RuleNodeConfiguration);
 | 
			
		||||
 | 
			
		||||
  protected notifyConfigurationUpdated(configuration: RuleNodeConfiguration) {
 | 
			
		||||
    this.configurationChangedEmiter.emit(configuration);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected onValidate() {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user