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