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