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