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