forked from loafle/openapi-generator-original
[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:
parent
446e168f43
commit
880df7a7a4
@ -17,40 +17,14 @@
|
||||
|
||||
package org.openapitools.codegen.plugin;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||
import static org.openapitools.codegen.config.CodegenConfiguratorUtils.*;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.Files;
|
||||
import io.swagger.parser.OpenAPIParser;
|
||||
import io.swagger.v3.core.util.Json;
|
||||
import io.swagger.v3.core.util.Yaml;
|
||||
import io.swagger.v3.parser.OpenAPIResolver;
|
||||
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.util.ClasspathHelper;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
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.MojoExecution;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
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.plugins.annotations.*;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
|
||||
import org.apache.maven.project.MavenProjectHelper;
|
||||
import org.openapitools.codegen.CliOption;
|
||||
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.*;
|
||||
import org.openapitools.codegen.config.CodegenConfigurator;
|
||||
import org.openapitools.codegen.config.GlobalSettings;
|
||||
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.LoggerFactory;
|
||||
import org.sonatype.plexus.build.incremental.BuildContext;
|
||||
import org.sonatype.plexus.build.incremental.DefaultBuildContext;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
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.
|
||||
@ -609,19 +581,11 @@ public class CodeGenMojo extends AbstractMojo {
|
||||
}
|
||||
|
||||
if (Boolean.TRUE.equals(skipIfSpecIsUnchanged)) {
|
||||
File storedInputSpecHashFile = getHashFile(inputSpecFile);
|
||||
final File storedInputSpecHashFile = getHashFile(inputSpecFile);
|
||||
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();
|
||||
if (storedInputSpecHash.equals(inputSpecHash)) {
|
||||
getLog().info(
|
||||
"Code generation is skipped because input was unchanged");
|
||||
if (storedInputSpecHash.equals(calculateInputSpecHash(inputSpec))) {
|
||||
getLog().info("Code generation is skipped because input was unchanged");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1010,8 +974,6 @@ public class CodeGenMojo extends AbstractMojo {
|
||||
|
||||
// Store a checksum of the input spec
|
||||
File storedInputSpecHashFile = getHashFile(inputSpecFile);
|
||||
String inputSpecHash = calculateInputSpecHash(inputSpecFile);
|
||||
|
||||
if (storedInputSpecHashFile.getParent() != null && !new File(storedInputSpecHashFile.getParent()).exists()) {
|
||||
File parent = new File(storedInputSpecHashFile.getParent());
|
||||
if (!parent.mkdirs()) {
|
||||
@ -1019,8 +981,8 @@ public class CodeGenMojo extends AbstractMojo {
|
||||
" 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) {
|
||||
// Maven logs exceptions thrown by plugins only if invoked with -e
|
||||
// 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.
|
||||
* Does not take into account if input spec is hosted on remote resource
|
||||
* @return openapi specification file hash
|
||||
* @throws IOException
|
||||
* @param inputSpec - Openapi specification input file. Can denote a URL or file path.
|
||||
* @return openapi specification hash
|
||||
*/
|
||||
private String calculateInputSpecHash(File inputSpecFile) throws IOException {
|
||||
|
||||
URL inputSpecRemoteUrl = inputSpecRemoteUrl();
|
||||
|
||||
File inputSpecTempFile = inputSpecFile;
|
||||
|
||||
if (inputSpecRemoteUrl != null) {
|
||||
inputSpecTempFile = java.nio.file.Files.createTempFile("openapi-spec", ".tmp").toFile();
|
||||
|
||||
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();
|
||||
private String calculateInputSpecHash(String inputSpec) {
|
||||
final ParseOptions parseOptions = new ParseOptions();
|
||||
parseOptions.setResolve(true);
|
||||
|
||||
final URL remoteUrl = inputSpecRemoteUrl();
|
||||
return Hashing.sha256().hashBytes(
|
||||
new OpenAPIParser().readLocation(remoteUrl == null ? inputSpec : remoteUrl.toString(), null, parseOptions)
|
||||
.getOpenAPI().toString().getBytes(StandardCharsets.UTF_8)
|
||||
).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,17 +16,6 @@
|
||||
|
||||
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.maven.artifact.Artifact;
|
||||
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.repository.internal.MavenRepositorySystemUtils;
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||
import org.eclipse.aether.internal.impl.DefaultLocalPathComposer;
|
||||
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
|
||||
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 {
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
@ -62,8 +66,7 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void testCommonConfiguration(String profile) throws Exception {
|
||||
File folder = Files.createTempDirectory("test").toFile();
|
||||
CodeGenMojo mojo = loadMojo(folder, "src/test/resources/default", profile);
|
||||
CodeGenMojo mojo = loadMojo(newTempFolder(), "src/test/resources/default", profile);
|
||||
mojo.execute();
|
||||
assertEquals("java", getVariableValueFromObject(mojo, "generatorName"));
|
||||
assertEquals("jersey2", getVariableValueFromObject(mojo, "library"));
|
||||
@ -79,43 +82,42 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
|
||||
public void testHashGenerationFileContainsExecutionId() throws Exception {
|
||||
// GIVEN
|
||||
Path folder = Files.createTempDirectory("test");
|
||||
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId");
|
||||
final Path tempDir = newTempFolder();
|
||||
CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/default", "file", "executionId");
|
||||
|
||||
// WHEN
|
||||
mojo.execute();
|
||||
|
||||
// THEN
|
||||
Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator");
|
||||
assertTrue(hashFolder.resolve("petstore.yaml-executionId.sha256").toFile().exists());
|
||||
assertTrue(Files.exists(tempDir.resolve(
|
||||
"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.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testSkipRegenerationForClasspathSpecFileNoChange() throws Exception {
|
||||
|
||||
//GIVEN
|
||||
/* Setup the mojo */
|
||||
final Path folder = Files.createTempDirectory("test-classpath");
|
||||
final CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/classpath", null, "executionId");
|
||||
/* Set up the mojo */
|
||||
final Path tempDir = newTempFolder();
|
||||
final CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/classpath", null, "executionId");
|
||||
|
||||
/* Perform an initial generation */
|
||||
mojo.execute();
|
||||
|
||||
/* Check the hash file was created */
|
||||
final Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator");
|
||||
assertTrue(hashFolder.resolve("petstore-on-classpath.yaml-executionId.sha256").toFile().exists());
|
||||
final Path generatedDir = tempDir.resolve("target/generated-sources/common-maven/remote-openapi");
|
||||
assertTrue(Files.exists(
|
||||
generatedDir.resolve(".openapi-generator/petstore-on-classpath.yaml-executionId.sha256")
|
||||
));
|
||||
|
||||
/* Remove the generated source */
|
||||
Files.walk(folder.resolve("target/generated-sources/common-maven/remote-openapi/src"))
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.map(Path::toFile)
|
||||
.forEach(File::delete);
|
||||
|
||||
try (Stream<Path> files = Files.walk(generatedDir.resolve("src"))) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
|
||||
}
|
||||
|
||||
// WHEN
|
||||
/* Execute the mojo again */
|
||||
@ -123,41 +125,37 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
|
||||
// THEN
|
||||
/* 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",
|
||||
folder.resolve("target/generated-sources/common-maven/remote-openapi/src").toFile().exists());
|
||||
|
||||
assertFalse("src directory should not have been regenerated", Files.exists(generatedDir.resolve("src")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testSkipRegenerationForClasspathSpecFileWithChange() throws Exception {
|
||||
|
||||
//GIVEN
|
||||
/* Setup the mojo */
|
||||
final Path folder = Files.createTempDirectory("test-classpath");
|
||||
final CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/classpath", null, "executionId");
|
||||
/* Set up the mojo */
|
||||
final Path tempDir = newTempFolder();
|
||||
final CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/classpath", null, "executionId");
|
||||
|
||||
/* Perform an initial generation */
|
||||
mojo.execute();
|
||||
|
||||
/* Check the hash file was created, proving a generation occurred */
|
||||
final Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator");
|
||||
assertTrue(hashFolder.resolve("petstore-on-classpath.yaml-executionId.sha256").toFile().exists());
|
||||
final Path generatedDir = tempDir.resolve("target/generated-sources/common-maven/remote-openapi");
|
||||
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 */
|
||||
Files.write(
|
||||
hashFolder.resolve("petstore-on-classpath.yaml-executionId.sha256"),
|
||||
Arrays.asList("bd1bf4a953c858f9d47b67ed6029daacf1707e5cbd3d2e4b01383ba30363366f"));
|
||||
generatedDir.resolve(".openapi-generator/petstore-on-classpath.yaml-executionId.sha256"),
|
||||
List.of("bd1bf4a953c858f9d47b67ed6029daacf1707e5cbd3d2e4b01383ba30363366f")
|
||||
);
|
||||
|
||||
/* Remove the generated source */
|
||||
Files.walk(folder.resolve("target/generated-sources/common-maven/remote-openapi/src"))
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.map(Path::toFile)
|
||||
.forEach(File::delete);
|
||||
try(Stream<Path> files = Files.walk(generatedDir.resolve("src"))) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
|
||||
}
|
||||
|
||||
|
||||
// WHEN
|
||||
@ -165,29 +163,27 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
mojo.execute();
|
||||
|
||||
// THEN
|
||||
/* Verify that the source directory has not been repopulated. If it has then we generated code again */
|
||||
assertTrue("src directory should have been regenerated",
|
||||
folder.resolve("target/generated-sources/common-maven/remote-openapi/src").toFile().exists());
|
||||
|
||||
/* Verify that the source directory has been repopulated. */
|
||||
assertTrue("src directory should have been regenerated", Files.exists(generatedDir.resolve("src")));
|
||||
}
|
||||
|
||||
public void testCollapsedSpecProduced() throws Exception {
|
||||
// GIVEN
|
||||
Path folder = Files.createTempDirectory("test");
|
||||
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId");
|
||||
final Path tempDir = newTempFolder();
|
||||
CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/default", "file", "executionId");
|
||||
|
||||
// WHEN
|
||||
mojo.execute();
|
||||
|
||||
// THEN
|
||||
File collapseSpecFile = folder.resolve("target/generated-sources/common-maven/remote-openapi/petstore-full-spec.yaml").toFile();
|
||||
assertTrue(collapseSpecFile.exists());
|
||||
assertTrue(Files.exists(
|
||||
tempDir.resolve("target/generated-sources/common-maven/remote-openapi/petstore-full-spec.yaml")
|
||||
));
|
||||
}
|
||||
|
||||
public void testCollapsedSpecAddedToArtifacts() throws Exception {
|
||||
// GIVEN
|
||||
Path folder = Files.createTempDirectory("test");
|
||||
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId");
|
||||
CodeGenMojo mojo = loadMojo(newTempFolder(), "src/test/resources/default", "file", "executionId");
|
||||
|
||||
// WHEN
|
||||
mojo.execute();
|
||||
@ -201,8 +197,7 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
|
||||
public void testAnyInputSpecMustBeProvided() throws Exception {
|
||||
// GIVEN
|
||||
Path folder = Files.createTempDirectory("test");
|
||||
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId");
|
||||
CodeGenMojo mojo = loadMojo(newTempFolder(), "src/test/resources/default", "file", "executionId");
|
||||
mojo.inputSpec = null;
|
||||
mojo.inputSpecRootDirectory = null;
|
||||
|
||||
@ -215,8 +210,8 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
|
||||
public void testInputSpecRootDirectoryDoesNotRequireInputSpec() throws Exception {
|
||||
// GIVEN
|
||||
Path folder = Files.createTempDirectory("test");
|
||||
CodeGenMojo mojo = loadMojo(folder.toFile(), "src/test/resources/default", "file", "executionId");
|
||||
final Path tempDir = newTempFolder();
|
||||
CodeGenMojo mojo = loadMojo(tempDir, "src/test/resources/default", "file", "executionId");
|
||||
mojo.inputSpec = null;
|
||||
mojo.inputSpecRootDirectory = "src/test/resources/default";
|
||||
|
||||
@ -225,17 +220,43 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
|
||||
// THEN
|
||||
/* Check the hash file was created */
|
||||
final Path hashFolder = folder.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator");
|
||||
assertTrue(hashFolder.resolve("_merged_spec.yaml-executionId.sha256").toFile().exists());
|
||||
final Path hashFolder = tempDir.resolve("target/generated-sources/common-maven/remote-openapi/.openapi-generator");
|
||||
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");
|
||||
}
|
||||
|
||||
protected CodeGenMojo loadMojo(File temporaryFolder, String projectRoot, String profile, String executionId) throws Exception {
|
||||
File file = new File(projectRoot);
|
||||
FileUtils.copyDirectory(file, temporaryFolder);
|
||||
protected CodeGenMojo loadMojo(Path temporaryFolder, String projectRoot, String profile, String executionId) throws Exception {
|
||||
FileUtils.copyDirectory(new File(projectRoot), temporaryFolder.toFile());
|
||||
MavenProject project = readMavenProject(temporaryFolder, profile);
|
||||
MavenSession session = newMavenSession(project);
|
||||
MojoExecution execution = newMojoExecution("generate");
|
||||
@ -249,22 +270,29 @@ public class CodeGenMojoTest extends BaseTestCase {
|
||||
return executionWithId;
|
||||
}
|
||||
|
||||
protected MavenProject readMavenProject(File basedir, String profile)
|
||||
throws Exception {
|
||||
File pom = new File(basedir, "pom.xml");
|
||||
LocalRepository localRepo = new LocalRepository(new File(basedir, "local-repo"));
|
||||
protected MavenProject readMavenProject(Path basedir, String profile) throws Exception {
|
||||
LocalRepository localRepo = new LocalRepository(basedir.resolve("local-repo").toFile());
|
||||
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
|
||||
session.setLocalRepositoryManager(new SimpleLocalRepositoryManagerFactory().newInstance(session, localRepo));
|
||||
MavenExecutionRequest request = new DefaultMavenExecutionRequest();
|
||||
request.setBaseDirectory(basedir);
|
||||
session.setLocalRepositoryManager(
|
||||
new SimpleLocalRepositoryManagerFactory(new DefaultLocalPathComposer()).newInstance(session, localRepo)
|
||||
);
|
||||
MavenExecutionRequest request = new DefaultMavenExecutionRequest().setBaseDirectory(basedir.toFile());
|
||||
if (profile != null) {
|
||||
request.addActiveProfile(profile);
|
||||
}
|
||||
ProjectBuildingRequest configuration = request.getProjectBuildingRequest();
|
||||
configuration.setRepositorySession(session);
|
||||
configuration.setResolveDependencies(true);
|
||||
MavenProject project = lookup(ProjectBuilder.class).build(pom, configuration).getProject();
|
||||
ProjectBuildingRequest configuration = request.getProjectBuildingRequest()
|
||||
.setRepositorySession(session)
|
||||
.setResolveDependencies(true);
|
||||
MavenProject project = lookup(ProjectBuilder.class)
|
||||
.build(basedir.resolve("pom.xml").toFile(), configuration)
|
||||
.getProject();
|
||||
assertNotNull(project);
|
||||
return project;
|
||||
}
|
||||
|
||||
static private Path newTempFolder() throws IOException {
|
||||
final Path tempDir = Files.createTempDirectory("test");
|
||||
tempDir.toFile().deleteOnExit();
|
||||
return tempDir;
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
@ -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>
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user