Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Templating Engine API and handlebars support #690

Closed
wants to merge 15 commits into from

Conversation

rienafairefr
Copy link
Contributor

@rienafairefr rienafairefr commented Jul 30, 2018

PR checklist

Description of the PR

After reading the discussion #510 about switching template systems I thought why not try to implement Handlebars support

In order to suport migration to handlebars templates, I've added the handlebars.java, and in the Generator if the file ends with .mustache then we use Mustache, if it ends with .handlebars we use Handlebars. Keeps backward compatibility easily.

There is a small compatibility fix for maximum compatibility with Mustache (see here

No templates for any language have been migrated for now.

@macjohnny
Copy link
Member

this sounds good, backwards compatibility is always a very nice thing ;-)

@jimschubert
Copy link
Member

I don't know if "migration to handlebars" is a great thing. I'd rather see a pluggable templating engine that each framework can choose either a built-in implementation (we could support mustache + handlebars) or a community-driven adapter for some other engine.

One thing I'm a little cautious about with this PR is that it allows users to mix mustache and handlebars templates within the same generator. We can guard against this in the generators in the repository, but we'd have no real control over what custom templates users implement.

@rienafairefr
Copy link
Contributor Author

@jimschubert I'm not sure what would be the problem in mix and matching templating engines in a given generator. For generators that have a large template codebase mix and matching would alleviate the effort needed to fully migrate to a different template. Or use a certain templating engine because it's simpler for some files, but too heavy for other files ?

I agree though, a pluggable system would be great 👍, registering a given plugin for a given file extension ? Or a system where the templating engine is selected by a command line option ?

@jimschubert
Copy link
Member

@rienafairefr my concern with mixing two template types is that users will begin customizing on this, and it will become more difficult to "migrate" from one as default to another (that is, it'll more likely always be mixed).

I have plans to eventually create an interface for transforming files, and provide both a Mustache and a Handlebars implementation. Types implementing CodegenConfig would then expose that interface as a property, and DefaultGenerator would invoke methods on the interface. This basically offloads the templating aspect from DefaultGenerator to be a concern of the framework generator, and allows frameworks to eventually use framework-specific or community/language-preferred templating.

For instance, the Ruby community may prefer ERB. The JavaScript community may prefer haml or something else. It would be great if one could do:

# Add some jar implementing SmartyTemplateAdapter to classpath
export CLASSPATH=…
openapi-generator generate -g php --template-engine SmartyTemplateAdapter…

This should remove one of the larger barriers I've seen users have, which is modifying the mustache templates and trying to figure out the object structure passed into the templates.

I have an image in #503 which I think shows the extraction of a template engine a bit. I'd also like to have a clearly defined type being passed into the engine of choice rather than the current Map<String, Object>. Such a change should make it easier for us to identify breaking changes, and potentially add unit tests for templates themselves.

I consider template engine cleanup to be part of the medium-term efforts defined on the roadmap.

I'll try to find some time in the next few weeks to create a tracking project for these types of design considerations.

@rienafairefr
Copy link
Contributor Author

@jimschubert OK I see your point, would be better as you described it in #503 among others.

Reworking this PR's code to have an interface for templating engines is something I could maybe do, might send an update.

For the usage,

# Add some jar implementing SmartyTemplateAdapter to classpath
export CLASSPATH=…
openapi-generator generate -g php --template-engine SmartyTemplateAdapter…

is modifying the classpath the only way ? I could see using a plugin framework for that, a subfolder 'plugins' in the working directory would contain JARs, each implementing a tempalting engine, then the
openapi-generator generate -g php --template-engine smarty would select the SmartyTemplateEngine from the JAR. There would be a openapi-generator-api package containing the interface to implement, separate from openapi-generator ?

A fixed type for the template engine input seems also good to me, I'm also quite allergic to Map<String, Object> 👍

@jimschubert
Copy link
Member

@rienafairefr I've done a prototype that has a plugin system.

A (minor?) issue with doing the plugin route is that it could cause versioning conflicts if the plugins pulled in dependencies we're already using. You could get that with the class path usage, but this way wouldn't be hidden from the user.

If you do get a chance to create a template engine interface, that would be fantastic. If not, I'm going to try to get it done in the next week or two.

@rienafairefr
Copy link
Contributor Author

OK thanks for the pointers.

I have something working for templating interface (and a branch built on it with plugins), I'm pushing it to this PR, should be simpler than a new one and will keep these conversations.

@rienafairefr rienafairefr force-pushed the templating branch 2 times, most recently from 3b837f1 to 5365210 Compare August 17, 2018 10:46
@rienafairefr rienafairefr changed the title Handlebars templates files support Templating Engine API and handlebars support Aug 17, 2018
Copy link
Member

@jimschubert jimschubert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few comments. I see that you're actively committing, so my review isn't 100% complete. I just wanted to submit comments on what exists now.

</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>openapi-generator-api</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this package be renamed to openapi-generator-core? The -api seems redundant with the openapi, and some may confuse this package with openapi-generator-online.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, that's a better name for sure.

/*
Used to determine whether a given supporting file is a template
*/
String getFileExtension();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be an array. For example, Handlebars common extensions are .hbs and .handlebars.

.compile(template);

writeToFile(outputFilename, tmpl.execute(bundle));
if (templateFile.endsWith(templatingEngine.getFileExtension())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A key point that is missing here is:

compiler = config.processCompiler(compiler);

This allows generators to extend or configure the Mustache.Compiler instance. See example at

public Mustache.Compiler processCompiler(Mustache.Compiler compiler) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I see.

These are changing some Mustache-specific parameters though, not sure I I extract those to the TemplatingEngineAdapter interface, or I can just do the compiler = config.processCompiler(compiler); step only when using a MustacheEngineAdapter ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, 2nd option is definitely the easiest to implement, just pushed that. I think I'll be adding a processTemplatingEngine to the CodegenConfig interface, and *Codegen writers could do it themselves, casting to their particular language/generator preferred TemplatingEngineAdpater

public interface TemplatingGenerator {

// returns the template content by name
String getFullTemplate(String name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name isn't too clear to me (is it a full path to the template, or template contents)? The comment clarifies this, and should be a standard JavaDoc comment.

Could this be renamed to getFullTemplateContents for clarity? Usually, naming isn't that important, but this will quickly become a method name required by implementers which makes it harder to change later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, getFullTemplateContents seems better to me also :-)


writeToFile(adjustedOutputFilename, tmpl.execute(templateData));
String templateContent = templatingEngine.doProcessTemplateToFile(this, templateData, templateName);
writeToFile(adjustedOutputFilename, templateContent);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, also missing extension of the templating engine:

compiler = config.processCompiler(compiler);

@jimschubert
Copy link
Member

@rienafairefr I forgot to include this in my review comments, but the new module will need to be added to the root Dockerfile as well.

diff --git a/Dockerfile b/Dockerfile
index e53ca77d8..24e0b1814 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,6 +18,7 @@ COPY ./modules/openapi-generator-gradle-plugin ${GEN_DIR}/modules/openapi-genera
 COPY ./modules/openapi-generator-maven-plugin ${GEN_DIR}/modules/openapi-generator-maven-plugin
 COPY ./modules/openapi-generator-online ${GEN_DIR}/modules/openapi-generator-online
 COPY ./modules/openapi-generator-cli ${GEN_DIR}/modules/openapi-generator-cli
+COPY ./modules/openapi-generator-api ${GEN_DIR}/modules/openapi-generator-api
 COPY ./modules/openapi-generator ${GEN_DIR}/modules/openapi-generator
 COPY ./pom.xml ${GEN_DIR}
 

@rienafairefr
Copy link
Contributor Author

OK, I've added the module to the dockerfile, thanks

@rienafairefr rienafairefr force-pushed the templating branch 3 times, most recently from 38c1fdc to 7fa582c Compare August 20, 2018 09:05
@rienafairefr
Copy link
Contributor Author

OK @jimschubert I think I converged on something working with your review, thanks. Anyone else I should @mention ? Should this still target master, BTW ? Creating the -core package interface seems to me like a major change.

@jimschubert
Copy link
Member

@rienafairefr this should target the 4.0.0 branch as it is breaking changes (changing the CodegenConfig interface).

@wing328 and maybe @jmini could help review

@rienafairefr rienafairefr changed the base branch from master to 4.0.x August 22, 2018 08:13
@rienafairefr rienafairefr force-pushed the templating branch 3 times, most recently from 6921769 to 0ee5694 Compare August 22, 2018 12:13
@loganknecht
Copy link

loganknecht commented Mar 11, 2019

Hey @rienafairefr, @jimschubert, and @wing328

Can you please make sure I'm not going crazy? I am still not able to generate the correct api spec when using the --template-dir flag using this branch.

The issue is that I have this command

java -jar /Users/lknecht/Repositories/sdk-codegen/openapi-generator-cli.jar generate \
         -i /Users/lknecht/Repositories/sdk-codegen/specs/action.yaml \
         --skip-validate-spec -g go \
         -DpackageName=action \
         --engine handlebars \
         --template-dir /Users/lknecht/Repositories/sdk-codegen/scripts/templates/go \
         -o /Users/lknecht/Repositories/sdk-codegen/generated/action-go

And I'm doing a very simple template configuration. There are three templates I'm providing for generation. The three files are

  • api.hbs
  • model.hbs
  • partial_header.hbs

The template files look like this
api.hbs

{{>partial_header}}
package {{packageName}}

{{#operations}}
import (
    "net/http"
)

type Generated{{classname}}Servicer interface {
    {{#operation}}
        {{#allParams}}
            {{#required}}
                {{#if isPathParam}}
                Found path parameter
                {{/if}}
            {{/required}}
        {{/allParams}}
    {{/operation}}
}
{{/operations}}

model.hbs

package {{packageName}}
{{#models}}
Don't generate any model information :)
{{/models}}

partial_header.hbs

/*
 {{#appName}}
 * {{{appName}}}
 *
 {{/appName}}
 {{#appDescription}}
 * {{{appDescription}}}
 *
 {{/appDescription}}
 {{#version}}
 * API version: {{{version}}}
 {{/version}}
 {{#infoEmail}}
 * Contact: {{{infoEmail}}}
 {{/infoEmail}}
{{^withGoCodegenComment}}
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
{{/withGoCodegenComment}}
 */
{{#withGoCodegenComment}}

// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
{{/withGoCodegenComment}}

However the output I'm getting is looking like this
model_action.go

/*
 * Action Service
 *
 * The Splunk Cloud Action service receives incoming notifications, then uses pre-defined action templates to turn these notifications into meaningful actions.
 *
 * API version: v1beta2
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
 */

package action

type Action struct {
	// The name of the action, as one or more identifier strings separated by periods. Each identifier string consists of lowercase letters, digits, and underscores, and cannot start with a digit.
	Name string `json:"name"`
	Kind Kind `json:"kind"`
	// A human-readable title for the action. Must be less than 128 characters.
	Title string `json:"title,omitempty"`
	Subject string `json:"subject,omitempty"`
	// HTML content to send as the body of the email. You can use a template in this field.
	Body string `json:"body,omitempty"`
	// Optional text to send as the text/plain part of the email. If this field is not set for an email action, the Action service converts the value from the body field to text and sends that as the text/plain part when triggering the action.
	BodyPlainText string `json:"bodyPlainText,omitempty"`
	// An array of email addresses.
	Addresses []string `json:"addresses,omitempty"`
	// Only HTTPS is allowed. 
	WebhookUrl string `json:"webhookUrl,omitempty"`
	// The (possibly) templated payload body, which is POSTed to the webhookUrl when triggered. 
	WebhookPayload string `json:"webhookPayload,omitempty"`
}

and the api information looks entirely different from the template as well.

HOWEVER, it looks EXACTLY like the default template provided by the open api generator's model.mustache file located in openapi-generator/modules/openapi-generator/src/main/resources/go/models.mustache.

{{>partial_header}}
package {{packageName}}
{{#models}}
{{#imports}}
{{#-first}}
import (
{{/-first}}
	"{{import}}"
{{#-last}}
)
{{/-last}}
{{/imports}}
{{#model}}
{{#isEnum}}
{{#description}}
// {{{classname}}} : {{{description}}}
{{/description}}
type {{{classname}}} {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}}

// List of {{{name}}}
const (
	{{#allowableValues}}
	{{#enumVars}}
	{{^-first}}
	{{/-first}}
	{{name}} {{{classname}}} = "{{{value}}}"
	{{/enumVars}}
	{{/allowableValues}}
){{/isEnum}}{{^isEnum}}{{#description}}
// {{{description}}}{{/description}}
type {{classname}} struct {
{{#vars}}
{{^-first}}
{{/-first}}
{{#description}}
	// {{{description}}}
{{/description}}
	{{name}} {{#isNullable}}*{{/isNullable}}{{{dataType}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"{{#withXml}} xml:"{{baseName}}{{#isXmlAttribute}},attr{{/isXmlAttribute}}"{{/withXml}}{{#vendorExtensions.x-go-custom-tag}} {{{.}}}{{/vendorExtensions.x-go-custom-tag}}`
{{/vars}}
}
{{/isEnum}}
{{/model}}
{{/models}}

Steps to Reproduce

git clone https://github.com/rienafairefr/openapi-generator.git && \
cd openapi-generator && \
git checkout templating && \
mvn clean install
java -jar /path/to/openapi-generator-cli.jar generate \
         -i /path/to/example_spec.yaml \
         --skip-validate-spec -g go \
         -DpackageName=<go_package_name> \
         --engine handlebars \
         --template-dir /path/to/templates/directory \
         -o /path/to/output/directory

Conclusion
In conclusion, I would like to communicate that the --template-dir flag is NOT working when I pass in a template directory using handlebars file. I can't tell if this is me doing something silly, or if there is something going on here. But my previous experience has been that the --template-dir did work when I used it for generation. What IS happening is that it's using the default templates provided in the directory openapi-generator/modules/openapi-generator/target/classes/go/model.mustache instead and as a result I am unable to use the custom templating functionality.

Super keen to hear your feedback on this folks!

As always keep up the great work! All of your hard work is very much appreciated!

@rienafairefr
Copy link
Contributor Author

@wing328 just rebased on latest master, including the modifications by @jimschubert (Thanks!)
@loganknecht the code seems to compile fine with the latest version.

@loganknecht
Copy link

loganknecht commented Mar 11, 2019

@rienafairefr I asked a couple of cohorts and the compilation issue appears to be machine specific.

However the --template-dir issue where my handlebars templates aren't being used is still a problem.

Is there any chance you can provide an example of how to do code generation using the --template-dir flag in conjunction with the handlebars templates?

@jimschubert
Copy link
Member

@loganknecht I've spent a little time evaluating the example you presented above, I really appreciate it. It looks like the branch only supported the default Java Bean style accessors, so if you were trying to access any properties in the bundle we pass to the template which don't follow Java Bean style… you wouldn't have been able to access those properties. This is likely the problem you're seeing.

I pushed a branch to this repo at https://github.com/OpenAPITools/openapi-generator/tree/rienafairefr-templating. I was wondering if you could try this out? If it works, we can go ahead and merge that branch which is based off of this PR's head commit as it also updates README.md with the new option and a caveat that only Mustache is officially supported for embedded templates.

Here's a minimal api.hbs to get you started in evaluation on that branch:

{{>partial_header}}
package {{packageName}}

import (
    "net/http"
{{~#each imports}}
    "{{import}}"
{{~/each}}
)

type Generated{{classname}}Servicer 

{{#operations}}

{{!-- an operation's "value" is an array of CodegenOperation --}}
{{#each operation as |op index|}}

{{#if op.hasOptionalParams}}

type {{{op.nickname}}}Opts struct {
{{~#allParams~}}
{{^required}}
{{~#isPrimitiveType~}}
{{^isBinary}}
	{{vendorExtensions.x-exportParamName}} optional.{{vendorExtensions.x-optionalDataType}}
{{~/isBinary}}
{{~#isBinary~}}
	{{vendorExtensions.x-exportParamName}} optional.Interface
{{~/isBinary~}}
{{~/isPrimitiveType~}}
{{^isPrimitiveType}}
	{{vendorExtensions.x-exportParamName}} optional.Interface
{{~/isPrimitiveType~}}
{{~/required~}}
{{~/allParams}}
}

{{/if}}

// operation number: {{index}}
func (a *Generated{{{classname}}}Servicer) {{op.nickname}}(ctx context.Context{{#if op.hasParams}}, {{/if}}{{#each op.allParams}}{{#if required}}{{paramName}} {{{dataType}}}{{#if this.hasMore}}, {{/if}}{{/if}}{{/each}}{{#if op.hasOptionalParams}}localVarOptionals *{{{op.nickname}}}Opts{{/if}}) ({{#if returnType}}{{{returnType}}}, {{/if}}*http.Response, error) {
    return {{#if returnType}}{{{returnType}}}, {{/if}}nil, nil
}
{{/each}}
{{/operations}}

Running this locally appears to generate expected struct and signatures (I cleaned some whitespace manually):

package openapi

import (
    "net/http"
    "fmt"
    "github.com/antihax/optional"
    "os"
)

type GeneratedPetApiServicer 

// operation number: 0
func (a *GeneratedPetApiServicer) AddPet(ctx context.Context, body Pet) (*http.Response, error) {
    return nil, nil
}

type DeletePetOpts struct {
	ApiKey optional.String
}

// operation number: 1
func (a *GeneratedPetApiServicer) DeletePet(ctx context.Context, petId int64, localVarOptionals *DeletePetOpts) (*http.Response, error) {
    return nil, nil
}

// operation number: 2
func (a *GeneratedPetApiServicer) FindPetsByStatus(ctx context.Context, status []string) ([]Pet, *http.Response, error) {
    return []Pet, nil, nil
}

// operation number: 3
func (a *GeneratedPetApiServicer) FindPetsByTags(ctx context.Context, tags []string) ([]Pet, *http.Response, error) {
    return []Pet, nil, nil
}

// operation number: 4
func (a *GeneratedPetApiServicer) GetPetById(ctx context.Context, petId int64) (Pet, *http.Response, error) {
    return Pet, nil, nil
}

// operation number: 5
func (a *GeneratedPetApiServicer) UpdatePet(ctx context.Context, body Pet) (*http.Response, error) {
    return nil, nil
}

type UpdatePetWithFormOpts struct {
	Name optional.String
	Status optional.String
}

// operation number: 6
func (a *GeneratedPetApiServicer) UpdatePetWithForm(ctx context.Context, petId int64, localVarOptionals *UpdatePetWithFormOpts) (*http.Response, error) {
    return nil, nil
}

type UploadFileOpts struct {
	AdditionalMetadata optional.StringFile optional.Interface
}

// operation number: 7
func (a *GeneratedPetApiServicer) UploadFile(ctx context.Context, petId int64, localVarOptionals *UploadFileOpts) (ApiResponse, *http.Response, error) {
    return ApiResponse, nil, nil
}

@loganknecht
Copy link

Hey @jimschubert! Sorry to leave you hanging on validating this! I did try to validate this, but I keep having personal system errors with some type of locale issue.

For clarification, my development system is set to Japanese, and I'm not sure if that's causing an issue. What happens is I get test failures for something called a "Forbidden API usage" when string.format is called in the TemplateEngineAdapter.

Because of this I don't know if I will be able to correctly verify this. If you think this is ready and that it can be used in conjunction with the --engine and --template-dir flags, then please don't hold this up on my account.

Thank you so much for all the great work you guys are doing!

@jimschubert jimschubert self-assigned this Mar 26, 2019
@jimschubert
Copy link
Member

@loganknecht thanks for the information. Sounds like a static analysis error. I'll rerun later today or tomorrow and try to repro the error you're seeing. I may have only done a "quick build" which skips analysis, thinking it only skips unit tests.

@jimschubert
Copy link
Member

Checking in: I haven't yet had time to evaluate the language issue from above. I haven't forgotten about it.

@jimschubert
Copy link
Member

@loganknecht I see. I don't evaluate changes using mvn install, and I guess the static analysis done during mvn install isn't done in the PR checks.

Here's a diff you can apply locally:

diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java
index 19965f2e6d..3255b9c858 100644
--- a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java
+++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/api/AbstractTemplatingEngineAdapter.java
@@ -1,5 +1,7 @@
 package org.openapitools.codegen.api;
 
+import java.util.Locale;
+
 /**
  * Provides abstractions around the template engine adapter interface, for reuse by implementers.
  */
@@ -17,7 +19,7 @@ public abstract class AbstractTemplatingEngineAdapter implements TemplatingEngin
         String[] result = new String[extensions.length];
         for (int i = 0; i < extensions.length; i++) {
             String extension = extensions[i];
-            result[i] = String.format("%s.%s", getPathWithoutExtension(location), extension);
+            result[i] = String.format(Locale.ROOT, "%s.%s", getPathWithoutExtension(location), extension);
         }
         return result;
     }

I've also pushed the change to the previously mentioned branch. Let me know if that works for you, and I'll coordinate with the core team about next steps for the PR.

@mnboos
Copy link
Contributor

mnboos commented Apr 10, 2019

@rienafairefr @loganknecht Hope you can help me. I tried to use your fork but couldn't get it to work:

The folder templates\python contains only a single file with the extension .hbs.

C:\Temp\openapi>java -jar openapi-generator-cli-handlebars.jar generate -t templates\python -i sample_api.json -g python -o output_dir -e handlebars
[main] INFO  o.o.c.languages.PythonClientCodegen - Environment variable PYTHON_POST_PROCESS_FILE not defined so the Python code may not be properly formatted. To define it, try 'export PYTHON_POST_PROCESS_FILE="/usr/local/bin/yapf -i"' (Linux/Mac)
[main] INFO  o.o.c.languages.PythonClientCodegen - NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).
[main] ERROR o.o.codegen.AbstractGenerator - python\model.mustache.handlebars (The system cannot find the path specified)
[main] ERROR o.o.codegen.AbstractGenerator - python\model.mustache.handlebars (The system cannot find the path specified)
[main] ERROR o.o.codegen.AbstractGenerator - can't load template python\model.mustache.handlebars
[main] ERROR o.o.codegen.AbstractGenerator - can't load template python\model.mustache.handlebars
[main] ERROR o.o.codegen.AbstractGenerator - python\model.mustache.hbs (The system cannot find the path specified)
[main] ERROR o.o.codegen.AbstractGenerator - python\model.mustache.hbs (The system cannot find the path specified)
[main] ERROR o.o.codegen.AbstractGenerator - can't load template python\model.mustache.hbs
[main] ERROR o.o.codegen.AbstractGenerator - can't load template python\model.mustache.hbs
Exception in thread "main" java.lang.RuntimeException: Could not generate model 'Error'
        at org.openapitools.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:507)
        at org.openapitools.codegen.DefaultGenerator.generate(DefaultGenerator.java:898)
        at org.openapitools.codegen.cmd.Generate.run(Generate.java:362)
        at org.openapitools.codegen.OpenAPIGenerator.main(OpenAPIGenerator.java:61)
Caused by: java.lang.RuntimeException: couldnt find a subtemplate model.mustache
        at org.openapitools.codegen.templating.HandlebarsEngineAdapter.findTemplate(HandlebarsEngineAdapter.java:43)
        at org.openapitools.codegen.templating.HandlebarsEngineAdapter$1.sourceAt(HandlebarsEngineAdapter.java:25)
        at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:438)
        at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:419)
        at org.openapitools.codegen.templating.HandlebarsEngineAdapter.compileTemplate(HandlebarsEngineAdapter.java:31)
        at org.openapitools.codegen.DefaultGenerator.processTemplateToFile(DefaultGenerator.java:922)
        at org.openapitools.codegen.DefaultGenerator.generateModel(DefaultGenerator.java:320)
        at org.openapitools.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:496)
        ... 3 more

@mnboos
Copy link
Contributor

mnboos commented Apr 10, 2019

Got it working from branch rienafairefr-templating by manually creating the missing files as copies from the mustache templates.

Afterwards I realized, there is no possibility to add handlebars helpers, thus limiting the power of handlebars.

Additionally, this concerns me:

It's basically an empty try-catch and I'd rather be informed about invalid templates rather than errors being silently swallowed.

@jimschubert
Copy link
Member

@mnboos This branch is an initial implementation to get such feedback, and your initial take is very helpful. I agree about the missing helper issue, maybe we could support skipping this via some property ("strict-templating"?). We tend toward making things easier for new template/generator authors, so I'd like to avoid failing too hard if someone is coming from mustache to handlebars... but we should log a warning at the very least when this occurs.

The intent of the templating abstraction is to allow generator authors to extend templating to support their needs. Ideally, I'd like to support a small set of helpers with the built-in implementation. Do you have suggestions about which helpers are the most useful?

@mnboos
Copy link
Contributor

mnboos commented Apr 11, 2019

@jimschubert Thank you for your response! Just logging any missing handlebars helpers is totally fine, especially in terms of robustness.

I was missing a helper like startsWith but unfortunatetly, that's not available from the built-in helpers. However, it would still be possible using a combination of string helpers and conditionals.

Additionally, I'd like to have a way to create own templates. As far as I know, if I create a completely new template, it won't be processed. This would be helpful, for example for creating a custom test class or something else.

What is also a bit unclear to me is whether I have to have all templates available for the chosen templating engine or if it should fall back to mustache.

@jimschubert
Copy link
Member

I've created #2656 to track user-defined extensions to the templates file arrays which the generator processes.

I'll look at what it would take to define some built-in helpers similar to what we have for mustache.

@jimschubert
Copy link
Member

I've opened #2657 with a clean merge of master, planning to get this merged before the 4.0 release.

Closing this in favor of 2657.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants