diff --git a/docs/templating.md b/docs/templating.md index a2f5b2335b1..1f8cad04332 100644 --- a/docs/templating.md +++ b/docs/templating.md @@ -26,6 +26,29 @@ OpenAPI Generator supports user-defined templates. This approach is often the ea > **Note:** You cannot use this approach to create new templates, only override existing ones. If you'd like to create a new generator to contribute back to the project, see `new.sh` in the repository root. If you'd like to create a private generator for more templating control, see the [customization](./customization.md) docs. +OpenAPI Generator not only supports local files for templating, but also templates defined on the classpath. This is a great option if you want to reuse templates across multiple projects. To load a template via classpath, you'll need to generate a little differently. For example, if you've created an artifact called `template-classpath-example` which contains extended templates for the `htmlDocs` generator with the following structure: + +``` +└── src + ├── main + │   ├── java + │   └── resources + │   └── templates + │   └── htmlDocs + │   ├── index.mustache + │   └── style.css.mustache +``` + +You can define your classpath to contain your JAR and the openapi-generator-cli _fat jar_, then invoke main class `org.openapitools.codegen.OpenAPIGenerator`. For instance, + +```sh +java -cp /path/totemplate-classpath-example-1.0-SNAPSHOT.jar:modules/openapi-generator-cli/target/openapi-generator-cli.jar \ + org.openapitools.codegen.OpenAPIGenerator generate -i https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml \ + -g html -o template-example -t templates/htmlDocs +``` + +Note that our template directory is relative to the resource directory of the JAR defined on the classpath. + ### Custom Logic For this example, let's modify a Java client to use AOP via [jcabi/jcabi-aspects](https://github.com/jcabi/jcabi-aspects). We'll log API method execution at the `INFO` level. The jcabi-aspects project could also be used to implement method retries on failures; this would be a great exercise to further play around with templating. diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/WorkflowSettings.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/WorkflowSettings.java index 2bc94188cfa..0fdc6c6e99c 100644 --- a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/WorkflowSettings.java +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/WorkflowSettings.java @@ -21,6 +21,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; @@ -406,15 +409,29 @@ public class WorkflowSettings { */ public Builder withTemplateDir(String templateDir) { if (templateDir != null) { + URI uri = null; File f = new File(templateDir); // check to see if the folder exists - if (!(f.exists() && f.isDirectory())) { + if (f.exists() && f.isDirectory()) { + uri = f.toURI(); + this.templateDir = Paths.get(uri).toAbsolutePath().toString(); + } else { + URL url = this.getClass().getClassLoader().getResource(templateDir); + if (url != null) { + try { + uri = url.toURI(); + this.templateDir = templateDir; + } catch (URISyntaxException e) { + LOGGER.warn("The requested template was found on the classpath, but resulted in a syntax error."); + } + } + } + + if (uri == null) { throw new IllegalArgumentException( "Template directory " + templateDir + " does not exist."); } - - this.templateDir = Paths.get(f.toURI()).toAbsolutePath().toString(); } return this; diff --git a/modules/openapi-generator-maven-plugin/README.md b/modules/openapi-generator-maven-plugin/README.md index 9714ace95f9..2522af1f212 100644 --- a/modules/openapi-generator-maven-plugin/README.md +++ b/modules/openapi-generator-maven-plugin/README.md @@ -48,6 +48,7 @@ mvn clean compile | `generatorName` | `openapi.generator.maven.plugin.generatorName` | target generator name | `output` | `openapi.generator.maven.plugin.output` | target output path (default is `${project.build.directory}/generated-sources/openapi`. Can also be set globally through the `openapi.generator.maven.plugin.output` property) | `templateDirectory` | `openapi.generator.maven.plugin.templateDirectory` | directory with mustache templates +| `templateResourcePath` | `openapi.generator.maven.plugin.templateResourcePath` | directory with mustache templates via resource path. This option will overwrite any option defined in `templateDirectory`. | `addCompileSourceRoot` | `openapi.generator.maven.plugin.addCompileSourceRoot` | add the output directory to the project as a source root (`true` by default) | `modelPackage` | `openapi.generator.maven.plugin.modelPackage` | the package to use for generated model objects/classes | `apiPackage` | `openapi.generator.maven.plugin.apiPackage` | the package to use for generated api objects/classes diff --git a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java index 49fb45efa07..c03586f39a0 100644 --- a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java +++ b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java @@ -40,6 +40,7 @@ import java.util.Set; import com.google.common.io.ByteSource; import com.google.common.io.CharSource; import io.swagger.v3.parser.util.ClasspathHelper; +import org.apache.commons.lang3.StringUtils; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -133,6 +134,12 @@ public class CodeGenMojo extends AbstractMojo { @Parameter(name = "templateDirectory", property = "openapi.generator.maven.plugin.templateDirectory") private File templateDirectory; + /** + * Resource path containing template files. + */ + @Parameter(name = "templateResourcePath", property = "openapi.generator.maven.plugin.templateResourcePath") + private String templateResourcePath; + /** * The name of templating engine to use, "mustache" (default) or "handlebars" (beta) */ @@ -583,6 +590,13 @@ public class CodeGenMojo extends AbstractMojo { configurator.setTemplateDir(templateDirectory.getAbsolutePath()); } + if (StringUtils.isNotEmpty(templateResourcePath)) { + if (null != templateDirectory) { + LOGGER.warn("Both templateDirectory and templateResourcePath were configured. templateResourcePath overwrites templateDirectory."); + } + configurator.setTemplateDir(templateResourcePath); + } + if (null != engine) { configurator.setTemplatingEngineName(engine); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java index 64b74e1ad0a..ba17f231aeb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/AbstractGenerator.java @@ -161,14 +161,14 @@ public abstract class AbstractGenerator implements TemplatingGenerator { if (StringUtils.isNotEmpty(library)) { //look for the file in the library subfolder of the supplied template final String libTemplateFile = buildLibraryFilePath(config.templateDir(), library, templateFile); - if (new File(libTemplateFile).exists()) { + if (new File(libTemplateFile).exists() || this.getClass().getClassLoader().getResource(libTemplateFile) != null) { return libTemplateFile; } } //check the supplied template main folder for the file final String template = config.templateDir() + File.separator + templateFile; - if (new File(template).exists()) { + if (new File(template).exists() || this.getClass().getClassLoader().getResource(template) != null) { return template; }