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