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[];
53 getValidationPattern(type: string): RegExp;
54 validateIntRange(value: string): boolean;
55 changeKeyOfMap(newKey: string, index: number, fieldName: string): void;
56 deleteMapItem(index: number): void;
57 addMapItemFields(): void;
58 parseToCorrectType(objectOfValues: any, locationInObj: string, type: string): void;
59 getNumber(num: number): any[];
60 validateSubToscaFunction(key: string): boolean;
61 onEnableTosca(toscaFlag: boolean, index: number);
62 onGetToscaFunction(toscaGetFunction: ToscaGetFunction, key: string);
65 export class TypeMapDirective implements ng.IDirective {
68 valueObjRef: '=', // ref to map object in the parent value object
69 componentInstanceMap: '=',
70 schemaProperty: '=', // get the schema.property object
71 parentFormObj: '=', // ref to parent form (get angular form object)
72 fieldsPrefixName: '=', // prefix for form fields names
73 readOnly: '=', // is form read only
74 defaultValue: '@', // this map default value
86 constructor(private DataTypesService: DataTypesService,
87 private MapKeyValidationPattern: RegExp,
88 private ValidationUtils: ValidationUtils,
89 private $timeout: ng.ITimeoutService) {
92 public static factory = (DataTypesService: DataTypesService,
93 MapKeyValidationPattern: RegExp,
94 ValidationUtils: ValidationUtils,
95 $timeout: ng.ITimeoutService) => {
96 return new TypeMapDirective(DataTypesService, MapKeyValidationPattern, ValidationUtils, $timeout);
98 template = (): string => {
99 return require('./type-map-directive.html');
102 link = (scope: ITypeMapScope, element: any, $attr: any) => {
103 scope.showAddBtn = angular.isDefined(scope.showAddBtn) ? scope.showAddBtn : true;
104 scope.MapKeyValidationPattern = this.MapKeyValidationPattern;
105 scope.isMapKeysUnique = true;
107 if (scope.mapKeys === undefined) {
108 if (scope.valueObjRef) {
109 scope.mapKeys = Object.keys(scope.valueObjRef);
110 } else if (scope.defaultValue) {
111 const defaultValue = JSON.parse(scope.defaultValue);
112 scope.valueObjRef = defaultValue;
113 scope.mapKeys = Object.keys(defaultValue);
115 console.warn('Missing value keys');
120 scope.showToscaFunction = new Array(scope.mapKeys.length);
121 scope.mapKeys.forEach((key, index) => {
122 scope.showToscaFunction[index] = false;
123 if (scope.parentProperty && scope.parentProperty.subPropertyToscaFunctions != null) {
124 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
125 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
126 scope.showToscaFunction[index] = true;
132 console.warn('Missing map keys');
135 // reset valueObjRef and mapKeys when schema type is changed
136 scope.$watchCollection('schemaProperty.type', (newData: any): void => {
137 scope.isSchemaTypeDataType = this.isDataTypeForSchemaType(scope.schemaProperty, scope.types);
138 if (scope.valueObjRef) {
139 scope.mapKeys = Object.keys(scope.valueObjRef);
140 // keeping another copy of the keys, as the mapKeys gets overridden sometimes
141 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
145 scope.$watchCollection('valueObjRef', (newData: any): void => {
146 if (scope.valueObjRef) {
147 scope.mapKeys = Object.keys(scope.valueObjRef);
148 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
150 console.warn('valueObjRef missing', scope.valueObjRef);
154 // when user brows between properties in "edit property form"
155 scope.$watchCollection('fieldsPrefixName', (newData: any): void => {
156 if (scope.valueObjRef) {
157 scope.mapKeys = Object.keys(scope.valueObjRef);
158 // keeping another copy of the keys, as the mapKeys gets overridden sometimes
159 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
162 if ($attr.defaultValue) {
163 scope.mapDefaultValue = JSON.parse($attr.defaultValue);
167 // return dummy array in order to prevent rendering map-keys ng-repeat again when a map key is changed
168 scope.getNumber = (num: number): any[] => {
169 return new Array(num);
172 scope.getValidationPattern = (type: string): RegExp => {
173 return this.ValidationUtils.getValidationPattern(type);
176 scope.validateIntRange = (value: string): boolean => {
177 return !value || this.ValidationUtils.validateIntRange(value);
180 scope.changeKeyOfMap = (newKey: string, index: number, fieldName: string): void => {
181 const currentKeySet = Object.keys(scope.valueObjRef);
182 const currentKey = currentKeySet[index];
183 const existingKeyIndex = currentKeySet.indexOf(newKey);
184 if (existingKeyIndex > -1 && existingKeyIndex != index) {
185 scope.parentFormObj[fieldName].$setValidity('keyExist', false);
186 scope.isMapKeysUnique = false;
190 scope.parentFormObj[fieldName].$setValidity('keyExist', true);
191 scope.isMapKeysUnique = true;
192 if (!scope.parentFormObj[fieldName].$invalid) {
193 // To preserve the order of the keys, delete each one and recreate
195 angular.copy(scope.valueObjRef, newObj);
196 angular.forEach(newObj, function(value: any, key: string) {
197 delete scope.valueObjRef[key];
198 if (key == currentKey) {
199 scope.valueObjRef[newKey] = value;
201 scope.valueObjRef[key] = value;
207 scope.deleteMapItem = (index: number): void => {
208 const keyToChange = scope.mapKeys[index];
209 delete scope.valueObjRef[scope.mapKeys[index]];
210 scope.mapKeys.splice(index, 1);
211 scope.showToscaFunction.splice(index, 1);
212 if (scope.parentProperty && scope.parentProperty.subPropertyToscaFunctions != null) {
213 const subToscaFunctionList: SubPropertyToscaFunction[] = [];
214 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
215 if (SubPropertyToscaFunction.subPropertyPath.indexOf(keyToChange) == -1) {
216 subToscaFunctionList.push(SubPropertyToscaFunction);
219 scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
221 if (!scope.mapKeys.length) {// only when user removes all pairs of key-value fields - put the default
222 if (scope.mapDefaultValue) {
223 angular.copy(scope.mapDefaultValue, scope.valueObjRef);
224 scope.mapKeys = Object.keys(scope.valueObjRef);
229 scope.onEnableTosca = (toscaFlag: boolean, flagIndex: number): void => {
230 scope.showToscaFunction[flagIndex] = toscaFlag;
231 scope.valueObjRef[scope.mapKeys[flagIndex]] = null;
233 if (scope.parentProperty.subPropertyToscaFunctions != null) {
234 const subToscaFunctionList: SubPropertyToscaFunction[] = [];
235 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
236 if (SubPropertyToscaFunction.subPropertyPath.indexOf(scope.mapKeys[flagIndex]) == -1) {
237 subToscaFunctionList.push(SubPropertyToscaFunction);
240 scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
245 scope.onGetToscaFunction = (toscaGetFunction: ToscaGetFunction, key: string): void => {
246 if (scope.parentProperty.subPropertyToscaFunctions != null) {
247 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
248 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
249 SubPropertyToscaFunction.toscaFunction = toscaGetFunction;
255 if (scope.parentProperty.subPropertyToscaFunctions == null) {
256 scope.parentProperty.subPropertyToscaFunctions = [];
258 const subPropertyToscaFunction = new SubPropertyToscaFunction();
259 subPropertyToscaFunction.toscaFunction = toscaGetFunction;
260 subPropertyToscaFunction.subPropertyPath = [key];
261 scope.parentProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
264 scope.addMapItemFields = (): void => {
265 if (!scope.valueObjRef) {
266 scope.valueObjRef = {};
267 scope.showToscaFunction = [];
270 scope.valueObjRef[''] = null;
271 scope.mapKeys = Object.keys(scope.valueObjRef);
272 scope.showToscaFunction.push(false);
275 scope.parseToCorrectType = (objectOfValues: any, locationInObj: string, type: string): void => {
276 if (objectOfValues[locationInObj] && type != PROPERTY_TYPES.STRING) {
277 objectOfValues[locationInObj] = JSON.parse(objectOfValues[locationInObj]);
281 scope.validateSubToscaFunction = (key: string): boolean => {
282 if (scope.parentProperty.subPropertyToscaFunctions != null) {
283 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
284 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
293 private isDataTypeForSchemaType = (property: SchemaProperty, types: DataTypesMap): boolean => {
294 property.simpleType = '';
295 if (property.type && PROPERTY_DATA.TYPES.indexOf(property.type) > -1) {
298 const simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type, types);
300 property.simpleType = simpleType;
306 private getTypeForDataTypeDerivedFromSimple = (dataTypeName: string, types: DataTypesMap): string => {
307 if (!types[dataTypeName]) {
310 if (types[dataTypeName].derivedFromName == 'tosca.datatypes.Root' || types[dataTypeName].properties) {
313 if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(types[dataTypeName].derivedFromName) > -1) {
314 return types[dataTypeName].derivedFromName;
316 return this.getTypeForDataTypeDerivedFromSimple(types[dataTypeName].derivedFromName, types);
320 TypeMapDirective.factory.$inject = ['Sdc.Services.DataTypesService', 'MapKeyValidationPattern', 'ValidationUtils', '$timeout'];