Skip to content

[all] Adds strict spec option #2783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ SYNOPSIS
[--remove-operation-id-prefix]
[--reserved-words-mappings <reserved word mappings>...]
[(-s | --skip-overwrite)] [--skip-validate-spec]
[--strict-spec <true/false strict behavior>]
[(-t <template directory> | --template-dir <template directory>)]
[--type-mappings <type mappings>...] [(-v | --verbose)]

Expand Down
34 changes: 28 additions & 6 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@ SYNOPSIS
[--api-package <api package>] [--artifact-id <artifact id>]
[--artifact-version <artifact version>]
[(-c <configuration file> | --config <configuration file>)]
[-D <system properties>...] [--enable-post-process-file]
[-D <system properties>...]
[(-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>]
Expand All @@ -259,6 +261,7 @@ SYNOPSIS
[--remove-operation-id-prefix]
[--reserved-words-mappings <reserved word mappings>...]
[(-s | --skip-overwrite)] [--skip-validate-spec]
[--strict-spec <true/false strict behavior>]
[(-t <template directory> | --template-dir <template directory>)]
[--type-mappings <type mappings>...] [(-v | --verbose)]

Expand Down Expand Up @@ -289,21 +292,29 @@ OPTIONS
artifact version in generated pom.xml

-c <configuration file>, --config <configuration file>
Path to json configuration file. File content should be in a json
format {"optionKey":"optionValue", "optionKey1":"optionValue1"...}
Supported options can be different for each language. Run
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.

-D <system properties>
sets specified system properties in the format of
name=value,name=value (or multiple options, each with name=value)

-e <templating engine>, --engine <templating engine>
templating engine: "mustache" (default) or "handlebars" (beta)

--enable-post-process-file
enablePostProcessFile

-g <generator name>, --generator-name <generator name>
generator to use (see langs command for list)
generator to use (see list command for list)

--generate-alias-as-model
Generate alias to map, array as models

--git-repo-id <git repo id>
Git repo ID, e.g. openapi-generator.
Expand Down Expand Up @@ -354,6 +365,9 @@ OPTIONS
piping the JSON output of debug options (e.g. `-DdebugOperations`)
to an external parser directly while testing a generator.

--minimal-update
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.
Expand All @@ -368,6 +382,9 @@ OPTIONS
-o <output directory>, --output <output directory>
where to write the generated files (current dir by default)

--package-name <package name>
package for generated classes (where supported)

--release-note <release note>
Release note, default to 'Minor update'.

Expand All @@ -386,12 +403,17 @@ OPTIONS
--skip-validate-spec
Skips the default behavior of validating an input specification.

--strict-spec <true/false strict behavior>
'MUST' and 'SHALL' wording in OpenAPI spec is strictly adhered to.
e.g. when false, no fixes will be applied to documents which pass
validation but don't follow the spec.

-t <template directory>, --template-dir <template directory>
folder containing the template files

--type-mappings <type mappings>
sets mappings between OpenAPI spec types and generated code types in
the format of OpenaAPIType=generatedType,OpenAPIType=generatedType.
the format of OpenAPIType=generatedType,OpenAPIType=generatedType.
For example: array=List,map=Map,string=String. You can also have
multiple occurrences of this option.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ public class Generate implements Runnable {
description = "Skips the default behavior of validating an input specification.")
private Boolean skipValidateSpec;

@Option(name = {"--strict-spec"},
title = "true/false strict behavior",
description = "'MUST' and 'SHALL' wording in OpenAPI spec is strictly adhered to. e.g. when false, no fixes will be applied to documents which pass validation but don't follow the spec.",
arity = 1)
private Boolean strictSpecBehavior;

@Option(name = {"--log-to-stderr"},
title = "Log to STDERR",
description = "write all log messages (not just errors) to STDOUT."
Expand Down Expand Up @@ -368,10 +374,15 @@ public void run() {
if (generateAliasAsModel != null) {
configurator.setGenerateAliasAsModel(generateAliasAsModel);
}

if (minimalUpdate != null) {
configurator.setEnableMinimalUpdate(minimalUpdate);
}

if (strictSpecBehavior != null) {
configurator.setStrictSpecBehavior(strictSpecBehavior);
}

applySystemPropertiesKvpList(systemProperties, configurator);
applyInstantiationTypesKvpList(instantiationTypes, configurator);
applyImportMappingsKvpList(importMappings, configurator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,26 @@ public void testSkipOverwrite() throws Exception {
};
}

@Test
public void testStrictSpec() throws Exception {

setupAndRunGenericTest("--strict-spec", "true");
new FullVerifications() {
{
configurator.setStrictSpecBehavior(true);
times = 1;
}
};

setupAndRunGenericTest("--strict-spec", "false");
new FullVerifications() {
{
configurator.setStrictSpecBehavior(false);
times = 1;
}
};
}

@Test
public void testPackageName() throws Exception {
final String value = "io.foo.bar.baz";
Expand Down
1 change: 1 addition & 0 deletions modules/openapi-generator-maven-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ mvn clean compile
- `logToStderr` - write all log messages (not just errors) to STDOUT
- `enablePostProcessFile` - enable file post-processing hook
- `skipValidateSpec` - Whether or not to skip validating the input spec prior to generation. By default, invalid specifications will result in an error.
- `strictSpec` - Whether or not to treat an input document strictly against the spec. 'MUST' and 'SHALL' wording in OpenAPI spec is strictly adhered to. e.g. when false, no fixes will be applied to documents which pass validation but don't follow the spec.
- `generateAliasAsModel` - generate alias (array, map) as model
- `generateApis` - generate the apis (`true` by default)
- `generateApiTests` - generate the api tests (`true` by default. Only available if `generateApis` is `true`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ public class CodeGenMojo extends AbstractMojo {
@Parameter(name = "skipValidateSpec", required = false)
private Boolean skipValidateSpec;

/**
* To treat a document strictly against the spec.
*/
@Parameter(name = "strictSpec", required = false)
private Boolean strictSpecBehavior;

/**
* To generate alias (array, map) as model
*/
Expand Down Expand Up @@ -459,6 +465,10 @@ public void execute() throws MojoExecutionException {
configurator.setValidateSpec(!skipValidateSpec);
}

if (strictSpecBehavior != null) {
configurator.setStrictSpecBehavior(strictSpecBehavior);
}

if (logToStderr != null) {
configurator.setLogToStderr(logToStderr);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,8 @@ public interface CodegenConfig {
public boolean isEnableMinimalUpdate();

public void setEnableMinimalUpdate(boolean isEnableMinimalUpdate);

boolean isStrictSpecBehavior();

void setStrictSpecBehavior(boolean strictSpecBehavior);
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ public class DefaultCodegen implements CodegenConfig {
// flag to indicate whether to only update files whose contents have changed
protected boolean enableMinimalUpdate = false;

// acts strictly upon a spec, potentially modifying it to have consistent behavior across generators.
protected boolean strictSpecBehavior = true;

// make openapi available to all methods
protected OpenAPI openAPI;

Expand Down Expand Up @@ -2387,11 +2390,13 @@ public CodegenOperation fromOperation(String path,
}
operationId = removeNonNameElementToCamelCase(operationId);

if (path.startsWith("/")) {
op.path = path;
} else {
if (isStrictSpecBehavior() && !path.startsWith("/")) {
// modifies an operation.path to strictly conform to OpenAPI Spec
op.path = "/" + path;
} else {
op.path = path;
}

op.operationId = toOperationId(operationId);
op.summary = escapeText(operation.getSummary());
op.unescapedNotes = operation.getDescription();
Expand Down Expand Up @@ -4900,4 +4905,24 @@ public void setEnableMinimalUpdate(boolean enableMinimalUpdate) {
this.enableMinimalUpdate = enableMinimalUpdate;
}

/**
* Indicates whether the codegen configuration should treat documents as strictly defined by the OpenAPI specification.
*
* @return true to act strictly upon spec documents, potentially modifying the spec to strictly fit the spec.
*/
@Override
public boolean isStrictSpecBehavior() {
return this.strictSpecBehavior;
}

/**
* Sets the boolean valid indicating whether generation will work strictly against the specification, potentially making
* minor changes to the input document.
*
* @param strictSpecBehavior true if we will behave strictly, false to allow specification documents which pass validation to be loosely interpreted against the spec.
*/
@Override
public void setStrictSpecBehavior(final boolean strictSpecBehavior) {
this.strictSpecBehavior = strictSpecBehavior;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public class CodegenConfigurator implements Serializable {
private boolean validateSpec;
private boolean enablePostProcessFile;
private boolean enableMinimalUpdate;
private boolean strictSpecBehavior;
private String templateDir;
private String templatingEngineName;
private String auth;
Expand Down Expand Up @@ -117,6 +118,7 @@ public class CodegenConfigurator implements Serializable {

public CodegenConfigurator() {
this.validateSpec = true;
this.strictSpecBehavior = true;
this.setOutputDir(".");
}

Expand Down Expand Up @@ -252,6 +254,15 @@ public CodegenConfigurator setModelNameSuffix(String suffix) {
return this;
}

public boolean isStrictSpecBehavior() {
return strictSpecBehavior;
}

public CodegenConfigurator setStrictSpecBehavior(boolean strictSpecBehavior) {
this.strictSpecBehavior = strictSpecBehavior;
return this;
}

public boolean isVerbose() {
return verbose;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,35 @@ public void testProcessPaths() throws Exception {
Assert.assertEquals(defaultList.get(3).path, "/path4");
Assert.assertEquals(defaultList.get(3).allParams.size(), 1);
}



@Test
public void testNonStrictProcessPaths() throws Exception {
OpenAPI openAPI = TestUtils.createOpenAPI();
openAPI.setPaths(new Paths());
openAPI.getPaths().addPathItem("path1/", new PathItem().get(new Operation().operationId("op1").responses(new ApiResponses().addApiResponse("201", new ApiResponse().description("OK")))));
openAPI.getPaths().addPathItem("path2/", new PathItem().get(new Operation().operationId("op2").addParametersItem(new QueryParameter().name("p1").schema(new StringSchema())).responses(new ApiResponses().addApiResponse("201", new ApiResponse().description("OK")))));

ClientOptInput opts = new ClientOptInput();
opts.setOpenAPI(openAPI);
CodegenConfig config = new DefaultCodegen();
config.setStrictSpecBehavior(false);
opts.setConfig(config);
opts.setOpts(new ClientOpts());

DefaultGenerator generator = new DefaultGenerator();
generator.opts(opts);
Map<String, List<CodegenOperation>> result = generator.processPaths(openAPI.getPaths());
Assert.assertEquals(result.size(), 1);
List<CodegenOperation> defaultList = result.get("Default");
Assert.assertEquals(defaultList.size(), 2);
Assert.assertEquals(defaultList.get(0).path, "path1/");
Assert.assertEquals(defaultList.get(0).allParams.size(), 0);
Assert.assertEquals(defaultList.get(1).path, "path2/");
Assert.assertEquals(defaultList.get(1).allParams.size(), 1);
}


@Test
public void minimalUpdateTest() throws IOException {
OpenAPI openAPI = TestUtils.createOpenAPI();
Expand Down