From bf0bd29ef16d9a9934ef16466bc9b19410b5c231 Mon Sep 17 00:00:00 2001 From: Jim Schubert Date: Sat, 12 Sep 2020 18:09:59 -0400 Subject: [PATCH] [core][feature] User custom added templates (#7366) --- docs/customization.md | 71 ++++++++++ .../org/openapitools/codegen/cmd/Meta.java | 8 +- .../codegen/api/TemplateDefinition.java | 125 ++++++++++++++++ .../codegen/api/TemplateFileType.java | 61 ++++++++ .../openapitools/codegen/ClientOptInput.java | 16 +++ .../codegen/CodegenConstants.java | 1 + .../codegen/DefaultGenerator.java | 99 +++++++++++-- .../openapitools/codegen/SupportingFile.java | 103 ++++++++------ .../codegen/config/CodegenConfigurator.java | 11 +- .../codegen/config/DynamicSettings.java | 49 +++++-- .../codegen/languages/AdaCodegen.java | 16 +-- .../codegen/languages/AdaServerCodegen.java | 18 +-- .../languages/JavaCXFExtServerCodegen.java | 12 +- .../codegen/languages/JavaClientCodegen.java | 2 +- .../codegen/config/DynamicSettingsTest.java | 133 ++++++++++++++++++ .../spring/KotlinSpringServerCodegenTest.java | 2 +- 16 files changed, 644 insertions(+), 83 deletions(-) create mode 100644 modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateDefinition.java create mode 100644 modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateFileType.java diff --git a/docs/customization.md b/docs/customization.md index 8bf21c7dbae..050deb29157 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -3,6 +3,77 @@ id: customization title: Customization --- +## User-defined Templates + +The most common scenario for user customization is to override the built-in templates with small modifications. That scenario's documentation is in our [templating](./templating.md) page, and differs from user-defined templates. + +Prior to release 5.0.0, whenever a user wanted to include templates which weren't built-in or weren't known to the generator at compile time, they'd need to follow the more involved approach of creating a custom generator as documented later in this document. Beginning in 5.0.0, a user may now provide additional supporting files and extensions to built-in templates via configuration. This feature requires using the external configuration file feature. + +Consider that you might want to add some static documentation such as `AUTHORS.md` and a custom tooling script. Rather than a single file for API definitions you also want an implementation file and a separate interface file for each. + +You might have an external configuration file named `config.yaml` which defines additional properties like this for a `kotlin` client generator: + +```yaml +additionalProperties: + artifactId: kotlin-petstore-client + serializableModel: "true" + dateLibrary: java8 +``` + +You would generate via CLI with the command: + +```shell script +openapi-generator generate -g kotlin -i spec.yaml -o outdir -c config.yaml +``` + +To support the above scenario with custom templates, ensure that you're pointing to your custom template directory and add a `files` node with template file definitions to your config: + +``` +templateDir: my_custom_templates +additionalProperties: + artifactId: kotlin-petstore-client + serializableModel: "true" + dateLibrary: java8 +files: + AUTHORS.md: {} + api_interfaces.mustache: + templateType: API + destinationFilename: Interface.kt + api.mustache: + templateType: API + destinationFilename: Impl.kt + other/check.mustache: + folder: scripts + destinationFilename: check.sh + templateType: SupportingFiles +``` + +The keys under the `files` node are your template filenames. These honor the same resolution order as all other templates. + +The above configuration will do the following: + +* Copy `my_custom_templates/AUTHORS.md` to the generated output directory without processing via the template engine (due to template file extension). The empty object definition following `AUTHORS.md` allows the tool to infer the target output filename in the root of the output directory. +* Compile a user-provided `my_custom_templates/api_interfaces.mustache` following our usual API template compilation logic. That is, one file will be created per API; APIs are generated defined according to tags in your spec documentation. The destination filename of `Interface.kt` will act as a suffix for the filename. So, a tag of `Equipment` will output a corresponding `EquipmentInterface.kt`. +* Because `api.mustache` is the same mustache filename as used in your target generator (`kotlin` in this example), we support the following: + - The destination filename provides a suffix for the generated output. APIs generate per tag in your specification. So, a tag of `Equipment` will output a corresponding `EquipmentImpl.kt`. This option will be used whether `api.mustache` targets a user customized template or a built-in template. + - The built-in template will be used if you haven't provided an customized template. The kotlin generator defines the suffix as simply `.kt`, so this scenario would modify only the generated file suffixes according to the previous bullet point. + - Your `api.mustache` will be used if it exists in your custom template directory. For generators with library options, such as `jvm-okhttp3` in the kotlin generator, your file must exist in the same relative location as the embedded template. For kotlin using the `jvm-okhttp3` library option, this file would need to be located at `my_custom_templates/libraries/jvm-okhttp/api.mustache`. See [templating](./templating.md) for more details. +* Compile `my_custom_templates/other/check.mustache` with the supporting files bundle, and output to `scripts/check.sh` in your output directory. Note that we don't currently support setting file flags on output, so scripts such as these will either have to be sourced rather than executed, or have file flags set separately after generation (external to our tooling). + +The `templateType` option will default to `SupportingFiles`, so the option for `other/check.mustache` is redundant and provided to demonstrate the full template file configuration options. The available template types are: + +* API +* APIDocs +* APITests +* Model +* ModelDocs +* ModelTests +* SupportingFiles + +Excluding `SupportingFiles`, each of the above options may result in multiple files. API related types create a file per API. Model related types create a file for each model. + +Note that user-defined templates will merge with built-in template definitions. If a supporting file with the sample template file path exists, it will be replaced with the user-defined template, otherwise the user-defined template will be added to the list of template files to compile. If the generator's built-in template is `model_docs.mustache` and you define `model-docs.mustache`, this will result in duplicated model docs (if `destinationFilename` differs) or undefined behavior as whichever template compiles last will overwrite the previous model docs (if `destinationFilename` matches the extension or suffix in the generator's code). + ## Custom Generator (and Template) If none of the built-in generators suit your needs and you need to do more than just modify the mustache templates to tweak generated code, you can create a brand new generator and its associated templates. OpenAPI Generator can help with this, using the `meta` command: diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Meta.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Meta.java index 0c4a8f7cf18..52043e2ce0c 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Meta.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Meta.java @@ -144,8 +144,8 @@ public class Meta extends OpenApiGeneratorCommand { return support -> { try { File destinationFolder = - new File(new File(targetDir.getAbsolutePath()), support.folder); - File outputFile = new File(destinationFolder, support.destinationFilename); + new File(new File(targetDir.getAbsolutePath()), support.getFolder()); + File outputFile = new File(destinationFolder, support.getDestinationFilename()); TemplateManager templateProcessor = new TemplateManager( new TemplateManagerOptions(false, false), @@ -153,13 +153,13 @@ public class Meta extends OpenApiGeneratorCommand { new TemplatePathLocator[]{ new CommonTemplateContentLocator("codegen") } ); - String template = templateProcessor.readTemplate(new File(TEMPLATE_DIR_CLASSPATH, support.templateFile).getPath()); + String template = templateProcessor.readTemplate(new File(TEMPLATE_DIR_CLASSPATH, support.getTemplateFile()).getPath()); String formatted = template; Mustache.TemplateLoader loader = name -> templateProcessor.getTemplateReader(name.concat(MUSTACHE_EXTENSION)); - if (support.templateFile.endsWith(MUSTACHE_EXTENSION)) { + if (support.getTemplateFile().endsWith(MUSTACHE_EXTENSION)) { LOGGER.info("writing file to {}", outputFile.getAbsolutePath()); formatted = Mustache.compiler().withLoader(loader).defaultValue("") diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateDefinition.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateDefinition.java new file mode 100644 index 00000000000..51d6c929e03 --- /dev/null +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateDefinition.java @@ -0,0 +1,125 @@ +package org.openapitools.codegen.api; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + * TemplateDefinition is a type which defines the basics of a template file and target output location. + */ +@SuppressWarnings("unused") +public class TemplateDefinition { + private final String templateFile; + private final String folder; + private final String destinationFilename; + + protected TemplateFileType templateType; + + /** + *

Constructor for TemplateDefinition.

+ * + * @param templateFile a template path relative to user template or embedded template. + * @param destinationFilename a target output location for the file, relative to the output directory. + */ + public TemplateDefinition(String templateFile, String destinationFilename) { + this(templateFile, "", destinationFilename); + } + + /** + * Parameter-less constructor for TemplateDefinition, used for simplified serialization (see DynamicSettings). + */ + public TemplateDefinition() { + this("", "", ""); + } + + /** + *

Constructor for TemplateDefinition.

+ * + * @param templateFile a template path relative to user template or embedded template. + * @param folder a folder in the target output directory in which to place the target file. + * @param destinationFilename a target output location for the file, relative to the output directory. + */ + public TemplateDefinition(String templateFile, String folder, String destinationFilename) { + if (templateFile == null) throw new IllegalArgumentException("templateFile may not be null."); + if (folder == null) throw new IllegalArgumentException("folder may not be null."); + if (destinationFilename == null) throw new IllegalArgumentException("destinationFilename may not be null."); + + this.templateFile = templateFile; + this.folder = folder; + this.destinationFilename = destinationFilename; + this.templateType = TemplateFileType.SupportingFiles; + } + + /** + * Gets target output location for the file, relative to the output directory. + * + * @return a target output location for the file, relative to the output directory. + */ + public String getDestinationFilename() { + return destinationFilename; + } + + /** + * Gets a folder in the target output directory in which to place the target file. + * + * @return a a folder in the target output directory in which to place the target file. + */ + public String getFolder() { + return folder; + } + + /** + * Gets a template path relative to user template or embedded template. + * + * @return a template path relative to user template or embedded template. + */ + public String getTemplateFile() { + return templateFile; + } + + /** + * Gets the type of template + * + * @return a {@link TemplateFileType} enum which defines the type of this template. + */ + public TemplateFileType getTemplateType() { + return templateType; + } + + /** + * Sets the type of template + * + * @param templateType a {@link TemplateFileType} enum which defines the type of this template + */ + public void setTemplateType(TemplateFileType templateType) { + this.templateType = templateType; + } + + /** {@inheritDoc} */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TemplateDefinition)) return false; + TemplateDefinition that = (TemplateDefinition) o; + return getTemplateFile().equals(that.getTemplateFile()) && + getFolder().equals(that.getFolder()) && + getDestinationFilename().equals(that.getDestinationFilename()) && + getTemplateType() == that.getTemplateType(); + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return Objects.hash(getTemplateFile(), getFolder(), getDestinationFilename(), getTemplateType()); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return new StringJoiner(", ", TemplateDefinition.class.getSimpleName() + "[", "]") + .add("templateFile='" + templateFile + "'") + .add("folder='" + folder + "'") + .add("destinationFilename='" + destinationFilename + "'") + .add("templateType=" + templateType) + .toString(); + } +} diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateFileType.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateFileType.java new file mode 100644 index 00000000000..68ff5610fe3 --- /dev/null +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/TemplateFileType.java @@ -0,0 +1,61 @@ +package org.openapitools.codegen.api; + +import java.util.StringJoiner; + +/** + * Represents the type of a generator's templating files. These types of template files may be processed differently + * (multiple times per definition, or conditionally based on user or generator configuration). + */ +public enum TemplateFileType { + API(Constants.APIS), + Model(Constants.MODELS), + APIDocs(Constants.API_DOCS), + ModelDocs(Constants.MODEL_DOCS), + APITests(Constants.API_TESTS), + ModelTests(Constants.MODEL_TESTS), + SupportingFiles(Constants.SUPPORTING_FILES); + + private final String templateType; + + TemplateFileType(String templateType) { this.templateType = templateType; } + + /** + * Returns the value for this template file type + * + * @return The template type of this enum. + */ + public String value() { return this.templateType; } + + /** {@inheritDoc} */ + @Override + public String toString() { + return new StringJoiner(", ", TemplateFileType.class.getSimpleName() + "[", "]") + .add("templateType='" + templateType + "'") + .toString(); + } + + /** + * Obtains the {@link TemplateFileType} for an input string. + * + * @param templateType a {@link java.lang.String} object. + * @return a {@link TemplateFileType} object. + */ + public static TemplateFileType forTemplateType(String templateType) { + for (TemplateFileType value : values()) { + if (value.templateType.equals(templateType)) { + return value; + } + } + throw new IllegalArgumentException("templateType not found in the available values."); + } + + public static class Constants { + public static final String APIS = "apis"; + public static final String MODELS = "models"; + public static final String SUPPORTING_FILES = "supportingFiles"; + public static final String MODEL_TESTS = "modelTests"; + public static final String MODEL_DOCS = "modelDocs"; + public static final String API_TESTS = "apiTests"; + public static final String API_DOCS = "apiDocs"; + } +} diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java index ebbadee8e03..0d95bb4aef9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java @@ -20,6 +20,7 @@ package org.openapitools.codegen; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.parser.core.models.AuthorizationValue; +import org.openapitools.codegen.api.TemplateDefinition; import org.openapitools.codegen.auth.AuthParser; import java.util.List; @@ -28,6 +29,7 @@ public class ClientOptInput { private CodegenConfig config; private OpenAPI openAPI; private List auths; + private List userDefinedTemplates; public ClientOptInput openAPI(OpenAPI openAPI) { this.setOpenAPI(openAPI); @@ -39,6 +41,11 @@ public class ClientOptInput { return this; } + public ClientOptInput userDefinedTemplates(List userDefinedTemplates) { + this.userDefinedTemplates = userDefinedTemplates; + return this; + } + @Deprecated public ClientOptInput auth(String urlEncodedAuthString) { this.setAuth(urlEncodedAuthString); @@ -65,7 +72,14 @@ public class ClientOptInput { return config; } + public List getUserDefinedTemplates() { + // not deprecated as this is added to match other functionality, we need to move to Context instead of ClientOptInput. + return userDefinedTemplates; + } + /** + * Sets the generator/config instance + * * @deprecated use {@link #config(CodegenConfig)} instead * @param config codegen config */ @@ -84,6 +98,8 @@ public class ClientOptInput { } /** + * Sets the OpenAPI document + * * @deprecated use {@link #openAPI(OpenAPI)} instead * @param openAPI the specification */ 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 c0950191a91..38528a6c6fe 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 @@ -30,6 +30,7 @@ public class CodegenConstants { public static final String MODEL_DOCS = "modelDocs"; public static final String API_TESTS = "apiTests"; public static final String API_DOCS = "apiDocs"; + public static final String WITH_XML = "withXml"; public static final String SKIP_FORM_MODEL = "skipFormModel"; /* /end System Properties */ 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 4724495da58..04c3c72b9c4 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,6 +17,7 @@ package org.openapitools.codegen; +import com.google.common.collect.ImmutableList; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; @@ -29,13 +30,16 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.security.*; import io.swagger.v3.oas.models.tags.Tag; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.comparator.PathFileComparator; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.api.TemplateDefinition; import org.openapitools.codegen.api.TemplatePathLocator; import org.openapitools.codegen.api.TemplateProcessor; import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.api.TemplatingEngineAdapter; +import org.openapitools.codegen.api.TemplateFileType; import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; import org.openapitools.codegen.languages.PythonClientExperimentalCodegen; import org.openapitools.codegen.meta.GeneratorMetadata; @@ -58,6 +62,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.time.ZonedDateTime; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.removeStart; @@ -86,6 +91,8 @@ public class DefaultGenerator implements Generator { private Map generatorPropertyDefaults = new HashMap<>(); protected TemplateProcessor templateProcessor = null; + private List userDefinedTemplates = new ArrayList<>(); + public DefaultGenerator() { this(false); @@ -102,6 +109,10 @@ public class DefaultGenerator implements Generator { this.opts = opts; this.openAPI = opts.getOpenAPI(); this.config = opts.getConfig(); + List userFiles = opts.getUserDefinedTemplates(); + if (userFiles != null) { + this.userDefinedTemplates = ImmutableList.copyOf(userFiles); + } TemplateManagerOptions templateManagerOptions = new TemplateManagerOptions(this.config.isEnableMinimalUpdate(),this.config.isSkipOverwrite()); @@ -679,8 +690,8 @@ public class DefaultGenerator implements Generator { for (SupportingFile support : config.supportingFiles()) { try { String outputFolder = config.outputFolder(); - if (StringUtils.isNotEmpty(support.folder)) { - outputFolder += File.separator + support.folder; + if (StringUtils.isNotEmpty(support.getFolder())) { + outputFolder += File.separator + support.getFolder(); } File of = new File(outputFolder); if (!of.isDirectory()) { @@ -688,20 +699,20 @@ public class DefaultGenerator implements Generator { once(LOGGER).debug("Output directory {} not created. It {}.", outputFolder, of.exists() ? "already exists." : "may not have appropriate permissions."); } } - String outputFilename = new File(support.destinationFilename).isAbsolute() // split - ? support.destinationFilename - : outputFolder + File.separator + support.destinationFilename.replace('/', File.separatorChar); + String outputFilename = new File(support.getDestinationFilename()).isAbsolute() // split + ? support.getDestinationFilename() + : outputFolder + File.separator + support.getDestinationFilename().replace('/', File.separatorChar); boolean shouldGenerate = true; if (supportingFilesToGenerate != null && !supportingFilesToGenerate.isEmpty()) { - shouldGenerate = supportingFilesToGenerate.contains(support.destinationFilename); + shouldGenerate = supportingFilesToGenerate.contains(support.getDestinationFilename()); } - File written = processTemplateToFile(bundle, support.templateFile, outputFilename, shouldGenerate, CodegenConstants.SUPPORTING_FILES); + File written = processTemplateToFile(bundle, support.getTemplateFile(), outputFilename, shouldGenerate, CodegenConstants.SUPPORTING_FILES); if (written != null) { files.add(written); if (config.isEnablePostProcessFile() && !dryRun) { - config.postProcessFile(written, "api-doc"); + config.postProcessFile(written, "supporting-file"); } } } catch (Exception e) { @@ -845,6 +856,8 @@ public class DefaultGenerator implements Generator { config.processOpenAPI(openAPI); + processUserDefinedTemplates(); + List files = new ArrayList<>(); // models List filteredSchemas = ModelUtils.getSchemasUsedOnlyInFormParam(openAPI); @@ -908,6 +921,76 @@ public class DefaultGenerator implements Generator { return files; } + private void processUserDefinedTemplates() { + // TODO: initial behavior is "merge" user defined with built-in templates. consider offering user a "replace" option. + if (userDefinedTemplates != null && !userDefinedTemplates.isEmpty()) { + Map supportingFilesMap = config.supportingFiles().stream() + .collect(Collectors.toMap(TemplateDefinition::getTemplateFile, Function.identity())); + + // TemplateFileType.SupportingFiles + userDefinedTemplates.stream() + .filter(i -> i.getTemplateType().equals(TemplateFileType.SupportingFiles)) + .forEach(userDefinedTemplate -> { + SupportingFile newFile = new SupportingFile( + userDefinedTemplate.getTemplateFile(), + userDefinedTemplate.getFolder(), + userDefinedTemplate.getDestinationFilename() + ); + if (supportingFilesMap.containsKey(userDefinedTemplate.getTemplateFile())) { + SupportingFile f = supportingFilesMap.get(userDefinedTemplate.getTemplateFile()); + config.supportingFiles().remove(f); + + if (!f.isCanOverwrite()) { + newFile.doNotOverwrite(); + } + } + config.supportingFiles().add(newFile); + }); + + // Others, excluding TemplateFileType.SupportingFiles + userDefinedTemplates.stream() + .filter(i -> !i.getTemplateType().equals(TemplateFileType.SupportingFiles)) + .forEach(userDefinedTemplate -> { + // determine file extension… + // if template is in format api.ts.mustache, we'll extract .ts + // if user has provided an example destination filename, we'll use that extension + String templateFile = userDefinedTemplate.getTemplateFile(); + int lastSeparator = templateFile.lastIndexOf('.'); + String templateExt = FilenameUtils.getExtension(templateFile.substring(0, lastSeparator)); + if (StringUtils.isBlank(templateExt)) { + // hack: destination filename in this scenario might be a suffix like Impl.java + templateExt = userDefinedTemplate.getDestinationFilename(); + } else { + templateExt = StringUtils.prependIfMissing(templateExt, "."); + } + + switch (userDefinedTemplate.getTemplateType()) { + case API: + config.apiTemplateFiles().put(templateFile, templateExt); + break; + case Model: + config.modelTemplateFiles().put(templateFile, templateExt); + break; + case APIDocs: + config.apiDocTemplateFiles().put(templateFile, templateExt); + break; + case ModelDocs: + config.modelDocTemplateFiles().put(templateFile, templateExt); + break; + case APITests: + config.apiTestTemplateFiles().put(templateFile, templateExt); + break; + case ModelTests: + config.modelTestTemplateFiles().put(templateFile, templateExt); + break; + case SupportingFiles: + // excluded by filter + break; + } + }); + } + } + protected File processTemplateToFile(Map templateData, String templateName, String outputFilename, boolean shouldGenerate, String skippedByOption) throws IOException { String adjustedOutputFilename = outputFilename.replaceAll("//", "/").replace('/', File.separatorChar); File target = new File(adjustedOutputFilename); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/SupportingFile.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/SupportingFile.java index ed2d7307f6b..2d72fa2f216 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/SupportingFile.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/SupportingFile.java @@ -17,53 +17,28 @@ package org.openapitools.codegen; -import java.util.Objects; +import org.openapitools.codegen.api.TemplateDefinition; +import org.openapitools.codegen.api.TemplateFileType; -public class SupportingFile { - public String templateFile; - public String folder; - public String destinationFilename; - public boolean canOverwrite = true; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Defines the template definition for a "supporting file", that is any file which is generic and not bound to + * api/model definitions and their relevant docs or tests. + * + * Supporting files are generated once for an entire application while api/model bound definitions are generated multiple + * times according to their target use. + */ +public class SupportingFile extends TemplateDefinition { + private boolean canOverwrite = true; public SupportingFile(String templateFile, String destinationFilename) { this(templateFile, "", destinationFilename); } public SupportingFile(String templateFile, String folder, String destinationFilename) { - this.templateFile = templateFile; - this.folder = folder; - this.destinationFilename = destinationFilename; - } - - @Override - public int hashCode() { - return Objects.hash(templateFile, folder, destinationFilename, canOverwrite); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - SupportingFile that = (SupportingFile) o; - - return Objects.equals(templateFile, that.templateFile) && - Objects.equals(folder, that.folder) && - Objects.equals(destinationFilename, that.destinationFilename) && - canOverwrite == that.canOverwrite; - } - - @SuppressWarnings("StringBufferReplaceableByString") - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("SupportingFile:").append("\n"); - builder.append("\ttemplateFile: ").append(templateFile).append("\n"); - builder.append("\tfolder: ").append(folder).append("\n"); - builder.append("\tcanOverwrite: ").append(Boolean.valueOf(canOverwrite)).append("\n"); - builder.append("\tdestinationFilename: ").append(destinationFilename).append("\n"); - - return builder.toString(); + super(templateFile, folder, destinationFilename); } /** @@ -75,6 +50,54 @@ public class SupportingFile { canOverwrite = false; return this; } + + /** + * Sets the type of template + * + * @param templateType a {@link TemplateFileType} enum which defines the type of this template + */ + @Override + public void setTemplateType(TemplateFileType templateType) { + + } + + /** + * Gets the type of template + * + * @return a {@link TemplateFileType} enum which defines the type of this template. + */ + @Override + public TemplateFileType getTemplateType() { + return TemplateFileType.SupportingFiles; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SupportingFile)) return false; + if (!super.equals(o)) return false; + SupportingFile that = (SupportingFile) o; + return isCanOverwrite() == that.isCanOverwrite(); + } + + public boolean isCanOverwrite() { + return canOverwrite; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), isCanOverwrite()); + } + + @Override + public String toString() { + return new StringJoiner(", ", SupportingFile.class.getSimpleName() + "[", "]") + .add("templateFile='" + getTemplateFile() + "'") + .add("folder='" + getFolder() + "'") + .add("destinationFilename='" + getDestinationFilename() + "'") + .add("canOverwrite=" + isCanOverwrite()) + .toString(); + } } 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 6b46f0d6109..c5b03683569 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 @@ -31,6 +31,7 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.openapitools.codegen.*; +import org.openapitools.codegen.api.TemplateDefinition; import org.openapitools.codegen.api.TemplatingEngineAdapter; import org.openapitools.codegen.auth.AuthParser; import org.openapitools.codegen.utils.ModelUtils; @@ -72,6 +73,8 @@ public class CodegenConfigurator { private Map serverVariables = new HashMap<>(); private String auth; + private List userDefinedTemplates = new ArrayList<>(); + public CodegenConfigurator() { } @@ -86,6 +89,7 @@ public class CodegenConfigurator { GeneratorSettings generatorSettings = settings.getGeneratorSettings(); WorkflowSettings workflowSettings = settings.getWorkflowSettings(); + List userDefinedTemplateSettings = settings.getFiles(); // We copy "cached" properties into configurator so it is appropriately configured with all settings in external files. // FIXME: target is to eventually move away from CodegenConfigurator properties except gen/workflow settings. @@ -120,6 +124,10 @@ public class CodegenConfigurator { configurator.generatorSettingsBuilder = GeneratorSettings.newBuilder(generatorSettings); configurator.workflowSettingsBuilder = WorkflowSettings.newBuilder(workflowSettings); + if (userDefinedTemplateSettings != null) { + configurator.userDefinedTemplates.addAll(userDefinedTemplateSettings); + } + return configurator; } return null; @@ -604,7 +612,8 @@ public class CodegenConfigurator { } ClientOptInput input = new ClientOptInput() - .config(config); + .config(config) + .userDefinedTemplates(userDefinedTemplates); return input.openAPI((OpenAPI)context.getSpecDocument()); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/DynamicSettings.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/DynamicSettings.java index b49a6e23e3f..2dcedc0f7c7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/DynamicSettings.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/DynamicSettings.java @@ -2,19 +2,21 @@ package org.openapitools.codegen.config; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.api.TemplateDefinition; +import org.openapitools.codegen.api.TemplateFileType; import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; /** - * Represents a serialization helper of {@link GeneratorSettings} and {@link WorkflowSettings}. When used to deserialize any available Jackson binding input, - * this will accumulate any "unknown properties" into {@link GeneratorSettings#getAdditionalProperties()} as a side effect of calling - * {@link DynamicSettings#getGeneratorSettings()}. + * Represents a serialization helper of {@link org.openapitools.codegen.config.GeneratorSettings} and {@link org.openapitools.codegen.config.WorkflowSettings}. When used to deserialize any available Jackson binding input, + * this will accumulate any "unknown properties" into {@link org.openapitools.codegen.config.GeneratorSettings#getAdditionalProperties()} as a side effect of calling + * {@link org.openapitools.codegen.config.DynamicSettings#getGeneratorSettings()}. */ @SuppressWarnings({"unused", "WeakerAccess"}) public class DynamicSettings { @@ -30,7 +32,33 @@ public class DynamicSettings { private WorkflowSettings workflowSettings; /** - * Gets the {@link GeneratorSettings} included in the config object. + * Gets the list of template files allowing user redefinition and addition of templating files + * + * @return A list of template files + */ + public List getFiles() { + if (files == null) return new ArrayList<>(); + + return files.entrySet().stream().map(kvp -> { + TemplateDefinition file = kvp.getValue(); + String templateFile = kvp.getKey(); + String destination = file.getDestinationFilename(); + if (TemplateFileType.SupportingFiles.equals(file.getTemplateType()) && StringUtils.isBlank(destination)) { + // this special case allows definitions such as LICENSE:{} + destination = templateFile; + } + TemplateDefinition definition = new TemplateDefinition(templateFile, file.getFolder(), destination); + definition.setTemplateType(file.getTemplateType()); + return definition; + }).collect(Collectors.toList()); + } + + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + @JsonProperty("files") + private Map files; + + /** + * Gets the {@link org.openapitools.codegen.config.GeneratorSettings} included in the config object. * * @return A new instance of settings */ @@ -47,7 +75,7 @@ public class DynamicSettings { } /** - * Gets the {@link WorkflowSettings} included in the config object. + * Gets the {@link org.openapitools.codegen.config.WorkflowSettings} included in the config object. * * @return A new instance of settings */ @@ -57,6 +85,9 @@ public class DynamicSettings { .build(); } + /** + *

Constructor for DynamicSettings.

+ */ @JsonCreator public DynamicSettings() { } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java index 1b25ab3dbb4..f7ec26fac07 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaCodegen.java @@ -97,10 +97,10 @@ public class AdaCodegen extends AbstractAdaCodegen implements CodegenConfig { String srcPrefix = "src" + File.separator; String modelPrefix = srcPrefix + "model" + File.separator + toFilename(modelPackage); String clientPrefix = srcPrefix + "client" + File.separator + toFilename(modelPackage); - supportingFiles.add(new SupportingFile("model-spec.mustache", null, modelPrefix + "-models.ads")); - supportingFiles.add(new SupportingFile("model-body.mustache", null, modelPrefix + "-models.adb")); - supportingFiles.add(new SupportingFile("client-spec.mustache", null, clientPrefix + "-clients.ads")); - supportingFiles.add(new SupportingFile("client-body.mustache", null, clientPrefix + "-clients.adb")); + supportingFiles.add(new SupportingFile("model-spec.mustache", "", modelPrefix + "-models.ads")); + supportingFiles.add(new SupportingFile("model-body.mustache", "", modelPrefix + "-models.adb")); + supportingFiles.add(new SupportingFile("client-spec.mustache", "", clientPrefix + "-clients.ads")); + supportingFiles.add(new SupportingFile("client-body.mustache", "", clientPrefix + "-clients.adb")); if (additionalProperties.containsKey(CodegenConstants.PROJECT_NAME)) { projectName = (String) additionalProperties.get(CodegenConstants.PROJECT_NAME); @@ -125,20 +125,20 @@ public class AdaCodegen extends AbstractAdaCodegen implements CodegenConfig { additionalProperties.put("isServer", false); additionalProperties.put(CodegenConstants.PROJECT_NAME, projectName); - String names[] = this.modelPackage.split("\\."); + String[] names = this.modelPackage.split("\\."); String pkgName = names[0]; additionalProperties.put("packageLevel1", pkgName); - supportingFiles.add(new SupportingFile("package-spec-level1.mustache", null, + supportingFiles.add(new SupportingFile("package-spec-level1.mustache", "", "src" + File.separator + toFilename(names[0]) + ".ads")); if (names.length > 1) { String fileName = toFilename(names[0]) + "-" + toFilename(names[1]) + ".ads"; pkgName = names[0] + "." + names[1]; additionalProperties.put("packageLevel2", pkgName); - supportingFiles.add(new SupportingFile("package-spec-level2.mustache", null, + supportingFiles.add(new SupportingFile("package-spec-level2.mustache", "", "src" + File.separator + fileName)); } pkgName = this.modelPackage; - supportingFiles.add(new SupportingFile("client.mustache", null, + supportingFiles.add(new SupportingFile("client.mustache", "", "src" + File.separator + toFilename(pkgName) + "-client.adb")); additionalProperties.put("packageName", toFilename(pkgName)); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java index 4c8d365fcaa..3565fa067fc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AdaServerCodegen.java @@ -92,12 +92,12 @@ public class AdaServerCodegen extends AbstractAdaCodegen implements CodegenConfi String serverPrefix = srcPrefix + "server" + File.separator + toFilename(modelPackage); String modelPrefix = srcPrefix + "model" + File.separator + toFilename(modelPackage); String implPrefix = srcPrefix + toFilename(modelPackage); - supportingFiles.add(new SupportingFile("model-spec.mustache", null, modelPrefix + "-models.ads")); - supportingFiles.add(new SupportingFile("model-body.mustache", null, modelPrefix + "-models.adb")); - supportingFiles.add(new SupportingFile("server-skeleton-spec.mustache", null, serverPrefix + "-skeletons.ads")); - supportingFiles.add(new SupportingFile("server-skeleton-body.mustache", null, serverPrefix + "-skeletons.adb")); - supportingFiles.add(new SupportingFile("server-spec.mustache", null, implPrefix + "-servers.ads")); - supportingFiles.add(new SupportingFile("server-body.mustache", null, implPrefix + "-servers.adb")); + supportingFiles.add(new SupportingFile("model-spec.mustache", "", modelPrefix + "-models.ads")); + supportingFiles.add(new SupportingFile("model-body.mustache", "", modelPrefix + "-models.adb")); + supportingFiles.add(new SupportingFile("server-skeleton-spec.mustache", "", serverPrefix + "-skeletons.ads")); + supportingFiles.add(new SupportingFile("server-skeleton-body.mustache", "", serverPrefix + "-skeletons.adb")); + supportingFiles.add(new SupportingFile("server-spec.mustache", "", implPrefix + "-servers.ads")); + supportingFiles.add(new SupportingFile("server-body.mustache", "", implPrefix + "-servers.adb")); supportingFiles.add(new SupportingFile("openapi.mustache", "web" + File.separator + "swagger", "openapi.json")); @@ -128,17 +128,17 @@ public class AdaServerCodegen extends AbstractAdaCodegen implements CodegenConfi String names[] = this.modelPackage.split("\\."); String pkgName = names[0]; additionalProperties.put("packageLevel1", pkgName); - supportingFiles.add(new SupportingFile("package-spec-level1.mustache", null, + supportingFiles.add(new SupportingFile("package-spec-level1.mustache", "", "src" + File.separator + toFilename(names[0]) + ".ads")); if (names.length > 1) { String fileName = toFilename(names[0]) + "-" + toFilename(names[1]) + ".ads"; pkgName = names[0] + "." + names[1]; additionalProperties.put("packageLevel2", pkgName); - supportingFiles.add(new SupportingFile("package-spec-level2.mustache", null, + supportingFiles.add(new SupportingFile("package-spec-level2.mustache", "", "src" + File.separator + fileName)); } pkgName = this.modelPackage; - supportingFiles.add(new SupportingFile("server.mustache", null, + supportingFiles.add(new SupportingFile("server.mustache", "", "src" + File.separator + toFilename(pkgName) + "-server.adb")); additionalProperties.put("packageName", toFilename(pkgName)); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java index f440163d4d6..2b3a0ebb46e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaCXFExtServerCodegen.java @@ -1368,12 +1368,20 @@ public class JavaCXFExtServerCodegen extends JavaCXFServerCodegen implements CXF } if (this.generateSpringApplication) { if (supportMultipleSpringServices) { + SupportingFile supportingFile = null; for (SupportingFile sf : supportingFiles) { - if ("server/ApplicationContext.xml.mustache".equals(sf.templateFile)) { - sf.destinationFilename = "ApplicationContext-" + invokerPackage + ".xml"; + if ("server/ApplicationContext.xml.mustache".equals(sf.getTemplateFile())) { + supportingFile = sf; break; } } + supportingFiles.remove(supportingFile); + SupportingFile updated = new SupportingFile( + supportingFile.getTemplateFile(), + supportingFile.getFolder(), + "ApplicationContext-" + invokerPackage + ".xml" + ); + supportingFiles.add(updated); } } } 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 e2607138daa..1e8fc07d322 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 @@ -481,7 +481,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen Iterator iter = supportingFiles.iterator(); while (iter.hasNext()) { SupportingFile sf = iter.next(); - if (sf.templateFile.startsWith("auth/")) { + if (sf.getTemplateFile().startsWith("auth/")) { iter.remove(); } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/config/DynamicSettingsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/config/DynamicSettingsTest.java index 5dd45a16a82..8f8b20e9245 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/config/DynamicSettingsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/config/DynamicSettingsTest.java @@ -1,11 +1,17 @@ package org.openapitools.codegen.config; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.guava.GuavaModule; import io.swagger.v3.core.util.Yaml; +import org.openapitools.codegen.api.TemplateDefinition; +import org.openapitools.codegen.api.TemplateFileType; import org.testng.annotations.Test; import java.io.File; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.testng.Assert.*; @@ -110,4 +116,131 @@ public class DynamicSettingsTest { assertEquals(generatorSettings.getAdditionalProperties().get("gitRepoId"), "GIT_REPO_ID"); assertEquals(generatorSettings.getAdditionalProperties().get("releaseNote"), "Minor update"); } + + @Test + public void testFullConfigWithFilesMap() throws JsonProcessingException { + ObjectMapper mapper = Yaml.mapper(); + mapper.registerModule(new GuavaModule()); + + String gitUserId = "openapitools"; + + String spec = new StringJoiner(System.lineSeparator(), "", "") + .add("supportPython2: true") // unwrapped additional property + .add("gitUserId: '" + gitUserId + "'") // unwrapped additional property + .add("generatorName: java") + .add("outputDir: samples/client/petstore/java/feign") + .add("library: feign") + .add("inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml") + .add("additionalProperties:") + .add(" booleanGetterPrefix: is") + .add(" artifactId: petstore-feign") + .add(" hideGenerationTimestamp: \"true\"") + .add("files:") + .add(" README.mustache:") + .add(" folder: ''") + .add(" destinationFilename: README.md") + .add(" build.sh:") + .add(" destinationFilename: build.sh") + .add(" additional/README.mustache:") + .add(" folder: additional") + .add(" destinationFilename: README.md") + .add(" custom/api.mustache:") + .add(" templateType: API") + .add(" custom/api_doc.mustache:") + .add(" templateType: APIDocs") + .add(" custom/api_test.mustache:") + .add(" templateType: APITests") + .add(" custom/model.mustache:") + .add(" templateType: Model") + .add(" custom/model_doc.mustache:") + .add(" templateType: ModelDocs") + .add(" custom/model_test.mustache:") + .add(" templateType: ModelTests") + .add(" additional/build.mustache:") + .add(" folder: build") + .add(" destinationFilename: build.gradle") + .add(" templateType: SupportingFiles") + .add(" LICENSE: {}") + .toString(); + + DynamicSettings dynamicSettings = mapper.readValue(spec, DynamicSettings.class); + GeneratorSettings generatorSettings = dynamicSettings.getGeneratorSettings(); + WorkflowSettings workflowSettings = dynamicSettings.getWorkflowSettings(); + List files = dynamicSettings.getFiles(); + + assertNotNull(dynamicSettings); + assertNotNull(generatorSettings); + assertNotNull(workflowSettings); + assertNotNull(files); + + Map addlProps = generatorSettings.getAdditionalProperties(); + assertEquals(addlProps.get("supportPython2"), true); + assertEquals(addlProps.get("gitUserId"), gitUserId); + assertEquals(addlProps.get("booleanGetterPrefix"), "is"); + assertEquals(addlProps.get("artifactId"), "petstore-feign"); + assertEquals(addlProps.get("hideGenerationTimestamp"), "true"); + assertEquals(generatorSettings.getGeneratorName(), "java"); + assertEquals(workflowSettings.getOutputDir(), "samples/client/petstore/java/feign"); + assertEquals(generatorSettings.getLibrary(), "feign"); + assertEquals(workflowSettings.getInputSpec(), "modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml"); + + assertEquals(files.size(), 11); + + Map mapped = files.stream().collect(Collectors.toMap(TemplateDefinition::getTemplateFile, Function.identity(), (a, b) -> a, TreeMap::new)); + + assertEquals(mapped.get("README.mustache").getTemplateFile(), "README.mustache"); + assertEquals(mapped.get("README.mustache").getFolder(), ""); + assertEquals(mapped.get("README.mustache").getDestinationFilename(), "README.md"); + assertEquals(mapped.get("README.mustache").getTemplateType(), TemplateFileType.SupportingFiles); + + assertEquals(mapped.get("build.sh").getTemplateFile(), "build.sh"); + assertEquals(mapped.get("build.sh").getFolder(), ""); + assertEquals(mapped.get("build.sh").getDestinationFilename(), "build.sh"); + assertEquals(mapped.get("build.sh").getTemplateType(), TemplateFileType.SupportingFiles); + + assertEquals(mapped.get("additional/README.mustache").getTemplateFile(), "additional/README.mustache"); + assertEquals(mapped.get("additional/README.mustache").getFolder(), "additional"); + assertEquals(mapped.get("additional/README.mustache").getDestinationFilename(), "README.md"); + assertEquals(mapped.get("additional/README.mustache").getTemplateType(), TemplateFileType.SupportingFiles); + + assertEquals(mapped.get("custom/api.mustache").getTemplateFile(), "custom/api.mustache"); + assertEquals(mapped.get("custom/api.mustache").getFolder(), ""); + assertEquals(mapped.get("custom/api.mustache").getDestinationFilename(), ""); + assertEquals(mapped.get("custom/api.mustache").getTemplateType(), TemplateFileType.API); + + assertEquals(mapped.get("custom/api_doc.mustache").getTemplateFile(), "custom/api_doc.mustache"); + assertEquals(mapped.get("custom/api_doc.mustache").getFolder(), ""); + assertEquals(mapped.get("custom/api_doc.mustache").getDestinationFilename(), ""); + assertEquals(mapped.get("custom/api_doc.mustache").getTemplateType(), TemplateFileType.APIDocs); + + assertEquals(mapped.get("custom/api_test.mustache").getTemplateFile(), "custom/api_test.mustache"); + assertEquals(mapped.get("custom/api_test.mustache").getFolder(), ""); + assertEquals(mapped.get("custom/api_test.mustache").getDestinationFilename(), ""); + assertEquals(mapped.get("custom/api_test.mustache").getTemplateType(), TemplateFileType.APITests); + + assertEquals(mapped.get("custom/model.mustache").getTemplateFile(), "custom/model.mustache"); + assertEquals(mapped.get("custom/model.mustache").getFolder(), ""); + assertEquals(mapped.get("custom/model.mustache").getDestinationFilename(), ""); + assertEquals(mapped.get("custom/model.mustache").getTemplateType(), TemplateFileType.Model); + + assertEquals(mapped.get("custom/model_doc.mustache").getTemplateFile(), "custom/model_doc.mustache"); + assertEquals(mapped.get("custom/model_doc.mustache").getFolder(), ""); + assertEquals(mapped.get("custom/model_doc.mustache").getDestinationFilename(), ""); + assertEquals(mapped.get("custom/model_doc.mustache").getTemplateType(), TemplateFileType.ModelDocs); + + assertEquals(mapped.get("custom/model_test.mustache").getTemplateFile(), "custom/model_test.mustache"); + assertEquals(mapped.get("custom/model_test.mustache").getFolder(), ""); + assertEquals(mapped.get("custom/model_test.mustache").getDestinationFilename(), ""); + assertEquals(mapped.get("custom/model_test.mustache").getTemplateType(), TemplateFileType.ModelTests); + + assertEquals(mapped.get("additional/build.mustache").getTemplateFile(), "additional/build.mustache"); + assertEquals(mapped.get("additional/build.mustache").getFolder(), "build"); + assertEquals(mapped.get("additional/build.mustache").getDestinationFilename(), "build.gradle"); + assertEquals(mapped.get("additional/build.mustache").getTemplateType(), TemplateFileType.SupportingFiles); + + assertEquals(mapped.get("LICENSE").getTemplateFile(), "LICENSE"); + assertEquals(mapped.get("LICENSE").getFolder(), ""); + assertEquals(mapped.get("LICENSE").getDestinationFilename(), "LICENSE"); + assertEquals(mapped.get("LICENSE").getTemplateType(), TemplateFileType.SupportingFiles); + } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java index 62b8353d69c..b77afc858f9 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java @@ -177,7 +177,7 @@ public class KotlinSpringServerCodegenTest { Assert.assertEquals(codegen.apiTemplateFiles().get("apiInterface.mustache"), ".kt"); Assert.assertEquals(codegen.apiTemplateFiles().get("apiInterface.mustache"), ".kt"); - Assert.assertTrue(codegen.supportingFiles().stream().anyMatch(supportingFile -> supportingFile.templateFile.equals("apiUtil.mustache"))); + Assert.assertTrue(codegen.supportingFiles().stream().anyMatch(supportingFile -> supportingFile.getTemplateFile().equals("apiUtil.mustache"))); } @Test(description = "test delegate with tags")