From 15f0e4fa63bb1fd533091d9ab85d3612752c2d27 Mon Sep 17 00:00:00 2001 From: Lvbo163 Date: Wed, 6 Sep 2017 14:32:16 +0800 Subject: [PATCH] support set body parameter by json Rest task's body parameter is definited by json object in swagger definition. Issue-ID: SDC-67 Change-Id: Idb1d96cf9d1135afd7b285105abfdd9a2c50380c Signed-off-by: Lvbo163 --- sdc-workflow-designer-ui/src/app/app.module.ts | 6 + .../editable-property.component.html | 25 +++ .../editable-property.component.ts | 48 +++++ .../parameter-tree/parameter-tree.component.css | 14 ++ .../parameter-tree/parameter-tree.component.html | 54 +++++ .../parameter-tree/parameter-tree.component.ts | 230 +++++++++++++++++++++ .../rest-task-parameters.component.html | 4 + .../rest-task-parameters.component.ts | 9 +- .../property/rest-task/rest-task.component.html | 4 +- .../app/services/swagger-tree-converter.service.ts | 157 ++++++++++++++ .../src/app/services/workflow-config.service.ts | 9 +- .../src/app/shared/shared.module.ts | 3 +- 12 files changed, 557 insertions(+), 6 deletions(-) create mode 100644 sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.html create mode 100644 sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.ts create mode 100644 sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.css create mode 100644 sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.html create mode 100644 sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.ts create mode 100644 sdc-workflow-designer-ui/src/app/services/swagger-tree-converter.service.ts diff --git a/sdc-workflow-designer-ui/src/app/app.module.ts b/sdc-workflow-designer-ui/src/app/app.module.ts index f2577196..32af5d0e 100644 --- a/sdc-workflow-designer-ui/src/app/app.module.ts +++ b/sdc-workflow-designer-ui/src/app/app.module.ts @@ -38,17 +38,22 @@ import { ModalModule } from "ngx-bootstrap/modal"; import { WorkflowConfigService } from "./services/workflow-config.service"; import { RestTaskComponent } from "./components/property/rest-task/rest-task.component"; import { RestTaskParametersComponent } from "./components/property/rest-task/rest-task-parameters/rest-task-parameters.component"; +import { ParameterTreeComponent } from "./components/parameter-tree/parameter-tree.component"; +import { EditablePropertyComponent } from "./components/editable-property/editable-property.component"; +import { SwaggerTreeConverterService } from "./services/swagger-tree-converter.service"; @NgModule({ declarations: [ AppComponent, CanvasComponent, + EditablePropertyComponent, MenuComponent, MicroserviceComponent, MicroserviceDetailComponent, MicroserviceListComponent, NodeComponent, ParameterComponent, + ParameterTreeComponent, PropertiesComponent, RestTaskComponent, RestTaskParametersComponent, @@ -68,6 +73,7 @@ import { RestTaskParametersComponent } from "./components/property/rest-task/res DataAccessService, HttpService, JsPlumbService, + SwaggerTreeConverterService, WorkflowConfigService, WorkflowService ], diff --git a/sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.html b/sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.html new file mode 100644 index 00000000..080a4350 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.html @@ -0,0 +1,25 @@ + + + {{parameter.value}} + + + + + + + + + + diff --git a/sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.ts b/sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.ts new file mode 100644 index 00000000..577a8f18 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/components/editable-property/editable-property.component.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial API and implementation and/or initial documentation + */ + +import { AfterViewInit, Component, EventEmitter, Input, Output } from '@angular/core'; + +import { ValueSource } from '../../model/value-source.enum'; +import { ValueType } from '../../model/value-type.enum'; +import { Parameter} from '../../model/workflow/parameter'; + +/** + * property component presents information of a workflow node. + * the presented information can be edit in this component. + * it may load information dynamically. the content may be different for different node type. + */ +@Component({ + selector: 'b4t-editable-property', + templateUrl: 'editable-property.component.html', +}) +export class EditablePropertyComponent { + @Input() public parameter: Parameter; + @Input() public showLabel: boolean; + @Input() public valueSource: ValueSource[]; + @Output() public parameterChange = new EventEmitter(); + + private editing = false; + + public isEditing(): boolean { + return this.editing; + } + + public startEdit() { + this.editing = true; + } + + public completeEdit() { + this.editing = false; + this.parameterChange.emit(this.parameter); + } +} diff --git a/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.css b/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.css new file mode 100644 index 00000000..cda8b0cc --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.css @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2017 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial API and implementation and/or initial documentation + */ +.ui-treenode-label { + width: calc(100% - 32px) !important; +} diff --git a/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.html b/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.html new file mode 100644 index 00000000..e97968df --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.html @@ -0,0 +1,54 @@ + + + + + + + + + [Array]({{node.children.length}}) {{node.label}}: + [Array]({{node.children.length}}) + + + + + + [Object] {{node.label}}: + [Object] + : + + + + + + + + + + + + [Map] {{node.label}}: + [Map] + : + + + + + + diff --git a/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.ts b/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.ts new file mode 100644 index 00000000..f5f098b7 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/components/parameter-tree/parameter-tree.component.ts @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2017 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial API and implementation and/or initial documentation + */ + +import { Component, Input, OnChanges, Output, SimpleChange, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { TreeNode } from 'primeng/primeng'; + +import { ValueSource } from '../../model/value-source.enum'; +import { ValueType } from '../../model/value-type.enum'; +import { Parameter } from '../../model/workflow/parameter'; +import { RestParameter } from '../../model/workflow/rest-parameter'; +import { RestTask } from '../../model/workflow/rest-task'; +import { SwaggerTreeConverterService } from '../../services/swagger-tree-converter.service'; +import { WorkflowUtil } from '../../util/workflow-util'; + +/** + * parameter tree presents parameter of task node's input and output parameters. + */ +@Component({ + selector: 'b4t-parameter-tree', + styleUrls: ['./parameter-tree.component.css'], + templateUrl: 'parameter-tree.component.html', + encapsulation: ViewEncapsulation.None +}) +export class ParameterTreeComponent implements OnChanges { + @Input() public parameters: TreeNode[]; + @Input() public task: RestTask; + @Input() public defaultValueSource: string; + @Input() public valueSource: ValueSource[]; + + constructor(private swaggerTreeConverterService: SwaggerTreeConverterService) { } + + public ngOnChanges(changes: SimpleChanges) { + const changeParameters = changes["parameters"]; + if (changeParameters && 0 < changeParameters.currentValue.length) { + this.formatParam(changeParameters.currentValue); + } + } + + public getParam(node: any) { + if (undefined === node.parameter.name) { + node.parameter.name = node.label; + node.parameter.valueSource = this.defaultValueSource; + } else { + if (node.parent.parameter.value[node.label]) { + node.parameter.value = node.parent.parameter.value[node.label].value; + node.parameter.valueSource = node.parent.parameter.value[node.label].valueSource; + } else { + const tempParamValue: any = {}; + tempParamValue.value = ''; + tempParamValue.valueSource = this.defaultValueSource; + node.parent.parameter.value[node.label] = tempParamValue; + node.parameter.value = tempParamValue.value; + node.parameter.valueSource = tempParamValue.valueSource; + } + } + return node.parameter; + } + + public paramChange(param: Parameter, node: any) { + if (node.label !== param.name) { + node.label = param.name; + this.propertyKeyChanged(node, param.value); + } + if (node.parent) { + if (node.parent.parameter.value[param.name]) { + node.parent.parameter.value[param.name].value = param.value; + node.parent.parameter.value[param.name].valueSource = param.valueSource; + } else { + node.parent.parameter.value[param.name] = { + value: param.value, + valueSource: param.valueSource + }; + } + } + } + + public getKeyParameter(node: any) { + return new Parameter('key', node.label, ValueSource[ValueSource.String], ValueType[ValueType.String]); + } + + public keyParameterChange(node: any, parameter: Parameter) { + node.label = parameter.value; + this.propertyKeyChanged(node, parameter.value); + } + + public getValueParameter(node: any, key: string) { + const nodeValue = node[key] ? node[key] : { + value: '', + valueSource: ValueSource[ValueSource.String], + }; + node[key] = nodeValue; + return nodeValue; + } + + public valueParameterChange(node: any, key: string, parameter: Parameter) { + node[key].value = parameter.value; + node[key].valueSource = parameter.valueSource; + } + + public addChildNode4DynamicObject(node: any) { + const copyItem = WorkflowUtil.deepClone(node.parameter.additionalProperties); + const key = Object.keys(node.parameter.value).length; + const childrenNode = this.swaggerTreeConverterService + .schema2TreeNode(key, this.task.serviceName, this.task.serviceVersion, copyItem); + + childrenNode.keyEditable = true; + node.parameter.value[key] = childrenNode.parameter.value; + + node.children.push(childrenNode); + } + + public propertyKeyChanged(node: any, newKey: string) { + const value = node.parent.parameter.value[node.label]; + node.parent.parameter.value[newKey] = value; + delete node.parent.parameter.value[node.label]; + + node.label = newKey; + } + + public addChildNode4ObjectArray(node: any) { + const copyItem = WorkflowUtil.deepClone(node.parameter.items); + + const childrenNode = this.swaggerTreeConverterService + .schema2TreeNode( + node.children.length, + this.task.serviceName, + this.task.serviceVersion, + copyItem); + + node.parameter.value.push(childrenNode.parameter.value); + node.children.push(childrenNode); + this.initParam(node); + } + + public deleteTreeNode(node: any) { + // delete data + node.parent.parameter.value.splice(node.label, 1); + node.parent.children.splice(node.label, 1); + + // update node index + node.parent.children.forEach((childNode, index) => childNode.label = index); + } + + public canEditValue(node: any): boolean { + return node.children.length === 0; + } + + public canDelete(node: any) { + const parent = node.parent; + if (parent && + (this.isArrayObject(parent) || this.isDynamicObject(parent))) { + return true; + } else { + return false; + } + } + + public updateObjectValue(node: any, value: string) { + const newValueObj = JSON.parse(value); + for (const key in node.parameter.value) { + delete node.parameter.value[key]; + } + + for (const key in newValueObj) { + node.parameter.value[key] = newValueObj[key]; + } + } + + public getObjectValue(node) { + return JSON.stringify(node.parameter.value); + } + + public canAdd(node: any) { + return this.isArrayObject(node) || this.isDynamicObject(node); + } + + private formatParam(params: any[]): void { + console.log(params); + params.forEach(param => this.initParam(param)); + } + + private initParam(parameter: any, value?: any): void { + if (!parameter || 0 === parameter.length) { + return; + } + switch (parameter.type) { + case 'default': + parameter.parameter.name = parameter.label; + if (value && value[parameter.label]) { + parameter.parameter.value = value[parameter.label].value; + parameter.parameter.valueSource = value[parameter.label].valueSource; + } else { + parameter.parameter.valueSource = this.defaultValueSource; + } + break; + case 'object': + for (let index = 0; index < parameter.children.length; index++) { + let param = parameter.children[index]; + this.initParam(param, parameter.parameter.value); + } + break; + case 'array': + for (let index = 0; index < parameter.children.length; index++) { + let param = parameter.children[index]; + this.initParam(param, parameter.parameter.value); + } + break; + default: + console.log('init a unsupport parameter, type is:' + parameter.type); + break; + } + } + + private isArrayObject(node: any): boolean { + return node.type === 'array'; + } + + private isDynamicObject(node: any): boolean { + return node.type === 'map'; + } +} diff --git a/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.html b/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.html index 4f98e689..53f02f17 100644 --- a/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.html +++ b/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.html @@ -12,3 +12,7 @@ *******************************************************************************/ --> + + + diff --git a/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.ts b/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.ts index f2d802c2..a50cc1be 100644 --- a/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.ts +++ b/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task-parameters/rest-task-parameters.component.ts @@ -18,6 +18,7 @@ import { RestTask } from '../../../../model/workflow/rest-task'; import { BroadcastService } from '../../../../services/broadcast.service'; import { RestParameter } from "../../../../model/workflow/rest-parameter"; import { ValueSource } from "../../../../model/value-source.enum"; +import { SwaggerTreeConverterService } from "../../../../services/swagger-tree-converter.service"; /** * property component presents information of a workflow node. @@ -39,7 +40,7 @@ export class RestTaskParametersComponent implements OnInit { private index = 1; - constructor(private broadcastService: BroadcastService) { + constructor(private broadcastService: BroadcastService, private swaggerTreeConverterService: SwaggerTreeConverterService) { } public ngOnInit() { @@ -55,8 +56,10 @@ export class RestTaskParametersComponent implements OnInit { this.task.parameters.forEach(param => { if (param.position === 'body') { - // TODO add body parameter handler - // this.bodyParameter.push(param); + const requestTreeNode = this.swaggerTreeConverterService + .schema2TreeNode('Request Param', this.task.serviceName, this.task.serviceVersion, param.schema); + param.value = param.schema.value; + this.bodyParameter.push(requestTreeNode); } else { this.requestParameters.push(param); } diff --git a/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task.component.html b/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task.component.html index a416944c..af428840 100644 --- a/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task.component.html +++ b/sdc-workflow-designer-ui/src/app/components/property/rest-task/rest-task.component.html @@ -43,4 +43,6 @@
- +
+ +
diff --git a/sdc-workflow-designer-ui/src/app/services/swagger-tree-converter.service.ts b/sdc-workflow-designer-ui/src/app/services/swagger-tree-converter.service.ts new file mode 100644 index 00000000..a83ae2e4 --- /dev/null +++ b/sdc-workflow-designer-ui/src/app/services/swagger-tree-converter.service.ts @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2017 ZTE Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * ZTE - initial API and implementation and/or initial documentation + *******************************************************************************/ + +import { Injectable } from '@angular/core'; +import { TreeNode } from 'primeng/primeng'; + +import { ValueSource } from '../model/value-source.enum'; +import { WorkflowUtil } from '../util/workflow-util'; +import { WorkflowConfigService } from "./workflow-config.service"; + +@Injectable() +export class SwaggerTreeConverterService { + private serviceName: string; + private serviceVersion: string; + + constructor(private configService: WorkflowConfigService) { + + } + + public schema2TreeNode(key: string | number, serviceName: string, serviceVersion: string, schema: any): any { + this.serviceName = serviceName; + this.serviceVersion = serviceVersion; + + if (schema.$ref) { + const treeNode = this.getTreeNodeBySwaggerDefinition(key, schema); + return treeNode; + } else { + this.setInitValue4Param(schema.value, schema); + return this.parameter2TreeNode(key, schema); + } + } + + private getTreeNodeBySwaggerDefinition(key: string | number, schema: any): TreeNode { + const swagger = this.configService.getSwaggerInfo(this.serviceName, this.serviceVersion); + if(swagger === undefined) { + console.log(`swagger definition not exist, [${this.serviceName}].[${this.serviceVersion}]`); + return null; + } + const swaggerDefinition = this.configService.getDefinition(swagger, schema.$ref); + + const definitionCopy = WorkflowUtil.deepClone(swaggerDefinition); + + this.setInitValue4Param(schema.value, definitionCopy); + if (schema.value !== definitionCopy.value) { + schema.value = definitionCopy.value; + } + + return this.schema2TreeNode(key, this.serviceName, this.serviceVersion, definitionCopy); + } + + private setInitValue4Param(value: any | any[], param: any) { + param.value = value; + if (value == null) { + if (param.type === 'object') { + param.value = {}; + } else if (param.type === 'array') { + param.value = []; + } else { // primary type + param.valueSource = ValueSource[ValueSource.String]; + } + } + } + + private parameter2TreeNode(name: string | number, paramDefinition: any): any { + const nodeType = this.getTreeNodeType(paramDefinition); + + const node = { + label: name, + type: nodeType, + children: [], + parameter: paramDefinition, + }; + + if (paramDefinition.type === 'object') { + node.children = this.getPropertyFromObject(paramDefinition.value, paramDefinition); + } else if (paramDefinition.type === 'array') { + this.setChildrenForArray(node, paramDefinition); + } + + return node; + } + + private getTreeNodeType(param: any): string { + const type = param.type; + if (type === 'array') { + return 'array'; + } else if (type === 'object') { + if (param.additionalProperties) { + return 'map'; + } else { + return 'object'; + } + } else { + return 'default'; + } + } + + private setChildrenForArray(node: any, param: any) { + param.value.forEach((itemValue, index) => { + const itemCopy = WorkflowUtil.deepClone(param.items); + itemCopy.value = itemValue; + node.children.push(this.schema2TreeNode(index, this.serviceName, this.serviceVersion, itemCopy)); + }); + } + + private getPropertyFromObject(objectValue: any, param: any): TreeNode[] { + if (param.properties) { + return this.getPropertyFromSimpleObject(objectValue, param.properties); + } else if (param.additionalProperties) { + return this.getPropertyFromMapOrDictionary(objectValue, param.additionalProperties); + } else { + return []; + } + + } + + private getPropertyFromSimpleObject(objectValue: any, properties: any): TreeNode[] { + const treeNodes: TreeNode[] = []; + for (const key in properties) { + const property = properties[key]; + this.setInitValue4Param(objectValue[key], property); + + if (property.value !== objectValue[key]) { + objectValue[key] = property.value; + } + + treeNodes.push(this.schema2TreeNode(key, this.serviceName, this.serviceVersion, property)); + } + return treeNodes; + } + + private getPropertyFromMapOrDictionary(mapOrDictionary: any, additionalProperties: any): TreeNode[] { + const treeNodes: TreeNode[] = []; + for (const key in mapOrDictionary) { + const propertyCopy = WorkflowUtil.deepClone(additionalProperties); + propertyCopy.value = mapOrDictionary[key]; + + const treeNode = this.schema2TreeNode(key, this.serviceName, this.serviceVersion, propertyCopy); + treeNode.keyEditable = true; + treeNodes.push(treeNode); + + if (mapOrDictionary[key] !== propertyCopy.value) { + mapOrDictionary[key] = propertyCopy.value; + } + } + return treeNodes; + } +} diff --git a/sdc-workflow-designer-ui/src/app/services/workflow-config.service.ts b/sdc-workflow-designer-ui/src/app/services/workflow-config.service.ts index 469fde92..38d9fb5c 100644 --- a/sdc-workflow-designer-ui/src/app/services/workflow-config.service.ts +++ b/sdc-workflow-designer-ui/src/app/services/workflow-config.service.ts @@ -15,7 +15,7 @@ import { WorkflowService } from "./workflow.service"; import { Microservice } from "../model/workflow/microservice"; import { Observable } from "rxjs/Rx"; import { HttpService } from "../util/http.service"; -import { Swagger } from "../model/swagger"; +import { Swagger, SwaggerSchemaObject } from "../model/swagger"; /** * WorkflowConfigService @@ -46,4 +46,11 @@ export class WorkflowConfigService { return undefined; } } + + public getDefinition(swagger: Swagger, position: string): SwaggerSchemaObject { + const definitionName = position.substring('#/definitions/'.length); + + return swagger.definitions[definitionName]; + } + } diff --git a/sdc-workflow-designer-ui/src/app/shared/shared.module.ts b/sdc-workflow-designer-ui/src/app/shared/shared.module.ts index 1591a13f..faa4d7dd 100644 --- a/sdc-workflow-designer-ui/src/app/shared/shared.module.ts +++ b/sdc-workflow-designer-ui/src/app/shared/shared.module.ts @@ -13,7 +13,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; -import { RadioButtonModule } from 'primeng/primeng'; +import { RadioButtonModule, TreeModule } from 'primeng/primeng'; import { RouterModule } from '@angular/router'; const module = [ @@ -21,6 +21,7 @@ const module = [ FormsModule, RadioButtonModule, RouterModule, + TreeModule, ]; @NgModule({ -- 2.16.6