Provide tosca function capability to complex type fields in composition view
[sdc.git] / catalog-ui / src / app / directives / property-types / data-type-fields-structure / data-type-fields-structure.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 /**
22  * Created by obarda on 1/27/2016.
23  */
24 'use strict';
25 import { DataTypesMap, DerivedFEProperty, PropertyDeclareAPIModel, PropertyModel } from 'app/models';
26 import { DataTypePropertyModel } from 'app/models/data-type-properties';
27 import { DataTypesService } from 'app/services';
28 import { ValidationUtils } from 'app/utils';
29 import { PropertiesUtils } from "../../../ng2/pages/properties-assignment/services/properties.utils";
30 import { InstanceFeDetails } from "app/models/instance-fe-details";
31 import { SubPropertyToscaFunction } from 'app/models/sub-property-tosca-function';
32 import { ToscaGetFunction } from 'app/models/tosca-get-function';
33 import * as _ from 'lodash';
34
35 export interface IDataTypeFieldsStructureScope extends ng.IScope {
36     parentFormObj: ng.IFormController;
37     dataTypeProperties: DataTypePropertyModel[];
38     parentProperty:PropertyModel;
39     componentInstanceMap: Map<string, InstanceFeDetails>;
40     typeName: string;
41     valueObjRef: any;
42     propertyNameValidationPattern: RegExp;
43     fieldsPrefixName: string;
44     readOnly: boolean;
45     currentTypeDefaultValue: any;
46     types: DataTypesMap;
47     expandByDefault: boolean;
48     expand: boolean;
49     expanded: boolean;
50     dataTypesService: DataTypesService;
51     constraints: string[];
52     isService:boolean;
53     showToscaFunction: Map<string, boolean>;
54     subpropertyMap: Map<string, PropertyDeclareAPIModel>;
55     complexToscapath: string;
56
57     expandAndCollapse(): void;
58     getValidationPattern(type: string): RegExp;
59     validateIntRange(value: string): boolean;
60     onValueChange(propertyName: string, type: string): void;
61     inputOnValueChange(property: any, value: any): void;
62     onEnableTosca(toscaFlag:boolean,propertyName:string);
63     verifyTosca(propertyName: string) : boolean;
64     getSubProperty(propertyName: string) : PropertyDeclareAPIModel;
65     getToscaPathValue(propertyName: string) : Array<string>;
66     onGetToscaFunction(toscaGetFunction: ToscaGetFunction, propertyName:string);
67 }
68
69 export class DataTypeFieldsStructureDirective implements ng.IDirective {
70
71     constraints: string[];
72
73     scope = {
74         valueObjRef: '=',
75         typeName: '=',
76         componentInstanceMap: '=',
77         parentProperty: '=',
78         parentFormObj: '=',
79         fieldsPrefixName: '=',
80         readOnly: '=',
81         defaultValue: '@',
82         types: '=',
83         expandByDefault: '=',
84         isService: '=',
85         complexToscapath: '='
86     };
87
88     restrict = 'E';
89     replace = true;
90
91     constructor(private DataTypesService: DataTypesService,
92                 private PropertyNameValidationPattern: RegExp,
93                 private ValidationUtils: ValidationUtils, 
94                 private PropertiesUtils: PropertiesUtils) {
95     }
96
97     public static factory = (DataTypesService: DataTypesService,
98                              PropertyNameValidationPattern: RegExp,
99                              ValidationUtils: ValidationUtils, 
100                              PropertiesUtils: PropertiesUtils) => {
101         return new DataTypeFieldsStructureDirective(DataTypesService, PropertyNameValidationPattern, ValidationUtils, PropertiesUtils);
102     }
103     template = (): string => {
104         return require('./data-type-fields-structure.html');
105     }
106
107     link = (scope: IDataTypeFieldsStructureScope, element: any, $attr: any) => {
108         scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
109
110         scope.$watchCollection('[typeName,fieldsPrefixName]', (newData: any): void => {
111             this.rerender(scope);
112         });
113         let childProp = this.PropertiesUtils.convertAddPropertyBAToPropertyFE(scope.parentProperty);
114         scope.subpropertyMap = new Map<string,PropertyDeclareAPIModel>();
115         scope.showToscaFunction = new Map<string,boolean>();
116         childProp.flattenedChildren.forEach(prop => {
117             scope.showToscaFunction.set(prop.name,false);
118             if (scope.parentProperty.subPropertyToscaFunctions != null) {
119                 scope.parentProperty.subPropertyToscaFunctions.forEach(SubPropertyToscaFunction => {
120                     if (SubPropertyToscaFunction.subPropertyPath.toString() == prop.name) {
121                         scope.showToscaFunction.set(prop.name,true);
122                     }
123                 });
124             }
125             scope.subpropertyMap.set(prop.name,new PropertyDeclareAPIModel(childProp, prop));
126         });
127         scope.expandAndCollapse = (): void => {
128             if (!scope.expanded) {
129                 this.initDataOnScope(scope, $attr);
130                 scope.expanded = true;
131             }
132             scope.expand = !scope.expand;
133         };
134
135         scope.getValidationPattern = (type: string): RegExp => {
136             return this.ValidationUtils.getValidationPattern(type);
137         };
138
139         scope.validateIntRange = (value: string): boolean => {
140             return !value || this.ValidationUtils.validateIntRange(value);
141         };
142
143         scope.onValueChange = (propertyName: string, type: string, ): void => {
144             scope.valueObjRef[propertyName] = !angular.isUndefined(scope.valueObjRef[propertyName]) ? scope.valueObjRef[propertyName] : scope.currentTypeDefaultValue[propertyName];
145             if (scope.valueObjRef[propertyName] && type != 'string') {
146                 scope.valueObjRef[propertyName] = JSON.parse(scope.valueObjRef[propertyName]);
147             }
148         };
149
150         scope.inputOnValueChange = (property: any) => {
151             if (property.constraints) {
152                 // this.constraints = property.constraints[0].validValues;
153             }
154
155             const value = !scope.parentFormObj[scope.fieldsPrefixName + property.name].$error.pattern
156                 && ('integer' == property.type && scope.parentFormObj[scope.fieldsPrefixName + property.name].$setValidity('pattern', scope.validateIntRange(scope.valueObjRef[property.name]))
157                 || scope.onValueChange(property.name, (property.simpleType || property.type)));
158             return value;
159         };
160
161         scope.onEnableTosca = (toscaFlag:boolean,key:string):void => {
162             scope.showToscaFunction.set(key,toscaFlag);
163             scope.valueObjRef[key] = "";
164             if (!toscaFlag) {
165                 if (scope.parentProperty.subPropertyToscaFunctions != null) {
166                     let subToscaFunctionList : Array<SubPropertyToscaFunction> = [];
167                     scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
168                         if (SubPropertyToscaFunction.subPropertyPath.toString() != key) {
169                             subToscaFunctionList.push(SubPropertyToscaFunction);
170                         }
171                     });
172                     scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
173                 }
174             }
175         };
176
177         scope.verifyTosca = (propName:string) : boolean => {
178             return scope.showToscaFunction.get(propName);
179         }
180
181         scope.getSubProperty = (propertyName: string) : PropertyDeclareAPIModel => {
182             return scope.subpropertyMap.get(propertyName);
183         }
184
185         scope.getToscaPathValue = (propertyName: string) : Array<string> => {
186             const parentObj : PropertyDeclareAPIModel = scope.subpropertyMap.get(propertyName);
187             if (parentObj.input instanceof DerivedFEProperty) {
188                 return parentObj.input.toscaPath;
189             }
190             return [propertyName];
191         }
192
193         scope.onGetToscaFunction = (toscaGetFunction: ToscaGetFunction, key:string): void => {
194             let toscaPath = key;
195             scope.valueObjRef[key] = "";
196             if (scope.parentProperty.subPropertyToscaFunctions != null) {
197                 let toscaFlag : boolean = true
198                 scope.parentProperty.subPropertyToscaFunctions.forEach(SubPropertyToscaFunction => {
199                     if (SubPropertyToscaFunction.subPropertyPath.toString() == toscaPath) {
200                         SubPropertyToscaFunction.toscaFunction = toscaGetFunction;
201                         toscaFlag = false;
202                         return;
203                     }
204                 });
205                 if (toscaFlag) {
206                     let subPropertyToscaFunction = new SubPropertyToscaFunction();
207                     subPropertyToscaFunction.toscaFunction = toscaGetFunction;
208                     subPropertyToscaFunction.subPropertyPath = [toscaPath];
209                     scope.parentProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
210                 }
211             } else {
212                 let subPropertyToscaFunction = new SubPropertyToscaFunction();
213                 subPropertyToscaFunction.toscaFunction = toscaGetFunction;
214                 subPropertyToscaFunction.subPropertyPath = [toscaPath];
215                 scope.parentProperty.subPropertyToscaFunctions = [subPropertyToscaFunction];
216             }
217         }
218
219     }
220     // public types=Utils.Constants.PROPERTY_DATA.TYPES;
221
222     // get data type properties array and return object with the properties and their default value
223     // (for example: get: [{name:"prop1",defaultValue:1 ...},{name:"prop2", defaultValue:"bla bla" ...}]
224     //              return: {prop1: 1, prop2: "bla bla"}
225     private getDefaultValue = (dataTypeProperties: DataTypePropertyModel[]): any => {
226         const defaultValue = {};
227         for (const element of dataTypeProperties) {
228             if (element.type != 'string') {
229                 if (!angular.isUndefined(element.defaultValue)) {
230                     defaultValue[element.name] = JSON.parse(element.defaultValue);
231                 }
232             } else {
233                 defaultValue[element.name] = element.defaultValue;
234             }
235         }
236         return defaultValue;
237     }
238
239     private initDataOnScope = (scope: any, $attr: any): void => {
240         scope.dataTypesService = this.DataTypesService;
241         scope.dataTypeProperties = this.getDataTypeProperties(scope.typeName, scope.types);
242         if ($attr.defaultValue) {
243             scope.currentTypeDefaultValue = JSON.parse($attr.defaultValue);
244         } else {
245             scope.currentTypeDefaultValue = this.getDefaultValue(scope.dataTypeProperties);
246         }
247
248         if (!scope.valueObjRef) {
249             scope.valueObjRef = {};
250         }
251
252         _.forEach(scope.currentTypeDefaultValue, (value, key) => {
253             if (angular.isUndefined(scope.valueObjRef[key])) {
254                 if (typeof scope.currentTypeDefaultValue[key] == 'object') {
255                     angular.copy(scope.currentTypeDefaultValue[key], scope.valueObjRef[key]);
256                 } else {
257                     scope.valueObjRef[key] = scope.currentTypeDefaultValue[key];
258                 }
259             }
260         });
261     }
262
263     private getDataTypeProperties = (dataTypeName: string, typesInModel: DataTypesMap): DataTypePropertyModel[] => {
264         let properties = typesInModel[dataTypeName].properties || [];
265         if (typesInModel[dataTypeName].derivedFromName != 'tosca.datatypes.Root') {
266             properties = this.getDataTypeProperties(typesInModel[dataTypeName].derivedFromName, typesInModel).concat(properties);
267         }
268         return properties;
269     }
270
271     private rerender = (scope: any): void => {
272         scope.expanded = false;
273         scope.expand = false;
274         if (scope.expandByDefault) {
275             scope.expandAndCollapse();
276         }
277     }
278 }
279
280 DataTypeFieldsStructureDirective.factory.$inject = ['Sdc.Services.DataTypesService', 'PropertyNameValidationPattern', 'ValidationUtils','PropertiesUtils'];