Reduce the number of problems in schema-service
[aai/schema-service.git] / aai-schema-gen / src / main / java / org / onap / aai / schemagen / swagger / GenerateSwagger.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 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 package org.onap.aai.schemagen.swagger;
22
23 import com.fasterxml.jackson.dataformat.yaml.snakeyaml.Yaml;
24 import com.fasterxml.jackson.dataformat.yaml.snakeyaml.constructor.SafeConstructor;
25
26 import freemarker.template.Configuration;
27 import freemarker.template.Template;
28 import freemarker.template.TemplateException;
29
30 import java.io.BufferedReader;
31 import java.io.File;
32 import java.io.FileReader;
33 import java.io.FileWriter;
34 import java.io.IOException;
35 import java.io.Writer;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Comparator;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.LinkedHashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.TreeMap;
46 import java.util.stream.Collectors;
47
48 import org.onap.aai.setup.SchemaVersions;
49
50 public class GenerateSwagger {
51
52     public static final String LINE_SEPARATOR = System.getProperty("line.separator");
53     public static final String DEFAULT_WIKI = "";
54
55     public static final String DEFAULT_SCHEMA_DIR = "../aai-schema";
56     // if the program is run from aai-common, use this directory as default"
57     public static final String ALT_SCHEMA_DIR = "aai-schema";
58     // used to check to see if program is run from aai-schema-gen
59     public static final String DEFAULT_RUN_DIR = "aai-schema-gen";
60
61     public static SchemaVersions schemaVersions;
62
63     public SchemaVersions getSchemaVersions() {
64         return schemaVersions;
65     }
66
67     public static void main(String[] args) throws IOException, TemplateException {
68
69         // SchemaVersions schemaVersions = SpringContextAware.getBean(SchemaVersions.class);
70         String CURRENT_VERSION = schemaVersions.getDefaultVersion().toString();
71         String schemaDir = System.getProperty("aai.schema.dir");
72         String versionToGenerate = System.getProperty("aai.generate.version");
73         String wikiLink = System.getProperty("aai.wiki.link");
74         String release = System.getProperty("aai.release", "onap");
75
76         if (schemaDir == null) {
77             if (System.getProperty("user.dir") != null
78                 && !System.getProperty("user.dir").contains(DEFAULT_RUN_DIR)) {
79                 System.out
80                     .println("Warning: Schema directory is not set so using default schema dir: "
81                         + ALT_SCHEMA_DIR);
82                 schemaDir = ALT_SCHEMA_DIR;
83             } else {
84                 System.out
85                     .println("Warning: Schema directory is not set so using default schema dir: "
86                         + DEFAULT_SCHEMA_DIR);
87                 schemaDir = DEFAULT_SCHEMA_DIR;
88             }
89         }
90
91         if (versionToGenerate == null) {
92             System.out.println(
93                 "Warning: Version to generate is not set so using default versionToGenerate "
94                     + CURRENT_VERSION);
95             versionToGenerate = CURRENT_VERSION;
96         }
97
98         if (wikiLink == null) {
99             System.out.println("Warning: aai.wiki.link property is not set so using default");
100             wikiLink = DEFAULT_WIKI;
101         }
102
103         String yamlFile = schemaDir + "/src/main/resources/" + release
104             + "/aai_swagger_yaml/aai_swagger_" + versionToGenerate + ".yaml";
105         File swaggerYamlFile = new File(yamlFile);
106
107         if (!swaggerYamlFile.exists()) {
108             System.err.println("Unable to find the swagger yaml file: " + swaggerYamlFile);
109             System.exit(1);
110         }
111
112         Yaml yaml = new Yaml(new SafeConstructor());
113         Map<String, Object> swaggerMap = null;
114
115         try (BufferedReader reader = new BufferedReader(new FileReader(swaggerYamlFile))) {
116             swaggerMap = (Map<String, Object>) yaml.load(reader);
117         } catch (Exception ex) {
118             System.err
119                 .println("Unable load yaml file: " + swaggerYamlFile + " : " + ex.getMessage());
120             throw new IOException();
121         }
122
123         Map<String, Object> map = (Map<String, Object>) swaggerMap.get("paths");
124         Map<String, Object> schemaDefinitionmap =
125             (Map<String, Object>) swaggerMap.get("definitions");
126         Map<String, Object> infoMap = (Map<String, Object>) swaggerMap.get("info");
127         Map<String, List<Api>> tagMap = new LinkedHashMap<>();
128
129         List<Api> apis = convertToApi(map);
130         apis.forEach((api) -> {
131             if (!tagMap.containsKey(api.getTag())) {
132                 List<Api> newApis = new ArrayList<>();
133                 newApis.add(api);
134                 tagMap.put(api.getTag(), newApis);
135             } else {
136                 tagMap.get(api.getTag()).add(api);
137             }
138         });
139
140         Map<String, List<Api>> sortedTagMap = new TreeMap<>(tagMap);
141         sortedTagMap.forEach((key, value) -> {
142             value.sort(Comparator.comparing(Api::getPath));
143         });
144
145         Map<String, Object> resultMap = new HashMap<>();
146
147         List<Definition> definitionList = convertToDefinition(schemaDefinitionmap);
148
149         definitionList =
150             definitionList.stream().sorted(Comparator.comparing(Definition::getDefinitionName))
151                 .collect(Collectors.toList());
152
153         resultMap.put("aaiApis", tagMap);
154         resultMap.put("sortedAaiApis", sortedTagMap);
155         resultMap.put("wikiLink", wikiLink);
156         resultMap.put("definitions", definitionList);
157         resultMap.put("version", versionToGenerate);
158         if (infoMap.containsKey("description")) {
159             String infoDescription = infoMap.get("description").toString();
160
161             infoDescription = Arrays.stream(infoDescription.split("\n")).map(line -> {
162                 line = line.trim();
163                 String hyperLink = "";
164                 if (line.trim().contains("Differences versus")) {
165                     return "";
166                 }
167                 if (line.trim().contains("https://")) {
168                     int startIndex = line.indexOf("https://");
169                     int endIndex = line.lastIndexOf("/");
170                     hyperLink = line.substring(startIndex, endIndex);
171                     return String.format("<a href=\"%s\">%s</a><br/>", hyperLink, line);
172                 }
173                 return String.format("%s<br/>", line);
174             })
175
176                 .collect(Collectors.joining(LINE_SEPARATOR));
177
178             resultMap.put("description", infoDescription);
179         }
180
181         Configuration configuration = new Configuration();
182         configuration.setClassForTemplateLoading(Api.class, "/");
183         String resourcePath = "src/main/resources";
184         if (System.getProperty("user.dir") != null
185             && !System.getProperty("user.dir").contains(DEFAULT_RUN_DIR)) {
186             configuration
187                 .setDirectoryForTemplateLoading(new File(DEFAULT_RUN_DIR + "/" + resourcePath));
188         } else {
189             configuration.setDirectoryForTemplateLoading(new File(resourcePath));
190         }
191         Template template = configuration.getTemplate("swagger.html.ftl");
192
193         String outputDirStr = schemaDir + "/src/main/resources/" + release + "/aai_swagger_html";
194
195         File outputDir = new File(outputDirStr);
196
197         if (!outputDir.exists()) {
198             boolean resp = outputDir.mkdir();
199             if (!resp) {
200                 System.err.println("Unable to create the directory: " + outputDirStr);
201                 System.exit(1);
202             }
203         } else if (outputDir.isFile()) {
204             System.err.println("Unable to create the directory: " + outputDirStr
205                 + " since a filename with that string exists");
206             System.exit(1);
207         }
208
209         Writer file =
210             new FileWriter(new File(outputDirStr + "/aai_swagger_" + versionToGenerate + ".html"));
211         template.process(resultMap, file);
212     }
213
214     public static List<Api> convertToApi(Map<String, Object> pathMap) {
215
216         if (pathMap == null)
217             throw new IllegalArgumentException();
218
219         List<Api> apis = new ArrayList<>();
220
221         pathMap.forEach((pathKey, pathValue) -> {
222
223             Api api = new Api();
224             Map<String, Object> httpVerbMap = (Map<String, Object>) pathValue;
225             List<Api.HttpVerb> httpVerbs = new ArrayList<>();
226
227             api.setPath(pathKey);
228
229             httpVerbMap.forEach((httpVerbKey, httpVerbValue) -> {
230
231                 Api.HttpVerb httpVerb = new Api.HttpVerb();
232
233                 Map<String, Object> httpVerbValueMap = (Map<String, Object>) httpVerbValue;
234
235                 httpVerb.setType(httpVerbKey);
236
237                 if (httpVerbValueMap.containsKey("tags")) {
238                     httpVerb.setTags((List<String>) httpVerbValueMap.get("tags"));
239                 }
240
241                 if (httpVerbValueMap.containsKey("summary")) {
242                     httpVerb.setSummary((String) httpVerbValueMap.get("summary"));
243                 }
244
245                 if (httpVerbValueMap.containsKey("operationId")) {
246                     httpVerb.setOperationId((String) httpVerbValueMap.get("operationId"));
247                 }
248
249                 if (httpVerbValueMap.containsKey("consumes")) {
250                     httpVerb.setConsumes((List<String>) httpVerbValueMap.get("consumes"));
251                     if (httpVerb.getConsumes() != null) {
252                         httpVerb.setConsumerEnabled(true);
253                     }
254                 }
255
256                 if (httpVerbValueMap.containsKey("produces")) {
257                     httpVerb.setProduces((List<String>) httpVerbValueMap.get("produces"));
258                 }
259
260                 if (httpVerbValueMap.containsKey("parameters")) {
261                     List<Map<String, Object>> parameters =
262                         (List<Map<String, Object>>) httpVerbValueMap.get("parameters");
263                     List<Map<String, Object>> requestParameters = parameters.stream()
264                         .filter((parameter) -> !parameter.get("name").equals("body"))
265                         .collect(Collectors.toList());
266                     httpVerb.setParameters(requestParameters);
267                     if (httpVerb.getParameters() != null) {
268                         httpVerb.setParametersEnabled(true);
269                     }
270
271                     List<Map<String, Object>> requestBodyList = parameters.stream()
272                         .filter((parameter) -> parameter.get("name").equals("body"))
273                         .collect(Collectors.toList());
274
275                     Map<String, Object> requestBody = null;
276
277                     if (requestBodyList != null && requestBodyList.size() == 1) {
278                         requestBody = requestBodyList.get(0);
279                         for (String key : requestBody.keySet()) {
280                             // Filter out all the relationship links that appear in the YAML
281                             if (key.equals("description")) {
282                                 String reqBody = (String) requestBody.get(key);
283                                 if (!reqBody.replaceAll("\\[.*.json\\)", "").equals(reqBody)) {
284                                     requestBody.put(key, reqBody.replaceAll("\\[.*.json\\)", ""));
285                                 }
286                             }
287                             // Filter out all the patchDefinition links that appear in the YAML
288                             if (key.equals("schema")) {
289                                 LinkedHashMap<String, String> reqBody =
290                                     (LinkedHashMap<String, String>) requestBody.get(key);
291                                 String schema = reqBody.get("$ref");
292                                 String schemaNopatch =
293                                     schema.replace("patchDefinitions", "definitions");
294
295                                 if (!schema.equals(schemaNopatch)) {
296                                     reqBody.put("$ref", schemaNopatch);
297                                     requestBody.put(key, reqBody);
298                                 }
299                             }
300                         }
301                         httpVerb.setBodyParametersEnabled(true);
302                         httpVerb.setBodyParameters(requestBody);
303
304                         if (requestBody != null && requestBody.containsKey("schema")) {
305                             Map<String, Object> schemaMap =
306                                 (Map<String, Object>) requestBody.get("schema");
307                             if (schemaMap != null && schemaMap.containsKey("$ref")) {
308                                 String schemaLink = schemaMap.get("$ref").toString();
309                                 httpVerb.setSchemaLink(schemaLink);
310                                 int retCode = schemaLink.lastIndexOf('/');
311                                 if (retCode != -1 && retCode != schemaLink.length()) {
312                                     httpVerb.setSchemaType(schemaLink.substring(retCode));
313                                 }
314                             }
315                         }
316                     }
317                 }
318
319                 if (httpVerbValueMap.containsKey("responses")) {
320
321                     List<Api.HttpVerb.Response> responses = new ArrayList<Api.HttpVerb.Response>();
322
323                     Map<String, Object> responsesMap =
324                         (Map<String, Object>) httpVerbValueMap.get("responses");
325
326                     responsesMap.entrySet().stream()
327                         .filter((res) -> !"default".equalsIgnoreCase(res.getKey()))
328                         .forEach((responseMap) -> {
329
330                             Map<String, Object> responseValueMap =
331                                 (Map<String, Object>) responseMap.getValue();
332
333                             Api.HttpVerb.Response response = new Api.HttpVerb.Response();
334
335                             response.setResponseCode(responseMap.getKey());
336                             response.setDescription((String) responseValueMap.get("description"));
337                             response.setVersion((String) responseValueMap.get("version"));
338
339                             if (responseValueMap != null
340                                 && responseValueMap.containsKey("schema")) {
341                                 Map<String, Object> schemaMap =
342                                     (Map<String, Object>) responseValueMap.get("schema");
343                                 if (schemaMap != null && schemaMap.containsKey("$ref")) {
344                                     String schemaLink = schemaMap.get("$ref").toString();
345                                     httpVerb.setHasReturnSchema(true);
346                                     // Filter out all the getDefinition links that appear in the
347                                     // YAML
348                                     httpVerb.setReturnSchemaLink(
349                                         schemaLink.replace("getDefinitions", "definitions"));
350                                     int retCode = schemaLink.lastIndexOf('/');
351                                     if (retCode != -1 && retCode != schemaLink.length()) {
352                                         httpVerb
353                                             .setReturnSchemaObject(schemaLink.substring(retCode));
354                                     }
355                                 }
356                             }
357
358                             responses.add(response);
359                         });
360
361                     httpVerb.setResponses(responses);
362                 }
363
364                 httpVerbs.add(httpVerb);
365             });
366
367             api.setHttpMethods(httpVerbs);
368             apis.add(api);
369         });
370
371         return apis;
372     }
373
374     public static List<Definition> convertToDefinition(Map<String, Object> definitionMap) {
375
376         if (definitionMap == null)
377             throw new IllegalArgumentException();
378
379         List<Definition> defintionsList = new ArrayList<>();
380
381         definitionMap.entrySet().forEach((entry) -> {
382
383             Definition definition = new Definition();
384             String key = entry.getKey();
385             Map<String, Object> valueMap = (Map<String, Object>) entry.getValue();
386
387             definition.setDefinitionName(key);
388
389             if (valueMap.containsKey("description")) {
390                 String description = valueMap.get("description").toString();
391                 description = formatDescription(description);
392                 definition.setDefinitionDescription(description);
393                 definition.setHasDescription(true);
394             }
395
396             List<Definition.Property> definitionProperties = new ArrayList<>();
397
398             List<String> requiredProperties = (valueMap.get("required") == null) ? new ArrayList<>()
399                 : (List<String>) valueMap.get("required");
400
401             Set<String> requiredPropsSet = new HashSet<>(requiredProperties);
402
403             valueMap.entrySet().stream().filter((e) -> "properties".equals(e.getKey()))
404                 .forEach((propertyEntries) -> {
405                     Map<String, Object> propertyRealEntries =
406                         (Map<String, Object>) propertyEntries.getValue();
407                     propertyRealEntries.forEach((propertyKey, value) -> {
408                         Definition.Property definitionProperty = new Definition.Property();
409                         if (requiredPropsSet.contains(propertyKey)) {
410                             definitionProperty.setRequired(true);
411                         }
412                         definitionProperty.setPropertyName(propertyKey);
413                         Map<String, Object> definitionPropertyMap = (Map<String, Object>) value;
414
415                         if (definitionPropertyMap.containsKey("description")) {
416                             definitionProperty.setPropertyDescription(
417                                 definitionPropertyMap.get("description").toString());
418                             definitionProperty.setHasPropertyDescription(true);
419                         }
420                         if (definitionPropertyMap.containsKey("type")) {
421                             String type = definitionPropertyMap.get("type").toString();
422                             definitionProperty.setPropertyType(type);
423                             definitionProperty.setHasType(true);
424                             if ("array".equals(type)) {
425                                 definitionProperty.setPropertyType("object[]");
426                                 if (!definitionPropertyMap.containsKey("items")) {
427                                     throw new RuntimeException(
428                                         "Unable to find the property items even though the type is array for "
429                                             + propertyKey);
430                                 } else {
431                                     Map<String, Object> itemMap =
432                                         (Map<String, Object>) definitionPropertyMap.get("items");
433                                     if (itemMap.containsKey("$ref")) {
434                                         definitionProperty.setHasPropertyReference(true);
435                                         String refItem = itemMap.get("$ref").toString();
436                                         int retCode = refItem.lastIndexOf('/');
437                                         if (retCode != -1 && retCode != refItem.length()) {
438                                             definitionProperty.setPropertyReferenceObjectName(
439                                                 refItem.substring(retCode + 1));
440                                         }
441                                         definitionProperty.setPropertyReference(refItem);
442                                     }
443                                 }
444                             } else {
445                                 if (definitionPropertyMap.containsKey("$ref")) {
446                                     definitionProperty.setHasPropertyReference(true);
447                                     String refItem = definitionPropertyMap.get("$ref").toString();
448                                     int retCode = refItem.lastIndexOf('/');
449                                     if (retCode != -1 && retCode != refItem.length()) {
450                                         definitionProperty.setPropertyReferenceObjectName(
451                                             refItem.substring(retCode + 1));
452                                     }
453                                     definitionProperty.setPropertyReference(refItem);
454                                 }
455                             }
456                         }
457                         definitionProperties.add(definitionProperty);
458                     });
459                 });
460
461             definition.setPropertyList(definitionProperties);
462
463             List<Definition.Property> schemaProperties = definitionProperties.stream()
464                 .filter(Definition.Property::isHasPropertyReference).collect(Collectors.toList());
465
466             List<Definition.Property> regularProperties = definitionProperties.stream()
467                 .filter((o) -> !o.isHasPropertyReference()).collect(Collectors.toList());
468
469             definition.setRegularPropertyList(regularProperties);
470             definition.setSchemaPropertyList(schemaProperties);
471
472             defintionsList.add(definition);
473         });
474         return defintionsList;
475     }
476
477     public static String formatDescription(String description) {
478
479         description = Arrays.stream(description.split("\n")).map((line) -> {
480             line = line.trim();
481             if (line.contains("######")) {
482                 line = line.replaceAll("#", "");
483                 line = line.trim();
484                 String headerId = line.toLowerCase().replaceAll("\\s", "-");
485
486                 if (line.contains("Related Nodes")) {
487                     return String.format("<h6 id=\"%s\">%s</h6>%s<ul>", headerId, line,
488                         LINE_SEPARATOR);
489                 } else {
490                     return String.format("<h6 id=\"%s\">%s</h6>", headerId, line);
491                 }
492             } else if (line.startsWith("-")) {
493                 line = line.replaceFirst("-", "");
494                 line = line.trim();
495                 return String.format("<li>%s</li>", line);
496             } else {
497                 return String.format("<p>%s</p>", line);
498             }
499         }).collect(Collectors.joining(LINE_SEPARATOR));
500
501         if (description.contains("<ul>")) {
502             description = description + "</ul>";
503         }
504
505         return description;
506     }
507
508 }