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 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): any[];
59 validateSubToscaFunction(key: string): boolean;
60 onEnableTosca(toscaFlag: boolean, index: number);
61 onGetToscaFunction(toscaGetFunction: ToscaGetFunction, key: string);
64 export class TypeMapDirective implements ng.IDirective {
67 valueObjRef: '=', // ref to map object in the parent value object
68 componentInstanceMap: '=',
69 schemaProperty: '=', // get the schema.property object
70 parentFormObj: '=', // ref to parent form (get angular form object)
71 fieldsPrefixName: '=', // prefix for form fields names
72 readOnly: '=', // is form read only
73 defaultValue: '@', // this map default value
84 constructor(private DataTypesService: DataTypesService,
85 private MapKeyValidationPattern: RegExp,
86 private ValidationUtils: ValidationUtils,
87 private $timeout: ng.ITimeoutService) {
90 public static factory = (DataTypesService: DataTypesService,
91 MapKeyValidationPattern: RegExp,
92 ValidationUtils: ValidationUtils,
93 $timeout: ng.ITimeoutService) => {
94 return new TypeMapDirective(DataTypesService, MapKeyValidationPattern, ValidationUtils, $timeout);
96 template = (): string => {
97 return require('./type-map-directive.html');
100 link = (scope: ITypeMapScope, element: any, $attr: any) => {
101 scope.showAddBtn = angular.isDefined(scope.showAddBtn) ? scope.showAddBtn : true;
102 scope.MapKeyValidationPattern = this.MapKeyValidationPattern;
103 scope.isMapKeysUnique = true;
105 if (scope.mapKeys === undefined) {
106 if (scope.valueObjRef) {
107 scope.mapKeys = Object.keys(scope.valueObjRef);
108 } else if (scope.defaultValue) {
109 const defaultValue = JSON.parse(scope.defaultValue);
110 scope.valueObjRef = defaultValue;
111 scope.mapKeys = Object.keys(defaultValue);
113 console.warn('Missing value keys');
118 scope.showToscaFunction = new Array(scope.mapKeys.length);
119 scope.mapKeys.forEach((key, index) => {
120 scope.showToscaFunction[index] = false;
121 if (scope.parentProperty && scope.parentProperty.subPropertyToscaFunctions != null) {
122 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
123 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
124 scope.showToscaFunction[index] = true;
130 console.warn('Missing map keys');
133 // reset valueObjRef and mapKeys when schema type is changed
134 scope.$watchCollection('schemaProperty.type', (newData: any): void => {
135 scope.isSchemaTypeDataType = this.isDataTypeForSchemaType(scope.schemaProperty, scope.types);
136 if (scope.valueObjRef) {
137 scope.mapKeys = Object.keys(scope.valueObjRef);
138 // keeping another copy of the keys, as the mapKeys gets overridden sometimes
139 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
143 scope.$watchCollection('valueObjRef', (newData: any): void => {
144 if (scope.valueObjRef) {
145 scope.mapKeys = Object.keys(scope.valueObjRef);
146 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
148 console.warn('valueObjRef missing', scope.valueObjRef);
152 // when user brows between properties in "edit property form"
153 scope.$watchCollection('fieldsPrefixName', (newData: any): void => {
154 if (scope.valueObjRef) {
155 scope.mapKeys = Object.keys(scope.valueObjRef);
156 // keeping another copy of the keys, as the mapKeys gets overridden sometimes
157 scope.mapKeysStatic = Object.keys(scope.valueObjRef);
160 if ($attr.defaultValue) {
161 scope.mapDefaultValue = JSON.parse($attr.defaultValue);
165 // return dummy array in order to prevent rendering map-keys ng-repeat again when a map key is changed
166 scope.getNumber = (num: number): any[] => {
167 return new Array(num);
170 scope.getValidationPattern = (type: string): RegExp => {
171 return this.ValidationUtils.getValidationPattern(type);
174 scope.validateIntRange = (value: string): boolean => {
175 return !value || this.ValidationUtils.validateIntRange(value);
178 scope.changeKeyOfMap = (newKey: string, index: number, fieldName: string): void => {
179 const currentKeySet = Object.keys(scope.valueObjRef);
180 const currentKey = currentKeySet[index];
181 const existingKeyIndex = currentKeySet.indexOf(newKey);
182 if (existingKeyIndex > -1 && existingKeyIndex != index) {
183 scope.parentFormObj[fieldName].$setValidity('keyExist', false);
184 scope.isMapKeysUnique = false;
188 scope.parentFormObj[fieldName].$setValidity('keyExist', true);
189 scope.isMapKeysUnique = true;
190 if (!scope.parentFormObj[fieldName].$invalid) {
191 // To preserve the order of the keys, delete each one and recreate
193 angular.copy(scope.valueObjRef, newObj);
194 angular.forEach(newObj, function(value: any, key: string) {
195 delete scope.valueObjRef[key];
196 if (key == currentKey) {
197 scope.valueObjRef[newKey] = value;
199 scope.valueObjRef[key] = value;
205 scope.deleteMapItem = (index: number): void => {
206 const keyToChange = scope.mapKeys[index];
207 delete scope.valueObjRef[scope.mapKeys[index]];
208 scope.mapKeys.splice(index, 1);
209 scope.showToscaFunction.splice(index, 1);
210 if (scope.parentProperty && scope.parentProperty.subPropertyToscaFunctions != null) {
211 const subToscaFunctionList: SubPropertyToscaFunction[] = [];
212 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
213 if (SubPropertyToscaFunction.subPropertyPath.indexOf(keyToChange) == -1) {
214 subToscaFunctionList.push(SubPropertyToscaFunction);
217 scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
219 if (!scope.mapKeys.length) {// only when user removes all pairs of key-value fields - put the default
220 if (scope.mapDefaultValue) {
221 angular.copy(scope.mapDefaultValue, scope.valueObjRef);
222 scope.mapKeys = Object.keys(scope.valueObjRef);
227 scope.onEnableTosca = (toscaFlag: boolean, flagIndex: number): void => {
228 scope.showToscaFunction[flagIndex] = toscaFlag;
229 scope.valueObjRef[scope.mapKeys[flagIndex]] = null;
231 if (scope.parentProperty.subPropertyToscaFunctions != null) {
232 const subToscaFunctionList: SubPropertyToscaFunction[] = [];
233 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction, index) => {
234 if (SubPropertyToscaFunction.subPropertyPath.indexOf(scope.mapKeys[flagIndex]) == -1) {
235 subToscaFunctionList.push(SubPropertyToscaFunction);
238 scope.parentProperty.subPropertyToscaFunctions = subToscaFunctionList;
243 scope.onGetToscaFunction = (toscaGetFunction: ToscaGetFunction, key: string): void => {
244 if (scope.parentProperty.subPropertyToscaFunctions != null) {
245 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
246 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
247 SubPropertyToscaFunction.toscaFunction = toscaGetFunction;
253 if (scope.parentProperty.subPropertyToscaFunctions == null) {
254 scope.parentProperty.subPropertyToscaFunctions = [];
256 const subPropertyToscaFunction = new SubPropertyToscaFunction();
257 subPropertyToscaFunction.toscaFunction = toscaGetFunction;
258 subPropertyToscaFunction.subPropertyPath = [key];
259 scope.parentProperty.subPropertyToscaFunctions.push(subPropertyToscaFunction);
262 scope.addMapItemFields = (): void => {
263 if (!scope.valueObjRef) {
264 scope.valueObjRef = {};
265 scope.showToscaFunction = [];
268 scope.valueObjRef[''] = null;
269 scope.mapKeys = Object.keys(scope.valueObjRef);
270 scope.showToscaFunction.push(false);
273 scope.parseToCorrectType = (objectOfValues: any, locationInObj: string, type: string): void => {
274 if (objectOfValues[locationInObj] && type != PROPERTY_TYPES.STRING) {
275 objectOfValues[locationInObj] = JSON.parse(objectOfValues[locationInObj]);
279 scope.validateSubToscaFunction = (key: string): boolean => {
280 if (scope.parentProperty.subPropertyToscaFunctions != null) {
281 scope.parentProperty.subPropertyToscaFunctions.forEach((SubPropertyToscaFunction) => {
282 if (SubPropertyToscaFunction.subPropertyPath.indexOf(key) != -1) {
291 private isDataTypeForSchemaType = (property: SchemaProperty, types: DataTypesMap): boolean => {
292 property.simpleType = '';
293 if (property.type && PROPERTY_DATA.TYPES.indexOf(property.type) > -1) {
296 const simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type, types);
298 property.simpleType = simpleType;
304 private getTypeForDataTypeDerivedFromSimple = (dataTypeName: string, types: DataTypesMap): string => {
305 if (!types[dataTypeName]) {
308 if (types[dataTypeName].derivedFromName == 'tosca.datatypes.Root' || types[dataTypeName].properties) {
311 if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(types[dataTypeName].derivedFromName) > -1) {
312 return types[dataTypeName].derivedFromName;
314 return this.getTypeForDataTypeDerivedFromSimple(types[dataTypeName].derivedFromName, types);
318 TypeMapDirective.factory.$inject = ['Sdc.Services.DataTypesService', 'MapKeyValidationPattern', 'ValidationUtils', '$timeout'];