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=========================================================
21 import * as _ from "lodash";
22 import {Injectable} from '@angular/core';
28 InstanceBePropertiesMap,
29 InstanceFePropertiesMap,
33 import {DataTypeService} from "app/ng2/services/data-type.service";
34 import {PropertiesService} from "app/ng2/services/properties.service";
35 import {PROPERTY_TYPES} from "app/utils";
36 import { SubPropertyToscaFunction } from "app/models/sub-property-tosca-function";
39 export class PropertiesUtils {
41 constructor(private dataTypeService:DataTypeService, private propertiesService: PropertiesService) {}
44 * Entry point when getting properties from server
45 * For each instance, loop through each property, and:
46 * 1. Create flattened children
47 * 2. Check against inputs to see if any props are declared and disable them
48 * 3. Initialize valueObj (which also creates any new list/map flattened children as needed)
49 * Returns InstanceFePropertiesMap
51 public convertPropertiesMapToFEAndCreateChildren = (instancePropertiesMap:InstanceBePropertiesMap, isVF:boolean, inputs?:Array<InputFEModel>, model?:string): InstanceFePropertiesMap => {
52 let instanceFePropertiesMap:InstanceFePropertiesMap = new InstanceFePropertiesMap();
53 angular.forEach(instancePropertiesMap, (properties:Array<PropertyBEModel>, instanceId:string) => {
54 let propertyFeArray: Array<PropertyFEModel> = [];
55 _.forEach(properties, (property: PropertyBEModel) => {
57 if (this.dataTypeService.getDataTypeByModelAndTypeName(model, property.type)) { // if type not exist in data types remove property from list
59 let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE
60 if (!newFEProp.parentUniqueId) {
61 newFEProp.parentUniqueId = instanceId;
63 this.initValueObjectRef(newFEProp); //initialize valueObj AND creates flattened children
64 propertyFeArray.push(newFEProp);
65 newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
66 this.dataTypeService.checkForCustomBehavior(newFEProp);
68 if (newFEProp.isToscaFunction()) {
71 //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
72 if (newFEProp.getInputValues && newFEProp.getInputValues.length) {
73 newFEProp.getInputValues.forEach(propInputDetail => {
74 let inputPath = propInputDetail.inputPath;
75 if (!inputPath) { //TODO: this is a workaround until Marina adds inputPath
76 let input = inputs.find(input => input.uniqueId == propInputDetail.inputId);
77 if (!input) { console.log("CANNOT FIND INPUT FOR " + propInputDetail.inputId); return; }
78 else inputPath = input.inputPath;
80 if (inputPath == newFEProp.name) inputPath = undefined; // if not complex we need to remove the inputPath from FEModel so we not look for a child
81 newFEProp.setAsDeclared(inputPath); //if a path is sent, its a child prop. this param is optional
82 this.propertiesService.disableRelatedProperties(newFEProp, inputPath);
85 if (newFEProp.getPolicyValues && newFEProp.getPolicyValues.length) {
86 newFEProp.setAsDeclared(newFEProp.inputPath); //if a path is sent, its a child prop. this param is optional
87 this.propertiesService.disableRelatedProperties(newFEProp, newFEProp.inputPath);
91 instanceFePropertiesMap[instanceId] = propertyFeArray;
94 return instanceFePropertiesMap;
97 public convertAddPropertyBAToPropertyFE = (property: PropertyBEModel): PropertyFEModel => {
98 const newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE
99 this.initValueObjectRef(newFEProp);
100 newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
101 this.dataTypeService.checkForCustomBehavior(newFEProp);
105 public createListOrMapChildren = (property:PropertyFEModel | DerivedFEProperty, key: string, valueObj: any): Array<DerivedFEProperty> => {
106 let newProps: Array<DerivedFEProperty> = [];
107 let parentProp = new DerivedFEProperty(property, property.propertiesName, true, key, valueObj);
108 newProps.push(parentProp);
110 if (!property.schema.property.isSimpleType) {
111 let additionalChildren:Array<DerivedFEProperty> = this.createFlattenedChildren(property.schema.property.type, parentProp.propertiesName, key);
112 this.assignFlattenedChildrenValues(parentProp.valueObj, additionalChildren, parentProp.propertiesName);
113 additionalChildren.forEach(prop => {
114 prop.canBeDeclared = false;
115 if (property.subPropertyToscaFunctions != null) {
116 const subToscaFunctArray : SubPropertyToscaFunction[] = property.subPropertyToscaFunctions;
117 subToscaFunctArray.forEach(subToscaFunct => {
118 const keyArray : string[] = subToscaFunct.subPropertyPath;
119 if (keyArray.length > 1 && prop.mapKey == keyArray[0] && prop.name == keyArray[1]) {
120 prop.toscaFunction = subToscaFunct.toscaFunction;
126 newProps.push(...additionalChildren);
132 * Creates derivedFEProperties of a specified type and returns them.
134 private createFlattenedChildren = (type: string, parentName: string, key: string):Array<DerivedFEProperty> => {
135 let tempProps: Array<DerivedFEProperty> = [];
136 let dataTypeObj: DataTypeModel = this.dataTypeService.getDataTypeByTypeName(type);
137 this.dataTypeService.getDerivedDataTypeProperties(dataTypeObj, tempProps, parentName);
138 tempProps.forEach(tempDervObj => {
139 tempDervObj.mapKey = key;
141 return _.sortBy(tempProps, ['propertiesName']);
144 /* Sets the valueObj of parent property and its children.
145 * Note: This logic is different than assignflattenedchildrenvalues - here we merge values, there we pick either the parents value, props value, or default value - without merging.
147 public initValueObjectRef = (property: PropertyFEModel): void => {
148 property.resetValueObjValidation();
149 if (property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component
150 property.valueObj = property.value || property.defaultValue || null; // use null for empty value object
151 if (property.valueObj && typeof property.valueObj == 'object') {
152 property.valueObj = JSON.stringify(property.valueObj);
155 property.valueObj = property.getValueObj();
156 if (property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) {
157 property.flattenedChildren = [];
158 Object.keys(property.valueObj).forEach((key) => {
159 property.flattenedChildren.push(...this.createListOrMapChildren(property, key, property.valueObj[key]));
160 const lastCreatedChild = property.flattenedChildren.slice(-1)[0];
161 if (property.schemaType == PROPERTY_TYPES.MAP && property.valueObj[key]){
162 const nestedValue:object = property.valueObj[key];
163 Object.keys(nestedValue).forEach((keyNested) => {
164 property.flattenedChildren.push(...this.createListOrMapChildren(lastCreatedChild, keyNested, nestedValue[keyNested]));
168 } else if (property.derivedDataType === DerivedPropertyType.COMPLEX) {
169 property.flattenedChildren = this.createFlattenedChildren(property.type, property.name, "");
170 this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name);
171 this.setFlattenedChildernToscaFunction(property.subPropertyToscaFunctions, property.flattenedChildren, property.name);
172 property.flattenedChildren.forEach((childProp) => {
173 property.childPropUpdated(childProp);
178 property.updateValueObjOrig();
181 public setFlattenedChildernToscaFunction = (subPropertyToscaFunctions: SubPropertyToscaFunction[], derivedPropArray: Array<DerivedFEProperty>, topLevelPropertyName: string) => {
182 if (!subPropertyToscaFunctions || !derivedPropArray || !topLevelPropertyName){
185 derivedPropArray.forEach((prop, index) => {
186 const subPropertyPath = prop.propertiesName.substring(prop.propertiesName.indexOf(topLevelPropertyName) + topLevelPropertyName.length + 1);
187 subPropertyToscaFunctions.forEach(subPropertyToscaFunction => {
188 const toscaFunctionPath = subPropertyToscaFunction.subPropertyPath.join('#');
189 if (subPropertyPath === toscaFunctionPath){
190 prop.toscaFunction = subPropertyToscaFunction.toscaFunction;
197 * Loops through flattened properties array and to assign values
198 * Then, convert any neccessary strings to objects, and vis-versa
199 * For list or map property, creates new children props if valueObj has values
201 public assignFlattenedChildrenValues = (parentValueJSON: any, derivedPropArray: Array<DerivedFEProperty>, parentName: string) => {
202 if (!derivedPropArray || !parentName) return;
203 let propsToPushMap: Map<number, Array<DerivedFEProperty>> = new Map<number, Array<DerivedFEProperty>>();
204 derivedPropArray.forEach((prop, index) => {
206 let propNameInObj = prop.propertiesName.substring(prop.propertiesName.indexOf(parentName) + parentName.length + 1).split('#').join('.'); //extract everything after parent name
207 prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue || null); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue
208 prop.value = (prop.valueObj !== null && (typeof prop.valueObj) != 'string') ? JSON.stringify(prop.valueObj) : prop.valueObj;
210 if ((prop.isDeclared || prop.type == PROPERTY_TYPES.STRING || prop.type == PROPERTY_TYPES.JSON)) { //Stringify objects of items that are declared or from type string/json
211 prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == 'object') ? JSON.stringify(prop.valueObj) : prop.valueObj;
212 } else if(prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN){ //parse ints and non-string simple types
213 prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == PROPERTY_TYPES.STRING) ? JSON.parse(prop.valueObj) : prop.valueObj;
214 } else { //parse strings that should be objects
215 if (prop.derivedDataType == DerivedPropertyType.COMPLEX) {
216 prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj;
217 } else if (prop.derivedDataType == DerivedPropertyType.LIST) {
218 prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '[]') : prop.valueObj;
219 } else if (prop.derivedDataType == DerivedPropertyType.MAP) {
220 if (!prop.isChildOfListOrMap) {
221 prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj;
224 if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && prop.valueObj !== null && Object.keys(prop.valueObj).length) {
225 let newProps: Array<DerivedFEProperty> = [];
226 Object.keys(prop.valueObj).forEach((key) => {
227 newProps.push(...this.createListOrMapChildren(prop, key, prop.valueObj[key]));//create new children, assign their values, and then add to array
229 propsToPushMap[index + 1] = newProps;
233 prop.valueObj = PropertyFEModel.cleanValueObj(prop.valueObj);
236 //add props after we're done looping (otherwise our loop gets messed up). Push in reverse order, so we dont mess up indexes.
237 Object.keys(propsToPushMap).reverse().forEach((indexToInsert) => {
238 derivedPropArray.splice(+indexToInsert, 0, ...propsToPushMap[indexToInsert]); //slacker parsing
242 public resetPropertyValue = (property: PropertyFEModel, newValue: string, nestedPath?: string): void => {
243 property.value = newValue;
245 let newProp = property.flattenedChildren.find(prop => prop.propertiesName == nestedPath);
246 newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name);
247 property.updateValueObjOrig();
249 this.initValueObjectRef(property);