mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2025-06-29 12:10:54 +00:00
1391 jel minimal overwrite option (#2451)
Option to overwrite only changed files
This commit is contained in:
parent
fef2970dab
commit
dc78405a68
@ -217,6 +217,11 @@ public class Generate implements Runnable {
|
|||||||
@Option(name = {"--generate-alias-as-model"}, title = "generate alias (array, map) as model", description = CodegenConstants.GENERATE_ALIAS_AS_MODEL_DESC)
|
@Option(name = {"--generate-alias-as-model"}, title = "generate alias (array, map) as model", description = CodegenConstants.GENERATE_ALIAS_AS_MODEL_DESC)
|
||||||
private Boolean generateAliasAsModel;
|
private Boolean generateAliasAsModel;
|
||||||
|
|
||||||
|
@Option(name = {"--minimal-update"},
|
||||||
|
title = "Minimal update",
|
||||||
|
description = "Only write output files that have changed.")
|
||||||
|
private Boolean minimalUpdate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (logToStderr != null) {
|
if (logToStderr != null) {
|
||||||
@ -346,6 +351,9 @@ public class Generate implements Runnable {
|
|||||||
if (generateAliasAsModel != null) {
|
if (generateAliasAsModel != null) {
|
||||||
configurator.setGenerateAliasAsModel(generateAliasAsModel);
|
configurator.setGenerateAliasAsModel(generateAliasAsModel);
|
||||||
}
|
}
|
||||||
|
if (minimalUpdate != null) {
|
||||||
|
configurator.setEnableMinimalUpdate(minimalUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
applySystemPropertiesKvpList(systemProperties, configurator);
|
applySystemPropertiesKvpList(systemProperties, configurator);
|
||||||
applyInstantiationTypesKvpList(instantiationTypes, configurator);
|
applyInstantiationTypesKvpList(instantiationTypes, configurator);
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
package org.openapitools.codegen;
|
package org.openapitools.codegen;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.Arrays;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -28,23 +32,78 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
public abstract class AbstractGenerator {
|
public abstract class AbstractGenerator {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGenerator.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGenerator.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the minimal-file-update option enabled?
|
||||||
|
*
|
||||||
|
* @return Option value
|
||||||
|
*/
|
||||||
|
public abstract boolean getEnableMinimalUpdate();
|
||||||
|
|
||||||
@SuppressWarnings("static-method")
|
/**
|
||||||
|
* Write String to a file, formatting as UTF-8
|
||||||
|
*
|
||||||
|
* @param filename The name of file to write
|
||||||
|
* @param contents The contents string.
|
||||||
|
* @return File representing the written file.
|
||||||
|
* @throws IOException If file cannot be written.
|
||||||
|
*/
|
||||||
public File writeToFile(String filename, String contents) throws IOException {
|
public File writeToFile(String filename, String contents) throws IOException {
|
||||||
LOGGER.info("writing file " + filename);
|
return writeToFile(filename, contents.getBytes(Charset.forName("UTF-8")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write bytes to a file
|
||||||
|
*
|
||||||
|
* @param filename The name of file to write
|
||||||
|
* @param contents The contents bytes. Typically this is a UTF-8 formatted string.
|
||||||
|
* @return File representing the written file.
|
||||||
|
* @throws IOException If file cannot be written.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("static-method")
|
||||||
|
public File writeToFile(String filename, byte contents[]) throws IOException {
|
||||||
|
if (getEnableMinimalUpdate()) {
|
||||||
|
String tempFilename = filename + ".tmp";
|
||||||
|
// Use Paths.get here to normalize path (for Windows file separator, space escaping on Linux/Mac, etc)
|
||||||
|
File outputFile = Paths.get(filename).toFile();
|
||||||
|
File tempFile = null;
|
||||||
|
try {
|
||||||
|
tempFile = writeToFileRaw(tempFilename, contents);
|
||||||
|
if (!filesEqual(tempFile, outputFile)) {
|
||||||
|
LOGGER.info("writing file " + filename);
|
||||||
|
Files.move(tempFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
tempFile = null;
|
||||||
|
} else {
|
||||||
|
LOGGER.info("skipping unchanged file " + filename);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (tempFile != null && tempFile.exists()) {
|
||||||
|
try {
|
||||||
|
tempFile.delete();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
LOGGER.error("Error removing temporary file " + tempFile, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputFile;
|
||||||
|
} else {
|
||||||
|
LOGGER.info("writing file " + filename);
|
||||||
|
return writeToFileRaw(filename, contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean filesEqual(File file1, File file2) throws IOException {
|
||||||
|
return file1.exists() && file2.exists() && Arrays.equals(Files.readAllBytes(file1.toPath()), Files.readAllBytes(file2.toPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private File writeToFileRaw(String filename, byte[] contents) throws IOException {
|
||||||
// Use Paths.get here to normalize path (for Windows file separator, space escaping on Linux/Mac, etc)
|
// Use Paths.get here to normalize path (for Windows file separator, space escaping on Linux/Mac, etc)
|
||||||
File output = Paths.get(filename).toFile();
|
File output = Paths.get(filename).toFile();
|
||||||
|
|
||||||
if (output.getParent() != null && !new File(output.getParent()).exists()) {
|
if (output.getParent() != null && !new File(output.getParent()).exists()) {
|
||||||
File parent = new File(output.getParent());
|
File parent = Paths.get(output.getParent()).toFile();
|
||||||
parent.mkdirs();
|
parent.mkdirs();
|
||||||
}
|
}
|
||||||
|
Files.write(output.toPath(), contents);
|
||||||
try (Writer out = new BufferedWriter(new OutputStreamWriter(
|
|
||||||
new FileOutputStream(output), "UTF-8"))) {
|
|
||||||
out.write(contents);
|
|
||||||
}
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,4 +260,8 @@ public interface CodegenConfig {
|
|||||||
*/
|
*/
|
||||||
void setOpenAPI(OpenAPI openAPI);
|
void setOpenAPI(OpenAPI openAPI);
|
||||||
|
|
||||||
|
public boolean isEnableMinimalUpdate();
|
||||||
|
|
||||||
|
public void setEnableMinimalUpdate(boolean isEnableMinimalUpdate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,8 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
protected String ignoreFilePathOverride;
|
protected String ignoreFilePathOverride;
|
||||||
// flag to indicate whether to use environment variable to post process file
|
// flag to indicate whether to use environment variable to post process file
|
||||||
protected boolean enablePostProcessFile = false;
|
protected boolean enablePostProcessFile = false;
|
||||||
|
// flag to indicate whether to only update files whose contents have changed
|
||||||
|
protected boolean enableMinimalUpdate = false;
|
||||||
|
|
||||||
// make openapi available to all methods
|
// make openapi available to all methods
|
||||||
protected OpenAPI openAPI;
|
protected OpenAPI openAPI;
|
||||||
@ -4834,4 +4836,22 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
this.enablePostProcessFile = enablePostProcessFile;
|
this.enablePostProcessFile = enablePostProcessFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the boolean value indicating the state of the option for updating only changed files
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isEnableMinimalUpdate() {
|
||||||
|
return enableMinimalUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the boolean value indicating the state of the option for updating only changed files
|
||||||
|
*
|
||||||
|
* @param enableMinimalUpdate true to enable minimal update
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setEnableMinimalUpdate(boolean enableMinimalUpdate) {
|
||||||
|
this.enableMinimalUpdate = enableMinimalUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,11 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
|||||||
private String contextPath;
|
private String contextPath;
|
||||||
private Map<String, String> generatorPropertyDefaults = new HashMap<>();
|
private Map<String, String> generatorPropertyDefaults = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getEnableMinimalUpdate() {
|
||||||
|
return config.isEnableMinimalUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Generator opts(ClientOptInput opts) {
|
public Generator opts(ClientOptInput opts) {
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
@ -797,16 +802,13 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected File writeInputStreamToFile(String filename, InputStream in, String templateFile) throws FileNotFoundException, IOException {
|
protected File writeInputStreamToFile(String filename, InputStream in, String templateFile) throws FileNotFoundException, IOException {
|
||||||
File outputFile = java.nio.file.Paths.get(filename).toFile();
|
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
OutputStream out = new FileOutputStream(outputFile, false);
|
byte bytes[] = IOUtils.toByteArray(in);
|
||||||
LOGGER.info("writing file " + outputFile);
|
return writeToFile(filename, bytes);
|
||||||
IOUtils.copy(in, out);
|
|
||||||
out.close();
|
|
||||||
} else {
|
} else {
|
||||||
LOGGER.error("can't open '" + templateFile + "' for input; cannot write '" + filename + "'");
|
LOGGER.error("can't open '" + templateFile + "' for input; cannot write '" + filename + "'");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return outputFile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> buildSupportFileBundle(List<Object> allOperations, List<Object> allModels) {
|
private Map<String, Object> buildSupportFileBundle(List<Object> allOperations, List<Object> allModels) {
|
||||||
|
@ -101,6 +101,7 @@ public class CodegenConfigurator implements Serializable {
|
|||||||
private boolean logToStderr;
|
private boolean logToStderr;
|
||||||
private boolean validateSpec;
|
private boolean validateSpec;
|
||||||
private boolean enablePostProcessFile;
|
private boolean enablePostProcessFile;
|
||||||
|
private boolean enableMinimalUpdate;
|
||||||
private String templateDir;
|
private String templateDir;
|
||||||
private String auth;
|
private String auth;
|
||||||
private String apiPackage;
|
private String apiPackage;
|
||||||
@ -239,6 +240,15 @@ public class CodegenConfigurator implements Serializable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getEnableMinimalUpdate() {
|
||||||
|
return enableMinimalUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodegenConfigurator setEnableMinimalUpdate(boolean enableMinimalUpdate) {
|
||||||
|
this.enableMinimalUpdate = enableMinimalUpdate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isGenerateAliasAsModel() {
|
public boolean isGenerateAliasAsModel() {
|
||||||
return ModelUtils.isGenerateAliasAsModel();
|
return ModelUtils.isGenerateAliasAsModel();
|
||||||
}
|
}
|
||||||
@ -545,6 +555,7 @@ public class CodegenConfigurator implements Serializable {
|
|||||||
config.setIgnoreFilePathOverride(ignoreFileOverride);
|
config.setIgnoreFilePathOverride(ignoreFileOverride);
|
||||||
config.setRemoveOperationIdPrefix(removeOperationIdPrefix);
|
config.setRemoveOperationIdPrefix(removeOperationIdPrefix);
|
||||||
config.setEnablePostProcessFile(enablePostProcessFile);
|
config.setEnablePostProcessFile(enablePostProcessFile);
|
||||||
|
config.setEnableMinimalUpdate(enableMinimalUpdate);
|
||||||
|
|
||||||
config.instantiationTypes().putAll(instantiationTypes);
|
config.instantiationTypes().putAll(instantiationTypes);
|
||||||
config.typeMapping().putAll(typeMappings);
|
config.typeMapping().putAll(typeMappings);
|
||||||
|
@ -9,11 +9,15 @@ import io.swagger.v3.oas.models.media.StringSchema;
|
|||||||
import io.swagger.v3.oas.models.parameters.QueryParameter;
|
import io.swagger.v3.oas.models.parameters.QueryParameter;
|
||||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.models.responses.ApiResponses;
|
import io.swagger.v3.oas.models.responses.ApiResponses;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import org.testng.Assert;
|
import org.testng.Assert;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class DefaultGeneratorTest {
|
public class DefaultGeneratorTest {
|
||||||
|
|
||||||
@ -46,4 +50,32 @@ public class DefaultGeneratorTest {
|
|||||||
Assert.assertEquals(defaultList.get(3).path, "/path4");
|
Assert.assertEquals(defaultList.get(3).path, "/path4");
|
||||||
Assert.assertEquals(defaultList.get(3).allParams.size(), 1);
|
Assert.assertEquals(defaultList.get(3).allParams.size(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void minimalUpdateTest() throws IOException {
|
||||||
|
OpenAPI openAPI = TestUtils.createOpenAPI();
|
||||||
|
ClientOptInput opts = new ClientOptInput();
|
||||||
|
opts.setOpenAPI(openAPI);
|
||||||
|
DefaultCodegen codegen = new DefaultCodegen();
|
||||||
|
codegen.setEnableMinimalUpdate(true);
|
||||||
|
opts.setConfig(codegen);
|
||||||
|
opts.setOpts(new ClientOpts());
|
||||||
|
DefaultGenerator generator = new DefaultGenerator();
|
||||||
|
generator.opts(opts);
|
||||||
|
File testPath = new File("temp/overwrite.test");
|
||||||
|
if (testPath.exists()) {
|
||||||
|
testPath.delete();
|
||||||
|
}
|
||||||
|
generator.writeToFile(testPath.toString(), "some file contents");
|
||||||
|
long createTime = testPath.lastModified();
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
}
|
||||||
|
generator.writeToFile(testPath.toString(), "some file contents");
|
||||||
|
Assert.assertEquals(createTime, testPath.lastModified());
|
||||||
|
File testPathTmp = new File("temp/overwrite.test.tmp");
|
||||||
|
Assert.assertFalse(testPathTmp.exists());
|
||||||
|
testPath.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user