Allow multiple entry for map/list when tosca function is selected
[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 clearCheckedInstancePropertyValue() {
633         const checkedInstanceProperty: PropertyBEModel = this.buildCheckedInstanceProperty();
634         const currentValue : any = checkedInstanceProperty.value;
635         checkedInstanceProperty.getInputValues = null;
636         checkedInstanceProperty.value = null;
637         checkedInstanceProperty.toscaFunction = null;
638         if (checkedInstanceProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName){
639             const propertiesNameArray = (<PropertyDeclareAPIModel>checkedInstanceProperty).propertiesName;
640             const parts = propertiesNameArray.split("#");
641             let currentKey = (<DerivedFEProperty>checkedInstanceProperty.input).toscaPath;
642             if (propertiesNameArray.length > 1){
643                 const index = checkedInstanceProperty.subPropertyToscaFunctions.findIndex(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, currentKey.length > 0 ? currentKey : parts.slice(1)));
644                 checkedInstanceProperty.subPropertyToscaFunctions.splice(index, 1);
645             }
646             if (this.enableToscaFunction) {
647                 this.processSubtoscaFunction(checkedInstanceProperty,null);
648                 return;
649             }
650         }
651         if (this.selectedInstanceData instanceof ComponentInstance) {
652             var toRemove = this.changedData.filter(obj => {
653                 return obj.name == checkedInstanceProperty.name && obj.type == checkedInstanceProperty.type && obj.value == null;
654             });
655             const index: number = this.changedData.indexOf(toRemove[0]);
656             if (index !== -1) {
657                 this.changedData.splice(index, 1);
658             }
659             this.updateInstanceProperty(checkedInstanceProperty);
660         } else if (this.selectedInstanceData instanceof GroupInstance) {
661             this.updateGroupInstanceProperty(checkedInstanceProperty);
662         } else if (this.selectedInstanceData instanceof PolicyInstance) {
663             this.updatePolicyInstanceProperty(checkedInstanceProperty);
664         }
665     }
666
667     private processSubtoscaFunction(checkedProperty : PropertyDeclareAPIModel, toscaFunction: ToscaFunction) {
668         const instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
669         const instanceId: string = instancesIds[0];
670         this.instanceFePropertiesMap[instanceId].forEach(prop => {
671             if (prop.flattenedChildren) {
672                 prop.flattenedChildren.forEach((child) => {
673                     if (child.isSelected && !child.isDeclared && !child.isDisabled) {
674                         prop.subPropertyToscaFunctions = checkedProperty.subPropertyToscaFunctions;
675                         if (toscaFunction) {
676                             child.value = toscaFunction.buildValueString();
677                             child.valueObj = toscaFunction.buildValueObject();
678                             child.toscaFunction = toscaFunction;
679                             this.hasChangedData = true;
680                             if (this.changedData.length == 0) {
681                                 this.changedData.push(prop);
682                             }
683                         } else {
684                             child.valueObj = null;
685                             child.toscaFunction = null;
686                         }
687                         child.isSelected = false;
688                         this.togggleToscaBtn(false);                                
689                     }
690                 });
691             }
692         });
693     }
694
695     private updateCheckedInstancePropertyFunctionValue(toscaFunction: ToscaFunction) {
696         const checkedProperty: PropertyBEModel = this.buildCheckedInstanceProperty();
697         if (checkedProperty instanceof PropertyDeclareAPIModel && (<PropertyDeclareAPIModel>checkedProperty).propertiesName){
698             const propertiesName = (<PropertyDeclareAPIModel>checkedProperty).propertiesName;
699             const parts = propertiesName.split("#");
700             let currentKey = (<DerivedFEProperty>checkedProperty.input).toscaPath;
701             if (checkedProperty.subPropertyToscaFunctions == null){
702                 checkedProperty.subPropertyToscaFunctions = [];
703             }
704             let subPropertyToscaFunction = checkedProperty.subPropertyToscaFunctions.find(existingSubPropertyToscaFunction => this.areEqual(existingSubPropertyToscaFunction.subPropertyPath, currentKey.length > 0 ? currentKey : parts.slice(1)));
705             if (!subPropertyToscaFunction){
706                  subPropertyToscaFunction = new SubPropertyToscaFunction();
707                  checkedProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
708             }
709             subPropertyToscaFunction.toscaFunction = toscaFunction;
710             subPropertyToscaFunction.subPropertyPath = currentKey.length > 0 ? currentKey : parts.slice(1);
711             if (this.enableToscaFunction) {
712                 this.processSubtoscaFunction(checkedProperty,toscaFunction);
713                 return;
714             }
715         } else {
716             checkedProperty.subPropertyToscaFunctions = null;
717             checkedProperty.toscaFunction = toscaFunction;
718         }
719         if (this.selectedInstanceData instanceof ComponentInstance) {
720             this.updateInstanceProperty(checkedProperty);
721         } else if (this.selectedInstanceData instanceof GroupInstance) {
722             this.updateGroupInstanceProperty(checkedProperty);
723         } else if (this.selectedInstanceData instanceof PolicyInstance) {
724             this.updatePolicyInstanceProperty(checkedProperty);
725         }
726     }
727
728     private isComplexSchemaType(propertyType: string): boolean {
729         return PROPERTY_DATA.SIMPLE_TYPES.indexOf(propertyType) === -1;
730     }
731
732     private isListOrMap(propertyType: string): boolean {
733         return PROPERTY_TYPES.MAP === propertyType || PROPERTY_TYPES.LIST === propertyType;
734     }
735
736     private areEqual(array1: string[], array2: string[]): boolean {
737         return array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
738     }
739
740     updateInstanceProperty(instanceProperty: PropertyBEModel) {
741         this.loadingProperties = true;
742         this.enableToscaFunction = false;
743         this.checkedToscaCount = 0;
744
745         this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
746             this.selectedInstanceData.uniqueId, [instanceProperty])
747         .subscribe(() => {
748             this.changeSelectedInstance(this.getSelectedInstance());
749         }, (error) => {
750             this.loadingProperties = false;
751             console.error(error);
752         }, () => {
753             this.loadingProperties = false;
754         });
755     }
756
757     updateGroupInstanceProperty(instanceProperty: PropertyBEModel) {
758         this.loadingProperties = true;
759         this.componentInstanceServiceNg2.updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId,
760             this.selectedInstanceData.uniqueId, [instanceProperty])
761         .subscribe(() => {
762             this.changeSelectedInstance(this.getSelectedInstance());
763         }, (error) => {
764             this.loadingProperties = false;
765             console.error(error);
766         }, () => {
767             this.loadingProperties = false;
768         });
769     }
770
771     updatePolicyInstanceProperty(instanceProperty: PropertyBEModel) {
772         this.loadingProperties = true;
773         this.componentInstanceServiceNg2.updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId,
774             this.selectedInstanceData.uniqueId, [instanceProperty])
775         .subscribe(() => {
776             this.changeSelectedInstance(this.getSelectedInstance());
777         }, (error) => {
778             this.loadingProperties = false;
779             console.error(error);
780         }, () => {
781             this.loadingProperties = false;
782         });
783     }
784
785     declareInput = (): void => {
786         if (this.checkedPropertiesCount == 1) {
787             this.openAddInputNameAndDeclareInputModal();
788         } else if (this.checkedPropertiesCount > 1) {
789             this.declareInputFromProperties();
790         }
791     }
792
793     /*** DECLARE PROPERTIES/INPUTS ***/
794     declareInputFromProperties = (inputName?: string): void => {
795         console.debug("==>" + this.constructor.name + ": declareProperties");
796
797         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
798         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
799         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
800         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
801         let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
802
803         angular.forEach(instancesIds, (instanceId: string): void => {
804             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
805             if (selectedInstanceData instanceof ComponentInstance) {
806                 if (!this.isInput(selectedInstanceData.originType)) {
807                     // convert Property FE model -> Property BE model, extract only checked
808                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
809                     if (inputName) {
810                         selectedComponentInstancesProperties[instanceId][0].inputName = inputName;
811                     }
812                 } else {
813                     selectedComponentInstancesInputs[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
814                 }
815             } else if (selectedInstanceData instanceof GroupInstance) {
816                 selectedGroupInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
817             } else if (selectedInstanceData instanceof PolicyInstance) {
818                 selectedPolicyInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
819             }
820         });
821
822         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
823
824         //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties
825         inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] =
826             (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat(
827                 _.filter(
828                     inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
829                     (prop: PropertyBEModel) => this.isCapabilityProperty(prop)
830                 )
831             );
832         inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId] = _.filter(
833             inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId],
834             prop => !this.isCapabilityProperty(prop)
835         );
836         if (inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId].length === 0) {
837             delete inputsToCreate.componentInstanceInputsMap[this.selectedInstanceData.uniqueId];
838         }
839
840         let isCapabilityPropertyChanged = false;
841         _.forEach(
842             inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId],
843             (prop: PropertyBEModel) => {
844                 prop.name = prop.origName || prop.name;
845                 if (this.isCapabilityProperty(prop)) {
846                     isCapabilityPropertyChanged = true;
847                 }
848             }
849         );
850         this.topologyTemplateService
851             .createInput(this.component, inputsToCreate, this.isSelf())
852             .subscribe((response) => {
853                 this.selectInstanceRow(SERVICE_SELF_TITLE);
854                 this.onInstanceSelectedUpdate(this.instances[0]);
855                 this.setInputTabIndication(response.length);
856                 this.checkedPropertiesCount = 0;
857                 this.checkedChildPropertiesCount = 0;
858                 _.forEach(response, (input: InputBEModel) => {
859                     const newInput: InputFEModel = new InputFEModel(input);
860                     this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
861                     this.inputs.push(newInput);
862                     this.updatePropertyValueAfterDeclare(newInput);
863                 });
864                 if (isCapabilityPropertyChanged) {
865                     this.reloadInstanceCapabilities();
866                 }
867             }, error => {}); //ignore error
868     };
869
870     generateDefaultInputName = (): string => {
871         let defaultInputName: string;
872         let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []);
873         angular.forEach(instancesIds, (instanceId: string) => {
874             let selectedProperty: PropertyBEModel = new PropertyBEModel(this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId])[0]);
875             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
876             defaultInputName = this.generateInputName(selectedInstanceData.invariantName, selectedProperty.name);
877         });
878         return defaultInputName;
879     }
880
881     private generateInputName = (componentName: string, propertyName: string): string => {
882         let inputName: string;
883         if (propertyName) {
884             if (propertyName.includes("::")) {
885                 propertyName = propertyName.replace(/::/g, "_");
886             }
887             if (componentName) {
888                 inputName = componentName + "_" + propertyName;
889             } else {
890                 inputName = propertyName;
891             }
892         }
893         return inputName;
894     }
895
896     private openAddInputNameAndDeclareInputModal = (): void => {
897         const modalTitle = this.translateService.translate('ADD_INPUT_NAME_TO_DECLARE');
898         const modalButtons = [];
899         const modal = this.modalService.createCustomModal(new ModalModel(
900             'sm',
901             modalTitle,
902             null,
903             modalButtons,
904             null /* type */
905         ));
906         modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_SAVE'), 'blue',
907             () => {
908                 const inputName: string = modal.instance.dynamicContent.instance.inputNameForm.value;
909                 if (inputName) {
910                     this.declareInputFromProperties(inputName);
911                 } else {
912                     this.notification.warning({
913                         message: 'Failed to set input name',
914                         title: 'Warning'
915                     });
916                 }
917                 this.modalService.closeCurrentModal();
918             }
919         ));
920         modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CANCEL'), 'outline grey', () => {
921             this.modalService.closeCurrentModal();
922         }));
923         this.modalService.addDynamicContentToModal(modal, DeclareInputComponent, {defaultInputName: this.generateDefaultInputName()});
924         modal.instance.open();
925     }
926
927     declareListProperties = (): void => {
928         // get selected properties
929         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
930         let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
931         let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
932         let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap();
933         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
934         let propertyNameList: Array<string> = [];
935         let insId :string;
936
937         angular.forEach(instancesIds, (instanceId: string): void => {
938             insId = instanceId;
939             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
940             let checkedProperties: PropertyBEModel[] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
941
942             if (selectedInstanceData instanceof ComponentInstance) {
943                 if (!this.isInput(selectedInstanceData.originType)) {
944                     // convert Property FE model -> Property BE model, extract only checked
945                     selectedComponentInstancesProperties[instanceId] = checkedProperties;
946                 } else {
947                     selectedComponentInstancesInputs[instanceId] = checkedProperties;
948                 }
949             } else if (selectedInstanceData instanceof GroupInstance) {
950                 selectedGroupInstancesProperties[instanceId] = checkedProperties;
951             } else if (selectedInstanceData instanceof PolicyInstance) {
952                 selectedPolicyInstancesProperties[instanceId] = checkedProperties;
953             }
954
955             angular.forEach(checkedProperties, (property: PropertyBEModel) => {
956                 propertyNameList.push(property.name);
957             });
958         });
959
960         let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties);
961
962         let modalTitle = 'Declare Properties as List Input';
963         const modal = this.modalService.createCustomModal(new ModalModel(
964             'sm', /* size */
965             modalTitle, /* title */
966             null, /* content */
967             [ /* buttons */
968                 new ButtonModel(
969                     'Save', /* text */
970                     'blue', /* css class */
971                     () => { /* callback */
972                         let content:any = modal.instance.dynamicContent.instance;
973
974                         /* listInput */
975                         let reglistInput: InstanceBePropertiesMap = new InstanceBePropertiesMap();
976                         let typelist: any = PROPERTY_TYPES.LIST;
977                         let uniID: any = insId;
978                         let boolfalse: any = false;
979                         let required: any = content.propertyModel.required;
980                         let schem :any = {
981                             "empty": boolfalse,
982                             "property": {
983                                 "type": content.propertyModel.simpleType,
984                                 "required": required
985                             }
986                         }
987                         let schemaProp :any = {
988                             "type": content.propertyModel.simpleType,
989                             "required": required
990                         }
991
992                         reglistInput.description = content.propertyModel.description;
993                         reglistInput.name = content.propertyModel.name;
994                         reglistInput.type = typelist;
995                         reglistInput.schemaType = content.propertyModel.simpleType;
996                         reglistInput.instanceUniqueId = uniID;
997                         reglistInput.uniqueId = uniID;
998                         reglistInput.required = required;
999                         reglistInput.schema = schem;
1000                         reglistInput.schemaProperty = schemaProp;
1001
1002                         let input = {
1003                             componentInstInputsMap: content.inputsToCreate,
1004                             listInput: reglistInput
1005                         };
1006
1007                         this.topologyTemplateService
1008                         .createListInput(this.component, input, this.isSelf())
1009                         .subscribe(response => {
1010                             this.setInputTabIndication(response.length);
1011                             this.checkedPropertiesCount = 0;
1012                             this.checkedChildPropertiesCount = 0;
1013                             _.forEach(response, (input: InputBEModel) => {
1014                                 let newInput: InputFEModel = new InputFEModel(input);
1015                                 this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
1016                                 this.inputs.push(newInput);
1017                                 // create list input does not return updated properties info, so need to reload
1018                                 //this.updatePropertyValueAfterDeclare(newInput);
1019                                 // Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
1020                                 this.changeSelectedInstance(this.selectedInstanceData);
1021
1022                                 modal.instance.close();
1023                             });
1024                         }, error => {}); //ignore error
1025             
1026                     }
1027                     /*, getDisabled: function */
1028                 ),
1029                 new ButtonModel('Cancel', 'outline grey', () => {
1030                     modal.instance.close();
1031                 }),
1032             ],
1033             null /* type */
1034         ));
1035         // 3rd arg is passed to DeclareListComponent instance
1036         this.modalService.addDynamicContentToModal(modal, DeclareListComponent, {properties: inputsToCreate, propertyNameList: propertyNameList});
1037         modal.instance.open();
1038     };
1039
1040      /*** DECLARE PROPERTIES/POLICIES ***/
1041      declarePropertiesToPolicies = (): void => {
1042         let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap();
1043         let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []);
1044
1045         angular.forEach(instancesIds, (instanceId: string): void => {
1046             let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId);
1047             if (selectedInstanceData instanceof ComponentInstance) {
1048                 if (!this.isInput(selectedInstanceData.originType)) {
1049                     selectedComponentInstancesProperties[instanceId] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceId]);
1050                 }
1051             }
1052         });
1053
1054         let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null);
1055         this.loadingPolicies = true;
1056
1057         this.topologyTemplateService
1058             .createPolicy(this.component, policiesToCreate, this.isSelf())
1059             .subscribe(response => {
1060                 this.setPolicyTabIndication(response.length);
1061                 this.checkedPropertiesCount = 0;
1062                 this.displayPoliciesAsDeclared(response);
1063                 this.loadingPolicies = false;
1064             }); //ignore error
1065
1066     }
1067
1068     displayPoliciesAsDeclared = (policies) => {
1069         _.forEach(policies, (policy: any) => {
1070             let newPolicy: InputFEModel = new InputFEModel(policy);
1071             this.inputsUtils.resetInputDefaultValue(newPolicy, policy.defaultValue);
1072             newPolicy.relatedPropertyName = policy.name;
1073             newPolicy.relatedPropertyValue = policy.value;
1074             this.updatePropertyValueAfterDeclare(newPolicy);
1075             this.policies.push(policy);
1076         });
1077     }
1078
1079     saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
1080         return new Promise((resolve, reject) => {
1081             if (!this.isValidChangedData) {
1082                 reject('Changed data is invalid - cannot save!');
1083                 return;
1084             }
1085             if (!this.changedData.length) {
1086                 resolve([]);
1087                 return;
1088             }
1089
1090             // make request and its handlers
1091             let request;
1092             let handleSuccess, handleError;
1093             let changedInputsProperties = [], changedCapabilitiesProperties = [];
1094             if (this.isPropertiesTabSelected) {
1095                 const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
1096                     changedProp = <PropertyFEModel>changedProp;
1097                     const propBE = new PropertyBEModel(changedProp);
1098                     propBE.toscaPresentation = new ToscaPresentationData();
1099                     propBE.toscaPresentation.ownerId = changedProp.parentUniqueId;
1100                     propBE.value = changedProp.getJSONValue();
1101                     propBE.name = changedProp.origName || changedProp.name;
1102                     delete propBE.origName;
1103                     return propBE;
1104                 });
1105                 changedCapabilitiesProperties = _.filter(changedProperties, prop => this.isCapabilityProperty(prop));
1106
1107                 if (this.selectedInstanceData instanceof ComponentInstance) {
1108                     if (this.isInput(this.selectedInstanceData.originType)) {
1109                         changedInputsProperties = _.filter(changedProperties, prop => !this.isCapabilityProperty(prop));
1110                         if (changedInputsProperties.length && changedCapabilitiesProperties.length) {
1111                             request = Observable.forkJoin(
1112                                 this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties),
1113                                 this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId,
1114                                     this.selectedInstanceData.uniqueId, changedCapabilitiesProperties)
1115                             );
1116                         }
1117                         else if (changedInputsProperties.length) {
1118                             request = this.componentInstanceServiceNg2
1119                                 .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties);
1120                         }
1121                         else if (changedCapabilitiesProperties.length) {
1122                             request = this.componentInstanceServiceNg2
1123                                 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties);
1124                         }
1125                         handleSuccess = (response) => {
1126                             // reset each changed property with new value and remove it from changed properties list
1127                             response.forEach((resInput) => {
1128                                 const changedProp = <PropertyFEModel>this.changedData.shift();
1129                                 this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
1130                             });
1131                         };
1132                     } else {
1133                         if (this.isSelf()) {
1134                             request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId,  _.map(changedProperties, cp => {
1135                                 delete cp.constraints;
1136                                 return cp;
1137                             }));
1138                         } else {
1139                             request = this.componentInstanceServiceNg2
1140                                 .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
1141                         }
1142                         handleSuccess = (response) => {
1143                             // reset each changed property with new value and remove it from changed properties list
1144                             response.forEach((resProp) => {
1145                                 const changedProp = <PropertyFEModel>this.changedData.shift();
1146                                 this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
1147                             });
1148                             resolve(response);
1149                         };
1150                     }
1151                 } else if (this.selectedInstanceData instanceof GroupInstance) {
1152                     request = this.componentInstanceServiceNg2
1153                         .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
1154                     handleSuccess = (response) => {
1155                         // reset each changed property with new value and remove it from changed properties list
1156                         response.forEach((resProp) => {
1157                             const changedProp = <PropertyFEModel>this.changedData.shift();
1158                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
1159                         });
1160                         resolve(response);
1161                     };
1162                 } else if (this.selectedInstanceData instanceof PolicyInstance) {
1163                     request = this.componentInstanceServiceNg2
1164                         .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties);
1165                     handleSuccess = (response) => {
1166                         // reset each changed property with new value and remove it from changed properties list
1167                         response.forEach((resProp) => {
1168                             const changedProp = <PropertyFEModel>this.changedData.shift();
1169                             this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
1170                         });
1171                         resolve(response);
1172                     };
1173                 }
1174             } else if (this.isInputsTabSelected) {
1175             
1176                 const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
1177                     changedInput = <InputFEModel>changedInput;
1178                     const inputBE = new InputBEModel(changedInput);
1179                     inputBE.defaultValue = changedInput.getJSONDefaultValue();
1180                     return inputBE;
1181                 });
1182                 request = this.componentServiceNg2
1183                     .updateComponentInputs(this.component, changedInputs);
1184                 handleSuccess = (response) => {
1185                     // reset each changed property with new value and remove it from changed properties list
1186                     response.forEach((resInput) => {
1187                         const changedInput = <InputFEModel>this.changedData.shift();
1188                         this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
1189                         changedInput.required = resInput.required;
1190                         changedInput.requiredOrig = resInput.required;
1191                     });
1192                 }
1193             }
1194
1195             this.savingChangedData = true;
1196             request.subscribe(
1197                 (response) => {
1198                     this.savingChangedData = false;
1199                     if (changedCapabilitiesProperties.length) {
1200                         this.reloadInstanceCapabilities();
1201                     }
1202                     handleSuccess && handleSuccess(response);
1203                     this.updateHasChangedData();
1204                     resolve(response);
1205                 },
1206                 (error) => {
1207                     this.savingChangedData = false;
1208                     handleError && handleError(error);
1209                     this.updateHasChangedData();
1210                     reject(error);
1211                 }
1212             );
1213         });
1214     };
1215
1216     reloadInstanceCapabilities = (): void => {
1217         let currentInstanceIndex = _.findIndex(this.instances, instance => instance.uniqueId == this.selectedInstanceData.uniqueId);
1218         this.componentServiceNg2.getComponentResourceInstances(this.component).subscribe(result => {
1219             let instanceCapabilitiesData: CapabilitiesGroup = _.reduce(result.componentInstances, (res, instance) => {
1220                 if (instance.uniqueId === this.selectedInstanceData.uniqueId) {
1221                     return instance.capabilities;
1222                 }
1223                 return res;
1224             }, new CapabilitiesGroup());
1225             (<ComponentInstance>this.instances[currentInstanceIndex]).capabilities = instanceCapabilitiesData;
1226         });
1227     };
1228
1229     reverseChangedData = ():void => {
1230         // make reverse item handler
1231         let handleReverseItem;
1232         if (this.isPropertiesTabSelected) {
1233             handleReverseItem = (changedItem) => {
1234                 changedItem = <PropertyFEModel>changedItem;
1235                 this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
1236             };
1237         } else if (this.isInputsTabSelected) {
1238             handleReverseItem = (changedItem) => {
1239                 changedItem = <InputFEModel>changedItem;
1240                 this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
1241                 changedItem.resetMetadata();
1242                 changedItem.required = changedItem.requiredOrig;
1243             };
1244         }
1245
1246         this.changedData.forEach(handleReverseItem);
1247         this.changedData = [];
1248         this.updateHasChangedData();
1249     };
1250
1251     updateHasChangedData = ():boolean => {
1252         const curHasChangedData:boolean = (this.changedData.length > 0);
1253         if (curHasChangedData !== this.hasChangedData) {
1254             this.hasChangedData = curHasChangedData;
1255             if(this.hasChangedData) {
1256                 this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, this.hasChangedData, this.showUnsavedChangesAlert);
1257             } else {
1258                 this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, false);
1259             }
1260         } 
1261         return this.hasChangedData;
1262     };
1263
1264     doSaveChangedData = (onSuccessFunction?:Function, onError?:Function):void => {
1265         this.saveChangedData().then(
1266             () => {
1267                 this.notification.success({
1268                     message: 'Successfully saved changes',
1269                     title: 'Saved'
1270                 });
1271                 if(onSuccessFunction) onSuccessFunction();
1272             },
1273             () => {
1274                 this.notification.error({
1275                     message: 'Failed to save changes!',
1276                     title: 'Failure'
1277                 });
1278                 if(onError) onError();
1279             }
1280         );
1281     };
1282
1283     showUnsavedChangesAlert = ():Promise<any> => {
1284         let modalTitle:string;
1285         if (this.isPropertiesTabSelected) {
1286             modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
1287         } else if (this.isInputsTabSelected) {
1288             modalTitle = `Unsaved inputs for ${this.component.name}`;
1289         }
1290
1291         return new Promise<any>((resolve, reject) => {
1292             const modal = this.ModalServiceSdcUI.openCustomModal(
1293                 {
1294                     title: modalTitle,
1295                     size: 'sm',
1296                     type: SdcUiCommon.ModalType.custom,
1297                     testId: "navigate-modal",
1298
1299                     buttons: [
1300                         {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()},
1301                         {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}},
1302                         {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)}
1303                     ] as SdcUiCommon.IModalButtonComponent[]
1304                 } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData});
1305         });
1306
1307     }
1308
1309     updatePropertyValueAfterDeclare = (input: InputFEModel) => {
1310         if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
1311             const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1);
1312             const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => {
1313                 return feProperty.name == input.relatedPropertyName &&
1314                     (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_')));
1315             });
1316             const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined;
1317             propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value
1318             this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath);
1319             this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath);
1320         }
1321     }
1322
1323     //used for declare button, to keep count of newly checked properties (and ignore declared properties)
1324     updateCheckedPropertyCount = (increment: boolean): void => {
1325         this.checkedPropertiesCount += (increment) ? 1 : -1;
1326         this.checkedToscaCount = 0;
1327         this.enableToscaFunction = false;
1328         console.debug("CheckedProperties count is now.... " + this.checkedPropertiesCount);
1329     };
1330
1331     updateCheckedChildPropertyCount = (increment: boolean): void => {
1332         this.checkedChildPropertiesCount += (increment) ? 1 : -1;
1333     };
1334
1335     togggleToscaBtn = (toscaFlag: boolean) : void => {
1336         this.checkedToscaCount += toscaFlag ? 1 : -1;
1337         if(this.checkedToscaCount == 1){
1338             this.enableToscaFunction = true;
1339         }else{
1340             this.enableToscaFunction = false;
1341         }
1342     };
1343
1344     setInputTabIndication = (numInputs: number): void => {
1345         this.propertyInputTabs.setTabIndication('Inputs', numInputs);
1346     };
1347
1348     setPolicyTabIndication = (numPolicies: number): void => {
1349         this.propertyInputTabs.setTabIndication('Policies', numPolicies);
1350     }
1351
1352     resetUnsavedChangesForInput = (input:InputFEModel) => {
1353         this.inputsUtils.resetInputDefaultValue(input, input.defaultValue);
1354         this.changedData = this.changedData.filter((changedItem) => changedItem.uniqueId !== input.uniqueId);
1355         this.updateHasChangedData();
1356     }
1357
1358     deleteInput = (input: InputFEModel) => {
1359         //reset any unsaved changes to the input before deleting it
1360         this.resetUnsavedChangesForInput(input);
1361
1362         console.debug("==>" + this.constructor.name + ": deleteInput");
1363         let inputToDelete = new InputBEModel(input);
1364
1365         this.componentServiceNg2
1366             .deleteInput(this.component, inputToDelete)
1367             .subscribe(response => {
1368                 this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId);
1369
1370                 //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead!
1371                 this.changeSelectedInstance(this.selectedInstanceData);
1372                 // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)];
1373
1374                 // if (instanceFeProperties) {
1375                 //     let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => {
1376                 //         return prop.name == input.propertyName;
1377                 //     });
1378
1379                 //     if (propToEnable) {
1380                 //         if (propToEnable.name == response.inputPath) response.inputPath = null;
1381                 //         propToEnable.setNonDeclared(response.inputPath);
1382                 //         //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath);
1383                 //         this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath);
1384                 //     }
1385                 // }
1386             }, error => {}); //ignore error
1387     };
1388
1389     deletePolicy = (policy: PolicyInstance) => {
1390         this.loadingPolicies = true;
1391         this.topologyTemplateService
1392             .deletePolicy(this.component, policy)
1393             .subscribe((response) => {
1394                 this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId);
1395                 this.changeSelectedInstance(this.selectedInstanceData);
1396                 this.loadingPolicies = false;
1397             });
1398     };
1399
1400     deleteProperty = (property: PropertyFEModel) => {
1401         const propertyToDelete = new PropertyFEModel(property);
1402         this.loadingProperties = true;
1403         const feMap = this.instanceFePropertiesMap;
1404         this.topologyTemplateService
1405             .deleteServiceProperty(this.component.uniqueId, propertyToDelete)
1406             .subscribe((response) => {
1407                 const props = feMap[this.component.uniqueId];
1408                 props.splice(props.findIndex(p => p.uniqueId === response),1);
1409                 this.loadingProperties = false;
1410             }, (error) => {
1411                 this.loadingProperties = false;
1412                 console.error(error);
1413             });
1414     }
1415
1416     /*** addProperty ***/
1417     addProperty = (model: string) => {
1418         this.loadDataTypesByComponentModel(model)
1419         let modalTitle = 'Add Property';
1420         let modal = this.modalService.createCustomModal(new ModalModel(
1421             'sm',
1422             modalTitle,
1423             null,
1424             [
1425                 new ButtonModel('Save', 'blue', () => {
1426                     modal.instance.dynamicContent.instance.isLoading = true;
1427                     const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel;
1428                     this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty)
1429                         .subscribe((response) => {
1430                             modal.instance.dynamicContent.instance.isLoading = false;
1431                             const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response);
1432                             this.instanceFePropertiesMap[this.component.uniqueId].push(newProp);
1433                             modal.instance.close();
1434                         }, (error) => {
1435                             modal.instance.dynamicContent.instance.isLoading = false;
1436                             this.notification.error({
1437                                 message: 'Failed to add property:' + error,
1438                                 title: 'Failure'
1439                             });
1440                         });
1441                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1442                 new ButtonModel('Cancel', 'outline grey', () => {
1443                     modal.instance.close();
1444                 }),
1445             ],
1446             null
1447         ));
1448         modal.instance.open();
1449         this.modalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1450     }
1451
1452     /*** addInput ***/
1453     addInput = () => {
1454         let modalTitle = 'Add Input';
1455         let modal = this.modalService.createCustomModal(new ModalModel(
1456             'sm',
1457             modalTitle,
1458             null,
1459             [
1460                 new ButtonModel('Save', 'blue', () => {
1461                     modal.instance.dynamicContent.instance.isLoading = true;
1462                     const newInput: InputBEModel = modal.instance.dynamicContent.instance.propertyModel;
1463                     this.topologyTemplateService.createServiceInput(this.component.uniqueId, newInput)
1464                         .subscribe((response) => {
1465                             modal.instance.dynamicContent.instance.isLoading = false;
1466                             const newInputProp: InputFEModel = this.inputsUtils.convertInputBEToInputFE(response);
1467                             this.inputs.push(newInputProp);
1468                             modal.instance.close();
1469                         }, (error) => {
1470                             modal.instance.dynamicContent.instance.isLoading = false;
1471                             this.notification.error({
1472                                 message: 'Failed to add input:' + error,
1473                                 title: 'Failure'
1474                             });
1475                         });
1476                 }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()),
1477                 new ButtonModel('Cancel', 'outline grey', () => {
1478                     modal.instance.close();
1479                 }),
1480             ],
1481             null
1482         ));
1483         this.modalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {});
1484         modal.instance.open();
1485     }
1486
1487     /*** SEARCH RELATED FUNCTIONS ***/
1488     searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => {
1489         let instanceBePropertiesMap:InstanceBePropertiesMap;
1490         this.componentServiceNg2
1491             .filterComponentInstanceProperties(this.component, filterData)
1492             .subscribe((response) => {
1493                 this.processInstancePropertiesResponse(response, false);
1494                 this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
1495                 this.searchPropertyName = filterData.propertyName;//mark in table
1496                 this.hierarchyNavTabs.triggerTabChange('Composition');
1497                 this.propertiesNavigationData = [];
1498                 this.displayClearSearch = true;
1499             }, (error) => {}); //ignore error
1500
1501     }
1502
1503     clearSearch = () => {
1504         this.instancesNavigationData = this.instances;
1505         this.searchPropertyName = "";
1506         this.hierarchyPropertiesDisplayOptions.searchText = "";
1507         this.displayClearSearch = false;
1508         this.advanceSearch.clearAll();
1509         this.searchQuery = '';
1510     };
1511
1512     clickOnClearSearch = () => {
1513         this.clearSearch();
1514         this.selectFirstInstanceByDefault();
1515         this.hierarchyNavTabs.triggerTabChange('Composition');
1516     };
1517
1518     private isInput = (instanceType:string):boolean =>{
1519         return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
1520     }
1521
1522     loadDataTypesByComponentModel(model:string) {
1523         this.propertyCreatorComponent.filterDataTypesByModel(model);
1524     }
1525
1526 }