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