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