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