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;
|
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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