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