1391 jel minimal overwrite option (#2451)

Option to overwrite only changed files
This commit is contained in:
john lilley 2019-03-20 10:17:10 -06:00 committed by William Cheng
parent fef2970dab
commit dc78405a68
7 changed files with 151 additions and 15 deletions

View File

@ -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)
private Boolean generateAliasAsModel;
@Option(name = {"--minimal-update"},
title = "Minimal update",
description = "Only write output files that have changed.")
private Boolean minimalUpdate;
@Override
public void run() {
if (logToStderr != null) {
@ -346,6 +351,9 @@ public class Generate implements Runnable {
if (generateAliasAsModel != null) {
configurator.setGenerateAliasAsModel(generateAliasAsModel);
}
if (minimalUpdate != null) {
configurator.setEnableMinimalUpdate(minimalUpdate);
}
applySystemPropertiesKvpList(systemProperties, configurator);
applyInstantiationTypesKvpList(instantiationTypes, configurator);

View File

@ -17,6 +17,10 @@
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -28,23 +32,78 @@ import java.util.regex.Pattern;
public abstract class AbstractGenerator {
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 {
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)
File output = Paths.get(filename).toFile();
if (output.getParent() != null && !new File(output.getParent()).exists()) {
File parent = new File(output.getParent());
File parent = Paths.get(output.getParent()).toFile();
parent.mkdirs();
}
try (Writer out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(output), "UTF-8"))) {
out.write(contents);
}
Files.write(output.toPath(), contents);
return output;
}

View File

@ -260,4 +260,8 @@ public interface CodegenConfig {
*/
void setOpenAPI(OpenAPI openAPI);
public boolean isEnableMinimalUpdate();
public void setEnableMinimalUpdate(boolean isEnableMinimalUpdate);
}

View File

@ -111,6 +111,8 @@ public class DefaultCodegen implements CodegenConfig {
protected String ignoreFilePathOverride;
// flag to indicate whether to use environment variable to post process file
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
protected OpenAPI openAPI;
@ -4834,4 +4836,22 @@ public class DefaultCodegen implements CodegenConfig {
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;
}
}

View File

@ -67,6 +67,11 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
private String contextPath;
private Map<String, String> generatorPropertyDefaults = new HashMap<>();
@Override
public boolean getEnableMinimalUpdate() {
return config.isEnableMinimalUpdate();
}
@Override
public Generator opts(ClientOptInput 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 {
File outputFile = java.nio.file.Paths.get(filename).toFile();
if (in != null) {
OutputStream out = new FileOutputStream(outputFile, false);
LOGGER.info("writing file " + outputFile);
IOUtils.copy(in, out);
out.close();
byte bytes[] = IOUtils.toByteArray(in);
return writeToFile(filename, bytes);
} else {
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) {

View File

@ -101,6 +101,7 @@ public class CodegenConfigurator implements Serializable {
private boolean logToStderr;
private boolean validateSpec;
private boolean enablePostProcessFile;
private boolean enableMinimalUpdate;
private String templateDir;
private String auth;
private String apiPackage;
@ -239,6 +240,15 @@ public class CodegenConfigurator implements Serializable {
return this;
}
public boolean getEnableMinimalUpdate() {
return enableMinimalUpdate;
}
public CodegenConfigurator setEnableMinimalUpdate(boolean enableMinimalUpdate) {
this.enableMinimalUpdate = enableMinimalUpdate;
return this;
}
public boolean isGenerateAliasAsModel() {
return ModelUtils.isGenerateAliasAsModel();
}
@ -545,6 +555,7 @@ public class CodegenConfigurator implements Serializable {
config.setIgnoreFilePathOverride(ignoreFileOverride);
config.setRemoveOperationIdPrefix(removeOperationIdPrefix);
config.setEnablePostProcessFile(enablePostProcessFile);
config.setEnableMinimalUpdate(enableMinimalUpdate);
config.instantiationTypes().putAll(instantiationTypes);
config.typeMapping().putAll(typeMappings);

View File

@ -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.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import java.io.File;
import java.io.IOException;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DefaultGeneratorTest {
@ -46,4 +50,32 @@ public class DefaultGeneratorTest {
Assert.assertEquals(defaultList.get(3).path, "/path4");
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();
}
}