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