diff options
author | vempo <vitaliy.emporopulo@amdocs.com> | 2018-07-24 17:34:04 +0300 |
---|---|---|
committer | vempo <vitaliy.emporopulo@amdocs.com> | 2018-07-25 11:39:10 +0300 |
commit | a52d50e788792a63e97a9176ab319d53db7a2853 (patch) | |
tree | b1c2222cacf4b8192aea16d1e0315b1f005c5347 /deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services | |
parent | 3c2665debb400aef7f0ed9e235698d2ff9f859db (diff) |
Replaced old implementation at root
Old project files and directories has been moved
under 'deprecated-workflow-designer'. The old project
is not built by the CI anymore, but can be still built manually.
New modules/directories have been moved up and integrated with
the CI system.
Change-Id: I1528c792bcbcce9e50bfc294a1328a20e72c91cf
Issue-ID: SDC-1559
Signed-off-by: vempo <vitaliy.emporopulo@amdocs.com>
Diffstat (limited to 'deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services')
16 files changed, 2231 insertions, 0 deletions
diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/auth.service.spec.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/auth.service.spec.ts new file mode 100644 index 00000000..9bd500ef --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/auth.service.spec.ts @@ -0,0 +1,26 @@ +/** + * 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 { TestBed, inject } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AuthService] + }); + }); + + it('should be created', inject([AuthService], (service: AuthService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/auth.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/auth.service.ts new file mode 100644 index 00000000..a8e60753 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/auth.service.ts @@ -0,0 +1,65 @@ +/** + * 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 { Http } from '@angular/http'; +import { BroadcastService } from './broadcast.service'; + +@Injectable() +export class AuthService { + private static AuthUrl = "/api/oauth2/v1/perms/user/operations"; + private static AllOperations = ["operation.apds.create", "operation.apds.delete", "operation.apds.modify", + "operation.apds.view", "operation.apds.import", "operation.apds.export", "operation.apds.deploy", + "operation.apds.test"]; + private static ModifyOperation = 'operation.apds.modify'; + + constructor(private http: Http, private broadcastService: BroadcastService) { + this.checkRights(); + // keep alive + // setInterval(() => { + // console.log(`Keep session alive. Request per 3 minutes. ${new Date()}`); + // this.checkRights(); + // }, 180000); + } + public checkRights() { + // let data = { operations: AuthService.AllOperations }; + // this.http.post(AuthService.AuthUrl, data).subscribe(res => { + // let hasRightOP = res.json().operations; + // if (hasRightOP.length > 0) { + // this.broadcastService.broadcast(this.broadcastService.openRight, true); + // if (hasRightOP.indexOf(AuthService.ModifyOperation) > -1) { + // this.broadcastService.broadcast(this.broadcastService.saveRight, true); + // } else { + // this.broadcastService.broadcast(this.broadcastService.saveRight, false); + // } + // } else { + // this.broadcastService.broadcast(this.broadcastService.openRight, false); + // this.broadcastService.broadcast(this.broadcastService.saveRight, false); + // } + // }, error => { + // switch (error.status) { + // // Incase oauth service not exists or operation set not exists + // case 404: + // case 501: + // case 502: + // this.broadcastService.broadcast(this.broadcastService.openRight, true); + // this.broadcastService.broadcast(this.broadcastService.saveRight, true); + // break; + // default: + // this.broadcastService.broadcast(this.broadcastService.openRight, false); + // this.broadcastService.broadcast(this.broadcastService.saveRight, false); + // break; + // } + // }); + this.broadcastService.broadcast(this.broadcastService.openRight, true); + this.broadcastService.broadcast(this.broadcastService.saveRight, true); + } +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/broadcast.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/broadcast.service.ts new file mode 100644 index 00000000..410ae9b0 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/broadcast.service.ts @@ -0,0 +1,75 @@ +/** + * 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 { Subject } from 'rxjs/Subject'; + +import { PlanModel } from '../model/plan-model'; +import { RestConfig } from '../model/rest-config'; +import { Swagger } from '../model/swagger'; +import { SequenceFlow } from '../model/workflow/sequence-flow'; +import { WorkflowNode } from '../model/workflow/workflow-node'; +import { WorkflowElement } from '../model/workflow/workflow-element'; + +/** + * BroadcastService + * All of the observable subject should be registered to this service. + * It provider a broadcast method to broadcast data. the broadcast method would catch error while broadcasting. + */ +@Injectable() +export class BroadcastService { + public openRight = new Subject<boolean>(); + public openRight$ = this.openRight.asObservable(); + + public saveRight = new Subject<boolean>(); + public saveRight$ = this.saveRight.asObservable(); + + public initModel = new Subject<PlanModel>(); + public initModel$ = this.initModel.asObservable(); + + public showProperty = new Subject<WorkflowElement>(); + public showProperty$ = this.showProperty.asObservable(); + + public planModel = new Subject<PlanModel>(); + public planModel$ = this.planModel.asObservable(); + + public updateModelToscaConfig = new Subject<any[]>(); + public updateModelToscaConfig$ = this.updateModelToscaConfig.asObservable(); + + public updateModelRestConfig = new Subject<RestConfig[]>(); + public updateModelRestConfig$ = this.updateModelRestConfig.asObservable(); + + public updateNodeTypeConfig = new Subject<any[]>(); + public updateNodeTypeConfig$ = this.updateNodeTypeConfig.asObservable(); + + public saveEvent = new Subject<PlanModel>(); + public saveEvent$ = this.saveEvent.asObservable(); + + public selectedElement = new Subject<WorkflowElement[]>(); + public selectedElement$ = this.selectedElement.asObservable(); + + public swagger = new Subject<any>(); + public swagger$ = this.swagger.asObservable(); + + /** + * broadcast datas + * this method will catch the exceptions for the broadcast + * @param subject will broadcast data + * @param data will be broadcasted + */ + public broadcast(subject: Subject<any>, data: any) { + try { + subject.next(data); + } catch (err) { + console.error(err); + } + } +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/display-info.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/display-info.service.ts new file mode 100644 index 00000000..f7c55e0e --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/display-info.service.ts @@ -0,0 +1,27 @@ +/** + * 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 { HttpService } from "../util/http.service"; +import { Observable } from "rxjs"; +import { ModelService } from "./model.service"; + +@Injectable() +export class DisplayInfoService { + private displayInfoUrl = '/api/workflow-modeler/v1/ext-activities/displayInfo?scene='; + + constructor(private modelService: ModelService, private httpService: HttpService) { + } + + public getDisplayInfo(): Observable<any> { + return this.httpService.get(this.displayInfoUrl + this.modelService.getPlanModel().scene); + } +}
\ No newline at end of file diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/interface.service.spec.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/interface.service.spec.ts new file mode 100644 index 00000000..0aeb3f52 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/interface.service.spec.ts @@ -0,0 +1,26 @@ +/** + * 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 { TestBed, inject } from '@angular/core/testing'; + +import { InterfaceService } from './interface.service'; + +describe('PersistenceService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [InterfaceService] + }); + }); + + it('should be created', inject([InterfaceService], (service: InterfaceService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/interface.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/interface.service.ts new file mode 100644 index 00000000..9dcafe96 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/interface.service.ts @@ -0,0 +1,53 @@ +/** + * 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 { ActivatedRoute } from '@angular/router/'; + +import { Observable } from 'rxjs/Observable'; +import { TranslateService } from '@ngx-translate/core'; + +import { HttpService } from '../util/http.service'; +import { BroadcastService } from './broadcast.service'; +import { NoticeService } from './notice.service'; +import { PlanModel } from '../model/plan-model'; + +@Injectable() +export class InterfaceService { + private static ModelUrl = '/api/workflow-modeler/v1/models/'; + constructor(private activatedRoute: ActivatedRoute, private http: HttpService, + private broadcast: BroadcastService, private notice: NoticeService, private translate: TranslateService) { + this.getModelData(); + } + + public getModelData() { + this.activatedRoute.queryParams.subscribe(queryParams => { + let modelId = queryParams.hasOwnProperty("id") ? queryParams.id : ""; + let name = queryParams.hasOwnProperty("name") ? queryParams.name : ""; + let uuid = queryParams.hasOwnProperty("uuid") ? queryParams.uuid :""; + let operationId = queryParams.hasOwnProperty("operationId") ? queryParams.operationId : ""; + + this.http.get(InterfaceService.ModelUrl + modelId + + "?name=" + name + "&uuid=" + uuid + + "&operationId=" + operationId).subscribe(data => { + this.broadcast.broadcast(this.broadcast.initModel, data); + }, error => { + this.translate.get('WORKFLOW.MSG.LOAD_FAIL').subscribe((res: string) => { + this.notice.error(res); + }); + }); + }); + } + + public saveModelData(planModel: PlanModel): Observable<PlanModel> { + return this.http.put(InterfaceService.ModelUrl + planModel.id, planModel); + } +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/jsplumb.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/jsplumb.service.ts new file mode 100644 index 00000000..0cf93efe --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/jsplumb.service.ts @@ -0,0 +1,581 @@ +/** + * 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 * as jsp from 'jsplumb'; + +import {Subscription} from 'rxjs/Subscription'; +import {WorkflowNode} from '../model/workflow/workflow-node'; +import {BroadcastService} from './broadcast.service'; +import {ModelService} from './model.service'; +import {SequenceFlow} from '../model/workflow/sequence-flow'; +import {Position} from '../model/workflow/position'; + +/** + * JsPlumbService + * provides all of the operations about jsplumb plugin. + */ +@Injectable() +export class JsPlumbService { + public jsplumbInstanceMap = new Map<string, any>(); + public subscriptionMap = new Map<string, Subscription>(); + + private padding = 20; + private rootClass = 'canvas'; + private selectNodes: WorkflowNode[] = []; + + constructor(private modelService: ModelService, private broadcastService: BroadcastService) { + this.broadcastService.selectedElement$.subscribe(elements => { + this.selectNodes = []; + if (elements && 0 < elements.length) { + for (let index = 0; index < elements.length; index++) { + let element = elements[index]; + if (this.modelService.isNode(element)) { + let node = element as WorkflowNode; + this.selectNodes.push(node); + } + } + } + }); + } + + public connectChildrenNodes(parentNodeId: string) { + const jsplumbInstance = this.jsplumbInstanceMap.get(parentNodeId); + + const nodes: WorkflowNode[] = this.modelService.getChildrenNodes(parentNodeId); + nodes.forEach(node => this.connect4OneNode(node, jsplumbInstance)); + } + + public connect4OneNode(node: WorkflowNode, jsplumbInstance: any) { + node.connection.forEach(sequenceFlow => { + const connection = jsplumbInstance.connect({ + source: sequenceFlow.sourceRef, + target: sequenceFlow.targetRef, + }); + if (sequenceFlow.name) { + connection.setLabel(sequenceFlow.name); + } + }); + } + + public initJsPlumbInstance(id: string) { + if (this.jsplumbInstanceMap.get(id)) { + return; + } + const jsplumbInstance = jsp.jsPlumb.getInstance(); + + jsplumbInstance.importDefaults({ + Anchor: "Continuous", + Endpoint: "Blank", + Container: "pallete", + ReattachConnections: true, + Connector: ['Flowchart', { + stub: [0, 0], + cornerRadius: 5, + alwaysRespectStubs: true + }], + PaintStyle: { + stroke: "#7D8695", + strokeWidth: 1, + radius: 1, + outlineStroke: "transform", + outlineWidth: 4 + }, + ConnectorStyle: { + stroke: "#7D8695", + strokeWidth: 1, + outlineStroke: "transform", + outlineWidth: 4 + }, + ConnectorHoverStyle: { + stroke: "#00ABFF", + strokeWidth: 2, + outlineStroke: "transform", + outlineWidth: 4 + }, + ConnectionOverlays: [ + ['Arrow', { + location: 1, + id: 'arrow', + cssClass: 'icon-port', + width: 11, + length: 12 + }], + ['Label', {label: '', id: 'label'}] + ] + }); + + // add connection to model data while a new connection is build + jsplumbInstance.bind('connection', info => { + this.modelService.addConnection(info.connection.sourceId, info.connection.targetId); + + this.subscribe4Connection(info.connection); + + info.connection.bind('click', (connection, event) => { + if ('Label' === connection.type) { + return; + } + event.stopPropagation(); + const sequenceFlow = this.modelService.getSequenceFlow(connection.sourceId, connection.targetId); + this.broadcastService.broadcast(this.broadcastService.showProperty, null); + this.broadcastService.broadcast(this.broadcastService.selectedElement, [sequenceFlow]); + }); + + info.connection.bind('dblclick', connection => { + if ('Label' === connection.type) { + return; + } + const sequenceFlow = this.modelService.getSequenceFlow(connection.sourceId, connection.targetId); + this.broadcastService.broadcast(this.broadcastService.showProperty, sequenceFlow); + }); + }); + + this.jsplumbInstanceMap.set(id, jsplumbInstance); + } + + private subscribe4Connection(connection: any) { + const pre = connection.sourceId + connection.targetId; + let sequenceFlowSubscription = this.subscriptionMap.get(pre + 'sequenceFlowSubscription'); + if (sequenceFlowSubscription && !sequenceFlowSubscription.closed) { + sequenceFlowSubscription.unsubscribe(); + } + + let currentThis = this; + sequenceFlowSubscription = this.broadcastService.selectedElement$.subscribe(elements => { + let selected = false; + if (elements && 0 < elements.length) { + for (let index = 0; index < elements.length; index++) { + let element = elements[index]; + if (!this.modelService.isNode(element)) { + let sequence = element as SequenceFlow; + if (sequence.sourceRef === connection.sourceId + && sequence.targetRef === connection.targetId) { + selected = true; + } + } + } + } + if (selected) { + connection.setPaintStyle({ + stroke: '#00ABFF', + strokeWidth: 1, + radius: 1, + outlineStroke: "transform", + outlineWidth: 4 + }); + } else { + connection.setPaintStyle({ + stroke: '#7D8695', + strokeWidth: 1, + radius: 1, + outlineStroke: "transform", + outlineWidth: 4 + }); + } + }); + this.subscriptionMap.set(pre + 'sequenceFlowSubscription', sequenceFlowSubscription); + } + + private unsubscription4Connection(connectionSelection: any) { + connectionSelection.each(connection => { + const pre = connection.sourceId + connection.targetId; + this.subscriptionMap.get(pre + 'sequenceFlowSubscription').unsubscribe(); + }); + } + + public deleteConnect(sourceId: string, targetId: string) { + const sourceNode = this.modelService.getNodeMap().get(sourceId); + const jsplumbInstance = this.jsplumbInstanceMap.get(sourceNode.parentId); + const connectionSelection = jsplumbInstance.select({source: sourceId, target: targetId}); + this.unsubscription4Connection(connectionSelection); + connectionSelection.delete(); + } + + public setLabel(sourceId: string, targetId: string, label: string) { + const sourceNode = this.modelService.getNodeMap().get(sourceId); + const jsplumbInstance = this.jsplumbInstanceMap.get(sourceNode.parentId); + const connections = jsplumbInstance.select({source: sourceId, target: targetId}); + connections.setLabel(label); + } + + public getParentNodeId(id: string): string { + const nodeElement = jsp.jsPlumb.getSelector('#' + id); + const parentNode = this.getParentNodeEl(nodeElement[0]); + + return parentNode ? parentNode.id : null; + } + + public initNode(node: WorkflowNode) { + const jsplumbInstance = this.jsplumbInstanceMap.get(node.parentId); + + this.jsplumbInstanceMap.get(this.modelService.rootNodeId).draggable(node.id, { + scope: 'node', + filter: '.ui-resizable-handle', + classes: { + 'ui-draggable': 'dragging' + }, + // grid: [5, 5], + drag: event => { + // out of container edge, reset to minimal value. + if (0 > event.pos[0]) { + event.el.style.left = '0px'; + } + if (0 > event.pos[1]) { + event.el.style.top = '0px'; + } + + if (0 < this.selectNodes.length) { + let moveAll = false; + this.selectNodes.forEach(element => { + if (element.id === event.el.id) { + moveAll = true; + } + }); + if (moveAll) { + this.selectNodes.forEach(selectNode => { + if (selectNode.id !== event.el.id) { + selectNode.position.left += event.e.movementX; + selectNode.position.left = 0 > selectNode.position.left ? 0 : selectNode.position.left; + selectNode.position.top += event.e.movementY; + selectNode.position.top = 0 > selectNode.position.top ? 0 : selectNode.position.top; + } + jsplumbInstance.revalidate(jsplumbInstance.getSelector('#' + selectNode.id)); + }); + } + } + }, + stop: event => { + this.selectNodes.forEach(selectNode => { + jsplumbInstance.revalidate(jsplumbInstance.getSelector('#' + selectNode.id)); + }); + } + }); + + jsplumbInstance.makeTarget(node.id, { + maxConnections: -1, + beforeDrop: function (info) { + const sourceId = info.sourceId; + const targetId = info.targetId; + if (sourceId === targetId) { + return false; + } + const sameConnections = this.instance.getConnections({source: sourceId, target: targetId}); + if (sameConnections && 0 < sameConnections.length) { + return false; + } + return true; + } + + }); + + jsplumbInstance.makeSource(node.id, { + filter: '.anchor, .anchor *', + maxConnections: -1, + }); + } + + public nodeDroppable(node: WorkflowNode, rank: number) { + const jsplumbInstance = this.jsplumbInstanceMap.get(node.parentId); + + const selector = jsplumbInstance.getSelector('#' + node.id); + this.jsplumbInstanceMap.get(this.modelService.rootNodeId).droppable(selector, { + scope: 'node', + rank, + tolerance: 'pointer', + drop: event => { + if (!this.isChildNode(event.drop.el, event.drag.el)) { + this.drop(event); + } + return true; + }, + canDrop: drag => { + const nodeMap = this.modelService.getNodeMap(); + const ancestorNode = nodeMap.get(drag.el.id); + + const isAncestor = this.modelService.isDescendantNode(ancestorNode, node.id); + return !isAncestor; + }, + }); + } + + private isChildNode(childElement, parentElement) { + while (childElement !== parentElement) { + childElement = childElement.parentNode; + if (childElement.classList.contains('canvas')) { + return false; + } + } + + return true; + } + + private drop(event) { + const dragEl = event.drag.el; + const dropEl = event.drop.el; + console.log(this.modelService.getNodes()); + + + this.resizeParent(dragEl, dropEl); + + const nodeLeft = dragEl.getBoundingClientRect().left; + const nodeTop = dragEl.getBoundingClientRect().top; + const parentLeft = dropEl.getBoundingClientRect().left; + const parentTop = dropEl.getBoundingClientRect().top; + const left = nodeLeft - parentLeft + dropEl.scrollLeft; + const top = nodeTop - parentTop + dropEl.scrollTop; + dragEl.style.top = top + 'px'; + dragEl.style.left = left + 'px'; + + // 12 is title height + const nameHeight = this.getNodeNameHeight(dragEl); + this.modelService.updatePosition(dragEl.id, left, top, dragEl.getBoundingClientRect().width, dragEl.getBoundingClientRect().height - nameHeight); + + const originalParentNode = this.getParentNodeEl(dragEl); + const originalParentNodeId = originalParentNode ? originalParentNode.id : this.modelService.rootNodeId; + + const targetParentNodeId = dropEl.classList.contains('node') ? dropEl.id : this.modelService.rootNodeId; + this.changeParent(dragEl.id, originalParentNodeId, targetParentNodeId); + } + + private getNodeNameHeight(element: any): number { + const children = element.children; + if (children && children.length) { + const nameElement = children[0]; + if (nameElement && nameElement.className === 'name') { + return nameElement.getBoundingClientRect().height; + } + } + return 0; + } + + private changeParent(id: string, originalParentNodeId: string, targetParentNodeId: string) { + if (originalParentNodeId !== targetParentNodeId) { + this.jsplumbInstanceMap.get(originalParentNodeId).removeAllEndpoints(id); + this.modelService.changeParent(id, originalParentNodeId, targetParentNodeId); + } + } + + private getParentNodeEl(element) { + while (!(element.parentNode.classList.contains('node') || element.parentNode.classList.contains('canvas'))) { + element = element.parentNode; + } + + if (element.parentNode.classList.contains('canvas')) { // top level node + return null; + } else { + return element.parentNode; + } + } + + public canvasDroppable() { + const jsplumbInstance = this.jsplumbInstanceMap.get(this.modelService.rootNodeId); + const canvasSelector = jsplumbInstance.getSelector('.canvas'); + jsplumbInstance.droppable(canvasSelector, { + scope: 'node', + rank: 0, + grid: [5, 5], + drop: event => this.drop(event), + }); + } + + public buttonDraggable() { + const jsplumbInstance = this.jsplumbInstanceMap.get(this.modelService.rootNodeId); + const selector = jsplumbInstance.getSelector('.item'); + jsplumbInstance.draggable(selector, { + scope: 'btn', + clone: true + }); + } + + public buttonDroppable() { + const jsplumbInstance = this.jsplumbInstanceMap.get(this.modelService.rootNodeId); + const selector = jsplumbInstance.getSelector('.canvas'); + jsplumbInstance.droppable(selector, { + scope: 'btn', + // grid: [5, 5], + drop: event => { + const el = jsplumbInstance.getSelector(event.drag.el); + const type = el.attributes.nodeType.value; + let typeId = null; + if (el.attributes.nodeTypeId && el.attributes.nodeTypeId.value) { + typeId = el.attributes.nodeTypeId.value; + } + // Mouse position minus drop canvas start position plus scroll position. + let left = event.e.x - event.drop.pagePosition[0] + event.drop.el.scrollLeft; + let top = event.e.y - event.drop.pagePosition[1] + event.drop.el.scrollTop; + if (0 > left) { + left = 0; + } + if (0 > top) { + top = 0; + } + const name = el.attributes.name.value; + this.modelService.addNode(type, typeId, name, left, top); + }, + }); + } + + public remove(node: WorkflowNode) { + const jsplumbInstance = this.jsplumbInstanceMap.get(node.parentId); + + // unsubscription4Connection + const connectionsAsSource = jsplumbInstance.select({source: node.id}); + this.unsubscription4Connection(connectionsAsSource); + const connectionsAsTarget = jsplumbInstance.select({target: node.id}); + this.unsubscription4Connection(connectionsAsTarget); + + jsplumbInstance.remove(node.id); + } + + public resizeParent(element: any, parentElement: any) { + if (parentElement.classList.contains(this.rootClass)) { + return; + } + + if (!parentElement.classList.contains('node')) { + this.resizeParent(element, parentElement.parentNode); + return; + } + + const leftResized = this.resizeParentLeft(element, parentElement); + const rightResized = this.resizeParentRight(element, parentElement); + const topResized = this.resizeParentTop(element, parentElement); + const bottomResized = this.resizeParentBottom(element, parentElement); + + if (leftResized || rightResized || topResized || bottomResized) { + if (parentElement.classList.contains('node')) { + const rect = parentElement.getBoundingClientRect(); + const nameHeight = this.getNodeNameHeight(parentElement); + this.modelService.updatePosition(parentElement.id, + parentElement.offsetLeft, + parentElement.offsetTop, + // title height + rect.width, rect.height - nameHeight); + } + this.resizeParent(parentElement, parentElement.parentNode); + } + } + + private resizeParentLeft(element: any, parentElement: any): boolean { + let resized = false; + + const actualLeft = element.getBoundingClientRect().left; + const actualParentLeft = parentElement.getBoundingClientRect().left; + + if (actualLeft - this.padding < actualParentLeft) { + const width = actualParentLeft - actualLeft + this.padding; + + this.translateElement(parentElement, -width, 0, width, 0); + this.translateChildren(parentElement, element, width, 0); + resized = true; + } + + return resized; + } + + private resizeParentRight(element: any, parentElement: any): boolean { + let resized = false; + + const actualLeft = element.getBoundingClientRect().left; + const actualRight = actualLeft + element.offsetWidth; + + const actualParentLeft = parentElement.getBoundingClientRect().left; + + if ((actualParentLeft + parentElement.offsetWidth) < actualRight + this.padding) { + this.setElementWidth(parentElement, actualRight + this.padding - actualParentLeft); + resized = true; + } + + return resized; + } + + private resizeParentBottom(element: any, parentElement: any): boolean { + let resized = false; + + const actualTop = element.getBoundingClientRect().top; + const actualBottom = actualTop + element.offsetHeight; + + const actualParentTop = parentElement.getBoundingClientRect().top; + const actualParentBottom = actualParentTop + parentElement.offsetHeight; + + if (actualParentBottom < actualBottom + this.padding) { + this.setElementHeight(parentElement, actualBottom + this.padding - actualParentTop); + resized = true; + } + + return resized; + } + + private resizeParentTop(element: any, parentElement: any): boolean { + let resized = false; + + const actualTop = element.getBoundingClientRect().top; + const actualParentTop = parentElement.getBoundingClientRect().top; + + if (actualTop - this.padding < actualParentTop) { + const height = actualParentTop - actualTop + this.padding; + + this.translateElement(parentElement, 0, -height, 0, height); + this.translateChildren(parentElement, element, 0, height); + resized = true; + } + + return resized; + } + + private translateElement(element, left: number, top: number, width: number, height: number) { + const offsetLeft = element.offsetLeft + left; + element.style.left = offsetLeft + 'px'; + + const offsetTop = element.offsetTop + top; + element.style.top = offsetTop + 'px'; + + const offsetWidth = element.offsetWidth + width; + element.style.width = offsetWidth + 'px'; + + const offsetHeight = element.offsetHeight + height; + element.style.height = offsetHeight + 'px'; + + if (element.classList.contains('node')) { + const node = this.modelService.getNodeMap().get(element.id); + this.jsplumbInstanceMap.get(node.parentId).revalidate(element.id); + } + } + + private translateChildren(parentElment, excludeElement, left: number, top: number) { + const len = parentElment.children.length; + for (let i = 0; i < len; i++) { + const childElment = parentElment.children[i]; + if (childElment.localName === 'wfm-node') { + this.translateElement(childElment.children[0], left, top, 0, 0); + } + } + } + + private setElementHeight(element, height: number) { + element.style.height = height + 'px'; + } + + private setElementWidth(element, width: number) { + element.style.width = width + 'px'; + } + + private getActualPosition(element, offset: string) { + let actualPosition = element[offset]; + let current = element.offsetParent; + while (current !== null) { + actualPosition += element[offset]; + current = current.offsetParent; + } + return actualPosition; + } +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/model.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/model.service.ts new file mode 100644 index 00000000..8cb7b86a --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/model.service.ts @@ -0,0 +1,625 @@ +/** + * 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 { isNullOrUndefined } from 'util'; + +import { PlanModel } from '../model/plan-model'; +import { PlanTreeviewItem } from '../model/plan-treeview-item'; +import { RestConfig } from '../model/rest-config'; +import { + Swagger, + SwaggerModel, + SwaggerModelSimple, + SwaggerPrimitiveObject, + SwaggerReferenceObject +} from '../model/swagger'; +import { ErrorEvent } from '../model/workflow/error-event'; +import { IntermediateCatchEvent } from '../model/workflow/intermediate-catch-event'; +import { NodeType } from '../model/workflow/node-type.enum'; +import { Parameter } from '../model/workflow/parameter'; +import { Position } from '../model/workflow/position'; +import { RestTask } from '../model/workflow/rest-task'; +import { SequenceFlow } from '../model/workflow/sequence-flow'; +import { StartEvent } from '../model/workflow/start-event'; +import { SubProcess } from '../model/workflow/sub-process'; +import { ToscaNodeTask } from '../model/workflow/tosca-node-task'; +import { WorkflowNode } from '../model/workflow/workflow-node'; +import { NodeTemplate } from '../model/topology/node-template'; +import { ValueSource } from '../model/value-source.enum'; +import { BroadcastService } from './broadcast.service'; +import { RestService } from './rest.service'; +import { SwaggerTreeConverterService } from './swagger-tree-converter.service'; +import { TimerEventDefinition, TimerEventDefinitionType } from "../model/workflow/timer-event-definition"; +import { InterfaceService } from './interface.service'; +import { ServiceTask } from '../model/workflow/service-task'; +import { NodeTypeService } from './node-type.service'; +import { WorkflowUtil } from '../util/workflow-util'; +import { TranslateService } from '@ngx-translate/core'; +import { NoticeService } from './notice.service'; + +/** + * ModelService + * provides all operations about plan model. + */ +@Injectable() +export class ModelService { + public rootNodeId = 'root'; + + private planModel: PlanModel = new PlanModel(); + private tempPlanModel: PlanModel = new PlanModel(); + + constructor(private interfaceService: InterfaceService, private broadcastService: BroadcastService, + private restService: RestService, private nodeTypeService: NodeTypeService, + private translate: TranslateService, private notice: NoticeService) { + this.broadcastService.initModel$.subscribe(planModel => { + planModel.data.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 = planModel; + this.tempPlanModel = WorkflowUtil.deepClone(this.planModel); + }); + // Do not use restConfig property. + // this.broadcastService.updateModelRestConfig$.subscribe(restConfigs => { + // this.updateRestConfig(restConfigs); + // }); + } + + public getPlanModel(): PlanModel { + return this.planModel; + } + + public getChildrenNodes(parentId: string): WorkflowNode[] { + if (!parentId || parentId === this.rootNodeId) { + return this.planModel.data.nodes; + } else { + const node = this.getNodeMap().get(parentId); + if (node.type === 'subProcess') { + return (<SubProcess>node).children; + } else { + return []; + } + } + } + + public getNodes(): WorkflowNode[] { + return this.planModel.data.nodes; + } + + public getSequenceFlow(sourceRef: string, targetRef: string): SequenceFlow { + const node = this.getNodeMap().get(sourceRef); + if (node) { + const sequenceFlow = node.connection.find(tmp => tmp.targetRef === targetRef); + return sequenceFlow; + } else { + return null; + } + } + + public addNode(type: string, typeId: string, name: string, left: number, top: number) { + const id = this.generateNodeProperty('id', type); + const nodeName = this.generateNodeProperty('name', name); + const workflowPos = new Position(left, top); + let node; + if ('serviceTask' === type || 'scriptTask' === type || 'restTask' === type) { + node = this.createNodeByTypeId(id, nodeName, type, typeId, workflowPos); + } else { + node = this.createNodeByType(id, nodeName, type, workflowPos); + } + this.planModel.data.nodes.push(node); + } + + private generateNodeProperty(key: string, type: string): string { + let nodeProperty = type; + const nodes = this.getNodes(); + console.log(nodes); + const existNode = nodes.find(node => node[key] === nodeProperty); + if (existNode) { + let count = 2; + do { + nodeProperty = type + '_' + count; + count++; + } while (nodes.find(node => node[key] === nodeProperty)) + } + return nodeProperty; + } + + private generateNodeName(typeId: string): string { + const language = this.translate.currentLang.indexOf('en') > -1 ? 'en_US' : 'zh_CN'; + const nodeType = this.nodeTypeService.getNodeDataTypeById(typeId); + let displayName; + if (nodeType.displayName && nodeType.displayName[language]) { + displayName = nodeType.displayName[language]; + } else { + displayName = nodeType.type; + } + return this.generateNodeProperty('name', displayName); + } + + public createNodeByTypeId(id: string, name: string, type: string, typeId: string, position: Position): WorkflowNode { + const nodeDataType = this.nodeTypeService.getNodeDataTypeById(typeId); + const initPosition = new Position(position.left, position.top, nodeDataType.icon.width, nodeDataType.icon.height); + // switch (type) { + // case NodeType[NodeType.serviceTask]: + // let serviceTask: ServiceTask = { + // id: id, type: type, name: name, parentId: this.rootNodeId, + // position: initPosition, connection: [], class: nodeDataType.activity.class, + // input: nodeDataType.activity.input, output: nodeDataType.activity.output + // }; + // return serviceTask; + // case NodeType[NodeType.scriptTask]: + // let scriptTask: ScriptTask = { + // id: id, type: type, name: name, parentId: this.rootNodeId, + // position: initPosition, connection: [], scriptFormat: nodeDataType.activity.scriptFormat, + // script: nodeDataType.activity.script + // }; + // return scriptTask; + // case NodeType[NodeType.restTask]: + // let restTaskNode: RestTask = { + // id: id, type: type, name: name, parentId: this.rootNodeId, + // position: initPosition, connection: [], produces: [], consumes: [], parameters: [], responses: [] + // }; + // return restTaskNode; + // default: + let node: WorkflowNode = { + id: id, type: type, typeId: nodeDataType.id, icon: nodeDataType.icon.name, name: name, + parentId: this.rootNodeId, position: initPosition, connection: [] + }; + return 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.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: <TimerEventDefinition>{ type: TimerEventDefinitionType[TimerEventDefinitionType.timeDuration] } + }; + return intermediateCatchEvent; + 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 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.data.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 updateRestConfig(restConfigs: RestConfig[]): void { + this.planModel.data.configs = { restConfigs: restConfigs }; + // console.log(this.planModel.configs); + } + + public getNodeMap(): Map<string, WorkflowNode> { + const map = new Map<string, WorkflowNode>(); + this.toNodeMap(this.planModel.data.nodes, map); + return map; + } + + public getAncestors(id: string): WorkflowNode[] { + const nodeMap = this.getNodeMap(); + const ancestors = []; + + let ancestor = nodeMap.get(id); + do { + ancestor = this.getParentNode(ancestor.id, nodeMap); + if (ancestor && ancestor.id !== id) { + ancestors.push(ancestor); + } + } while (ancestor); + + return ancestors; + } + + private getParentNode(id: string, nodeMap: Map<string, WorkflowNode>): WorkflowNode { + let parentNode; + nodeMap.forEach((node, key) => { + if (NodeType[NodeType.subProcess] === node.type) { + const childNode = (<SubProcess>node).children.find(child => child.id === id); + if (childNode) { + parentNode = node; + } + } + + }); + + return parentNode; + } + + public isDescendantNode(node: WorkflowNode, descendantId: string): boolean { + if (NodeType[NodeType.subProcess] !== node.type) { + return false; + } + const tmp = (<SubProcess>node).children.find(child => { + return child.id === descendantId || (NodeType[NodeType.subProcess] === child.type && this.isDescendantNode(<SubProcess>child, descendantId)); + }); + + return tmp !== undefined; + } + + private toNodeMap(nodes: WorkflowNode[], map: Map<string, WorkflowNode>) { + nodes.forEach(node => { + if (node.type === 'subProcess') { + this.toNodeMap((<SubProcess>node).children, map); + } + map.set(node.id, node); + }); + } + + 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); + } + } + } + + 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(parentId: string, nodeId: string): WorkflowNode { + const nodeMap = this.getNodeMap(); + + const nodes = this.getChildrenNodes(parentId); + + // delete related connections + nodes.forEach(node => this.deleteConnection(node.id, nodeId)); + + // delete current node + const index = nodes.findIndex(node => node.id === nodeId); + if (index !== -1) { + const node = nodes.splice(index, 1)[0]; + node.connection = []; + return node; + } + + return null; + } + + 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 getPlanParameters(nodeId: string): PlanTreeviewItem[] { + const preNodeList = new Array<WorkflowNode>(); + this.getPreNodes(nodeId, this.getNodeMap(), preNodeList); + + return this.loadNodeOutputs(preNodeList); + } + + private loadNodeOutputs(nodes: WorkflowNode[]): PlanTreeviewItem[] { + const params = new Array<PlanTreeviewItem>(); + nodes.forEach(node => { + switch (node.type) { + case NodeType[NodeType.startEvent]: + params.push(this.loadOutput4StartEvent(<StartEvent>node)); + break; + case NodeType[NodeType.toscaNodeManagementTask]: + params.push(this.loadOutput4ToscaNodeTask(<ToscaNodeTask>node)); + break; + case NodeType[NodeType.restTask]: + params.push(this.loadOutput4RestTask(<RestTask>node)); + break; + default: + break; + } + }); + + return params; + } + + private loadOutput4StartEvent(node: StartEvent): PlanTreeviewItem { + 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}]`, [], false); + item.children.push(this.createStatusCodeTreeViewItem(node.id)); + + if (node.responses && 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.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 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) { + // 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, nodeMap: Map<string, WorkflowNode>, preNodes: WorkflowNode[]) { + const preNode4CurrentNode = []; + nodeMap.forEach(node => { + if (this.isPreNode(node, nodeId)) { + const existNode = preNodes.find(tmpNode => tmpNode.id === node.id); + if (existNode) { + // current node already exists. this could avoid loop circle. + } else { + preNode4CurrentNode.push(node); + preNodes.push(node); + } + } + }); + + preNode4CurrentNode.forEach(node => this.getPreNodes(node.id, nodeMap, preNodes)); + } + + public isPreNode(preNode: WorkflowNode, id: string): boolean { + const targetNode = preNode.connection.find(connection => connection.targetRef === id); + return targetNode !== undefined; + } + + public save(callback?: Function) { + console.log('****************** save data *********************'); + console.log(JSON.stringify(this.planModel)); + // Check data + if(!this.checkData()){ + return; + } + this.interfaceService.saveModelData(this.planModel).subscribe(response => { + this.translate.get('WORKFLOW.MSG.SAVE_SUCCESS').subscribe((res: string) => { + this.notice.success(res); + // Update the cache. + this.tempPlanModel = WorkflowUtil.deepClone(this.planModel); + if (callback) { + callback(); + } + }); + }, error => { + this.translate.get('WORKFLOW.MSG.SAVE_FAIL').subscribe((res: string) => { + this.notice.error(res); + }); + }); + ; + } + + private createId() { + const nodeMap = this.getNodeMap(); + + for (let i = 0; i < nodeMap.size; i++) { + const key = 'node' + i; + if (!nodeMap.get(key)) { + return key; + } + } + + return 'node' + nodeMap.size; + } + + public isModify(): boolean { + // Compare the cache. + return JSON.stringify(this.planModel) != JSON.stringify(this.tempPlanModel); + } + + private checkData(): boolean { + if (this.planModel && this.planModel.data && this.planModel.data.nodes) { + let nodes = this.planModel.data.nodes; + for (let index = 0; index < nodes.length; index++) { + const node = nodes[index]; + if (NodeType[NodeType.startEvent] === node.type) { + let startEvent = node as StartEvent; + if (startEvent.parameters) { + for (let i = 0; i < startEvent.parameters.length; i++) { + const parameter = startEvent.parameters[i]; + if (!parameter.name) { + this.translate.get('WORKFLOW.MSG.PROCESS_VARIABLE_EMPTY').subscribe((res: string) => { + this.notice.error(res); + }); + return false; + } + if(i + 1 < startEvent.parameters.length){ + for (let j = i + 1; j < startEvent.parameters.length; j++) { + const param = startEvent.parameters[j]; + if(parameter.name === param.name){ + this.translate.get('WORKFLOW.MSG.PROCESS_VARIABLE_SAME').subscribe((res: string) => { + this.notice.error(res); + }); + return false; + } + } + } + } + } + } + } + } + return true; + } +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/node-type.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/node-type.service.ts new file mode 100644 index 00000000..4c66db18 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/node-type.service.ts @@ -0,0 +1,68 @@ +/** + * 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 { HttpService } from "../util/http.service"; +import { BroadcastService } from "./broadcast.service"; +import { NodeDataType } from "../model/node-data-type/node-data-type"; +import { Parameter } from "../model/workflow/parameter"; +import { TranslateService } from "@ngx-translate/core"; + +@Injectable() +export class NodeTypeService { + private nodeDataTypes: NodeDataType[] = []; + private nodeDataTypeUrl = '/api/workflow-modeler/v1/ext-activities?sence=test'; + + constructor(private httpService: HttpService, private translateService: TranslateService, + private broadcastService: BroadcastService) { + this.initNodeDataTypes(); + } + + private initNodeDataTypes(): void { + this.httpService.get(this.nodeDataTypeUrl) + .toPromise() + .then(resp => { + if (resp && 0 < resp.length) { + this.nodeDataTypes = resp; + } + this.broadcastService.broadcast(this.broadcastService.updateNodeTypeConfig, null); + }); + } + + public getAllNodeDataTypes(): NodeDataType[] { + return this.nodeDataTypes; + } + + public getNodeDataTypeById(id: string): NodeDataType | null { + return this.nodeDataTypes.find(nodeDataType => nodeDataType.id === id); + } + + public GetI18nName(key: string, displayName: any): string { + //todo: add logic + let name = key; + if (displayName) { + let language = 'zh_CN'; + if (this.translateService.currentLang.indexOf('en') > -1) { + language = 'en_US'; + } + if (displayName.language && '' != displayName.language) { + name = displayName.language; + } + } + return name; + } + + public static GetParameterByDataType(nodeDataType: any): Parameter { + //todo: add logic + let param = new Parameter('', '', ''); + return param; + } +}
\ No newline at end of file diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/notice.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/notice.service.ts new file mode 100644 index 00000000..ea081e96 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/notice.service.ts @@ -0,0 +1,54 @@ +/** + * 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 { Subject } from 'rxjs/Subject'; + +import { Notice } from '../model/notice'; +import { NoticeType } from '../model/notice-type.enum'; + +/** + * NotifyService + * display notify infos. + */ +@Injectable() +export class NoticeService { + public showNotice = new Subject<Notice>(); + public showNotice$ = this.showNotice.asObservable(); + constructor() { } + + public success(content: string, timeout: number = 5000) { + this.addNotice(NoticeType.success, content, timeout); + } + + public info(content: string, timeout: number = 10000) { + this.addNotice(NoticeType.info, content, timeout); + } + + public warn(content: string, timeout: number = 30000) { + this.addNotice(NoticeType.warning, content, timeout); + } + + public error(content: string, timeout: number = 0) { + this.addNotice(NoticeType.danger, content, timeout); + } + + /** + * showNotify + * @param type + * @param content + */ + private addNotice(type: NoticeType, content: string, timeout: number = 0): void { + const notice = new Notice(type, content, timeout); + this.showNotice.next(notice); + } +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/rest.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/rest.service.ts new file mode 100644 index 00000000..e3bde51b --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/rest.service.ts @@ -0,0 +1,151 @@ +/******************************************************************************* + * 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 { Http, RequestOptionsArgs } from '@angular/http'; +import { Observable } from 'rxjs/Rx'; +import { isNullOrUndefined } from 'util'; + +import { SwaggerMethod } from '../model/swagger'; +import { SwaggerResponseClass } from '../model/swagger'; +import { Swagger, SwaggerSchemaObject } from '../model/swagger'; +import { RestConfig } from '../model/rest-config'; +import { BroadcastService } from './broadcast.service'; +import { NoticeService } from './notice.service'; +import { SettingService } from './setting.service'; + +@Injectable() +export class RestService { + private restConfigs: RestConfig[] = []; + private runtimeURL = '/openoapi/catalog/v1/sys/config'; + private msbPublishServiceURL = '/api/msdiscover/v1/publishservicelist'; + + constructor(private broadcastService: BroadcastService, private http: Http, + private noticeService: NoticeService, private settgingService: SettingService) { + this.settgingService.getSetting().subscribe(setting => { + if (true === setting.supportRestNode) { + this.initSwaggerInfo(); + } else { + this.broadcastService.broadcast(this.broadcastService.updateModelRestConfig, null); + } + }); + } + + public getRestConfigs(): RestConfig[] { + return this.restConfigs; + } + + public getRestConfig(id: string): RestConfig { + return this.restConfigs.find(tmp => tmp.id === id); + } + + public getSwaggerInfo(id: string): Swagger { + const restConfig = this.restConfigs.find(tmp => tmp.id === id); + return restConfig === undefined ? undefined : restConfig.swagger; + } + + public getResponseParameters(swagger: Swagger, interfaceUrl: string, operation: string): SwaggerResponseClass[] { + const path = swagger.paths[interfaceUrl]; + const method: SwaggerMethod = path[operation]; + let responses: SwaggerResponseClass[] = []; + + for (const key of Object.keys(method.responses)) { + if (key.startsWith('20') && method.responses[key].schema && method.responses[key].schema.$ref) { + let response: SwaggerResponseClass = method.responses[key]; + responses.push(response); + } + } + + return responses; + } + + public getDefinition(swagger: Swagger, position: string): SwaggerSchemaObject { + const definitionName = position.substring('#/definitions/'.length); + return swagger.definitions[definitionName]; + } + + private initSwaggerInfo(): void { + this.http.get(this.runtimeURL).subscribe(runtimeResponse => { + const tenant = runtimeResponse.json().tenant; + console.log('Current namespace is:' + tenant); + this.initSwaggerInfoByTenant(tenant); + }, error => { + console.warn('No tenant interface!'); + this.initSwaggerInfoByTenant(''); + }); + } + + private initSwaggerInfoByTenant(tenant: string): void { + this.http.get(this.msbPublishServiceURL, { params: { namespace: tenant } }).subscribe(serviceResponse => { + if (!Array.isArray(serviceResponse.json())) { + return; + } + const services = serviceResponse.json(); + const protocel = location.protocol.slice(0, location.protocol.length - 1); + const swaggerObservableArray: Observable<any>[] = []; + services.forEach(serviceInfo => { + if ('REST' === serviceInfo.protocol && protocel === serviceInfo.publish_protocol) { + // this service don't have sawgger file. + if ('/activiti-rest' !== serviceInfo.publish_url) { + const id = serviceInfo.serviceName + '.' + serviceInfo.version; + this.restConfigs.push(new RestConfig(id, serviceInfo.serviceName, serviceInfo.version, serviceInfo.publish_url)); + let swaggerUrl = ''; + if (undefined !== serviceInfo.swagger_url && '' !== serviceInfo.swagger_url) { + swaggerUrl = serviceInfo.publish_url + '/' + serviceInfo.swagger_url; + } else { + // default swagger url is: '/swagger.json' + swaggerUrl = serviceInfo.publish_url + '/swagger.json'; + } + const options: any = { + headers: { + Accept: 'application/json', + } + }; + swaggerObservableArray.push(this.http.get(swaggerUrl, options).timeout(5000).catch((error): Observable<any> => { + console.log('Request swagger from:"' + swaggerUrl + '" faild!'); + return Observable.of(null); + })); + } + } + }); + Observable.forkJoin(swaggerObservableArray).subscribe( + responses => { + let deleteArray: number[] = []; + responses.forEach((response, index) => { + // mark http get failed request index or set the swagger into restConfigs + if (null === response) { + deleteArray.push(index); + } else { + try { + const swagger = response.json(); + this.restConfigs[index].swagger = new Swagger(swagger); + } catch (e) { + deleteArray.push(index); + console.warn('Do not support this sawgger file format:' + response.text()); + } + } + }); + console.log('Get all swagger file finish.'); + // delete failed request from all restConfigs array + deleteArray.reverse(); + deleteArray.forEach(deleteIndex => { + this.restConfigs.splice(deleteIndex, 1); + }); + this.broadcastService.broadcast(this.broadcastService.updateModelRestConfig, this.restConfigs); + console.log('Load all swagger finished.'); + } + ); + }); + + } +} + diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/setting.service.spec.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/setting.service.spec.ts new file mode 100644 index 00000000..a85d2c27 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/setting.service.spec.ts @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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 { TestBed, inject } from '@angular/core/testing'; + +import { SettingService } from './setting.service'; + +describe('SettingService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [SettingService] + }); + }); + + it('should be created', inject([SettingService], (service: SettingService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/setting.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/setting.service.ts new file mode 100644 index 00000000..266c5533 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/setting.service.ts @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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 { Observable } from "rxjs/Rx"; + +import { HttpService } from '../util/http.service'; + +@Injectable() +export class SettingService { + + constructor(private http: HttpService) { } + + public getSetting(): Observable<any> { + const options: any = { + headers: { + Accept: 'application/json', + } + }; + return this.http.get('assets/global-setting.json', options); + } + +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/swagger-tree-converter.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/swagger-tree-converter.service.ts new file mode 100644 index 00000000..8128b44c --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/swagger-tree-converter.service.ts @@ -0,0 +1,244 @@ +/******************************************************************************* + * 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 { RestService } from './rest.service'; +import { Swagger } from "../model/swagger"; +import { ValueObject } from '../model/value-object'; +import { ValueType } from '../model/value-type.enum'; + +@Injectable() +export class SwaggerTreeConverterService { + + private swagger: Swagger; + + constructor(private restService: RestService) { } + + public schema2TreeNode(swagger: Swagger, key: string | number, schema: any, value?: any): any { + this.swagger = swagger; + if (schema.$ref) { + const treeNode = this.getTreeNodeBySwaggerDefinition(key, schema, value); + return treeNode; + } else { + value = this.getInitValue4Param(schema, value); + return this.parameter2TreeNode(key, schema, value); + } + } + + private getTreeNodeBySwaggerDefinition(key: string | number, schema: any, value: any): TreeNode { + const swaggerDefinition = this.restService.getDefinition(this.swagger, schema.$ref); + + const definitionCopy = WorkflowUtil.deepClone(swaggerDefinition); + + value = this.getInitValue4Param(definitionCopy, value); + + return this.schema2TreeNode(this.swagger, key, definitionCopy, value); + } + + private getInitValue4Param(definition: any, value: any) { + if (definition.$ref) { + definition = this.restService.getDefinition(this.swagger, definition.$ref); + } + let valueObject: ValueObject = { valueSource: ValueSource[ValueSource.string] }; + if (undefined == value) { + valueObject.value = definition.default; + if (ValueType[ValueType.array] === definition.type || ValueType[ValueType.object] === definition.type) { + valueObject.valueSource = ValueSource[ValueSource.Definition]; + } else { + valueObject.valueSource = definition.type; + } + } else { + valueObject.valueSource = value.valueSource; + valueObject.value = undefined === value.value ? definition.default : value.value; + } + if (definition.type === 'object') { + // if (undefined == value) { + // valueObject.value = definition.default; + // if (ValueType[ValueType.array] === definition.type || ValueType[ValueType.object] === definition.type) { + // valueObject.valueSource = ValueSource[ValueSource.Definition]; + // } else { + // valueObject.valueSource = definition.type; + // } + // } else { + // valueObject.valueSource = value.valueSource; + // valueObject.value = undefined === value.value ? definition.default : value.value; + // } + return this.getInitValue4Object(valueObject); + } else if (definition.type === 'array') { + return this.getInitValue4Array(valueObject); + } else { // primary type + // valueObject.value = undefined === value ? definition.default : value; + // valueObject.valueSource = definition.type; + return this.getInitValue4Primary(valueObject); + } + } + + private getInitValue4Object(value: any) { + const newValue = { + value: {}, + valueSource: ValueSource[ValueSource.Definition] + }; + + if (!value || '' === value) { + return newValue; + } else { + if (value.valueSource !== ValueSource[ValueSource.Definition]) { + return value; + } else { + if (typeof value.value !== 'object') { + value.value = {}; + } + return value; + } + } + } + + private getInitValue4Array(value: any) { + const newValue = { + value: [], + valueSource: ValueSource[ValueSource.Definition] + }; + + if (!value || '' === value) { + return newValue; + } else { + if (value.valueSource !== ValueSource[ValueSource.Definition]) { + return value; + } else { + if (!(value.value instanceof Array)) { + value.value = []; + } + return value; + } + } + } + + private getInitValue4Primary(value: any) { + const newValue = { + value: '', + valueSource: ValueSource[ValueSource.string] + }; + + if (!value) { + return newValue; + } else { + if (typeof value.value === 'object') { + value.value = ''; + } + return value; + } + } + + private parameter2TreeNode(name: string | number, definition: any, value: any): any { + const nodeType = this.getTreeNodeType(definition); + + const node = { + label: name, + type: nodeType, + required: definition.required, + children: [], + definition: definition, + value: value, + }; + + if (value.valueSource === ValueSource[ValueSource.Definition]) { + if (definition.type === 'object') { + node.children = this.getPropertyFromObject(definition, value.value); + } else if (definition.type === 'array') { + node.children = this.setChildrenForArray(definition, value.value); + } + } + + 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(definition: any, value: any[]): any[] { + const children = []; + value.forEach((itemValue, index) => { + const itemCopy = WorkflowUtil.deepClone(definition.items); + children.push(this.schema2TreeNode(this.swagger, index, itemCopy, itemValue)); + }); + + return children; + } + + private getPropertyFromObject(definition: any, value: any): TreeNode[] { + if (definition.properties) { + return this.getPropertyFromSimpleObject(definition.properties, value, definition.required); + } else if (definition.additionalProperties) { + return this.getPropertyFromMapOrDictionary(definition.additionalProperties, value); + } else { + console.log('getPropertyFromObject() return [], param is:' + JSON.stringify(definition)); + return []; + } + + } + + private getPropertyFromSimpleObject(properties: any, objectValue: any, required: string[]): TreeNode[] { + const treeNodes: TreeNode[] = []; + for (const key in properties) { + let property = properties[key]; + // init required property + property.required = false; + if (Array.isArray(required)) { + for (let index = 0; index < required.length; index++) { + if (required[index] === key) { + property.required = true; + break; + } + } + } + + objectValue[key] = this.getInitValue4Param(property, objectValue[key]); + + const treeNode = this.schema2TreeNode(this.swagger, key, property, objectValue[key]); + treeNodes.push(treeNode); + } + return treeNodes; + } + + private getPropertyFromMapOrDictionary(additionalProperties: any, mapOrDictionary: any): TreeNode[] { + const treeNodes: TreeNode[] = []; + for (const key in mapOrDictionary) { + const propertyCopy = WorkflowUtil.deepClone(additionalProperties); + propertyCopy.value = mapOrDictionary[key]; + + const treeNode = this.schema2TreeNode(this.swagger, key, propertyCopy, propertyCopy.value); + treeNode.keyEditable = true; + treeNodes.push(treeNode); + + if (mapOrDictionary[key] !== propertyCopy.value) { + mapOrDictionary[key] = propertyCopy.value; + } + } + return treeNodes; + } +} diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/tosca.service.spec.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/tosca.service.spec.ts new file mode 100644 index 00000000..a54fba63 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/tosca.service.spec.ts @@ -0,0 +1,26 @@ +/** + * 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 { TestBed, inject } from '@angular/core/testing'; + +import { ToscaService } from './tosca.service'; + +describe('ToscaService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ToscaService] + }); + }); + + it('should be created', inject([ToscaService], (service: ToscaService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/tosca.service.ts b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/tosca.service.ts new file mode 100644 index 00000000..dd62a717 --- /dev/null +++ b/deprecated-workflow-designer/sdc-workflow-designer-ui/src/app/services/tosca.service.ts @@ -0,0 +1,153 @@ +/** + * 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 { Observable } from "rxjs/Rx"; + +import { NodeTemplate } from '../model/topology/node-template'; +import { SettingService } from './setting.service'; +import { HttpService } from '../util/http.service'; +import { BroadcastService } from './broadcast.service'; + +@Injectable() +export class ToscaService { + private nodeTemplate: NodeTemplate[] = []; + private topologyProperties: { + name: string; + value: string; + }[] = []; + private namespace; + private serviceTemplateId; + private repositoryURL; + constructor(private settingService: SettingService, private httpService: HttpService, + private broadcastService: BroadcastService) { + this.settingService.getSetting().subscribe(setting => { + if (true === setting.supportToscaNode) { + // todo: need to load tosca interface and tosca properties + this.initTOSCAInfo(); + } else { + this.broadcastService.broadcast(this.broadcastService.updateModelToscaConfig, null); + } + }); + } + + public getNodeTemplate(): NodeTemplate[] { + return this.nodeTemplate; + } + + public getTopologyProperties() { + return this.topologyProperties; + } + + public initTOSCAInfo() { + this.getNodeTemplates().subscribe(nodes => { + if (0 === nodes.length) { + return; + } + + const subscribes = nodes.map(node => this.loadTopologyProperties(node)); + Observable.forkJoin(subscribes).map(nodesProperties => { + const allProperties: { name: string, value: string }[] = []; + nodesProperties.forEach((properties, index) => { + properties.forEach(property => { + // allProperties.push(nodes[index].name + '.' + property); + const propertyOption = { + name: `${nodes[index].name}.${property}`, + value: `[${nodes[index].name}].[${property}]` + }; + allProperties.push(propertyOption); + }); + }); + return allProperties; + }).subscribe(allProperties => { + this.topologyProperties = allProperties; + this.broadcastService.broadcast(this.broadcastService.updateModelToscaConfig, this.topologyProperties); + }); + }); + } + + public getNodeTemplates(): Observable<NodeTemplate[]> { + const url = 'servicetemplates/' + this.encode(this.namespace) + + '/' + this.encode(this.serviceTemplateId) + '/topologytemplate/'; + + return this.httpService.get(this.getFullUrl(url)).map(response => { + const nodeTemplates = []; + for (const key in response.nodeTemplates) { + if (response.nodeTemplates.hasOwnProperty(key)) { + const nodeTemplate = response.nodeTemplates[key]; + nodeTemplates.push({ + id: nodeTemplate.id, + type: nodeTemplate.type.replace(/^\{(.+)\}(.+)/, '$2'), + name: nodeTemplate.name, + namespace: nodeTemplate.type.replace(/^\{(.+)\}(.+)/, '$1'), + }); + } + } + return nodeTemplates; + }); + } + + public loadTopologyProperties(nodeTemplate: NodeTemplate): Observable<string[]> { + const url = 'nodetypes/' + this.encode(nodeTemplate.namespace) + + '/' + this.encode(nodeTemplate.type) + '/propertiesdefinition/winery/list/'; + + return this.httpService.get(this.getFullUrl(url)).map(properties => + properties.map(property => property.key)); + } + + public loadNodeTemplateInterfaces(nodeTemplate: NodeTemplate): Observable<string[]> { + const url = 'nodetypes/' + this.encode(nodeTemplate.namespace) + + '/' + this.encode(nodeTemplate.type) + '/interfaces/'; + + return this.httpService.get(this.getFullUrl(url)); + } + public loadNodeTemplateOperations(nodeTemplate: NodeTemplate, + interfaceName: string): Observable<string[]> { + const url = 'nodetypes/' + this.encode(nodeTemplate.namespace) + + '/' + this.encode(nodeTemplate.type) + '/interfaces/' + this.encode(interfaceName) + '/operations/'; + + return this.httpService.get(this.getFullUrl(url)); + } + + public loadNodeTemplateOperationParameter(nodeTemplate: NodeTemplate, + interfaceName: string, + operation: string): Observable<any> { + const relativePath = 'nodetypes/' + this.encode(nodeTemplate.namespace) + '/' + this.encode(nodeTemplate.type) + + '/interfaces/' + this.encode(interfaceName) + '/operations/' + this.encode(operation) + '/'; + + // input parameters + const inputObservable = this.httpService + .get(this.getFullUrl(relativePath + 'inputparameters')); + + // output parameters + const outputObservable = this.httpService + .get(this.getFullUrl(relativePath + 'outputparameters')); + + return Observable.forkJoin([inputObservable, outputObservable]).map(params => { + return { + input: params[0], + output: params[1], + }; + }); + } + + private decode(param: string): string { + return decodeURIComponent(decodeURIComponent(param)); + } + + private encode(param: string): string { + return encodeURIComponent(encodeURIComponent(param)); + } + + private getFullUrl(relativePath: string) { + return this.repositoryURL + relativePath; + } +} |