forked from loafle/openapi-generator-original
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.
This commit is contained in:
parent
61ed2eecef
commit
8bbeb8b7e3
@ -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}
|
||||
|
||||
|
10
README.md
10
README.md
@ -426,9 +426,12 @@ SYNOPSIS
|
||||
[--artifact-version <artifact version>]
|
||||
[(-c <configuration file> | --config <configuration file>)]
|
||||
[-D <system properties>...]
|
||||
[(-e <templating engine> | --engine <templating engine>)]
|
||||
[--enable-post-process-file]
|
||||
[(-g <generator name> | --generator-name <generator name>)]
|
||||
[--git-repo-id <git repo id>] [--git-user-id <git user id>]
|
||||
[--group-id <group id>] [--http-user-agent <http user agent>]
|
||||
[--generate-alias-as-model] [--git-repo-id <git repo id>]
|
||||
[--git-user-id <git user id>] [--group-id <group id>]
|
||||
[--http-user-agent <http user agent>]
|
||||
(-i <spec file> | --input-spec <spec file>)
|
||||
[--ignore-file-override <ignore file override location>]
|
||||
[--import-mappings <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
|
||||
|
@ -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"
|
||||
|
@ -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 class Generate implements Runnable {
|
||||
configurator.setTemplateDir(templateDir);
|
||||
}
|
||||
|
||||
if (isNotEmpty(templatingEngine)) {
|
||||
configurator.setTemplatingEngineName(templatingEngine);
|
||||
}
|
||||
|
||||
if (isNotEmpty(apiPackage)) {
|
||||
configurator.setApiPackage(apiPackage);
|
||||
}
|
||||
|
16
modules/openapi-generator-core/pom.xml
Normal file
16
modules/openapi-generator-core/pom.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>openapi-generator-project</artifactId>
|
||||
<groupId>org.openapitools</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../..</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>openapi-generator-core</artifactId>
|
||||
<name>openapi-generator-core</name>
|
||||
<url>https://github.com/openapitools/openapi-generator</url>
|
||||
</project>
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<String, Object> 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();
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -215,6 +215,16 @@
|
||||
<artifactId>jmustache</artifactId>
|
||||
<version>${jmustache-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jknack</groupId>
|
||||
<artifactId>handlebars</artifactId>
|
||||
<version>${handlebars.java-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jknack</groupId>
|
||||
<artifactId>handlebars-jackson2</artifactId>
|
||||
<version>${handlebars.java-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
@ -300,6 +310,11 @@
|
||||
<artifactId>jackson-datatype-threetenbp</artifactId>
|
||||
<version>${jackson-threetenbp-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openapitools</groupId>
|
||||
<artifactId>openapi-generator-core</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
|
@ -22,6 +22,7 @@ import java.nio.file.Files;
|
||||
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.nio.file.Paths;
|
||||
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);
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,7 @@ import io.swagger.v3.oas.models.media.Schema;
|
||||
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);
|
||||
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -41,9 +41,11 @@ import org.apache.commons.lang3.StringEscapeUtils;
|
||||
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 class DefaultCodegen implements CodegenConfig {
|
||||
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 class DefaultCodegen implements CodegenConfig {
|
||||
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)
|
||||
*
|
||||
|
@ -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.io.IOUtils;
|
||||
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 class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
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 class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
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 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
|
||||
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 class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
configureGeneratorProperties();
|
||||
configureOpenAPIInfo();
|
||||
|
||||
// If the template adapter is mustache, we'll set the config-modified Compiler.
|
||||
configPostProcessMustacheCompiler();
|
||||
|
||||
List<File> files = new ArrayList<File>();
|
||||
// models
|
||||
List<String> filteredSchemas = ModelUtils.getSchemasUsedOnlyInFormParam(openAPI);
|
||||
@ -911,24 +912,16 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullTemplateContents(String templateName) {
|
||||
return readTemplate(getFullTemplateFile(config, templateName));
|
||||
}
|
||||
|
||||
protected File processTemplateToFile(Map<String, Object> 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);
|
||||
}
|
||||
|
||||
|
@ -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,41 +27,25 @@ import io.swagger.v3.oas.models.OpenAPI;
|
||||
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 org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.openapitools.codegen.*;
|
||||
import org.openapitools.codegen.auth.AuthParser;
|
||||
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.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.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.utils.ModelUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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
|
||||
@ -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 class CodegenConfigurator implements Serializable {
|
||||
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 class CodegenConfigurator implements Serializable {
|
||||
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 class CodegenConfigurator implements Serializable {
|
||||
final List<AuthorizationValue> 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<String> validationMessages = new HashSet<>(result.getMessages());
|
||||
OpenAPI specification = result.getOpenAPI();
|
||||
@ -732,4 +722,9 @@ public class CodegenConfigurator implements Serializable {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public CodegenConfigurator setTemplatingEngineName(String templatingEngineName) {
|
||||
this.templatingEngineName = templatingEngineName;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import io.swagger.v3.oas.models.media.Schema;
|
||||
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;
|
||||
|
@ -25,7 +25,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
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;
|
||||
|
||||
|
@ -21,7 +21,7 @@ import com.samskivert.mustache.Mustache;
|
||||
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;
|
||||
|
||||
|
@ -23,7 +23,7 @@ import org.openapitools.codegen.*;
|
||||
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;
|
||||
|
@ -24,7 +24,7 @@ import org.openapitools.codegen.CliOption;
|
||||
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;
|
||||
|
||||
|
@ -21,7 +21,7 @@ import com.samskivert.mustache.Mustache;
|
||||
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;
|
||||
|
@ -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<String, Object> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<String, Object> 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;
|
||||
}
|
||||
}
|
@ -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<Object> {
|
||||
|
||||
/**
|
||||
* Indicates the string starts with the defined value.
|
||||
* For example:
|
||||
*
|
||||
* <pre>
|
||||
* {{startsWith a b ["insensitive"]}}
|
||||
* </pre>
|
||||
*
|
||||
* <pre>
|
||||
* {{startsWith a text='b' [insensitive=true]}}
|
||||
* </pre>
|
||||
*
|
||||
* Render 'yes' or 'no':
|
||||
* <pre>
|
||||
* {{#startsWith a b}}
|
||||
* yes
|
||||
* {{else}}
|
||||
* no
|
||||
* {{/startsWith}}
|
||||
* </pre>
|
||||
*
|
||||
* Render 'true' or 'false':
|
||||
* <pre>
|
||||
* {{startsWith a b}}
|
||||
* </pre>
|
||||
*
|
||||
* Render 'y' or 'n':
|
||||
* <pre>
|
||||
* {{startsWith a b yes='y' no='n'}}
|
||||
* </pre>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
0
modules/openapi-generator/src/main/resources/typescript-axios/git_push.sh.mustache
vendored
Normal file → Executable file
0
modules/openapi-generator/src/main/resources/typescript-axios/git_push.sh.mustache
vendored
Normal file → Executable file
@ -10,6 +10,7 @@ import org.openapitools.codegen.MockDefaultGenerator;
|
||||
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;
|
||||
|
@ -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<String, Object> 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<String, Object> data = new HashMap<String, Object>() {{
|
||||
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<String, Object> data = new HashMap<String, Object>() {{
|
||||
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<String, Object> data = new HashMap<String, Object>() {{
|
||||
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");
|
||||
}
|
||||
}
|
2
pom.xml
2
pom.xml
@ -1293,6 +1293,7 @@
|
||||
<module>modules/openapi-generator-maven-plugin</module>
|
||||
<module>modules/openapi-generator-gradle-plugin</module>
|
||||
<module>modules/openapi-generator-online</module>
|
||||
<module>modules/openapi-generator-core</module>
|
||||
</modules>
|
||||
<reporting>
|
||||
<outputDirectory>target/site</outputDirectory>
|
||||
@ -1372,6 +1373,7 @@
|
||||
<slf4j-version>1.7.12</slf4j-version>
|
||||
<scala-maven-plugin-version>3.2.1</scala-maven-plugin-version>
|
||||
<jmustache-version>1.14</jmustache-version>
|
||||
<handlebars.java-version>4.1.2</handlebars.java-version>
|
||||
<testng-version>6.14.3</testng-version>
|
||||
<surefire-version>2.22.1</surefire-version>
|
||||
<jmockit-version>1.43</jmockit-version>
|
||||
|
Loading…
x
Reference in New Issue
Block a user