Fix data convert error for tree node
[sdc/sdc-workflow-designer.git] / sdc-workflow-designer-ui / src / app / services / swagger-tree-converter.service.ts
1 /*******************************************************************************
2  * Copyright (c) 2017 ZTE Corporation.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * and the Apache License 2.0 which both accompany this distribution,
6  * and are available at http://www.eclipse.org/legal/epl-v10.html
7  * and http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Contributors:
10  *     ZTE - initial API and implementation and/or initial documentation
11  *******************************************************************************/
12
13 import { Injectable } from '@angular/core';
14 import { TreeNode } from 'primeng/primeng';
15
16 import { ValueSource } from '../model/value-source.enum';
17 import { WorkflowUtil } from '../util/workflow-util';
18 import { Swagger } from "../model/swagger";
19 import { WorkflowConfigService } from "./workflow-config.service";
20
21 @Injectable()
22 export class SwaggerTreeConverterService {
23
24     private swagger: Swagger;
25
26     constructor(private workflowConfigService: WorkflowConfigService) {
27
28     }
29
30     public schema2TreeNode(swagger: Swagger, key: string | number, schema: any, value?: any): any {
31       this.swagger = swagger;
32         if (schema.$ref) {
33             const treeNode = this.getTreeNodeBySwaggerDefinition(key, schema, value);
34             return treeNode;
35         } else {
36             value = this.getInitValue4Param(schema, value);
37             return this.parameter2TreeNode(key, schema, value);
38         }
39     }
40
41     private getTreeNodeBySwaggerDefinition(key: string | number, schema: any, value: any): TreeNode {
42         const swaggerDefinition = this.workflowConfigService.getDefinition(this.swagger, schema.$ref);
43
44         const definitionCopy = WorkflowUtil.deepClone(swaggerDefinition);
45
46         value = this.getInitValue4Param(definitionCopy, value);
47
48         return this.schema2TreeNode(this.swagger, key, definitionCopy, value);
49     }
50
51     private getInitValue4Param(definition: any, value: any) {
52       if (definition.$ref) {
53         definition = this.workflowConfigService.getDefinition(this.swagger, definition.$ref);
54       }
55       if (definition.type === 'object') {
56         return this.getInitValue4Object(value);
57       } else if (definition.type === 'array') {
58         return this.getInitValue4Array(value);
59       } else { // primary type
60         return this.getInitValue4Primate(value);
61       }
62     }
63
64     private getInitValue4Object(value: any) {
65       const newValue = {
66         value: {},
67         valueSource: ValueSource[ValueSource.Definition]
68       };
69
70       if(!value) {
71         return newValue;
72       } else {
73         if(value.valueSource !== ValueSource[ValueSource.Definition]) {
74           return value;
75         } else {
76           if(!value.value || typeof value.value !== 'object') {
77             value.value = {};
78           }
79           return value;
80         }
81       }
82     }
83
84     private getInitValue4Array(value: any) {
85       const newValue = {
86         value: [],
87         valueSource: ValueSource[ValueSource.Definition]
88       };
89
90       if(!value) {
91         return newValue;
92       } else {
93         if(value.valueSource !== ValueSource[ValueSource.Definition]) {
94           return value;
95         } else {
96           if(!(value.value instanceof Array)) {
97             value.value = [];
98           }
99           return value;
100         }
101       }
102     }
103
104     private getInitValue4Primate(value: any) {
105       const newValue = {
106         value: '',
107         valueSource: ValueSource[ValueSource.String]
108       };
109
110       if(!value) {
111         return newValue;
112       } else {
113         if(typeof value.value === 'object') {
114           value.value = '';
115         }
116         return value;
117       }
118     }
119
120     private parameter2TreeNode(name: string | number, definition: any, value: any): any {
121         const nodeType = this.getTreeNodeType(definition);
122
123         const node = {
124             label: name,
125             type: nodeType,
126             required: definition.required,
127             children: [],
128             definition: definition,
129             value: value,
130         };
131
132         if(value.valueSource === ValueSource[ValueSource.Definition]) {
133           if (definition.type === 'object') {
134               node.children = this.getPropertyFromObject(definition, value.value);
135           } else if (definition.type === 'array') {
136               this.setChildrenForArray(definition, value.value);
137           }
138         }
139
140         return node;
141     }
142
143     private getTreeNodeType(param: any): string {
144         const type = param.type;
145         if (type === 'array') {
146             return 'array';
147         } else if (type === 'object') {
148             if (param.additionalProperties) {
149                 return 'map';
150             } else {
151                 return 'object';
152             }
153         } else {
154             return 'default';
155         }
156     }
157
158     private setChildrenForArray(definition: any, value: any[]): any[] {
159       const children = [];
160       value.forEach((itemValue, index) => {
161             const itemCopy = WorkflowUtil.deepClone(definition.items);
162             children.push(this.schema2TreeNode(this.swagger, index, itemCopy, itemValue));
163         });
164
165         return children;
166     }
167
168     private getPropertyFromObject(definition: any, value: any): TreeNode[] {
169         if (definition.properties) {
170             return this.getPropertyFromSimpleObject(definition.properties, value, definition.required);
171         } else if (definition.additionalProperties) {
172             return this.getPropertyFromMapOrDictionary(definition.additionalProperties, value);
173         } else {
174             console.log('getPropertyFromObject() return [], param is:' + JSON.stringify(definition));
175             return [];
176         }
177
178     }
179
180     private getPropertyFromSimpleObject(properties: any, objectValue: any, required: string[]): TreeNode[] {
181         const treeNodes: TreeNode[] = [];
182         for (const key in properties) {
183           let property = properties[key];
184             // init required property
185             property.required = false;
186             if (Array.isArray(required)) {
187                 for (let index = 0; index < required.length; index++) {
188                     if (required[index] === key) {
189                         property.required = true;
190                         break;
191                     }
192                 }
193             }
194
195             objectValue[key] = this.getInitValue4Param(property, objectValue[key]);
196
197             const treeNode = this.schema2TreeNode(this.swagger, key, property, objectValue[key]);
198             treeNodes.push(treeNode);
199         }
200         return treeNodes;
201     }
202
203     private getPropertyFromMapOrDictionary(additionalProperties: any, mapOrDictionary: any): TreeNode[] {
204         const treeNodes: TreeNode[] = [];
205         for (const key in mapOrDictionary) {
206             const propertyCopy = WorkflowUtil.deepClone(additionalProperties);
207             propertyCopy.value = mapOrDictionary[key];
208
209             const treeNode = this.schema2TreeNode(this.swagger, key, propertyCopy, propertyCopy.value);
210             treeNode.keyEditable = true;
211             treeNodes.push(treeNode);
212
213             if (mapOrDictionary[key] !== propertyCopy.value) {
214                 mapOrDictionary[key] = propertyCopy.value;
215             }
216         }
217         return treeNodes;
218     }
219 }