From ee1cbf6f4bacd28aa8c1615e16c25bf29ad6e4e8 Mon Sep 17 00:00:00 2001 From: Jim Schubert Date: Sun, 18 Oct 2020 12:05:56 -0400 Subject: [PATCH] [bug][maven] Fix custom windows classpaths in maven plugin (#7587) * [maven] Fallback to templates using classpath rather than OS-specific paths Previous checks would cause logic in Windows to return early, for built-in templates only. This reorganizes and simplifies the ordering behavior. * Match classpath check in WorkflowSettings with that in TemplateManager * [maven] Much needed unit/integration tests This follows similar approach used in PMD and other plugins managed by maven. Unit tests simply verify we can load configuration as expected into the Mojo. Integration tests execute actual sample projects bound to the current build's Maven plugin. This uses maven-invoker-plugin, which also allows for specifying the maven options in invoker.properties to execute the test. It also provides a verification framework using groovy files with the required naming convention of "verify.groovy". This allows us to quickly and easily check that certain files are outputted by generation, and we may also spotcheck file contents. templateResourcePath option is skipped on windows. I've tested back to version 3.3.3 and this doesn't seem to have worked consistently with how the property works on non-Windows. * Set groovy 3.0.5 for test harness * Print stacktrace on Maven error in Travis * [maven] Set groovy version in tests to supported in Java 11+ * Puts maven integration tests in separate profile called 'integration' --- .travis.yml | 4 +- .../codegen/config/WorkflowSettings.java | 13 +- .../examples/java-client.xml | 3 +- .../examples/templates/README.mustache | 21 + .../openapi-generator-maven-plugin/pom.xml | 56 +- .../invoker.properties | 2 + .../src/it/custom-template-resource/pom.xml | 57 ++ .../it/custom-template-resource/verify.groovy | 39 + .../src/it/custom-template/invoker.properties | 2 + .../src/it/custom-template/pom.xml | 53 ++ .../custom-template/templates/README.mustache | 21 + .../src/it/custom-template/verify.groovy | 38 + .../codegen/plugin/BaseTestCase.java | 31 + .../codegen/plugin/CodeGenMojoTest.java | 46 ++ .../plugin/stubs/CommonMavenProjectStub.java | 25 + .../codegen/plugin/stubs/StubUtility.java | 94 +++ .../unit/common-maven/common-maven.xml | 56 ++ .../resources/unit/common-maven/petstore.yaml | 736 ++++++++++++++++++ .../src/main/java/com/example/Example.java | 7 + .../GeneratorTemplateContentLocator.java | 20 +- .../codegen/DefaultGeneratorTest.java | 204 +++++ 21 files changed, 1518 insertions(+), 10 deletions(-) create mode 100644 modules/openapi-generator-maven-plugin/examples/templates/README.mustache create mode 100644 modules/openapi-generator-maven-plugin/src/it/custom-template-resource/invoker.properties create mode 100644 modules/openapi-generator-maven-plugin/src/it/custom-template-resource/pom.xml create mode 100644 modules/openapi-generator-maven-plugin/src/it/custom-template-resource/verify.groovy create mode 100644 modules/openapi-generator-maven-plugin/src/it/custom-template/invoker.properties create mode 100644 modules/openapi-generator-maven-plugin/src/it/custom-template/pom.xml create mode 100644 modules/openapi-generator-maven-plugin/src/it/custom-template/templates/README.mustache create mode 100644 modules/openapi-generator-maven-plugin/src/it/custom-template/verify.groovy create mode 100644 modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/BaseTestCase.java create mode 100644 modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/CodeGenMojoTest.java create mode 100644 modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/CommonMavenProjectStub.java create mode 100644 modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/StubUtility.java create mode 100644 modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/common-maven.xml create mode 100644 modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/petstore.yaml create mode 100644 modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/src/main/java/com/example/Example.java diff --git a/.travis.yml b/.travis.yml index ddf43cf484c3..359a3b0688b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -148,8 +148,8 @@ script: - /bin/bash ./bin/utils/detect_tab_in_java_class.sh # run integration tests defined in maven pom.xml # WARN: Travis will timeout after 10 minutes of no stdout/stderr activity, which is problematic with mvn --quiet. - - mvn --no-snapshot-updates --quiet --batch-mode --show-version clean install -Dorg.slf4j.simpleLogger.defaultLogLevel=error - - mvn --no-snapshot-updates --quiet --batch-mode --show-version verify -Psamples -Dorg.slf4j.simpleLogger.defaultLogLevel=error + - mvn -e --no-snapshot-updates --quiet --batch-mode --show-version clean install -Dorg.slf4j.simpleLogger.defaultLogLevel=error + - mvn -e --no-snapshot-updates --quiet --batch-mode --show-version verify -Psamples -Dorg.slf4j.simpleLogger.defaultLogLevel=error after_success: # push to maven repo - if [ $SONATYPE_USERNAME ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 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 68d8b2e9b292..8d24d3b0a50d 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 @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Pattern; /** * Represents those settings applied to a generation workflow. @@ -444,10 +445,20 @@ public Builder withTemplateDir(String templateDir) { uri = f.toURI(); this.templateDir = Paths.get(uri).toAbsolutePath().normalize().toString(); } else { - URL url = this.getClass().getClassLoader().getResource(templateDir); + String cpDir; + // HACK: this duplicates TemplateManager.getCPResourcePath a bit. We should probably move that function to core. + if (!"/".equals(File.separator)) { + // Windows users may pass path specific to OS, but classpath must be "/" separators + cpDir = templateDir.replaceAll(Pattern.quote(File.separator), "/"); + } else { + cpDir = templateDir; + } + + URL url = this.getClass().getClassLoader().getResource(cpDir); if (url != null) { try { uri = url.toURI(); + // we can freely set to templateDir here and allow templating to manage template lookups this.templateDir = templateDir; } catch (URISyntaxException e) { LOGGER.warn("The requested template was found on the classpath, but resulted in a syntax error."); diff --git a/modules/openapi-generator-maven-plugin/examples/java-client.xml b/modules/openapi-generator-maven-plugin/examples/java-client.xml index c966e5c63ee4..caa28302a7f5 100644 --- a/modules/openapi-generator-maven-plugin/examples/java-client.xml +++ b/modules/openapi-generator-maven-plugin/examples/java-client.xml @@ -53,8 +53,7 @@ java - + ${project.basedir}/templates diff --git a/modules/openapi-generator-maven-plugin/examples/templates/README.mustache b/modules/openapi-generator-maven-plugin/examples/templates/README.mustache new file mode 100644 index 000000000000..cee4f752e72a --- /dev/null +++ b/modules/openapi-generator-maven-plugin/examples/templates/README.mustache @@ -0,0 +1,21 @@ +# TEST TEST TEST + +# {{artifactId}} + +{{appName}} + +- API version: {{appVersion}} +{{^hideGenerationTimestamp}} + +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} + +{{#appDescriptionWithNewLines}}{{{appDescriptionWithNewLines}}}{{/appDescriptionWithNewLines}} + +{{#infoUrl}} + For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)* + +… etc. diff --git a/modules/openapi-generator-maven-plugin/pom.xml b/modules/openapi-generator-maven-plugin/pom.xml index d0eb7ad57921..af8a424ee7bb 100644 --- a/modules/openapi-generator-maven-plugin/pom.xml +++ b/modules/openapi-generator-maven-plugin/pom.xml @@ -16,6 +16,8 @@ UTF-8 **/src/main/java/org/openapitools/codegen/plugin/**/* + + 3.0.5 @@ -60,6 +62,24 @@ test + + org.apache.maven.shared + maven-verifier + 1.7.2 + test + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 3.3.0 + test + + + org.codehaus.plexus + plexus-utils + 3.3.0 + test + @@ -108,7 +128,41 @@ - + + integration + + + + org.apache.maven.plugins + maven-invoker-plugin + 3.2.1 + + verify + true + true + false + true + + + + org.codehaus.groovy + groovy + ${groovy.version} + runtime + + + + + integration-test + + run + + + + + + + static-analysis diff --git a/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/invoker.properties b/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/invoker.properties new file mode 100644 index 000000000000..1f2cc145e635 --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = -nsu generate-sources +invoker.name = Test Custom Templates via Resource diff --git a/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/pom.xml b/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/pom.xml new file mode 100644 index 000000000000..b8d127dadd73 --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/pom.xml @@ -0,0 +1,57 @@ + + + + + 4.0.0 + + org.openapitools.maven.its + custom-template-resource + 1.0-SNAPSHOT + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/modules/openapi-generator/src/test/resources/2_0/petstore.yaml + kotlin + ${basedir}/out + + bash + + true + + + + + default + generate-sources + + generate + + + + + + + diff --git a/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/verify.groovy b/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/verify.groovy new file mode 100644 index 000000000000..0af466d2635a --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/it/custom-template-resource/verify.groovy @@ -0,0 +1,39 @@ +/* + * Copyright 2020 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +File readme = new File(basedir, "out/README.md") + +assert readme.isFile() +if (File.separator == "/") { + // For whatever reason, resource path templates fail in this test in Windows + assert readme.text.contains("# OpenAPI Petstore Bash client") +} + +File gradle = new File(basedir, "out/build.gradle") +assert gradle.isFile() + +File api = new File(basedir, "out/src/main/kotlin/org/openapitools/client/apis/PetApi.kt") +assert api.isFile() + +File model = new File(basedir, "out/src/main/kotlin/org/openapitools/client/models/Pet.kt") +assert model.isFile() + +// note that in Java 11+, this anything matching this condition could fail due to +// Illegal reflective access by org.codehaus.groovy.reflection.CachedClass +// and cause tests to fail. This is more to document for engineers. +if (GroovySystem.version.tokenize('.')[0].toInteger() < 3) { + throw new IllegalStateException("Found:" + GroovySystem.version + ", need Groovy 3.x or higher for Java 11+, so we require it for all versions") +} diff --git a/modules/openapi-generator-maven-plugin/src/it/custom-template/invoker.properties b/modules/openapi-generator-maven-plugin/src/it/custom-template/invoker.properties new file mode 100644 index 000000000000..65393cde95a5 --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/it/custom-template/invoker.properties @@ -0,0 +1,2 @@ +invoker.goals = -nsu generate-sources +invoker.name = Test Custom Templates diff --git a/modules/openapi-generator-maven-plugin/src/it/custom-template/pom.xml b/modules/openapi-generator-maven-plugin/src/it/custom-template/pom.xml new file mode 100644 index 000000000000..52313b33d2fe --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/it/custom-template/pom.xml @@ -0,0 +1,53 @@ + + + + + 4.0.0 + + org.openapitools.maven.its + custom-template + 1.0-SNAPSHOT + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/modules/openapi-generator/src/test/resources/2_0/petstore.yaml + kotlin + ${basedir}/out + ${project.basedir}/templates + + true + + + + + remote + generate-sources + + generate + + + + + + + diff --git a/modules/openapi-generator-maven-plugin/src/it/custom-template/templates/README.mustache b/modules/openapi-generator-maven-plugin/src/it/custom-template/templates/README.mustache new file mode 100644 index 000000000000..cee4f752e72a --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/it/custom-template/templates/README.mustache @@ -0,0 +1,21 @@ +# TEST TEST TEST + +# {{artifactId}} + +{{appName}} + +- API version: {{appVersion}} +{{^hideGenerationTimestamp}} + +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} + +{{#appDescriptionWithNewLines}}{{{appDescriptionWithNewLines}}}{{/appDescriptionWithNewLines}} + +{{#infoUrl}} + For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + +*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)* + +… etc. diff --git a/modules/openapi-generator-maven-plugin/src/it/custom-template/verify.groovy b/modules/openapi-generator-maven-plugin/src/it/custom-template/verify.groovy new file mode 100644 index 000000000000..026989152580 --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/it/custom-template/verify.groovy @@ -0,0 +1,38 @@ +/* + * Copyright 2020 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +File readme = new File(basedir, "out/README.md") + +assert readme.isFile() +assert readme.text.contains("# TEST TEST TEST") +assert readme.text.contains("# kotlin-client") +assert readme.text.contains("OpenAPI Petstore") + +File gradle = new File(basedir, "out/build.gradle") +assert gradle.isFile() + +File api = new File(basedir, "out/src/main/kotlin/org/openapitools/client/apis/PetApi.kt") +assert api.isFile() + +File model = new File(basedir, "out/src/main/kotlin/org/openapitools/client/models/Pet.kt") +assert model.isFile() + +// note that in Java 11+, this anything matching this condition could fail due to +// Illegal reflective access by org.codehaus.groovy.reflection.CachedClass +// and cause tests to fail. This is more to document for engineers. +if (GroovySystem.version.tokenize('.')[0].toInteger() < 3) { + throw new IllegalStateException("Found:" + GroovySystem.version + ", need Groovy 3.x or higher for Java 11+, so we require it for all versions") +} diff --git a/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/BaseTestCase.java b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/BaseTestCase.java new file mode 100644 index 000000000000..ba69d021af3d --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/BaseTestCase.java @@ -0,0 +1,31 @@ +/* + * Copyright 2020 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.plugin; + +import org.apache.maven.plugin.testing.AbstractMojoTestCase; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * A base test class where we can add helper methods and whatnot + */ +public abstract class BaseTestCase extends AbstractMojoTestCase { + protected Path getUnitTestDir() { + return Paths.get(getBasedir(), "src", "test", "resources", "unit"); + } +} diff --git a/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/CodeGenMojoTest.java b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/CodeGenMojoTest.java new file mode 100644 index 000000000000..7b29b522409c --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/CodeGenMojoTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.plugin; + +import org.junit.Test; +import org.openapitools.codegen.plugin.stubs.StubUtility; + +import java.io.File; +import java.util.Map; + +public class CodeGenMojoTest extends BaseTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @SuppressWarnings("unchecked") + public void testCommonConfiguration() throws Exception { + File testPom = StubUtility.basedPath(getUnitTestDir().toFile(), "common-maven", "common-maven.xml").toFile(); + final CodeGenMojo mojo = (CodeGenMojo) lookupMojo("generate", testPom); + mojo.execute(); + assertEquals("java", getVariableValueFromObject(mojo, "generatorName")); + assertEquals("jersey2", getVariableValueFromObject(mojo, "library")); + assertEquals("remote.org.openapitools.client.api", getVariableValueFromObject(mojo, "apiPackage")); + assertEquals("remote.org.openapitools.client.model", getVariableValueFromObject(mojo, "modelPackage")); + assertEquals("remote.org.openapitools.client", getVariableValueFromObject(mojo, "invokerPackage")); + + Map configOptions = (Map) getVariableValueFromObject(mojo, "configOptions"); + assertNotNull(configOptions); + assertEquals("joda", configOptions.get("dateLibrary")); + } +} \ No newline at end of file diff --git a/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/CommonMavenProjectStub.java b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/CommonMavenProjectStub.java new file mode 100644 index 000000000000..9577061f0a87 --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/CommonMavenProjectStub.java @@ -0,0 +1,25 @@ +/* + * Copyright 2020 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.plugin.stubs; + +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; + +public class CommonMavenProjectStub extends MavenProjectStub { + public CommonMavenProjectStub() { + StubUtility.configureStub(this,"common-maven", "common-maven.xml"); + } +} diff --git a/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/StubUtility.java b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/StubUtility.java new file mode 100644 index 000000000000..c7ccd1a2a9ee --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/test/java/org/openapitools/codegen/plugin/stubs/StubUtility.java @@ -0,0 +1,94 @@ +/* + * Copyright 2020 OpenAPI-Generator Contributors (https://openapi-generator.tech) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools.codegen.plugin.stubs; + +import org.apache.maven.model.Build; +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugin.testing.stubs.DefaultArtifactHandlerStub; +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +public abstract class StubUtility { + private StubUtility() { + throw new UnsupportedOperationException("Utility class"); + } + + /** + * Configures a stub to conventional directories based on test name and pom file name. + *

+ * Taken largely from PMD plugin: + * https://github.com/apache/maven-pmd-plugin/blob/d766fdb0c93a6630ad7a788f260746b05c804a71/src/test/java/org/apache/maven/plugins/pmd/stubs/CustomConfigurationMavenProjectStub.java + * License: Apache 2.0 + * + * @param configure The stub to configure + * @param testName A name used to identify this stub, used in resource lookup + * @param pomFileName The filename and extension, e.g. "my-test-pom.xml" + */ + public static void configureStub(MavenProjectStub configure, String testName, String pomFileName) { + MavenXpp3Reader pomReader = new MavenXpp3Reader(); + Model model = null; + try { + File pomFile = basedPath( + configure.getBasedir(), "src", "test", "resources", "unit", testName, pomFileName + ).toFile(); + model = pomReader.read(new InputStreamReader(new FileInputStream(pomFile), StandardCharsets.UTF_8)); + configure.setModel(model); + } catch (Exception ignored) { + + } + + configure.setGroupId(model.getGroupId()); + configure.setArtifactId(model.getArtifactId()); + configure.setVersion(model.getVersion()); + configure.setName(model.getName()); + configure.setUrl(model.getUrl()); + configure.setPackaging(model.getPackaging()); + + Build build = new Build(); + build.setFinalName(model.getBuild().getFinalName()); + build.setDirectory(basedPath(configure.getBasedir(), "target", "test", "unit", testName, "target").toString()); + build.setSourceDirectory(basedPath(configure.getBasedir(), "src", "test", "resources", "unit", testName).toString()); + configure.setBuild(build); + + List compileSourceRoots = new ArrayList<>(); + compileSourceRoots.add(basedPath(configure.getBasedir(), "src", "test", "resources", "unit", testName, "src").toString()); + configure.setCompileSourceRoots(compileSourceRoots); + + ArtifactStub artifactStub = new ArtifactStub(); + artifactStub.setGroupId(configure.getGroupId()); + artifactStub.setArtifactId(configure.getArtifactId()); + artifactStub.setVersion(configure.getVersion()); + artifactStub.setArtifactHandler(new DefaultArtifactHandlerStub("jar")); + configure.setArtifact(artifactStub); + + configure.setFile(configure.getBasedir().toPath().resolve(pomFileName).toFile()); + } + + public static Path basedPath(File baseDir, String first, String... more) { + return baseDir.toPath().resolve(Paths.get(first, more)).normalize().toAbsolutePath(); + } +} diff --git a/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/common-maven.xml b/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/common-maven.xml new file mode 100644 index 000000000000..767caeab2ddf --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/common-maven.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + common.maven + common-maven + jar + 1.0.0-SNAPSHOT + OpenAPI Generator Configuration Test + https://openapi-generator.tech/ + + common-maven + + + org.openapitools + openapi-generator-maven-plugin + + + ${basedir}/src/test/resources/unit/common-maven/petstore.yaml + java + + joda + + jersey2 + ${basedir}/target/generated-sources/common-maven/remote-openapi + remote.org.openapitools.client.api + remote.org.openapitools.client.model + remote.org.openapitools.client + + + + default + generate-sources + + generate + + + + + + + \ No newline at end of file diff --git a/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/petstore.yaml b/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/petstore.yaml new file mode 100644 index 000000000000..f5e98eec38da --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/petstore.yaml @@ -0,0 +1,736 @@ +openapi: 3.0.0 +servers: + - url: 'http://petstore.swagger.io/v2' +info: + description: >- + This is a sample server Petstore server. For this sample, you can use the api key + `special-key` to test the authorization filters. + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +tags: + - name: pet + description: Everything about your Pets + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user +paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + description: '' + operationId: addPet + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + $ref: '#/components/requestBodies/Pet' + put: + tags: + - pet + summary: Update an existing pet + description: '' + operationId: updatePet + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + $ref: '#/components/requestBodies/Pet' + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + style: form + explode: false + schema: + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid status value + security: + - petstore_auth: + - 'read:pets' + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: >- + Multiple tags can be provided with comma separated strings. Use tag1, + tag2, tag3 for testing. + operationId: findPetsByTags + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + style: form + explode: false + schema: + type: array + items: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid tag value + security: + - petstore_auth: + - 'read:pets' + deprecated: true + '/pet/{petId}': + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Pet' + application/json: + schema: + $ref: '#/components/schemas/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + schema: + type: integer + format: int64 + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + name: + description: Updated name of the pet + type: string + status: + description: Updated status of the pet + type: string + delete: + tags: + - pet + summary: Deletes a pet + description: '' + operationId: deletePet + parameters: + - name: api_key + in: header + required: false + schema: + type: string + - name: petId + in: path + description: Pet id to delete + required: true + schema: + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + '/pet/{petId}/uploadImage': + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + additionalMetadata: + description: Additional data to pass to server + type: string + file: + description: file to upload + type: string + format: binary + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: '' + operationId: placeOrder + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid Order + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Order' + description: order placed for purchasing the pet + required: true + '/store/order/{orderId}': + get: + tags: + - store + summary: Find purchase order by ID + description: >- + For valid response try integer IDs with value <= 5 or > 10. Other values + will generated exceptions + operationId: getOrderById + parameters: + - name: orderId + in: path + description: ID of pet that needs to be fetched + required: true + schema: + type: integer + format: int64 + minimum: 1 + maximum: 5 + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/Order' + application/json: + schema: + $ref: '#/components/schemas/Order' + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: >- + For valid response try integer IDs with value < 1000. Anything above + 1000 or nonintegers will generate API errors + operationId: deleteOrder + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Created user object + required: true + /user/createWithArray: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithArrayInput + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + $ref: '#/components/requestBodies/UserArray' + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithListInput + responses: + default: + description: successful operation + security: + - api_key: [] + requestBody: + $ref: '#/components/requestBodies/UserArray' + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + parameters: + - name: username + in: query + description: The user name for login + required: true + schema: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + - name: password + in: query + description: The password for login in clear text + required: true + schema: + type: string + responses: + '200': + description: successful operation + headers: + Set-Cookie: + description: >- + Cookie authentication key for use with the `api_key` + apiKey authentication. + schema: + type: string + example: AUTH_KEY=abcde12345; Path=/; HttpOnly + X-Rate-Limit: + description: calls per hour allowed by the user + schema: + type: integer + format: int32 + X-Expires-After: + description: date in UTC when toekn expires + schema: + type: string + format: date-time + content: + application/xml: + schema: + type: string + application/json: + schema: + type: string + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + responses: + default: + description: successful operation + security: + - api_key: [] + '/user/{username}': + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + parameters: + - name: username + in: path + description: The name that needs to be fetched. Use user1 for testing. + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/xml: + schema: + $ref: '#/components/schemas/User' + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid user supplied + '404': + description: User not found + security: + - api_key: [] + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + description: Updated user object + required: true + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + schema: + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found + security: + - api_key: [] +externalDocs: + description: Find out more about Swagger + url: 'http://swagger.io' +components: + requestBodies: + UserArray: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + description: List of user object + required: true + Pet: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + application/xml: + schema: + $ref: '#/components/schemas/Pet' + description: Pet object that needs to be added to the store + required: true + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + api_key: + type: apiKey + name: api_key + in: header + schemas: + Order: + title: Pet Order + description: An order for a pets from the pet store + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + xml: + name: Order + Category: + title: Pet category + description: A category for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$' + xml: + name: Category + User: + title: a User + description: A User who is purchasing from the pet store + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + format: int32 + description: User Status + xml: + name: User + Tag: + title: Pet Tag + description: A tag for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + title: a Pet + description: A pet for sale in the pet store + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/components/schemas/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + $ref: '#/components/schemas/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + title: An uploaded response + description: Describes the result of uploading an image resource + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string diff --git a/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/src/main/java/com/example/Example.java b/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/src/main/java/com/example/Example.java new file mode 100644 index 000000000000..8447f00216de --- /dev/null +++ b/modules/openapi-generator-maven-plugin/src/test/resources/unit/common-maven/src/main/java/com/example/Example.java @@ -0,0 +1,7 @@ +package com.example; + +public class Example { + public static void main(String[] args) { + System.out.println("You did it!"); + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java index 14b71d865385..861b9bc3a3d3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/templating/GeneratorTemplateContentLocator.java @@ -6,6 +6,7 @@ import org.openapitools.codegen.api.TemplatePathLocator; import java.io.File; +import java.nio.file.Paths; /** * Locates templates according to {@link CodegenConfig} settings. @@ -23,7 +24,7 @@ public GeneratorTemplateContentLocator(CodegenConfig codegenConfig) { } private String buildLibraryFilePath(String dir, String library, String file) { - return dir + File.separator + "libraries" + File.separator + library + File.separator + file; + return Paths.get(dir, "libraries", library, file).normalize().toString(); } /** @@ -34,6 +35,10 @@ private String buildLibraryFilePath(String dir, String library, String file) { * @return true if file is an embedded resource, false if it does not exist */ public boolean embeddedTemplateExists(String name) { + return classpathTemplateExists(name); + } + + private boolean classpathTemplateExists(String name) { return this.getClass().getClassLoader().getResource(TemplateManager.getCPResourcePath(name)) != null; } @@ -60,20 +65,26 @@ public String getFullTemplatePath(String relativeTemplateFile) { if (StringUtils.isNotEmpty(library)) { //look for the file in the library subfolder of the supplied template final String libTemplateFile = buildLibraryFilePath(config.templateDir(), library, relativeTemplateFile); - if (new File(libTemplateFile).exists() || this.getClass().getClassLoader().getResource(libTemplateFile) != null) { + // looks for user-defined file or classpath + // supports template dir which refers to local file system or custom path in classpath as defined by templateDir + if (new File(libTemplateFile).exists() || classpathTemplateExists(libTemplateFile)) { return libTemplateFile; } } - //check the supplied template main folder for the file + // check the supplied template main folder for the file + // File.separator is necessary here as the file load is OS-specific final String template = config.templateDir() + File.separator + relativeTemplateFile; - if (new File(template).exists() || this.getClass().getClassLoader().getResource(template) != null) { + // looks for user-defined file or classpath + // supports template dir which refers to local file system or custom path in classpath as defined by templateDir + if (new File(template).exists() || classpathTemplateExists(template)) { return template; } //try the embedded template library folder next if (StringUtils.isNotEmpty(library)) { final String embeddedLibTemplateFile = buildLibraryFilePath(config.embeddedTemplateDir(), library, relativeTemplateFile); + // *only* looks for those files in classpath as defined by embeddedTemplateDir if (embeddedTemplateExists(embeddedLibTemplateFile)) { // Fall back to the template file embedded/packaged in the JAR file library folder... return embeddedLibTemplateFile; @@ -82,6 +93,7 @@ public String getFullTemplatePath(String relativeTemplateFile) { // Fall back to the template file for generator root directory embedded/packaged in the JAR file... String loc = config.embeddedTemplateDir() + File.separator + relativeTemplateFile; + // *only* looks for those files in classpath as defined by embeddedTemplateDir if (embeddedTemplateExists(loc)) { return loc; } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java index 1a1806d6f835..1bf5dc702579 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultGeneratorTest.java @@ -433,4 +433,208 @@ public void testRefModelValidationProperties() { Assert.assertEquals(((Schema) codegenResponse.schema).getPattern(), expectedPattern); Assert.assertEquals(codegenResponse.pattern, escapedPattern); } + + @Test + public void testBuiltinLibraryTemplates() throws IOException { + Path target = Files.createTempDirectory("test"); + File output = target.toFile(); + try { + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("kotlin") + .setLibrary("jvm-okhttp4") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setSkipOverwrite(false) + .setOutputDir(target.toAbsolutePath().toString()); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(false); + + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_TESTS, "false"); + + List files = generator.opts(clientOptInput).generate(); + + Assert.assertEquals(files.size(), 20); + + // Generator should report a library templated file as a generated file + TestUtils.ensureContainsFile(files, output, "src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt"); + + // Generated file should exist on the filesystem after generation + File generatedFile = new File(output, "src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt"); + Assert.assertTrue(generatedFile.exists()); + + // Generated file should contain some expected text + TestUtils.assertFileContains(generatedFile.toPath(), "package org.openapitools.client.infrastructure", + "open class ClientException", + "open class ServerException"); + } finally { + output.delete(); + } + } + + @Test + public void testBuiltinNonLibraryTemplates() throws IOException { + Path target = Files.createTempDirectory("test"); + File output = target.toFile(); + try { + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("kotlin") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setSkipOverwrite(false) + .setOutputDir(target.toAbsolutePath().toString()); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(false); + + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_TESTS, "false"); + + List files = generator.opts(clientOptInput).generate(); + + Assert.assertEquals(files.size(), 20); + + // Generator should report README.md as a generated file + TestUtils.ensureContainsFile(files, output, "README.md"); + + // Generated file should exist on the filesystem after generation + File readme = new File(output, "README.md"); + Assert.assertTrue(readme.exists()); + + // README.md should contain some expected text + TestUtils.assertFileContains(readme.toPath(), "# org.openapitools.client - Kotlin client library for OpenAPI Petstore", + "## Requires", + "## Build", + "## Features/Implementation Notes"); + } finally { + output.delete(); + } + } + + @Test + public void testCustomLibraryTemplates() throws IOException { + Path target = Files.createTempDirectory("test"); + Path templates = Files.createTempDirectory("templates"); + File output = target.toFile(); + try { + // Create custom template + File customTemplate = new File(templates.toFile(), "libraries/jvm-okhttp/infrastructure/Errors.kt.mustache"); + new File(customTemplate.getParent()).mkdirs(); + StringBuilder sb = new StringBuilder(); + sb.append("// {{someKey}}").append("\n"); + sb.append("@file:Suppress(\"unused\")").append("\n"); + sb.append("package org.openapitools.client.infrastructure").append("\n"); + sb.append("import java.lang.RuntimeException").append("\n"); + sb.append("open class CustomException(").append("\n"); + sb.append(" message: kotlin.String? = null, val statusCode: Int = -1, val response: Response? = null) : RuntimeException(message) {").append("\n"); + sb.append(" companion object {").append("\n"); + sb.append(" private const val serialVersionUID: Long = 789L").append("\n"); + sb.append(" }").append("\n"); + sb.append("}").append("\n"); + Files.write(customTemplate.toPath(), + sb.toString().getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("kotlin") + .addAdditionalProperty("someKey", "testCustomLibraryTemplates") + .setTemplateDir(templates.toAbsolutePath().toString()) + .setLibrary("jvm-okhttp4") + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setSkipOverwrite(false) + .setOutputDir(target.toAbsolutePath().toString()); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(false); + + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_TESTS, "false"); + + List files = generator.opts(clientOptInput).generate(); + + Assert.assertEquals(files.size(), 20); + + // Generator should report a library templated file as a generated file + TestUtils.ensureContainsFile(files, output, "src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt"); + + // Generated file should exist on the filesystem after generation + File readme = new File(output, "src/main/kotlin/org/openapitools/client/infrastructure/Errors.kt"); + Assert.assertTrue(readme.exists()); + + // Generated file should contain our custom templated text + TestUtils.assertFileContains(readme.toPath(), "// testCustomLibraryTemplates", + "package org.openapitools.client.infrastructure", + "open class CustomException(", + "private const val serialVersionUID: Long = 789L"); + } finally { + output.delete(); + templates.toFile().delete(); + } + } + + @Test + public void testCustomNonLibraryTemplates() throws IOException { + Path target = Files.createTempDirectory("test"); + Path templates = Files.createTempDirectory("templates"); + File output = target.toFile(); + try { + // Create custom template + File customTemplate = new File(templates.toFile(), "README.mustache"); + new File(customTemplate.getParent()).mkdirs(); + Files.write(customTemplate.toPath(), + "# {{someKey}}".getBytes(StandardCharsets.UTF_8), + StandardOpenOption.CREATE); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("kotlin") + .addAdditionalProperty("someKey", "testCustomNonLibraryTemplates") + .setTemplateDir(templates.toAbsolutePath().toString()) + .setInputSpec("src/test/resources/3_0/petstore.yaml") + .setSkipOverwrite(false) + .setOutputDir(target.toAbsolutePath().toString()); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(false); + + generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_DOCS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); + generator.setGeneratorPropertyDefault(CodegenConstants.API_TESTS, "false"); + + List files = generator.opts(clientOptInput).generate(); + + Assert.assertEquals(files.size(), 20); + + // Generator should report README.md as a generated file + TestUtils.ensureContainsFile(files, output, "README.md"); + + // Generated file should exist on the filesystem after generation + File readme = new File(output, "README.md"); + Assert.assertTrue(readme.exists()); + + // README.md should contain our custom templated text + TestUtils.assertFileContains(readme.toPath(), "# testCustomNonLibraryTemplates"); + } finally { + output.delete(); + templates.toFile().delete(); + } + } } +