Include common lambdas in generators (#3368)

* Include commonly used Mustache lambdas in DefaultCodegen.
* Use or extend common Mustache lambdas from DefaultCodegen.
* Extract OnChangeLambda to own file.
* Javadoc fixed in addMustacheLambdas().
This commit is contained in:
Michal Foksa
2019-07-18 06:28:40 +02:00
committed by Jérémie Bresson
parent 610ca28681
commit a85bd0f8f9
9 changed files with 141 additions and 179 deletions

View File

@@ -19,7 +19,11 @@ package org.openapitools.codegen;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Mustache.Compiler;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
@@ -48,6 +52,11 @@ import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.serializer.SerializerUtils;
import org.openapitools.codegen.templating.MustacheEngineAdapter;
import org.openapitools.codegen.templating.mustache.CamelCaseLambda;
import org.openapitools.codegen.templating.mustache.IndentedLambda;
import org.openapitools.codegen.templating.mustache.LowercaseLambda;
import org.openapitools.codegen.templating.mustache.TitlecaseLambda;
import org.openapitools.codegen.templating.mustache.UppercaseLambda;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -199,6 +208,47 @@ public class DefaultCodegen implements CodegenConfig {
}
}
/***
* Preset map builder with commonly used Mustache lambdas.
*
* To extend the map, override addMustacheLambdas(), call parent method
* first and then add additional lambdas to the returned builder.
*
* If common lambdas are not desired, override addMustacheLambdas() method
* and return empty builder.
*
* @return preinitialized map builder with common lambdas
*/
protected ImmutableMap.Builder<String, Lambda> addMustacheLambdas() {
return new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("lowercase", new LowercaseLambda().generator(this))
.put("uppercase", new UppercaseLambda())
.put("titlecase", new TitlecaseLambda())
.put("camelcase", new CamelCaseLambda().generator(this))
.put("indented", new IndentedLambda())
.put("indented_8", new IndentedLambda(8, " "))
.put("indented_12", new IndentedLambda(12, " "))
.put("indented_16", new IndentedLambda(16, " "));
}
private void registerMustacheLambdas() {
ImmutableMap<String, Lambda> lambdas = addMustacheLambdas().build();
if (lambdas.size() == 0) {
return;
}
if (additionalProperties.containsKey("lambda")) {
LOGGER.warn("A property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'. " +
"You'll likely need to use a custom template, " +
"see https://github.com/OpenAPITools/openapi-generator/blob/master/docs/templating.md. ");
additionalProperties.put("_lambda", lambdas);
} else {
additionalProperties.put("lambda", lambdas);
}
}
// override with any special post-processing for all models
@SuppressWarnings({"static-method", "unchecked"})
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
@@ -1060,6 +1110,9 @@ public class DefaultCodegen implements CodegenConfig {
// initialize special character mapping
initalizeSpecialCharacterMapping();
// Register common Mustache lambdas.
registerMustacheLambdas();
}
/**
@@ -1523,7 +1576,7 @@ public class DefaultCodegen implements CodegenConfig {
// If the format matches a typeMapping (supplied with the --typeMappings flag)
// then treat the format as a primitive type.
// This allows the typeMapping flag to add a new custom type which can then
// be used in the format field.
// be used in the format field.
return schema.getFormat();
}
return "string";

View File

@@ -17,8 +17,9 @@
package org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import com.google.common.collect.ImmutableMap.Builder;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
@@ -359,32 +360,12 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
// This either updates additionalProperties with the above fixes, or sets the default if the option was not specified.
additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix);
addMustacheLambdas(additionalProperties);
}
private void addMustacheLambdas(Map<String, Object> objs) {
Map<String, Mustache.Lambda> lambdas = new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("lowercase", new LowercaseLambda().generator(this))
.put("uppercase", new UppercaseLambda())
.put("titlecase", new TitlecaseLambda())
.put("camelcase", new CamelCaseLambda().generator(this))
.put("camelcase_param", new CamelCaseLambda().generator(this).escapeAsParamName(true))
.put("indented", new IndentedLambda())
.put("indented_8", new IndentedLambda(8, " "))
.put("indented_12", new IndentedLambda(12, " "))
.put("indented_16", new IndentedLambda(16, " "))
.build();
if (objs.containsKey("lambda")) {
LOGGER.warn("A property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'. " +
"You'll likely need to use a custom template, " +
"see https://github.com/swagger-api/swagger-codegen#modifying-the-client-library-format. ");
objs.put("_lambda", lambdas);
} else {
objs.put("lambda", lambdas);
}
@Override
protected Builder<String, Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
.put("camelcase_param", new CamelCaseLambda().generator(this).escapeAsParamName(true));
}
@Override

View File

@@ -17,8 +17,8 @@
package org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import com.google.common.collect.ImmutableMap.Builder;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;
@@ -35,7 +35,6 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
abstract public class AbstractCppCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCppCodegen.class);
@@ -260,22 +259,12 @@ abstract public class AbstractCppCodegen extends DefaultCodegen implements Codeg
LOGGER.info("Environment variable CPP_POST_PROCESS_FILE not defined so the C++ code may not be properly formatted. To define it, try 'export CPP_POST_PROCESS_FILE=\"/usr/local/bin/clang-format -i\"' (Linux/Mac)");
LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
}
addMustacheLambdas(additionalProperties);
}
private void addMustacheLambdas(Map<String, Object> objs) {
Map<String, Mustache.Lambda> lambdas = new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("multiline_comment_4", new IndentedLambda(4, " ", "///"))
.build();
if (objs.containsKey("lambda")) {
LOGGER.warn("A property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'.");
objs.put("_lambda", lambdas);
} else {
objs.put("lambda", lambdas);
}
@Override
protected Builder<String, Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
.put("multiline_comment_4", new IndentedLambda(4, " ", "///"));
}
@Override
@@ -304,17 +293,17 @@ abstract public class AbstractCppCodegen extends DefaultCodegen implements Codeg
}
}
}
@Override
public void preprocessOpenAPI(OpenAPI openAPI) {
URL url = URLPathUtils.getServerURL(openAPI);
String port = URLPathUtils.getPort(url, "");
String host = url.getHost();
if(!port.isEmpty()) {
this.additionalProperties.put("serverPort", port);
this.additionalProperties.put("serverPort", port);
}
if(!host.isEmpty()) {
this.additionalProperties.put("serverHost", host);
}
this.additionalProperties.put("serverHost", host);
}
}
}

View File

@@ -16,8 +16,9 @@
package org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import com.google.common.collect.ImmutableMap.Builder;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
@@ -67,7 +68,7 @@ public abstract class AbstractFSharpCodegen extends DefaultCodegen implements Co
protected Set<String> collectionTypes;
protected Set<String> mapTypes;
// true if nullable types will be supported (as option)
// true if nullable types will be supported (as option)
protected boolean supportNullable = Boolean.TRUE;
protected Set<String> nullableType = new HashSet<String>();
@@ -329,32 +330,12 @@ public abstract class AbstractFSharpCodegen extends DefaultCodegen implements Co
// This either updates additionalProperties with the above fixes, or sets the default if the option was not specified.
additionalProperties.put(CodegenConstants.INTERFACE_PREFIX, interfacePrefix);
addMustacheLambdas(additionalProperties);
}
private void addMustacheLambdas(Map<String, Object> objs) {
Map<String, Mustache.Lambda> lambdas = new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("lowercase", new LowercaseLambda().generator(this))
.put("uppercase", new UppercaseLambda())
.put("titlecase", new TitlecaseLambda())
.put("camelcase", new CamelCaseLambda().generator(this))
.put("camelcase_param", new CamelCaseLambda().generator(this).escapeAsParamName(true))
.put("indented", new IndentedLambda())
.put("indented_8", new IndentedLambda(8, " "))
.put("indented_12", new IndentedLambda(12, " "))
.put("indented_16", new IndentedLambda(16, " "))
.build();
if (objs.containsKey("lambda")) {
LOGGER.warn("A property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'. " +
"You'll likely need to use a custom template, " +
"see https://github.com/swagger-api/swagger-codegen#modifying-the-client-library-format. ");
objs.put("_lambda", lambdas);
} else {
objs.put("lambda", lambdas);
}
@Override
protected Builder<String, Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
.put("camelcase_param", new CamelCaseLambda().generator(this).escapeAsParamName(true));
}
@Override

View File

@@ -18,13 +18,11 @@
package org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.templating.mustache.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -211,31 +209,6 @@ public class KotlinServerCodegen extends AbstractKotlinCodegen {
final String infrastructureFolder = (sourceFolder + File.separator + packageName + File.separator + "infrastructure").replace(".", File.separator);
supportingFiles.add(new SupportingFile("ApiKeyAuth.kt.mustache", infrastructureFolder, "ApiKeyAuth.kt"));
addMustacheLambdas(additionalProperties);
}
private void addMustacheLambdas(Map<String, Object> objs) {
Map<String, Mustache.Lambda> lambdas = new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("lowercase", new LowercaseLambda().generator(this))
.put("uppercase", new UppercaseLambda())
.put("titlecase", new TitlecaseLambda())
.put("camelcase", new CamelCaseLambda().generator(this))
.put("indented", new IndentedLambda())
.put("indented_8", new IndentedLambda(8, " "))
.put("indented_12", new IndentedLambda(12, " "))
.put("indented_16", new IndentedLambda(16, " "))
.build();
if (objs.containsKey("lambda")) {
LOGGER.warn("A property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'. " +
"You'll likely need to use a custom template, " +
"see https://github.com/OpenAPITools/openapi-generator/blob/master/docs/templating.md. ");
objs.put("_lambda", lambdas);
} else {
objs.put("lambda", lambdas);
}
}
public static class Constants {

View File

@@ -16,9 +16,11 @@
package org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;
import org.openapitools.codegen.*;
@@ -51,7 +53,6 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
));
public static final String TITLE = "title";
public static final String LAMBDA = "lambda";
public static final String SERVER_PORT = "serverPort";
public static final String BASE_PACKAGE = "basePackage";
public static final String SPRING_BOOT = "spring-boot";
@@ -370,22 +371,14 @@ public class KotlinSpringServerCodegen extends AbstractKotlinCodegen
sanitizeDirectory(sourceFolder + File.separator + basePackage), "Application.kt"));
}
addMustacheLambdas(additionalProperties);
// spring uses the jackson lib, and we disallow configuration.
additionalProperties.put("jackson", "true");
}
private void addMustacheLambdas(final Map<String, Object> objs) {
Map<String, Mustache.Lambda> lambdas =
new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("escapeDoubleQuote", new EscapeLambda("\"", "\\\""))
.build();
if (objs.containsKey(LAMBDA)) {
LOGGER.warn("The lambda property is a reserved word, and will be overwritten!");
}
objs.put(LAMBDA, lambdas);
@Override
protected Builder<String, Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
.put("escapeDoubleQuote", new EscapeLambda("\"", "\\\""));
}
@Override

View File

@@ -18,21 +18,15 @@
package org.openapitools.codegen.languages;
import org.openapitools.codegen.*;
import org.openapitools.codegen.templating.mustache.CamelCaseLambda;
import org.openapitools.codegen.templating.mustache.IndentedLambda;
import org.openapitools.codegen.templating.mustache.LowercaseLambda;
import org.openapitools.codegen.templating.mustache.TitlecaseLambda;
import org.openapitools.codegen.templating.mustache.UppercaseLambda;
import org.openapitools.codegen.templating.mustache.OnChangeLambda;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
import com.google.common.collect.ImmutableMap.Builder;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.oas.models.Operation;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -75,32 +69,12 @@ public class OpenAPIYamlGenerator extends DefaultCodegen implements CodegenConfi
}
LOGGER.info("Output file [outputFile={}]", outputFile);
supportingFiles.add(new SupportingFile("openapi.mustache", outputFile));
addMustacheLambdas(additionalProperties);
}
private void addMustacheLambdas(Map<String, Object> objs) {
Map<String, Mustache.Lambda> lambdas = new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("lowercase", new LowercaseLambda().generator(this))
.put("uppercase", new UppercaseLambda())
.put("titlecase", new TitlecaseLambda())
.put("camelcase", new CamelCaseLambda().generator(this))
.put("indented", new IndentedLambda())
.put("indented_8", new IndentedLambda(8, " "))
.put("indented_12", new IndentedLambda(12, " "))
.put("indented_16", new IndentedLambda(16, " "))
.put("onchange", new OnChangeLambda())
.build();
if (objs.containsKey("lambda")) {
LOGGER.warn("A property named 'lambda' already exists. Mustache lambdas renamed from 'lambda' to '_lambda'. " +
"You'll likely need to use a custom template, " +
"see https://github.com/OpenAPITools/openapi-generator/blob/master/docs/templating.md. ");
objs.put("_lambda", lambdas);
} else {
objs.put("lambda", lambdas);
}
@Override
protected Builder<String, Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
.put("onchange", new OnChangeLambda());
}
/**
@@ -133,24 +107,4 @@ public class OpenAPIYamlGenerator extends DefaultCodegen implements CodegenConfi
return input;
}
/**
* Lambda writes current fragment to the output when it is different than
* previous fragment.
*/
public static class OnChangeLambda implements Mustache.Lambda {
private static final Logger LOGGER = LoggerFactory.getLogger(OnChangeLambda.class);
private String lastVal = null;
@Override
public void execute(Template.Fragment frag, Writer out) throws IOException {
String curVal = frag.execute();
LOGGER.debug("[lastVal={}, curVal={}]", lastVal, curVal);
if (curVal != null && !curVal.equals(lastVal)) {
out.write(curVal);
lastVal = curVal;
}
}
}
}

View File

@@ -16,8 +16,9 @@
package org.openapitools.codegen.languages;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import com.google.common.collect.ImmutableMap.Builder;
import com.samskivert.mustache.Mustache.Lambda;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import org.openapitools.codegen.*;
@@ -192,15 +193,12 @@ public class ScalaPlayFrameworkServerCodegen extends AbstractScalaCodegen implem
supportingFiles.add(new SupportingFile("public/openapi.json.mustache", "public", "openapi.json"));
supportingFiles.add(new SupportingFile("app/apiDocController.scala.mustache", String.format(Locale.ROOT, "app/%s", apiPackage.replace(".", File.separator)), "ApiDocController.scala"));
}
addMustacheLambdas(additionalProperties);
}
private void addMustacheLambdas(Map<String, Object> objs) {
Map<String, Mustache.Lambda> lambdas = new ImmutableMap.Builder<String, Mustache.Lambda>()
.put("indented_4", new IndentedLambda(4, " "))
.put("indented_8", new IndentedLambda(8, " "))
.build();
objs.put("lambda", lambdas);
@Override
protected Builder<String, Lambda> addMustacheLambdas() {
return super.addMustacheLambdas()
.put("indented_4", new IndentedLambda(4, " "));
}
@SuppressWarnings("unchecked")

View File

@@ -0,0 +1,40 @@
package org.openapitools.codegen.templating.mustache;
import java.io.IOException;
import java.io.Writer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
/**
* Lambda writes current fragment to the output when it is different than
* the previous fragment.
*
* Register:
* <pre>
* additionalProperties.put("onchange", new OnChangeLambda());
* </pre>
*
* Use:
* <pre>
* {{#onchange}}{{name}}{{/onchange}}
* </pre>
*/
public class OnChangeLambda implements Mustache.Lambda {
private static final Logger LOGGER = LoggerFactory.getLogger(OnChangeLambda.class);
private String lastVal = null;
@Override
public void execute(Template.Fragment frag, Writer out) throws IOException {
String curVal = frag.execute();
LOGGER.debug("[lastVal={}, curVal={}]", lastVal, curVal);
if (curVal != null && !curVal.equals(lastVal)) {
out.write(curVal);
lastVal = curVal;
}
}
}