diff --git a/README.md b/README.md index cb649e07da5..da7d0752f0d 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ NAME SYNOPSIS swagger generate [(-a | --auth )] + [(-c | --config )] + [-D ] (-i | --input-spec ) (-l | --lang ) [(-o | --output )] @@ -72,6 +74,16 @@ OPTIONS remotely. Pass in a URL-encoded string of name:header with a comma separating multiple values + -c , --config + Path to json configuration file. File content should be in a json + format {"optionKey":"optionValue", "optionKey1":"optionValue1"...} + Supported options can be different for each language. Run + config-help -l {lang} command for language specific config options. + + -D + sets specified system properties in the format of + name=value,name=value + -i , --input-spec location of the swagger spec, as URL or file (required) @@ -166,8 +178,60 @@ SwaggerYamlGenerator.java TizenClientCodegen.java ``` -Each of these files creates reasonable defaults so you can get running quickly. But if you want to configure package names, prefixes, model folders, etc., you may want to extend these. +Each of these files creates reasonable defaults so you can get running quickly. But if you want to configure package names, prefixes, model folders, etc. you can use a json config file to pass the values. + +``` +java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \ + -i http://petstore.swagger.io/v2/swagger.json \ + -l java \ + -o samples/client/petstore/java \ + -c path/to/config.json +``` +Supported config options can be different per language. Running `config-help -l {lang}` will show available options. + +``` +java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jarr config-help -l java +``` + +Output + +``` +CONFIG OPTIONS + modelPackage + package for generated models + + apiPackage + package for generated api classes + + invokerPackage + root package for generated code + + groupId + groupId in generated pom.xml + + artifactId + artifactId in generated pom.xml + + artifactVersion + artifact version in generated pom.xml + + sourceFolder + source folder for generated code +``` + +Your config file for java can look like + +``` +{ + "groupId":"com.my.company", + "artifactId":"MyClent", + "artifactVersion":"1.2.0" +} +``` + +For all the unspecified options default values will be used. +Another way to override default options is to extend config class for specific language. To change, for example, the prefix for the Objective-C generated files, simply subclass the ObjcClientCodegen.java: ``` diff --git a/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/SwaggerCodegen.java b/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/SwaggerCodegen.java index c8c718fb67b..f2c794776ce 100644 --- a/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/SwaggerCodegen.java +++ b/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/SwaggerCodegen.java @@ -1,5 +1,6 @@ package com.wordnik.swagger.codegen; +import com.wordnik.swagger.codegen.cmd.ConfigHelp; import com.wordnik.swagger.codegen.cmd.Generate; import com.wordnik.swagger.codegen.cmd.Langs; import com.wordnik.swagger.codegen.cmd.Meta; @@ -27,7 +28,8 @@ public static void main(String[] args) { Generate.class, Meta.class, Langs.class, - Help.class + Help.class, + ConfigHelp.class ); builder.build().parse(args).run(); diff --git a/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/cmd/ConfigHelp.java b/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/cmd/ConfigHelp.java new file mode 100644 index 00000000000..fbdca12e6e8 --- /dev/null +++ b/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/cmd/ConfigHelp.java @@ -0,0 +1,49 @@ +package com.wordnik.swagger.codegen.cmd; + +import com.wordnik.swagger.codegen.CliOption; +import com.wordnik.swagger.codegen.CodegenConfig; +import io.airlift.airline.Command; +import io.airlift.airline.Option; +import java.util.ServiceLoader; +import static java.util.ServiceLoader.load; + +@Command(name = "config-help", description = "Config help for chosen lang") +public class ConfigHelp implements Runnable { + + @Option(name = {"-l", "--lang"}, title = "language", required = true, + description = "language to get config help for") + private String lang; + + @Override + public void run() { + System.out.println(); + CodegenConfig config = forName(lang); + System.out.println("CONFIG OPTIONS"); + for (CliOption langCliOption : config.cliOptions()) { + System.out.println("\t" + langCliOption.getOpt()); + System.out.println("\t " + langCliOption.getDescription()); + System.out.println(); + } + } + + /** + * Tries to load config class with SPI first, then with class name directly from classpath + * @param name name of config, or full qualified class name in classpath + * @return config class + */ + private static CodegenConfig forName(String name) { + ServiceLoader loader = load(CodegenConfig.class); + for (CodegenConfig config : loader) { + if (config.getName().equals(name)) { + return config; + } + } + + // else try to load directly + try { + return (CodegenConfig) Class.forName(name).newInstance(); + } catch (Exception e) { + throw new RuntimeException("Can't load config class with name ".concat(name), e); + } + } +} diff --git a/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/cmd/Generate.java b/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/cmd/Generate.java index 47449d84286..3485eb8dd24 100644 --- a/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/cmd/Generate.java +++ b/modules/swagger-codegen-cli/src/main/java/com/wordnik/swagger/codegen/cmd/Generate.java @@ -1,10 +1,13 @@ package com.wordnik.swagger.codegen.cmd; +import com.wordnik.swagger.codegen.CliOption; import com.wordnik.swagger.codegen.ClientOptInput; import com.wordnik.swagger.codegen.ClientOpts; import com.wordnik.swagger.codegen.CodegenConfig; import com.wordnik.swagger.codegen.DefaultGenerator; import com.wordnik.swagger.models.Swagger; +import config.Config; +import config.ConfigParser; import io.airlift.airline.Command; import io.airlift.airline.Option; import io.swagger.parser.SwaggerParser; @@ -57,6 +60,11 @@ public class Generate implements Runnable { @Option( name= {"-D"}, title = "system properties", description = "sets specified system properties in " + "the format of name=value,name=value") private String systemProperties; + + @Option( name= {"-c", "--config"}, title = "configuration file", description = "Path to json configuration file. " + + "File content should be in a json format {\"optionKey\":\"optionValue\", \"optionKey1\":\"optionValue1\"...} " + + "Supported options can be different for each language. Run config-help -l {lang} command for language specific config options.") + private String configFile; @Override public void run() { @@ -76,6 +84,17 @@ public void run() { if (null != templateDir) { config.additionalProperties().put(TEMPLATE_DIR_PARAM, new File(templateDir).getAbsolutePath()); } + + if(null != configFile){ + Config genConfig = ConfigParser.read(configFile); + if (null != genConfig) { + for (CliOption langCliOption : config.cliOptions()) { + if (genConfig.hasOption(langCliOption.getOpt())) { + config.additionalProperties().put(langCliOption.getOpt(), genConfig.getOption(langCliOption.getOpt())); + } + } + } + } input.setConfig(config); diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/CliOption.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/CliOption.java new file mode 100644 index 00000000000..8ea0b60db26 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/CliOption.java @@ -0,0 +1,23 @@ +package com.wordnik.swagger.codegen; + +public class CliOption { + private final String opt; + private String description; + + public CliOption(String opt, String description) { + this.opt = opt; + this.description = description; + } + + public String getOpt() { + return opt; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/Codegen.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/Codegen.java index efda171ff82..c28031a9f27 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/Codegen.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/Codegen.java @@ -140,4 +140,4 @@ public static CodegenConfig getConfig(String name) { } } } -} +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/CodegenConfig.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/CodegenConfig.java index 167610fcd70..b112923b6d7 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/CodegenConfig.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/CodegenConfig.java @@ -27,6 +27,7 @@ public interface CodegenConfig { String getTypeDeclaration(Property p); String getTypeDeclaration(String name); void processOpts(); + List cliOptions(); String generateExamplePath(String path, Operation operation); Set reservedWords(); diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java index 2513bedfcb9..d7788ef8865 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java @@ -77,11 +77,24 @@ public class DefaultCodegen { protected String templateDir; protected Map additionalProperties = new HashMap(); protected List supportingFiles = new ArrayList(); + protected List cliOptions = new ArrayList(); + + public List cliOptions() { + return cliOptions; + } public void processOpts(){ if(additionalProperties.containsKey("templateDir")) { this.setTemplateDir((String)additionalProperties.get("templateDir")); } + + if(additionalProperties.containsKey("modelPackage")) { + this.setModelPackage((String)additionalProperties.get("modelPackage")); + } + + if(additionalProperties.containsKey("apiPackage")) { + this.setApiPackage((String)additionalProperties.get("apiPackage")); + } } // override with any special post-processing @@ -178,6 +191,14 @@ public void setTemplateDir(String templateDir) { this.templateDir = templateDir; } + public void setModelPackage(String modelPackage) { + this.modelPackage = modelPackage; + } + + public void setApiPackage(String apiPackage) { + this.apiPackage = apiPackage; + } + public String toApiFilename(String name) { return toApiName(name); } @@ -281,6 +302,9 @@ public DefaultCodegen() { importMapping.put("LocalDateTime", "org.joda.time.*"); importMapping.put("LocalDate", "org.joda.time.*"); importMapping.put("LocalTime", "org.joda.time.*"); + + cliOptions.add(new CliOption("modelPackage", "package for generated models")); + cliOptions.add(new CliOption("apiPackage", "package for generated api classes")); } diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/JavaClientCodegen.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/JavaClientCodegen.java index 7c0ac7422ff..736b533ebc6 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/JavaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/JavaClientCodegen.java @@ -45,19 +45,6 @@ public JavaClientCodegen() { "native", "super", "while") ); - additionalProperties.put("invokerPackage", invokerPackage); - additionalProperties.put("groupId", groupId); - additionalProperties.put("artifactId", artifactId); - additionalProperties.put("artifactVersion", artifactVersion); - - supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); - supportingFiles.add(new SupportingFile("apiInvoker.mustache", - (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "ApiInvoker.java")); - supportingFiles.add(new SupportingFile("JsonUtil.mustache", - (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "JsonUtil.java")); - supportingFiles.add(new SupportingFile("apiException.mustache", - (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "ApiException.java")); - languageSpecificPrimitives = new HashSet( Arrays.asList( "String", @@ -71,8 +58,65 @@ public JavaClientCodegen() { ); instantiationTypes.put("array", "ArrayList"); instantiationTypes.put("map", "HashMap"); + + cliOptions.add(new CliOption("invokerPackage", "root package for generated code")); + cliOptions.add(new CliOption("groupId", "groupId in generated pom.xml")); + cliOptions.add(new CliOption("artifactId", "artifactId in generated pom.xml")); + cliOptions.add(new CliOption("artifactVersion", "artifact version in generated pom.xml")); + cliOptions.add(new CliOption("sourceFolder", "source folder for generated code")); } + @Override + public void processOpts() { + super.processOpts(); + + if(additionalProperties.containsKey("invokerPackage")) { + this.setInvokerPackage((String)additionalProperties.get("invokerPackage")); + } + else{ + //not set, use default to be passed to template + additionalProperties.put("invokerPackage", invokerPackage); + } + + if(additionalProperties.containsKey("groupId")) { + this.setGroupId((String)additionalProperties.get("groupId")); + } + else{ + //not set, use to be passed to template + additionalProperties.put("groupId", groupId); + } + + if(additionalProperties.containsKey("artifactId")) { + this.setArtifactId((String)additionalProperties.get("artifactId")); + } + else{ + //not set, use to be passed to template + additionalProperties.put("artifactId", artifactId); + } + + if(additionalProperties.containsKey("artifactVersion")) { + this.setArtifactVersion((String)additionalProperties.get("artifactVersion")); + } + else{ + //not set, use to be passed to template + additionalProperties.put("artifactVersion", artifactVersion); + } + + if(additionalProperties.containsKey("sourceFolder")) { + this.setSourceFolder((String)additionalProperties.get("sourceFolder")); + } + + supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); + supportingFiles.add(new SupportingFile("apiInvoker.mustache", + (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "ApiInvoker.java")); + supportingFiles.add(new SupportingFile("JsonUtil.mustache", + (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "JsonUtil.java")); + supportingFiles.add(new SupportingFile("apiException.mustache", + (sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator), "ApiException.java")); + } + + + @Override public String escapeReservedWord(String name) { return "_" + name; @@ -169,5 +213,23 @@ public String toOperationId(String operationId) { return camelize(operationId, true); } + public void setInvokerPackage(String invokerPackage) { + this.invokerPackage = invokerPackage; + } + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public void setArtifactVersion(String artifactVersion) { + this.artifactVersion = artifactVersion; + } + + public void setSourceFolder(String sourceFolder) { + this.sourceFolder = sourceFolder; + } } diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/ObjcClientCodegen.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/ObjcClientCodegen.java index d1e227ce744..75d33511a9e 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/ObjcClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/ObjcClientCodegen.java @@ -1,6 +1,5 @@ package com.wordnik.swagger.codegen.languages; -import com.wordnik.swagger.util.Json; import com.wordnik.swagger.codegen.*; import com.wordnik.swagger.models.properties.*; @@ -10,7 +9,8 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig { protected Set foundationClasses = new HashSet(); protected String sourceFolder = "client"; - protected static String PREFIX = "SWG"; + protected String classPrefix = "SWG"; + protected String projectName = "swaggerClient"; public CodegenType getTag() { return CodegenType.CLIENT; @@ -34,12 +34,6 @@ public ObjcClientCodegen() { templateDir = "objc"; modelPackage = ""; - String appName = System.getProperty("appName"); - if(appName == null) { - appName = "swaggerClient"; - } - additionalProperties.put("projectName", appName); - defaultIncludes = new HashSet( Arrays.asList( "bool", @@ -111,7 +105,31 @@ public ObjcClientCodegen() { instantiationTypes.put("array", "NSMutableArray"); instantiationTypes.put("map", "NSMutableDictionary"); + + cliOptions.add(new CliOption("classPrefix", "prefix for generated classes")); + cliOptions.add(new CliOption("sourceFolder", "source folder for generated code")); + cliOptions.add(new CliOption("projectName", "name of the Xcode project in generated Podfile")); + } + @Override + public void processOpts() { + super.processOpts(); + + if(additionalProperties.containsKey("sourceFolder")) { + this.setSourceFolder((String)additionalProperties.get("sourceFolder")); + } + + if(additionalProperties.containsKey("classPrefix")) { + this.setClassPrefix((String)additionalProperties.get("classPrefix")); + } + + if(additionalProperties.containsKey("projectName")) { + this.setProjectName((String)additionalProperties.get("projectName")); + } + else{ + additionalProperties.put("projectName", projectName); + } + supportingFiles.add(new SupportingFile("SWGObject.h", sourceFolder, "SWGObject.h")); supportingFiles.add(new SupportingFile("SWGObject.m", sourceFolder, "SWGObject.m")); supportingFiles.add(new SupportingFile("SWGQueryParamCollection.h", sourceFolder, "SWGQueryParamCollection.h")); @@ -220,7 +238,7 @@ public String toModelName(String type) { } // custom classes else { - return PREFIX + camelize(type); + return classPrefix + camelize(type); } } @@ -266,11 +284,11 @@ public String modelFileFolder() { @Override public String toApiName(String name) { - return PREFIX + camelize(name) + "Api"; + return classPrefix + camelize(name) + "Api"; } public String toApiFilename(String name) { - return PREFIX + camelize(name) + "Api"; + return classPrefix + camelize(name) + "Api"; } @Override @@ -313,4 +331,15 @@ public String toOperationId(String operationId) { return camelize(operationId, true); } + public void setSourceFolder(String sourceFolder) { + this.sourceFolder = sourceFolder; + } + + public void setClassPrefix(String classPrefix) { + this.classPrefix = classPrefix; + } + + public void setProjectName(String projectName) { + this.projectName = projectName; + } } diff --git a/modules/swagger-codegen/src/main/java/config/Config.java b/modules/swagger-codegen/src/main/java/config/Config.java new file mode 100644 index 00000000000..9bc4bf58624 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/config/Config.java @@ -0,0 +1,33 @@ +package config; + +import com.google.common.collect.ImmutableMap; +import java.util.HashMap; +import java.util.Map; + +public class Config { + private Map options; + + public Config() { + this.options = new HashMap(); + } + + public Config(Map properties) { + this.options = properties; + } + + public Map getOptions() { + return ImmutableMap.copyOf(options); + } + + public boolean hasOption(String opt){ + return options.containsKey(opt); + } + + public String getOption(String opt){ + return options.get(opt); + } + + public void setOption(String opt, String value){ + options.put(opt, value); + } +} diff --git a/modules/swagger-codegen/src/main/java/config/ConfigParser.java b/modules/swagger-codegen/src/main/java/config/ConfigParser.java new file mode 100644 index 00000000000..cbb1b78f645 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/config/ConfigParser.java @@ -0,0 +1,41 @@ +package config; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.util.Iterator; +import java.util.Map; + +public class ConfigParser { + + public static Config read(String location) { + + System.out.println("reading config from " + location); + + ObjectMapper mapper = new ObjectMapper(); + + Config config = new Config(); + + try { + JsonNode rootNode = mapper.readTree(new File(location)); + Iterator> optionNodes = rootNode.fields(); + + while (optionNodes.hasNext()) { + Map.Entry optionNode = (Map.Entry) optionNodes.next(); + + if(optionNode.getValue().isValueNode()){ + config.setOption(optionNode.getKey(), optionNode.getValue().asText()); + } + else{ + System.out.println("omitting non-value node " + optionNode.getKey()); + } + } + } + catch (Exception e) { + System.out.println(e.getMessage()); + return null; + } + + return config; + } +}