[SDC] rebase 1710 code
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / properties.utils.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 import { Injectable } from '@angular/core';
22 import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMap, InstanceFePropertiesMap, SchemaProperty, DerivedFEProperty, DerivedFEPropertyMap, DerivedPropertyType, InputFEModel} from "app/models";
23 import { DataTypeService } from "app/ng2/services/data-type.service";
24 import { PropertiesService } from "app/ng2/services/properties.service";
25 import { PROPERTY_TYPES } from "app/utils";
26 import { UUID } from "angular2-uuid";
27
28 @Injectable()
29 export class PropertiesUtils {
30
31     constructor(private dataTypeService:DataTypeService, private propertiesService: PropertiesService) {}
32
33     /**
34      * Entry point when getting properties from server
35      * For each instance, loop through each property, and:
36      * 1. Create flattened children
37      * 2. Check against inputs to see if any props are declared and disable them
38      * 3. Initialize valueObj (which also creates any new list/map flattened children as needed)
39      * Returns InstanceFePropertiesMap
40      */
41     public convertPropertiesMapToFEAndCreateChildren = (instancePropertiesMap:InstanceBePropertiesMap, isVF:boolean, inputs?:Array<InputFEModel>): InstanceFePropertiesMap => {
42         let instanceFePropertiesMap:InstanceFePropertiesMap = new InstanceFePropertiesMap();
43         angular.forEach(instancePropertiesMap, (properties:Array<PropertyBEModel>, instanceId:string) => {
44             let propertyFeArray: Array<PropertyFEModel> = [];
45             _.forEach(properties, (property: PropertyBEModel) => {
46
47                 if (!this.dataTypeService.getDataTypeByTypeName(property.type)) { // if type not exist in data types remove property from list
48                     console.log("ERROR: missing type " + property.type + " in dataTypes , of property ", property);
49                 } else {
50
51                     let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE
52
53                     if (newFEProp.derivedDataType == DerivedPropertyType.COMPLEX) { //Create children if prop is not simple, list, or map.
54                         newFEProp.flattenedChildren = this.createFlattenedChildren(newFEProp.type, newFEProp.name);
55                     }
56                     if (newFEProp.getInputValues && newFEProp.getInputValues.length) { //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
57                         newFEProp.getInputValues.forEach(propInputDetail => {
58                             let inputPath = propInputDetail.inputPath;
59                             if (!inputPath) { //TODO: this is a workaround until Marina adds inputPath
60                                 let input = inputs.find(input => input.uniqueId == propInputDetail.inputId);
61                                 if (!input) { console.log("CANNOT FIND INPUT FOR " + propInputDetail.inputId); return; }
62                                 else inputPath = input.inputPath;
63                             }
64                             if (inputPath == newFEProp.name) inputPath = undefined; // if not complex we need to remove the inputPath from FEModel so we not look for a child
65                             newFEProp.setAsDeclared(inputPath); //if a path is sent, its a child prop. this param is optional
66                             this.propertiesService.disableRelatedProperties(newFEProp, inputPath);
67                         });
68                     }
69                     this.initValueObjectRef(newFEProp); //initialize valueObj.
70                     propertyFeArray.push(newFEProp);
71                     newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
72                     this.dataTypeService.checkForCustomBehavior(newFEProp);
73                 }
74             });
75             instanceFePropertiesMap[instanceId] = propertyFeArray;
76
77         });
78         return instanceFePropertiesMap;
79     }
80
81     public createListOrMapChildren = (property:PropertyFEModel | DerivedFEProperty, key: string, valueObj: any): Array<DerivedFEProperty> => {
82         let newProps: Array<DerivedFEProperty> = [];
83         let parentProp = new DerivedFEProperty(property, property.propertiesName, true, key, valueObj);
84         newProps.push(parentProp);
85
86         if (!property.schema.property.isSimpleType) {
87             let additionalChildren:Array<DerivedFEProperty> = this.createFlattenedChildren(property.schema.property.type, parentProp.propertiesName);
88             this.assignFlattenedChildrenValues(parentProp.valueObj, additionalChildren, parentProp.propertiesName);
89             additionalChildren.forEach(prop => prop.canBeDeclared = false);
90             newProps.push(...additionalChildren);
91         }
92         return newProps;
93     }
94
95     /**
96      * Creates derivedFEProperties of a specified type and returns them.
97      */
98     private createFlattenedChildren = (type: string, parentName: string):Array<DerivedFEProperty> => {
99         let tempProps: Array<DerivedFEProperty> = [];
100         let dataTypeObj: DataTypeModel = this.dataTypeService.getDataTypeByTypeName(type);
101         this.dataTypeService.getDerivedDataTypeProperties(dataTypeObj, tempProps, parentName);
102         return tempProps;
103     }
104
105     /* Sets the valueObj of parent property and its children.
106     * 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.
107     */
108     public initValueObjectRef = (property: PropertyFEModel): void => {
109         if (property.derivedDataType == DerivedPropertyType.SIMPLE || 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
110             property.valueObj = property.value || property.defaultValue;
111
112             if (property.isDeclared && typeof property.valueObj == 'object')  property.valueObj = JSON.stringify(property.valueObj);
113         } else {
114             if (property.derivedDataType == DerivedPropertyType.LIST) {
115                 property.valueObj = _.merge([], JSON.parse(property.defaultValue || '[]'), JSON.parse(property.value || '[]')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined.
116             } else {
117                 property.valueObj = _.merge({}, JSON.parse(property.defaultValue || '{}'), JSON.parse(property.value || '{}')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined.
118             }
119             if ((property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) && Object.keys(property.valueObj).length) {
120                 Object.keys(property.valueObj).forEach((key) => {
121                     property.flattenedChildren.push(...this.createListOrMapChildren(property, key, property.valueObj[key]))
122                 });
123             } else {
124                 this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name);
125             }
126         }
127     }
128
129     /*
130     * Loops through flattened properties array and to assign values
131     * Then, convert any neccessary strings to objects, and vis-versa
132     * For list or map property, creates new children props if valueObj has values
133     */
134     public assignFlattenedChildrenValues = (parentValueJSON: any, derivedPropArray: Array<DerivedFEProperty>, parentName: string) => {
135         if (!derivedPropArray || !parentName) return;
136         let propsToPushMap: Map<number, Array<DerivedFEProperty>> = new Map<number, Array<DerivedFEProperty>>();
137         derivedPropArray.forEach((prop, index) => {
138
139             let propNameInObj = prop.propertiesName.substring(prop.propertiesName.indexOf(parentName) + parentName.length + 1).split('#').join('.'); //extract everything after parent name
140             prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue
141
142             if ( prop.isDeclared && typeof prop.valueObj == 'object') { //Stringify objects of items that are declared
143                 prop.valueObj = JSON.stringify(prop.valueObj);
144             } else if(typeof prop.valueObj == PROPERTY_TYPES.STRING
145                 && (prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN)){ //parse ints and non-string simple types
146                 prop.valueObj = JSON.parse(prop.valueObj);
147             } else { //parse strings that should be objects
148                 if (prop.derivedDataType == DerivedPropertyType.COMPLEX && typeof prop.valueObj != 'object') {
149                     prop.valueObj = JSON.parse(prop.valueObj || '{}');
150                 } else if (prop.derivedDataType == DerivedPropertyType.LIST && typeof prop.valueObj != 'object') {
151                     prop.valueObj = JSON.parse(prop.valueObj || '[]');
152                 } else if (prop.derivedDataType == DerivedPropertyType.MAP && typeof prop.valueObj != 'object' && (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType)) { //dont parse values for children of map of simple
153                     prop.valueObj = JSON.parse(prop.valueObj || '{}');
154                 }
155                 if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && Object.keys(prop.valueObj).length) {
156                     let newProps: Array<DerivedFEProperty> = [];
157                     Object.keys(prop.valueObj).forEach((key) => {
158                         newProps.push(...this.createListOrMapChildren(prop, key, prop.valueObj[key]));//create new children, assign their values, and then add to array
159                     });
160                     propsToPushMap[index + 1] = newProps;
161                 }
162             }
163         });
164
165         //add props after we're done looping (otherwise our loop gets messed up). Push in reverse order, so we dont mess up indexes.
166         Object.keys(propsToPushMap).reverse().forEach((indexToInsert) => {
167             derivedPropArray.splice(+indexToInsert, 0, ...propsToPushMap[indexToInsert]); //slacker parsing
168         });
169     }
170
171     public resetPropertyValue = (property: PropertyFEModel, newValue: string, nestedPath?: string): void => {
172         property.value = newValue;
173         if (nestedPath) {
174             let newProp = property.flattenedChildren.find(prop => prop.propertiesName == nestedPath);
175             newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name);
176         } else {
177             this.initValueObjectRef(property);
178         }
179     }
180
181
182
183 }