2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.aai.schemagen.swagger;
23 import com.fasterxml.jackson.dataformat.yaml.snakeyaml.Yaml;
24 import com.fasterxml.jackson.dataformat.yaml.snakeyaml.constructor.SafeConstructor;
26 import freemarker.template.Configuration;
27 import freemarker.template.Template;
28 import freemarker.template.TemplateException;
30 import java.io.BufferedReader;
32 import java.io.FileReader;
33 import java.io.FileWriter;
34 import java.io.IOException;
35 import java.io.Writer;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Comparator;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.LinkedHashMap;
42 import java.util.List;
45 import java.util.TreeMap;
46 import java.util.stream.Collectors;
48 import org.onap.aai.setup.SchemaVersions;
50 public class GenerateSwagger {
52 public static final String LINE_SEPARATOR = System.getProperty("line.separator");
53 public static final String DEFAULT_WIKI = "";
55 public static final String DEFAULT_SCHEMA_DIR = "../aai-schema";
56 // if the program is run from aai-common, use this directory as default"
57 public static final String ALT_SCHEMA_DIR = "aai-schema";
58 // used to check to see if program is run from aai-schema-gen
59 public static final String DEFAULT_RUN_DIR = "aai-schema-gen";
61 public static SchemaVersions schemaVersions;
63 public SchemaVersions getSchemaVersions() {
64 return schemaVersions;
67 public static void main(String[] args) throws IOException, TemplateException {
69 // SchemaVersions schemaVersions = SpringContextAware.getBean(SchemaVersions.class);
70 String CURRENT_VERSION = schemaVersions.getDefaultVersion().toString();
71 String schemaDir = System.getProperty("aai.schema.dir");
72 String versionToGenerate = System.getProperty("aai.generate.version");
73 String wikiLink = System.getProperty("aai.wiki.link");
74 String release = System.getProperty("aai.release", "onap");
76 if (schemaDir == null) {
77 if (System.getProperty("user.dir") != null
78 && !System.getProperty("user.dir").contains(DEFAULT_RUN_DIR)) {
80 .println("Warning: Schema directory is not set so using default schema dir: "
82 schemaDir = ALT_SCHEMA_DIR;
85 .println("Warning: Schema directory is not set so using default schema dir: "
86 + DEFAULT_SCHEMA_DIR);
87 schemaDir = DEFAULT_SCHEMA_DIR;
91 if (versionToGenerate == null) {
93 "Warning: Version to generate is not set so using default versionToGenerate "
95 versionToGenerate = CURRENT_VERSION;
98 if (wikiLink == null) {
99 System.out.println("Warning: aai.wiki.link property is not set so using default");
100 wikiLink = DEFAULT_WIKI;
103 String yamlFile = schemaDir + "/src/main/resources/" + release
104 + "/aai_swagger_yaml/aai_swagger_" + versionToGenerate + ".yaml";
105 File swaggerYamlFile = new File(yamlFile);
107 if (!swaggerYamlFile.exists()) {
108 System.err.println("Unable to find the swagger yaml file: " + swaggerYamlFile);
112 Yaml yaml = new Yaml(new SafeConstructor());
113 Map<String, Object> swaggerMap = null;
115 try (BufferedReader reader = new BufferedReader(new FileReader(swaggerYamlFile))) {
116 swaggerMap = (Map<String, Object>) yaml.load(reader);
117 } catch (Exception ex) {
119 .println("Unable load yaml file: " + swaggerYamlFile + " : " + ex.getMessage());
120 throw new IOException();
123 Map<String, Object> map = (Map<String, Object>) swaggerMap.get("paths");
124 Map<String, Object> schemaDefinitionmap =
125 (Map<String, Object>) swaggerMap.get("definitions");
126 Map<String, Object> infoMap = (Map<String, Object>) swaggerMap.get("info");
127 Map<String, List<Api>> tagMap = new LinkedHashMap<>();
129 List<Api> apis = convertToApi(map);
130 apis.forEach((api) -> {
131 if (!tagMap.containsKey(api.getTag())) {
132 List<Api> newApis = new ArrayList<>();
134 tagMap.put(api.getTag(), newApis);
136 tagMap.get(api.getTag()).add(api);
140 Map<String, List<Api>> sortedTagMap = new TreeMap<>(tagMap);
141 sortedTagMap.forEach((key, value) -> {
142 value.sort(Comparator.comparing(Api::getPath));
145 Map<String, Object> resultMap = new HashMap<>();
147 List<Definition> definitionList = convertToDefinition(schemaDefinitionmap);
150 definitionList.stream().sorted(Comparator.comparing(Definition::getDefinitionName))
151 .collect(Collectors.toList());
153 resultMap.put("aaiApis", tagMap);
154 resultMap.put("sortedAaiApis", sortedTagMap);
155 resultMap.put("wikiLink", wikiLink);
156 resultMap.put("definitions", definitionList);
157 resultMap.put("version", versionToGenerate);
158 if (infoMap.containsKey("description")) {
159 String infoDescription = infoMap.get("description").toString();
161 infoDescription = Arrays.stream(infoDescription.split("\n")).map(line -> {
163 String hyperLink = "";
164 if (line.trim().contains("Differences versus")) {
167 if (line.trim().contains("https://")) {
168 int startIndex = line.indexOf("https://");
169 int endIndex = line.lastIndexOf("/");
170 hyperLink = line.substring(startIndex, endIndex);
171 return String.format("<a href=\"%s\">%s</a><br/>", hyperLink, line);
173 return String.format("%s<br/>", line);
176 .collect(Collectors.joining(LINE_SEPARATOR));
178 resultMap.put("description", infoDescription);
181 Configuration configuration = new Configuration();
182 configuration.setClassForTemplateLoading(Api.class, "/");
183 String resourcePath = "src/main/resources";
184 if (System.getProperty("user.dir") != null
185 && !System.getProperty("user.dir").contains(DEFAULT_RUN_DIR)) {
187 .setDirectoryForTemplateLoading(new File(DEFAULT_RUN_DIR + "/" + resourcePath));
189 configuration.setDirectoryForTemplateLoading(new File(resourcePath));
191 Template template = configuration.getTemplate("swagger.html.ftl");
193 String outputDirStr = schemaDir + "/src/main/resources/" + release + "/aai_swagger_html";
195 File outputDir = new File(outputDirStr);
197 if (!outputDir.exists()) {
198 boolean resp = outputDir.mkdir();
200 System.err.println("Unable to create the directory: " + outputDirStr);
203 } else if (outputDir.isFile()) {
204 System.err.println("Unable to create the directory: " + outputDirStr
205 + " since a filename with that string exists");
210 new FileWriter(new File(outputDirStr + "/aai_swagger_" + versionToGenerate + ".html"));
211 template.process(resultMap, file);
214 public static List<Api> convertToApi(Map<String, Object> pathMap) {
217 throw new IllegalArgumentException();
219 List<Api> apis = new ArrayList<>();
221 pathMap.forEach((pathKey, pathValue) -> {
224 Map<String, Object> httpVerbMap = (Map<String, Object>) pathValue;
225 List<Api.HttpVerb> httpVerbs = new ArrayList<>();
227 api.setPath(pathKey);
229 httpVerbMap.forEach((httpVerbKey, httpVerbValue) -> {
231 Api.HttpVerb httpVerb = new Api.HttpVerb();
233 Map<String, Object> httpVerbValueMap = (Map<String, Object>) httpVerbValue;
235 httpVerb.setType(httpVerbKey);
237 if (httpVerbValueMap.containsKey("tags")) {
238 httpVerb.setTags((List<String>) httpVerbValueMap.get("tags"));
241 if (httpVerbValueMap.containsKey("summary")) {
242 httpVerb.setSummary((String) httpVerbValueMap.get("summary"));
245 if (httpVerbValueMap.containsKey("operationId")) {
246 httpVerb.setOperationId((String) httpVerbValueMap.get("operationId"));
249 if (httpVerbValueMap.containsKey("consumes")) {
250 httpVerb.setConsumes((List<String>) httpVerbValueMap.get("consumes"));
251 if (httpVerb.getConsumes() != null) {
252 httpVerb.setConsumerEnabled(true);
256 if (httpVerbValueMap.containsKey("produces")) {
257 httpVerb.setProduces((List<String>) httpVerbValueMap.get("produces"));
260 if (httpVerbValueMap.containsKey("parameters")) {
261 List<Map<String, Object>> parameters =
262 (List<Map<String, Object>>) httpVerbValueMap.get("parameters");
263 List<Map<String, Object>> requestParameters = parameters.stream()
264 .filter((parameter) -> !parameter.get("name").equals("body"))
265 .collect(Collectors.toList());
266 httpVerb.setParameters(requestParameters);
267 if (httpVerb.getParameters() != null) {
268 httpVerb.setParametersEnabled(true);
271 List<Map<String, Object>> requestBodyList = parameters.stream()
272 .filter((parameter) -> parameter.get("name").equals("body"))
273 .collect(Collectors.toList());
275 Map<String, Object> requestBody = null;
277 if (requestBodyList != null && requestBodyList.size() == 1) {
278 requestBody = requestBodyList.get(0);
279 for (String key : requestBody.keySet()) {
280 // Filter out all the relationship links that appear in the YAML
281 if (key.equals("description")) {
282 String reqBody = (String) requestBody.get(key);
283 if (!reqBody.replaceAll("\\[.*.json\\)", "").equals(reqBody)) {
284 requestBody.put(key, reqBody.replaceAll("\\[.*.json\\)", ""));
287 // Filter out all the patchDefinition links that appear in the YAML
288 if (key.equals("schema")) {
289 LinkedHashMap<String, String> reqBody =
290 (LinkedHashMap<String, String>) requestBody.get(key);
291 String schema = reqBody.get("$ref");
292 String schemaNopatch =
293 schema.replace("patchDefinitions", "definitions");
295 if (!schema.equals(schemaNopatch)) {
296 reqBody.put("$ref", schemaNopatch);
297 requestBody.put(key, reqBody);
301 httpVerb.setBodyParametersEnabled(true);
302 httpVerb.setBodyParameters(requestBody);
304 if (requestBody != null && requestBody.containsKey("schema")) {
305 Map<String, Object> schemaMap =
306 (Map<String, Object>) requestBody.get("schema");
307 if (schemaMap != null && schemaMap.containsKey("$ref")) {
308 String schemaLink = schemaMap.get("$ref").toString();
309 httpVerb.setSchemaLink(schemaLink);
310 int retCode = schemaLink.lastIndexOf('/');
311 if (retCode != -1 && retCode != schemaLink.length()) {
312 httpVerb.setSchemaType(schemaLink.substring(retCode));
319 if (httpVerbValueMap.containsKey("responses")) {
321 List<Api.HttpVerb.Response> responses = new ArrayList<Api.HttpVerb.Response>();
323 Map<String, Object> responsesMap =
324 (Map<String, Object>) httpVerbValueMap.get("responses");
326 responsesMap.entrySet().stream()
327 .filter((res) -> !"default".equalsIgnoreCase(res.getKey()))
328 .forEach((responseMap) -> {
330 Map<String, Object> responseValueMap =
331 (Map<String, Object>) responseMap.getValue();
333 Api.HttpVerb.Response response = new Api.HttpVerb.Response();
335 response.setResponseCode(responseMap.getKey());
336 response.setDescription((String) responseValueMap.get("description"));
337 response.setVersion((String) responseValueMap.get("version"));
339 if (responseValueMap != null
340 && responseValueMap.containsKey("schema")) {
341 Map<String, Object> schemaMap =
342 (Map<String, Object>) responseValueMap.get("schema");
343 if (schemaMap != null && schemaMap.containsKey("$ref")) {
344 String schemaLink = schemaMap.get("$ref").toString();
345 httpVerb.setHasReturnSchema(true);
346 // Filter out all the getDefinition links that appear in the
348 httpVerb.setReturnSchemaLink(
349 schemaLink.replace("getDefinitions", "definitions"));
350 int retCode = schemaLink.lastIndexOf('/');
351 if (retCode != -1 && retCode != schemaLink.length()) {
353 .setReturnSchemaObject(schemaLink.substring(retCode));
358 responses.add(response);
361 httpVerb.setResponses(responses);
364 httpVerbs.add(httpVerb);
367 api.setHttpMethods(httpVerbs);
374 public static List<Definition> convertToDefinition(Map<String, Object> definitionMap) {
376 if (definitionMap == null)
377 throw new IllegalArgumentException();
379 List<Definition> defintionsList = new ArrayList<>();
381 definitionMap.entrySet().forEach((entry) -> {
383 Definition definition = new Definition();
384 String key = entry.getKey();
385 Map<String, Object> valueMap = (Map<String, Object>) entry.getValue();
387 definition.setDefinitionName(key);
389 if (valueMap.containsKey("description")) {
390 String description = valueMap.get("description").toString();
391 description = formatDescription(description);
392 definition.setDefinitionDescription(description);
393 definition.setHasDescription(true);
396 List<Definition.Property> definitionProperties = new ArrayList<>();
398 List<String> requiredProperties = (valueMap.get("required") == null) ? new ArrayList<>()
399 : (List<String>) valueMap.get("required");
401 Set<String> requiredPropsSet = new HashSet<>(requiredProperties);
403 valueMap.entrySet().stream().filter((e) -> "properties".equals(e.getKey()))
404 .forEach((propertyEntries) -> {
405 Map<String, Object> propertyRealEntries =
406 (Map<String, Object>) propertyEntries.getValue();
407 propertyRealEntries.forEach((propertyKey, value) -> {
408 Definition.Property definitionProperty = new Definition.Property();
409 if (requiredPropsSet.contains(propertyKey)) {
410 definitionProperty.setRequired(true);
412 definitionProperty.setPropertyName(propertyKey);
413 Map<String, Object> definitionPropertyMap = (Map<String, Object>) value;
415 if (definitionPropertyMap.containsKey("description")) {
416 definitionProperty.setPropertyDescription(
417 definitionPropertyMap.get("description").toString());
418 definitionProperty.setHasPropertyDescription(true);
420 if (definitionPropertyMap.containsKey("type")) {
421 String type = definitionPropertyMap.get("type").toString();
422 definitionProperty.setPropertyType(type);
423 definitionProperty.setHasType(true);
424 if ("array".equals(type)) {
425 definitionProperty.setPropertyType("object[]");
426 if (!definitionPropertyMap.containsKey("items")) {
427 throw new RuntimeException(
428 "Unable to find the property items even though the type is array for "
431 Map<String, Object> itemMap =
432 (Map<String, Object>) definitionPropertyMap.get("items");
433 if (itemMap.containsKey("$ref")) {
434 definitionProperty.setHasPropertyReference(true);
435 String refItem = itemMap.get("$ref").toString();
436 int retCode = refItem.lastIndexOf('/');
437 if (retCode != -1 && retCode != refItem.length()) {
438 definitionProperty.setPropertyReferenceObjectName(
439 refItem.substring(retCode + 1));
441 definitionProperty.setPropertyReference(refItem);
445 if (definitionPropertyMap.containsKey("$ref")) {
446 definitionProperty.setHasPropertyReference(true);
447 String refItem = definitionPropertyMap.get("$ref").toString();
448 int retCode = refItem.lastIndexOf('/');
449 if (retCode != -1 && retCode != refItem.length()) {
450 definitionProperty.setPropertyReferenceObjectName(
451 refItem.substring(retCode + 1));
453 definitionProperty.setPropertyReference(refItem);
457 definitionProperties.add(definitionProperty);
461 definition.setPropertyList(definitionProperties);
463 List<Definition.Property> schemaProperties = definitionProperties.stream()
464 .filter(Definition.Property::isHasPropertyReference).collect(Collectors.toList());
466 List<Definition.Property> regularProperties = definitionProperties.stream()
467 .filter((o) -> !o.isHasPropertyReference()).collect(Collectors.toList());
469 definition.setRegularPropertyList(regularProperties);
470 definition.setSchemaPropertyList(schemaProperties);
472 defintionsList.add(definition);
474 return defintionsList;
477 public static String formatDescription(String description) {
479 description = Arrays.stream(description.split("\n")).map((line) -> {
481 if (line.contains("######")) {
482 line = line.replaceAll("#", "");
484 String headerId = line.toLowerCase().replaceAll("\\s", "-");
486 if (line.contains("Related Nodes")) {
487 return String.format("<h6 id=\"%s\">%s</h6>%s<ul>", headerId, line,
490 return String.format("<h6 id=\"%s\">%s</h6>", headerId, line);
492 } else if (line.startsWith("-")) {
493 line = line.replaceFirst("-", "");
495 return String.format("<li>%s</li>", line);
497 return String.format("<p>%s</p>", line);
499 }).collect(Collectors.joining(LINE_SEPARATOR));
501 if (description.contains("<ul>")) {
502 description = description + "</ul>";