[maven-plugin] Generate hash from actual resolved spec rather than inputSpec file (#18849)

* Ensure temp directories are deleted after test execution

* Implement test that external $ref changes are reflected in checksum

* Generate hash checksum from actual resolved spec instead of inputSpec file

Otherwise regeneration will not happen when skipIfSpecIsUnchanged is enabled,
although formally the spec content has changed.

Fixes #4512 and #16489

* Use try-with-resources to ensure stream is closed properly on exit

* Fix deprecation warning on SimpleLocalRepositoryManagerFactory no-arg constructor

* Apply minor code cleanup

- use fluent setters where possible
- remove undocumented @throws from JavaDoc
- use List.of() instead of Arrays.asList() for single-element-collection
  (more memory efficient)
- fix some grammar issues in comments and JavaDoc

* Use non-blocking java.nio API for file existence checks
This commit is contained in:
Philzen 2024-06-05 11:19:22 +02:00 committed by GitHub
parent 446e168f43
commit 880df7a7a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 337 additions and 176 deletions

View File

@ -17,40 +17,14 @@
package org.openapitools.codegen.plugin; package org.openapitools.codegen.plugin;
import static org.apache.commons.lang3.StringUtils.isNotEmpty; import com.google.common.hash.Hashing;
import static org.openapitools.codegen.config.CodegenConfiguratorUtils.*; import com.google.common.io.Files;
import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Yaml; import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.parser.OpenAPIResolver; import io.swagger.v3.parser.OpenAPIResolver;
import io.swagger.v3.parser.OpenAPIV3Parser; import io.swagger.v3.parser.OpenAPIV3Parser;
import io.swagger.v3.parser.core.models.AuthorizationValue;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import com.google.common.io.ByteSource;
import com.google.common.io.CharSource;
import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.util.ClasspathHelper;
import lombok.Setter; import lombok.Setter;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
@ -59,30 +33,28 @@ import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.*;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper; import org.apache.maven.project.MavenProjectHelper;
import org.openapitools.codegen.CliOption; import org.openapitools.codegen.*;
import org.openapitools.codegen.ClientOptInput;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.auth.AuthParser;
import org.openapitools.codegen.config.CodegenConfigurator; import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.config.GlobalSettings;
import org.openapitools.codegen.config.MergedSpecBuilder; import org.openapitools.codegen.config.MergedSpecBuilder;
import org.sonatype.plexus.build.incremental.BuildContext;
import org.sonatype.plexus.build.incremental.DefaultBuildContext;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.sonatype.plexus.build.incremental.BuildContext;
import org.sonatype.plexus.build.incremental.DefaultBuildContext;
import com.google.common.hash.Hashing; import java.io.File;
import com.google.common.io.Files; import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.openapitools.codegen.config.CodegenConfiguratorUtils.*;
/** /**
* Goal which generates client/server code from a OpenAPI json/yaml definition. * Goal which generates client/server code from a OpenAPI json/yaml definition.
@ -609,19 +581,11 @@ public class CodeGenMojo extends AbstractMojo {
} }
if (Boolean.TRUE.equals(skipIfSpecIsUnchanged)) { if (Boolean.TRUE.equals(skipIfSpecIsUnchanged)) {
File storedInputSpecHashFile = getHashFile(inputSpecFile); final File storedInputSpecHashFile = getHashFile(inputSpecFile);
if (storedInputSpecHashFile.exists()) { if (storedInputSpecHashFile.exists()) {
String inputSpecHash = null;
try {
inputSpecHash = calculateInputSpecHash(inputSpecFile);
} catch (IOException ex) {
ex.printStackTrace();
}
@SuppressWarnings("UnstableApiUsage")
String storedInputSpecHash = Files.asCharSource(storedInputSpecHashFile, StandardCharsets.UTF_8).read(); String storedInputSpecHash = Files.asCharSource(storedInputSpecHashFile, StandardCharsets.UTF_8).read();
if (storedInputSpecHash.equals(inputSpecHash)) { if (storedInputSpecHash.equals(calculateInputSpecHash(inputSpec))) {
getLog().info( getLog().info("Code generation is skipped because input was unchanged");
"Code generation is skipped because input was unchanged");
return; return;
} }
} }
@ -1010,8 +974,6 @@ public class CodeGenMojo extends AbstractMojo {
// Store a checksum of the input spec // Store a checksum of the input spec
File storedInputSpecHashFile = getHashFile(inputSpecFile); File storedInputSpecHashFile = getHashFile(inputSpecFile);
String inputSpecHash = calculateInputSpecHash(inputSpecFile);
if (storedInputSpecHashFile.getParent() != null && !new File(storedInputSpecHashFile.getParent()).exists()) { if (storedInputSpecHashFile.getParent() != null && !new File(storedInputSpecHashFile.getParent()).exists()) {
File parent = new File(storedInputSpecHashFile.getParent()); File parent = new File(storedInputSpecHashFile.getParent());
if (!parent.mkdirs()) { if (!parent.mkdirs()) {
@ -1019,8 +981,8 @@ public class CodeGenMojo extends AbstractMojo {
" to store the checksum of the input spec."); " to store the checksum of the input spec.");
} }
} }
Files.asCharSink(storedInputSpecHashFile, StandardCharsets.UTF_8).write(inputSpecHash);
Files.asCharSink(storedInputSpecHashFile, StandardCharsets.UTF_8).write(calculateInputSpecHash(inputSpec));
} catch (Exception e) { } catch (Exception e) {
// Maven logs exceptions thrown by plugins only if invoked with -e // Maven logs exceptions thrown by plugins only if invoked with -e
// I find it annoying to jump through hoops to get basic diagnostic information, // I find it annoying to jump through hoops to get basic diagnostic information,
@ -1035,45 +997,21 @@ public class CodeGenMojo extends AbstractMojo {
} }
/** /**
* Calculate openapi specification file hash. If specification is hosted on remote resource it is downloaded first * Calculate an SHA256 hash for the openapi specification.
* If the specification is hosted on a remote resource it is downloaded first.
* *
* @param inputSpecFile - Openapi specification input file to calculate its hash. * @param inputSpec - Openapi specification input file. Can denote a URL or file path.
* Does not take into account if input spec is hosted on remote resource * @return openapi specification hash
* @return openapi specification file hash
* @throws IOException
*/ */
private String calculateInputSpecHash(File inputSpecFile) throws IOException { private String calculateInputSpecHash(String inputSpec) {
final ParseOptions parseOptions = new ParseOptions();
URL inputSpecRemoteUrl = inputSpecRemoteUrl(); parseOptions.setResolve(true);
File inputSpecTempFile = inputSpecFile; final URL remoteUrl = inputSpecRemoteUrl();
return Hashing.sha256().hashBytes(
if (inputSpecRemoteUrl != null) { new OpenAPIParser().readLocation(remoteUrl == null ? inputSpec : remoteUrl.toString(), null, parseOptions)
inputSpecTempFile = java.nio.file.Files.createTempFile("openapi-spec", ".tmp").toFile(); .getOpenAPI().toString().getBytes(StandardCharsets.UTF_8)
).toString();
URLConnection conn = inputSpecRemoteUrl.openConnection();
if (isNotEmpty(auth)) {
List<AuthorizationValue> authList = AuthParser.parse(auth);
for (AuthorizationValue a : authList) {
conn.setRequestProperty(a.getKeyName(), a.getValue());
}
}
try (ReadableByteChannel readableByteChannel = Channels.newChannel(conn.getInputStream())) {
FileChannel fileChannel;
try (FileOutputStream fileOutputStream = new FileOutputStream(inputSpecTempFile)) {
fileChannel = fileOutputStream.getChannel();
fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
}
}
}
ByteSource inputSpecByteSource =
inputSpecTempFile.exists()
? Files.asByteSource(inputSpecTempFile)
: CharSource.wrap(ClasspathHelper.loadFileFromClasspath(inputSpecTempFile.toString().replaceAll("\\\\","/")))
.asByteSource(StandardCharsets.UTF_8);
return inputSpecByteSource.hash(Hashing.sha256()).toString();
} }
/** /**

View File

@ -16,17 +16,6 @@
package org.openapitools.codegen.plugin; package org.openapitools.codegen.plugin;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.DefaultMavenExecutionRequest; import org.apache.maven.execution.DefaultMavenExecutionRequest;
@ -39,9 +28,24 @@ import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils; import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.internal.impl.DefaultLocalPathComposer;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory; import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
import org.eclipse.aether.repository.LocalRepository; import org.eclipse.aether.repository.LocalRepository;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class CodeGenMojoTest extends BaseTestCase { public class CodeGenMojoTest extends BaseTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
@ -62,8 +66,7 @@ public class CodeGenMojoTest extends BaseTestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void testCommonConfiguration(String profile) throws Exception { private void testCommonConfiguration(String profile) throws Exception {
File folder = Files.createTempDirectory("test").toFile(); CodeGenMojo mojo = loadMojo(newTempFolder(), "src/test/resources/default", profile);
CodeGenMojo mojo = loadMojo(folder, "src/test/resources/default", profile);
mojo.execute(); mojo.execute();
assertEquals("java", getVariableValueFromObject(mojo, "generatorName")); assertEquals("java", getVariableValueFromObject(mojo, "generatorName"));
assertEquals("jersey2", getVariableValueFromObject(mojo, "library")); assertEquals("jersey2", getVariableValueFromObject(mojo, "library"));
@ -79,43 +82,42 @@ public class CodeGenMojoTest extends BaseTestCase {
public void testHashGenerationFileContainsExecutionId() throws Exception { public void testHashGenerationFileContainsExecutionId() throws Exception {
// GIVEN // GIVEN
Path folder = Files.createTempDirectory("test"); final Path tempDir = newTempFolder();
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId"); CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/default", "file", "executionId");
// WHEN // WHEN
mojo.execute(); mojo.execute();
// THEN // THEN
Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator"); assertTrue(Files.exists(tempDir.resolve(
assertTrue(hashFolder.resolve("petstore.yaml-executionId.sha256").toFile().exists()); "target/generated-sources/common-maven/remote-openapi/.openapi-generator/petstore.yaml-executionId.sha256"
)));
} }
/** /**
* For a Pom file which refers to a input file which will be on the classpath, as opposed to a file path, * For a Pom file which refers to an input file which will be on the classpath, as opposed to a file path,
* test that the spec is not regenerated when the hash has not changed. * test that the spec is not regenerated when the hash has not changed.
*
* @throws Exception
*/ */
public void testSkipRegenerationForClasspathSpecFileNoChange() throws Exception { public void testSkipRegenerationForClasspathSpecFileNoChange() throws Exception {
//GIVEN //GIVEN
/* Setup the mojo */ /* Set up the mojo */
final Path folder = Files.createTempDirectory("test-classpath"); final Path tempDir = newTempFolder();
final CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/classpath", null, "executionId"); final CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/classpath", null, "executionId");
/* Perform an initial generation */ /* Perform an initial generation */
mojo.execute(); mojo.execute();
/* Check the hash file was created */ /* Check the hash file was created */
final Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator"); final Path generatedDir = tempDir.resolve("target/generated-sources/common-maven/remote-openapi");
assertTrue(hashFolder.resolve("petstore-on-classpath.yaml-executionId.sha256").toFile().exists()); assertTrue(Files.exists(
generatedDir.resolve(".openapi-generator/petstore-on-classpath.yaml-executionId.sha256")
));
/* Remove the generated source */ /* Remove the generated source */
Files.walk(folder.resolve("target/generated-sources/common-maven/remote-openapi/src")) try (Stream<Path> files = Files.walk(generatedDir.resolve("src"))) {
.sorted(Comparator.reverseOrder()) //noinspection ResultOfMethodCallIgnored
.map(Path::toFile) files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
.forEach(File::delete); }
// WHEN // WHEN
/* Execute the mojo again */ /* Execute the mojo again */
@ -123,41 +125,37 @@ public class CodeGenMojoTest extends BaseTestCase {
// THEN // THEN
/* Verify that the source directory has not been repopulated. If it has then we generated code again */ /* Verify that the source directory has not been repopulated. If it has then we generated code again */
assertFalse("src directory should not have been regenerated", assertFalse("src directory should not have been regenerated", Files.exists(generatedDir.resolve("src")));
folder.resolve("target/generated-sources/common-maven/remote-openapi/src").toFile().exists());
} }
/** /**
* For a Pom file which refers to a input file which will be on the classpath, as opposed to a file path, * For a Pom file which refers to an input file which will be on the classpath, as opposed to a file path,
* test that the generated source is regenerated when the hash has changed. * test that the generated source is regenerated when the hash has changed.
*
* @throws Exception
*/ */
public void testSkipRegenerationForClasspathSpecFileWithChange() throws Exception { public void testSkipRegenerationForClasspathSpecFileWithChange() throws Exception {
//GIVEN //GIVEN
/* Setup the mojo */ /* Set up the mojo */
final Path folder = Files.createTempDirectory("test-classpath"); final Path tempDir = newTempFolder();
final CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/classpath", null, "executionId"); final CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/classpath", null, "executionId");
/* Perform an initial generation */ /* Perform an initial generation */
mojo.execute(); mojo.execute();
/* Check the hash file was created, proving a generation occurred */ /* Check the hash file was created, proving a generation occurred */
final Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator"); final Path generatedDir = tempDir.resolve("target/generated-sources/common-maven/remote-openapi");
assertTrue(hashFolder.resolve("petstore-on-classpath.yaml-executionId.sha256").toFile().exists()); assertTrue(Files.exists(generatedDir.resolve(".openapi-generator/petstore-on-classpath.yaml-executionId.sha256")));
/* Update the hash contents to be a different value, simulating a spec change */ /* Update the hash contents to be a different value, simulating a spec change */
Files.write( Files.write(
hashFolder.resolve("petstore-on-classpath.yaml-executionId.sha256"), generatedDir.resolve(".openapi-generator/petstore-on-classpath.yaml-executionId.sha256"),
Arrays.asList("bd1bf4a953c858f9d47b67ed6029daacf1707e5cbd3d2e4b01383ba30363366f")); List.of("bd1bf4a953c858f9d47b67ed6029daacf1707e5cbd3d2e4b01383ba30363366f")
);
/* Remove the generated source */ /* Remove the generated source */
Files.walk(folder.resolve("target/generated-sources/common-maven/remote-openapi/src")) try(Stream<Path> files = Files.walk(generatedDir.resolve("src"))) {
.sorted(Comparator.reverseOrder()) //noinspection ResultOfMethodCallIgnored
.map(Path::toFile) files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
.forEach(File::delete); }
// WHEN // WHEN
@ -165,29 +163,27 @@ public class CodeGenMojoTest extends BaseTestCase {
mojo.execute(); mojo.execute();
// THEN // THEN
/* Verify that the source directory has not been repopulated. If it has then we generated code again */ /* Verify that the source directory has been repopulated. */
assertTrue("src directory should have been regenerated", assertTrue("src directory should have been regenerated", Files.exists(generatedDir.resolve("src")));
folder.resolve("target/generated-sources/common-maven/remote-openapi/src").toFile().exists());
} }
public void testCollapsedSpecProduced() throws Exception { public void testCollapsedSpecProduced() throws Exception {
// GIVEN // GIVEN
Path folder = Files.createTempDirectory("test"); final Path tempDir = newTempFolder();
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId"); CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/default", "file", "executionId");
// WHEN // WHEN
mojo.execute(); mojo.execute();
// THEN // THEN
File collapseSpecFile = folder.resolve("target/generated-sources/common-maven/remote-openapi/petstore-full-spec.yaml").toFile(); assertTrue(Files.exists(
assertTrue(collapseSpecFile.exists()); tempDir.resolve("target/generated-sources/common-maven/remote-openapi/petstore-full-spec.yaml")
));
} }
public void testCollapsedSpecAddedToArtifacts() throws Exception { public void testCollapsedSpecAddedToArtifacts() throws Exception {
// GIVEN // GIVEN
Path folder = Files.createTempDirectory("test"); CodeGenMojo mojo = loadMojo(newTempFolder(), "src/test/resources/default", "file", "executionId");
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId");
// WHEN // WHEN
mojo.execute(); mojo.execute();
@ -201,8 +197,7 @@ public class CodeGenMojoTest extends BaseTestCase {
public void testAnyInputSpecMustBeProvided() throws Exception { public void testAnyInputSpecMustBeProvided() throws Exception {
// GIVEN // GIVEN
Path folder = Files.createTempDirectory("test"); CodeGenMojo mojo = loadMojo(newTempFolder(), "src/test/resources/default", "file", "executionId");
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId");
mojo.inputSpec = null; mojo.inputSpec = null;
mojo.inputSpecRootDirectory = null; mojo.inputSpecRootDirectory = null;
@ -215,8 +210,8 @@ public class CodeGenMojoTest extends BaseTestCase {
public void testInputSpecRootDirectoryDoesNotRequireInputSpec() throws Exception { public void testInputSpecRootDirectoryDoesNotRequireInputSpec() throws Exception {
// GIVEN // GIVEN
Path folder = Files.createTempDirectory("test"); final Path tempDir = newTempFolder();
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId"); CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/default", "file", "executionId");
mojo.inputSpec = null; mojo.inputSpec = null;
mojo.inputSpecRootDirectory = "src/test/resources/default"; mojo.inputSpecRootDirectory = "src/test/resources/default";
@ -225,17 +220,43 @@ public class CodeGenMojoTest extends BaseTestCase {
// THEN // THEN
/* Check the hash file was created */ /* Check the hash file was created */
final Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator"); final Path hashFolder = tempDir.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator");
assertTrue(hashFolder.resolve("_merged_spec.yaml-executionId.sha256").toFile().exists()); assertTrue(Files.exists(hashFolder.resolve("_merged_spec.yaml-executionId.sha256")));
} }
protected CodeGenMojo loadMojo(File temporaryFolder, String projectRoot, String profile) throws Exception { /**
* Regression test for <a href="https://github.com/OpenAPITools/openapi-generator/issues/16489">#16489</a>
*/
public void test_skipIfSpecIsUnchanged_recognizesUpdatesInExternalReferencedFile() throws Exception {
//GIVEN
final Path tempDir = newTempFolder();
final Path generatedDir = tempDir.resolve("target/generated-sources/issue-16489");
final Path hashFile = generatedDir.resolve(".openapi-generator/petstore.yaml-default.sha256");
final CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/issue-16489", null);
mojo.execute(); // Perform an initial generation
var currentHash = Files.readString(hashFile); // read hash
FileUtils.deleteDirectory(generatedDir.resolve("src").toFile()); // Remove the generated source
Files.writeString( // change schema definition in external file
tempDir.resolve("schemas/Pet.yaml"),"\n wrapped: true", StandardOpenOption.APPEND
);
// WHEN
mojo.execute(); // Execute the mojo again
// THEN
assertNotEquals(
Files.readString(hashFile), currentHash, "Checksum should not be the same after external file change"
);
assertTrue("Src directory should have been regenerated", Files.exists(generatedDir.resolve("src")));
}
protected CodeGenMojo loadMojo(Path temporaryFolder, String projectRoot, String profile) throws Exception {
return loadMojo(temporaryFolder, projectRoot, profile, "default"); return loadMojo(temporaryFolder, projectRoot, profile, "default");
} }
protected CodeGenMojo loadMojo(File temporaryFolder, String projectRoot, String profile, String executionId) throws Exception { protected CodeGenMojo loadMojo(Path temporaryFolder, String projectRoot, String profile, String executionId) throws Exception {
File file = new File(projectRoot); FileUtils.copyDirectory(new File(projectRoot), temporaryFolder.toFile());
FileUtils.copyDirectory(file, temporaryFolder);
MavenProject project = readMavenProject(temporaryFolder, profile); MavenProject project = readMavenProject(temporaryFolder, profile);
MavenSession session = newMavenSession(project); MavenSession session = newMavenSession(project);
MojoExecution execution = newMojoExecution("generate"); MojoExecution execution = newMojoExecution("generate");
@ -249,22 +270,29 @@ public class CodeGenMojoTest extends BaseTestCase {
return executionWithId; return executionWithId;
} }
protected MavenProject readMavenProject(File basedir, String profile) protected MavenProject readMavenProject(Path basedir, String profile) throws Exception {
throws Exception { LocalRepository localRepo = new LocalRepository(basedir.resolve("local-repo").toFile());
File pom = new File(basedir, "pom.xml");
LocalRepository localRepo = new LocalRepository(new File(basedir, "local-repo"));
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setLocalRepositoryManager(new SimpleLocalRepositoryManagerFactory().newInstance(session, localRepo)); session.setLocalRepositoryManager(
MavenExecutionRequest request = new DefaultMavenExecutionRequest(); new SimpleLocalRepositoryManagerFactory(new DefaultLocalPathComposer()).newInstance(session, localRepo)
request.setBaseDirectory(basedir); );
MavenExecutionRequest request = new DefaultMavenExecutionRequest().setBaseDirectory(basedir.toFile());
if (profile != null) { if (profile != null) {
request.addActiveProfile(profile); request.addActiveProfile(profile);
} }
ProjectBuildingRequest configuration = request.getProjectBuildingRequest(); ProjectBuildingRequest configuration = request.getProjectBuildingRequest()
configuration.setRepositorySession(session); .setRepositorySession(session)
configuration.setResolveDependencies(true); .setResolveDependencies(true);
MavenProject project = lookup(ProjectBuilder.class).build(pom, configuration).getProject(); MavenProject project = lookup(ProjectBuilder.class)
.build(basedir.resolve("pom.xml").toFile(), configuration)
.getProject();
assertNotNull(project); assertNotNull(project);
return project; return project;
} }
static private Path newTempFolder() throws IOException {
final Path tempDir = Files.createTempDirectory("test");
tempDir.toFile().deleteOnExit();
return tempDir;
}
} }

View File

@ -0,0 +1,105 @@
openapi: 3.0.0
servers:
- url: 'http://petstore.swagger.io/v2'
info:
description: Sample file with just two endpoints and one schema (defined in an external file $ref)
version: 1.0.0
title: OpenAPI Petstore
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'
'/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: []
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'
components:
requestBodies:
Pet:
content:
application/json:
schema:
$ref: 'schemas/Pet.yaml'
application/xml:
schema:
$ref: 'schemas/Pet.yaml'
description: Pet object that needs to be added to the store
required: true
schemas:
Pet:
$ref: 'schemas/Pet.yaml'

View File

@ -0,0 +1,61 @@
<!--
~ 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.
-->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>common.maven</groupId>
<artifactId>issue-16489</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<name>OpenAPI Generator Configuration Test</name>
<url>https://openapi-generator.tech/</url>
<build>
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.6.0</version>
<configuration>
<generatorName>java</generatorName>
<skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged>
<library>native</library>
<output>${basedir}/target/generated-sources/issue-16489</output>
<additionalProperties>hideGenerationTimestamp=true</additionalProperties>
<inputSpec>${basedir}/petstore.yaml</inputSpec>
<generateModelTests>false</generateModelTests>
<generateModelDocumentation>false</generateModelDocumentation>
<generateApiTests>false</generateApiTests>
<generateApis>false</generateApis>
<generateSupportingFiles>false</generateSupportingFiles>
<configOptions>
<interfaceOnly>true</interfaceOnly>
</configOptions>
</configuration>
<executions>
<execution>
<id>generate</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,29 @@
title: a Pet
description: A pet for sale in the pet store
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet