Create inputs independent of properties
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / properties-assignment.page.component.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
21 import * as _ from "lodash";
22 import { Component, ViewChild, Inject, TemplateRef } from "@angular/core";
23 import { PropertiesService } from "../../services/properties.service";
24 import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models";
25 import { ResourceType } from "app/utils";
26 import { ComponentServiceNg2 } from "../../services/component-services/component.service";
27 import { TopologyTemplateService } from "../../services/component-services/topology-template.service";
28 import { ComponentInstanceServiceNg2 } from "../../services/component-instance-services/component-instance.service"
29 import { InputBEModel, InputFEModel, ComponentInstance, GroupInstance, PolicyInstance, PropertyBEModel, DerivedFEProperty, SimpleFlatProperty } from "app/models";
30 import { KeysPipe } from 'app/ng2/pipes/keys.pipe';
31 import { WorkspaceMode, EVENTS, PROPERTY_TYPES } from "../../../utils/constants";
32 import { EventListenerService } from "app/services/event-listener-service"
33 import { HierarchyDisplayOptions } from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
34 import { FilterPropertiesAssignmentComponent } from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component";
35 import { PropertyRowSelectedEvent } from "../../components/logic/properties-table/properties-table.component";
36 import { HierarchyNavService } from "./services/hierarchy-nav.service";
37 import { PropertiesUtils } from "./services/properties.utils";
38 import { ComponentModeService } from "../../services/component-services/component-mode.service";
39 import { Tabs, Tab } from "../../components/ui/tabs/tabs.component";
40 import { InputsUtils } from "./services/inputs.utils";
41 import { InstanceFeDetails } from "../../../models/instance-fe-details";
42 import { SdcUiServices, SdcUiCommon } from "onap-ui-angular";
43 import { UnsavedChangesComponent } from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component";
44 import {PropertyCreatorComponent} from "./property-creator/property-creator.component";
45 import {ModalService} from "../../services/modal.service";
46 import { DeclareListComponent } from "./declare-list/declare-list.component";
47 import { CapabilitiesGroup, Capability } from "../../../models/capability";
48 import { ToscaPresentationData } from "../../../models/tosca-presentation";
49 import { Observable } from "rxjs";
50
51 const SERVICE_SELF_TITLE = "SELF";
52 @Component({
53     templateUrl: './properties-assignment.page.component.html',
54     styleUrls: ['./properties-assignment.page.component.less']
55 })
56 export class PropertiesAssignmentComponent {
57     title = "Properties & Inputs";
58
59     component: ComponentData;
60     componentInstanceNamesMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();//instanceUniqueId, {name, iconClass}
61
62     propertiesNavigationData = [];
63     instancesNavigationData = [];
64
65     instanceFePropertiesMap:InstanceFePropertiesMap;
66     inputs: Array<InputFEModel> = [];
67     policies: Array<PolicyInstance> = [];
68     instances: Array<ComponentInstance|GroupInstance|PolicyInstance> = [];
69     searchQuery: string;
70     propertyStructureHeader: string;
71
72     selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty();
73     selectedInstanceData: ComponentInstance|GroupInstance|PolicyInstance = null;
74     checkedPropertiesCount: number = 0;
75     checkedChildPropertiesCount: number = 0;
76
77     hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens');
78     hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
79     displayClearSearch = false;
80     searchPropertyName:string;
81     currentMainTab:Tab;
82     isInputsTabSelected:boolean;
83     isPropertiesTabSelected:boolean;
84     isPoliciesTabSelected:boolean;
85     isReadonly:boolean;
86     resourceIsReadonly:boolean;
87     loadingInstances:boolean = false;
88     loadingInputs:boolean = false;
89     loadingPolicies:boolean = false;
90     loadingProperties:boolean = false;
91     changedData:Array<PropertyFEModel|InputFEModel>;
92     hasChangedData:boolean;
93     isValidChangedData:boolean;
94     savingChangedData:boolean;
95     stateChangeStartUnregister:Function;
96     serviceBePropertiesMap: InstanceBePropertiesMap;
97     serviceBeCapabilitiesPropertiesMap: InstanceBePropertiesMap;
98     selectedInstance_FlattenCapabilitiesList: Capability[];
99
100     @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
101     @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
102     @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
103    
104     constructor(private propertiesService: PropertiesService,
105                 private hierarchyNavService: HierarchyNavService,
106                 private propertiesUtils:PropertiesUtils,
107                 private inputsUtils:InputsUtils,
108                 private componentServiceNg2:ComponentServiceNg2,
109                 private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
110                 @Inject("$stateParams") _stateParams,
111                 @Inject("$scope") private $scope:ng.IScope,
112                 @Inject("$state") private $state:ng.ui.IStateService,
113                 @Inject("Notification") private Notification:any,
114                 private componentModeService:ComponentModeService,
115                 private EventListenerService:EventListenerService,
116                 private ModalServiceSdcUI: SdcUiServices.ModalService,
117                 private ModalService: ModalService,
118                 private keysPipe:KeysPipe,
119                 private topologyTemplateService: TopologyTemplateService) {
120
121         this.instanceFePropertiesMap = new InstanceFePropertiesMap();
122         /* This is the way you can access the component data, please do not use any data except metadata, all other data should be received from the new api calls on the first time
123         than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/
124         this.component = _stateParams.component;
125         this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.onCheckout);
126         this.updateViewMode();
127
128         this.changedData = [];
129         this.updateHasChangedData();
130         this.isValidChangedData = true;
131     }
132
133     ngOnInit() {
134         console.log("==>" + this.constructor.name + ": ngOnInit");
135         this.loadingInputs = true;
136         this.loadingPolicies = true;
137         this.loadingInstances = true;
138         this.loadingProperties = true;
139         this.topologyTemplateService
140             .getComponentInputsWithProperties(this.component.componentType, this.component.uniqueId)
141             .subscribe(response => {
142                 _.forEach(response.inputs, (input: InputBEModel) => {
143                     const newInput: InputFEModel = new InputFEModel(input);
144                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
145                     this.inputs.push(newInput); //only push items that were declared via SDC
146                 });
147                 this.loadingInputs = false;
148
149             }, error => {}); //ignore error
150         this.componentServiceNg2
151             .getComponentResourcePropertiesData(this.component)
152             .subscribe(response => {
153                 this.loadingPolicies = false;
154                 this.instances = [];
155                 this.instances.push(...response.componentInstances);
156                 this.instances.push(...response.groupInstances);
157                 this.instances.push(...response.policies);
158
159                 _.forEach(response.policies, (policy: any) => {
160                     const newPolicy: InputFEModel = new InputFEModel(policy);
161                     this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
162                     this.policies.push(policy);
163                 });
164
165                 // add the service self instance to the top of the list.
166                 const serviceInstance = new ComponentInstance();
167                 serviceInstance.name = SERVICE_SELF_TITLE;
168                 serviceInstance.uniqueId = this.component.uniqueId;
169                 this.instances.unshift(serviceInstance);
170
171                 _.forEach(this.instances, (instance) => {
172                     this.instancesNavigationData.push(instance);
173                     this.componentInstanceNamesMap[instance.uniqueId] = <InstanceFeDetails>{name: instance.name, iconClass:instance.iconClass, originArchived:instance.originArchived};
174                 });
175                 this.loadingInstances = false;
176                 if (this.instancesNavigationData[0] == undefined) {
177                     this.loadingProperties = false;
178                 }
179                 this.selectFirstInstanceByDefault();
180             }, error => { this.loadingInstances = false; }); //ignore error
181
182         this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
183             // stop if has changed properties
184             if (this.hasChangedData) {
185                 event.preventDefault();
186                 this.showUnsavedChangesAlert().then(() => {
187                     this.$state.go(toState, toParams);
188                 }, () => {});
189             }
190         });
191     };
192
193     ngOnDestroy() {
194         this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
195         this.stateChangeStartUnregister();
196     }
197
198     selectFirstInstanceByDefault = () => {
199         if (this.instancesNavigationData[0] !== undefined) {
200             this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
201         }
202     };
203
204     updateViewMode = () => {
205         this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW;
206     }
207
208     onCheckout = (component:ComponentData) => {
209         this.component = component;
210         this.updateViewMode();
211     }
212
213     isSelf = ():boolean => {
214         return this.selectedInstanceData && this.selectedInstanceData.uniqueId == this.component.uniqueId;
215     }
216
217     getServiceProperties(){
218         this.loadingProperties = false;
219         this.topologyTemplateService
220             .getServiceProperties(this.component.uniqueId)
221             .subscribe((response) => {
222                 this.serviceBePropertiesMap = new InstanceBePropertiesMap();
223                 this.serviceBePropertiesMap[this.component.uniqueId] = response;
224                 this.processInstancePropertiesResponse(this.serviceBePropertiesMap, false);
225                 this.loadingProperties = false;
226             }, (error) => {
227                 this.loadingProperties = false;
228             });
229     }
230
231     onInstanceSelectedUpdate = (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
232         // stop if has changed properties
233         if (this.hasChangedData) {
234             this.showUnsavedChangesAlert().then((resolve)=> {
235                 this.changeSelectedInstance(instance)
236             }, (reject) => {
237             });
238             return;
239         }
240         this.changeSelectedInstance(instance);
241     };
242
243     changeSelectedInstance =  (instance: ComponentInstance|GroupInstance|PolicyInstance) => {
244         this.selectedInstanceData = instance;
245         this.loadingProperties = true;
246         if (instance instanceof ComponentInstance) {
247             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
248             if (this.isInput(instance.originType)) {
249                 this.componentInstanceServiceNg2
250                     .getComponentInstanceInputs(this.component, instance)
251                     .subscribe(response => {
252                         instanceBePropertiesMap[instance.uniqueId] = response;
253                         this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
254                         this.loadingProperties = false;
255                     }, error => {
256                     }); //ignore error
257             } else if (this.isSelf()) {
258                 this.getServiceProperties();
259             } else {
260                 this.componentInstanceServiceNg2
261                     .getComponentInstanceProperties(this.component, instance.uniqueId)
262                     .subscribe(response => {
263                         instanceBePropertiesMap[instance.uniqueId] = response;
264                         this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
265                         this.loadingProperties = false;
266                     }, error => {
267                     }); //ignore error
268             }
269
270             this.resourceIsReadonly = (instance.componentName === "vnfConfiguration");
271         } else if (instance instanceof GroupInstance) {
272             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
273             this.componentInstanceServiceNg2
274                 .getComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId)
275                 .subscribe((response) => {
276                     instanceBePropertiesMap[instance.uniqueId] = response;
277                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
278                     this.loadingProperties = false;
279                 });
280         } else if (instance instanceof PolicyInstance) {
281             let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
282             this.componentInstanceServiceNg2
283                 .getComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId)
284                 .subscribe((response) => {
285                     instanceBePropertiesMap[instance.uniqueId] = response;
286                     this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
287                     this.loadingProperties = false;
288                 });
289         } else {
290             this.loadingProperties = false;
291         }
292
293         if (this.searchPropertyName) {
294             this.clearSearch();
295         }
296         //clear selected property from the navigation
297         this.selectedFlatProperty = new SimpleFlatProperty();
298         this.propertiesNavigationData = [];
299     };
300
301     /**
302      * Entry point handling response from server
303      */
304     processInstancePropertiesResponse = (instanceBePropertiesMap: InstanceBePropertiesMap, originTypeIsVF: boolean) => {
305         this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
306         this.checkedPropertiesCount = 0;
307         this.checkedChildPropertiesCount = 0;
308     };
309
310     processInstanceCapabilitiesPropertiesResponse = (originTypeIsVF: boolean) => {
311         let selectedComponentInstanceData = <ComponentInstance>(this.selectedInstanceData);
312         let currentUniqueId = this.selectedInstanceData.uniqueId;
313         this.serviceBeCapabilitiesPropertiesMap = new InstanceBePropertiesMap();
314         let isCapabilityOwnedByInstance: boolean;
315         this.serviceBeCapabilitiesPropertiesMap[currentUniqueId] = _.reduce(
316             this.selectedInstance_FlattenCapabilitiesList,
317             (result, cap: Capability) => {
318                 isCapabilityOwnedByInstance = cap.ownerId === currentUniqueId ||
319                     selectedComponentInstanceData.isServiceProxy() || selectedComponentInstanceData.isServiceSubstitution() && 
320                     cap.ownerId === selectedComponentInstanceData.sourceModelUid;
321                 if (cap.properties && isCapabilityOwnedByInstance) {
322                     _.forEach(cap.properties, prop => {
323                         if (!prop.origName) {
324                             prop.origName = prop.name;
325                             prop.name = cap.name + '_' + prop.name;//for display. (before save - the name returns to its orig value: prop.name)
326                         }
327                     });
328                     return result.concat(cap.properties);
329                 }
330                 return result;
331             }, []);
332         let instanceFECapabilitiesPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(this.serviceBeCapabilitiesPropertiesMap, originTypeIsVF, this.inputs); //create flattened children, disable declared props, and init values
333         //update FECapabilitiesProperties with their origName according to BeCapabilitiesProperties
334         _.forEach(instanceFECapabilitiesPropertiesMap[currentUniqueId], prop => {
335             prop.origName = _.find(this.serviceBeCapabilitiesPropertiesMap[currentUniqueId], p => p.uniqueId === prop.uniqueId).origName;
336         });
337         //concatenate capabilitiesProps to all props list
338         this.instanceFePropertiesMap[currentUniqueId] = (this.instanceFePropertiesMap[currentUniqueId] || []).concat(instanceFECapabilitiesPropertiesMap[currentUniqueId]);
339         this.checkedPropertiesCount = 0;
340     };
341
342     isCapabilityProperty = (prop: PropertyBEModel) => {
343         return _.find(this.selectedInstance_FlattenCapabilitiesList, cap => cap.uniqueId === prop.parentUniqueId);
344     };
345
346     /*** VALUE CHANGE EVENTS ***/
347     dataChanged = (item:PropertyFEModel|InputFEModel) => {
348         let itemHasChanged;
349         if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
350             itemHasChanged = item.hasValueObjChanged();
351         } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
352             itemHasChanged = item.hasChanged();
353         } else if (this.isPoliciesTabSelected && item instanceof InputFEModel) {
354             itemHasChanged = item.hasDefaultValueChanged();
355         }
356
357         const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
358         if (itemHasChanged) {
359             if (dataChangedIdx === -1) {
360                 this.changedData.push(item);
361             }
362         } else {
363             if (dataChangedIdx !== -1) {
364                 this.changedData.splice(dataChangedIdx, 1);
365             }
366         }
367
368         if (this.isPropertiesTabSelected) {
369             this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
370         } else if (this.isInputsTabSelected) {
371             this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid && (<InputFEModel>changedItem).metadataIsValid);
372         } else if (this.isPoliciesTabSelected) {
373             this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
374         }
375         this.updateHasChangedData();
376     };
377
378
379     /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/
380
381     /**
382      * Handle select node in navigation area, and select the row in table
383      */
384     onPropertySelectedUpdate = ($event) => {
385         console.log("==>" + this.constructor.name + ": onPropertySelectedUpdate");
386         this.selectedFlatProperty = $event;
387         let parentProperty:PropertyFEModel = this.propertiesService.getParentPropertyFEModelFromPath(this.instanceFePropertiesMap[this.selectedFlatProperty.instanceName], this.selectedFlatProperty.path);
388         parentProperty.expandedChildPropertyId = this.selectedFlatProperty.path;
389     };
390
391     /**
392      * When user select row in table, this will prepare the hirarchy object for the tree.
393      */
394     selectPropertyRow = (propertyRowSelectedEvent:PropertyRowSelectedEvent) => {
395         console.log("==>" + this.constructor.name + ": selectPropertyRow " + propertyRowSelectedEvent.propertyModel.name);
396         let property = propertyRowSelectedEvent.propertyModel;
397         let instanceName = propertyRowSelectedEvent.instanceName;
398         this.propertyStructureHeader = null;
399
400         // Build hirarchy tree for the navigation and update propertiesNavigationData with it.
401         if (!(this.selectedInstanceData instanceof ComponentInstance) || this.selectedInstanceData.originType !== ResourceType.VF) {
402             let simpleFlatProperty:Array<SimpleFlatProperty>;
403             if (property instanceof PropertyFEModel) {
404                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName);
405             } else if (property instanceof DerivedFEProperty) {
406                 // Need to find parent PropertyFEModel
407                 let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => {
408                     return property.propertiesName.indexOf(tmpFeProperty.name)===0;
409                 });
410                 simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName);
411             }
412             this.propertiesNavigationData = simpleFlatProperty;
413         }
414
415         // Update the header in the navigation tree with property name.
416         this.propertyStructureHeader = (property.propertiesName.split('#'))[0];
417
418         // Set selected property in table
419         this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
420         this.hierarchyNavTabs.triggerTabChange('Property Structure');
421     };
422
423
424     selectInstanceRow = ($event) => {//get instance name
425         this.selectedInstanceData =  _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
426             return instance.name == $event;
427         });
428         this.hierarchyNavTabs.triggerTabChange('Composition');
429     };
430
431     tabChanged = (event) => {
432         // stop if has changed properties
433         if (this.hasChangedData) {
434             this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
435             this.showUnsavedChangesAlert().then((proceed) => {
436                 this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
437             }, ()=> {
438             });
439             return;
440         }
441
442         console.log("==>" + this.constructor.name + ": tabChanged " + event);
443         this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
444         this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
445         this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
446         this.isPoliciesTabSelected = this.currentMainTab.title === "Policies";
447         this.propertyStructureHeader = null;
448         this.searchQuery = '';
449     };
450
451
452
453     /*** DECLARE PROPERTIES/INPUTS ***/
454     declareProperties = (): void => {
455         console.log("==>" + this.constructor.name + ": declareProperties");
456
457         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
458         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
459         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
460         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
461         let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
462
463         angular.forEach(instancesIds, (instanceId: string): void => {
464             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
465             if (selectedInstanceData instanceof ComponentInstance) {
466                 if (!this.isInput(selectedInstanceData.originType)) {
467                     // convert Property FE model -> Property BE model, extract only checked
468                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
469                 } else {
470                     selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
471                 }
472             } else if (selectedInstanceData instanceof GroupInstance) {
473                 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
474             } else if (selectedInstanceData instanceof PolicyInstance) {
475                 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
476             }
477         });
478
479         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
480
481         //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties
482         inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] =
483             (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat(
484                 _.filter(
485                     inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
486                     (prop: PropertyBEModel) => this.isCapabilityProperty(prop)
487                 )
488             );
489         inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId] = _.filter(
490             inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
491             prop => !this.isCapabilityProperty(prop)
492         );
493         if (inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId].length === 0) {
494             delete inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId];
495         }
496
497         let isCapabilityPropertyChanged = false;
498         _.forEach(
499             inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId],
500             (prop: PropertyBEModel) => {
501                 prop.name = prop.origName || prop.name;
502                 if (this.isCapabilityProperty(prop)) {
503                     isCapabilityPropertyChanged = true;
504                 }
505             }
506         );
507         this.topologyTemplateService
508             .createInput(this.component, inputsToCreate, this.isSelf())
509             .subscribe((response) => {
510                 this.setInputTabIndication(response.length);
511                 this.checkedPropertiesCount = 0;
512                 this.checkedChildPropertiesCount = 0;
513                 _.forEach(response, (input: InputBEModel) => {
514                     const newInput: InputFEModel = new InputFEModel(input);
515                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
516                     this.inputs.push(newInput);
517                     this.updatePropertyValueAfterDeclare(newInput);
518                 });
519                 if (isCapabilityPropertyChanged) {
520                     this.reloadInstanceCapabilities();
521                 }
522             }, error => {}); //ignore error
523     };
524
525     declareListProperties = (): void => {
526         console.log('declareListProperties() - enter');
527
528         // get selected properties
529         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
530         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
531         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
532         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
533         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
534         let propertyNameList: Array<string> = [];
535         let insId :string;
536
537         angular.forEach(instancesIds, (instanceId: string): void => {
538             console.log("instanceId="+instanceId);
539             insId = instanceId;
540             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
541             let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
542
543             if (selectedInstanceData instanceof ComponentInstance) {
544                 if (!this.isInput(selectedInstanceData.originType)) {
545                     // convert Property FE model -> Property BE model, extract only checked
546                     selectedComponentInstancesProperties[instanceId] = checkedProperties;
547                 } else {
548                     selectedComponentInstancesInputs[instanceId] = checkedProperties;
549                 }
550             } else if (selectedInstanceData instanceof GroupInstance) {
551                 selectedGroupInstancesProperties[instanceId] = checkedProperties;
552             } else if (selectedInstanceData instanceof PolicyInstance) {
553                 selectedPolicyInstancesProperties[instanceId] = checkedProperties;
554             }
555
556             angular.forEach(checkedProperties, (property: PropertyBEModel) => {
557                 propertyNameList.push(property.name);
558             });
559         });
560
561         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
562
563         let modalTitle = 'Declare Properties as List Input';
564         const modal = this.ModalService.createCustomModal(new ModalModel(
565             'sm', /* size */
566             modalTitle, /* title */
567             null, /* content */
568             [ /* buttons */
569                 new ButtonModel(
570                     'Save', /* text */
571                     'blue', /* css class */
572                     () => { /* callback */
573                         let content:any = modal.instance.dynamicContent.instance;
574
575                         /* listInput */
576                         let reglistInput: InstanceBePropertiesMap = new InstanceBePropertiesMap();
577                         let typelist: any = PROPERTY_TYPES.LIST;
578                         let uniID: any = insId;
579                         let boolfalse: any = false;
580                         let required: any = content.propertyModel.required;
581                         let schem :any = {
582                             "empty": boolfalse,
583                             "property": {
584                                 "type": content.propertyModel.simpleType,
585                                 "required": required
586                             }
587                         }
588                         let schemaProp :any = {
589                             "type": content.propertyModel.simpleType,
590                             "required": required
591                         }
592
593                         reglistInput.description = content.propertyModel.description;
594                         reglistInput.name = content.propertyModel.name;
595                         reglistInput.type = typelist;
596                         reglistInput.schemaType = content.propertyModel.simpleType;
597                         reglistInput.instanceUniqueId = uniID;
598                         reglistInput.uniqueId = uniID;
599                         reglistInput.required = required;
600                         reglistInput.schema = schem;
601                         reglistInput.schemaProperty = schemaProp;
602
603                         let input = {
604                             componentInstInputsMap: content.inputsToCreate,
605                             listInput: reglistInput
606                         };
607                         console.log("save button clicked. input=", input);
608
609                         this.topologyTemplateService
610                         .createListInput(this.component, input, this.isSelf())
611                         .subscribe(response => {
612                             this.setInputTabIndication(response.length);
613                             this.checkedPropertiesCount = 0;
614                             this.checkedChildPropertiesCount = 0;
615                             _.forEach(response, (input: InputBEModel) => {
616                                 let newInput: InputFEModel = new InputFEModel(input);
617                                 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
618                                 this.inputs.push(newInput);
619                                 // create list input does not return updated properties info, so need to reload
620                                 //this.updatePropertyValueAfterDeclare(newInput);
621                                 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
622                                 this.changeSelectedInstance(this.selectedInstanceData);
623
624                                 modal.instance.close();
625                             });
626                         }, error => {}); //ignore error
627             
628                     }
629                     /*, getDisabled: function */
630                 ),
631                 new ButtonModel('Cancel', 'outline grey', () => {
632                     modal.instance.close();
633                 }),
634             ],
635             null /* type */
636         ));
637         // 3rd arg is passed to DeclareListComponent instance
638         this.ModalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
639         modal.instance.open();
640         console.log('declareListProperties() - leave');
641     };
642
643      /*** DECLARE PROPERTIES/POLICIES ***/
644      declarePropertiesToPolicies = (): void => {
645         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
646         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
647
648         angular.forEach(instancesIds, (instanceId: string): void => {
649             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
650             if (selectedInstanceData instanceof ComponentInstance) {
651                 if (!this.isInput(selectedInstanceData.originType)) {
652                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
653                 }
654             }
655         });
656
657         let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
658         this.loadingPolicies = true;
659
660         this.topologyTemplateService
661             .createPolicy(this.component, policiesToCreate, this.isSelf())
662             .subscribe(response => {
663                 this.setPolicyTabIndication(response.length);
664                 this.checkedPropertiesCount = 0;
665                 this.displayPoliciesAsDeclared(response);
666                 this.loadingPolicies = false;
667             }); //ignore error
668
669     }
670
671     displayPoliciesAsDeclared = (policies) => {
672         _.forEach(policies, (policy: any) => {
673             let newPolicy: InputFEModel = new InputFEModel(policy);
674             this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
675             newPolicy.relatedPropertyName = policy.name;
676             newPolicy.relatedPropertyValue = policy.value;
677             this.updatePropertyValueAfterDeclare(newPolicy);
678             this.policies.push(policy);
679         });
680     }
681
682     saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
683         return new Promise((resolve, reject) => {
684             if (!this.isValidChangedData) {
685                 reject('Changed data is invalid - cannot save!');
686                 return;
687             }
688             if (!this.changedData.length) {
689                 resolve([]);
690                 return;
691             }
692
693             // make request and its handlers
694             let request;
695             let handleSuccess, handleError;
696             let changedInputsProperties = [], changedCapabilitiesProperties = [];
697             if (this.isPropertiesTabSelected) {
698                 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
699                     changedProp = <PropertyFEModel>changedProp;
700                     const propBE = new PropertyBEModel(changedProp);
701                     propBE.toscaPresentation = new ToscaPresentationData();
702                     propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
703                     propBE.value = changedProp.getJSONValue();
704                     propBE.name = changedProp.origName || changedProp.name;
705                     delete propBE.origName;
706                     return propBE;
707                 });
708                 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
709
710                 if (this.selectedInstanceData instanceof ComponentInstance) {
711                     if (this.isInput(this.selectedInstanceData.originType)) {
712                         changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
713                         if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
714                             request = Observable.forkJoin(
715                                 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
716                                 this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
717                                     this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
718                             );
719                         }
720                         else if (changedInputsProperties.length) {
721                             request = this.componentInstanceServiceNg2
722                                 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
723                         }
724                         else if (changedCapabilitiesProperties.length) {
725                             request = this.componentInstanceServiceNg2
726                                 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
727                         }
728                         handleSuccess = (response) => {
729                             // reset each changed property with new value and remove it from changed properties list
730                             response.forEach((resInput) => {
731                                 const changedProp = <PropertyFEModel>this.changedData.shift();
732                                 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
733                             });
734                             console.log('updated instance inputs:', response);
735                         };
736                     } else {
737                         if (this.isSelf()) {
738                             request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId,  _.map(changedProperties, cp => {
739                                 delete cp.constraints;
740                                 return cp;
741                             }));
742                         } else {
743                             request = this.componentInstanceServiceNg2
744                                 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
745                         }
746                         handleSuccess = (response) => {
747                             // reset each changed property with new value and remove it from changed properties list
748                             response.forEach((resProp) => {
749                                 const changedProp = <PropertyFEModel>this.changedData.shift();
750                                 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
751                             });
752                             resolve(response);
753                             console.log("updated instance properties: ", response);
754                         };
755                     }
756                 } else if (this.selectedInstanceData instanceof GroupInstance) {
757                     request = this.componentInstanceServiceNg2
758                         .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
759                     handleSuccess = (response) => {
760                         // reset each changed property with new value and remove it from changed properties list
761                         response.forEach((resProp) => {
762                             const changedProp = <PropertyFEModel>this.changedData.shift();
763                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
764                         });
765                         resolve(response);
766                         console.log("updated group instance properties: ", response);
767                     };
768                 } else if (this.selectedInstanceData instanceof PolicyInstance) {
769                     request = this.componentInstanceServiceNg2
770                         .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
771                     handleSuccess = (response) => {
772                         // reset each changed property with new value and remove it from changed properties list
773                         response.forEach((resProp) => {
774                             const changedProp = <PropertyFEModel>this.changedData.shift();
775                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
776                         });
777                         resolve(response);
778                         console.log("updated policy instance properties: ", response);
779                     };
780                 }
781             } else if (this.isInputsTabSelected) {
782             
783                 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
784                     changedInput = <InputFEModel>changedInput;
785                     const inputBE = new InputBEModel(changedInput);
786                     inputBE.defaultValue = changedInput.getJSONDefaultValue();
787                     return inputBE;
788                 });
789                 request = this.componentServiceNg2
790                     .updateComponentInputs(this.component, changedInputs);
791                 handleSuccess = (response) => {
792                     // reset each changed property with new value and remove it from changed properties list
793                     response.forEach((resInput) => {
794                         const changedInput = <InputFEModel>this.changedData.shift();
795                         this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
796                         changedInput.required = resInput.required;
797                         changedInput.requiredOrig = resInput.required;
798                     });
799                     console.log("updated the component inputs and got this response: ", response);
800                 }
801             }
802
803             this.savingChangedData = true;
804             request.subscribe(
805                 (response) => {
806                     this.savingChangedData = false;
807                     if (changedCapabilitiesProperties.length) {
808                         this.reloadInstanceCapabilities();
809                     }
810                     handleSuccess && handleSuccess(response);
811                     this.updateHasChangedData();
812                     resolve(response);
813                 },
814                 (error) => {
815                     this.savingChangedData = false;
816                     handleError && handleError(error);
817                     this.updateHasChangedData();
818                     reject(error);
819                 }
820             );
821         });
822     };
823
824     reloadInstanceCapabilities = (): void => {
825         let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
826         this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
827             let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
828                 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
829                     return instance.capabilities;
830                 }
831                 return res;
832             }, new CapabilitiesGroup());
833             (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
834         });
835     };
836
837     reverseChangedData = ():void => {
838         // make reverse item handler
839         let handleReverseItem;
840         if (this.isPropertiesTabSelected) {
841             handleReverseItem = (changedItem) => {
842                 changedItem = <PropertyFEModel>changedItem;
843                 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
844                 this.checkedPropertiesCount = 0;
845                 this.checkedChildPropertiesCount = 0;
846             };
847         } else if (this.isInputsTabSelected) {
848             handleReverseItem = (changedItem) => {
849                 changedItem = <InputFEModel>changedItem;
850                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
851                 changedItem.resetMetadata();
852                 changedItem.required = changedItem.requiredOrig;
853             };
854         }
855
856         this.changedData.forEach(handleReverseItem);
857         this.changedData = [];
858         this.updateHasChangedData();
859     };
860
861     updateHasChangedData = ():boolean => {
862         const curHasChangedData:boolean = (this.changedData.length > 0);
863         if (curHasChangedData !== this.hasChangedData) {
864             this.hasChangedData = curHasChangedData;
865             if(this.hasChangedData) {
866                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
867             } else {
868                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
869             }
870         } 
871         return this.hasChangedData;
872     };
873
874     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
875         this.saveChangedData().then(
876             () => {
877                 this.Notification.success({
878                     message: 'Successfully saved changes',
879                     title: 'Saved'
880                 });
881                 if(onSuccessFunction) onSuccessFunction();
882                 if(this.isPropertiesTabSelected){
883                     this.checkedPropertiesCount = 0;
884                     this.checkedChildPropertiesCount = 0;
885                 }
886             },
887             () => {
888                 this.Notification.error({
889                     message: 'Failed to save changes!',
890                     title: 'Failure'
891                 });
892                 if(onError) onError();
893             }
894         );
895     };
896
897     showUnsavedChangesAlert = ():Promise<any> => {
898         let modalTitle:string;
899         if (this.isPropertiesTabSelected) {
900             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
901         } else if (this.isInputsTabSelected) {
902             modalTitle = `Unsaved inputs for ${this.component.name}`;
903         }
904
905         return new Promise<any>((resolve, reject) => {
906             const modal = this.ModalServiceSdcUI.openCustomModal(
907                 {
908                     title: modalTitle,
909                     size: 'sm',
910                     type: SdcUiCommon.ModalType.custom,
911                     testId: "navigate-modal",
912
913                     buttons: [
914                         {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
915                         {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
916                         {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
917                     ] as SdcUiCommon.IModalButtonComponent[]
918                 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
919         });
920
921     }
922
923     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
924         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
925             const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
926             const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
927                 return feProperty.name == input.relatedPropertyName &&
928                     (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
929             });
930             const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
931             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
932             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
933             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
934         }
935     }
936
937     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
938     updateCheckedPropertyCount = (increment: boolean): void => {
939         this.checkedPropertiesCount += (increment) ? 1 : -1;
940         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
941     };
942
943     updateCheckedChildPropertyCount = (increment: boolean): void => {
944         this.checkedChildPropertiesCount += (increment) ? 1 : -1;
945     };
946
947     setInputTabIndication = (numInputs: number): void => {
948         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
949     };
950
951     setPolicyTabIndication = (numPolicies: number): void => {
952         this.propertyInputTabs.setTabIndication('Policies', numPolicies);
953     }
954
955     resetUnsavedChangesForInput = (input:InputFEModel) => {
956         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
957         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
958         this.updateHasChangedData();
959     }
960
961     deleteInput = (input: InputFEModel) => {
962         //reset any unsaved changes to the input before deleting it
963         this.resetUnsavedChangesForInput(input);
964
965         console.log("==>" + this.constructor.name + ": deleteInput");
966         let inputToDelete = new InputBEModel(input);
967
968         this.componentServiceNg2
969             .deleteInput(this.component, inputToDelete)
970             .subscribe(response => {
971                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
972
973                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
974                 this.changeSelectedInstance(this.selectedInstanceData);
975                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
976
977                 // if (instanceFeProperties) {
978                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
979                 //         return prop.name == input.propertyName;
980                 //     });
981
982                 //     if (propToEnable) {
983                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
984                 //         propToEnable.setNonDeclared(response.inputPath);
985                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
986                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
987                 //     }
988                 // }
989             }, error => {}); //ignore error
990     };
991
992     deletePolicy = (policy: PolicyInstance) => {
993         this.loadingPolicies = true;
994         this.topologyTemplateService
995             .deletePolicy(this.component, policy)
996             .subscribe((response) => {
997                 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
998                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
999                 this.changeSelectedInstance(this.selectedInstanceData);
1000                 this.loadingPolicies = false;
1001             });
1002     };
1003
1004     deleteProperty = (property: PropertyFEModel) => {
1005         const propertyToDelete = new PropertyFEModel(property);
1006         this.loadingProperties = true;
1007         const feMap = this.instanceFePropertiesMap;
1008         this.topologyTemplateService
1009             .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1010             .subscribe((response) => {
1011                 const props = feMap[this.component.uniqueId];
1012                 props.splice(props.findIndex(p => p.uniqueId === response),1);
1013                 this.loadingProperties = false;
1014             }, (error) => {
1015                 this.loadingProperties = false;
1016                 console.error(error);
1017             });
1018     }
1019
1020     /*** addProperty ***/
1021     addProperty = () => {
1022         let modalTitle = 'Add Property';
1023         let modal = this.ModalService.createCustomModal(new ModalModel(
1024             'sm',
1025             modalTitle,
1026             null,
1027             [
1028                 new ButtonModel('Save', 'blue', () => {
1029                     modal.instance.dynamicContent.instance.isLoading = true;
1030                     const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1031                     this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1032                         .subscribe((response) => {
1033                             modal.instance.dynamicContent.instance.isLoading = false;
1034                             const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1035                             this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1036                             modal.instance.close();
1037                         }, (error) => {
1038                             modal.instance.dynamicContent.instance.isLoading = false;
1039                             this.Notification.error({
1040                                 message: 'Failed to add property:' + error,
1041                                 title: 'Failure'
1042                             });
1043                         });
1044                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1045                 new ButtonModel('Cancel', 'outline grey', () => {
1046                     modal.instance.close();
1047                 }),
1048             ],
1049             null
1050         ));
1051         this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1052         modal.instance.open();
1053     }
1054
1055     /*** addInput ***/
1056     addInput = () => {
1057         let modalTitle = 'Add Input';
1058         let modal = this.ModalService.createCustomModal(new ModalModel(
1059             'sm',
1060             modalTitle,
1061             null,
1062             [
1063                 new ButtonModel('Save', 'blue', () => {
1064                     modal.instance.dynamicContent.instance.isLoading = true;
1065                     const newInput: InputBEModel = modal.instance.dynamicContent.instance.propertyModel;
1066                     this.topologyTemplateService.createServiceInput(this.component.uniqueId, newInput)
1067                         .subscribe((response) => {
1068                             modal.instance.dynamicContent.instance.isLoading = false;
1069                             const newInputProp: InputFEModel = this.inputsUtils.convertInputBEToInputFE(response);
1070                             this.inputs.push(newInputProp);
1071                             modal.instance.close();
1072                         }, (error) => {
1073                             modal.instance.dynamicContent.instance.isLoading = false;
1074                             this.Notification.error({
1075                                 message: 'Failed to add input:' + error,
1076                                 title: 'Failure'
1077                             });
1078                         });
1079                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1080                 new ButtonModel('Cancel', 'outline grey', () => {
1081                     modal.instance.close();
1082                 }),
1083             ],
1084             null
1085         ));
1086         this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1087         modal.instance.open();
1088     }
1089
1090     /*** SEARCH RELATED FUNCTIONS ***/
1091     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1092         let instanceBePropertiesMap:InstanceBePropertiesMap;
1093         this.componentServiceNg2
1094             .filterComponentInstanceProperties(this.component, filterData)
1095             .subscribe((response) => {
1096                 this.processInstancePropertiesResponse(response, false);
1097                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1098                 this.searchPropertyName = filterData.propertyName;//mark in table
1099                 this.hierarchyNavTabs.triggerTabChange('Composition');
1100                 this.propertiesNavigationData = [];
1101                 this.displayClearSearch = true;
1102             }, (error) => {}); //ignore error
1103
1104     }
1105
1106     clearSearch = () => {
1107         this.instancesNavigationData = this.instances;
1108         this.searchPropertyName = "";
1109         this.hierarchyPropertiesDisplayOptions.searchText = "";
1110         this.displayClearSearch = false;
1111         this.advanceSearch.clearAll();
1112         this.searchQuery = '';
1113     };
1114
1115     clickOnClearSearch = () => {
1116         this.clearSearch();
1117         this.selectFirstInstanceByDefault();
1118         this.hierarchyNavTabs.triggerTabChange('Composition');
1119     };
1120
1121     private isInput = (instanceType:string):boolean =>{
1122         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
1123     }
1124     
1125
1126 }