-
Notifications
You must be signed in to change notification settings - Fork 6k
Swagger Codegen migration from Mustache and Handlebars templates.
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:
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.
- For
{{#-last}}
element we should replace the-
character with@
. We'll need to use:{{#@last}}
- The same with
{{#-first}}
that becomes{{#@first}}
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.
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}}
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.
would suggest when compiling to use flag to ignore tests. The tests requires extra dependencies and the compilation might fail due to that.