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