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 { saveAs } from 'file-saver';
51 import { PackageCreationExtractionService } from '../package-creation/package-creation-extraction.service';
52 import { CBAPackage } from '../package-creation/mapping-models/CBAPacakge.model';
53 import { TopologyTemplate } from './model/designer.topologyTemplate.model';
54 import { ToastrService } from 'ngx-toastr';
55 import { DesignerDashboardState } from './model/designer.dashboard.state';
56 import { NgxUiLoaderService } from 'ngx-ui-loader';
59 selector: 'app-designer',
60 templateUrl: './designer.component.html',
61 styleUrls: ['./designer.component.css'],
62 encapsulation: ViewEncapsulation.None
64 export class DesignerComponent implements OnInit, OnDestroy {
66 controllerSideBar: boolean;
67 actionAttributesSideBar: boolean;
68 functionAttributeSidebar: boolean;
69 viewedPackage: BluePrintDetailModel = new BluePrintDetailModel();
70 customActionName: string;
74 boardGraph: joint.dia.Graph;
75 boardPaper: joint.dia.Paper;
77 paletteGraph: joint.dia.Graph;
78 palettePaper: joint.dia.Paper;
79 ngUnsubscribe = new Subject();
80 opt = { tx: 100, ty: 100 };
82 folder: FolderNodeElement = new FolderNodeElement();
83 zipFile: JSZip = new JSZip();
84 cbaPackage: CBAPackage;
85 actions: string[] = [];
88 designerState: DesignerDashboardState;
89 currentActionName: string;
93 private designerStore: DesignerStore,
94 private functionStore: FunctionsStore,
95 private packageCreationStore: PackageCreationStore,
96 private packageCreationUtils: PackageCreationUtils,
97 private graphUtil: GraphUtil,
98 private graphGenerator: GraphGenerator,
99 private route: ActivatedRoute,
100 private router: Router,
101 private designerService: DesignerService,
102 private packageCreationService: PackageCreationService,
103 private packageCreationExtractionService: PackageCreationExtractionService,
104 private activatedRoute: ActivatedRoute,
105 private ngxService: NgxUiLoaderService,
106 private toastService: ToastrService) {
107 this.controllerSideBar = true;
108 this.actionAttributesSideBar = false;
109 this.showAction = false;
110 this.functionAttributeSidebar = false;
115 this.controllerSideBar = !this.controllerSideBar;
116 if (this.controllerSideBar === false) {
117 this.cl = 'editBar2';
119 if (this.controllerSideBar === true) {
125 this.actionAttributesSideBar = !this.actionAttributesSideBar;
130 this.zipFile.generateAsync({ type: 'blob' })
132 const formData = new FormData();
133 formData.append('file', blob);
134 this.designerService.publishBlueprint(formData).subscribe(res => {
135 this.toastService.success('Package Deployed Successfuly');
136 console.log('Package Deployed...');
138 this.toastService.error(error.message, 'Package error');
141 // this.deployBluePrint = false;
148 * - There is a board (main paper) that will the action and function selected from the palette
149 * itmes in this board will be used to create tosca workflow and node templates
150 * - There is also palette , whis contains all the possible functions and actions
151 * that can be dragged into the board
152 * - There is also a fly paper , which is temporarliy paper created on the fly
153 * when items is dragged from the palette- and it's deleted when the item is dropped over
155 * for more info about the drag and drop algorithem used please visit the following link:
156 * https://stackoverflow.com/a/36932973/1340034
160 // this.ngxService.start();
161 this.customActionName = this.route.snapshot.paramMap.get('actionName');
162 if (this.customActionName !== '') {
163 this.showAction = true;
165 this.initializeBoard();
166 this.initializePalette();
167 this.stencilPaperEventListeners();
168 const id = this.route.snapshot.paramMap.get('id');
169 this.designerService.getPagedPackages(id).subscribe(
170 (bluePrintDetailModels) => {
171 if (bluePrintDetailModels) {
172 this.viewedPackage = bluePrintDetailModels[0];
173 this.packageCreationService.downloadPackage(this.viewedPackage.artifactName + '/'
174 + this.viewedPackage.artifactVersion)
175 .subscribe(response => {
176 const blob = new Blob([response], { type: 'application/octet-stream' });
177 this.packageCreationExtractionService.extractBlobToStore(blob);
181 this.packageCreationStore.state$.subscribe(cba => {
182 this.cbaPackage = cba;
183 console.log(cba.templateTopology.content);
184 this.designerStore.saveSourceContent(cba.templateTopology.content);
189 * the code to retrieve from server is commented
191 this.functionStore.state$
193 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
194 takeUntil(this.ngUnsubscribe))
195 .subscribe(state => {
197 if (state.serverFunctions) {
198 console.log('inside subscriotn on functions store -->', state.serverFunctions);
200 // this.viewedFunctions = state.functions;
201 const list = state.serverFunctions;
203 const cells = this.graphUtil.buildPaletteGraphFromList(list);
204 this.paletteGraph.resetCells(cells);
207 cells.forEach(cell => {
208 cell.translate(5, (cell.attributes.size.height + 5) * idx++);
213 this.designerStore.state$
215 distinctUntilChanged((a: any, b: any) => JSON.stringify(a) === JSON.stringify(b)),
216 takeUntil(this.ngUnsubscribe))
217 .subscribe(state => {
218 this.designerState = state;
219 if (state.sourceContent) {
220 console.log('inside desinger.component---> ', state);
221 // generate graph from store objects if exist
222 const topologtTemplate: TopologyTemplate = JSON.parse(state.sourceContent);
223 console.log(topologtTemplate);
224 delete state.sourceContent;
225 this.graphGenerator.clear(this.boardGraph);
226 this.graphGenerator.populate(topologtTemplate, this.boardGraph);
228 console.log('all cells', this.boardGraph.getCells());
230 * auto arrange elements in graph
231 * https://resources.jointjs.com/docs/jointjs/v3.1/joint.html#layout.DirectedGraph
233 joint.layout.DirectedGraph.layout(this.boardGraph.getCells(), {
236 setLinkVertices: false,
239 clusterPadding: { top: 100, left: 30, right: 10, bottom: 100 },
244 for (const workflowsKey in topologtTemplate.workflows) {
245 if (workflowsKey && !this.actions.includes(workflowsKey)) {
246 this.actions.push(workflowsKey);
247 /* tslint:disable:no-string-literal */
248 if (!this.designerState.template.workflows[workflowsKey]['inputs']) {
249 this.designerState.template.workflows[workflowsKey]['inputs'] = {};
251 if (!this.designerState.template.workflows[workflowsKey]['outputs']) {
252 this.designerState.template.workflows[workflowsKey]['outputs'] = {};
260 this.functionStore.retrieveFuntions();
261 this.activatedRoute.paramMap.subscribe(res => {
262 this.packageId = res.get('id');
267 initializePalette() {
268 if (!this.paletteGraph) {
269 this.paletteGraph = new joint.dia.Graph();
270 this.palettePaper = new joint.dia.Paper({
271 el: $('#palette-paper'),
272 model: this.paletteGraph,
275 height: $('#palette-paper').height(),
277 // color: 'rgba(0, 255, 0, 0.3)'
280 // elements in paletter need to be fixed, please refer to flying paper concept
286 if (!this.boardGraph) {
287 console.log('initializeBoard...');
288 this.boardGraph = new joint.dia.Graph();
289 this.boardPaper = new joint.dia.Paper({
290 el: $('#board-paper'),
291 model: this.boardGraph,
294 // origin: { x: 80, y: 70 },
295 gridSize: 10, // background line sizes
298 // color: 'rgba(0, 255, 0, 0.3)'
300 cellViewNamespace: joint.shapes
303 this.boardPaper.on('all', element => {
304 // console.log(element);
307 this.boardPaper.on('link:pointerdown', link => {
311 this.boardPaper.on('element:pointerdown', element => {
312 // this.modelSelected.emit(element.model.get('model'));
315 this.boardPaper.on('blank:pointerclick', () => {
316 // this.selectedModel = undefined;
319 this.boardGraph.on('change:position', (cell) => {
321 const parentId = cell.get('parent');
327 const parent = this.boardGraph.getCell(parentId);
329 const parentBbox = parent.getBBox();
330 const cellBbox = cell.getBBox();
331 if (parentBbox.containsPoint(cellBbox.origin()) &&
332 parentBbox.containsPoint(cellBbox.topRight()) &&
333 parentBbox.containsPoint(cellBbox.corner()) &&
334 parentBbox.containsPoint(cellBbox.bottomLeft())) {
336 // All the four corners of the child are inside
341 // Revert the child position.
342 cell.set('position', cell.previous('position'));
345 console.log('done initializing Board...');
348 insertCustomActionIntoBoard() {
349 console.log('saving action to store action workflow....');
350 let actionName = this.graphUtil.generateNewActionName();
351 while (this.actions.includes(actionName)) {
352 actionName = this.graphUtil.generateNewActionName();
354 this.graphUtil.createCustomActionWithName(actionName, this.boardGraph);
355 this.designerStore.addDeclarativeWorkFlow(actionName);
356 this.actions.push(actionName);
359 stencilPaperEventListeners() {
360 this.palettePaper.on('cell:pointerdown', (draggedCell, pointerDownEvent, x, y) => {
364 style="position:fixed;z-index:100;opacity:.7;pointer-event:none;background-color: transparent !important;"></div>`
366 const flyGraph = new joint.dia.Graph();
367 const flyPaper = new joint.dia.Paper({
372 const flyShape = draggedCell.model.clone();
373 const pos = draggedCell.model.position();
379 flyShape.position(0, 0);
380 flyGraph.addCell(flyShape);
381 $('#flyPaper').offset({
382 left: pointerDownEvent.pageX - offset.x,
383 top: pointerDownEvent.pageY - offset.y
385 $('body').on('mousemove.fly', mouseMoveEvent => {
386 $('#flyPaper').offset({
387 left: mouseMoveEvent.pageX - offset.x,
388 top: mouseMoveEvent.pageY - offset.y
392 $('body').on('mouseup.fly', mouseupEvent => {
393 const mouseupX = mouseupEvent.pageX;
394 const mouseupY = mouseupEvent.pageY;
395 const target = this.boardPaper.$el.offset();
396 // Dropped over paper ?
397 if (mouseupX > target.left &&
398 mouseupX < target.left + this.boardPaper.$el.width() &&
399 mouseupY > target.top && y < target.top + this.boardPaper.$el.height()) {
400 const functionType = this.graphUtil.getFunctionTypeFromPaletteFunction(flyShape);
401 // step name is CDS realted terminology, please refer to tosca types
402 const stepName = functionType;
403 const functionElementForBoard = this.graphUtil.dropFunctionOverActionWithPosition(
404 stepName, functionType,
409 const parentCell = this.graphUtil.getParent(functionElementForBoard, this.boardPaper);
412 parentCell.model.attributes.type === ActionElementTypeName &&
413 this.graphUtil.canEmpedMoreChildern(parentCell.model, this.boardGraph)) {
415 if (this.graphUtil.isEmptyParent(parentCell.model)) {
416 // first function in action
417 const actionName = parentCell.model.attributes.attrs['#label'].text;
418 this.designerStore.addStepToDeclarativeWorkFlow(actionName, stepName, functionType);
419 if (functionType === 'dg-generic') {
420 this.designerStore.addDgGenericNodeTemplate(stepName);
422 this.designerStore.addNodeTemplate(stepName, functionType);
425 // second action means there was a dg-generic node before
426 this.designerStore.addNodeTemplate(stepName, functionType);
427 // this will fail if multiple dg-generic were added
428 // TODO prevent multi functions of the same type inside the same action
429 const dgGenericNode = this.graphUtil.getDgGenericChild(parentCell.model, this.boardGraph)[0];
430 const dgGenericNodeName = this.graphUtil.getFunctionNameFromBoardFunction(dgGenericNode);
431 this.designerStore.addDgGenericDependency(dgGenericNodeName, stepName);
435 // Prevent recursive embedding.
437 parentCell.model.get('parent') !== functionElementForBoard.id) {
438 parentCell.model.embed(functionElementForBoard);
441 console.log('function dropped outside action or not allowed, rolling back...');
442 alert('function dropped outside action or not allowed, rolling back...');
443 functionElementForBoard.remove();
446 $('body').off('mousemove.fly').off('mouseup.fly');
447 // flyShape.remove();
448 $('#flyPaper').remove();
451 console.log('done stencilPaperEventListeners()...');
455 this.ngUnsubscribe.next();
456 this.ngUnsubscribe.complete();
460 this.ngxService.start();
461 FilesContent.clear();
462 let packageCreationModes: PackageCreationModes;
463 this.cbaPackage = PackageCreationModes.mapModeType(this.cbaPackage);
464 this.cbaPackage.metaData = PackageCreationModes.setEntryPoint(this.cbaPackage.metaData);
465 packageCreationModes = PackageCreationBuilder.getCreationMode(this.cbaPackage);
466 this.designerStore.state$.subscribe(state => {
467 this.cbaPackage.templateTopology.content = this.packageCreationUtils.transformToJson(state.template);
469 packageCreationModes.execute(this.cbaPackage, this.packageCreationUtils);
470 this.filesData.push(this.folder.TREE_DATA);
471 this.saveBluePrintToDataBase();
476 this.ngxService.start();
477 console.log('start enrich ------------');
478 this.packageCreationStore.addTopologyTemplate(this.cbaPackage.templateTopology);
480 this.enrichPackage();
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);
493 private enrichPackage() {
495 this.zipFile.generateAsync({ type: 'blob' })
497 this.packageCreationService.enrichAndDeployPackage(blob).subscribe(response => {
498 // this.packageCreationService.enrichPackage(blob).subscribe(response => {
499 response = JSON.parse(response);
500 console.log(response);
501 console.log('success');
502 const id = response['blueprintModel']['id'];
503 this.router.navigate(['/packages/designer/' + id]);
504 // const blobInfo = new Blob([response], { type: 'application/octet-stream' });
505 // this.packageCreationStore.clear();
506 // this.packageCreationExtractionService.extractBlobToStore(blobInfo);
507 this.toastService.success('Enriched & Deployed successfully ');
510 this.toastService.error(err.message, 'Enrich Failed');
511 this.ngxService.stop();
513 this.ngxService.stop();
516 this.toastService.error(error.mesasge, 'Error occurs during enrichment process');
517 console.error('Error -' + error.message);
519 this.ngxService.stop();
524 this.zipFile = new JSZip();
525 FilesContent.getMapOfFilesNamesAndContent().forEach((value, key) => {
526 this.zipFile.folder(key.split('/')[0]);
527 this.zipFile.file(key, value);
532 saveBluePrintToDataBase() {
534 this.zipFile.generateAsync({ type: 'blob' })
536 this.packageCreationService.savePackage(blob).subscribe(
537 bluePrintDetailModels => {
538 this.toastService.success('Package is updated successfully');
539 const id = bluePrintDetailModels.toString().split('id')[1].split(':')[1].split('"')[1];
540 this.router.navigate(['/packages/designer/' + id]);
541 console.log('success');
543 this.toastService.error('Error Occured during editing process' + error.message);
544 console.log('Error -' + error.message);
546 this.ngxService.stop();
551 this.ngxService.stop();
555 openActionAttributes(customActionName: string) {
556 console.log('opening here action attributes');
557 this.currentActionName = customActionName;
558 this.actionAttributesSideBar = true;
559 this.functionAttributeSidebar = false;
560 this.designerStore.setCurrentAction(customActionName);
561 /* tslint:disable:no-string-literal */
562 this.steps = Object.keys(this.designerState.template.workflows[customActionName]['steps']);
565 openFunctionAttributes(customFunctionName: string) {
566 // console.log(customFunctionName);
567 this.actionAttributesSideBar = false;
568 this.functionAttributeSidebar = true;
569 // console.log(this.designerState.template.workflows[this.currentActionName]
570 // ['steps'][customFunctionName]['target']);
571 this.designerStore.setCurrentFunction(this.designerState.template.workflows[this.currentActionName]
572 ['steps'][customFunctionName]['target']);
575 getTarget(stepname) {
577 // console.log(this.currentActionName + " -- " + stepname)
578 return this.designerState.template.workflows[this.currentActionName]
579 ['steps'][stepname]['target'];
587 this.ngxService.start();
588 this.packageCreationService.downloadPackage(this.viewedPackage.artifactName + '/'
589 + this.viewedPackage.artifactVersion).subscribe(response => {
590 const blob = new Blob([response], { type: 'application/octet-stream' });
591 saveAs(blob, this.viewedPackage.artifactName + '-' + this.viewedPackage.artifactVersion + '-CBA.zip');
594 this.toastService.error('Package ' + this.viewedPackage.artifactName + 'has error when downloading' +
596 this.ngxService.stop();
598 this.toastService.success('Package ' + this.viewedPackage.artifactName + 'downloaded successfully');
599 this.ngxService.stop();