[sdc] update code of sdc
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / properties.utils.ts
1 import { Injectable } from '@angular/core';
2 import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMap, InstanceFePropertiesMap, SchemaProperty, DerivedFEProperty, DerivedFEPropertyMap, DerivedPropertyType, InputFEModel} from "app/models";
3 import { DataTypeService } from "app/ng2/services/data-type.service";
4 import { PropertiesService } from "app/ng2/services/properties.service";
5 import { PROPERTY_TYPES } from "app/utils";
6 import { UUID } from "angular2-uuid";
7
8 @Injectable()
9 export class PropertiesUtils {
10
11     constructor(private dataTypeService:DataTypeService, private propertiesService: PropertiesService) {}
12
13     /**
14      * Entry point when getting properties from server
15      * For each instance, loop through each property, and:
16      * 1. Create flattened children
17      * 2. Check against inputs to see if any props are declared and disable them
18      * 3. Initialize valueObj (which also creates any new list/map flattened children as needed)
19      * Returns InstanceFePropertiesMap
20      */
21     public convertPropertiesMapToFEAndCreateChildren = (instancePropertiesMap:InstanceBePropertiesMap, inputs:Array<InputFEModel>): InstanceFePropertiesMap => {
22         let instanceFePropertiesMap:InstanceFePropertiesMap = new InstanceFePropertiesMap();
23         angular.forEach(instancePropertiesMap, (properties:Array<PropertyBEModel>, instanceName:string) => {
24             let instanceInputs: Array<InputFEModel> = inputs.filter(input => input.instanceName == instanceName.split('.').pop());
25             let propertyFeArray: Array<PropertyFEModel> = [];
26             _.forEach(properties, (property: PropertyBEModel) => {
27
28                 if (!this.dataTypeService.getDataTypeByTypeName(property.type)) { // if type not exist in data types remove property from list
29                     console.log("ERROR: missing type " + property.type + " in dataTypes , of property ", property);
30                 } else {
31
32                     let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE
33
34                     if (newFEProp.derivedDataType == DerivedPropertyType.COMPLEX) { //Create children if prop is not simple, list, or map.
35                         newFEProp.flattenedChildren = this.createFlattenedChildren(newFEProp.type, newFEProp.name);
36                     }
37                     if (instanceInputs.length) { //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
38                         instanceInputs.filter(input => input.propertyName == newFEProp.name).forEach((input) => {
39                             newFEProp.setAsDeclared(input.inputPath); //if a path was sent, its a child prop. this param is optional
40                             this.propertiesService.disableRelatedProperties(newFEProp, input.inputPath);
41                         });
42                     }
43                     this.initValueObjectRef(newFEProp); //initialize valueObj.
44                     propertyFeArray.push(newFEProp);
45                     newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
46                     this.dataTypeService.checkForCustomBehavior(newFEProp);
47                 }    
48             });
49             instanceFePropertiesMap[instanceName] = propertyFeArray;
50
51         });
52         return instanceFePropertiesMap;
53     }
54     private createListOrMapChildrenFromValueObj = (property: PropertyFEModel) => {
55         if ((property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP)
56             && Object.keys(property.valueObj).length) {
57
58             Object.keys(property.valueObj).forEach((key) => {
59                 let newProps: Array<DerivedFEProperty> = this.createListOrMapChildren(property, key, property.valueObj[key]);
60                 property.flattenedChildren.push(...newProps);
61             });
62             
63         }
64     }
65
66     public createListOrMapChildren = (property:PropertyBEModel, key: string, valueObj: any): Array<DerivedFEProperty> => {
67         let newProps: Array<DerivedFEProperty> = [];
68         let parentProp = new DerivedFEProperty(property, property.propertiesName, true, key, valueObj);
69         newProps.push(parentProp);
70
71         if (!property.schema.property.isSimpleType) {
72             let additionalChildren:Array<DerivedFEProperty> = this.createFlattenedChildren(property.schema.property.type, parentProp.propertiesName);
73             this.assignFlattenedChildrenValues(parentProp.valueObj, additionalChildren, parentProp.propertiesName);
74             additionalChildren.forEach(prop => prop.canBeDeclared = false);
75             newProps.push(...additionalChildren);
76         }
77         return newProps;
78     }
79
80     /**
81      * Creates derivedFEProperties of a specified type and returns them.
82      */
83     private createFlattenedChildren = (type: string, parentName: string):Array<DerivedFEProperty> => {
84         let tempProps: Array<DerivedFEProperty> = [];
85         let dataTypeObj: DataTypeModel = this.dataTypeService.getDataTypeByTypeName(type);
86         this.dataTypeService.getDerivedDataTypeProperties(dataTypeObj, tempProps, parentName);
87         return tempProps;
88     }
89     
90     /* Sets the valueObj of parent property and its children. 
91     * 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.
92     */
93     public initValueObjectRef = (property: PropertyFEModel): void => {
94         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
95             property.valueObj = property.value || property.defaultValue;
96
97             if (property.isDeclared && typeof property.valueObj == 'object')  property.valueObj = JSON.stringify(property.valueObj);
98         } else {
99             if (property.derivedDataType == DerivedPropertyType.LIST) {
100                 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.
101             } else {
102                 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.
103             }
104             if (property.derivedDataType == DerivedPropertyType.COMPLEX) {
105                 this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name);
106             } else {
107                 this.createListOrMapChildrenFromValueObj(property);
108             }
109         }
110     }
111
112     /*
113     * Loops through flattened properties array and to assign values
114     * Then, convert any neccessary strings to objects, and vis-versa
115     * For list or map property, creates new children props if valueObj has values
116     */
117     public assignFlattenedChildrenValues = (parentValueJSON: any, derivedPropArray: Array<DerivedFEProperty>, parentName: string) => {
118         if (!derivedPropArray || !parentName) return;
119         derivedPropArray.forEach((prop, index) => {
120
121             let propNameInObj = prop.propertiesName.substring(prop.propertiesName.indexOf(parentName) + parentName.length + 1).split('#').join('.'); //extract everything after parent name
122             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
123             
124             if ((prop.derivedDataType == DerivedPropertyType.SIMPLE || prop.isDeclared) && typeof prop.valueObj == 'object') { //Stringify objects that should be strings
125                 prop.valueObj = JSON.stringify(prop.valueObj);
126             } else { //parse strings that should be objects
127                 if ((prop.derivedDataType == DerivedPropertyType.COMPLEX || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj != 'object') {
128                     prop.valueObj = JSON.parse(prop.valueObj || '{}');
129                 } else if (prop.derivedDataType == DerivedPropertyType.LIST && typeof prop.valueObj != 'object') {
130                     prop.valueObj = JSON.parse(prop.valueObj || '[]');
131                 }
132                 if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && Object.keys(prop.valueObj).length) {
133                     let newProps: Array<DerivedFEProperty> = [];
134                     Object.keys(prop.valueObj).forEach((key) => {
135                         newProps.push(...this.createListOrMapChildren(prop, key, prop.valueObj[key]));//create new children, assign their values, and then add to array
136                     });
137                     derivedPropArray.splice(index + 1, 0, ...newProps);
138                 }
139             }
140         });
141     }
142
143     public resetPropertyValue = (property: PropertyFEModel, newValue: string, inputPath?: string): void => {
144         property.value = newValue;
145         if (inputPath) {
146             let newProp = property.flattenedChildren.find(prop => prop.propertiesName == inputPath);
147             newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name);
148         } else {
149             this.initValueObjectRef(property);
150         }
151     }
152
153
154
155 }