From 8bbeb8b7e34f70811fb12254d570c801bcee8a12 Mon Sep 17 00:00:00 2001 From: Jim Schubert Date: Thu, 25 Apr 2019 21:52:50 -0400 Subject: [PATCH] Feature: Experimental Handlebars support (rienafairefr) (#2657) * new module: openapi-generator-core * templating engine adapters to support extension (currently only Handlebars) * new `-e` templating engine CLI option * adapt Generator to process Template with the passed TemplatingEngineAdpater * add a MustacheEngineAdapter to the codegen in the unit tests * force default MustacheEngineAdapter * copy new core module in the root Dockerfile * add processTemplatingEngine to CodegenConfig, to be overriden by Codegen classes if needed * support multiple file extensions per templating engine adapter * Extends handlebars experimental adapter with explicit contextual resolvers (e.g. map processing) * Add new openapi-generator-core/pom.xml to release_version_update.sh * A detailed message will be logged on missing handlebars helper * Adds README documentation around template default and beta options * Moves mustache package under new templating package * Include built-in handlebars helpers which require explicit registration, and custom `startsWith` helper. --- Dockerfile | 1 + README.md | 10 ++- bin/utils/release_version_update.sh | 1 + .../openapitools/codegen/cmd/Generate.java | 8 ++ modules/openapi-generator-core/pom.xml | 16 ++++ .../api/AbstractTemplatingEngineAdapter.java | 40 +++++++++ .../codegen/api/TemplatingEngineAdapter.java | 30 +++++++ .../codegen/api/TemplatingGenerator.java | 17 ++++ modules/openapi-generator/pom.xml | 15 ++++ .../codegen/AbstractGenerator.java | 3 +- .../openapitools/codegen/CodegenConfig.java | 8 +- .../codegen/CodegenConstants.java | 3 + .../openapitools/codegen/DefaultCodegen.java | 20 +++++ .../codegen/DefaultGenerator.java | 57 ++++++------ .../codegen/config/CodegenConfigurator.java | 61 ++++++------- .../languages/AbstractCSharpCodegen.java | 2 +- .../codegen/languages/AbstractCppCodegen.java | 2 +- .../languages/ErlangClientCodegen.java | 2 +- .../codegen/languages/JavaClientCodegen.java | 2 +- .../languages/KotlinServerCodegen.java | 2 +- .../ScalaPlayFrameworkServerCodegen.java | 2 +- .../templating/HandlebarsEngineAdapter.java | 75 ++++++++++++++++ .../templating/MustacheEngineAdapter.java | 52 +++++++++++ .../templating/handlebars/StringHelpers.java | 65 ++++++++++++++ .../mustache/CamelCaseLambda.java | 2 +- .../mustache/CaseFormatLambda.java | 2 +- .../mustache/IndentedLambda.java | 2 +- .../mustache/JoinWithCommaLambda.java | 2 +- .../mustache/LowercaseLambda.java | 2 +- .../mustache/TitlecaseLambda.java | 2 +- .../mustache/UppercaseLambda.java | 2 +- .../typescript-axios/git_push.sh.mustache | 0 .../jaxrs/JavaJerseyServerCodegenTest.java | 1 + .../handlebars/StringHelpersTest.java | 86 +++++++++++++++++++ pom.xml | 2 + 35 files changed, 515 insertions(+), 82 deletions(-) create mode 100644 modules/openapi-generator-core/pom.xml create mode 100644 modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java create mode 100644 modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingEngineAdapter.java create mode 100644 modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingGenerator.java create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/handlebars/StringHelpers.java rename modules/openapi-generator/src/main/java/org/openapitools/codegen/{ => templating}/mustache/CamelCaseLambda.java (97%) rename modules/openapi-generator/src/main/java/org/openapitools/codegen/{ => templating}/mustache/CaseFormatLambda.java (96%) rename modules/openapi-generator/src/main/java/org/openapitools/codegen/{ => templating}/mustache/IndentedLambda.java (98%) rename modules/openapi-generator/src/main/java/org/openapitools/codegen/{ => templating}/mustache/JoinWithCommaLambda.java (96%) rename modules/openapi-generator/src/main/java/org/openapitools/codegen/{ => templating}/mustache/LowercaseLambda.java (97%) rename modules/openapi-generator/src/main/java/org/openapitools/codegen/{ => templating}/mustache/TitlecaseLambda.java (98%) rename modules/openapi-generator/src/main/java/org/openapitools/codegen/{ => templating}/mustache/UppercaseLambda.java (96%) mode change 100644 => 100755 modules/openapi-generator/src/main/resources/typescript-axios/git_push.sh.mustache create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/handlebars/StringHelpersTest.java diff --git a/Dockerfile b/Dockerfile index e53ca77d84a8..6264997fd610 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY ./modules/openapi-generator-gradle-plugin ${GEN_DIR}/modules/openapi-genera COPY ./modules/openapi-generator-maven-plugin ${GEN_DIR}/modules/openapi-generator-maven-plugin COPY ./modules/openapi-generator-online ${GEN_DIR}/modules/openapi-generator-online COPY ./modules/openapi-generator-cli ${GEN_DIR}/modules/openapi-generator-cli +COPY ./modules/openapi-generator-core ${GEN_DIR}/modules/openapi-generator-core COPY ./modules/openapi-generator ${GEN_DIR}/modules/openapi-generator COPY ./pom.xml ${GEN_DIR} diff --git a/README.md b/README.md index 9d1bbff4fdb6..ac23eef4dd1b 100644 --- a/README.md +++ b/README.md @@ -426,9 +426,12 @@ SYNOPSIS [--artifact-version ] [(-c | --config )] [-D ...] + [(-e | --engine )] + [--enable-post-process-file] [(-g | --generator-name )] - [--git-repo-id ] [--git-user-id ] - [--group-id ] [--http-user-agent ] + [--generate-alias-as-model] [--git-repo-id ] + [--git-user-id ] [--group-id ] + [--http-user-agent ] (-i | --input-spec ) [--ignore-file-override ] [--import-mappings ...] @@ -584,6 +587,9 @@ OpenAPI Generator core team members are contributors who have been making signif :heart: = Link to support the contributor directly #### Template Creator + +**NOTE**: Embedded templates are only supported in _Mustache_ format. Support for all other formats is experimental and subject to change at any time. + Here is a list of template creators: * API Clients: * Ada: @stcarrez diff --git a/bin/utils/release_version_update.sh b/bin/utils/release_version_update.sh index c7a41c0d12ca..d79b6b04de2e 100755 --- a/bin/utils/release_version_update.sh +++ b/bin/utils/release_version_update.sh @@ -36,6 +36,7 @@ echo "Release preparation: replacing $FROM with $TO in different files" declare -a files=("modules/openapi-generator-cli/pom.xml" "modules/openapi-generator-gradle-plugin/gradle.properties" "modules/openapi-generator-gradle-plugin/pom.xml" + "modules/openapi-generator-core/pom.xml" "modules/openapi-generator-maven-plugin/pom.xml" "modules/openapi-generator-online/pom.xml" "modules/openapi-generator/pom.xml" diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java index 5191bbbd1f9b..28dc4dcdb261 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java @@ -70,6 +70,10 @@ public class Generate implements Runnable { description = "folder containing the template files") private String templateDir; + @Option(name = {"-e", "--engine"}, title = "templating engine", + description = "templating engine: \"mustache\" (default) or \"handlebars\" (beta)") + private String templatingEngine; + @Option( name = {"-a", "--auth"}, title = "authorization", @@ -284,6 +288,10 @@ public void run() { configurator.setTemplateDir(templateDir); } + if (isNotEmpty(templatingEngine)) { + configurator.setTemplatingEngineName(templatingEngine); + } + if (isNotEmpty(apiPackage)) { configurator.setApiPackage(apiPackage); } diff --git a/modules/openapi-generator-core/pom.xml b/modules/openapi-generator-core/pom.xml new file mode 100644 index 000000000000..5e2b77ab81c6 --- /dev/null +++ b/modules/openapi-generator-core/pom.xml @@ -0,0 +1,16 @@ + + + + openapi-generator-project + org.openapitools + 4.0.0-SNAPSHOT + ../.. + + 4.0.0 + + openapi-generator-core + openapi-generator-core + https://github.com/openapitools/openapi-generator + \ No newline at end of file diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java new file mode 100644 index 000000000000..3255b9c85846 --- /dev/null +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java @@ -0,0 +1,40 @@ +package org.openapitools.codegen.api; + +import java.util.Locale; + +/** + * Provides abstractions around the template engine adapter interface, for reuse by implementers. + */ +public abstract class AbstractTemplatingEngineAdapter implements TemplatingEngineAdapter { + + /** + * Gets all possible template paths for a given location. + * + * @param location The full location of the template. + * + * @return A new array of locations, modified according to the extensions or other adapter rules. + */ + protected String[] getModifiedFileLocation(String location) { + String[] extensions = getFileExtensions(); + String[] result = new String[extensions.length]; + for (int i = 0; i < extensions.length; i++) { + String extension = extensions[i]; + result[i] = String.format(Locale.ROOT, "%s.%s", getPathWithoutExtension(location), extension); + } + return result; + } + + /** + * Returns the path without an extension for an input location. + * + * @param location The location of the file, with original file extension intact. + * + * @return The full path, without extension (e.g. /path/to/file.txt => /path/to/file) + */ + private String getPathWithoutExtension(String location) { + if (location == null) return null; + int idx = location.lastIndexOf('.'); + if (idx == -1) return location; + return location.substring(0, idx); + } +} diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingEngineAdapter.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingEngineAdapter.java new file mode 100644 index 000000000000..6340bd2b282c --- /dev/null +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingEngineAdapter.java @@ -0,0 +1,30 @@ +package org.openapitools.codegen.api; + +import java.io.IOException; +import java.util.Map; + +/** + * Each templating engine is called by an Adapter, selected at runtime + */ +public interface TemplatingEngineAdapter{ + + /** + * Compiles a template into a string + * + * @param generator From where we can fetch the templates content (e.g. an instance of DefaultGenerator) + * @param bundle The map of values to pass to the template + * @param templateFile The name of the template (e.g. model.mustache ) + * @return the processed template result + * @throws IOException an error ocurred in the template processing + */ + String compileTemplate(TemplatingGenerator generator, Map bundle, + String templateFile) throws IOException; + + /** + * During generation, if a supporting file has a file extension that is + * inside that array, then it is considered a templated supporting file + * and we use the templating engine adapter to generate it + * @return string array of the valid file extensions for this templating engine + */ + String[] getFileExtensions(); +} diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingGenerator.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingGenerator.java new file mode 100644 index 000000000000..498067e51354 --- /dev/null +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplatingGenerator.java @@ -0,0 +1,17 @@ +package org.openapitools.codegen.api; + +/** + * interface to the full template content + * implementers might take into account the -t cli option, + * look in the resources for a language specific template, etc + */ +public interface TemplatingGenerator { + + /** + * returns the template content by name + * @param name the template name (e.g. model.mustache) + * @return the contents of that template + */ + String getFullTemplateContents(String name); + +} diff --git a/modules/openapi-generator/pom.xml b/modules/openapi-generator/pom.xml index 49d287cea7e4..4c003f2f3210 100644 --- a/modules/openapi-generator/pom.xml +++ b/modules/openapi-generator/pom.xml @@ -215,6 +215,16 @@ jmustache ${jmustache-version} + + com.github.jknack + handlebars + ${handlebars.java-version} + + + com.github.jknack + handlebars-jackson2 + ${handlebars.java-version} + commons-io commons-io @@ -300,6 +310,11 @@ jackson-datatype-threetenbp ${jackson-threetenbp-version} + + org.openapitools + openapi-generator-core + 4.0.0-SNAPSHOT + diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java index 65bd31ceb55f..f4f62b30bcb9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java @@ -22,6 +22,7 @@ import java.nio.file.StandardCopyOption; import java.util.Arrays; import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.api.TemplatingGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +31,7 @@ import java.util.Scanner; import java.util.regex.Pattern; -public abstract class AbstractGenerator { +public abstract class AbstractGenerator implements TemplatingGenerator { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGenerator.class); /** diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java index 2e788e48cf7f..4ebbdf5c6370 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java @@ -24,6 +24,7 @@ import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.servers.ServerVariable; +import org.openapitools.codegen.api.TemplatingEngineAdapter; import java.io.File; import java.util.List; @@ -149,6 +150,8 @@ public interface CodegenConfig { Compiler processCompiler(Compiler compiler); + TemplatingEngineAdapter processTemplatingEngine(TemplatingEngineAdapter templatingEngine); + String sanitizeTag(String tag); String toApiFilename(String name); @@ -260,8 +263,11 @@ public interface CodegenConfig { */ void setOpenAPI(OpenAPI openAPI); + void setTemplatingEngine(TemplatingEngineAdapter s); + + TemplatingEngineAdapter getTemplatingEngine(); + public boolean isEnableMinimalUpdate(); public void setEnableMinimalUpdate(boolean isEnableMinimalUpdate); - } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java index 517e829aaf12..f9c21a4968b0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java @@ -184,6 +184,9 @@ public class CodegenConstants { public static final String DOTNET_FRAMEWORK = "targetFramework"; public static final String DOTNET_FRAMEWORK_DESC = "The target .NET framework version."; + public static final String TEMPLATING_ENGINE = "templatingEngine"; + public static final String TEMPLATING_ENGINE_DESC = "The templating engine plugin to use: \"mustache\" (default) or \"handlebars\" (beta)"; + public static enum MODEL_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, original} public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, original, UPPERCASE} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 3032d3cc3702..5f7693a97108 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -41,9 +41,11 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.openapitools.codegen.CodegenDiscriminator.MappedModel; +import org.openapitools.codegen.api.TemplatingEngineAdapter; import org.openapitools.codegen.config.GeneratorProperties; import org.openapitools.codegen.examples.ExampleGenerator; import org.openapitools.codegen.serializer.SerializerUtils; +import org.openapitools.codegen.templating.MustacheEngineAdapter; import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -111,6 +113,8 @@ public class DefaultCodegen implements CodegenConfig { protected String ignoreFilePathOverride; // flag to indicate whether to use environment variable to post process file protected boolean enablePostProcessFile = false; + private TemplatingEngineAdapter templatingEngine = new MustacheEngineAdapter(); + // flag to indicate whether to only update files whose contents have changed protected boolean enableMinimalUpdate = false; @@ -462,6 +466,12 @@ public Compiler processCompiler(Compiler compiler) { return compiler; } + // override with any special handling for the templating engine + @SuppressWarnings("unused") + public TemplatingEngineAdapter processTemplatingEngine(TemplatingEngineAdapter templatingEngine) { + return templatingEngine; + } + // override with any special text escaping logic @SuppressWarnings("static-method") public String escapeText(String input) { @@ -3828,6 +3838,16 @@ public String sanitizeName(String name) { return sanitizeName(name, "\\W"); } + @Override + public void setTemplatingEngine(TemplatingEngineAdapter templatingEngine) { + this.templatingEngine = templatingEngine; + } + + @Override + public TemplatingEngineAdapter getTemplatingEngine() { + return this.templatingEngine; + } + /** * Sanitize name (parameter, property, method, etc) * diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 48083534dd58..adef44d4555d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -17,8 +17,6 @@ package org.openapitools.codegen; -import com.samskivert.mustache.Mustache; -import com.samskivert.mustache.Template; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; @@ -35,7 +33,10 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.config.GeneratorProperties; +import org.openapitools.codegen.api.TemplatingEngineAdapter; import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; +import org.openapitools.codegen.templating.MustacheEngineAdapter; +import org.openapitools.codegen.config.GeneratorProperties; import org.openapitools.codegen.utils.ImplementationVersion; import org.openapitools.codegen.utils.ModelUtils; import org.openapitools.codegen.utils.URLPathUtils; @@ -53,6 +54,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator { protected ClientOptInput opts; protected OpenAPI openAPI; protected CodegenIgnoreProcessor ignoreProcessor; + protected TemplatingEngineAdapter templatingEngine; private Boolean generateApis = null; private Boolean generateModels = null; private Boolean generateSupportingFiles = null; @@ -77,6 +79,7 @@ public Generator opts(ClientOptInput opts) { this.openAPI = opts.getOpenAPI(); this.config = opts.getConfig(); this.config.additionalProperties().putAll(opts.getOpts().getProperties()); + this.templatingEngine = this.config.getTemplatingEngine(); String ignoreFileLocation = this.config.getIgnoreFilePathOverride(); if (ignoreFileLocation != null) { @@ -95,6 +98,13 @@ public Generator opts(ClientOptInput opts) { return this; } + private void configPostProcessMustacheCompiler() { + if (this.templatingEngine instanceof MustacheEngineAdapter) { + MustacheEngineAdapter mustacheEngineAdapter = (MustacheEngineAdapter) this.templatingEngine; + mustacheEngineAdapter.setCompiler(this.config.processCompiler(mustacheEngineAdapter.getCompiler())); + } + } + /** * Programmatically disable the output of .openapi-generator/VERSION, .openapi-generator-ignore, * or other metadata files used by OpenAPI Generator. @@ -702,21 +712,9 @@ private void generateSupportingFiles(List files, Map bundl } if (ignoreProcessor.allowsFile(new File(outputFilename))) { - if (templateFile.endsWith("mustache")) { - String template = readTemplate(templateFile); - Mustache.Compiler compiler = Mustache.compiler(); - compiler = config.processCompiler(compiler); - Template tmpl = compiler - .withLoader(new Mustache.TemplateLoader() { - @Override - public Reader getTemplate(String name) { - return getTemplateReader(getFullTemplateFile(config, name + ".mustache")); - } - }) - .defaultValue("") - .compile(template); - - writeToFile(outputFilename, tmpl.execute(bundle)); + if (Arrays.stream(templatingEngine.getFileExtensions()).anyMatch(templateFile::endsWith)) { + String templateContent = templatingEngine.compileTemplate(this, bundle, support.templateFile); + writeToFile(outputFilename, templateContent); File written = new File(outputFilename); files.add(written); if (config.isEnablePostProcessFile()) { @@ -891,6 +889,9 @@ public List generate() { configureGeneratorProperties(); configureOpenAPIInfo(); + // If the template adapter is mustache, we'll set the config-modified Compiler. + configPostProcessMustacheCompiler(); + List files = new ArrayList(); // models List filteredSchemas = ModelUtils.getSchemasUsedOnlyInFormParam(openAPI); @@ -911,24 +912,16 @@ public List generate() { return files; } + @Override + public String getFullTemplateContents(String templateName) { + return readTemplate(getFullTemplateFile(config, templateName)); + } + protected File processTemplateToFile(Map templateData, String templateName, String outputFilename) throws IOException { String adjustedOutputFilename = outputFilename.replaceAll("//", "/").replace('/', File.separatorChar); if (ignoreProcessor.allowsFile(new File(adjustedOutputFilename))) { - String templateFile = getFullTemplateFile(config, templateName); - String template = readTemplate(templateFile); - Mustache.Compiler compiler = Mustache.compiler(); - compiler = config.processCompiler(compiler); - Template tmpl = compiler - .withLoader(new Mustache.TemplateLoader() { - @Override - public Reader getTemplate(String name) { - return getTemplateReader(getFullTemplateFile(config, name + ".mustache")); - } - }) - .defaultValue("") - .compile(template); - - writeToFile(adjustedOutputFilename, tmpl.execute(templateData)); + String templateContent = templatingEngine.compileTemplate(this, templateData, templateName); + writeToFile(adjustedOutputFilename, templateContent); return new File(adjustedOutputFilename); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java index 9c813441ed5f..82a12aec16c0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java @@ -17,8 +17,6 @@ package org.openapitools.codegen.config; -import static org.apache.commons.lang3.StringUtils.isNotEmpty; - import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.databind.ObjectMapper; @@ -29,42 +27,26 @@ import io.swagger.v3.parser.core.models.AuthorizationValue; import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.SwaggerParseResult; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.Validate; -import org.openapitools.codegen.CliOption; -import org.openapitools.codegen.ClientOptInput; -import org.openapitools.codegen.ClientOpts; -import org.openapitools.codegen.CodegenConfig; -import org.openapitools.codegen.CodegenConfigLoader; -import org.openapitools.codegen.CodegenConstants; -import org.openapitools.codegen.SpecValidationException; +import org.openapitools.codegen.*; import org.openapitools.codegen.auth.AuthParser; -import org.openapitools.codegen.languages.CSharpNancyFXServerCodegen; -import org.openapitools.codegen.languages.CppQt5ClientCodegen; -import org.openapitools.codegen.languages.CppRestSdkClientCodegen; -import org.openapitools.codegen.languages.CppTizenClientCodegen; -import org.openapitools.codegen.languages.JavaJerseyServerCodegen; -import org.openapitools.codegen.languages.PhpLumenServerCodegen; -import org.openapitools.codegen.languages.PhpSlimServerCodegen; -import org.openapitools.codegen.languages.PhpZendExpressivePathHandlerServerCodegen; -import org.openapitools.codegen.languages.RubySinatraServerCodegen; -import org.openapitools.codegen.languages.ScalaAkkaClientCodegen; -import org.openapitools.codegen.languages.ScalaHttpClientCodegen; -import org.openapitools.codegen.languages.SwiftClientCodegen; +import org.openapitools.codegen.languages.*; +import org.openapitools.codegen.templating.HandlebarsEngineAdapter; +import org.openapitools.codegen.templating.MustacheEngineAdapter; import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.nio.file.Paths; +import java.util.*; + +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + /** * A class that contains all codegen configuration properties a user would want to manipulate. An * instance could be created by deserializing a JSON file or being populated from CLI or Maven @@ -103,6 +85,7 @@ public class CodegenConfigurator implements Serializable { private boolean enablePostProcessFile; private boolean enableMinimalUpdate; private String templateDir; + private String templatingEngineName; private String auth; private String apiPackage; private String modelPackage; @@ -582,6 +565,7 @@ public ClientOptInput toClientOptInput() { checkAndSetAdditionalProperty(artifactVersion, CodegenConstants.ARTIFACT_VERSION); checkAndSetAdditionalProperty(templateDir, toAbsolutePathStr(templateDir), CodegenConstants.TEMPLATE_DIR); + checkAndSetAdditionalProperty(templatingEngineName, CodegenConstants.TEMPLATING_ENGINE); checkAndSetAdditionalProperty(modelNamePrefix, CodegenConstants.MODEL_NAME_PREFIX); checkAndSetAdditionalProperty(modelNameSuffix, CodegenConstants.MODEL_NAME_SUFFIX); checkAndSetAdditionalProperty(gitUserId, CodegenConstants.GIT_USER_ID); @@ -595,6 +579,13 @@ public ClientOptInput toClientOptInput() { config.setLibrary(library); } + // Built-in templates are mustache, but allow users to use a simplified handlebars engine for their custom templates. + if (isEmpty(templatingEngineName) || templatingEngineName.equals("mustache")) { + config.setTemplatingEngine(new MustacheEngineAdapter()); + } else if (templatingEngineName.equals("handlebars")) { + config.setTemplatingEngine(new HandlebarsEngineAdapter()); + } + config.additionalProperties().putAll(additionalProperties); ClientOptInput input = new ClientOptInput().config(config); @@ -602,8 +593,7 @@ public ClientOptInput toClientOptInput() { final List authorizationValues = AuthParser.parse(auth); ParseOptions options = new ParseOptions(); options.setResolve(true); - SwaggerParseResult result = - new OpenAPIParser().readLocation(inputSpec, authorizationValues, options); + SwaggerParseResult result = new OpenAPIParser().readLocation(inputSpec, authorizationValues, options); Set validationMessages = new HashSet<>(result.getMessages()); OpenAPI specification = result.getOpenAPI(); @@ -732,4 +722,9 @@ public static CodegenConfigurator fromFile(String configFile) { } return null; } + + public CodegenConfigurator setTemplatingEngineName(String templatingEngineName) { + this.templatingEngineName = templatingEngineName; + return this; + } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java index ff5cdc474ea4..63a99ff9384d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java @@ -25,7 +25,7 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.*; -import org.openapitools.codegen.mustache.*; +import org.openapitools.codegen.templating.mustache.*; import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java index 62b5331f1e0d..dffc475fb76d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCppCodegen.java @@ -25,7 +25,7 @@ import org.openapitools.codegen.CodegenConfig; import org.openapitools.codegen.CodegenProperty; import org.openapitools.codegen.DefaultCodegen; -import org.openapitools.codegen.mustache.IndentedLambda; +import org.openapitools.codegen.templating.mustache.IndentedLambda; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java index 61fcfd7057b9..b9d46f66a9ea 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ErlangClientCodegen.java @@ -21,7 +21,7 @@ import com.samskivert.mustache.Template; import io.swagger.v3.oas.models.media.Schema; import org.openapitools.codegen.*; -import org.openapitools.codegen.mustache.JoinWithCommaLambda; +import org.openapitools.codegen.templating.mustache.JoinWithCommaLambda; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index c42f839ceca9..24681b371e6a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -23,7 +23,7 @@ import org.openapitools.codegen.languages.features.BeanValidationFeatures; import org.openapitools.codegen.languages.features.GzipFeatures; import org.openapitools.codegen.languages.features.PerformBeanValidationFeatures; -import org.openapitools.codegen.mustache.CaseFormatLambda; +import org.openapitools.codegen.templating.mustache.CaseFormatLambda; import org.openapitools.codegen.utils.ProcessUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java index 5c8defdf871b..8c31c1a8a5e6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/KotlinServerCodegen.java @@ -24,7 +24,7 @@ import org.openapitools.codegen.CodegenConstants; import org.openapitools.codegen.CodegenType; import org.openapitools.codegen.SupportingFile; -import org.openapitools.codegen.mustache.*; +import org.openapitools.codegen.templating.mustache.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java index e8c8346db3ee..0285e31ccf72 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaPlayFrameworkServerCodegen.java @@ -21,7 +21,7 @@ import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.Schema; import org.openapitools.codegen.*; -import org.openapitools.codegen.mustache.*; +import org.openapitools.codegen.templating.mustache.IndentedLambda; import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java new file mode 100644 index 000000000000..fb4dcfcdcd7e --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/HandlebarsEngineAdapter.java @@ -0,0 +1,75 @@ +package org.openapitools.codegen.templating; + +import com.github.jknack.handlebars.Context; +import com.github.jknack.handlebars.Handlebars; +import com.github.jknack.handlebars.Jackson2Helper; +import com.github.jknack.handlebars.Template; +import com.github.jknack.handlebars.context.FieldValueResolver; +import com.github.jknack.handlebars.context.JavaBeanValueResolver; +import com.github.jknack.handlebars.context.MapValueResolver; +import com.github.jknack.handlebars.helper.ConditionalHelpers; +import com.github.jknack.handlebars.helper.StringHelpers; +import com.github.jknack.handlebars.io.AbstractTemplateLoader; +import com.github.jknack.handlebars.io.StringTemplateSource; +import com.github.jknack.handlebars.io.TemplateLoader; +import com.github.jknack.handlebars.io.TemplateSource; +import org.openapitools.codegen.api.AbstractTemplatingEngineAdapter; +import org.openapitools.codegen.api.TemplatingGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Locale; +import java.util.Map; + + +public class HandlebarsEngineAdapter extends AbstractTemplatingEngineAdapter { + static Logger LOGGER = LoggerFactory.getLogger(HandlebarsEngineAdapter.class); + private final String[] extensions = new String[]{"handlebars", "hbs"}; + + public String compileTemplate(TemplatingGenerator generator, + Map bundle, String templateFile) throws IOException { + TemplateLoader loader = new AbstractTemplateLoader() { + @Override + public TemplateSource sourceAt(String location) { + return findTemplate(generator, location); + } + }; + + Context context = Context + .newBuilder(bundle) + .resolver( + MapValueResolver.INSTANCE, + JavaBeanValueResolver.INSTANCE, + FieldValueResolver.INSTANCE) + .build(); + + Handlebars handlebars = new Handlebars(loader); + handlebars.registerHelperMissing((obj, options) -> { + LOGGER.warn(String.format(Locale.ROOT, "Unregistered helper name '%s', processing template:\n%s", options.helperName, options.fn.text())); + return ""; + }); + handlebars.registerHelper("json", Jackson2Helper.INSTANCE); + StringHelpers.register(handlebars); + handlebars.registerHelpers(ConditionalHelpers.class); + handlebars.registerHelpers(org.openapitools.codegen.templating.handlebars.StringHelpers.class); + Template tmpl = handlebars.compile(templateFile); + return tmpl.apply(context); + } + + public TemplateSource findTemplate(TemplatingGenerator generator, String templateFile) { + for (String file : getModifiedFileLocation(templateFile)) { + try { + return new StringTemplateSource(file, generator.getFullTemplateContents(file)); + } catch (Exception ignored) { + } + } + throw new RuntimeException("couldnt find a subtemplate " + templateFile); + } + + @Override + public String[] getFileExtensions() { + return extensions; + } +} + diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java new file mode 100644 index 000000000000..37e85d1ecdeb --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/MustacheEngineAdapter.java @@ -0,0 +1,52 @@ +package org.openapitools.codegen.templating; + +import com.samskivert.mustache.Mustache; +import com.samskivert.mustache.Template; +import org.openapitools.codegen.api.TemplatingEngineAdapter; +import org.openapitools.codegen.api.TemplatingGenerator; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Map; + + +public class MustacheEngineAdapter implements TemplatingEngineAdapter { + + public String[] extensions = new String[]{"mustache"}; + Mustache.Compiler compiler = Mustache.compiler(); + + @Override + public String compileTemplate(TemplatingGenerator generator, Map bundle, + String templateFile) throws IOException { + Template tmpl = compiler + .withLoader(name -> findTemplate(generator, name)) + .defaultValue("") + .compile(generator.getFullTemplateContents(templateFile)); + + return tmpl.execute(bundle); + } + + public Reader findTemplate(TemplatingGenerator generator, String name) { + for (String extension : extensions) { + try { + return new StringReader(generator.getFullTemplateContents(name + "." + extension)); + } catch (Exception ignored) { + } + } + throw new RuntimeException("couldnt find a subtemplate " + name); + } + + public Mustache.Compiler getCompiler() { + return compiler; + } + + public void setCompiler(Mustache.Compiler compiler) { + this.compiler = compiler; + } + + @Override + public String[] getFileExtensions() { + return extensions; + } +} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/handlebars/StringHelpers.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/handlebars/StringHelpers.java new file mode 100644 index 000000000000..c8a8170ca96f --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/handlebars/StringHelpers.java @@ -0,0 +1,65 @@ +package org.openapitools.codegen.templating.handlebars; + +import com.github.jknack.handlebars.Helper; +import com.github.jknack.handlebars.Options; +import com.github.jknack.handlebars.TagType; + +import java.io.IOException; +import java.util.Locale; + +public enum StringHelpers implements Helper { + + /** + * Indicates the string starts with the defined value. + * For example: + * + *
+     * {{startsWith a b ["insensitive"]}}
+     * 
+ * + *
+     * {{startsWith a text='b' [insensitive=true]}}
+     * 
+ * + * Render 'yes' or 'no': + *
+     *   {{#startsWith a b}}
+     *     yes
+     *   {{else}}
+     *     no
+     *   {{/startsWith}}
+     * 
+ * + * Render 'true' or 'false': + *
+     *   {{startsWith a b}}
+     * 
+ * + * Render 'y' or 'n': + *
+     *   {{startsWith a b yes='y' no='n'}}
+     * 
+ * + * If value is "handlebars.java", the output will be "Handlebars.java". + */ + startsWith { + @Override + public Object apply(Object value, Options options) throws IOException { + String match = options.param(0, options.hash("text", "")); + if (match.length() < 1) { + return false; + } + + boolean caseInsensitive = options.hash("insensitive", false); + boolean result = caseInsensitive ? value.toString().toLowerCase(Locale.ROOT).startsWith(match.toLowerCase(Locale.ROOT)) : value.toString().startsWith(match); + + if (options.tagType == TagType.SECTION) { + return result ? options.fn() : options.inverse(); + } + + return result + ? options.hash("yes", true) + : options.hash("no", false); + } + } +} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/CamelCaseLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/CamelCaseLambda.java similarity index 97% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/CamelCaseLambda.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/CamelCaseLambda.java index 110302b1bb8d..2c5fbd9e5520 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/CamelCaseLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/CamelCaseLambda.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.openapitools.codegen.mustache; +package org.openapitools.codegen.templating.mustache; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/CaseFormatLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/CaseFormatLambda.java similarity index 96% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/CaseFormatLambda.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/CaseFormatLambda.java index 6f449c42ce53..a925525380b6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/CaseFormatLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/CaseFormatLambda.java @@ -1,4 +1,4 @@ -package org.openapitools.codegen.mustache; +package org.openapitools.codegen.templating.mustache; import com.google.common.base.CaseFormat; import com.samskivert.mustache.Mustache; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/IndentedLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/IndentedLambda.java similarity index 98% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/IndentedLambda.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/IndentedLambda.java index 66873c311c1b..c3656ed64aff 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/IndentedLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/IndentedLambda.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.openapitools.codegen.mustache; +package org.openapitools.codegen.templating.mustache; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/JoinWithCommaLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/JoinWithCommaLambda.java similarity index 96% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/JoinWithCommaLambda.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/JoinWithCommaLambda.java index 70e3afa6a859..ec99428222b7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/JoinWithCommaLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/JoinWithCommaLambda.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.openapitools.codegen.mustache; +package org.openapitools.codegen.templating.mustache; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/LowercaseLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/LowercaseLambda.java similarity index 97% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/LowercaseLambda.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/LowercaseLambda.java index ba466fd0073a..42f292f2400a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/LowercaseLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/LowercaseLambda.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.openapitools.codegen.mustache; +package org.openapitools.codegen.templating.mustache; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/TitlecaseLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/TitlecaseLambda.java similarity index 98% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/TitlecaseLambda.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/TitlecaseLambda.java index e9ec42991b6e..a51218f0eeab 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/TitlecaseLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/TitlecaseLambda.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.openapitools.codegen.mustache; +package org.openapitools.codegen.templating.mustache; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/UppercaseLambda.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/UppercaseLambda.java similarity index 96% rename from modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/UppercaseLambda.java rename to modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/UppercaseLambda.java index 98b835b55bc4..b225eafd3c66 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/mustache/UppercaseLambda.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/mustache/UppercaseLambda.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.openapitools.codegen.mustache; +package org.openapitools.codegen.templating.mustache; import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Template; diff --git a/modules/openapi-generator/src/main/resources/typescript-axios/git_push.sh.mustache b/modules/openapi-generator/src/main/resources/typescript-axios/git_push.sh.mustache old mode 100644 new mode 100755 diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJerseyServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJerseyServerCodegenTest.java index 9116ed7b00d5..6d2ac1c23e77 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJerseyServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJerseyServerCodegenTest.java @@ -10,6 +10,7 @@ import org.openapitools.codegen.MockDefaultGenerator.WrittenTemplateBasedFile; import org.openapitools.codegen.TestUtils; import org.openapitools.codegen.languages.JavaJerseyServerCodegen; +import org.openapitools.codegen.templating.MustacheEngineAdapter; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/handlebars/StringHelpersTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/handlebars/StringHelpersTest.java new file mode 100644 index 000000000000..5bea5678bbba --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/templating/handlebars/StringHelpersTest.java @@ -0,0 +1,86 @@ +package org.openapitools.codegen.templating.handlebars; + +import com.github.jknack.handlebars.Context; +import com.github.jknack.handlebars.Handlebars; +import com.github.jknack.handlebars.Template; +import com.github.jknack.handlebars.context.FieldValueResolver; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.HashMap; + +import static org.testng.Assert.assertEquals; + +public class StringHelpersTest { + + private Handlebars handlebars = null; + + private void evaluate(HashMap data, String template, String expect) throws IOException { + + Context context = Context + .newBuilder(data) + .resolver( + FieldValueResolver.INSTANCE) + .build(); + + Template tmpl = handlebars.compileInline(template); + String actual = tmpl.apply(context); + assertEquals(actual, expect); + } + + @BeforeMethod + public void setup() { + handlebars = new Handlebars(); + handlebars.registerHelpers(StringHelpers.class); + } + + @Test(description = "Handlebars StringHelpers.startsWith, section") + public void startsWithSectionalTest() throws IOException { + HashMap data = new HashMap() {{ + put("asdf", "asdf"); + put("a", "a"); + put("b", "b"); + }}; + + String template = "{{~#startsWith asdf a ~}}yes{{~else~}}no{{~/startsWith~}}"; + evaluate(data, template, "yes"); + + template = "{{~#startsWith asdf b ~}}yes{{~else~}}no{{~/startsWith~}}"; + evaluate(data, template, "no"); + } + + @Test(description = "Handlebars StringHelpers.startsWith") + public void startsWithTest() throws IOException { + HashMap data = new HashMap() {{ + put("asdf", "asdf"); + put("ASDF", "ASDF"); + put("a", "a"); + put("b", "b"); + }}; + evaluate(data, "{{startsWith asdf a}}", "true"); + evaluate(data, "{{startsWith asdf b}}", "false"); + evaluate(data, "{{startsWith ASDF a insensitive=true }}", "true"); + evaluate(data, "{{startsWith ASDF a insensitive=false }}", "false"); + evaluate(data, "{{startsWith ASDF 'a' insensitive=true }}", "true"); + evaluate(data, "{{startsWith ASDF b insensitive=true }}", "false"); + evaluate(data, "{{startsWith ASDF 'b' insensitive=true }}", "false"); + evaluate(data, "{{startsWith ASDF insensitive=true text='a'}}", "true"); + evaluate(data, "{{startsWith ASDF insensitive=true text='a' yes='✓' no='✘'}}", "✓"); + evaluate(data, "{{startsWith ASDF insensitive=false text='a' yes='✓' no='✘'}}", "✘"); + } + + @Test(description = "Handlebars StringHelpers.startsWith, yes/no override") + public void startsWithYesOverrideTest() throws IOException { + HashMap data = new HashMap() {{ + put("asdf", "asdf"); + put("a", "a"); + put("b", "b"); + }}; + String template = "{{startsWith asdf a yes='y' no='n'}}"; + evaluate(data, template, "y"); + + template = "{{startsWith asdf b yes='y' no='n'}}"; + evaluate(data, template, "n"); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index b0a268b4f0d0..7458c5b549bd 100644 --- a/pom.xml +++ b/pom.xml @@ -1293,6 +1293,7 @@ modules/openapi-generator-maven-plugin modules/openapi-generator-gradle-plugin modules/openapi-generator-online + modules/openapi-generator-core target/site @@ -1372,6 +1373,7 @@ 1.7.12 3.2.1 1.14 + 4.1.2 6.14.3 2.22.1 1.43