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