2 ============LICENSE_START==========================================
3 ===================================================================
4 Copyright (C) 2019 Orange. All rights reserved.
5 ===================================================================
6 Modification Copyright (c) 2020 IBM
7 ===================================================================
8 Modification Copyright (c) 2020 Orange
9 ===================================================================
11 Unless otherwise specified, all software contained herein is licensed
12 under the Apache License, Version 2.0 (the License);
13 you may not use this software except in compliance with the License.
14 You may obtain a copy of the License at
16 http://www.apache.org/licenses/LICENSE-2.0
18 Unless required by applicable law or agreed to in writing, software
19 distributed under the License is distributed on an "AS IS" BASIS,
20 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 See the License for the specific language governing permissions and
22 limitations under the License.
23 ============LICENSE_END============================================
26 import dagre from 'dagre';
27 import graphlib from 'graphlib';
28 import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
29 import * as joint from 'jointjs';
30 import './jointjs/elements/palette.function.element';
31 import './jointjs/elements/action.element';
32 import './jointjs/elements/board.function.element';
33 import { DesignerStore } from './designer.store';
34 import { ActionElementTypeName } from 'src/app/common/constants/app-constants';
35 import { GraphUtil } from './graph.util';
36 import { GraphGenerator } from './graph.generator.util';
37 import { FunctionsStore } from './functions.store';
38 import { Subject } from 'rxjs';
39 import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
40 import { BluePrintDetailModel } from '../model/BluePrint.detail.model';
41 import { ActivatedRoute, Router } from '@angular/router';
42 import { DesignerService } from './designer.service';
43 import { FilesContent, FolderNodeElement } from '../package-creation/mapping-models/metadata/MetaDataTab.model';
44 import { PackageCreationModes } from '../package-creation/creationModes/PackageCreationModes';
45 import { PackageCreationBuilder } from '../package-creation/creationModes/PackageCreationBuilder';
46 import { PackageCreationStore } from '../package-creation/package-creation.store';
47 import { PackageCreationService } from '../package-creation/package-creation.service';
48 import { PackageCreationUtils } from '../package-creation/package-creation.utils';
49 import * as JSZip from 'jszip';
50 import { PackageCreationExtractionService } from '../package-creation/package-creation-extraction.service';
51 import { CBAPackage } from '../package-creation/mapping-models/CBAPacakge.model';
52 import { TopologyTemplate } from './model/designer.topologyTemplate.model';
53 import { ToastrService } from 'ngx-toastr';
54 import { DesignerDashboardState } from './model/designer.dashboard.state';
55 import { NgxUiLoaderService } from 'ngx-ui-loader';
58 selector: 'app-designer',
59 templateUrl: './designer.component.html',
60 styleUrls: ['./designer.component.css'],
61 encapsulation: ViewEncapsulation.None
63 export class DesignerComponent implements OnInit, OnDestroy {
65 controllerSideBar: boolean;
66 actionAttributesSideBar: boolean;
67 functionAttributeSidebar: boolean;
68 viewedPackage: BluePrintDetailModel = new BluePrintDetailModel();
69 customActionName: string;
73 boardGraph: joint.dia.Graph;
74 boardPaper: joint.dia.Paper;
76 paletteGraph: joint.dia.Graph;
77 palettePaper: joint.dia.Paper;
78 ngUnsubscribe = new Subject();
79 opt = { tx: 100, ty: 100 };
81 folder: FolderNodeElement = new FolderNodeElement();
82 zipFile: JSZip = new JSZip();
83 cbaPackage: CBAPackage;
84 actions: string[] = [];
87 designerState: DesignerDashboardState;
88 currentActionName: string;
92 private designerStore: DesignerStore,
93 private functionStore: FunctionsStore,
94 private packageCreationStore: PackageCreationStore,
95 private packageCreationUtils: PackageCreationUtils,
96 private graphUtil: GraphUtil,
97 private graphGenerator: GraphGenerator,
98 private route: ActivatedRoute,
99 private router: Router,
100 private designerService: DesignerService,
101 private packageCreationService: PackageCreationService,
102 private packageCreationExtractionService: PackageCreationExtractionService,
103 private activatedRoute: ActivatedRoute,
104 private ngxService: NgxUiLoaderService,
105 private toastService: ToastrService) {
106 this.controllerSideBar = true;
107 this.actionAttributesSideBar = false;
108 this.showAction = false;
109 this.functionAttributeSidebar = false;
114 this.controllerSideBar = !this.controllerSideBar;
115 if (this.controllerSideBar === false) {
116 this.cl = 'editBar2';
118 if (this.controllerSideBar === true) {
124 this.actionAttributesSideBar = !this.actionAttributesSideBar;
129 this.zipFile.generateAsync({ type: 'blob' })
131 const formData = new FormData();
132 formData.append('file', blob);
133 this.designerService.publishBlueprint(formData).subscribe(res => {
134 this.toastService.success('Package Deployed Successfuly');
135 console.log('Package Deployed...');
137 this.toastService.error(error.message, 'Package error');
140 // this.deployBluePrint = false;
147 * - There is a board (main paper) that will the action and function selected from the palette
148 * itmes in this board will be used to create tosca workflow and node templates
149 * - There is also palette , whis contains all the possible functions and actions
150 * that can be dragged into the board
151 * - There is also a fly paper , which is temporarliy paper created on the fly
152 * when items is dragged from the palette- and it's deleted when the item is dropped over
154 * for more info about the drag and drop algorithem used please visit the following link:
155 * https://stackoverflow.com/a/36932973/1340034
159 // this.ngxService.start();
160 this.customActionName = this.route.snapshot.paramMap.get('actionName');
161 if (this.customActionName !== '') {
162 this.showAction = true;
164 this.initializeBoard();
165 this.initializePalette();
166 this.stencilPaperEventListeners();
167 const id = this.route.snapshot.paramMap.get('id');
168 this.designerService.getPagedPackages(id).subscribe(
169 (bluePrintDetailModels) => {
170 if (bluePrintDetailModels) {
171 this.viewedPackage = bluePrintDetailModels[0];
172 this.packageCreationService.downloadPackage(this.viewedPackage.artifactName + '/'
173 + this.viewedPackage.artifactVersion)
174 .subscribe(response => {
175 const blob = new Blob([response], { type: 'application/octet-stream' });
176 this.packageCreationExtractionService.extractBlobToStore(blob);
180 this.packageCreationStore.state$.subscribe(cba => {
181 this.cbaPackage = cba;
182 console.log(cba.templateTopology.content);
183 this.designerStore.saveSourceContent(cba.templateTopology.content);
188 * the code to retrieve from server is commented
190 this.functionStore.state$
192 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
193 takeUntil(this.ngUnsubscribe))
194 .subscribe(state => {
196 if (state.serverFunctions) {
197 console.log('inside subscriotn on functions store -->', state.serverFunctions);
199 // this.viewedFunctions = state.functions;
200 const list = state.serverFunctions;
202 const cells = this.graphUtil.buildPaletteGraphFromList(list);
203 this.paletteGraph.resetCells(cells);
206 cells.forEach(cell => {
207 cell.translate(5, (cell.attributes.size.height + 5) * idx++);
212 this.designerStore.state$
214 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
215 takeUntil(this.ngUnsubscribe))
216 .subscribe(state => {
217 this.designerState = state;
218 if (state.sourceContent) {
219 console.log('inside desinger.component---> ', state);
220 // generate graph from store objects if exist
221 const topologtTemplate: TopologyTemplate = JSON.parse(state.sourceContent);
222 console.log(topologtTemplate);
223 delete state.sourceContent;
224 this.graphGenerator.clear(this.boardGraph);
225 this.graphGenerator.populate(topologtTemplate, this.boardGraph);
227 console.log('all cells', this.boardGraph.getCells());
229 * auto arrange elements in graph
230 * https://resources.jointjs.com/docs/jointjs/v3.1/joint.html#layout.DirectedGraph
232 joint.layout.DirectedGraph.layout(this.boardGraph.getCells(), {
235 setLinkVertices: false,
238 clusterPadding: { top: 100, left: 30, right: 10, bottom: 100 },
243 for (const workflowsKey in topologtTemplate.workflows) {
244 if (workflowsKey && !this.actions.includes(workflowsKey)) {
245 this.actions.push(workflowsKey);
246 /* tslint:disable:no-string-literal */
247 if (!this.designerState.template.workflows[workflowsKey]['inputs']) {
248 this.designerState.template.workflows[workflowsKey]['inputs'] = {};
250 if (!this.designerState.template.workflows[workflowsKey]['outputs']) {
251 this.designerState.template.workflows[workflowsKey]['outputs'] = {};
259 this.functionStore.retrieveFuntions();
260 this.activatedRoute.paramMap.subscribe(res => {
261 this.packageId = res.get('id');
266 initializePalette() {
267 if (!this.paletteGraph) {
268 this.paletteGraph = new joint.dia.Graph();
269 this.palettePaper = new joint.dia.Paper({
270 el: $('#palette-paper'),
271 model: this.paletteGraph,
274 height: $('#palette-paper').height(),
276 // color: 'rgba(0, 255, 0, 0.3)'
279 // elements in paletter need to be fixed, please refer to flying paper concept
285 if (!this.boardGraph) {
286 console.log('initializeBoard...');
287 this.boardGraph = new joint.dia.Graph();
288 this.boardPaper = new joint.dia.Paper({
289 el: $('#board-paper'),
290 model: this.boardGraph,
293 // origin: { x: 80, y: 70 },
294 gridSize: 10, // background line sizes
297 // color: 'rgba(0, 255, 0, 0.3)'
299 cellViewNamespace: joint.shapes
302 this.boardPaper.on('all', element => {
303 // console.log(element);
306 this.boardPaper.on('link:pointerdown', link => {
310 this.boardPaper.on('element:pointerdown', element => {
311 // this.modelSelected.emit(element.model.get('model'));
314 this.boardPaper.on('blank:pointerclick', () => {
315 // this.selectedModel = undefined;
318 this.boardGraph.on('change:position', (cell) => {
320 const parentId = cell.get('parent');
326 const parent = this.boardGraph.getCell(parentId);
328 const parentBbox = parent.getBBox();
329 const cellBbox = cell.getBBox();
330 if (parentBbox.containsPoint(cellBbox.origin()) &&
331 parentBbox.containsPoint(cellBbox.topRight()) &&
332 parentBbox.containsPoint(cellBbox.corner()) &&
333 parentBbox.containsPoint(cellBbox.bottomLeft())) {
335 // All the four corners of the child are inside
340 // Revert the child position.
341 cell.set('position', cell.previous('position'));
344 console.log('done initializing Board...');
347 insertCustomActionIntoBoard() {
348 console.log('saving action to store action workflow....');
349 let actionName = this.graphUtil.generateNewActionName();
350 while (this.actions.includes(actionName)) {
351 actionName = this.graphUtil.generateNewActionName();
353 this.graphUtil.createCustomActionWithName(actionName, this.boardGraph);
354 this.designerStore.addDeclarativeWorkFlow(actionName);
355 this.actions.push(actionName);
358 stencilPaperEventListeners() {
359 this.palettePaper.on('cell:pointerdown', (draggedCell, pointerDownEvent, x, y) => {
363 style="position:fixed;z-index:100;opacity:.7;pointer-event:none;background-color: transparent !important;"></div>`
365 const flyGraph = new joint.dia.Graph();
366 const flyPaper = new joint.dia.Paper({
371 const flyShape = draggedCell.model.clone();
372 const pos = draggedCell.model.position();
378 flyShape.position(0, 0);
379 flyGraph.addCell(flyShape);
380 $('#flyPaper').offset({
381 left: pointerDownEvent.pageX - offset.x,
382 top: pointerDownEvent.pageY - offset.y
384 $('body').on('mousemove.fly', mouseMoveEvent => {
385 $('#flyPaper').offset({
386 left: mouseMoveEvent.pageX - offset.x,
387 top: mouseMoveEvent.pageY - offset.y
391 $('body').on('mouseup.fly', mouseupEvent => {
392 const mouseupX = mouseupEvent.pageX;
393 const mouseupY = mouseupEvent.pageY;
394 const target = this.boardPaper.$el.offset();
395 // Dropped over paper ?
396 if (mouseupX > target.left &&
397 mouseupX < target.left + this.boardPaper.$el.width() &&
398 mouseupY > target.top && y < target.top + this.boardPaper.$el.height()) {
399 const functionType = this.graphUtil.getFunctionTypeFromPaletteFunction(flyShape);
400 // step name is CDS realted terminology, please refer to tosca types
401 const stepName = functionType;
402 const functionElementForBoard = this.graphUtil.dropFunctionOverActionWithPosition(
403 stepName, functionType,
408 const parentCell = this.graphUtil.getParent(functionElementForBoard, this.boardPaper);
411 parentCell.model.attributes.type === ActionElementTypeName &&
412 this.graphUtil.canEmpedMoreChildern(parentCell.model, this.boardGraph)) {
414 if (this.graphUtil.isEmptyParent(parentCell.model)) {
415 // first function in action
416 const actionName = parentCell.model.attributes.attrs['#label'].text;
417 this.designerStore.addStepToDeclarativeWorkFlow(actionName, stepName, functionType);
418 if (functionType === 'dg-generic') {
419 this.designerStore.addDgGenericNodeTemplate(stepName);
421 this.designerStore.addNodeTemplate(stepName, functionType);
424 // second action means there was a dg-generic node before
425 this.designerStore.addNodeTemplate(stepName, functionType);
426 // this will fail if multiple dg-generic were added
427 // TODO prevent multi functions of the same type inside the same action
428 const dgGenericNode = this.graphUtil.getDgGenericChild(parentCell.model, this.boardGraph)[0];
429 const dgGenericNodeName = this.graphUtil.getFunctionNameFromBoardFunction(dgGenericNode);
430 this.designerStore.addDgGenericDependency(dgGenericNodeName, stepName);
434 // Prevent recursive embedding.
436 parentCell.model.get('parent') !== functionElementForBoard.id) {
437 parentCell.model.embed(functionElementForBoard);
440 console.log('function dropped outside action or not allowed, rolling back...');
441 alert('function dropped outside action or not allowed, rolling back...');
442 functionElementForBoard.remove();
445 $('body').off('mousemove.fly').off('mouseup.fly');
446 // flyShape.remove();
447 $('#flyPaper').remove();
450 console.log('done stencilPaperEventListeners()...');
454 this.ngUnsubscribe.next();
455 this.ngUnsubscribe.complete();
459 this.ngxService.start();
460 FilesContent.clear();
461 let packageCreationModes: PackageCreationModes;
462 this.cbaPackage = PackageCreationModes.mapModeType(this.cbaPackage);
463 this.cbaPackage.metaData = PackageCreationModes.setEntryPoint(this.cbaPackage.metaData);
464 packageCreationModes = PackageCreationBuilder.getCreationMode(this.cbaPackage);
465 this.designerStore.state$.subscribe(state => {
466 this.cbaPackage.templateTopology.content = this.packageCreationUtils.transformToJson(state.template);
468 packageCreationModes.execute(this.cbaPackage, this.packageCreationUtils);
469 this.filesData.push(this.folder.TREE_DATA);
470 this.saveBluePrintToDataBase();
475 this.ngxService.start();
476 this.packageCreationStore.addTopologyTemplate(this.cbaPackage.templateTopology);
478 this.enrichPackage();
479 this.designerStore.clear();
480 this.packageCreationStore.clear();
483 private formTreeData() {
484 FilesContent.clear();
485 let packageCreationModes: PackageCreationModes;
486 this.cbaPackage = PackageCreationModes.mapModeType(this.cbaPackage);
487 this.cbaPackage.metaData = PackageCreationModes.setEntryPoint(this.cbaPackage.metaData);
488 packageCreationModes = PackageCreationBuilder.getCreationMode(this.cbaPackage);
489 packageCreationModes.execute(this.cbaPackage, this.packageCreationUtils);
490 this.filesData.push(this.folder.TREE_DATA);
492 private enrichPackage() {
494 this.zipFile.generateAsync({ type: 'blob' })
496 this.packageCreationService.enrichPackage(blob).subscribe(response => {
497 console.log('success');
498 const blobInfo = new Blob([response], { type: 'application/octet-stream' });
499 this.packageCreationStore.clear();
500 this.packageCreationExtractionService.extractBlobToStore(blobInfo);
501 this.toastService.success('Enriched successfully ');
504 this.toastService.error(err.message, 'Enrich Failed');
506 this.ngxService.stop();
509 this.toastService.error(error.mesasge, 'error happened ');
510 console.error('Error -' + error.message);
512 this.ngxService.stop();
517 this.zipFile = new JSZip();
518 FilesContent.getMapOfFilesNamesAndContent().forEach((value, key) => {
519 this.zipFile.folder(key.split('/')[0]);
520 this.zipFile.file(key, value);
525 saveBluePrintToDataBase() {
527 this.zipFile.generateAsync({ type: 'blob' })
529 this.packageCreationService.savePackage(blob).subscribe(
530 bluePrintDetailModels => {
531 this.toastService.info('success updating the package');
532 const id = bluePrintDetailModels.toString().split('id')[1].split(':')[1].split('"')[1];
533 this.router.navigate(['/packages/designer/' + id]);
534 console.log('success');
536 this.toastService.error('error happened when editing ' + error.message);
537 console.log('Error -' + error.message);
539 this.ngxService.stop();
542 () => { this.ngxService.stop(); });
545 openActionAttributes(customActionName: string) {
546 console.log('opening here action attributes');
547 this.currentActionName = customActionName;
548 this.actionAttributesSideBar = true;
549 this.functionAttributeSidebar = false;
550 this.designerStore.setCurrentAction(customActionName);
551 /* tslint:disable:no-string-literal */
552 this.steps = Object.keys(this.designerState.template.workflows[customActionName]['steps']);
555 openFunctionAttributes(customFunctionName: string) {
556 // console.log(customFunctionName);
557 this.actionAttributesSideBar = false;
558 this.functionAttributeSidebar = true;
559 // console.log(this.designerState.template.workflows[this.currentActionName]
560 // ['steps'][customFunctionName]['target']);
561 this.designerStore.setCurrentFunction(this.designerState.template.workflows[this.currentActionName]
562 ['steps'][customFunctionName]['target']);
565 getTarget(stepname) {
566 return this.designerState.template.workflows[this.currentActionName]
567 ['steps'][stepname]['target'];