Description
Description
Hey all, I love your work.
I've upgraded from 2.3.1 and have found that my custom mustache files are no longer found when running swagger-codegen. I have dug in a bit and have found that the templateDir
variable is overwritten after being properly set from my gradle task. What's worse, is that I successfully was able to override a method on JavaClientCodegen
to set templateDir
properly again, and when it came time for processing the template the template was not found for some mysterious reason.
Swagger-codegen version
3.0.0-rc0
Relevant code
I ended up cloning the repositories and building locally to be able to run with the latest code since I was having the issue on 3.0.0-rc0 and found This issue over on the swagger-codegen-generators GitHub project that made it seem possible it was already resolved on the SNAPSHOT.
Gradle buildSrc:
apply plugin: 'java'
repositories {
mavenLocal()
jcenter()
}
dependencies {
compile group: 'io.swagger', name: 'swagger-codegen-cli', version: '3.0.0-SNAPSHOT'
// compile group: 'io.swagger', name: 'swagger-codegen-generators', version: '1.0.0-SNAPSHOT'
// I tried both of the above
}
build.gradle:
import io.swagger.codegen.config.CodegenConfigurator
import io.swagger.codegen.DefaultGenerator
apply plugin: 'java'
apply plugin: 'maven'
buildscript {
repositories {
mavenLocal()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath('io.swagger:swagger-codegen:3.0.0-SNAPSHOT')
}
}
def swaggerInput = "src/main/resources/swaggerdoc.json"
def swaggerOutputDir = file('build/swagger')
task ('swagger') {
inputs.file(swaggerInput)
outputs.dir(swaggerOutputDir)
doLast {
def config = new CodegenConfigurator()
config.setInputSpec(swaggerInput)
config.setOutputDir(swaggerOutputDir.path)
config.setLang('com.job.qa.automation.lib.codegen.MyJavaCodegen')
config.setTemplateDir('src/main/resources/templates')
config.setAdditionalProperties([
'invokerPackage' : 'com.job.api.util',
'modelPackage' : 'com.job.api.dto',
'apiPackage' : 'com.job.api.controllers',
'dateLibrary' : 'java8',
])
new DefaultGenerator().opts(config.toClientOptInput()).generate()
}
}
MyJavaCodegen. I should note that I've already successfully used a custom codegen class such as this, but stripped it down to just focus on this template problem:
package com.job.qa.automation.lib.codegen;
import io.swagger.codegen.languages.java.JavaClientCodegen;
public class MyJavaCodegen extends JavaClientCodegen {
public MyJavaCodegen() {
super();
}
@Override
public void processOpts() {
super.processOpts();
templateDir = "src/main/resources/templates";
modelTemplateFiles.remove("model.mustache");
modelTemplateFiles.put("myDto.mustache", ".java");
}
}
Relevant JavaClientCodegen code. The templateVersion
code was added not too far back, but even before it was added we still had the situation where the templateDir
set in the super.processOpts()
call is always overwritten just after when also setting embeddedTemplateDir
:
@Override
public void processOpts() {
super.processOpts();
String templateVersion = getTemplateVersion();
if (StringUtils.isNotBlank(templateVersion)) {
embeddedTemplateDir = templateDir = String.format("%s/Java", templateVersion);
} else {
embeddedTemplateDir = templateDir = String.format("%s/Java", DEFAULT_TEMPLATE_VERSION);
}
But again, this is only half of the problem, even with MyJavaCodegen salvaging the templateDir
value, we run into a problem again.
Caused by: java.io.FileNotFoundException: /src/main/resources/templates/myDto.mustache
at com.github.jknack.handlebars.io.URLTemplateLoader.sourceAt(URLTemplateLoader.java:70)
at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:357)
at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:343)
at io.swagger.codegen.DefaultGenerator.getHandlebars(DefaultGenerator.java:1019)
at io.swagger.codegen.DefaultGenerator.processTemplateToFile(DefaultGenerator.java:742)
at io.swagger.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:394)
... 82 more
Okay, it looks like we should change
templateDir = "src/main/resources/templates";
to
templateDir = new File("src/main/resources/templates").getAbsolutePath();
This yields:
Caused by: java.io.FileNotFoundException: //Users/admin/checkout/project/src/main/resources/templates/myDto.mustache
at com.github.jknack.handlebars.io.URLTemplateLoader.sourceAt(URLTemplateLoader.java:70)
at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:357)
at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:343)
at io.swagger.codegen.DefaultGenerator.getHandlebars(DefaultGenerator.java:1019)
at io.swagger.codegen.DefaultGenerator.processTemplateToFile(DefaultGenerator.java:742)
at io.swagger.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:394)
... 82 more
That path starts //Users
... interesting. I debugged the program and set a breakpoint to change the path to not include the double forward slash, but it still failed with the same exception.
What happens if I don't try to use my custom mustache files, but still try to set the templateDir
? Well, if I leave the modelTemplateFiles
HashMap alone in MyJavaCodegen
I get the following exception:
Caused by: java.io.FileNotFoundException: /src/main/resources/templates/v2/Java/model.mustache
at com.github.jknack.handlebars.io.URLTemplateLoader.sourceAt(URLTemplateLoader.java:70)
at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:357)
at com.github.jknack.handlebars.Handlebars.compile(Handlebars.java:343)
at io.swagger.codegen.DefaultGenerator.getHandlebars(DefaultGenerator.java:1019)
at io.swagger.codegen.DefaultGenerator.processTemplateToFile(DefaultGenerator.java:742)
at io.swagger.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:394)
I wasn't able to make any sense of the the problem within sourceAt(URLTemplateLoader.java:70)
.
Its source is the following:
@Override
public TemplateSource sourceAt(final String uri) throws IOException {
notEmpty(uri, "The uri is required.");
String location = resolve(normalize(uri));
URL resource = getResource(location);
if (resource == null) {
throw new FileNotFoundException(location);
}
return new URLTemplateSource(location, resource);
}
And we are failing at getResource(location
which is the following:
@Override
protected URL getResource(final String location) {
return getClass().getResource(location);
}
I tried other location
strings for files elsewhere on my filesystem, and in the top level of my resources
directory, such as simply "swaggerdoc.json"
, but everything failed the same way, returning null
.
Steps to reproduce
./gradlew swagger
after the setup I've described above.
Related issues/PRs
Similar issue that happened July 2017
#4627
Suggest a fix/enhancement
Part of the solution is likely to remove the setting of templateDir
from JavaClientCodegen#processOpts
. If there is for some reason a need for templateDir
to either not be null or to be a valid directory, we can take care of that in the getter in DefaultCodegenConfig
, just like embeddedTemplateDir
is as follows:
public String embeddedTemplateDir() {
if (embeddedTemplateDir != null) {
return embeddedTemplateDir;
} else {
return templateDir;
}
}
templateDir
could be as follows:
public String templateDir() {
if (templateDir != null) {
return templateDir;
} else {
return embeddedTemplateDir;
}
}
Or this can be taken care of in JavaClientCodegen#processOpts
by making the beginning of the method the following:
@Override
public void processOpts() {
super.processOpts();
String templateVersion = getTemplateVersion();
if (StringUtils.isNotBlank(templateVersion)) {
embeddedTemplateDir = String.format("%s/Java", templateVersion);
} else {
embeddedTemplateDir = String.format("%s/Java", DEFAULT_TEMPLATE_VERSION);
}
if (templateDir == null || !new File(templateDir).exists()) {
templateDir = embeddedTemplateDir;
}