forked from loafle/openapi-generator-original
[cli][core] Add support for dry-run and display (#5332)
* 👕🎨 Minor refactor DefaultGenerator This cleans up some lint warnings and improve general code cleanliness of DefaultGenerator. Specifically: * logger strings are now using the built-in log formatter rather than constructing new strings regardless of log level. * Diamond operators are used where possible * Some long-unused commented code has been removed * Lambdas are used where possible * Redundant operations are merged (HashMap constructor used rather than subsequent putAll on a collection, for example) * [cli][core] Add support for dry-run and display CLI now supports `--dry-run`, which will output a file change status similar to git status --porcelain. The user may also specify `--verbose` for a one-liner below each file explaining why the change operation might take place.
This commit is contained in:
parent
1ec2c26053
commit
db47b95fc9
@ -3,6 +3,76 @@ id: debugging
|
||||
title: Debugging
|
||||
---
|
||||
|
||||
## Generation
|
||||
|
||||
As a user there may be times when generated outputs don't match your expectations it's unclear why. The CLI supports a `--dry-run` option which may be used to inspect the anticipated file operations without making changes to the file system.
|
||||
|
||||
Suppose you generate using the `--minimal-update` option, and you notice on subsequent generations of a client that no files have changed. This is by design.
|
||||
|
||||
For example, if you generate the aspnetcore generator passing `--minimal-update --dry-run` to the sample generation script in the code repository:
|
||||
|
||||
```bash
|
||||
export JAVA_OPTS="-Dlog.level=off"
|
||||
./bin/aspnetcore-petstore-server.sh --minimal-update --dry-run
|
||||
```
|
||||
|
||||
You'll see the output similar to the following:
|
||||
|
||||
```
|
||||
# START SCRIPT: ./bin/aspnetcore-petstore-server.sh
|
||||
|
||||
|
||||
Dry Run Results:
|
||||
|
||||
s /path/to/aspnetcore/.openapi-generator-ignore
|
||||
n /path/to/aspnetcore/.openapi-generator/VERSION
|
||||
n /path/to/aspnetcore/Org.OpenAPITools.sln
|
||||
n /path/to/aspnetcore/README.md
|
||||
n /path/to/aspnetcore/build.bat
|
||||
n /path/to/aspnetcore/build.sh
|
||||
w /path/to/aspnetcore/src/Org.OpenAPITools/.gitignore
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Attributes/ValidateModelStateAttribute.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Authentication/ApiAuthentication.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Controllers/PetApi.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Controllers/StoreApi.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Controllers/UserApi.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Converters/CustomEnumConverter.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Dockerfile
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Filters/BasePathFilter.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Filters/GeneratePathParamsValidationFilter.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Models/ApiResponse.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Models/Category.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Models/Order.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Models/Pet.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Models/Tag.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Models/User.cs
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Org.OpenAPITools.csproj
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Program.cs
|
||||
w /path/to/aspnetcore/src/Org.OpenAPITools/Properties/launchSettings.json
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/Startup.cs
|
||||
w /path/to/aspnetcore/src/Org.OpenAPITools/appsettings.json
|
||||
w /path/to/aspnetcore/src/Org.OpenAPITools/wwwroot/README.md
|
||||
w /path/to/aspnetcore/src/Org.OpenAPITools/wwwroot/index.html
|
||||
n /path/to/aspnetcore/src/Org.OpenAPITools/wwwroot/openapi-original.json
|
||||
w /path/to/aspnetcore/src/Org.OpenAPITools/wwwroot/web.config
|
||||
|
||||
|
||||
States:
|
||||
|
||||
- w Write
|
||||
- n Write if New/Updated
|
||||
- i Ignored
|
||||
- s Skipped Overwrite
|
||||
- k Skipped by user option(s)
|
||||
- e Error evaluating file write state
|
||||
|
||||
```
|
||||
|
||||
The output lists the files which would be written in a normal run of the tool. Notice that we skip `.openapi-generator-ignore` because the file exists and we don't want to blow away the user's generation rules. Most of these files will overwrite output files only if the contents slated for write are different from those on the filesystem; this is denoted by an `n` preceding the filename. Some of the above lines begin with a `w`, meaning these files will _always_ result in a write operation.
|
||||
|
||||
If you find an operation that you feel should result in a different state, please [open an issue](https://github.com/OpenAPITools/openapi-generator/issues/new/choose) or [submit a pull request](https://github.com/OpenAPITools/openapi-generator/compare) to change the behavior (we welcome all contributions).
|
||||
|
||||
|
||||
## Templates
|
||||
|
||||
Sometimes, you may have issues with variables in your templates. As discussed in the [templating](./templating.md) docs, we offer a variety of system properties for inspecting the models bound to templates.
|
||||
|
@ -242,16 +242,16 @@ NAME
|
||||
SYNOPSIS
|
||||
openapi-generator-cli generate
|
||||
[(-a <authorization> | --auth <authorization>)]
|
||||
[--api-package <api package>] [--artifact-id <artifact id>]
|
||||
[--artifact-version <artifact version>]
|
||||
[--api-name-suffix <api name suffix>] [--api-package <api package>]
|
||||
[--artifact-id <artifact id>] [--artifact-version <artifact version>]
|
||||
[(-c <configuration file> | --config <configuration file>)]
|
||||
[-D <system properties>...]
|
||||
[-D <system properties>...] [--dry-run]
|
||||
[(-e <templating engine> | --engine <templating engine>)]
|
||||
[--enable-post-process-file]
|
||||
[(-g <generator name> | --generator-name <generator name>)]
|
||||
[--generate-alias-as-model] [--git-repo-id <git repo id>]
|
||||
[--git-user-id <git user id>] [--group-id <group id>]
|
||||
[--http-user-agent <http user agent>]
|
||||
[--generate-alias-as-model] [--git-host <git host>]
|
||||
[--git-repo-id <git repo id>] [--git-user-id <git user id>]
|
||||
[--group-id <group id>] [--http-user-agent <http user agent>]
|
||||
(-i <spec file> | --input-spec <spec file>)
|
||||
[--ignore-file-override <ignore file override location>]
|
||||
[--import-mappings <import mappings>...]
|
||||
@ -283,6 +283,11 @@ OPTIONS
|
||||
remotely. Pass in a URL-encoded string of name:header with a comma
|
||||
separating multiple values
|
||||
|
||||
--api-name-suffix <api name suffix>
|
||||
Suffix that will be appended to all API names ('tags'). Default:
|
||||
Api. e.g. Pet => PetApi. Note: Only ruby, python, jaxrs generators
|
||||
suppport this feature at the moment.
|
||||
|
||||
--api-package <api package>
|
||||
package for generated api classes
|
||||
|
||||
@ -295,23 +300,26 @@ OPTIONS
|
||||
generated library's filename
|
||||
|
||||
-c <configuration file>, --config <configuration file>
|
||||
Path to configuration file configuration file. It can be json or
|
||||
yaml.If file is json, the content should have the format
|
||||
{"optionKey":"optionValue", "optionKey1":"optionValue1"...}.If file
|
||||
is yaml, the content should have the format optionKey:
|
||||
optionValueSupported options can be different for each language. Run
|
||||
config-help -g {generator name} command for language specific config
|
||||
options.
|
||||
Path to configuration file. It can be JSON or YAML. If file is JSON,
|
||||
the content should have the format {"optionKey":"optionValue",
|
||||
"optionKey1":"optionValue1"...}. If file is YAML, the content should
|
||||
have the format optionKey: optionValue. Supported options can be
|
||||
different for each language. Run config-help -g {generator name}
|
||||
command for language-specific config options.
|
||||
|
||||
-D <system properties>
|
||||
sets specified system properties in the format of
|
||||
name=value,name=value (or multiple options, each with name=value)
|
||||
|
||||
--dry-run
|
||||
Try things out and report on potential changes (without actually
|
||||
making changes).
|
||||
|
||||
-e <templating engine>, --engine <templating engine>
|
||||
templating engine: "mustache" (default) or "handlebars" (beta)
|
||||
|
||||
--enable-post-process-file
|
||||
enablePostProcessFile
|
||||
Enable post-processing file using environment variables.
|
||||
|
||||
-g <generator name>, --generator-name <generator name>
|
||||
generator to use (see list command for list)
|
||||
@ -324,6 +332,9 @@ OPTIONS
|
||||
i.e. the 'additionalproperties' attribute is set on that object.
|
||||
An 'array' schema is a list of sub schemas in a OAS document.
|
||||
|
||||
--git-host <git host>
|
||||
Git host, e.g. gitlab.com.
|
||||
|
||||
--git-repo-id <git repo id>
|
||||
Git repo ID, e.g. openapi-generator.
|
||||
|
||||
@ -377,12 +388,10 @@ OPTIONS
|
||||
Only write output files that have changed.
|
||||
|
||||
--model-name-prefix <model name prefix>
|
||||
Prefix that will be prepended to all model names. Default is the
|
||||
empty string.
|
||||
Prefix that will be prepended to all model names.
|
||||
|
||||
--model-name-suffix <model name suffix>
|
||||
Suffix that will be appended to all model names. Default is the
|
||||
empty string.
|
||||
Suffix that will be appended to all model names.
|
||||
|
||||
--model-package <model package>
|
||||
package for generated models
|
||||
@ -415,8 +424,8 @@ OPTIONS
|
||||
generation.
|
||||
|
||||
--server-variables <server variables>
|
||||
sets server variables for spec documents which support variable
|
||||
templating of servers.
|
||||
sets server variables overrides for spec documents which support
|
||||
variable templating of servers.
|
||||
|
||||
--skip-validate-spec
|
||||
Skips the default behavior of validating an input specification.
|
||||
|
@ -91,6 +91,9 @@ public class Generate implements Runnable {
|
||||
+ "overwritten during the generation.")
|
||||
private Boolean skipOverwrite;
|
||||
|
||||
@Option(name = { "--dry-run" }, title = "Dry run",
|
||||
description = "Try things out and report on potential changes (without actually making changes).")
|
||||
private Boolean isDryRun;
|
||||
|
||||
@Option(name = {"--package-name"}, title = "package name",
|
||||
description = CodegenConstants.PACKAGE_NAME_DESC)
|
||||
@ -416,7 +419,7 @@ public class Generate implements Runnable {
|
||||
|
||||
// this null check allows us to inject for unit testing.
|
||||
if (generator == null) {
|
||||
generator = new DefaultGenerator();
|
||||
generator = new DefaultGenerator(isDryRun);
|
||||
}
|
||||
|
||||
generator.opts(clientOptInput);
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
package org.openapitools.codegen;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
@ -28,12 +28,16 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class AbstractGenerator implements TemplatingGenerator {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGenerator.class);
|
||||
|
||||
protected boolean dryRun = false;
|
||||
protected Map<String, DryRunStatus> dryRunStatusMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Is the minimal-file-update option enabled?
|
||||
*
|
||||
@ -50,19 +54,19 @@ public abstract class AbstractGenerator implements TemplatingGenerator {
|
||||
* @throws IOException If file cannot be written.
|
||||
*/
|
||||
public File writeToFile(String filename, String contents) throws IOException {
|
||||
return writeToFile(filename, contents.getBytes(Charset.forName("UTF-8")));
|
||||
return writeToFile(filename, contents.getBytes(StandardCharsets.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.
|
||||
* @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 {
|
||||
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)
|
||||
|
@ -53,6 +53,9 @@ import java.nio.file.Path;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import static org.openapitools.codegen.utils.OnceLogger.once;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
protected final Logger LOGGER = LoggerFactory.getLogger(DefaultGenerator.class);
|
||||
protected CodegenConfig config;
|
||||
@ -73,11 +76,21 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
private String contextPath;
|
||||
private Map<String, String> generatorPropertyDefaults = new HashMap<>();
|
||||
|
||||
|
||||
public DefaultGenerator() {
|
||||
}
|
||||
|
||||
public DefaultGenerator(Boolean dryRun) {
|
||||
this.dryRun = Boolean.TRUE.equals(dryRun);
|
||||
LOGGER.info("Generating with dryRun={}", this.dryRun);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getEnableMinimalUpdate() {
|
||||
return config.isEnableMinimalUpdate();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Generator opts(ClientOptInput opts) {
|
||||
this.opts = opts;
|
||||
@ -285,17 +298,35 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
for (String templateName : config.modelTestTemplateFiles().keySet()) {
|
||||
String suffix = config.modelTestTemplateFiles().get(templateName);
|
||||
String filename = config.modelTestFileFolder() + File.separator + config.toModelTestFilename(modelName) + suffix;
|
||||
// do not overwrite test file that already exists
|
||||
if (new File(filename).exists()) {
|
||||
LOGGER.info("File exists. Skipped overwriting " + filename);
|
||||
continue;
|
||||
}
|
||||
File written = processTemplateToFile(models, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
config.postProcessFile(written, "model-test");
|
||||
|
||||
if (generateModelTests) {
|
||||
// do not overwrite test file that already exists (regardless of config's skipOverwrite setting)
|
||||
if (new File(filename).exists()) {
|
||||
LOGGER.info("File exists. Skipped overwriting {}", filename);
|
||||
if (dryRun) {
|
||||
dryRunStatusMap.put(filename,
|
||||
new DryRunStatus(
|
||||
java.nio.file.Paths.get(filename),
|
||||
DryRunStatus.State.SkippedOverwrite,
|
||||
"Test files never overwrite an existing file of the same name."
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
File written = processTemplateToFile(models, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(written, "model-test");
|
||||
}
|
||||
}
|
||||
} else if (dryRun) {
|
||||
dryRunStatusMap.put(filename,
|
||||
new DryRunStatus(
|
||||
java.nio.file.Paths.get(filename),
|
||||
DryRunStatus.State.Skipped,
|
||||
"Skipped by modelTests option supplied by user."
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -305,57 +336,86 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
String docExtension = config.getDocExtension();
|
||||
String suffix = docExtension != null ? docExtension : config.modelDocTemplateFiles().get(templateName);
|
||||
String filename = config.modelDocFileFolder() + File.separator + config.toModelDocFilename(modelName) + suffix;
|
||||
if (!config.shouldOverwrite(filename)) {
|
||||
LOGGER.info("Skipped overwriting " + filename);
|
||||
continue;
|
||||
}
|
||||
File written = processTemplateToFile(models, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
config.postProcessFile(written, "model-doc");
|
||||
|
||||
if (generateModelDocumentation) {
|
||||
if (!config.shouldOverwrite(filename)) {
|
||||
LOGGER.info("Skipped overwriting {}", filename);
|
||||
if (dryRun) {
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(java.nio.file.Paths.get(filename), DryRunStatus.State.SkippedOverwrite));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
File written = processTemplateToFile(models, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(written, "model-doc");
|
||||
}
|
||||
}
|
||||
} else if (dryRun) {
|
||||
dryRunStatusMap.put(filename,
|
||||
new DryRunStatus(
|
||||
java.nio.file.Paths.get(filename),
|
||||
DryRunStatus.State.Skipped,
|
||||
"Skipped by modelDocs option supplied by user."
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getModelFilenameByTemplate(String modelName, String templateName){
|
||||
String suffix = config.modelTemplateFiles().get(templateName);
|
||||
return config.modelFileFolder() + File.separator + config.toModelFilename(modelName) + suffix;
|
||||
}
|
||||
|
||||
private void generateModel(List<File> files, Map<String, Object> models, String modelName) throws IOException {
|
||||
for (String templateName : config.modelTemplateFiles().keySet()) {
|
||||
String suffix = config.modelTemplateFiles().get(templateName);
|
||||
String filename = config.modelFileFolder() + File.separator + config.toModelFilename(modelName) + suffix;
|
||||
String filename = getModelFilenameByTemplate(modelName, templateName);
|
||||
if (!config.shouldOverwrite(filename)) {
|
||||
LOGGER.info("Skipped overwriting " + filename);
|
||||
LOGGER.info("Skipped overwriting {}", filename);
|
||||
if (dryRun) {
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(
|
||||
java.nio.file.Paths.get(filename),
|
||||
DryRunStatus.State.SkippedOverwrite
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
File written = processTemplateToFile(models, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(written, "model");
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("Unknown issue writing {}", filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void generateModels(List<File> files, List<Object> allModels, List<String> unusedModels) {
|
||||
if (!generateModels) {
|
||||
// TODO: Process these anyway and add to dryRun info
|
||||
LOGGER.info("Skipping generation of models.");
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Schema> schemas = ModelUtils.getSchemas(this.openAPI);
|
||||
if (schemas == null) {
|
||||
LOGGER.warn("Skipping generation of models because specification document has no schemas.");
|
||||
return;
|
||||
}
|
||||
|
||||
String modelNames = GlobalSettings.getProperty("models");
|
||||
Set<String> modelsToGenerate = null;
|
||||
if (modelNames != null && !modelNames.isEmpty()) {
|
||||
modelsToGenerate = new HashSet<String>(Arrays.asList(modelNames.split(",")));
|
||||
modelsToGenerate = new HashSet<>(Arrays.asList(modelNames.split(",")));
|
||||
}
|
||||
|
||||
Set<String> modelKeys = schemas.keySet();
|
||||
if (modelsToGenerate != null && !modelsToGenerate.isEmpty()) {
|
||||
Set<String> updatedKeys = new HashSet<String>();
|
||||
Set<String> updatedKeys = new HashSet<>();
|
||||
for (String m : modelKeys) {
|
||||
if (modelsToGenerate.contains(m)) {
|
||||
updatedKeys.add(m);
|
||||
@ -366,59 +426,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
|
||||
// store all processed models
|
||||
Map<String, Object> allProcessedModels = new TreeMap<String, Object>(new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return ObjectUtils.compare(config.toModelName(o1), config.toModelName(o2));
|
||||
}
|
||||
/* TODO need to revise the logic below
|
||||
|
||||
Model model1 = definitions.get(o1);
|
||||
Model model2 = definitions.get(o2);
|
||||
|
||||
int model1InheritanceDepth = getInheritanceDepth(model1);
|
||||
int model2InheritanceDepth = getInheritanceDepth(model2);
|
||||
|
||||
if (model1InheritanceDepth == model2InheritanceDepth) {
|
||||
return ObjectUtils.compare(config.toModelName(o1), config.toModelName(o2));
|
||||
} else if (model1InheritanceDepth > model2InheritanceDepth) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private int getInheritanceDepth(Model model) {
|
||||
int inheritanceDepth = 0;
|
||||
Model parent = getParent(model);
|
||||
|
||||
while (parent != null) {
|
||||
inheritanceDepth++;
|
||||
parent = getParent(parent);
|
||||
}
|
||||
|
||||
return inheritanceDepth;
|
||||
}
|
||||
|
||||
private Model getParent(Model model) {
|
||||
if (model instanceof ComposedModel) {
|
||||
Model parent = ((ComposedModel) model).getParent();
|
||||
if (parent == null) {
|
||||
// check for interfaces
|
||||
List<RefModel> interfaces = ((ComposedModel) model).getInterfaces();
|
||||
if (interfaces.size() > 0) {
|
||||
RefModel interf = interfaces.get(0);
|
||||
return definitions.get(interf.getSimpleRef());
|
||||
}
|
||||
}
|
||||
if (parent != null) {
|
||||
return definitions.get(parent.getReference());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} */
|
||||
});
|
||||
Map<String, Object> allProcessedModels = new TreeMap<>((o1, o2) -> ObjectUtils.compare(config.toModelName(o1), config.toModelName(o2)));
|
||||
|
||||
Boolean skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ?
|
||||
Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) :
|
||||
@ -429,7 +437,18 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
try {
|
||||
//don't generate models that have an import mapping
|
||||
if (config.importMapping().containsKey(name)) {
|
||||
LOGGER.debug("Model " + name + " not imported due to import mapping");
|
||||
LOGGER.debug("Model {} not imported due to import mapping", name);
|
||||
if (dryRun) {
|
||||
// HACK: Because this returns early, could lead to some invalid model reporting.
|
||||
for (String templateName : config.modelTemplateFiles().keySet()) {
|
||||
String filename = getModelFilenameByTemplate(name, templateName);
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(
|
||||
java.nio.file.Paths.get(filename),
|
||||
DryRunStatus.State.Skipped,
|
||||
"Skipped prior to model processing due to import mapping conflict (either by user or by generator)."
|
||||
));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -437,9 +456,10 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
if (unusedModels.contains(name)) {
|
||||
if (Boolean.FALSE.equals(skipFormModel)) {
|
||||
// if skipFormModel sets to true, still generate the model and log the result
|
||||
LOGGER.info("Model " + name + " (marked as unused due to form parameters) is generated due to the system property skipFormModel=false (default)");
|
||||
LOGGER.info("Model {} (marked as unused due to form parameters) is generated due to the system property skipFormModel=false (default)", name);
|
||||
} else {
|
||||
LOGGER.info("Model " + name + " not generated since it's marked as unused (due to form parameters) and skipFormModel (system property) set to true");
|
||||
LOGGER.info("Model {} not generated since it's marked as unused (due to form parameters) and skipFormModel (system property) set to true", name);
|
||||
// TODO: Should this be added to dryRun? If not, this seems like a weird place to return early from processing.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -447,7 +467,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
Schema schema = schemas.get(name);
|
||||
|
||||
if (ModelUtils.isFreeFormObject(schema)) { // check to see if it'a a free-form object
|
||||
LOGGER.info("Model " + name + " not generated since it's a free-form object");
|
||||
LOGGER.info("Model {} not generated since it's a free-form object", name);
|
||||
continue;
|
||||
} else if (ModelUtils.isMapSchema(schema)) { // check to see if it's a "map" model
|
||||
// A composed schema (allOf, oneOf, anyOf) is considered a Map schema if the additionalproperties attribute is set
|
||||
@ -455,13 +475,13 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
// in the inner schemas, and the outer schema does not have properties.
|
||||
if (!ModelUtils.isGenerateAliasAsModel() && !ModelUtils.isComposedSchema(schema) && (schema.getProperties() == null || schema.getProperties().isEmpty())) {
|
||||
// schema without property, i.e. alias to map
|
||||
LOGGER.info("Model " + name + " not generated since it's an alias to map (without property) and `generateAliasAsModel` is set to false (default)");
|
||||
LOGGER.info("Model {} not generated since it's an alias to map (without property) and `generateAliasAsModel` is set to false (default)", name);
|
||||
continue;
|
||||
}
|
||||
} else if (ModelUtils.isArraySchema(schema)) { // check to see if it's an "array" model
|
||||
if (!ModelUtils.isGenerateAliasAsModel() && (schema.getProperties() == null || schema.getProperties().isEmpty())) {
|
||||
// schema without property, i.e. alias to array
|
||||
LOGGER.info("Model " + name + " not generated since it's an alias to array (without property) and `generateAliasAsModel` is set to false (default)");
|
||||
LOGGER.info("Model {} not generated since it's an alias to array (without property) and `generateAliasAsModel` is set to false (default)", name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -509,14 +529,12 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
// to generate model files
|
||||
generateModel(files, models, modelName);
|
||||
|
||||
if (generateModelTests) {
|
||||
// to generate model test files
|
||||
generateModelTests(files, models, modelName);
|
||||
}
|
||||
if (generateModelDocumentation) {
|
||||
// to generate model documentation files
|
||||
generateModelDocumentation(files, models, modelName);
|
||||
}
|
||||
// to generate model test files
|
||||
generateModelTests(files, models, modelName);
|
||||
|
||||
// to generate model documentation files
|
||||
generateModelDocumentation(files, models, modelName);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not generate model '" + modelName + "'", e);
|
||||
}
|
||||
@ -528,18 +546,21 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void generateApis(List<File> files, List<Object> allOperations, List<Object> allModels) {
|
||||
if (!generateApis) {
|
||||
// TODO: Process these anyway and present info via dryRun?
|
||||
LOGGER.info("Skipping generation of APIs.");
|
||||
return;
|
||||
}
|
||||
Map<String, List<CodegenOperation>> paths = processPaths(this.openAPI.getPaths());
|
||||
Set<String> apisToGenerate = null;
|
||||
String apiNames = GlobalSettings.getProperty("apis");
|
||||
if (apiNames != null && !apiNames.isEmpty()) {
|
||||
apisToGenerate = new HashSet<String>(Arrays.asList(apiNames.split(",")));
|
||||
apisToGenerate = new HashSet<>(Arrays.asList(apiNames.split(",")));
|
||||
}
|
||||
if (apisToGenerate != null && !apisToGenerate.isEmpty()) {
|
||||
Map<String, List<CodegenOperation>> updatedPaths = new TreeMap<String, List<CodegenOperation>>();
|
||||
Map<String, List<CodegenOperation>> updatedPaths = new TreeMap<>();
|
||||
for (String m : paths.keySet()) {
|
||||
if (apisToGenerate.contains(m)) {
|
||||
updatedPaths.put(m, paths.get(m));
|
||||
@ -550,12 +571,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
for (String tag : paths.keySet()) {
|
||||
try {
|
||||
List<CodegenOperation> ops = paths.get(tag);
|
||||
Collections.sort(ops, new Comparator<CodegenOperation>() {
|
||||
@Override
|
||||
public int compare(CodegenOperation one, CodegenOperation another) {
|
||||
return ObjectUtils.compare(one.operationId, another.operationId);
|
||||
}
|
||||
});
|
||||
ops.sort((one, another) -> ObjectUtils.compare(one.operationId, another.operationId));
|
||||
Map<String, Object> operation = processOperations(config, tag, ops, allModels);
|
||||
URL url = URLPathUtils.getServerURL(openAPI, config.serverVariableOverrides());
|
||||
operation.put("basePath", basePath);
|
||||
@ -583,7 +599,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
|
||||
// process top-level x-group-parameters
|
||||
if (config.vendorExtensions().containsKey("x-group-parameters")) {
|
||||
Boolean isGroupParameters = Boolean.valueOf(config.vendorExtensions().get("x-group-parameters").toString());
|
||||
boolean isGroupParameters = Boolean.parseBoolean(config.vendorExtensions().get("x-group-parameters").toString());
|
||||
|
||||
Map<String, Object> objectMap = (Map<String, Object>) operation.get("operations");
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -598,7 +614,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
// Pass sortParamsByRequiredFlag through to the Mustache template...
|
||||
boolean sortParamsByRequiredFlag = true;
|
||||
if (this.config.additionalProperties().containsKey(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG)) {
|
||||
sortParamsByRequiredFlag = Boolean.valueOf(this.config.additionalProperties().get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG).toString());
|
||||
sortParamsByRequiredFlag = Boolean.parseBoolean(this.config.additionalProperties().get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG).toString());
|
||||
}
|
||||
operation.put("sortParamsByRequiredFlag", sortParamsByRequiredFlag);
|
||||
|
||||
@ -607,7 +623,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
processMimeTypes(swagger.getProduces(), operation, "produces");
|
||||
*/
|
||||
|
||||
allOperations.add(new HashMap<String, Object>(operation));
|
||||
allOperations.add(new HashMap<>(operation));
|
||||
for (int i = 0; i < allOperations.size(); i++) {
|
||||
Map<String, Object> oo = (Map<String, Object>) allOperations.get(i);
|
||||
if (i < (allOperations.size() - 1)) {
|
||||
@ -617,56 +633,81 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
|
||||
for (String templateName : config.apiTemplateFiles().keySet()) {
|
||||
String filename = config.apiFilename(templateName, tag);
|
||||
if (!config.shouldOverwrite(filename) && new File(filename).exists()) {
|
||||
LOGGER.info("Skipped overwriting " + filename);
|
||||
File apiFile = new File(filename);
|
||||
if (!config.shouldOverwrite(filename) && apiFile.exists()) {
|
||||
LOGGER.info("Skipped overwriting {}", filename);
|
||||
if (dryRun) {
|
||||
DryRunStatus status = new DryRunStatus(apiFile.toPath(), DryRunStatus.State.SkippedOverwrite);
|
||||
dryRunStatusMap.put(filename, status);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
File written = processTemplateToFile(operation, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(written, "api");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (generateApiTests) {
|
||||
// to generate api test files
|
||||
for (String templateName : config.apiTestTemplateFiles().keySet()) {
|
||||
String filename = config.apiTestFilename(templateName, tag);
|
||||
// to generate api test files
|
||||
for (String templateName : config.apiTestTemplateFiles().keySet()) {
|
||||
String filename = config.apiTestFilename(templateName, tag);
|
||||
File apiTestFile = new File(filename);
|
||||
if (generateApiTests) {
|
||||
// do not overwrite test file that already exists
|
||||
if (new File(filename).exists()) {
|
||||
LOGGER.info("File exists. Skipped overwriting " + filename);
|
||||
if (apiTestFile.exists()) {
|
||||
LOGGER.info("File exists. Skipped overwriting {}", filename);
|
||||
if (dryRun) {
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(apiTestFile.toPath(), DryRunStatus.State.SkippedOverwrite));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
File written = processTemplateToFile(operation, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(written, "api-test");
|
||||
}
|
||||
}
|
||||
} else if (dryRun) {
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(
|
||||
apiTestFile.toPath(),
|
||||
DryRunStatus.State.Skipped,
|
||||
"Skipped by apiTests option supplied by user."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (generateApiDocumentation) {
|
||||
// to generate api documentation files
|
||||
for (String templateName : config.apiDocTemplateFiles().keySet()) {
|
||||
String filename = config.apiDocFilename(templateName, tag);
|
||||
if (!config.shouldOverwrite(filename) && new File(filename).exists()) {
|
||||
LOGGER.info("Skipped overwriting " + filename);
|
||||
// to generate api documentation files
|
||||
for (String templateName : config.apiDocTemplateFiles().keySet()) {
|
||||
String filename = config.apiDocFilename(templateName, tag);
|
||||
File apiDocFile = new File(filename);
|
||||
if (generateApiDocumentation) {
|
||||
if (!config.shouldOverwrite(filename) && apiDocFile.exists()) {
|
||||
LOGGER.info("Skipped overwriting {}", filename);
|
||||
if (dryRun) {
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(apiDocFile.toPath(), DryRunStatus.State.SkippedOverwrite));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
File written = processTemplateToFile(operation, templateName, filename);
|
||||
if (written != null) {
|
||||
files.add(written);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(written, "api-doc");
|
||||
}
|
||||
}
|
||||
} else if (dryRun) {
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(
|
||||
apiDocFile.toPath(),
|
||||
DryRunStatus.State.Skipped,
|
||||
"Skipped by apiDocs option supplied by user."
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,12 +724,14 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
|
||||
private void generateSupportingFiles(List<File> files, Map<String, Object> bundle) {
|
||||
if (!generateSupportingFiles) {
|
||||
// TODO: process these anyway and report via dryRun?
|
||||
LOGGER.info("Skipping generation of supporting files.");
|
||||
return;
|
||||
}
|
||||
Set<String> supportingFilesToGenerate = null;
|
||||
String supportingFiles = GlobalSettings.getProperty(CodegenConstants.SUPPORTING_FILES);
|
||||
if (supportingFiles != null && !supportingFiles.isEmpty()) {
|
||||
supportingFilesToGenerate = new HashSet<String>(Arrays.asList(supportingFiles.split(",")));
|
||||
supportingFilesToGenerate = new HashSet<>(Arrays.asList(supportingFiles.split(",")));
|
||||
}
|
||||
|
||||
for (SupportingFile support : config.supportingFiles()) {
|
||||
@ -699,13 +742,22 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
File of = new File(outputFolder);
|
||||
if (!of.isDirectory()) {
|
||||
of.mkdirs();
|
||||
if(!dryRun && !of.mkdirs()) {
|
||||
once(LOGGER).debug("Output directory {} not created. It {}.", outputFolder, of.exists() ? "already exists." : "may not have appropriate permissions.");
|
||||
}
|
||||
}
|
||||
String outputFilename = new File(support.destinationFilename).isAbsolute() // split
|
||||
? support.destinationFilename
|
||||
: outputFolder + File.separator + support.destinationFilename.replace('/', File.separatorChar);
|
||||
if (!config.shouldOverwrite(outputFilename)) {
|
||||
LOGGER.info("Skipped overwriting " + outputFilename);
|
||||
LOGGER.info("Skipped overwriting {}", outputFilename);
|
||||
if (dryRun) {
|
||||
Path skippedSupportingFile = java.nio.file.Paths.get(outputFilename);
|
||||
DryRunStatus status = new DryRunStatus(
|
||||
skippedSupportingFile,
|
||||
DryRunStatus.State.SkippedOverwrite
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String templateFile;
|
||||
@ -719,6 +771,15 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
shouldGenerate = supportingFilesToGenerate.contains(support.destinationFilename);
|
||||
}
|
||||
if (!shouldGenerate) {
|
||||
if (dryRun) {
|
||||
Path skippedSupportingFile = java.nio.file.Paths.get(outputFilename);
|
||||
DryRunStatus status = new DryRunStatus(
|
||||
skippedSupportingFile,
|
||||
DryRunStatus.State.Skipped,
|
||||
"Skipped by supportingFiles option supplied by user."
|
||||
);
|
||||
dryRunStatusMap.put(outputFilename, status);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -744,12 +805,16 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
File outputFile = writeInputStreamToFile(outputFilename, in, templateFile);
|
||||
files.add(outputFile);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(outputFile, "supporting-common");
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
LOGGER.info("Skipped generation of " + outputFilename + " due to rule in .openapi-generator-ignore");
|
||||
if (dryRun) {
|
||||
dryRunStatusMap.put(outputFilename, new DryRunStatus(java.nio.file.Paths.get(outputFilename), DryRunStatus.State.Ignored));
|
||||
}
|
||||
LOGGER.info("Skipped generation of {} due to rule in .openapi-generator-ignore", outputFilename);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not generate supporting file '" + support + "'", e);
|
||||
@ -770,23 +835,35 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
throw new RuntimeException("Could not generate supporting file '" + openapiGeneratorIgnore + "'", e);
|
||||
}
|
||||
files.add(ignoreFile);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(ignoreFile, "openapi-generator-ignore");
|
||||
}
|
||||
} else if (generateMetadata && dryRun && ignoreFile.exists()) {
|
||||
dryRunStatusMap.put(ignoreFileNameTarget, new DryRunStatus(ignoreFile.toPath(), DryRunStatus.State.SkippedOverwrite));
|
||||
} else if (!generateMetadata && dryRun) {
|
||||
dryRunStatusMap.put(ignoreFileNameTarget, new DryRunStatus(
|
||||
ignoreFile.toPath(),
|
||||
DryRunStatus.State.Skipped,
|
||||
"Skipped by generateMetadata option supplied by user"
|
||||
));
|
||||
}
|
||||
|
||||
String versionMetadata = config.outputFolder() + File.separator + ".openapi-generator" + File.separator + "VERSION";
|
||||
if (generateMetadata) {
|
||||
final String versionMetadata = config.outputFolder() + File.separator + ".openapi-generator" + File.separator + "VERSION";
|
||||
File versionMetadataFile = new File(versionMetadata);
|
||||
try {
|
||||
writeToFile(versionMetadata, ImplementationVersion.read());
|
||||
files.add(versionMetadataFile);
|
||||
if (config.isEnablePostProcessFile()) {
|
||||
if (config.isEnablePostProcessFile() && !dryRun) {
|
||||
config.postProcessFile(ignoreFile, "openapi-generator-version");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not generate supporting file '" + versionMetadata + "'", e);
|
||||
}
|
||||
} else if(!generateMetadata && dryRun) {
|
||||
Path metadata = java.nio.file.Paths.get(versionMetadata);
|
||||
DryRunStatus status = new DryRunStatus(metadata, DryRunStatus.State.Skipped, "Skipped by generateMetadata option supplied by user.");
|
||||
dryRunStatusMap.put(versionMetadata, status);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -809,23 +886,13 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
|
||||
}
|
||||
|
||||
protected File writeInputStreamToFile(String filename, InputStream in, String templateFile) throws FileNotFoundException, IOException {
|
||||
if (in != null) {
|
||||
byte bytes[] = IOUtils.toByteArray(in);
|
||||
return writeToFile(filename, bytes);
|
||||
} else {
|
||||
LOGGER.error("can't open '" + templateFile + "' for input; cannot write '" + filename + "'");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> buildSupportFileBundle(List<Object> allOperations, List<Object> allModels) {
|
||||
|
||||
Map<String, Object> bundle = new HashMap<String, Object>();
|
||||
bundle.putAll(config.additionalProperties());
|
||||
Map<String, Object> bundle = new HashMap<>(config.additionalProperties());
|
||||
bundle.put("apiPackage", config.apiPackage());
|
||||
|
||||
Map<String, Object> apis = new HashMap<String, Object>();
|
||||
Map<String, Object> apis = new HashMap<>();
|
||||
apis.put("apis", allOperations);
|
||||
|
||||
URL url = URLPathUtils.getServerURL(openAPI, config.serverVariableOverrides());
|
||||
@ -903,7 +970,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
|
||||
if (config.getGeneratorMetadata() == null) {
|
||||
LOGGER.warn(String.format(Locale.ROOT, "Generator '%s' is missing generator metadata!", config.getName()));
|
||||
LOGGER.warn("Generator '{}' is missing generator metadata!", config.getName());
|
||||
} else {
|
||||
GeneratorMetadata generatorMetadata = config.getGeneratorMetadata();
|
||||
if (StringUtils.isNotEmpty(generatorMetadata.getGenerationMessage())) {
|
||||
@ -929,13 +996,13 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
// If the template adapter is mustache, we'll set the config-modified Compiler.
|
||||
configPostProcessMustacheCompiler();
|
||||
|
||||
List<File> files = new ArrayList<File>();
|
||||
List<File> files = new ArrayList<>();
|
||||
// models
|
||||
List<String> filteredSchemas = ModelUtils.getSchemasUsedOnlyInFormParam(openAPI);
|
||||
List<Object> allModels = new ArrayList<Object>();
|
||||
List<Object> allModels = new ArrayList<>();
|
||||
generateModels(files, allModels, filteredSchemas);
|
||||
// apis
|
||||
List<Object> allOperations = new ArrayList<Object>();
|
||||
List<Object> allOperations = new ArrayList<>();
|
||||
generateApis(files, allOperations, allModels);
|
||||
|
||||
// supporting files
|
||||
@ -943,6 +1010,43 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
generateSupportingFiles(files, bundle);
|
||||
config.processOpenAPI(openAPI);
|
||||
|
||||
if(dryRun) {
|
||||
boolean verbose = Boolean.parseBoolean(GlobalSettings.getProperty("verbose"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(System.lineSeparator()).append(System.lineSeparator());
|
||||
sb.append("Dry Run Results:");
|
||||
sb.append(System.lineSeparator()).append(System.lineSeparator());
|
||||
|
||||
dryRunStatusMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
|
||||
DryRunStatus status = entry.getValue();
|
||||
try {
|
||||
status.appendTo(sb);
|
||||
sb.append(System.lineSeparator());
|
||||
if (verbose) {
|
||||
sb.append(" ")
|
||||
.append(StringUtils.rightPad(status.getState().getDescription(), 20, "."))
|
||||
.append(" ").append(status.getReason())
|
||||
.append(System.lineSeparator());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.debug("Unable to document dry run status for {}.", entry.getKey());
|
||||
}
|
||||
});
|
||||
|
||||
sb.append(System.lineSeparator()).append(System.lineSeparator());
|
||||
sb.append("States:");
|
||||
sb.append(System.lineSeparator()).append(System.lineSeparator());
|
||||
|
||||
for (DryRunStatus.State state : DryRunStatus.State.values()) {
|
||||
sb.append(" - ").append(state.getShortDisplay()).append(" ").append(state.getDescription()).append(System.lineSeparator());
|
||||
}
|
||||
|
||||
sb.append(System.lineSeparator());
|
||||
|
||||
System.err.println(sb.toString());
|
||||
}
|
||||
|
||||
// reset GlobalSettings, so that the running thread can be reused for another generator-run
|
||||
GlobalSettings.reset();
|
||||
|
||||
@ -968,18 +1072,22 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
|
||||
protected File processTemplateToFile(Map<String, Object> templateData, String templateName, String outputFilename) throws IOException {
|
||||
String adjustedOutputFilename = outputFilename.replaceAll("//", "/").replace('/', File.separatorChar);
|
||||
if (ignoreProcessor.allowsFile(new File(adjustedOutputFilename))) {
|
||||
File target = new File(adjustedOutputFilename);
|
||||
if (ignoreProcessor.allowsFile(target)) {
|
||||
String templateContent = templatingEngine.compileTemplate(this, templateData, templateName);
|
||||
writeToFile(adjustedOutputFilename, templateContent);
|
||||
return new File(adjustedOutputFilename);
|
||||
return target;
|
||||
} else if (this.dryRun) {
|
||||
dryRunStatusMap.put(adjustedOutputFilename, new DryRunStatus(target.toPath(), DryRunStatus.State.Ignored));
|
||||
return target;
|
||||
}
|
||||
|
||||
LOGGER.info("Skipped generation of " + adjustedOutputFilename + " due to rule in .openapi-generator-ignore");
|
||||
LOGGER.info("Skipped generation of {} due to rule in .openapi-generator-ignore", adjustedOutputFilename);
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<String, List<CodegenOperation>> processPaths(Paths paths) {
|
||||
Map<String, List<CodegenOperation>> ops = new TreeMap<String, List<CodegenOperation>>();
|
||||
Map<String, List<CodegenOperation>> ops = new TreeMap<>();
|
||||
for (String resourcePath : paths.keySet()) {
|
||||
PathItem path = paths.get(resourcePath);
|
||||
processOperation(resourcePath, "get", path.getGet(), ops, path);
|
||||
@ -1000,10 +1108,10 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
|
||||
if (GlobalSettings.getProperty("debugOperations") != null) {
|
||||
LOGGER.info("processOperation: resourcePath= " + resourcePath + "\t;" + httpMethod + " " + operation + "\n");
|
||||
LOGGER.info("processOperation: resourcePath= {}\t;{} {}\n", resourcePath, httpMethod, operation);
|
||||
}
|
||||
|
||||
List<Tag> tags = new ArrayList<Tag>();
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
List<String> tagNames = operation.getTags();
|
||||
List<Tag> swaggerTags = openAPI.getTags();
|
||||
if (tagNames != null) {
|
||||
@ -1038,7 +1146,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
per the swagger 2.0 spec "A unique parameter is defined by a combination of a name and location"
|
||||
i'm assuming "location" == "in"
|
||||
*/
|
||||
Set<String> operationParameters = new HashSet<String>();
|
||||
Set<String> operationParameters = new HashSet<>();
|
||||
if (operation.getParameters() != null) {
|
||||
for (Parameter parameter : operation.getParameters()) {
|
||||
operationParameters.add(generateParameterId(parameter));
|
||||
@ -1055,7 +1163,6 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, Schema> schemas = openAPI.getComponents() != null ? openAPI.getComponents().getSchemas() : null;
|
||||
final Map<String, SecurityScheme> securitySchemes = openAPI.getComponents() != null ? openAPI.getComponents().getSecuritySchemes() : null;
|
||||
final List<SecurityRequirement> globalSecurities = openAPI.getSecurity();
|
||||
for (Tag tag : tags) {
|
||||
@ -1102,14 +1209,15 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
return parameter.getName() + ":" + parameter.getIn();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> processOperations(CodegenConfig config, String tag, List<CodegenOperation> ops, List<Object> allModels) {
|
||||
Map<String, Object> operations = new HashMap<String, Object>();
|
||||
Map<String, Object> objs = new HashMap<String, Object>();
|
||||
Map<String, Object> operations = new HashMap<>();
|
||||
Map<String, Object> objs = new HashMap<>();
|
||||
objs.put("classname", config.toApiName(tag));
|
||||
objs.put("pathPrefix", config.toApiVarName(tag));
|
||||
|
||||
// check for operationId uniqueness
|
||||
Set<String> opIds = new HashSet<String>();
|
||||
Set<String> opIds = new HashSet<>();
|
||||
int counter = 0;
|
||||
for (CodegenOperation op : ops) {
|
||||
String opId = op.nickname;
|
||||
@ -1124,15 +1232,15 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
operations.put("operations", objs);
|
||||
operations.put("package", config.apiPackage());
|
||||
|
||||
Set<String> allImports = new TreeSet<String>();
|
||||
Set<String> allImports = new TreeSet<>();
|
||||
for (CodegenOperation op : ops) {
|
||||
allImports.addAll(op.imports);
|
||||
}
|
||||
|
||||
List<Map<String, String>> imports = new ArrayList<Map<String, String>>();
|
||||
List<Map<String, String>> imports = new ArrayList<>();
|
||||
Set<String> mappingSet = new TreeSet<>();
|
||||
for (String nextImport : allImports) {
|
||||
Map<String, String> im = new LinkedHashMap<String, String>();
|
||||
Map<String, String> im = new LinkedHashMap<>();
|
||||
String mapping = config.importMapping().get(nextImport);
|
||||
if (mapping == null) {
|
||||
mapping = config.toModelImport(nextImport);
|
||||
@ -1168,16 +1276,16 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
}
|
||||
|
||||
private Map<String, Object> processModels(CodegenConfig config, Map<String, Schema> definitions) {
|
||||
Map<String, Object> objs = new HashMap<String, Object>();
|
||||
Map<String, Object> objs = new HashMap<>();
|
||||
objs.put("package", config.modelPackage());
|
||||
List<Object> models = new ArrayList<Object>();
|
||||
Set<String> allImports = new LinkedHashSet<String>();
|
||||
List<Object> models = new ArrayList<>();
|
||||
Set<String> allImports = new LinkedHashSet<>();
|
||||
for (String key : definitions.keySet()) {
|
||||
Schema schema = definitions.get(key);
|
||||
if (schema == null)
|
||||
throw new RuntimeException("schema cannot be null in processModels");
|
||||
CodegenModel cm = config.fromModel(key, schema);
|
||||
Map<String, Object> mo = new HashMap<String, Object>();
|
||||
Map<String, Object> mo = new HashMap<>();
|
||||
mo.put("model", cm);
|
||||
mo.put("importPath", config.toModelImport(cm.classname));
|
||||
models.add(mo);
|
||||
@ -1187,7 +1295,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
allImports.addAll(cm.imports);
|
||||
}
|
||||
objs.put("models", models);
|
||||
Set<String> importSet = new TreeSet<String>();
|
||||
Set<String> importSet = new TreeSet<>();
|
||||
for (String nextImport : allImports) {
|
||||
String mapping = config.importMapping().get(nextImport);
|
||||
if (mapping == null) {
|
||||
@ -1202,9 +1310,9 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
importSet.add(mapping);
|
||||
}
|
||||
}
|
||||
List<Map<String, String>> imports = new ArrayList<Map<String, String>>();
|
||||
List<Map<String, String>> imports = new ArrayList<>();
|
||||
for (String s : importSet) {
|
||||
Map<String, String> item = new HashMap<String, String>();
|
||||
Map<String, String> item = new HashMap<>();
|
||||
item.put("import", s);
|
||||
imports.add(item);
|
||||
}
|
||||
@ -1295,7 +1403,7 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
return authMethods;
|
||||
}
|
||||
|
||||
List<CodegenSecurity> result = new ArrayList<CodegenSecurity>();
|
||||
List<CodegenSecurity> result = new ArrayList<>();
|
||||
|
||||
for (CodegenSecurity security : authMethods) {
|
||||
boolean filtered = false;
|
||||
@ -1356,4 +1464,51 @@ public class DefaultGenerator extends AbstractGenerator implements Generator {
|
||||
|
||||
return oauthMethods;
|
||||
}
|
||||
|
||||
protected File writeInputStreamToFile(String filename, InputStream in, String templateFile) throws IOException {
|
||||
if (in != null) {
|
||||
byte[] bytes = IOUtils.toByteArray(in);
|
||||
if (dryRun) {
|
||||
Path path = java.nio.file.Paths.get(filename);
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(path));
|
||||
return path.toFile();
|
||||
}
|
||||
|
||||
return writeToFile(filename, bytes);
|
||||
} else {
|
||||
LOGGER.error("can't open '{}' for input; cannot write '{}'", templateFile, filename);
|
||||
if (dryRun) {
|
||||
Path path = java.nio.file.Paths.get(filename);
|
||||
dryRunStatusMap.put(filename, new DryRunStatus(path, DryRunStatus.State.Error));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
public File writeToFile(String filename, byte[] contents) throws IOException {
|
||||
if (dryRun) {
|
||||
Path path = java.nio.file.Paths.get(filename);
|
||||
DryRunStatus status = new DryRunStatus(path);
|
||||
if (getEnableMinimalUpdate()) {
|
||||
status.setState(DryRunStatus.State.WriteIfNewer);
|
||||
} else {
|
||||
status.setState(DryRunStatus.State.Write);
|
||||
}
|
||||
|
||||
dryRunStatusMap.put(filename, status);
|
||||
return path.toFile();
|
||||
} else {
|
||||
return super.writeToFile(filename, contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,160 @@
|
||||
package org.openapitools.codegen;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Holds details about a file's write status for display via the --dry-run option of CLI
|
||||
*/
|
||||
class DryRunStatus {
|
||||
private Path path;
|
||||
private State state;
|
||||
private String reason;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link DryRunStatus} for a given path and status of {@link State#Write}
|
||||
*
|
||||
* @param path The target path where the file would write
|
||||
*/
|
||||
public DryRunStatus(Path path) {
|
||||
this(path, State.Write);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link DryRunStatus} for a path and a target state
|
||||
*
|
||||
* @param path The target path where the file would write
|
||||
* @param state The evaluated state as determined by the generation workflow
|
||||
*/
|
||||
public DryRunStatus(Path path, State state) {
|
||||
this.path = path;
|
||||
setState(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of {@link DryRunStatus} for a path, target state, and presenting a specific reason for that state
|
||||
*
|
||||
* @param path The target path where the file would write
|
||||
* @param state The evaluated state as determined by the generation workflow
|
||||
* @param reason A reason for the state, beyond any generic reason
|
||||
*/
|
||||
public DryRunStatus(Path path, State state, String reason) {
|
||||
this.path = path;
|
||||
this.state = state;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a user display text to the {@link Appendable} instance
|
||||
*
|
||||
* @param appendable An object implementing {@link Appendable} (such as {@link StringBuilder}
|
||||
* @throws IOException via contract of {@link Path#toAbsolutePath()}
|
||||
*/
|
||||
public void appendTo(Appendable appendable) throws IOException {
|
||||
appendable.append(String.format(Locale.ROOT, "%s %s", this.state.getShortDisplay(), this.path.toAbsolutePath()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the target path of the file write operation
|
||||
*
|
||||
* @return a {@link Path} instance
|
||||
*/
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reason for the file's {@link State}
|
||||
*
|
||||
* @return A human-readable string which explains why this file's dry-run resulted in the defined {@link State}
|
||||
*/
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link State} as determined by the generator's workflow
|
||||
*
|
||||
* @return A {@link State} enum detailing the expected operation of the generator's workflow
|
||||
*/
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link State} as determined by the generator's workflow.
|
||||
* <p>
|
||||
* This method will provide a default reason. To explicitly provide a reason for the {@link State}, use {@link DryRunStatus#DryRunStatus(Path, State, String)}
|
||||
*
|
||||
* @param state A {@link State} enum detailing the expected operation of the generator's workflow
|
||||
*/
|
||||
public void setState(State state) {
|
||||
if (state != this.state) {
|
||||
switch (state) {
|
||||
case Write:
|
||||
this.reason = "File will be written.";
|
||||
break;
|
||||
case WriteIfNewer:
|
||||
this.reason = "File will be written only if it is new or if contents differ from an existing file.";
|
||||
break;
|
||||
case Ignored:
|
||||
this.reason = "Ignored via rules defined in codegen ignore file.";
|
||||
break;
|
||||
case SkippedOverwrite:
|
||||
this.reason = "File is configured not to overwrite an existing file of the same name.";
|
||||
break;
|
||||
case Error:
|
||||
this.reason = "File error: template does not exist, or file is not accessible.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the possible states of a file write operation as determined by the Generator
|
||||
*/
|
||||
enum State {
|
||||
Write("w", "Write"),
|
||||
WriteIfNewer("n", "Write if New/Updated"),
|
||||
Ignored("i", "Ignored"),
|
||||
SkippedOverwrite("s", "Skipped Overwrite"),
|
||||
Skipped("k", "Skipped by user option(s)"),
|
||||
Error("e", "Error evaluating file write state");
|
||||
|
||||
private final String shortDisplay;
|
||||
private final String description;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link State} with required short value and human-readable description
|
||||
*
|
||||
* @param shortDisplay The short value used for display
|
||||
* @param description A description of the state which is more human-readable than the enum's name
|
||||
*/
|
||||
State(String shortDisplay, String description) {
|
||||
|
||||
this.shortDisplay = shortDisplay;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a description of the state which is more human-readable than the enum's name
|
||||
*
|
||||
* @return A human-readable description
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short value used for display
|
||||
*
|
||||
* @return A character representing this state
|
||||
*/
|
||||
public String getShortDisplay() {
|
||||
return shortDisplay;
|
||||
}
|
||||
}
|
||||
}
|
@ -439,6 +439,9 @@ public class CodegenConfigurator {
|
||||
GlobalSettings.setProperty("debugModels", "");
|
||||
GlobalSettings.setProperty("debugOperations", "");
|
||||
GlobalSettings.setProperty("debugSupportingFiles", "");
|
||||
GlobalSettings.setProperty("verbose", "true");
|
||||
} else {
|
||||
GlobalSettings.setProperty("verbose", "false");
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : workflowSettings.getSystemProperties().entrySet()) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user