From 38dfd59a8b5d05266e5567f79dcf30fd10ef7c54 Mon Sep 17 00:00:00 2001 From: Lvbo163 Date: Wed, 10 Jan 2018 15:05:39 +0800 Subject: split element by type split elements by type in toolbar Issue-ID: SDC-895 Change-Id: I0254c9c5f938df80942d60ffefe4b8577c788233 Signed-off-by: Lvbo163 --- .../src/app/services/model.service.ts | 366 +++++++++++++++++---- 1 file changed, 299 insertions(+), 67 deletions(-) (limited to 'sdc-workflow-designer-ui/src/app/services/model.service.ts') diff --git a/sdc-workflow-designer-ui/src/app/services/model.service.ts b/sdc-workflow-designer-ui/src/app/services/model.service.ts index 6ce49d04..65d3e490 100644 --- a/sdc-workflow-designer-ui/src/app/services/model.service.ts +++ b/sdc-workflow-designer-ui/src/app/services/model.service.ts @@ -18,12 +18,22 @@ import { NodeType } from "../model/workflow/node-type.enum"; import { StartEvent } from "../model/workflow/start-event"; import { SequenceFlow } from "../model/workflow/sequence-flow"; import { RestTask } from "../model/workflow/rest-task"; +import { ErrorEvent } from "../model/workflow/error-event"; import { PlanTreeviewItem } from "../model/plan-treeview-item"; import { WorkflowConfigService } from "./workflow-config.service"; import { Swagger, SwaggerModelSimple, SwaggerReferenceObject } from "../model/swagger"; import { WorkflowService } from "./workflow.service"; import { IntermediateCatchEvent } from "../model/workflow/intermediate-catch-event"; import { ScriptTask } from "../model/workflow/script-task"; +import { ToscaNodeTask } from '../model/workflow/tosca-node-task'; +import { NodeTemplate } from '../model/topology/node-template'; +import { SubProcess } from '../model/workflow/sub-process'; +import { TimerEventDefinition, TimerEventDefinitionType } from '../model/workflow/timer-event-definition'; +import { Parameter } from '../model/workflow/parameter'; +import { ValueSource } from '../model/value-source.enum'; +import { RestService } from './rest.service'; +import { BroadcastService } from './broadcast.service'; +import { RestConfig } from '../model/rest-config'; /** * WorkflowService @@ -31,78 +41,231 @@ import { ScriptTask } from "../model/workflow/script-task"; */ @Injectable() export class ModelService { - - constructor(private workflowService: WorkflowService, private configService: WorkflowConfigService) { - + public rootNodeId = 'root'; + + private planModel: PlanModel = new PlanModel(); + + constructor(private broadcastService: BroadcastService, private restService: RestService, private workflowService: WorkflowService, private configService: WorkflowConfigService) { + this.broadcastService.planModel$.subscribe(plan => { + plan.nodes.forEach(node => { + switch (node.type) { + case NodeType[NodeType.startEvent]: + node.position.width = 56; + node.position.height = 56; + break; + case NodeType[NodeType.endEvent]: + node.position.width = 56; + node.position.height = 56; + break; + case NodeType[NodeType.restTask]: + node.position.width = 56; + node.position.height = 56; + break; + case NodeType[NodeType.errorStartEvent]: + case NodeType[NodeType.errorEndEvent]: + node.position.width = 26; + node.position.height = 26; + break; + case NodeType[NodeType.toscaNodeManagementTask]: + node.position.width = 56; + node.position.height = 56; + break; + case NodeType[NodeType.subProcess]: + node.position.width = 56; + node.position.height = 56; + break; + case NodeType[NodeType.intermediateCatchEvent]: + node.position.width = 56; + node.position.height = 56; + break; + case NodeType[NodeType.scriptTask]: + node.position.width = 56; + node.position.height = 56; + break; + case NodeType[NodeType.exclusiveGateway]: + case NodeType[NodeType.parallelGateway]: + node.position.width = 26; + node.position.height = 26; + break; + default: + node.position.width = 56; + node.position.height = 56; + break; + } + }); + this.planModel = plan; + }); + this.broadcastService.updateModelRestConfig$.subscribe(restConfigs => { + this.updateRestConfig(restConfigs); + }); } public getProcess(): WorkflowNode[] { return this.workflowService.planModel.nodes; } - public addNode(name: string, type: string, top: number, left: number): WorkflowNode { - let node: WorkflowNode; - switch (type) { - case NodeType[NodeType.startEvent]: - node = new StartEvent(this.createId(), name, type, new Position(top, left), []); - break; - case NodeType[NodeType.restTask]: - node = new RestTask(this.createId(), name, type, new Position(top, left), []); - break; - case NodeType[NodeType.intermediateCatchEvent]: - node = new IntermediateCatchEvent(this.createId(), name, type, new Position(top, left), []); - break; - case NodeType[NodeType.scriptTask]: - node = new ScriptTask(this.createId(), name, type, new Position(top, left), []); - break; - default: - node = new WorkflowNode(this.createId(), name, type, new Position(top, left), []); - break; + public getNodes(): WorkflowNode[] { + return this.planModel.nodes; + } + + public addConnection(sourceId: string, targetId: string) { + const node = this.getNodeMap().get(sourceId); + if (node) { + const index = node.connection.findIndex(sequenceFlow => sequenceFlow.targetRef === targetId); + if (index === -1) { + const sequenceFlow: SequenceFlow = { sourceRef: sourceId, targetRef: targetId }; + node.connection.push(sequenceFlow); + } } + } - this.getProcess().push(node); - return node; + public deleteConnection(sourceId: string, targetId: string) { + const node = this.getNodeMap().get(sourceId); + if (node) { + const index = node.connection.findIndex(sequenceFlow => sequenceFlow.targetRef === targetId); + if (index !== -1) { + node.connection.splice(index, 1); + } + } } - public deleteNode(nodeId: string): WorkflowNode { + public deleteNode(parentId: string, nodeId: string): WorkflowNode { + const nodeMap = this.getNodeMap(); + + const nodes = this.getChildrenNodes(parentId); + // delete related connections - this.getProcess().forEach(node => this.deleteSequenceFlow(node.id, nodeId)); + nodes.forEach(node => this.deleteConnection(node.id, nodeId)); // delete current node - const index = this.getProcess().findIndex(node => node.id === nodeId); + const index = nodes.findIndex(node => node.id === nodeId); if (index !== -1) { - const node = this.getProcess().splice(index, 1)[0]; - node.sequenceFlows = []; + const node = nodes.splice(index, 1)[0]; + node.connection = []; return node; } - return undefined; + return null; } - public addSequenceFlow(sourceId: string, targetId: string) { - const node = this.getNodeById(sourceId); - if (node) { - const index = node.sequenceFlows.findIndex(sequenceFlow => sequenceFlow.targetRef === targetId); - if (index === -1) { - node.sequenceFlows.push(new SequenceFlow(sourceId, targetId)); - } + public addChild(parentId: string, child: WorkflowNode) { + this.getChildrenNodes(parentId).push(child); + } + + public deleteChild(node: SubProcess, id: string): WorkflowNode { + const index = node.children.findIndex(child => child.id === id); + if (index !== -1) { + const deletedNode = node.children.splice(index, 1); + return deletedNode[0]; } + + return null; } - public deleteSequenceFlow(sourceId: string, targetId: string) { - const node = this.getNodeById(sourceId); - if (node) { - const index = node.sequenceFlows.findIndex(sequenceFlow => sequenceFlow.targetRef === targetId); - if (index !== -1) { - node.sequenceFlows.splice(index, 1); + public updateRestConfig(restConfigs: RestConfig[]): void { + this.planModel.configs = { restConfigs: restConfigs }; + // console.log(this.planModel.configs); + } + + public getNodeMap(): Map { + const map = new Map(); + this.toNodeMap(this.planModel.nodes, map); + return map; + } + + private toNodeMap(nodes: WorkflowNode[], map: Map) { + nodes.forEach(node => { + if (node.type === 'subProcess') { + this.toNodeMap((node).children, map); } + map.set(node.id, node); + }); + } + + public addNode(name: string, type: string, left: number, top: number) { + const id = this.createId(); + const workflowPos = new Position(left, top); + const node = this.createNodeByType(id, name, type, workflowPos); + this.planModel.nodes.push(node); + } + + private createNodeByType(id: string, name: string, type: string, position: Position): WorkflowNode { + const bigPosition = new Position(position.left, position.top, 56, 56); + const smallPosition = new Position(position.left, position.top, 26, 26); + switch (type) { + case NodeType[NodeType.startEvent]: + let startEventNode: StartEvent = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [], parameters: [] + }; + return startEventNode; + case NodeType[NodeType.endEvent]: + let endEventNode: WorkflowNode = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [] + }; + return endEventNode; + case NodeType[NodeType.restTask]: + let restTaskNode: RestTask = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [], produces: [], consumes: [], parameters: [], responses: [] + }; + return restTaskNode; + case NodeType[NodeType.errorStartEvent]: + case NodeType[NodeType.errorEndEvent]: + let errorEventNode: ErrorEvent = { + id: id, type: type, name: '', parentId: this.rootNodeId, + position: smallPosition, connection: [], parameter: new Parameter('errorRef', '', ValueSource[ValueSource.String]) + }; + return errorEventNode; + case NodeType[NodeType.toscaNodeManagementTask]: + let toscaNodeTask: ToscaNodeTask = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [], input: [], output: [], template: new NodeTemplate() + }; + return toscaNodeTask; + case NodeType[NodeType.subProcess]: + let subProcess: SubProcess = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [], children: [] + }; + return subProcess; + case NodeType[NodeType.intermediateCatchEvent]: + let intermediateCatchEvent: IntermediateCatchEvent = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [], timerEventDefinition: { type: TimerEventDefinitionType[TimerEventDefinitionType.timeDuration] } + }; + return intermediateCatchEvent; + case NodeType[NodeType.scriptTask]: + let scriptTask: ScriptTask = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [], scriptFormat: 'JavaScript' + }; + return scriptTask; + case NodeType[NodeType.exclusiveGateway]: + case NodeType[NodeType.parallelGateway]: + let getway: WorkflowNode = { + id: id, type: type, name: '', parentId: this.rootNodeId, + position: smallPosition, connection: [] + }; + return getway; + default: + let node: WorkflowNode = { + id: id, type: type, name: name, parentId: this.rootNodeId, + position: bigPosition, connection: [] + }; + return node; } } + public isNode(object: any): boolean { + return undefined !== object.type; + } + public getSequenceFlow(sourceRef: string, targetRef: string): SequenceFlow { const node = this.getNodeById(sourceRef); if (node) { - const sequenceFlow = node.sequenceFlows.find(tmp => tmp.targetRef === targetRef); + const sequenceFlow = node.connection.find(tmp => tmp.targetRef === targetRef); return sequenceFlow; } else { return undefined; @@ -123,6 +286,9 @@ export class ModelService { case NodeType[NodeType.startEvent]: params.push(this.loadOutput4StartEvent(node)); break; + case NodeType[NodeType.toscaNodeManagementTask]: + params.push(this.loadOutput4ToscaNodeTask(node)); + break; case NodeType[NodeType.restTask]: params.push(this.loadOutput4RestTask(node)); break; @@ -135,54 +301,73 @@ export class ModelService { } private loadOutput4StartEvent(node: StartEvent): PlanTreeviewItem { - const startItem = new PlanTreeviewItem(node.name, `[${node.id}]`, []); + const startItem = new PlanTreeviewItem(node.name, `[${node.id}]`, [], false); node.parameters.map(param => startItem.children.push(new PlanTreeviewItem(param.name, `[${param.name}]`, []))); return startItem; } + private loadOutput4ToscaNodeTask(node: ToscaNodeTask): PlanTreeviewItem { + const item = new PlanTreeviewItem(node.name, `[${node.id}]`, [], false); + item.children.push(this.createStatusCodeTreeViewItem(node.id)); + const responseItem = this.createResponseTreeViewItem(node.id); + item.children.push(responseItem); + + node.output.map(param => + responseItem.children.push(new PlanTreeviewItem(param.name, `${responseItem.value}.[${param.name}]`, []))); + return item; + } + private loadOutput4RestTask(node: RestTask): PlanTreeviewItem { - const item = new PlanTreeviewItem(node.name, `[${node.id}]`, []); + const item = new PlanTreeviewItem(node.name, `[${node.id}]`, [], false); item.children.push(this.createStatusCodeTreeViewItem(node.id)); if (node.responses.length !== 0) { // load rest responses const responseItem = this.createResponseTreeViewItem(node.id); item.children.push(responseItem); + // todo: should list all available response or only the first one? if (node.responses[0]) { - const swagger = this.configService.getSwaggerInfo(node.serviceName, node.serviceVersion); - const swaggerDefinition = this.configService.getDefinition(swagger, node.responses[0].schema.$ref); - this.loadParamsBySwaggerDefinition(responseItem, swagger, swaggerDefinition); + const swagger = this.restService.getSwaggerInfo(node.restConfigId); + const SwaggerReferenceObject = node.responses[0].schema as SwaggerReferenceObject; + const swaggerDefinition = this.restService.getDefinition(swagger, SwaggerReferenceObject.$ref); + this.loadParamsBySwaggerDefinition(responseItem, swagger, swaggerDefinition); } } return item; } - private createStatusCodeTreeViewItem(nodeId: string): PlanTreeviewItem { - return new PlanTreeviewItem('statusCode', `[${nodeId}].[statusCode]`, []); - } - - private createResponseTreeViewItem(nodeId: string): PlanTreeviewItem { - return new PlanTreeviewItem('response', `[${nodeId}].[responseBody]`, []); - } - - private loadParamsBySwaggerDefinition(parentItem: PlanTreeviewItem, swagger: Swagger, definition: SwaggerModelSimple) { + private loadParamsBySwaggerDefinition(parentItem: PlanTreeviewItem, swagger: Swagger, definition: any) { Object.getOwnPropertyNames(definition.properties).map(key => { const property = definition.properties[key]; const value = `${parentItem.value}.[${key}]`; const propertyItem = new PlanTreeviewItem(key, value, []); parentItem.children.push(propertyItem); + // reference to swagger.ts function getSchemaObject() if (property instanceof SwaggerReferenceObject) { - const propertyDefinition = this.configService.getDefinition(swagger, property.$ref); - this.loadParamsBySwaggerDefinition(propertyItem, swagger, - propertyDefinition); + // handle reference parameter. + const propertyDefinition = this.restService.getDefinition(swagger, property.$ref); + this.loadParamsBySwaggerDefinition(propertyItem, swagger, propertyDefinition); + } else if (property instanceof SwaggerModelSimple) { + // handle object parameter. + this.loadParamsBySwaggerDefinition(propertyItem, swagger, property); + } else { + // skip } return propertyItem; }); } + private createStatusCodeTreeViewItem(nodeId: string): PlanTreeviewItem { + return new PlanTreeviewItem('statusCode', `[${nodeId}].[statusCode]`, []); + } + + private createResponseTreeViewItem(nodeId: string): PlanTreeviewItem { + return new PlanTreeviewItem('response', `[${nodeId}].[responseBody]`, []); + } + public getPreNodes(nodeId: string, preNodes: WorkflowNode[]) { const preNode4CurrentNode = []; this.getProcess().forEach(node => { @@ -201,7 +386,7 @@ export class ModelService { } public isPreNode(preNode: WorkflowNode, id: string): boolean { - const targetNode = preNode.sequenceFlows.find(connection => connection.targetRef === id); + const targetNode = preNode.connection.find(connection => connection.targetRef === id); return targetNode !== undefined; } @@ -210,15 +395,62 @@ export class ModelService { } private createId() { - const idSet = new Set(); - this.getProcess().forEach(node => idSet.add(node.id)); + const nodeMap = this.getNodeMap(); - for (let i = 0; i < idSet.size; i++) { - if (!idSet.has('node' + i)) { - return 'node' + i; + for (let i = 0; i < nodeMap.size; i++) { + const key = 'node' + i; + if (!nodeMap.get(key)) { + return key; } } - return 'node' + idSet.size; + return 'node' + nodeMap.size; + } + + public getChildrenNodes(parentId: string): WorkflowNode[] { + if (!parentId || parentId === this.rootNodeId) { + return this.planModel.nodes; + } else { + const node = this.getNodeMap().get(parentId); + if (node.type === 'subProcess') { + return (node).children; + } else { + return []; + } + } + } + + public changeParent(id: string, originalParentId: string, targetParentId: string) { + if (originalParentId === targetParentId) { + return; + } + + const node: WorkflowNode = this.deleteNode(originalParentId, id); + node.parentId = targetParentId; + + if (targetParentId) { + this.addChild(targetParentId, node); + } else { + this.planModel.nodes.push(node); + } + } + + public updatePosition(id: string, left: number, top: number, width: number, height: number) { + const node = this.getNodeMap().get(id); + node.position.left = left; + node.position.top = top; + node.position.width = width; + node.position.height = height; + } + + public isDescendantNode(node: WorkflowNode, descendantId: string): boolean { + if (NodeType[NodeType.subProcess] !== node.type) { + return false; + } + const tmp = (node).children.find(child => { + return child.id === descendantId || (NodeType[NodeType.subProcess] === child.type && this.isDescendantNode(child, descendantId)); + }); + + return tmp !== undefined; } } -- cgit 1.2.3-korg