Fix datatype in model not found
[sdc.git] / catalog-ui / src / app / directives / property-types / type-map / type-map-directive.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 rcohen on 9/15/2016.
23  */
24 'use strict';
25 import {ValidationUtils, PROPERTY_TYPES, PROPERTY_DATA} from "app/utils";
26 import {DataTypesService} from "app/services";
27 import {SchemaProperty, PropertyModel, DataTypesMap} from "app/models";
28 import {InstanceFeDetails} from "app/models/instance-fe-details";
29 import {ToscaGetFunction} from "app/models/tosca-get-function";
30 import {SubPropertyToscaFunction} from "app/models/sub-property-tosca-function";
31
32 export interface ITypeMapScope extends ng.IScope {
33     parentFormObj:ng.IFormController;
34     schemaProperty:SchemaProperty;
35     parentProperty:PropertyModel;
36     componentInstanceMap: Map<string, InstanceFeDetails>;
37     isMapKeysUnique:boolean;
38     isSchemaTypeDataType:boolean;
39     valueObjRef:any;
40     mapKeys:Array<string>;//array of map keys
41     mapKeysStatic:Array<string>;
42     MapKeyValidationPattern:RegExp;
43     fieldsPrefixName:string;
44     readOnly:boolean;
45     mapDefaultValue:any;
46     maxLength:number;
47     constraints:string[];
48     showAddBtn: boolean;
49     showToscaFunction: Array<boolean>;
50     types:DataTypesMap;
51
52     getValidationPattern(type:string):RegExp;
53     validateIntRange(value:string):boolean;
54     changeKeyOfMap(newKey:string, index:number, fieldName:string):void;
55     deleteMapItem(index:number):void;
56     addMapItemFields():void;
57     parseToCorrectType(objectOfValues:any, locationInObj:string, type:string):void;
58     getNumber(num:number):Array<any>;
59     validateSubToscaFunction(key:string):boolean;
60     onEnableTosca(toscaFlag:boolean,index:number);
61     onGetToscaFunction(toscaGetFunction: ToscaGetFunction, key:string);
62 }
63
64
65 export class TypeMapDirective implements ng.IDirective {
66
67     constructor(private DataTypesService:DataTypesService,
68                 private MapKeyValidationPattern:RegExp,
69                 private ValidationUtils:ValidationUtils,
70                 private $timeout:ng.ITimeoutService) {
71     }
72
73     scope = {
74         valueObjRef: '=',//ref to map object in the parent value object
75         componentInstanceMap: '=',
76         schemaProperty: '=',//get the schema.property object
77         parentFormObj: '=',//ref to parent form (get angular form object)
78         fieldsPrefixName: '=',//prefix for form fields names
79         readOnly: '=',//is form read only
80         defaultValue: '@',//this map default value
81         maxLength: '=',
82         constraints: '=',
83         showAddBtn: '=?',
84         parentProperty: '=',
85         types: '='
86     };
87
88     restrict = 'E';
89     replace = true;
90     template = (): string => {
91         return require('./type-map-directive.html');
92     };
93     
94     private isDataTypeForSchemaType = (property:SchemaProperty, types:DataTypesMap):boolean=> {
95         property.simpleType = "";
96         if (property.type && PROPERTY_DATA.TYPES.indexOf(property.type) > -1) {
97             return false;
98         }
99         let simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type, types);
100         if (simpleType) {
101             property.simpleType = simpleType;
102             return false;
103         }
104         return true;
105     };
106     
107     private getTypeForDataTypeDerivedFromSimple = (dataTypeName:string, types:DataTypesMap):string => {
108         if (!types[dataTypeName]) {
109             return 'string';
110         }
111         if (types[dataTypeName].derivedFromName == "tosca.datatypes.Root" || types[dataTypeName].properties) {
112             return null;
113         }
114         if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(types[dataTypeName].derivedFromName) > -1) {
115             return types[dataTypeName].derivedFromName
116         }
117         return this.getTypeForDataTypeDerivedFromSimple(types[dataTypeName].derivedFromName, types);
118     };
119
120     link = (scope:ITypeMapScope, element:any, $attr:any) => {
121         scope.showAddBtn = angular.isDefined(scope.showAddBtn) ? scope.showAddBtn : true;
122         scope.MapKeyValidationPattern = this.MapKeyValidationPattern;
123         scope.isMapKeysUnique = true;
124         if (scope.mapKeys === undefined) {
125             scope.mapKeys = Object.keys(scope.valueObjRef);
126         }
127         scope.showToscaFunction = new Array(scope.mapKeys.length);
128         scope.mapKeys.forEach((key, index) => {
129             scope.showToscaFunction[index] = false;
130             if (scope.parentProperty.subPropertyToscaFunctions != null) {
131                 scope.parentProperty.subPropertyToscaFunctions.forEach(SubPropertyToscaFunction => {
132                     if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
133                         scope.showToscaFunction[index] = true;
134                     }
135                 });
136             }
137         });
138
139         //reset valueObjRef and mapKeys when schema type is changed
140         scope.$watchCollection('schemaProperty.type', (newData:any):void => {
141             scope.isSchemaTypeDataType = this.isDataTypeForSchemaType(scope.schemaProperty, scope.types);
142             if (scope.valueObjRef) {
143                 scope.mapKeys = Object.keys(scope.valueObjRef);
144                 //keeping another copy of the keys, as the mapKeys gets overridden sometimes
145                 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
146             }
147         });
148
149         scope.$watchCollection('valueObjRef', (newData: any): void => {
150             scope.mapKeys = Object.keys(scope.valueObjRef);
151             scope.mapKeysStatic = Object.keys(scope.valueObjRef);
152         });
153
154         //when user brows between properties in "edit property form"
155         scope.$watchCollection('fieldsPrefixName', (newData:any):void => {
156             if (!scope.valueObjRef) {
157                 scope.valueObjRef = {};
158             }
159             scope.mapKeys = Object.keys(scope.valueObjRef);
160             //keeping another copy of the keys, as the mapKeys gets overridden sometimes
161             scope.mapKeysStatic = Object.keys(scope.valueObjRef);
162
163             if ($attr.defaultValue) {
164                 scope.mapDefaultValue = JSON.parse($attr.defaultValue);
165             }
166         });
167
168         //return dummy array in order to prevent rendering map-keys ng-repeat again when a map key is changed
169         scope.getNumber = (num:number):Array<any> => {
170             return new Array(num);
171         };
172
173         scope.getValidationPattern = (type:string):RegExp => {
174             return this.ValidationUtils.getValidationPattern(type);
175         };
176
177         scope.validateIntRange = (value:string):boolean => {
178             return !value || this.ValidationUtils.validateIntRange(value);
179         };
180
181         scope.changeKeyOfMap = (newKey:string, index:number, fieldName:string):void => {
182             const currentKeySet = Object.keys(scope.valueObjRef);
183             const currentKey = currentKeySet[index];
184             const existingKeyIndex = currentKeySet.indexOf(newKey);
185             if (existingKeyIndex > -1 && existingKeyIndex != index) {
186                 scope.parentFormObj[fieldName].$setValidity('keyExist', false);
187                 scope.isMapKeysUnique = false;
188                 return;
189             }
190
191             scope.parentFormObj[fieldName].$setValidity('keyExist', true);
192             scope.isMapKeysUnique = true;
193             if (!scope.parentFormObj[fieldName].$invalid) {
194                 //To preserve the order of the keys, delete each one and recreate
195                 let newObj = {};
196                 angular.copy(scope.valueObjRef, newObj);
197                 angular.forEach(newObj, function (value: any, key: string) {
198                     delete scope.valueObjRef[key];
199                     if (key == currentKey) {
200                         scope.valueObjRef[newKey] = value;
201                     } else {
202                         scope.valueObjRef[key] = value;
203                     }
204                 });
205             }
206         };
207
208         scope.deleteMapItem = (index:number):void=> {
209             const keyToChange = scope.mapKeys[index];
210             delete scope.valueObjRef[scope.mapKeys[index]];
211             scope.mapKeys.splice(index, 1);
212             scope.showToscaFunction.splice(index, 1);
213             if (scope.parentProperty.subPropertyToscaFunctions != null) {
214                 let subToscaFunctionList : Array<SubPropertyToscaFunction> = [];
215                 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
216                     if (SubPropertyToscaFunction.subPropertyPath.indexOf(keyToChange) == -1) {
217                         subToscaFunctionList.push(SubPropertyToscaFunction);
218                     }
219                 });
220                 scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
221             }
222             if (!scope.mapKeys.length) {//only when user removes all pairs of key-value fields - put the default
223                 if (scope.mapDefaultValue) {
224                     angular.copy(scope.mapDefaultValue, scope.valueObjRef);
225                     scope.mapKeys = Object.keys(scope.valueObjRef);
226                 }
227             }
228         };
229
230         scope.onEnableTosca = (toscaFlag:boolean,flagIndex:number):void => {
231             scope.showToscaFunction[flagIndex] = toscaFlag;
232             scope.valueObjRef[scope.mapKeys[flagIndex]] = null;
233             if (!toscaFlag) {
234                 if (scope.parentProperty.subPropertyToscaFunctions != null) {
235                     let subToscaFunctionList : Array<SubPropertyToscaFunction> = [];
236                     scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
237                         if (SubPropertyToscaFunction.subPropertyPath.indexOf(scope.mapKeys[flagIndex]) == -1) {
238                             subToscaFunctionList.push(SubPropertyToscaFunction);
239                         }
240                     });
241                     scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
242                 }
243             }
244         };
245
246         scope.onGetToscaFunction = (toscaGetFunction: ToscaGetFunction, key:string): void => {
247             if (scope.parentProperty.subPropertyToscaFunctions != null) {
248                 scope.parentProperty.subPropertyToscaFunctions.forEach(SubPropertyToscaFunction => {
249                     if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
250                         SubPropertyToscaFunction.toscaFunction = toscaGetFunction;
251                         return;
252                     }
253                 });
254
255             }
256             if (scope.parentProperty.subPropertyToscaFunctions == null){
257                 scope.parentProperty.subPropertyToscaFunctions = [];
258             }
259             let subPropertyToscaFunction = new SubPropertyToscaFunction();
260             subPropertyToscaFunction.toscaFunction = toscaGetFunction;
261             subPropertyToscaFunction.subPropertyPath = [key];
262             scope.parentProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
263         }
264
265         scope.addMapItemFields = ():void => {
266             scope.valueObjRef[''] = null;
267             scope.mapKeys = Object.keys(scope.valueObjRef);
268             scope.showToscaFunction.push(false);
269         };
270
271         scope.parseToCorrectType = (objectOfValues:any, locationInObj:string, type:string):void => {
272             if (objectOfValues[locationInObj] && type != PROPERTY_TYPES.STRING) {
273                 objectOfValues[locationInObj] = JSON.parse(objectOfValues[locationInObj]);
274             }
275         }
276
277         scope.validateSubToscaFunction = (key:string):boolean => {
278             if (scope.parentProperty.subPropertyToscaFunctions != null) {
279                 scope.parentProperty.subPropertyToscaFunctions.forEach(SubPropertyToscaFunction => {
280                     if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
281                         return true;
282                     }
283                 });
284             }
285             return false;
286         }
287     };
288
289     public static factory = (DataTypesService:DataTypesService,
290                              MapKeyValidationPattern:RegExp,
291                              ValidationUtils:ValidationUtils,
292                              $timeout:ng.ITimeoutService)=> {
293         return new TypeMapDirective(DataTypesService, MapKeyValidationPattern, ValidationUtils, $timeout);
294     };
295 }
296
297 TypeMapDirective.factory.$inject = ['Sdc.Services.DataTypesService', 'MapKeyValidationPattern', 'ValidationUtils', '$timeout'];