From 24cb0f619f27e83aada97f68ebef698d33569c6c Mon Sep 17 00:00:00 2001 From: wing328 Date: Tue, 27 Mar 2018 16:41:54 +0800 Subject: [PATCH] add ruby sinatra server generator --- .../languages/SinatraServerCodegen.java | 264 ++++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../sinatra/SinatraServerOptionsTest.java | 31 ++ .../options/SinatraServerOptionsProvider.java | 23 ++ 4 files changed, 319 insertions(+) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SinatraServerCodegen.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/sinatra/SinatraServerOptionsTest.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/options/SinatraServerOptionsProvider.java diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SinatraServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SinatraServerCodegen.java new file mode 100644 index 00000000000..9dfdb62828e --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SinatraServerCodegen.java @@ -0,0 +1,264 @@ +package org.openapitools.codegen.languages; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.CodegenType; +import org.openapitools.codegen.DefaultCodegen; +import org.openapitools.codegen.SupportingFile; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.core.util.Yaml; + +import java.io.File; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SinatraServerCodegen extends DefaultCodegen implements CodegenConfig { + + private static final Logger LOGGER = LoggerFactory.getLogger(SinatraServerCodegen.class); + + protected String gemName; + protected String moduleName; + protected String gemVersion = "1.0.0"; + protected String libFolder = "lib"; + + public SinatraServerCodegen() { + super(); + apiPackage = "lib"; + outputFolder = "generated-code" + File.separator + "sinatra"; + + // no model + modelTemplateFiles.clear(); + apiTemplateFiles.put("api.mustache", ".rb"); + embeddedTemplateDir = templateDir = "sinatra"; + + typeMapping.clear(); + languageSpecificPrimitives.clear(); + + setReservedWordsLowerCase( + Arrays.asList( + "__FILE__", "and", "def", "end", "in", "or", "self", "unless", "__LINE__", + "begin", "defined?", "ensure", "module", "redo", "super", "until", "BEGIN", + "break", "do", "false", "next", "rescue", "then", "when", "END", "case", + "else", "for", "nil", "retry", "true", "while", "alias", "class", "elsif", + "if", "not", "return", "undef", "yield") + ); + + languageSpecificPrimitives.add("int"); + languageSpecificPrimitives.add("array"); + languageSpecificPrimitives.add("map"); + languageSpecificPrimitives.add("string"); + languageSpecificPrimitives.add("DateTime"); + + typeMapping.put("long", "int"); + typeMapping.put("integer", "int"); + typeMapping.put("Array", "array"); + typeMapping.put("String", "string"); + typeMapping.put("List", "array"); + typeMapping.put("map", "map"); + //TODO binary should be mapped to byte array + // mapped to String as a workaround + typeMapping.put("binary", "string"); + typeMapping.put("UUID", "string"); + + // remove modelPackage and apiPackage added by default + cliOptions.clear(); + } + + @Override + public void processOpts() { + super.processOpts(); + + // use constant model/api package (folder path) + //setModelPackage("models"); + setApiPackage("api"); + + supportingFiles.add(new SupportingFile("my_app.mustache", "", "my_app.rb")); + supportingFiles.add(new SupportingFile("Swaggering.rb", libFolder, "swaggering.rb")); + supportingFiles.add(new SupportingFile("config.ru", "", "config.ru")); + supportingFiles.add(new SupportingFile("Gemfile", "", "Gemfile")); + supportingFiles.add(new SupportingFile("README.md", "", "README.md")); + supportingFiles.add(new SupportingFile("swagger.mustache","","swagger.yaml")); + } + + @Override + public CodegenType getTag() { + return CodegenType.SERVER; + } + + @Override + public String getName() { + return "ruby-sinatra"; + } + + @Override + public String getHelp() { + return "Generates a Ruby Sinatra server library."; + } + + @Override + public String escapeReservedWord(String name) { + if(this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return "_" + name; + } + + @Override + public String apiFileFolder() { + return outputFolder + File.separator + apiPackage.replace("/", File.separator); + } + + @Override + public String getTypeDeclaration(Schema p) { + if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]"; + } else if (p instanceof MapSchema) { + MapSchema mp = (MapSchema) p; + Schema inner = (Schema) mp.getAdditionalProperties(); + return getSchemaType(p) + "[string," + getTypeDeclaration(inner) + "]"; + } + return super.getTypeDeclaration(p); + } + + @Override + public String getSchemaType(Schema p) { + String openAPIType = super.getSchemaType(p); + String type = null; + if (typeMapping.containsKey(openAPIType)) { + type = typeMapping.get(openAPIType); + if (languageSpecificPrimitives.contains(type)) { + return type; + } + } else { + type = openAPIType; + } + if (type == null) { + return null; + } + return type; + } + + @Override + public String toDefaultValue(Schema p) { + return "null"; + } + + @Override + public String toVarName(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // if it's all uppper case, convert to lower case + if (name.matches("^[A-Z_]*$")) { + name = name.toLowerCase(); + } + + // camelize (lower first character) the variable name + // petId => pet_id + name = underscore(name); + + // for reserved word or word starting with number, append _ + if (isReservedWord(name) || name.matches("^\\d.*")) { + name = escapeReservedWord(name); + } + + return name; + } + + @Override + public String toParamName(String name) { + // should be the same as variable name + return toVarName(name); + } + + @Override + public String toModelName(String name) { + // model name cannot use reserved keyword, e.g. return + if (isReservedWord(name)) { + LOGGER.warn(name + " (reserved word) cannot be used as model filename. Renamed to " + camelize("model_" + name)); + name = "model_" + name; // e.g. return => ModelReturn (after camelize) + } + + // camelize the model name + // phone_number => PhoneNumber + return camelize(name); + } + + @Override + public String toModelFilename(String name) { + // model name cannot use reserved keyword, e.g. return + if (isReservedWord(name)) { + LOGGER.warn(name + " (reserved word) cannot be used as model filename. Renamed to " + underscore("model_" + name)); + name = "model_" + name; // e.g. return => ModelReturn (after camelize) + } + + // underscore the model file name + // PhoneNumber.rb => phone_number.rb + return underscore(name); + } + + @Override + public String toApiFilename(String name) { + // replace - with _ e.g. created-at => created_at + name = name.replaceAll("-", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // e.g. PhoneNumberApi.rb => phone_number_api.rb + return underscore(name) + "_api"; + } + + @Override + public String toApiName(String name) { + if (name.length() == 0) { + return "DefaultApi"; + } + // e.g. phone_number_api => PhoneNumberApi + return camelize(name) + "Api"; + } + + @Override + public String toOperationId(String operationId) { + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + String newOperationId = underscore("call_" + operationId); + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + newOperationId); + return newOperationId; + } + + return underscore(operationId); + } + + @Override + public Map postProcessSupportingFileData(Map objs) { + OpenAPI openAPI = (OpenAPI) objs.get("openapi"); + if (openAPI != null) { + try { + objs.put("swagger-yaml", Yaml.mapper().writeValueAsString(openAPI)); + } catch (JsonProcessingException e) { + LOGGER.error(e.getMessage(), e); + } + } + return super.postProcessSupportingFileData(objs); + } + + @Override + public String escapeQuotationMark(String input) { + // remove ' to avoid code injection + return input.replace("'", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("=end", "=_end").replace("=begin", "=_begin"); + } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 83da3ad5841..5d9649a09d9 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -8,3 +8,4 @@ org.openapitools.codegen.languages.Rails5ServerCodegen org.openapitools.codegen.languages.RubyClientCodegen org.openapitools.codegen.languages.SlimFrameworkServerCodegen org.openapitools.codegen.languages.SilexServerCodegen +org.openapitools.codegen.languages.SinatraServerCodegen diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/sinatra/SinatraServerOptionsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/sinatra/SinatraServerOptionsTest.java new file mode 100644 index 00000000000..3f906bd8c3f --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/sinatra/SinatraServerOptionsTest.java @@ -0,0 +1,31 @@ +package org.openapitools.codegen.sinatra; + +import org.openapitools.codegen.AbstractOptionsTest; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.languages.SinatraServerCodegen; +import org.openapitools.codegen.options.SinatraServerOptionsProvider; + +import mockit.Expectations; +import mockit.Tested; + +public class SinatraServerOptionsTest extends AbstractOptionsTest { + + @Tested + private SinatraServerCodegen clientCodegen; + + public SinatraServerOptionsTest() { + super(new SinatraServerOptionsProvider()); + } + + @Override + protected CodegenConfig getCodegenConfig() { + return clientCodegen; + } + + @SuppressWarnings("unused") + @Override + protected void setExpectations() { + new Expectations(clientCodegen) {{ + }}; + } +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/options/SinatraServerOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/options/SinatraServerOptionsProvider.java new file mode 100644 index 00000000000..51f1c2cef1d --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/options/SinatraServerOptionsProvider.java @@ -0,0 +1,23 @@ +package org.openapitools.codegen.options; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class SinatraServerOptionsProvider implements OptionsProvider { + @Override + public String getLanguage() { + return "ruby-sinatra"; + } + + @Override + public Map createOptions() { + //SinatraServerCodegen doesn't have its own options and base options are cleared + return ImmutableMap.of(); + } + + @Override + public boolean isServer() { + return true; + } +}