From b799d694f06d6f1883efd932e89cce93de58db84 Mon Sep 17 00:00:00 2001 From: wing328 Date: Tue, 27 Mar 2018 14:55:46 +0800 Subject: [PATCH] add php slim generator --- .../languages/SlimFrameworkServerCodegen.java | 286 ++++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../slim/SlimFrameworkServerOptionsTest.java | 33 ++ .../SlimFrameworkServerOptionsProvider.java | 34 +++ 4 files changed, 354 insertions(+) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SlimFrameworkServerCodegen.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/SlimFrameworkServerOptionsTest.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/options/SlimFrameworkServerOptionsProvider.java diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SlimFrameworkServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SlimFrameworkServerCodegen.java new file mode 100644 index 00000000000..0d7e38eaed8 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SlimFrameworkServerCodegen.java @@ -0,0 +1,286 @@ +package org.openapitools.codegen.languages; + +import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.CodegenConstants; +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.HashMap; +import java.util.HashSet; +import java.util.regex.Matcher; + +public class SlimFrameworkServerCodegen extends DefaultCodegen implements CodegenConfig { + protected String invokerPackage; + protected String srcBasePath = "lib"; + protected String groupId = "io.swagger"; + protected String artifactId = "swagger-server"; + protected String artifactVersion = "1.0.0"; + protected String packagePath = ""; // empty packagePath (top folder) + + + private String variableNamingConvention = "camelCase"; + + public SlimFrameworkServerCodegen() { + super(); + + // clear import mapping (from default generator) as slim does not use it + // at the moment + importMapping.clear(); + + invokerPackage = camelize("SwaggerServer"); + + //String packagePath = "SwaggerServer"; + + modelPackage = packagePath + "\\Models"; + apiPackage = packagePath; + outputFolder = "generated-code" + File.separator + "slim"; + modelTemplateFiles.put("model.mustache", ".php"); + + // no api files + apiTemplateFiles.clear(); + + embeddedTemplateDir = templateDir = "slim"; + + setReservedWordsLowerCase( + Arrays.asList( + "__halt_compiler", "abstract", "and", "array", "as", "break", "callable", "case", "catch", "class", "clone", "const", "continue", "declare", "default", "die", "do", "echo", "else", "elseif", "empty", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", "eval", "exit", "extends", "final", "for", "foreach", "function", "global", "goto", "if", "implements", "include", "include_once", "instanceof", "insteadof", "interface", "isset", "list", "namespace", "new", "or", "print", "private", "protected", "public", "require", "require_once", "return", "static", "switch", "throw", "trait", "try", "unset", "use", "var", "while", "xor") + ); + + additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage); + additionalProperties.put(CodegenConstants.GROUP_ID, groupId); + additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); + additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); + + // ref: http://php.net/manual/en/language.types.intro.php + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "boolean", + "int", + "integer", + "double", + "float", + "string", + "object", + "DateTime", + "mixed", + "number") + ); + + instantiationTypes.put("array", "array"); + instantiationTypes.put("map", "map"); + + // ref: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types + typeMapping = new HashMap(); + typeMapping.put("integer", "int"); + typeMapping.put("long", "int"); + typeMapping.put("float", "float"); + typeMapping.put("double", "double"); + typeMapping.put("string", "string"); + typeMapping.put("byte", "int"); + typeMapping.put("boolean", "bool"); + typeMapping.put("date", "\\DateTime"); + typeMapping.put("datetime", "\\DateTime"); + typeMapping.put("file", "\\SplFileObject"); + typeMapping.put("map", "map"); + typeMapping.put("array", "array"); + typeMapping.put("list", "array"); + typeMapping.put("object", "object"); + //TODO binary should be mapped to byte array + // mapped to String as a workaround + typeMapping.put("binary", "string"); + + supportingFiles.add(new SupportingFile("README.mustache", packagePath.replace('/', File.separatorChar), "README.md")); + supportingFiles.add(new SupportingFile("composer.json", packagePath.replace('/', File.separatorChar), "composer.json")); + supportingFiles.add(new SupportingFile("index.mustache", packagePath.replace('/', File.separatorChar), "index.php")); + supportingFiles.add(new SupportingFile(".htaccess", packagePath.replace('/', File.separatorChar), ".htaccess")); + } + + @Override + public CodegenType getTag() { + return CodegenType.SERVER; + } + + @Override + public String getName() { + return "php-slim"; + } + + @Override + public String getHelp() { + return "Generates a PHP Slim Framework 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 + "/" + toPackagePath(apiPackage, srcBasePath)); + } + + @Override + public String modelFileFolder() { + return (outputFolder + "/" + toPackagePath(modelPackage, srcBasePath)); + } + + @Override + public String getTypeDeclaration(Schema p) { + if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + return getTypeDeclaration(inner) + "[]"; + } else if (p instanceof MapSchema) { + MapSchema mp = (MapSchema) p; + Schema inner = (Schema) mp.getAdditionalProperties(); + return getSchemaType(p) + "[string," + getTypeDeclaration(inner) + "]"; + } else if (!StringUtils.isEmpty(p.get$ref())) { + String type = super.getTypeDeclaration(p); + return (!languageSpecificPrimitives.contains(type)) + ? "\\" + modelPackage + "\\" + type : type; + } + 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 if (instantiationTypes.containsKey(type)) { + return type; + } + } else { + type = openAPIType; + } + if (type == null) { + return null; + } + return toModelName(type); + } + + @Override + public String getTypeDeclaration(String name) { + if (!languageSpecificPrimitives.contains(name)) { + return "\\" + modelPackage + "\\" + name; + } + return super.getTypeDeclaration(name); + } + + @Override + public String toDefaultValue(Schema p) { + return "null"; + } + + public void setParameterNamingConvention(String variableNamingConvention) { + this.variableNamingConvention = variableNamingConvention; + } + + @Override + public String toVarName(String name) { + name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + if ("camelCase".equals(variableNamingConvention)) { + // return the name in camelCase style + // phone_number => phoneNumber + name = camelize(name, true); + } else { // default to snake case + // return the name in underscore style + // PhoneNumber => phone_number + name = underscore(name); + } + + // parameter name starting with number won't compile + // need to escape it by appending _ at the beginning + if (name.matches("^\\d.*")) { + name = "_" + 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 + if (isReservedWord(name)) { + escapeReservedWord(name); // e.g. return => _return + } + + // camelize the model name + // phone_number => PhoneNumber + return camelize(name); + } + + @Override + public String toModelFilename(String name) { + // should be the same as the model name + return toModelName(name); + } + + public String toPackagePath(String packageName, String basePath) { + packageName = packageName.replace(invokerPackage, ""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + if (basePath != null && basePath.length() > 0) { + basePath = basePath.replaceAll("[\\\\/]?$", "") + File.separatorChar; // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + } + + String regFirstPathSeparator; + if ("/".equals(File.separator)) { // for mac, linux + regFirstPathSeparator = "^/"; + } else { // for windows + regFirstPathSeparator = "^\\\\"; + } + + String regLastPathSeparator; + if ("/".equals(File.separator)) { // for mac, linux + regLastPathSeparator = "/$"; + } else { // for windows + regLastPathSeparator = "\\\\$"; + } + + return (getPackagePath() + File.separatorChar + basePath + // Replace period, backslash, forward slash with file separator in package name + + packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement(File.separator)) + // Trim prefix file separators from package path + .replaceAll(regFirstPathSeparator, "")) + // Trim trailing file separators from the overall path + .replaceAll(regLastPathSeparator+ "$", ""); + } + + public String getPackagePath() { + return packagePath; + } + + @Override + public String escapeQuotationMark(String input) { + // remove ' to avoid code injection + return input.replace("'", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", ""); + } + +} 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 da15bc2c0f0..15f20f51d8a 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 @@ -5,3 +5,4 @@ org.openapitools.codegen.languages.PhpClientCodegen org.openapitools.codegen.languages.PythonClientCodegen org.openapitools.codegen.languages.Rails5ServerCodegen org.openapitools.codegen.languages.RubyClientCodegen +org.openapitools.codegen.languages.SlimFrameworkServerCodegen diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/SlimFrameworkServerOptionsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/SlimFrameworkServerOptionsTest.java new file mode 100644 index 00000000000..6e2e06e3a67 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/slim/SlimFrameworkServerOptionsTest.java @@ -0,0 +1,33 @@ +package org.openapitools.codegen.slim; + +import org.openapitools.codegen.AbstractOptionsTest; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.languages.SlimFrameworkServerCodegen; +import org.openapitools.codegen.options.SlimFrameworkServerOptionsProvider; + +import mockit.Expectations; +import mockit.Tested; + +public class SlimFrameworkServerOptionsTest extends AbstractOptionsTest { + + @Tested + private SlimFrameworkServerCodegen clientCodegen; + + public SlimFrameworkServerOptionsTest() { + super(new SlimFrameworkServerOptionsProvider()); + } + + @Override + protected CodegenConfig getCodegenConfig() { + return clientCodegen; + } + + @SuppressWarnings("unused") + @Override + protected void setExpectations() { + new Expectations(clientCodegen) {{ + clientCodegen.setSortParamsByRequiredFlag(Boolean.valueOf(SlimFrameworkServerOptionsProvider.SORT_PARAMS_VALUE)); + times = 1; + }}; + } +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/options/SlimFrameworkServerOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/options/SlimFrameworkServerOptionsProvider.java new file mode 100644 index 00000000000..22f3a231f4b --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/options/SlimFrameworkServerOptionsProvider.java @@ -0,0 +1,34 @@ +package org.openapitools.codegen.options; + +import org.openapitools.codegen.CodegenConstants; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class SlimFrameworkServerOptionsProvider implements OptionsProvider { + public static final String SORT_PARAMS_VALUE = "false"; + public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true"; + public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false"; + public static final String PREPEND_FORM_OR_BODY_PARAMETERS_VALUE = "true"; + + @Override + public String getLanguage() { + return "slim"; + } + + @Override + public Map createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return builder.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_VALUE) + .put(CodegenConstants.ENSURE_UNIQUE_PARAMS, ENSURE_UNIQUE_PARAMS_VALUE) + .put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE) + .put(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS, PREPEND_FORM_OR_BODY_PARAMETERS_VALUE) + .build(); + } + + @Override + public boolean isServer() { + return true; + } +}