2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 * Created by rcohen on 9/15/2016.
25 import { DataTypesMap, PropertyModel, SchemaProperty } from 'app/models';
26 import { InstanceFeDetails } from 'app/models/instance-fe-details';
27 import { SubPropertyToscaFunction } from 'app/models/sub-property-tosca-function';
28 import { ToscaGetFunction } from 'app/models/tosca-get-function';
29 import { DataTypesService } from 'app/services';
30 import { PROPERTY_DATA, PROPERTY_TYPES, ValidationUtils } from 'app/utils';
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;
40 mapKeys: string[]; // array of map keys
41 mapKeysStatic: string[];
42 MapKeyValidationPattern: RegExp;
43 fieldsPrefixName: string;
47 constraints: string[];
49 showToscaFunction: boolean[];
52 complexToscapath: string;
54 getValidationPattern(type: string): RegExp;
55 validateIntRange(value: string): boolean;
56 changeKeyOfMap(newKey: string, index: number, fieldName: string): void;
57 deleteMapItem(index: number): void;
58 addMapItemFields(): void;
59 parseToCorrectType(objectOfValues: any, locationInObj: string, type: string): void;
60 getNumber(num: number): any[];
61 validateSubToscaFunction(key: string): boolean;
62 onEnableTosca(toscaFlag: boolean, index: number);
63 onGetToscaFunction(toscaGetFunction: ToscaGetFunction, key: string);
66 export class TypeMapDirective implements ng.IDirective {
69 valueObjRef: '=', // ref to map object in the parent value object
70 componentInstanceMap: '=',
71 schemaProperty: '=', // get the schema.property object
72 parentFormObj: '=', // ref to parent form (get angular form object)
73 fieldsPrefixName: '=', // prefix for form fields names
74 readOnly: '=', // is form read only
75 defaultValue: '@', // this map default value
88 constructor(private DataTypesService: DataTypesService,
89 private MapKeyValidationPattern: RegExp,
90 private ValidationUtils: ValidationUtils,
91 private $timeout: ng.ITimeoutService) {
94 public static factory = (DataTypesService: DataTypesService,
95 MapKeyValidationPattern: RegExp,
96 ValidationUtils: ValidationUtils,
97 $timeout: ng.ITimeoutService) => {
98 return new TypeMapDirective(DataTypesService, MapKeyValidationPattern, ValidationUtils, $timeout);
100 template = (): string => {
101 return require('./type-map-directive.html');
104 link = (scope: ITypeMapScope, element: any, $attr: any) => {
105 scope.showAddBtn = angular.isDefined(scope.showAddBtn) ? scope.showAddBtn : true;
106 scope.MapKeyValidationPattern = this.MapKeyValidationPattern;
107 scope.isMapKeysUnique = true;
109 if (scope.mapKeys === undefined) {
110 if (scope.valueObjRef) {
111 scope.mapKeys = Object.keys(scope.valueObjRef);
112 } else if (scope.defaultValue) {
113 const defaultValue = JSON.parse(scope.defaultValue);
114 scope.valueObjRef = defaultValue;
115 scope.mapKeys = Object.keys(defaultValue);
117 console.warn('Missing value keys');
122 scope.showToscaFunction = new Array(scope.mapKeys.length);
123 scope.mapKeys.forEach((key, index) => {
124 scope.showToscaFunction[index] = false;
125 if (scope.parentProperty && scope.parentProperty.subPropertyToscaFunctions != null) {
126 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
127 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
128 scope.showToscaFunction[index] = true;
134 console.warn('Missing map keys');
137 // reset valueObjRef and mapKeys when schema type is changed
138 scope.$watchCollection('schemaProperty.type', (newData: any): void => {
139 scope.isSchemaTypeDataType = this.DataTypesService.isDataTypeForSchemaType(scope.schemaProperty);
140 if (scope.valueObjRef) {
141 scope.mapKeys = Object.keys(scope.valueObjRef);
142 // keeping another copy of the keys, as the mapKeys gets overridden sometimes
143 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
147 scope.$watchCollection('valueObjRef', (newData: any): void => {
148 if (scope.valueObjRef) {
149 scope.mapKeys = Object.keys(scope.valueObjRef);
150 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
152 console.warn('valueObjRef missing', scope.valueObjRef);
156 // when user brows between properties in "edit property form"
157 scope.$watchCollection('fieldsPrefixName', (newData: any): void => {
158 if (scope.valueObjRef) {
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);
164 if ($attr.defaultValue) {
165 scope.mapDefaultValue = JSON.parse($attr.defaultValue);
169 // return dummy array in order to prevent rendering map-keys ng-repeat again when a map key is changed
170 scope.getNumber = (num: number): any[] => {
171 return new Array(num);
174 scope.getValidationPattern = (type: string): RegExp => {
175 return this.ValidationUtils.getValidationPattern(type);
178 scope.validateIntRange = (value: string): boolean => {
179 return !value || this.ValidationUtils.validateIntRange(value);
182 scope.changeKeyOfMap = (newKey: string, index: number, fieldName: string): void => {
183 const currentKeySet = Object.keys(scope.valueObjRef);
184 const currentKey = currentKeySet[index];
185 const existingKeyIndex = currentKeySet.indexOf(newKey);
186 if (existingKeyIndex > -1 && existingKeyIndex != index) {
187 if (scope.parentFormObj != null) {
188 scope.parentFormObj[fieldName].$setValidity('keyExist', false);
190 scope.isMapKeysUnique = false;
193 if (scope.parentFormObj != null) {
194 scope.parentFormObj[fieldName].$setValidity('keyExist', true);
195 if (!scope.parentFormObj[fieldName].$invalid) {
196 // To preserve the order of the keys, delete each one and recreate
198 angular.copy(scope.valueObjRef, newObj);
199 angular.forEach(newObj, function(value: any, key: string) {
200 delete scope.valueObjRef[key];
201 if (key == currentKey) {
202 scope.valueObjRef[newKey] = value;
204 scope.valueObjRef[key] = value;
209 scope.isMapKeysUnique = true;
213 scope.deleteMapItem = (index: number): void => {
214 const keyToChange = scope.mapKeys[index];
215 delete scope.valueObjRef[scope.mapKeys[index]];
216 scope.mapKeys.splice(index, 1);
217 scope.showToscaFunction.splice(index, 1);
218 if (scope.parentProperty && scope.parentProperty.subPropertyToscaFunctions != null) {
219 const subToscaFunctionList: SubPropertyToscaFunction[] = [];
220 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
221 if (SubPropertyToscaFunction.subPropertyPath.indexOf(keyToChange) == -1) {
222 subToscaFunctionList.push(SubPropertyToscaFunction);
225 scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
227 if (!scope.mapKeys.length) {// only when user removes all pairs of key-value fields - put the default
228 if (scope.mapDefaultValue) {
229 angular.copy(scope.mapDefaultValue, scope.valueObjRef);
230 scope.mapKeys = Object.keys(scope.valueObjRef);
235 scope.onEnableTosca = (toscaFlag: boolean, flagIndex: number): void => {
236 scope.showToscaFunction[flagIndex] = toscaFlag;
237 scope.valueObjRef[scope.mapKeys[flagIndex]] = null;
239 if (scope.parentProperty.subPropertyToscaFunctions != null) {
240 const subToscaFunctionList: SubPropertyToscaFunction[] = [];
241 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
242 if (SubPropertyToscaFunction.subPropertyPath.toString() != scope.mapKeys[flagIndex]) {
243 subToscaFunctionList.push(SubPropertyToscaFunction);
246 scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
251 scope.onGetToscaFunction = (toscaGetFunction: ToscaGetFunction, index:string): void => {
252 let key:string = index;
253 if (scope.parentProperty.subPropertyToscaFunctions != null) {
254 let toscaFlag : boolean = true;
255 scope.parentProperty.subPropertyToscaFunctions.forEach(SubPropertyToscaFunction => {
256 if (SubPropertyToscaFunction.subPropertyPath.toString() == key) {
257 SubPropertyToscaFunction.toscaFunction = toscaGetFunction;
263 let subPropertyToscaFunction = new SubPropertyToscaFunction();
264 subPropertyToscaFunction.toscaFunction = toscaGetFunction;
265 subPropertyToscaFunction.subPropertyPath = [key];
266 scope.parentProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
269 let subPropertyToscaFunction = new SubPropertyToscaFunction();
270 subPropertyToscaFunction.toscaFunction = toscaGetFunction;
271 subPropertyToscaFunction.subPropertyPath = [key];
272 scope.parentProperty.subPropertyToscaFunctions = [subPropertyToscaFunction];
276 scope.addMapItemFields = (): void => {
277 if (!scope.valueObjRef) {
278 scope.valueObjRef = {};
279 scope.showToscaFunction = [];
282 scope.valueObjRef[''] = null;
283 scope.mapKeys = Object.keys(scope.valueObjRef);
284 scope.showToscaFunction.push(false);
287 scope.parseToCorrectType = (objectOfValues: any, locationInObj: string, type: string): void => {
288 if (objectOfValues[locationInObj] && type != PROPERTY_TYPES.STRING) {
289 objectOfValues[locationInObj] = JSON.parse(objectOfValues[locationInObj]);
293 scope.validateSubToscaFunction = (key: string): boolean => {
294 if (scope.parentProperty.subPropertyToscaFunctions != null) {
295 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
296 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
305 private isDataTypeForSchemaType = (property: SchemaProperty, types: DataTypesMap): boolean => {
306 property.simpleType = '';
307 if (property.type && PROPERTY_DATA.TYPES.indexOf(property.type) > -1) {
310 const simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type, types);
312 property.simpleType = simpleType;
318 private getTypeForDataTypeDerivedFromSimple = (dataTypeName: string, types: DataTypesMap): string => {
319 if (!types[dataTypeName]) {
322 if (types[dataTypeName].derivedFromName == 'tosca.datatypes.Root' || types[dataTypeName].properties) {
325 if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(types[dataTypeName].derivedFromName) > -1) {
326 return types[dataTypeName].derivedFromName;
328 return this.getTypeForDataTypeDerivedFromSimple(types[dataTypeName].derivedFromName, types);
332 TypeMapDirective.factory.$inject = ['Sdc.Services.DataTypesService', 'MapKeyValidationPattern', 'ValidationUtils', '$timeout'];