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 this.packageCreationStore.addTopologyTemplate(this.cbaPackage.templateTopology);
479 this.enrichPackage();
480 this.designerStore.clear();
481 this.packageCreationStore.clear();
484 private formTreeData() {
485 FilesContent.clear();
486 let packageCreationModes: PackageCreationModes;
487 this.cbaPackage = PackageCreationModes.mapModeType(this.cbaPackage);
488 this.cbaPackage.metaData = PackageCreationModes.setEntryPoint(this.cbaPackage.metaData);
489 packageCreationModes = PackageCreationBuilder.getCreationMode(this.cbaPackage);
490 packageCreationModes.execute(this.cbaPackage, this.packageCreationUtils);
491 this.filesData.push(this.folder.TREE_DATA);
494 private enrichPackage() {
496 this.zipFile.generateAsync({type: 'blob'})
498 this.packageCreationService.enrichAndDeployPackage(blob).subscribe(response => {
499 // this.packageCreationService.enrichPackage(blob).subscribe(response => {
500 console.log('success');
501 const blobInfo = new Blob([response], {type: 'application/octet-stream'});
502 this.packageCreationStore.clear();
503 this.packageCreationExtractionService.extractBlobToStore(blobInfo);
504 this.toastService.success('Enriched & Deployed successfully ');
507 this.toastService.error(err.message, 'Enrich Failed');
508 this.ngxService.stop();
510 this.ngxService.stop();
513 this.toastService.error(error.mesasge, 'Error occurs during enrichment process');
514 console.error('Error -' + error.message);
516 this.ngxService.stop();
521 this.zipFile = new JSZip();
522 FilesContent.getMapOfFilesNamesAndContent().forEach((value, key) => {
523 this.zipFile.folder(key.split('/')[0]);
524 this.zipFile.file(key, value);
529 saveBluePrintToDataBase() {
531 this.zipFile.generateAsync({type: 'blob'})
533 this.packageCreationService.savePackage(blob).subscribe(
534 bluePrintDetailModels => {
535 this.toastService.success('Package is successfully updated ');
536 const id = bluePrintDetailModels.toString().split('id')[1].split(':')[1].split('"')[1];
537 this.router.navigate(['/packages/designer/' + id]);
538 console.log('success');
540 this.toastService.error('Error occured when editing ' + error.message);
541 console.log('Error -' + error.message);
543 this.ngxService.stop();
548 this.ngxService.stop();
552 openActionAttributes(customActionName: string) {
553 console.log('opening here action attributes');
554 this.currentActionName = customActionName;
555 this.actionAttributesSideBar = true;
556 this.functionAttributeSidebar = false;
557 this.designerStore.setCurrentAction(customActionName);
558 /* tslint:disable:no-string-literal */
559 this.steps = Object.keys(this.designerState.template.workflows[customActionName]['steps']);
562 openFunctionAttributes(customFunctionName: string) {
563 // console.log(customFunctionName);
564 this.actionAttributesSideBar = false;
565 this.functionAttributeSidebar = true;
566 // console.log(this.designerState.template.workflows[this.currentActionName]
567 // ['steps'][customFunctionName]['target']);
568 this.designerStore.setCurrentFunction(this.designerState.template.workflows[this.currentActionName]
569 ['steps'][customFunctionName]['target']);
572 getTarget(stepname) {
573 return this.designerState.template.workflows[this.currentActionName]
574 ['steps'][stepname]['target'];
579 this.ngxService.start();
580 this.packageCreationService.downloadPackage(this.viewedPackage.artifactName + '/'
581 + this.viewedPackage.artifactVersion).subscribe(response => {
582 const blob = new Blob([response], {type: 'application/octet-stream'});
583 saveAs(blob, this.viewedPackage.artifactName + '-' + this.viewedPackage.artifactVersion + '-CBA.zip');
586 this.toastService.error('Package ' + this.viewedPackage.artifactName + 'has error when downloading' +
588 this.ngxService.stop();
590 this.toastService.success('Package ' + this.viewedPackage.artifactName + 'has been downloaded successfully');
591 this.ngxService.stop();