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