Sync Integ to Master
[sdc.git] / catalog-ui / src / app / directives / structure-tree / structure-tree-directive.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 'use strict';
21 import * as _ from "lodash";
22 import {RelationshipModel, Component, ComponentInstance} from "app/models";
23 import {Dictionary} from "app/utils";
24
25
26 export interface IStructureTreeScope extends ng.IScope {
27
28     component:Component;
29     structureTree:StructureTree;
30 }
31
32 class StructureTree {
33
34     serviceRoot:ResourceInstanceNode;
35
36     constructor(private uniqueId:string, private resourceInstanceName:string, private resourceInstanceIcon:string, private certified:boolean) {
37         this.serviceRoot = new ResourceInstanceNode(uniqueId, resourceInstanceName, resourceInstanceIcon, certified);
38     }
39
40 }
41
42 class ResourceInstanceNode {
43     id:string;
44     icon:string;
45     name:string;
46     resourceInstancesList:Array<ResourceInstanceNode>;
47     isAlreadyInTree:boolean;
48     certified:boolean;
49
50
51     constructor(private uniqueId:string, private resourceInstanceName:string, private resourceInstanceIcon:string, certified:boolean) {
52         this.id = uniqueId;
53         this.name = resourceInstanceName;
54         this.icon = resourceInstanceIcon;
55         this.resourceInstancesList = [];
56         this.isAlreadyInTree = false;
57         this.certified = certified;
58     }
59 }
60
61 export class StructureTreeDirective implements ng.IDirective {
62
63
64     constructor() {
65     }
66
67     scope = {
68         component: '=',
69     };
70     restrict = 'E';
71     template = ():string => {
72         return require('./structure-tree-directive.html');
73     };
74
75     link = (scope:IStructureTreeScope, $elem:any) => {
76
77         let RESOURCE_INSTANCE_LIST:string = "resourceInstancesChildesList";
78         let resourceInstanceMap:Dictionary<string, ResourceInstanceNode>;
79         let relations:Array<RelationshipModel>;
80         //************* Start Building Tree Functions *******************//
81
82         //remove unnecessary instances
83         let initResourceInstanceMap = ():void => {
84
85             resourceInstanceMap = new Dictionary<string, ResourceInstanceNode>();
86
87             _.forEach(scope.component.componentInstances, (resourceInstance:ComponentInstance)=> {
88                 if (_.some(Object.keys(resourceInstance.capabilities), (key:string)=> {
89                         return 'tosca.capabilities.container' == key.toLowerCase();
90                     }) || _.some(Object.keys(resourceInstance.requirements), (key:string)=> {
91                         return 'tosca.capabilities.container' == key.toLowerCase();
92                     })) {
93
94                     let isCertified = 0 === (parseFloat(resourceInstance.componentVersion) % 1);
95                     let node:ResourceInstanceNode = new ResourceInstanceNode(resourceInstance.uniqueId,
96                         resourceInstance.name,
97                         resourceInstance.icon,
98                         isCertified);
99                     resourceInstanceMap.setValue(resourceInstance.uniqueId, node);
100                 }
101             });
102         };
103
104         //remove unnecessary relations
105         let initRelations = ():void => {
106             relations = _.filter(scope.component.componentInstancesRelations, (relation:RelationshipModel)=> {
107                 return resourceInstanceMap.containsKey(relation.fromNode) && resourceInstanceMap.containsKey(relation.toNode);
108             });
109         };
110
111         let buildTree = ():void => {
112             if (scope.component) {
113                 scope.structureTree = new StructureTree(scope.component.uniqueId, scope.component.name, scope.component.icon, 'CERTIFIED' === scope.component.lifecycleState);
114                 initResourceInstanceMap();
115                 initRelations();
116
117                 let parentNodesList = _.groupBy(relations, (node:any)=> {
118                     return node.fromNode;
119                 });
120
121                 for (let parent in parentNodesList) {
122                     _.forEach(parentNodesList[parent], (childNode)=> {
123                         parentNodesList[parent][RESOURCE_INSTANCE_LIST] = [];
124                         parentNodesList[parent][RESOURCE_INSTANCE_LIST].push(mergeAllSubtrees(childNode, parentNodesList));
125                     });
126                 }
127
128                 //add the resourceInstanceList for the service root node
129                 for (let parent in parentNodesList) {
130                     let resourceInstanceNode:ResourceInstanceNode = resourceInstanceMap.getValue(parent);
131                     resourceInstanceNode.resourceInstancesList = parentNodesList[parent];
132                     resourceInstanceNode.resourceInstancesList = parentNodesList[parent][RESOURCE_INSTANCE_LIST];
133                     resourceInstanceNode.isAlreadyInTree = true;
134                     scope.structureTree.serviceRoot.resourceInstancesList.push(resourceInstanceNode);
135                 }
136
137                 // Add all node that have no connection to the rootNode
138                 resourceInstanceMap.forEach((key:string, value:ResourceInstanceNode) => {
139                     if (!value.isAlreadyInTree) {
140                         scope.structureTree.serviceRoot.resourceInstancesList.push(value);
141                     }
142                 });
143             }
144         };
145
146         //this recursion is merging all the subtrees
147         let mergeAllSubtrees = (connectionData:any, parentNodesList:any):ResourceInstanceNode => {
148             let resourceInstanceNode:ResourceInstanceNode = resourceInstanceMap.getValue(connectionData.toNode);
149             resourceInstanceNode.isAlreadyInTree = true;
150             if (parentNodesList[resourceInstanceNode.id]) {
151                 if (parentNodesList[resourceInstanceNode.id][RESOURCE_INSTANCE_LIST]) {
152                     resourceInstanceNode.resourceInstancesList = parentNodesList[resourceInstanceNode.id][RESOURCE_INSTANCE_LIST];
153                 }
154                 else {
155                     _.forEach(parentNodesList[resourceInstanceNode.id], (children)=> {
156                         resourceInstanceNode.resourceInstancesList.push(mergeAllSubtrees(children, parentNodesList));
157                     });
158                 }
159                 delete parentNodesList[resourceInstanceNode.id];
160             }
161             return resourceInstanceNode;
162         };
163         //************* End Building Tree Functions *******************//
164
165         //************* Start Watchers *******************//
166         scope.$watch('component.name', ():void => {
167             if (scope.structureTree)
168                 scope.structureTree.serviceRoot.name = scope.component.name;
169         });
170
171         scope.$watch('component.icon', ():void => {
172             if (scope.structureTree)
173                 scope.structureTree.serviceRoot.icon = scope.component.icon;
174         });
175
176         scope.$watchCollection('component.componentInstancesRelations', ():void => {
177             buildTree();
178         });
179
180         scope.$watchCollection('component.componentInstances', ():void => {
181             buildTree();
182         });
183
184         //************* End  Watchers *******************//
185
186         buildTree();
187
188     };
189
190
191     public static factory = () => {
192         return new StructureTreeDirective();
193     };
194 }
195
196 StructureTreeDirective.factory.$inject = [];