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