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