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