Skip to content

Swagger Codegen migration from Mustache and Handlebars templates.

Akshay Mankar edited this page Feb 23, 2019 · 7 revisions

Mustaches and Handlebars use a similar syntax, the implementing changes have been small in order to migrate to handlebars, but there are some details that must be kept in mind.

Here is a list of the main changes done on .mustache files in order to support handlebars:

Blank spaces

To avoid blank spaces between elements we need to use ~ character. This sample {{#elem}} ... {{/elem}} should be used this way: {{#elem}} ... {{/elem~}} to avoid an extra line on the result.

First and last elements

  • For {{#-last}} element we should replace the - character with @. We'll need to use: {{#@last}}
  • The same with {{#-first}} that becomes {{#@first}}

Properties: is, isNot, has, hasNot...

The main change that affects templates is that is*, isNot*, has*, hasNot* properties have been removed from codegen pojo (CodegenModel, CodegenProperty, etc) and replaced for extensions. So one option to access those values is: {{#vendorExtension.x-is-enum}}. However we've created handlebar helpers in order to keep using this in a similar way, the sample before can also be expressed this way: {{#is this 'enum'}}, where this references to a codegen pojo in the template.

Evaluate a path against its parent context

Inside nested handlebars, in order to refer to the parent context, ../this must be used instead of this.

Complete example:

CodegenModel has following structure:

public class CodegenModel implements VendorExtendable {
    //...
    public Map<String, Object> allowableValues;
    //...
}

The values of allowableValues we are interested in computed in DefaultCodegenConfig.processModelEnums(Map<String, Object>). Here is a simplified version:

public void processModelEnums(Map<String, Object> objs) {
	//... 

    List<Map<String, String>> enumVars = new ArrayList<Map<String, String>>();
        //... 
        for (...) {
                //... 
        enumVar.put("name", name);
        enumVar.put("value", value);
        enumVars.add(enumVar);
        }
    cm.allowableValues.put("enumVars", enumVars);

}

Meaning that we have a map containing a list of maps.

Now consider a mustache template that reads the values in this structure (this example is inspired by a real example in enumClass.mustache. In the real example, the template defines the code generated inside the @XmlEnumValue annotation, I have simplified it):

    {{#allowableValues}}
Possible values: {{#enumVars}}{{{value}}}{{^@last}}, {{/@last}}{{/enumVars}}
    {{/allowableValues}}

Now imagine that inside {{#enumVars}}..{{/enumVars}} block, you need the type of the value that is defined in the root CodegenModel. With the mustache templating engine (with swagger v2, on the master branch) it was possible to use {{#isInteger}}..{{/isInteger}} to refer to the value defined in CodegenModel. With handlebars this possibility was removed and replaced by: {{#is this 'integer'}}..{{/is}}. But this produces following error:

Exception in thread "main" java.lang.RuntimeException: Could not generate model ‘XXX’
	at io.swagger.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:409)
	at io.swagger.codegen.DefaultGenerator.generate(DefaultGenerator.java:728)
Caused by: com.github.jknack.handlebars.HandlebarsException: /v2/JavaJaxRS/cxf/enumClass.mustache:6:30: java.lang.ClassCastException: java.util.HashMap cannot be cast to io.swagger.codegen.VendorExtendable
    /v2/JavaJaxRS/cxf/enumClass.mustache:6:30
	at io.swagger.codegen.languages.helpers.ExtensionHelper.apply(ExtensionHelper.java:1)
	at com.github.jknack.handlebars.internal.Block.merge(Block.java:211)
	at com.github.jknack.handlebars.internal.BaseTemplate.apply(BaseTemplate.java:130)
	at com.github.jknack.handlebars.internal.TemplateList.merge(TemplateList.java:95)
	at com.github.jknack.handlebars.internal.BaseTemplate.apply(BaseTemplate.java:130)
	... X more
Caused by: java.lang.ClassCastException: java.util.HashMap cannot be cast to io.swagger.codegen.VendorExtendable
	... 88 more

This is absolutely correct, because at this point this is not the expected CodegenModel but the HashMap. Solution can be found in the handlebars docs:

Nested handlebars paths can also include ../ segments, which evaluate their paths against a parent context.

This means that in our case, we need to do:

    {{#allowableValues}}
Possible values: {{#enumVars}}{{{value}}} ({{#is ../../this 'integer'}}INTEGER TYPE{{/is}}){{^@last}}, {{/@last}}{{/enumVars}}
    {{/allowableValues}}

Custom delimiters

In Mustache, it was possible to set custom delimiters. Example (source):

{{ default_tags }}
{{=<% %>=}}
<% erb_style_tags %>
<%={{ }}=%>
{{ default_tags_again }}

Explanation:

  • On the first and last line the default delimiters {{ and }} are used.
  • Line 2 indicates that <% and %> should be used instead of {{ and }}.
  • Line 3 uses the custom delimiters
  • Line 4 indicates that {{ and }} should be set instead of <% and %>.

This do not seems to work with handlebars. The braces helper needs to be used.

Example in swagger templates (example from /modules/swagger-codegen/src/main/htmlDocs2/sample_js.mustache).

Mustache:

...some text.. {{=< >=}}{<&dataType>}<={{ }}=> .. and more .......

But this does'nt work with handlebar java:

...some text.. {{braces "left"}}{{&dataType}}{{braces "right"}} .. and more .......

So these are basically the changes implemented in order to reuse a mustache template in handlebars. I understand that there are more considerations and features in handlebars that can be described, but till now these have been the required changes for migration.