From 1506adc20a9e6b22da60fdb66cc4a5512257545e Mon Sep 17 00:00:00 2001 From: Hugo Mercado Date: Tue, 9 Jan 2018 15:49:47 -0500 Subject: [PATCH] added option to read arguments from a JSON or YAML file. --- .../java/io/swagger/codegen/CLIHelper.java | 84 ++++++++++- .../java/io/swagger/codegen/cmd/Generate.java | 140 ++++++++++++++---- .../src/main/resources/oas3.yaml | 6 + .../io/swagger/codegen/cmd/GenerateTest.java | 89 +++++++++++ .../src/test/resources/args.json | 7 + .../src/test/resources/args.yaml | 7 + 6 files changed, 301 insertions(+), 32 deletions(-) create mode 100644 modules/swagger-codegen-cli/src/test/resources/args.json create mode 100644 modules/swagger-codegen-cli/src/test/resources/args.yaml diff --git a/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/CLIHelper.java b/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/CLIHelper.java index e3aa3887dab3..6949c249cbba 100644 --- a/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/CLIHelper.java +++ b/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/CLIHelper.java @@ -1,5 +1,8 @@ package io.swagger.codegen; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import com.google.common.io.Resources; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.BooleanSchema; @@ -11,14 +14,16 @@ import org.apache.commons.io.Charsets; import org.apache.commons.lang3.StringUtils; import java.io.IOException; +import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; -class CLIHelper { +public class CLIHelper { static String loadResourceOAS3File() { URL url = Resources.getResource("oas3.yaml"); @@ -81,7 +86,7 @@ class CLIHelper { return null; } - static Map createOptionValueMap(Schema schema, Map inputArgs) { + public static Map createOptionValueMap(Schema schema, Map inputArgs) { if(inputArgs == null || inputArgs.isEmpty()) { return null; } @@ -138,6 +143,43 @@ class CLIHelper { return optionValueMap; } + public static Map createOptionValueMap(JsonNode node) { + final Map optionValueMap = new HashMap<>(); + Iterator fieldNames = node.fieldNames(); + while (fieldNames.hasNext()) { + String argument = fieldNames.next(); + JsonNode valueNode = node.findValue(argument); + if (valueNode.isBoolean()) { + optionValueMap.put(argument, valueNode.booleanValue()); + } + else if (valueNode.isShort() || valueNode.isInt()) { + optionValueMap.put(argument, valueNode.intValue()); + } + else if (valueNode.isLong()) { + optionValueMap.put(argument, valueNode.longValue()); + } + else if (valueNode.isFloat()) { + optionValueMap.put(argument, valueNode.floatValue()); + } + else if (valueNode.isDouble()) { + optionValueMap.put(argument, valueNode.doubleValue()); + } + else if (valueNode.isArray()) { + String inputElements = valueNode.toString() + .replace("[", StringUtils.EMPTY) + .replace("]", StringUtils.EMPTY) + .replace("\"", StringUtils.EMPTY) + .replace(" ", StringUtils.EMPTY); + final List values = new ArrayList<>(Arrays.asList(inputElements.split(","))); + optionValueMap.put(argument, values); + } else { + optionValueMap.put(argument, valueNode.toString() + .replace("\"", StringUtils.EMPTY)); + } + } + return optionValueMap; + } + private static String fixOptionName(String option) { option = option.substring(countDashes(option)); return option.replace("-", "_"); @@ -151,4 +193,42 @@ class CLIHelper { } return 0; } + + public static boolean isValidJson(String content) { + + if (StringUtils.isBlank(content)) { + return false; + } + try { + new ObjectMapper().readTree(content); + return true; + } catch (IOException ex) { + return false; + } + } + + public static boolean isValidYaml(String content) { + if (StringUtils.isBlank(content)) { + return false; + } + try { + new YAMLMapper().readTree(content); + return true; + } catch (IOException ex) { + return false; + } + } + + public static boolean isValidURL(String urlStr) { + if (StringUtils.isBlank(urlStr)) { + return false; + } + try { + URI uri = new URI(urlStr); + return uri.getScheme().toLowerCase().startsWith("http"); + } + catch (Exception e) { + return false; + } + } } diff --git a/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/cmd/Generate.java b/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/cmd/Generate.java index 7ed93e41fce2..f162da330af4 100644 --- a/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/cmd/Generate.java +++ b/modules/swagger-codegen-cli/src/main/java/io/swagger/codegen/cmd/Generate.java @@ -1,14 +1,29 @@ package io.swagger.codegen.cmd; +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.codegen.CLIHelper; import io.swagger.codegen.ClientOptInput; import io.swagger.codegen.DefaultGenerator; import io.swagger.codegen.config.CodegenConfigurator; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.parser.util.RemoteUrl; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import static io.swagger.codegen.CLIHelper.isValidJson; +import static io.swagger.codegen.CLIHelper.isValidURL; +import static io.swagger.codegen.CLIHelper.isValidYaml; import static io.swagger.codegen.config.CodegenConfiguratorUtils.applyAdditionalPropertiesKvpList; import static io.swagger.codegen.config.CodegenConfiguratorUtils.applyImportMappingsKvpList; import static io.swagger.codegen.config.CodegenConfiguratorUtils.applyInstantiationTypesKvpList; @@ -26,36 +41,37 @@ public class Generate implements Runnable { public static final Logger LOG = LoggerFactory.getLogger(Generate.class); - private Boolean verbose; - private String lang; - private String output = ""; - private String spec; - private String templateDir; - private String auth; - private List systemProperties = new ArrayList<>(); - private String configFile; - private Boolean skipOverwrite; - private String apiPackage; - private String modelPackage; - private String modelNamePrefix; - private String modelNameSuffix; - private List instantiationTypes = new ArrayList<>(); - private List typeMappings = new ArrayList<>(); - private List additionalProperties = new ArrayList<>(); - private List languageSpecificPrimitives = new ArrayList<>(); - private List importMappings = new ArrayList<>(); - private String invokerPackage; - private String groupId; - private String artifactId; - private String artifactVersion; - private String library; - private String gitUserId; - private String gitRepoId; - private String releaseNote; - private String httpUserAgent; - private List reservedWordsMappings = new ArrayList<>(); - private String ignoreFileOverride; - private Boolean removeOperationIdPrefix; + protected Boolean verbose; + protected String lang; + protected String output = ""; + protected String spec; + protected String templateDir; + protected String auth; + protected List systemProperties = new ArrayList<>(); + protected String configFile; + protected Boolean skipOverwrite; + protected String apiPackage; + protected String modelPackage; + protected String modelNamePrefix; + protected String modelNameSuffix; + protected List instantiationTypes = new ArrayList<>(); + protected List typeMappings = new ArrayList<>(); + protected List additionalProperties = new ArrayList<>(); + protected List languageSpecificPrimitives = new ArrayList<>(); + protected List importMappings = new ArrayList<>(); + protected String invokerPackage; + protected String groupId; + protected String artifactId; + protected String artifactVersion; + protected String library; + protected String gitUserId; + protected String gitRepoId; + protected String releaseNote; + protected String httpUserAgent; + protected List reservedWordsMappings = new ArrayList<>(); + protected String ignoreFileOverride; + protected Boolean removeOperationIdPrefix; + private String url; public void setVerbose(Boolean verbose) { this.verbose = verbose; @@ -177,9 +193,15 @@ public class Generate implements Runnable { this.removeOperationIdPrefix = removeOperationIdPrefix; } + public void setUrl(String url) { + this.url = url; + } + @Override public void run() { + loadArguments(); + // attempt to read from config file CodegenConfigurator configurator = CodegenConfigurator.fromFile(configFile); @@ -289,4 +311,62 @@ public class Generate implements Runnable { new DefaultGenerator().opts(clientOptInput).generate(); } + + private void loadArguments() { + if (StringUtils.isBlank(this.url)) { + return; + } + final String content; + File file = new File(this.url); + if (file.exists() && file.isFile()) { + try { + content = FileUtils.readFileToString(file); + } catch (IOException e) { + LOG.error("Unable to read file: " + this.url, e); + return; + } + } else if (isValidURL(this.url)) { + try { + content = RemoteUrl.urlToString(this.url, null); + } catch (Exception e) { + LOG.error("Unable to read url: " + this.url, e); + return; + } + } else { + return; + } + + if (StringUtils.isBlank(content)) { + return; + } + + JsonNode node = null; + + if (isValidJson(content)) { + try { + node = Json.mapper().readTree(content.getBytes()); + } catch (IOException e) { + LOG.error("Unable to deserialize json from: " + this.url, e); + node = null; + } + } else if (isValidYaml(content)) { + try { + node = Yaml.mapper().readTree(content.getBytes()); + } catch (IOException e) { + LOG.error("Unable to deserialize yaml from: " + this.url, e); + node = null; + } + } + + if (node == null) { + return; + } + + final Map optionValueMap = CLIHelper.createOptionValueMap(node); + try { + BeanUtils.populate(this, optionValueMap); + } catch (Exception e) { + LOG.error("Error setting values to object.", e); + } + } } diff --git a/modules/swagger-codegen-cli/src/main/resources/oas3.yaml b/modules/swagger-codegen-cli/src/main/resources/oas3.yaml index b1597320cc0b..1e0577d9d1f5 100644 --- a/modules/swagger-codegen-cli/src/main/resources/oas3.yaml +++ b/modules/swagger-codegen-cli/src/main/resources/oas3.yaml @@ -173,6 +173,12 @@ components: title: "remove prefix of the operationId" description: "Remove prefix of operationId, e.g. config_getId => getId" x-option: "--remove-operation-id-prefix" + url: + type: "string" + title: "URL for arguments" + description: "load arguments from a local file or remote URL. Arguments found will replace any one placed on command." + x-option: "--url" + x-short-version: "-u" ConfigHelp: x-command: "config-help" x-command-description: "Config help for chosen lang" diff --git a/modules/swagger-codegen-cli/src/test/java/io/swagger/codegen/cmd/GenerateTest.java b/modules/swagger-codegen-cli/src/test/java/io/swagger/codegen/cmd/GenerateTest.java index f8d3a698df76..de73f497eed0 100644 --- a/modules/swagger-codegen-cli/src/test/java/io/swagger/codegen/cmd/GenerateTest.java +++ b/modules/swagger-codegen-cli/src/test/java/io/swagger/codegen/cmd/GenerateTest.java @@ -1,17 +1,27 @@ package io.swagger.codegen.cmd; +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.codegen.CLIHelper; import io.swagger.codegen.ClientOptInput; import io.swagger.codegen.DefaultGenerator; import io.swagger.codegen.SwaggerCodegen; import io.swagger.codegen.config.CodegenConfigurator; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Yaml; import mockit.Expectations; import mockit.FullVerifications; import mockit.Injectable; import mockit.Mocked; import mockit.Verifications; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; +import org.testng.Assert; import org.testng.annotations.Test; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + @SuppressWarnings("unused") public class GenerateTest { @@ -552,6 +562,85 @@ public class GenerateTest { }; } + @Test + public void testExternalArguments() throws Exception { + String content = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("args.json")); + JsonNode node = Json.mapper().readTree(content.getBytes()); + + Map valueMap = CLIHelper.createOptionValueMap(node); + Assert.assertNotNull(valueMap); + + Assert.assertTrue(valueMap.containsKey("lang")); + Assert.assertTrue(valueMap.containsKey("library")); + Assert.assertTrue(valueMap.containsKey("additionalProperties")); + Assert.assertTrue(valueMap.containsKey("spec")); + Assert.assertTrue(valueMap.containsKey("output")); + + + Assert.assertEquals(valueMap.get("lang"), "java"); + Assert.assertEquals(valueMap.get("library"), "jersey2"); + Assert.assertNotNull(valueMap.get("additionalProperties")); + Assert.assertTrue(valueMap.get("additionalProperties") instanceof ArrayList); + + List properties = (List) valueMap.get("additionalProperties"); + Assert.assertEquals(properties.size(), 2); + Assert.assertEquals(properties.get(0), "serializableModel=true"); + Assert.assertEquals(properties.get(1), "withXml=true"); + + + content = IOUtils.toString(getClass().getClassLoader().getResourceAsStream("args.yaml")); + node = Yaml.mapper().readTree(content.getBytes()); + + valueMap = CLIHelper.createOptionValueMap(node); + Assert.assertNotNull(valueMap); + + Assert.assertTrue(valueMap.containsKey("lang")); + Assert.assertTrue(valueMap.containsKey("library")); + Assert.assertTrue(valueMap.containsKey("additionalProperties")); + Assert.assertTrue(valueMap.containsKey("spec")); + Assert.assertTrue(valueMap.containsKey("output")); + + + Assert.assertEquals(valueMap.get("lang"), "java"); + Assert.assertEquals(valueMap.get("library"), "jersey2"); + Assert.assertNotNull(valueMap.get("additionalProperties")); + Assert.assertTrue(valueMap.get("additionalProperties") instanceof ArrayList); + + properties = (List) valueMap.get("additionalProperties"); + Assert.assertEquals(properties.size(), 2); + Assert.assertEquals(properties.get(0), "serializableModel=true"); + Assert.assertEquals(properties.get(1), "withXml=true"); + } + + @Test + public void testExternalArgumentsFromFile() throws Exception { + String location = "src/test/resources/args.json"; + + Generate generate = new Generate(); + generate.setUrl(location); + + generate.run(); + + Assert.assertEquals(generate.lang, "java"); + Assert.assertEquals(generate.library, "jersey2"); + Assert.assertEquals(generate.additionalProperties.size(), 2); + Assert.assertEquals(generate.additionalProperties.get(0), "serializableModel=true"); + Assert.assertEquals(generate.additionalProperties.get(1), "withXml=true"); + + location = "src/test/resources/args.yaml"; + + generate = new Generate(); + generate.setUrl(location); + + generate.run(); + + Assert.assertEquals(generate.lang, "java"); + Assert.assertEquals(generate.library, "jersey2"); + Assert.assertEquals(generate.additionalProperties.size(), 2); + Assert.assertEquals(generate.additionalProperties.get(0), "serializableModel=true"); + Assert.assertEquals(generate.additionalProperties.get(1), "withXml=true"); + } + private void setupAndRunGenericTest(String... additionalParameters) { setupAndRunTest("-i", "swagger.yaml", "-l", "java", "-o", "src/main/java", false, null, additionalParameters); diff --git a/modules/swagger-codegen-cli/src/test/resources/args.json b/modules/swagger-codegen-cli/src/test/resources/args.json new file mode 100644 index 000000000000..83e25c0c6776 --- /dev/null +++ b/modules/swagger-codegen-cli/src/test/resources/args.json @@ -0,0 +1,7 @@ +{ + "lang":"java", + "library":"jersey2", + "additionalProperties":["serializableModel=true,withXml=true"], + "spec":"/some/place/locally", + "output":"/some/place/locally" +} \ No newline at end of file diff --git a/modules/swagger-codegen-cli/src/test/resources/args.yaml b/modules/swagger-codegen-cli/src/test/resources/args.yaml new file mode 100644 index 000000000000..b27c66a8c80a --- /dev/null +++ b/modules/swagger-codegen-cli/src/test/resources/args.yaml @@ -0,0 +1,7 @@ +lang: java +library: jersey2 +additionalProperties: + - "serializableModel=true" + - "withXml=true" +spec: "/some/place/locally" +output: "/some/place/locally" \ No newline at end of file