Fix 'Unable to add metadata on inputs'-bug
[sdc.git] / catalog-ui / src / app / view-models / forms / property-forms / component-property-form / property-form-view-model.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 'use strict';
22 import * as _ from "lodash";
23 import {FormState, PROPERTY_DATA, PROPERTY_TYPES, PROPERTY_VALUE_CONSTRAINTS, ValidationUtils} from "app/utils";
24 import {DataTypesService} from "app/services";
25 import {DataTypesMap, PropertyModel, InputBEModel, Component, InputFEModel} from "app/models";
26 import {ComponentInstance} from "../../../../models/componentsInstances/componentInstance";
27 import {ComponentInstanceServiceNg2} from "app/ng2/services/component-instance-services/component-instance.service";
28 import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service";
29 import {SdcUiCommon, SdcUiComponents, SdcUiServices} from "onap-ui-angular";
30 import {CompositionService} from "app/ng2/pages/composition/composition.service";
31 import {WorkspaceService} from "app/ng2/pages/workspace/workspace.service";
32 import {Observable} from "rxjs";
33 import {TopologyTemplateService} from "app/ng2/services/component-services/topology-template.service";
34 import {InstanceFeDetails} from "../../../../models/instance-fe-details";
35 import {ToscaGetFunction} from "../../../../models/tosca-get-function";
36 import {ToscaFunctionValidationEvent} from "../../../../ng2/pages/properties-assignment/tosca-function/tosca-function.component";
37 import {CustomToscaFunction} from "../../../../models/default-custom-functions";
38 import {ToscaFunctionType} from "../../../../models/tosca-function-type.enum";
39
40 export interface IEditPropertyModel {
41     property: PropertyModel;
42     types: Array<string>;
43     simpleTypes: Array<string>;
44     hasGetFunctionValue: boolean;
45     isGetFunctionValid: boolean;
46 }
47
48 interface IPropertyFormViewModelScope extends ng.IScope {
49     forms: any;
50     editForm: ng.IFormController;
51     footerButtons: Array<any>;
52     isNew: boolean;
53     nameMaxLength: number;
54     isLoading: boolean;
55     componentMetadata: { isService: boolean, isVfc: boolean };
56     validationPattern: RegExp;
57     propertyNameValidationPattern: RegExp;
58     commentValidationPattern: RegExp;
59     editPropertyModel: IEditPropertyModel;
60     componentInstanceMap: Map<string, InstanceFeDetails>;
61     customToscaFunctions: Array<CustomToscaFunction>;
62     modalInstanceProperty: ng.ui.bootstrap.IModalServiceInstance;
63     currentPropertyIndex: number;
64     isLastProperty: boolean;
65     myValue: any;
66     nonPrimitiveTypes: Array<string>;
67     dataTypes: DataTypesMap;
68     isTypeDataType: boolean;
69     maxLength: number;
70     isViewOnly: boolean;
71     isPropertyValueOwner: boolean;
72     isVnfConfiguration: boolean;
73     constraints: string[];
74     modelNameFilter: string;
75     isGetFunctionValueType: boolean;
76     invalidMandatoryFields: boolean;
77
78     validateJson(json: string): boolean;
79
80     save(doNotCloseModal?: boolean): void;
81
82     getValidationPattern(type: string): RegExp;
83
84     validateIntRange(value: string): boolean;
85
86     close(): void;
87
88     onSchemaTypeChange(): void;
89
90     onTypeChange(resetSchema: boolean): void;
91
92     showSchema(): boolean;
93
94     delete(property: PropertyModel): void;
95
96     getPrev(): void;
97
98     getNext(): void;
99
100     isSimpleType(typeName: string): boolean;
101
102     getDefaultValue(): any;
103
104     onValueTypeChange(): void;
105 }
106
107 export class PropertyFormViewModel {
108
109     static '$inject' = [
110         '$scope',
111         'Sdc.Services.DataTypesService',
112         '$uibModalInstance',
113         'property',
114         'ValidationPattern',
115         'PropertyNameValidationPattern',
116         'CommentValidationPattern',
117         'ValidationUtils',
118         'component',
119         '$filter',
120         'ModalServiceSdcUI',
121         'filteredProperties',
122         '$timeout',
123         'isViewOnly',
124         'isPropertyValueOwner',
125         'propertyOwnerType',
126         'propertyOwnerId',
127         'ComponentInstanceServiceNg2',
128         'ComponentServiceNg2',
129         'TopologyTemplateService',
130         'CompositionService',
131         'workspaceService',
132         'inputProperty'
133     ];
134
135     private formState: FormState;
136
137     constructor(private $scope: IPropertyFormViewModelScope,
138                 private DataTypesService: DataTypesService,
139                 private $uibModalInstance: ng.ui.bootstrap.IModalServiceInstance,
140                 private property: PropertyModel,
141                 private ValidationPattern: RegExp,
142                 private PropertyNameValidationPattern: RegExp,
143                 private CommentValidationPattern: RegExp,
144                 private ValidationUtils: ValidationUtils,
145                 private component: Component,
146                 private $filter: ng.IFilterService,
147                 private modalService: SdcUiServices.ModalService,
148                 private filteredProperties: Array<PropertyModel>,
149                 private $timeout: ng.ITimeoutService,
150                 private isViewOnly: boolean,
151                 private isPropertyValueOwner: boolean,
152                 private propertyOwnerType: string,
153                 private propertyOwnerId: string,
154                 private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2,
155                 private ComponentServiceNg2: ComponentServiceNg2,
156                 private topologyTemplateService: TopologyTemplateService,
157                 private compositionService: CompositionService,
158                 private workspaceService: WorkspaceService,
159                 private inputProperty: InputFEModel) {
160
161         this.formState = angular.isDefined(property.name) ? FormState.UPDATE : FormState.CREATE;
162         this.initScope();
163     }
164
165     private initResource = (): void => {
166         this.$scope.editPropertyModel.property = new PropertyModel(this.property);
167         this.$scope.editPropertyModel.property.type = this.property.type ? this.property.type : null;
168         this.$scope.editPropertyModel.property.value = this.property.value ? this.property.value : this.property.defaultValue;
169         this.$scope.constraints = this.property.constraints && this.property.constraints[0] ? this.property.constraints[0]["validValues"] : null;
170         this.initToscaGetFunction();
171         this.setMaxLength();
172     };
173
174     private initToscaGetFunction() {
175         this.$scope.editPropertyModel.hasGetFunctionValue = this.$scope.editPropertyModel.property.isToscaFunction();
176         this.$scope.editPropertyModel.isGetFunctionValid = true;
177     }
178
179     private isDataTypeForPropertyType = (property: PropertyModel): boolean => {
180         property.simpleType = "";
181         if (property.type && PROPERTY_DATA.TYPES.indexOf(property.type) > -1) {
182             return false;
183         }
184         let simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type);
185         if (simpleType) {
186             property.simpleType = simpleType;
187             return false;
188         }
189         return true;
190     };
191
192     private getTypeForDataTypeDerivedFromSimple = (dataTypeName: string): string => {
193         if (!this.$scope.dataTypes[dataTypeName]) {
194             return 'string';
195         }
196         if (this.$scope.dataTypes[dataTypeName].derivedFromName == "tosca.datatypes.Root" || this.$scope.dataTypes[dataTypeName].properties) {
197             return null;
198         }
199         if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(this.$scope.dataTypes[dataTypeName].derivedFromName) > -1) {
200             return this.$scope.dataTypes[dataTypeName].derivedFromName
201         }
202         return this.getTypeForDataTypeDerivedFromSimple(this.$scope.dataTypes[dataTypeName].derivedFromName);
203     };
204
205     private initForNotSimpleType = (): void => {
206         const property = this.$scope.editPropertyModel.property;
207         this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property);
208         if (property.isToscaFunction()) {
209             this.initValueForGetFunction();
210             return;
211         }
212
213         if (this.isComplexType(property.type)) {
214             if (property.value || property.defaultValue) {
215                 this.$scope.myValue = JSON.parse(property.value || property.defaultValue);
216             } else {
217                 this.initEmptyComplexValue(property.type);
218             }
219         }
220     };
221
222     private initValueForGetFunction(): void {
223         const property = this.$scope.editPropertyModel.property;
224         if (property.defaultValue) {
225             this.$scope.myValue = JSON.parse(property.defaultValue);
226             return;
227         }
228         if (this.isComplexType(property.type)) {
229             this.initEmptyComplexValue(property.type);
230             return;
231         }
232
233         this.$scope.myValue = undefined;
234     }
235
236     private initComponentInstanceMap() {
237         this.$scope.componentInstanceMap = new Map<string, InstanceFeDetails>();
238         if (this.compositionService.componentInstances) {
239             this.compositionService.componentInstances.forEach(value => {
240                 this.$scope.componentInstanceMap.set(value.uniqueId, <InstanceFeDetails>{
241                     name: value.name
242                 });
243             });
244         }
245     }
246
247     private initCustomToscaFunctions() {
248         this.$scope.customToscaFunctions = [];
249         this.topologyTemplateService.getDefaultCustomFunction().toPromise().then((data) => {
250             for (let customFunction of data) {
251                 this.$scope.customToscaFunctions.push(new CustomToscaFunction(customFunction));
252             }
253         });
254     }
255
256     private initEmptyComplexValue(type: string): any {
257         switch (type) {
258             case PROPERTY_TYPES.MAP:
259                 this.$scope.myValue = { '': null };
260                 break;
261             case PROPERTY_TYPES.LIST:
262                 this.$scope.myValue = [''];
263                 break;
264             default:
265                 this.$scope.myValue = {};
266         }
267     }
268
269     private isComplexType(type: string): boolean {
270         if (!type) {
271             return false;
272         }
273         return PROPERTY_DATA.SIMPLE_TYPES.indexOf(type) == -1;
274     }
275
276     private setMaxLength = (): void => {
277         switch (this.$scope.editPropertyModel.property.type) {
278             case PROPERTY_TYPES.MAP:
279             case PROPERTY_TYPES.LIST:
280                 this.$scope.maxLength = this.$scope.editPropertyModel.property.schema.property.type == PROPERTY_TYPES.JSON ?
281                     PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH :
282                     PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
283                 break;
284             case PROPERTY_TYPES.JSON:
285                 this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH;
286                 break;
287             default:
288                 this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
289         }
290     };
291
292
293     private initScope = (): void => {
294
295         //scope properties
296         this.$scope.isViewOnly = this.isViewOnly;
297         this.$scope.isLoading = true;
298         this.$scope.forms = {};
299         this.$scope.validationPattern = this.ValidationPattern;
300         this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
301         this.$scope.commentValidationPattern = this.CommentValidationPattern;
302         this.$scope.nameMaxLength = PROPERTY_VALUE_CONSTRAINTS.NAME_MAX_LENGTH;
303         this.$scope.isNew = (this.formState === FormState.CREATE);
304         this.$scope.componentMetadata = {
305             isService: this.workspaceService.metadata.isService(),
306             isVfc: this.workspaceService.metadata.isVfc()
307         }
308         this.$scope.modalInstanceProperty = this.$uibModalInstance;
309         this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, i => i.name == this.property.name);
310         this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
311         const property = new PropertyModel(this.property);
312         this.$scope.editPropertyModel = {
313             'property': property,
314             types: PROPERTY_DATA.TYPES,
315             simpleTypes: PROPERTY_DATA.SIMPLE_TYPES,
316             hasGetFunctionValue: property.isToscaFunction(),
317             isGetFunctionValid: true,
318         };
319         this.$scope.editPropertyModel.types.sort((a, b) => a.localeCompare(b));
320         this.$scope.isPropertyValueOwner = this.isPropertyValueOwner;
321         this.$scope.propertyOwnerType = this.propertyOwnerType;
322         this.$scope.modelNameFilter = this.workspaceService.metadata.model;
323         //check if property of VnfConfiguration
324         this.$scope.isVnfConfiguration = false;
325         if(this.propertyOwnerType == "component" && angular.isArray(this.compositionService.componentInstances)) {
326             const componentPropertyOwner: ComponentInstance = this.compositionService.componentInstances.find((ci: ComponentInstance) => {
327                 return ci.uniqueId === this.property.resourceInstanceUniqueId;
328             });
329             if (componentPropertyOwner && componentPropertyOwner.componentName === 'vnfConfiguration') {
330                 this.$scope.isVnfConfiguration = true;
331             }
332         }
333         this.initResource();
334         this.initForNotSimpleType();
335         this.initComponentInstanceMap();
336         this.initCustomToscaFunctions();
337
338         this.$scope.validateJson = (json: string): boolean => {
339             if (!json) {
340                 return true;
341             }
342             return this.ValidationUtils.validateJson(json);
343         };
344
345         this.DataTypesService.fetchDataTypesByModel(this.workspaceService.metadata.model).then(response => {
346             this.$scope.dataTypes = response.data as DataTypesMap;
347             this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type: string) => {
348                 return this.$scope.editPropertyModel.types.indexOf(type) == -1;
349             }).sort((a, b) => a.localeCompare(b));
350             this.$scope.isLoading = false;
351         });
352
353         //scope methods
354         this.$scope.save = (doNotCloseModal?: boolean): void => {
355             let property: PropertyModel = this.$scope.editPropertyModel.property;
356             this.$scope.isLoading = true;
357             if (property.propertyView) {
358                 let input: InputBEModel = this.inputProperty;
359                 input.constraints = property.constraints;
360                 input.metadata = property.metadata;
361                 this.ComponentServiceNg2.updateComponentInputs(this.component, [input]).subscribe(
362                     (response) => {
363                         console.debug("Input property updated");
364                         this.$uibModalInstance.close();
365                     },
366                     (error) => {
367                         console.debug("Failed to update input property");
368                         this.$uibModalInstance.close();
369                     }
370                 );
371                 return;
372             }
373             this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description);
374             //if read only - or no changes made - just closes the modal
375             //need to check for property.value changes manually to detect if map properties deleted
376             if ((this.$scope.editPropertyModel.property.readonly && !this.$scope.isPropertyValueOwner)
377                 || (!this.$scope.forms.editForm.$dirty && angular.equals(JSON.stringify(this.$scope.myValue), this.$scope.editPropertyModel.property.value))) {
378                 this.$uibModalInstance.close();
379                 return;
380             }
381
382             this.$scope.isLoading = true;
383
384             let onPropertyFailure = (response): void => {
385                 console.error('Failed to update property', response);
386                 this.$scope.isLoading = false;
387             };
388
389             let onPropertySuccess = (propertyFromBE: PropertyModel): void => {
390                 this.$scope.isLoading = false;
391                 this.filteredProperties[this.$scope.currentPropertyIndex] = propertyFromBE;
392                 if (!doNotCloseModal) {
393                     this.$uibModalInstance.close(propertyFromBE);
394                 } else {
395                     this.$scope.forms.editForm.$setPristine();
396                     this.$scope.editPropertyModel.property = new PropertyModel();
397                 }
398             };
399
400             //Not clean, but doing this as a temporary fix until we update the property right panel modals
401             if (this.propertyOwnerType === "group"){
402                 this.ComponentInstanceServiceNg2.updateComponentGroupInstanceProperties(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, this.propertyOwnerId, [property])
403                     .subscribe((propertiesFromBE) => { onPropertySuccess(<PropertyModel>propertiesFromBE[0])}, error => onPropertyFailure(error));
404             } else if (this.propertyOwnerType === "policy"){
405                 if (!this.$scope.editPropertyModel.property.simpleType &&
406                     !this.$scope.isSimpleType(this.$scope.editPropertyModel.property.type) &&
407                     !_.isNil(this.$scope.myValue)) {
408                     property.value = JSON.stringify(this.$scope.myValue);
409                 }
410                 this.ComponentInstanceServiceNg2.updateComponentPolicyInstanceProperties(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, this.propertyOwnerId, [property])
411                     .subscribe((propertiesFromBE) => { onPropertySuccess(<PropertyModel>propertiesFromBE[0])}, error => onPropertyFailure(error));
412             } else {
413                 //in case we have uniqueId we call update method
414                 if (this.$scope.isPropertyValueOwner) {
415                     if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
416                         property.value = JSON.stringify(this.$scope.myValue);
417                     }
418                     this.updateInstanceProperties(property.resourceInstanceUniqueId, [property]).subscribe((propertiesFromBE) => onPropertySuccess(propertiesFromBE[0]),
419                         error => onPropertyFailure(error));
420                 } else {
421                     if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
422                         property.defaultValue = JSON.stringify(this.$scope.myValue);
423                         property.value = JSON.stringify(this.$scope.myValue);
424                     } else {
425                         this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.value;
426                     }
427                     this.addOrUpdateProperty(property).subscribe(onPropertySuccess, error => onPropertyFailure(error));
428                 }
429             }
430         };
431
432         this.$scope.getPrev = (): void => {
433             this.property = this.filteredProperties[--this.$scope.currentPropertyIndex];
434             this.initResource();
435             this.initForNotSimpleType();
436             this.$scope.isLastProperty = false;
437         };
438
439         this.$scope.getNext = (): void => {
440             this.property = this.filteredProperties[++this.$scope.currentPropertyIndex];
441             this.initResource();
442             this.initForNotSimpleType();
443             this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
444         };
445
446         this.$scope.isSimpleType = (typeName: string): boolean => {
447             return typeName && this.$scope.editPropertyModel.simpleTypes.indexOf(typeName) != -1;
448         };
449
450         this.$scope.showSchema = (): boolean => {
451             return [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP].indexOf(this.$scope.editPropertyModel.property.type) > -1;
452         };
453
454         this.$scope.getValidationPattern = (type: string): RegExp => {
455             return this.ValidationUtils.getValidationPattern(type);
456         };
457
458         this.$scope.validateIntRange = (value: string): boolean => {
459             return !value || this.ValidationUtils.validateIntRange(value);
460         };
461
462         this.$scope.close = (): void => {
463             this.$uibModalInstance.close();
464         };
465
466         // Add the done button at the footer.
467         this.$scope.footerButtons = [
468             { 'name': 'Save', 'css': 'blue', 'callback': this.$scope.save },
469             { 'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close }
470         ];
471
472         this.$scope.$watch("forms.editForm.$invalid", (newVal) => {
473             if (this.$scope.editPropertyModel.hasGetFunctionValue) {
474                 this.$scope.invalidMandatoryFields = !newVal || !this.$scope.editPropertyModel.property.toscaFunction || this.isViewOnly;
475                 this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields;
476             } else {
477                 this.$scope.invalidMandatoryFields = !newVal || this.isViewOnly;
478                 this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields;
479             }
480         });
481
482         this.$scope.$watch("forms.editForm.$valid", (newVal) => {
483             if (this.$scope.editPropertyModel.hasGetFunctionValue) {
484                 this.$scope.invalidMandatoryFields = !newVal || !this.$scope.editPropertyModel.property.toscaFunction || this.isViewOnly;
485                 this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields;
486             } else {
487                 this.$scope.invalidMandatoryFields = !newVal || this.isViewOnly;
488                 this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields;
489             }
490         });
491
492         this.$scope.getDefaultValue = (): any => {
493             return this.$scope.isPropertyValueOwner ? this.$scope.editPropertyModel.property.defaultValue : null;
494         };
495
496         this.$scope.onTypeChange = (): void => {
497             this.$scope.editPropertyModel.property.value = '';
498             this.$scope.editPropertyModel.property.defaultValue = '';
499             this.setMaxLength();
500             this.initForNotSimpleType();
501         };
502
503         this.$scope.onSchemaTypeChange = (): void => {
504             if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.MAP) {
505                 this.$scope.myValue = {};
506             } else if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.LIST) {
507                 this.$scope.myValue = [];
508             }
509             this.setMaxLength();
510         };
511
512         this.$scope.delete = (property: PropertyModel): void => {
513             let onOk: Function = (): void => {
514                 this.deleteProperty(property.uniqueId).subscribe(
515                     this.$scope.close
516                 );
517             };
518             let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
519             let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
520             const okButton = {testId: "OK", text: "OK", type: SdcUiCommon.ButtonType.info, callback: onOk, closeModal: true} as SdcUiComponents.ModalButtonComponent;
521             this.modalService.openInfoModal(title, message, 'delete-modal', [okButton]);
522         };
523
524         this.$scope.onValueTypeChange = (): void => {
525             this.setEmptyValue();
526             if (this.$scope.editPropertyModel.hasGetFunctionValue) {
527                 this.$scope.editPropertyModel.isGetFunctionValid = undefined;
528             } else {
529                 this.$scope.editPropertyModel.property.toscaFunction = undefined;
530                 this.$scope.editPropertyModel.isGetFunctionValid = true;
531             }
532         }
533
534         this.$scope.onConstraintChange = (constraints: any): void => {
535             console.log('$scope.onConstraintChange', constraints);
536
537             if (!this.$scope.invalidMandatoryFields) {
538                 this.$scope.footerButtons[0].disabled = !constraints.valid;
539             } else {
540                 this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields;
541             }
542             if (!constraints.constraints || constraints.constraints.length == 0) {
543                 this.$scope.editPropertyModel.property.propertyConstraints = null;
544                 this.$scope.editPropertyModel.property.constraints = null;
545                 return;
546             }
547             this.$scope.editPropertyModel.property.propertyConstraints = this.serializePropertyConstraints(constraints.constraints);
548             this.$scope.editPropertyModel.property.constraints = constraints.constraints;
549         }
550
551         this.$scope.onPropertyMetadataChange = (metadata: any): void => {
552             if (!this.$scope.invalidMandatoryFields) {
553                 this.$scope.footerButtons[0].disabled = !metadata.valid;
554             } else {
555                 this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields;
556             }
557             if (!metadata.metadata || metadata.metadata.length == 0) {
558                 this.$scope.editPropertyModel.property.metadata = null;
559                 return;
560             }
561             this.$scope.editPropertyModel.property.metadata = metadata.metadata;
562         }
563
564         this.$scope.onGetFunctionValidFunction = (toscaGetFunction: ToscaGetFunction): void => {
565             this.$scope.editPropertyModel.property.toscaFunction = toscaGetFunction;
566         }
567
568         this.$scope.onToscaFunctionValidityChange = (validationEvent: ToscaFunctionValidationEvent): void => {
569             if (validationEvent.isValid) {
570                 this.$scope.editPropertyModel.isGetFunctionValid = true;
571                 return;
572             }
573             this.$scope.editPropertyModel.isGetFunctionValid = undefined;
574         }
575     };
576
577     private serializePropertyConstraints(constraints: any[]): string[] {
578         if (constraints) {
579             let stringConstrsints = new Array();
580             constraints.forEach((constraint) => {
581                 stringConstrsints.push(JSON.stringify(constraint));
582             })
583             return stringConstrsints;
584         }
585         return null;
586     }
587
588     private setEmptyValue() {
589         const property1 = this.$scope.editPropertyModel.property;
590         property1.value = undefined;
591         if (this.isComplexType(property1.type)) {
592             this.initEmptyComplexValue(property1.type);
593             return;
594         }
595         this.$scope.myValue = '';
596     }
597
598     private updateInstanceProperties = (componentInstanceId: string, properties: PropertyModel[]): Observable<PropertyModel[]> => {
599
600         return this.ComponentInstanceServiceNg2.updateInstanceProperties(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, componentInstanceId, properties)
601         .map(newProperties => {
602             newProperties.forEach((newProperty) => {
603                 if (!_.isNil(newProperty.path)) {
604                     if (newProperty.path[0] === newProperty.resourceInstanceUniqueId) newProperty.path.shift();
605                     // find exist instance property in parent component for update the new value ( find bu uniqueId & path)
606                     let existProperty: PropertyModel = <PropertyModel>_.find(this.compositionService.componentInstancesProperties[newProperty.resourceInstanceUniqueId], {
607                         uniqueId: newProperty.uniqueId,
608                         path: newProperty.path
609                     });
610                     let index = this.compositionService.componentInstancesProperties[newProperty.resourceInstanceUniqueId].indexOf(existProperty);
611                     this.compositionService.componentInstancesProperties[newProperty.resourceInstanceUniqueId][index] = newProperty;
612                 }
613             });
614             return newProperties;
615         });
616     };
617
618     private addOrUpdateProperty = (property: PropertyModel): Observable<PropertyModel> => {
619         if (!property.uniqueId) {
620             let onSuccess = (newProperty: PropertyModel): PropertyModel => {
621                 this.filteredProperties.push(newProperty);
622                 return newProperty;
623             };
624             return this.topologyTemplateService.addProperty(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, property)
625             .map(onSuccess);
626         } else {
627             let onSuccess = (newProperty: PropertyModel): PropertyModel => {
628                 // find exist instance property in parent component for update the new value ( find bu uniqueId )
629                 let existProperty: PropertyModel = <PropertyModel>_.find(this.filteredProperties, { uniqueId: newProperty.uniqueId });
630                 let propertyIndex = this.filteredProperties.indexOf(existProperty);
631                 this.filteredProperties[propertyIndex] = newProperty;
632                 return newProperty;
633             };
634             return this.topologyTemplateService.updateProperty(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, property).map(onSuccess);
635         }
636     };
637
638     public deleteProperty = (propertyId: string): Observable<void> => {
639         let onSuccess = (): void => {
640             console.debug("Property deleted");
641             delete _.remove(this.filteredProperties, { uniqueId: propertyId })[0];
642         };
643         let onFailed = (): void => {
644             console.debug("Failed to delete property");
645         };
646         return this.topologyTemplateService.deleteProperty(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId, propertyId).map(onSuccess, onFailed);
647     };
648
649 }