Fix declare input button when checked/unchecked 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             };
845         } else if (this.isInputsTabSelected) {
846             handleReverseItem = (changedItem) => {
847                 changedItem = <InputFEModel>changedItem;
848                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
849                 changedItem.resetMetadata();
850                 changedItem.required = changedItem.requiredOrig;
851             };
852         }
853
854         this.changedData.forEach(handleReverseItem);
855         this.changedData = [];
856         this.updateHasChangedData();
857     };
858
859     updateHasChangedData = ():boolean => {
860         const curHasChangedData:boolean = (this.changedData.length > 0);
861         if (curHasChangedData !== this.hasChangedData) {
862             this.hasChangedData = curHasChangedData;
863             if(this.hasChangedData) {
864                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
865             } else {
866                 this.EventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
867             }
868         } 
869         return this.hasChangedData;
870     };
871
872     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
873         this.saveChangedData().then(
874             () => {
875                 this.Notification.success({
876                     message: 'Successfully saved changes',
877                     title: 'Saved'
878                 });
879                 if(onSuccessFunction) onSuccessFunction();
880             },
881             () => {
882                 this.Notification.error({
883                     message: 'Failed to save changes!',
884                     title: 'Failure'
885                 });
886                 if(onError) onError();
887             }
888         );
889     };
890
891     showUnsavedChangesAlert = ():Promise<any> => {
892         let modalTitle:string;
893         if (this.isPropertiesTabSelected) {
894             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
895         } else if (this.isInputsTabSelected) {
896             modalTitle = `Unsaved inputs for ${this.component.name}`;
897         }
898
899         return new Promise<any>((resolve, reject) => {
900             const modal = this.ModalServiceSdcUI.openCustomModal(
901                 {
902                     title: modalTitle,
903                     size: 'sm',
904                     type: SdcUiCommon.ModalType.custom,
905                     testId: "navigate-modal",
906
907                     buttons: [
908                         {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
909                         {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
910                         {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
911                     ] as SdcUiCommon.IModalButtonComponent[]
912                 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
913         });
914
915     }
916
917     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
918         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
919             const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
920             const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
921                 return feProperty.name == input.relatedPropertyName &&
922                     (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
923             });
924             const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
925             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
926             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
927             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
928         }
929     }
930
931     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
932     updateCheckedPropertyCount = (increment: boolean): void => {
933         this.checkedPropertiesCount += (increment) ? 1 : -1;
934         console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount);
935     };
936
937     updateCheckedChildPropertyCount = (increment: boolean): void => {
938         this.checkedChildPropertiesCount += (increment) ? 1 : -1;
939     };
940
941     setInputTabIndication = (numInputs: number): void => {
942         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
943     };
944
945     setPolicyTabIndication = (numPolicies: number): void => {
946         this.propertyInputTabs.setTabIndication('Policies', numPolicies);
947     }
948
949     resetUnsavedChangesForInput = (input:InputFEModel) => {
950         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
951         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
952         this.updateHasChangedData();
953     }
954
955     deleteInput = (input: InputFEModel) => {
956         //reset any unsaved changes to the input before deleting it
957         this.resetUnsavedChangesForInput(input);
958
959         console.log("==>" + this.constructor.name + ": deleteInput");
960         let inputToDelete = new InputBEModel(input);
961
962         this.componentServiceNg2
963             .deleteInput(this.component, inputToDelete)
964             .subscribe(response => {
965                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
966
967                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
968                 this.changeSelectedInstance(this.selectedInstanceData);
969                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
970
971                 // if (instanceFeProperties) {
972                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
973                 //         return prop.name == input.propertyName;
974                 //     });
975
976                 //     if (propToEnable) {
977                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
978                 //         propToEnable.setNonDeclared(response.inputPath);
979                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
980                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
981                 //     }
982                 // }
983             }, error => {}); //ignore error
984     };
985
986     deletePolicy = (policy: PolicyInstance) => {
987         this.loadingPolicies = true;
988         this.topologyTemplateService
989             .deletePolicy(this.component, policy)
990             .subscribe((response) => {
991                 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
992                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
993                 this.changeSelectedInstance(this.selectedInstanceData);
994                 this.loadingPolicies = false;
995             });
996     };
997
998     deleteProperty = (property: PropertyFEModel) => {
999         const propertyToDelete = new PropertyFEModel(property);
1000         this.loadingProperties = true;
1001         const feMap = this.instanceFePropertiesMap;
1002         this.topologyTemplateService
1003             .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1004             .subscribe((response) => {
1005                 const props = feMap[this.component.uniqueId];
1006                 props.splice(props.findIndex(p => p.uniqueId === response),1);
1007                 this.loadingProperties = false;
1008             }, (error) => {
1009                 this.loadingProperties = false;
1010                 console.error(error);
1011             });
1012     }
1013
1014     /*** addProperty ***/
1015     addProperty = () => {
1016         let modalTitle = 'Add Property';
1017         let modal = this.ModalService.createCustomModal(new ModalModel(
1018             'sm',
1019             modalTitle,
1020             null,
1021             [
1022                 new ButtonModel('Save', 'blue', () => {
1023                     modal.instance.dynamicContent.instance.isLoading = true;
1024                     const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1025                     this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1026                         .subscribe((response) => {
1027                             modal.instance.dynamicContent.instance.isLoading = false;
1028                             const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1029                             this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1030                             modal.instance.close();
1031                         }, (error) => {
1032                             modal.instance.dynamicContent.instance.isLoading = false;
1033                             this.Notification.error({
1034                                 message: 'Failed to add property:' + error,
1035                                 title: 'Failure'
1036                             });
1037                         });
1038                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1039                 new ButtonModel('Cancel', 'outline grey', () => {
1040                     modal.instance.close();
1041                 }),
1042             ],
1043             null
1044         ));
1045         this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1046         modal.instance.open();
1047     }
1048
1049     /*** addInput ***/
1050     addInput = () => {
1051         let modalTitle = 'Add Input';
1052         let modal = this.ModalService.createCustomModal(new ModalModel(
1053             'sm',
1054             modalTitle,
1055             null,
1056             [
1057                 new ButtonModel('Save', 'blue', () => {
1058                     modal.instance.dynamicContent.instance.isLoading = true;
1059                     const newInput: InputBEModel = modal.instance.dynamicContent.instance.propertyModel;
1060                     this.topologyTemplateService.createServiceInput(this.component.uniqueId, newInput)
1061                         .subscribe((response) => {
1062                             modal.instance.dynamicContent.instance.isLoading = false;
1063                             const newInputProp: InputFEModel = this.inputsUtils.convertInputBEToInputFE(response);
1064                             this.inputs.push(newInputProp);
1065                             modal.instance.close();
1066                         }, (error) => {
1067                             modal.instance.dynamicContent.instance.isLoading = false;
1068                             this.Notification.error({
1069                                 message: 'Failed to add input:' + error,
1070                                 title: 'Failure'
1071                             });
1072                         });
1073                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1074                 new ButtonModel('Cancel', 'outline grey', () => {
1075                     modal.instance.close();
1076                 }),
1077             ],
1078             null
1079         ));
1080         this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1081         modal.instance.open();
1082     }
1083
1084     /*** SEARCH RELATED FUNCTIONS ***/
1085     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1086         let instanceBePropertiesMap:InstanceBePropertiesMap;
1087         this.componentServiceNg2
1088             .filterComponentInstanceProperties(this.component, filterData)
1089             .subscribe((response) => {
1090                 this.processInstancePropertiesResponse(response, false);
1091                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1092                 this.searchPropertyName = filterData.propertyName;//mark in table
1093                 this.hierarchyNavTabs.triggerTabChange('Composition');
1094                 this.propertiesNavigationData = [];
1095                 this.displayClearSearch = true;
1096             }, (error) => {}); //ignore error
1097
1098     }
1099
1100     clearSearch = () => {
1101         this.instancesNavigationData = this.instances;
1102         this.searchPropertyName = "";
1103         this.hierarchyPropertiesDisplayOptions.searchText = "";
1104         this.displayClearSearch = false;
1105         this.advanceSearch.clearAll();
1106         this.searchQuery = '';
1107     };
1108
1109     clickOnClearSearch = () => {
1110         this.clearSearch();
1111         this.selectFirstInstanceByDefault();
1112         this.hierarchyNavTabs.triggerTabChange('Composition');
1113     };
1114
1115     private isInput = (instanceType:string):boolean =>{
1116         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
1117     }
1118     
1119
1120 }