Merge remote-tracking branch 'origin/master' into 4.3.x

This commit is contained in:
William Cheng 2019-12-28 12:00:09 +08:00
commit 81e15ed0bf
761 changed files with 40748 additions and 6074 deletions

View File

@ -628,6 +628,7 @@ Here are some companies/projects (alphabetical order) using OpenAPI Generator in
- [TUI InfoTec GmbH](http://www.tui-infotec.com/)
- [unblu inc.](https://www.unblu.com/)
- [Veamly](https://www.veamly.com/)
- [Woleet](https://www.woleet.io/)
- [WSO2](https://wso2.com/)
- [Vouchery.io](https://vouchery.io)
- [Xero](https://www.xero.com/)

View File

@ -26,6 +26,6 @@ fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="$@ generate -t modules/openapi-generator/src/main/resources/asciidoc-documentation --additional-properties=specDir=modules/openapi-generator/src/main/resources/asciidoc-documentation,snippetDir=. -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g asciidoc -o samples/documentation/asciidoc"
ags="generate -t modules/openapi-generator/src/main/resources/asciidoc-documentation --additional-properties=specDir=modules/openapi-generator/src/main/resources/asciidoc-documentation,snippetDir=. -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g asciidoc -o samples/documentation/asciidoc $@"
java ${JAVA_OPTS} -jar ${executable} ${ags}

View File

@ -0,0 +1,34 @@
#!/bin/sh
SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"
while [ -h "$SCRIPT" ] ; do
ls=$(ls -ld "$SCRIPT")
link=$(expr "$ls" : '.*-> \(.*\)$')
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=$(dirname "$SCRIPT")/"$link"
fi
done
if [ ! -d "${APP_DIR}" ]; then
APP_DIR=$(dirname "$SCRIPT")/..
APP_DIR=$(cd "${APP_DIR}"; pwd)
fi
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
if [ ! -f "$executable" ]
then
mvn -B clean package
fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -t modules/openapi-generator/src/main/resources/kotlin-client -i modules/openapi-generator/src/test/resources/3_0/issue-4062.yaml -g kotlin --artifact-id kotlin-uppercase-enum --additional-properties enumPropertyNaming=UPPERCASE -o samples/client/petstore/kotlin-uppercase-enum $@"
java ${JAVA_OPTS} -jar ${executable} ${ags}
#cp CI/samples.ci/client/petstore/kotlin-uppercase-enum/pom.xml samples/client/petstore/kotlin-uppercase-enum/pom.xml

View File

@ -3,3 +3,4 @@
./bin/python-server-aiohttp-petstore.sh
./bin/python-server-flask-petstore.sh
./bin/python-server-flask-petstore-python2.sh
./bin/python-server-blueplanet-petstore.sh

View File

@ -20,5 +20,6 @@ sidebar_label: asciidoc
|groupId|groupId in generated pom.xml| |null|
|artifactId|artifactId in generated pom.xml. This also becomes part of the generated library's filename| |null|
|artifactVersion|artifact version in generated pom.xml. This also becomes part of the generated library's filename| |null|
|snippetDir|path with includable markup snippets (e.g. test output generated by restdoc, default: .| |.|
|specDir|path with includable markup spec files (e.g. handwritten additional docs, default: .| |..|
|snippetDir|path with includable markup snippets (e.g. test output generated by restdoc, default: .)| |.|
|specDir|path with includable markup spec files (e.g. handwritten additional docs, default: ..)| |..|
|headerAttributes|generation of asciidoc header meta data attributes (set to false to suppress, default: true)| |true|

View File

@ -14,6 +14,9 @@ sidebar_label: dart-dio
|pubName|Name in generated pubspec| |null|
|pubVersion|Version in generated pubspec| |null|
|pubDescription|Description in generated pubspec| |null|
|pubAuthor|Author name in generated pubspec| |null|
|pubAuthorEmail|Email address of the author in generated pubspec| |null|
|pubHomepage|Homepage in generated pubspec| |null|
|useEnumExtension|Allow the 'x-enum-values' extension for enums| |null|
|sourceFolder|Source folder for generated code| |null|
|supportDart2|Support Dart 2.x (Dart 1.x support has been deprecated)| |true|

View File

@ -14,6 +14,9 @@ sidebar_label: dart-jaguar
|pubName|Name in generated pubspec| |null|
|pubVersion|Version in generated pubspec| |null|
|pubDescription|Description in generated pubspec| |null|
|pubAuthor|Author name in generated pubspec| |null|
|pubAuthorEmail|Email address of the author in generated pubspec| |null|
|pubHomepage|Homepage in generated pubspec| |null|
|useEnumExtension|Allow the 'x-enum-values' extension for enums| |null|
|sourceFolder|Source folder for generated code| |null|
|supportDart2|Support Dart 2.x (Dart 1.x support has been deprecated)| |true|

View File

@ -14,6 +14,9 @@ sidebar_label: dart
|pubName|Name in generated pubspec| |null|
|pubVersion|Version in generated pubspec| |null|
|pubDescription|Description in generated pubspec| |null|
|pubAuthor|Author name in generated pubspec| |null|
|pubAuthorEmail|Email address of the author in generated pubspec| |null|
|pubHomepage|Homepage in generated pubspec| |null|
|useEnumExtension|Allow the 'x-enum-values' extension for enums| |null|
|sourceFolder|Source folder for generated code| |null|
|supportDart2|Support Dart 2.x (Dart 1.x support has been deprecated)| |true|

View File

@ -13,4 +13,5 @@ sidebar_label: go-experimental
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|
|enumClassPrefix|Prefix enum with class name| |false|
|structPrefix|whether to prefix struct with the class name. e.g. DeletePetOpts => PetApiDeletePetOpts| |false|
|withAWSV4Signature|whether to include AWS v4 signature support| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|

View File

@ -13,4 +13,5 @@ sidebar_label: go
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|
|enumClassPrefix|Prefix enum with class name| |false|
|structPrefix|whether to prefix struct with the class name. e.g. DeletePetOpts => PetApiDeletePetOpts| |false|
|withAWSV4Signature|whether to include AWS v4 signature support| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|

View File

@ -7,3 +7,4 @@ sidebar_label: mysql-schema
| ------ | ----------- | ------ | ------- |
|defaultDatabaseName|Default database name for all MySQL queries| ||
|jsonDataTypeEnabled|Use special JSON MySQL data type for complex model properties. Requires MySQL version 5.7.8. Generates TEXT data type when disabled| |true|
|identifierNamingConvention|Naming convention of MySQL identifiers(table names and column names). This is not related to database name which is defined by defaultDatabaseName option|<dl><dt>**original**</dt><dd>Do not transform original names</dd><dt>**snake_case**</dt><dd>Use snake_case names</dd><dl>|original|

View File

@ -16,3 +16,4 @@ sidebar_label: python-aiohttp
|defaultController|default controller| |default_controller|
|supportPython2|support python2| |false|
|serverPort|TCP port to listen to in app.run| |8080|
|useNose|use the nose test framework| |false|

View File

@ -16,3 +16,4 @@ sidebar_label: python-blueplanet
|defaultController|default controller| |default_controller|
|supportPython2|support python2| |false|
|serverPort|TCP port to listen to in app.run| |8080|
|useNose|use the nose test framework| |false|

View File

@ -12,4 +12,5 @@ sidebar_label: python-experimental
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|generateSourceCodeOnly|Specifies that only a library source code is to be generated.| |false|
|useNose|use the nose test framework| |false|
|library|library template (sub-template) to use: asyncio, tornado, urllib3| |urllib3|

View File

@ -16,3 +16,4 @@ sidebar_label: python-flask
|defaultController|default controller| |default_controller|
|supportPython2|support python2| |false|
|serverPort|TCP port to listen to in app.run| |8080|
|useNose|use the nose test framework| |false|

View File

@ -12,4 +12,5 @@ sidebar_label: python
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|generateSourceCodeOnly|Specifies that only a library source code is to be generated.| |false|
|useNose|use the nose test framework| |false|
|library|library template (sub-template) to use: asyncio, tornado, urllib3| |urllib3|

View File

@ -69,6 +69,9 @@ public class CodegenConstants {
public static final String WITH_GO_CODEGEN_COMMENT = "withGoCodegenComment";
public static final String WITH_GO_CODEGEN_COMMENT_DESC = "whether to include Go codegen comment to disable Go Lint and collapse by default GitHub in PRs and diffs";
public static final String WITH_AWSV4_SIGNATURE_COMMENT = "withAWSV4Signature";
public static final String WITH_AWSV4_SIGNATURE_COMMENT_DESC = "whether to include AWS v4 signature support";
public static final String IS_GO_SUBMODULE = "isGoSubmodule";
public static final String IS_GO_SUBMODULE_DESC = "whether the generated Go module is a submodule";

View File

@ -1876,7 +1876,7 @@ public class DefaultCodegen implements CodegenConfig {
// parent model
final String parentName = ModelUtils.getParentName(composed, allDefinitions);
final List<String> allParents = ModelUtils.getAllParentsName(composed, allDefinitions);
final List<String> allParents = ModelUtils.getAllParentsName(composed, allDefinitions, false);
final Schema parent = StringUtils.isBlank(parentName) || allDefinitions == null ? null : allDefinitions.get(parentName);
// TODO revise the logic below to set dicriminator, xml attributes
@ -2083,10 +2083,8 @@ public class DefaultCodegen implements CodegenConfig {
Map<String, Schema> allDefinitions = ModelUtils.getSchemas(this.openAPI);
allDefinitions.forEach((childName, child) -> {
if (child instanceof ComposedSchema && ((ComposedSchema) child).getAllOf() != null) {
Set<String> parentSchemas = ((ComposedSchema) child).getAllOf().stream()
.filter(s -> s.get$ref() != null)
.map(s -> ModelUtils.getSimpleRef(s.get$ref()))
.collect(Collectors.toSet());
final List<String> parentSchemas = ModelUtils.getAllParentsName((ComposedSchema) child, allDefinitions, true);
if (parentSchemas.contains(schemaName)) {
discriminator.getMappedModels().add(new MappedModel(childName, toModelName(childName)));
}

View File

@ -26,6 +26,7 @@ import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
@ -163,7 +164,7 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
addOption(CodegenConstants.PROJECT_NAME, "GNAT project name",
this.projectName);
modelNameSuffix = "_Type";
modelNameSuffix = "Type";
embeddedTemplateDir = templateDir = "Ada";
languageSpecificPrimitives = new HashSet<String>(
@ -242,11 +243,37 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
* @return capitalized model name
*/
public String toModelName(final String name) {
String result = super.toModelName(name);
if (result.matches("^\\d.*") || result.startsWith("_")) {
result = "Model_" + result;
String result = camelize(sanitizeName(name));
if (!StringUtils.isEmpty(modelNamePrefix)) {
result = modelNamePrefix + "_" + result;
}
return result.replaceAll("[\\.-]", "_").replaceAll("__+", "_");
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(name)) {
String modelName = "Model_" + result;
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
// model name starts with number
if (result.matches("^\\d.*")) {
String modelName = "Model_" + result; // e.g. 200Response => Model_200Response (after camelize)
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
return modelName;
}
if (languageSpecificPrimitives.contains(result)) {
String modelName = "Model_" + result;
LOGGER.warn(name + " (model name matches existing language type) cannot be used as a model name. Renamed to " + modelName);
return modelName;
}
if (!StringUtils.isEmpty(modelNameSuffix)) {
result = result + "_" + modelNameSuffix;
}
return result;
}
@Override
@ -517,7 +544,7 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
if (v instanceof CodegenModel) {
CodegenModel m = (CodegenModel) v;
List<String> d = new ArrayList<String>();
for (CodegenProperty p : m.allVars) {
for (CodegenProperty p : m.vars) {
boolean isModel = false;
CodegenProperty item = p;
if (p.isContainer) {
@ -531,9 +558,10 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
isModel = true;
}
p.vendorExtensions.put("x-is-model-type", isModel);
Boolean required = p.getRequired();
// Convert optional members to use the Nullable_<T> type.
if (!p.required && nullableTypeMapping.containsKey(p.dataType)) {
if (!Boolean.TRUE.equals(required) && nullableTypeMapping.containsKey(p.dataType)) {
p.dataType = nullableTypeMapping.get(p.dataType);
}
}

View File

@ -477,7 +477,7 @@ public abstract class AbstractEiffelCodegen extends DefaultCodegen implements Co
// Because the child models extend the parents, the enums will be available via the parent.
// Only bother with reconciliation if the parent model has enums.
if (!parentCodegenModel.hasEnums) {
if (parentCodegenModel == null || !parentCodegenModel.hasEnums) {
return codegenModel;
}

View File

@ -38,6 +38,7 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege
private static final String NUMERIC_ENUM_PREFIX = "_";
protected boolean withGoCodegenComment = false;
protected boolean withAWSV4Signature = false;
protected boolean withXml = false;
protected boolean enumClassPrefix = false;
protected boolean structPrefix = false;
@ -633,6 +634,10 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege
this.withGoCodegenComment = withGoCodegenComment;
}
public void setWithAWSV4Signature(boolean withAWSV4Signature) {
this.withAWSV4Signature = withAWSV4Signature;
}
public void setWithXml(boolean withXml) {
this.withXml = withXml;
}

View File

@ -93,58 +93,24 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
// this includes hard reserved words defined by https://github.com/JetBrains/kotlin/blob/master/core/descriptors/src/org/jetbrains/kotlin/renderer/KeywordStringsGenerated.java
// as well as keywords from https://kotlinlang.org/docs/reference/keyword-reference.html
reservedWords = new HashSet<String>(Arrays.asList(
"abstract",
"annotation",
"as",
"break",
"case",
"catch",
"class",
"companion",
"const",
"constructor",
"continue",
"crossinline",
"data",
"delegate",
"do",
"else",
"enum",
"external",
"false",
"final",
"finally",
"for",
"fun",
"if",
"in",
"infix",
"init",
"inline",
"inner",
"interface",
"internal",
"is",
"it",
"lateinit",
"lazy",
"noinline",
"null",
"object",
"open",
"operator",
"out",
"override",
"package",
"private",
"protected",
"public",
"reified",
"return",
"sealed",
"super",
"suspend",
"tailrec",
"this",
"throw",
"true",
@ -153,7 +119,6 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
"typeof",
"val",
"var",
"vararg",
"when",
"while"
));
@ -565,7 +530,7 @@ public abstract class AbstractKotlinCodegen extends DefaultCodegen implements Co
modified = underscore(modified);
break;
case UPPERCASE:
modified = modified.toUpperCase(Locale.ROOT);
modified = underscore(modified).toUpperCase(Locale.ROOT);
break;
}

View File

@ -46,6 +46,7 @@ public class AsciidocDocumentationCodegen extends DefaultCodegen implements Code
public static final String SPEC_DIR = "specDir";
public static final String SNIPPET_DIR = "snippetDir";
public static final String HEADER_ATTRIBUTES_FLAG = "headerAttributes";
/**
* Lambda emitting an asciidoc "include::filename.adoc[]" if file is found in
@ -140,6 +141,7 @@ public class AsciidocDocumentationCodegen extends DefaultCodegen implements Code
protected String groupId = "org.openapitools";
protected String artifactId = "openapi-client";
protected String artifactVersion = "1.0.0";
protected boolean headerAttributes = true;
private IncludeMarkupLambda includeSpecMarkupLambda;
private IncludeMarkupLambda includeSnippetMarkupLambda;
@ -201,11 +203,14 @@ public class AsciidocDocumentationCodegen extends DefaultCodegen implements Code
cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_VERSION, CodegenConstants.ARTIFACT_VERSION_DESC));
cliOptions.add(new CliOption(SNIPPET_DIR,
"path with includable markup snippets (e.g. test output generated by restdoc, default: .")
"path with includable markup snippets (e.g. test output generated by restdoc, default: .)")
.defaultValue("."));
cliOptions.add(new CliOption(SPEC_DIR,
"path with includable markup spec files (e.g. handwritten additional docs, default: .")
"path with includable markup spec files (e.g. handwritten additional docs, default: ..)")
.defaultValue(".."));
cliOptions.add(CliOption.newBoolean(HEADER_ATTRIBUTES_FLAG,
"generation of asciidoc header meta data attributes (set to false to suppress, default: true)",
true));
additionalProperties.put("appName", "OpenAPI Sample description");
additionalProperties.put("appDescription", "A sample OpenAPI documentation");
@ -236,6 +241,14 @@ public class AsciidocDocumentationCodegen extends DefaultCodegen implements Code
return input; // just return the original string
}
public boolean isHeaderAttributes() {
return headerAttributes;
}
public void setHeaderAttributes(boolean headerAttributes) {
this.headerAttributes = headerAttributes;
}
@Override
public void processOpts() {
super.processOpts();
@ -260,6 +273,13 @@ public class AsciidocDocumentationCodegen extends DefaultCodegen implements Code
this.linkSnippetMarkupLambda = new LinkMarkupLambda(snippetDir);
additionalProperties.put("snippetlink", this.linkSnippetMarkupLambda);
if (additionalProperties.containsKey(HEADER_ATTRIBUTES_FLAG)) {
this.setHeaderAttributes(convertPropertyToBooleanAndWriteBack(HEADER_ATTRIBUTES_FLAG));
} else {
additionalProperties.put(HEADER_ATTRIBUTES_FLAG, headerAttributes);
}
}
@Override

View File

@ -572,7 +572,7 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
}
for (final CodegenProperty property : codegenModel.readWriteVars) {
if (property.defaultValue == null && property.baseName.equals(parentCodegenModel.discriminator.getPropertyName())) {
if (property.defaultValue == null && parentCodegenModel.discriminator != null && property.baseName.equals(parentCodegenModel.discriminator.getPropertyName())) {
property.defaultValue = "\"" + name + "\"";
}
}

View File

@ -252,7 +252,7 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen {
}
for (final CodegenProperty property : codegenModel.readWriteVars) {
if (property.defaultValue == null && property.baseName.equals(parentCodegenModel.discriminator.getPropertyName())) {
if (property.defaultValue == null && parentCodegenModel.discriminator != null && property.baseName.equals(parentCodegenModel.discriminator.getPropertyName())) {
property.defaultValue = "\"" + name + "\"";
}
}

View File

@ -74,16 +74,22 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
public static final String PUB_NAME = "pubName";
public static final String PUB_VERSION = "pubVersion";
public static final String PUB_DESCRIPTION = "pubDescription";
public static final String PUB_AUTHOR = "pubAuthor";
public static final String PUB_AUTHOR_EMAIL = "pubAuthorEmail";
public static final String PUB_HOMEPAGE = "pubHomepage";
public static final String USE_ENUM_EXTENSION = "useEnumExtension";
public static final String SUPPORT_DART2 = "supportDart2";
protected boolean browserClient = true;
protected String pubName = "openapi";
protected String pubVersion = "1.0.0";
protected String pubDescription = "OpenAPI API client";
protected String pubAuthor = "Author";
protected String pubAuthorEmail = "author@homepage";
protected String pubHomepage = "homepage";
protected boolean useEnumExtension = false;
protected String sourceFolder = "";
protected String apiDocPath = "docs" + File.separator;
protected String modelDocPath = "docs" + File.separator;
protected String apiDocPath = "doc" + File.separator;
protected String modelDocPath = "doc" + File.separator;
protected String apiTestPath = "test" + File.separator;
protected String modelTestPath = "test" + File.separator;
@ -108,7 +114,9 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
List<String> reservedWordsList = new ArrayList<String>();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(DartClientCodegen.class.getResourceAsStream("/dart/dart-keywords.txt"), Charset.forName("UTF-8")));
while(reader.ready()) { reservedWordsList.add(reader.readLine()); }
while (reader.ready()) {
reservedWordsList.add(reader.readLine());
}
reader.close();
} catch (Exception e) {
LOGGER.error("Error reading dart keywords. Exception: {}", e.getMessage());
@ -153,6 +161,9 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
cliOptions.add(new CliOption(PUB_NAME, "Name in generated pubspec"));
cliOptions.add(new CliOption(PUB_VERSION, "Version in generated pubspec"));
cliOptions.add(new CliOption(PUB_DESCRIPTION, "Description in generated pubspec"));
cliOptions.add(new CliOption(PUB_AUTHOR, "Author name in generated pubspec"));
cliOptions.add(new CliOption(PUB_AUTHOR_EMAIL, "Email address of the author in generated pubspec"));
cliOptions.add(new CliOption(PUB_HOMEPAGE, "Homepage in generated pubspec"));
cliOptions.add(new CliOption(USE_ENUM_EXTENSION, "Allow the 'x-enum-values' extension for enums"));
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, "Source folder for generated code"));
cliOptions.add(CliOption.newBoolean(SUPPORT_DART2, "Support Dart 2.x (Dart 1.x support has been deprecated)").defaultValue(Boolean.TRUE.toString()));
@ -214,6 +225,27 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
additionalProperties.put(PUB_DESCRIPTION, pubDescription);
}
if (additionalProperties.containsKey(PUB_AUTHOR)) {
this.setPubAuthor((String) additionalProperties.get(PUB_AUTHOR));
} else {
//not set, use to be passed to template
additionalProperties.put(PUB_AUTHOR, pubAuthor);
}
if (additionalProperties.containsKey(PUB_AUTHOR_EMAIL)) {
this.setPubAuthorEmail((String) additionalProperties.get(PUB_AUTHOR_EMAIL));
} else {
//not set, use to be passed to template
additionalProperties.put(PUB_AUTHOR_EMAIL, pubAuthorEmail);
}
if (additionalProperties.containsKey(PUB_HOMEPAGE)) {
this.setPubHomepage((String) additionalProperties.get(PUB_HOMEPAGE));
} else {
//not set, use to be passed to template
additionalProperties.put(PUB_HOMEPAGE, pubHomepage);
}
if (additionalProperties.containsKey(USE_ENUM_EXTENSION)) {
this.setUseEnumExtension(convertPropertyToBooleanAndWriteBack(USE_ENUM_EXTENSION));
} else {
@ -551,6 +583,18 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
this.pubDescription = pubDescription;
}
public void setPubAuthor(String pubAuthor) {
this.pubAuthor = pubAuthor;
}
public void setPubAuthorEmail(String pubAuthorEmail) {
this.pubAuthorEmail = pubAuthorEmail;
}
public void setPubHomepage(String pubHomepage) {
this.pubHomepage = pubHomepage;
}
public void setUseEnumExtension(boolean useEnumExtension) {
this.useEnumExtension = useEnumExtension;
}

View File

@ -17,6 +17,7 @@
package org.openapitools.codegen.languages;
import java.util.HashMap;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConstants;
@ -121,7 +122,8 @@ public class DartDioClientCodegen extends DartClientCodegen {
return "Generates a Dart Dio client library.";
}
@Override public void setBrowserClient(boolean browserClient) {
@Override
public void setBrowserClient(boolean browserClient) {
super.browserClient = browserClient;
}
@ -285,37 +287,26 @@ public class DartDioClientCodegen extends DartClientCodegen {
property.isNullable = true;
}
if (property.isListContainer) {
//Updates any List properties on a model to a BuiltList. This happens in post processing rather
//than type mapping as we only want this to apply to models, not every other class.
if ("List".equals(property.baseType)) {
property.setDatatype(
property.dataType.replaceAll(property.baseType, "BuiltList"));
property.setBaseType("BuiltList");
model.imports.add("BuiltList");
if ("Object".equals(property.items.baseType)) {
property.setDatatype(
property.dataType.replaceAll("Object", "JsonObject"));
property.items.setDatatype("JsonObject");
model.imports.add("JsonObject");
}
}
}
if (property.isMapContainer) {
//Updates any List properties on a model to a BuiltList. This happens in post processing rather
//than type mapping as we only want this to apply to models, not every other class.
if ("Map".equals(property.baseType)) {
property.setDatatype(property.dataType.replaceAll(property.baseType, "BuiltMap"));
property.setBaseType("BuiltMap");
model.imports.add("BuiltMap");
if ("Object".equals(property.items.baseType)) {
property.setDatatype(property.dataType.replaceAll("Object", "JsonObject"));
property.items.setDatatype("JsonObject");
model.imports.add("JsonObject");
}
}
}
property.setDatatype(property.getDataType()
.replaceAll("\\bList\\b", "BuiltList")
.replaceAll("\\bMap\\b", "BuiltMap")
.replaceAll("\\bObject\\b", "JsonObject")
);
property.setBaseType(property.getBaseType()
.replaceAll("\\bList\\b", "BuiltList")
.replaceAll("\\bMap\\b", "BuiltMap")
.replaceAll("\\bObject\\b", "JsonObject")
);
if (property.dataType.contains("BuiltList")) {
model.imports.add("BuiltList");
}
if (property.dataType.contains("BuiltMap")) {
model.imports.add("BuiltMap");
}
if (property.dataType.contains("JsonObject")) {
model.imports.add("JsonObject");
}
}
@Override

View File

@ -37,6 +37,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
public static final String WITH_GO_CODEGEN_COMMENT = "withGoCodegenComment";
public static final String WITH_XML = "withXml";
public static final String STRUCT_PREFIX = "structPrefix";
public static final String WITH_AWSV4_SIGNATURE = "withAWSV4Signature";
public GoClientCodegen() {
super();
@ -58,6 +59,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
cliOptions.add(CliOption.newBoolean(WITH_XML, "whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)"));
cliOptions.add(CliOption.newBoolean(CodegenConstants.ENUM_CLASS_PREFIX, CodegenConstants.ENUM_CLASS_PREFIX_DESC));
cliOptions.add(CliOption.newBoolean(STRUCT_PREFIX, "whether to prefix struct with the class name. e.g. DeletePetOpts => PetApiDeletePetOpts"));
cliOptions.add(CliOption.newBoolean(WITH_AWSV4_SIGNATURE, "whether to include AWS v4 signature support"));
// option to change the order of form/body parameter
cliOptions.add(CliOption.newBoolean(
@ -109,6 +111,13 @@ public class GoClientCodegen extends AbstractGoCodegen {
}
}
if (additionalProperties.containsKey(WITH_AWSV4_SIGNATURE)) {
setWithAWSV4Signature(Boolean.parseBoolean(additionalProperties.get(WITH_AWSV4_SIGNATURE).toString()));
if (withAWSV4Signature) {
additionalProperties.put(WITH_AWSV4_SIGNATURE, "true");
}
}
if (additionalProperties.containsKey(WITH_XML)) {
setWithXml(Boolean.parseBoolean(additionalProperties.get(WITH_XML).toString()));
if (withXml) {

View File

@ -24,12 +24,15 @@ import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.openapitools.codegen.utils.StringUtils.underscore;
public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(MysqlSchemaCodegen.class);
public static final String CODEGEN_VENDOR_EXTENSION_KEY = "x-mysqlSchema";
public static final String DEFAULT_DATABASE_NAME = "defaultDatabaseName";
public static final String JSON_DATA_TYPE_ENABLED = "jsonDataTypeEnabled";
public static final String IDENTIFIER_NAMING_CONVENTION = "identifierNamingConvention";
public static final Integer ENUM_MAX_ELEMENTS = 65535;
public static final Integer IDENTIFIER_MAX_LENGTH = 64;
@ -53,6 +56,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
protected String tableNamePrefix = "tbl_", tableNameSuffix = "";
protected String columnNamePrefix = "col_", columnNameSuffix = "";
protected Boolean jsonDataTypeEnabled = true;
protected String identifierNamingConvention = "original";
public MysqlSchemaCodegen() {
super();
@ -158,6 +162,16 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
cliOptions.clear();
addOption(DEFAULT_DATABASE_NAME, "Default database name for all MySQL queries", defaultDatabaseName);
addSwitch(JSON_DATA_TYPE_ENABLED, "Use special JSON MySQL data type for complex model properties. Requires MySQL version 5.7.8. Generates TEXT data type when disabled", jsonDataTypeEnabled);
// we used to snake_case table/column names, let's add this option
CliOption identifierNamingOpt = new CliOption(IDENTIFIER_NAMING_CONVENTION,
"Naming convention of MySQL identifiers(table names and column names). This is not related to database name which is defined by " + DEFAULT_DATABASE_NAME + " option");
identifierNamingOpt.addEnum("original", "Do not transform original names")
.addEnum("snake_case", "Use snake_case names")
.setDefault("original");
cliOptions.add(identifierNamingOpt);
}
@Override
@ -195,6 +209,10 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
additionalProperties.put(JSON_DATA_TYPE_ENABLED, getJsonDataTypeEnabled());
}
if (additionalProperties.containsKey(IDENTIFIER_NAMING_CONVENTION)) {
this.setIdentifierNamingConvention((String) additionalProperties.get(IDENTIFIER_NAMING_CONVENTION));
}
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("mysql_schema.mustache", "", "mysql_schema.sql"));
}
@ -208,11 +226,18 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> mo = (Map<String, Object>) _mo;
CodegenModel model = (CodegenModel) mo.get("model");
String modelName = model.getName();
String tableName = this.toTableName(modelName);
String modelDescription = model.getDescription();
Map<String, Object> modelVendorExtensions = model.getVendorExtensions();
Map<String, Object> mysqlSchema = new HashMap<String, Object>();
Map<String, Object> tableDefinition = new HashMap<String, Object>();
if (this.getIdentifierNamingConvention().equals("snake_case") && !modelName.equals(tableName)) {
// add original name in table comment
String commentExtra = "Original model name - " + modelName + ".";
modelDescription = (modelDescription == null || modelDescription.isEmpty()) ? commentExtra : modelDescription + ". " + commentExtra;
}
if (modelVendorExtensions.containsKey(CODEGEN_VENDOR_EXTENSION_KEY)) {
// user already specified schema values
LOGGER.info("Found vendor extension in '" + modelName + "' model, autogeneration skipped");
@ -220,7 +245,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
} else {
modelVendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("tableDefinition", tableDefinition);
tableDefinition.put("tblName", toTableName(modelName));
tableDefinition.put("tblName", tableName);
tableDefinition.put("tblComment", modelDescription);
}
}
@ -271,6 +296,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> columnDefinition = new HashMap<String, Object>();
ArrayList columnDataTypeArguments = new ArrayList();
String baseName = property.getBaseName();
String colName = this.toColumnName(baseName);
String dataType = property.getDataType();
String dataFormat = property.getDataFormat();
String description = property.getDescription();
@ -290,9 +316,15 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return;
}
if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) {
// add original name in column comment
String commentExtra = "Original param name - " + baseName + ".";
description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra;
}
vendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("columnDefinition", columnDefinition);
columnDefinition.put("colName", toColumnName(baseName));
columnDefinition.put("colName", colName);
if (Boolean.TRUE.equals(isEnum)) {
Map<String, Object> allowableValues = property.getAllowableValues();
@ -352,6 +384,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> columnDefinition = new HashMap<String, Object>();
ArrayList columnDataTypeArguments = new ArrayList();
String baseName = property.getBaseName();
String colName = this.toColumnName(baseName);
String dataType = property.getDataType();
String dataFormat = property.getDataFormat();
String description = property.getDescription();
@ -370,9 +403,15 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return;
}
if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) {
// add original name in column comment
String commentExtra = "Original param name - " + baseName + ".";
description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra;
}
vendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("columnDefinition", columnDefinition);
columnDefinition.put("colName", toColumnName(baseName));
columnDefinition.put("colName", colName);
if (Boolean.TRUE.equals(isEnum)) {
Map<String, Object> allowableValues = property.getAllowableValues();
@ -431,6 +470,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> columnDefinition = new HashMap<String, Object>();
ArrayList columnDataTypeArguments = new ArrayList();
String baseName = property.getBaseName();
String colName = this.toColumnName(baseName);
String description = property.getDescription();
String defaultValue = property.getDefaultValue();
Boolean required = property.getRequired();
@ -441,9 +481,15 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return;
}
if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) {
// add original name in column comment
String commentExtra = "Original param name - " + baseName + ".";
description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra;
}
vendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("columnDefinition", columnDefinition);
columnDefinition.put("colName", toColumnName(baseName));
columnDefinition.put("colName", colName);
columnDefinition.put("colDataType", "TINYINT");
columnDefinition.put("colDataTypeArguments", columnDataTypeArguments);
columnDataTypeArguments.add(toCodegenMysqlDataTypeArgument(1, false));
@ -477,6 +523,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> columnDefinition = new HashMap<String, Object>();
ArrayList columnDataTypeArguments = new ArrayList();
String baseName = property.getBaseName();
String colName = this.toColumnName(baseName);
String dataType = property.getDataType();
String dataFormat = property.getDataFormat();
String description = property.getDescription();
@ -492,9 +539,15 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return;
}
if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) {
// add original name in column comment
String commentExtra = "Original param name - " + baseName + ".";
description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra;
}
vendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("columnDefinition", columnDefinition);
columnDefinition.put("colName", toColumnName(baseName));
columnDefinition.put("colName", colName);
if (Boolean.TRUE.equals(isEnum)) {
Map<String, Object> allowableValues = property.getAllowableValues();
@ -548,6 +601,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> mysqlSchema = new HashMap<String, Object>();
Map<String, Object> columnDefinition = new HashMap<String, Object>();
String baseName = property.getBaseName();
String colName = this.toColumnName(baseName);
String dataType = property.getDataType();
Boolean required = property.getRequired();
String description = property.getDescription();
@ -559,9 +613,15 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return;
}
if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) {
// add original name in column comment
String commentExtra = "Original param name - " + baseName + ".";
description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra;
}
vendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("columnDefinition", columnDefinition);
columnDefinition.put("colName", toColumnName(baseName));
columnDefinition.put("colName", colName);
columnDefinition.put("colDataType", dataType);
if (Boolean.TRUE.equals(required)) {
@ -592,6 +652,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> mysqlSchema = new HashMap<String, Object>();
Map<String, Object> columnDefinition = new HashMap<String, Object>();
String baseName = property.getBaseName();
String colName = this.toColumnName(baseName);
String dataType = property.getDataType();
Boolean required = property.getRequired();
String description = property.getDescription();
@ -603,9 +664,15 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return;
}
if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) {
// add original name in column comment
String commentExtra = "Original param name - " + baseName + ".";
description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra;
}
vendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("columnDefinition", columnDefinition);
columnDefinition.put("colName", toColumnName(baseName));
columnDefinition.put("colName", colName);
columnDefinition.put("colDataType", dataType);
if (Boolean.FALSE.equals(getJsonDataTypeEnabled())) {
columnDefinition.put("colDataType", "TEXT");
@ -640,6 +707,7 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
Map<String, Object> mysqlSchema = new HashMap<String, Object>();
Map<String, Object> columnDefinition = new HashMap<String, Object>();
String baseName = property.getBaseName();
String colName = this.toColumnName(baseName);
Boolean required = property.getRequired();
String description = property.getDescription();
String defaultValue = property.getDefaultValue();
@ -650,9 +718,15 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return;
}
if (this.getIdentifierNamingConvention().equals("snake_case") && !baseName.equals(colName)) {
// add original name in column comment
String commentExtra = "Original param name - " + baseName + ".";
description = (description == null || description.isEmpty()) ? commentExtra : description + ". " + commentExtra;
}
vendorExtensions.put(CODEGEN_VENDOR_EXTENSION_KEY, mysqlSchema);
mysqlSchema.put("columnDefinition", columnDefinition);
columnDefinition.put("colName", toColumnName(baseName));
columnDefinition.put("colName", colName);
columnDefinition.put("colDataType", "TEXT");
if (Boolean.TRUE.equals(required)) {
@ -897,6 +971,9 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
*/
public String toTableName(String name) {
String identifier = toMysqlIdentifier(name, tableNamePrefix, tableNameSuffix);
if (identifierNamingConvention.equals("snake_case")) {
identifier = underscore(identifier);
}
if (identifier.length() > IDENTIFIER_MAX_LENGTH) {
LOGGER.warn("Table name cannot exceed 64 chars. Name '" + name + "' will be truncated");
identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH);
@ -913,6 +990,9 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
*/
public String toColumnName(String name) {
String identifier = toMysqlIdentifier(name, columnNamePrefix, columnNameSuffix);
if (identifierNamingConvention.equals("snake_case")) {
identifier = underscore(identifier);
}
if (identifier.length() > IDENTIFIER_MAX_LENGTH) {
LOGGER.warn("Column name cannot exceed 64 chars. Name '" + name + "' will be truncated");
identifier = identifier.substring(0, IDENTIFIER_MAX_LENGTH);
@ -1054,4 +1134,30 @@ public class MysqlSchemaCodegen extends DefaultCodegen implements CodegenConfig
return this.jsonDataTypeEnabled;
}
/**
* Sets identifier naming convention for table names and column names.
* This is not related to database name which is defined by defaultDatabaseName option.
*
* @param naming identifier naming convention (original|snake_case)
*/
public void setIdentifierNamingConvention(String naming) {
switch (naming) {
case "original":
case "snake_case":
this.identifierNamingConvention = naming;
break;
default:
LOGGER.warn("\"" + (String) naming + "\" is invalid \"identifierNamingConvention\" argument. Current \"" + (String) this.identifierNamingConvention + "\" used instead.");
}
}
/**
* Returns identifier naming convention for table names and column names.
*
* @return identifier naming convention
*/
public String getIdentifierNamingConvention() {
return this.identifierNamingConvention;
}
}

View File

@ -42,6 +42,8 @@ public class PhpSlim4ServerCodegen extends PhpSlimServerCodegen {
protected String psr7Implementation = "slim-psr7";
protected String mockDirName = "Mock";
protected String mockPackage = "";
protected String utilsDirName = "Utils";
protected String utilsPackage = "";
public PhpSlim4ServerCodegen() {
super();
@ -51,6 +53,7 @@ public class PhpSlim4ServerCodegen extends PhpSlimServerCodegen {
.build();
mockPackage = invokerPackage + "\\" + mockDirName;
utilsPackage = invokerPackage + "\\" + utilsDirName;
outputFolder = "generated-code" + File.separator + "slim4";
embeddedTemplateDir = templateDir = "php-slim4-server";
@ -86,8 +89,9 @@ public class PhpSlim4ServerCodegen extends PhpSlimServerCodegen {
super.processOpts();
if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
// Update the invokerPackage for the default mockPackage
// Update mockPackage and utilsPackage
mockPackage = invokerPackage + "\\" + mockDirName;
utilsPackage = invokerPackage + "\\" + utilsDirName;
}
// make mock src path available in mustache template
@ -95,6 +99,11 @@ public class PhpSlim4ServerCodegen extends PhpSlimServerCodegen {
additionalProperties.put("mockSrcPath", "./" + toSrcPath(mockPackage, srcBasePath));
additionalProperties.put("mockTestPath", "./" + toSrcPath(mockPackage, testBasePath));
// same for utils package
additionalProperties.put("utilsPackage", utilsPackage);
additionalProperties.put("utilsSrcPath", "./" + toSrcPath(utilsPackage, srcBasePath));
additionalProperties.put("utilsTestPath", "./" + toSrcPath(utilsPackage, testBasePath));
if (additionalProperties.containsKey(PSR7_IMPLEMENTATION)) {
this.setPsr7Implementation((String) additionalProperties.get(PSR7_IMPLEMENTATION));
}
@ -132,6 +141,12 @@ public class PhpSlim4ServerCodegen extends PhpSlimServerCodegen {
supportingFiles.add(new SupportingFile("openapi_data_mocker_interface.mustache", toSrcPath(mockPackage, srcBasePath), toInterfaceName("OpenApiDataMocker") + ".php"));
supportingFiles.add(new SupportingFile("openapi_data_mocker.mustache", toSrcPath(mockPackage, srcBasePath), "OpenApiDataMocker.php"));
supportingFiles.add(new SupportingFile("openapi_data_mocker_test.mustache", toSrcPath(mockPackage, testBasePath), "OpenApiDataMockerTest.php"));
// traits of ported utils
supportingFiles.add(new SupportingFile("string_utils_trait.mustache", toSrcPath(utilsPackage, srcBasePath), toTraitName("StringUtils") + ".php"));
supportingFiles.add(new SupportingFile("string_utils_trait_test.mustache", toSrcPath(utilsPackage, testBasePath), toTraitName("StringUtils") + "Test.php"));
supportingFiles.add(new SupportingFile("model_utils_trait.mustache", toSrcPath(utilsPackage, srcBasePath), toTraitName("ModelUtils") + ".php"));
supportingFiles.add(new SupportingFile("model_utils_trait_test.mustache", toSrcPath(utilsPackage, testBasePath), toTraitName("ModelUtils") + "Test.php"));
}
/**

View File

@ -48,6 +48,8 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme
public static final String CONTROLLER_PACKAGE = "controllerPackage";
public static final String DEFAULT_CONTROLLER = "defaultController";
public static final String SUPPORT_PYTHON2 = "supportPython2";
// nose is a python testing framework, we use pytest if USE_NOSE is unset
public static final String USE_NOSE = "useNose";
static final String MEDIA_TYPE = "mediaType";
protected int serverPort = 8080;
@ -57,6 +59,7 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme
protected String defaultController;
protected Map<Character, String> regexModifiers;
protected boolean fixBodyName;
protected boolean useNose = Boolean.FALSE;
public PythonAbstractConnexionServerCodegen(String templateDirectory, boolean fixBodyNameValue) {
super();
@ -156,6 +159,8 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme
defaultValue("false"));
cliOptions.add(new CliOption("serverPort", "TCP port to listen to in app.run").
defaultValue("8080"));
cliOptions.add(CliOption.newBoolean(USE_NOSE, "use the nose test framework").
defaultValue(Boolean.FALSE.toString()));
}
protected void addSupportingFiles() {
@ -200,6 +205,9 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme
additionalProperties.put(SUPPORT_PYTHON2, Boolean.TRUE);
typeMapping.put("long", "long");
}
if (additionalProperties.containsKey(USE_NOSE)) {
setUseNose((String) additionalProperties.get(USE_NOSE));
}
supportingFiles.add(new SupportingFile("__main__.mustache", packagePath(), "__main__.py"));
supportingFiles.add(new SupportingFile("util.mustache", packagePath(), "util.py"));
supportingFiles.add(new SupportingFile("typing_utils.mustache", packagePath(), "typing_utils.py"));
@ -214,6 +222,10 @@ public class PythonAbstractConnexionServerCodegen extends DefaultCodegen impleme
controllerPackage = packageName + "." + controllerPackage;
}
public void setUseNose(String val) {
this.useNose = Boolean.valueOf(val);
}
private static String packageToPath(String pkg) {
return pkg.replace(".", File.separator);
}

View File

@ -44,5 +44,7 @@ public class PythonAiohttpConnexionServerCodegen extends PythonAbstractConnexion
supportingFiles.add(new SupportingFile("conftest.mustache", testPackage, "conftest.py"));
supportingFiles.add(new SupportingFile("__init__test.mustache", testPackage, "__init__.py"));
supportingFiles.add(new SupportingFile("__init__main.mustache", packagePath(), "__init__.py"));
supportingFiles.add(new SupportingFile("tox.mustache", "", "tox.ini"));
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
}
}

View File

@ -40,6 +40,8 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
public static final String PACKAGE_URL = "packageUrl";
public static final String DEFAULT_LIBRARY = "urllib3";
// nose is a python testing framework, we use pytest if USE_NOSE is unset
public static final String USE_NOSE = "useNose";
protected String packageName = "openapi_client";
protected String packageVersion = "1.0.0";
@ -47,6 +49,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
protected String packageUrl;
protected String apiDocPath = "docs/";
protected String modelDocPath = "docs/";
protected boolean useNose = Boolean.FALSE;
protected Map<Character, String> regexModifiers;
@ -151,6 +154,8 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
.defaultValue(Boolean.TRUE.toString()));
cliOptions.add(new CliOption(CodegenConstants.SOURCECODEONLY_GENERATION, CodegenConstants.SOURCECODEONLY_GENERATION_DESC)
.defaultValue(Boolean.FALSE.toString()));
cliOptions.add(CliOption.newBoolean(USE_NOSE, "use the nose test framework").
defaultValue(Boolean.FALSE.toString()));
supportedLibraries.put("urllib3", "urllib3-based client");
supportedLibraries.put("asyncio", "Asyncio-based client (python 3.5+)");
@ -216,6 +221,10 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
setPackageUrl((String) additionalProperties.get(PACKAGE_URL));
}
if (additionalProperties.containsKey(USE_NOSE)) {
setUseNose((String) additionalProperties.get(USE_NOSE));
}
String readmePath = "README.md";
String readmeTemplate = "README.mustache";
if (generateSourceCodeOnly) {
@ -228,6 +237,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
supportingFiles.add(new SupportingFile("tox.mustache", "", "tox.ini"));
supportingFiles.add(new SupportingFile("test-requirements.mustache", "", "test-requirements.txt"));
supportingFiles.add(new SupportingFile("requirements.mustache", "", "requirements.txt"));
supportingFiles.add(new SupportingFile("setup_cfg.mustache", "", "setup.cfg"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
@ -579,6 +589,10 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
this.packageName = packageName;
}
public void setUseNose(String val) {
this.useNose = Boolean.valueOf(val);
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}

View File

@ -26,6 +26,7 @@ import io.swagger.v3.oas.models.media.FileSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.XML;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.servers.Server;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
@ -65,6 +66,11 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
private static final String uuidType = "uuid::Uuid";
private static final String bytesType = "swagger::ByteArray";
private static final String xmlMimeType = "application/xml";
private static final String octetMimeType = "application/octet-stream";
private static final String plainMimeType = "text/plain";
private static final String jsonMimeType = "application/json";
public RustServerCodegen() {
super();
@ -485,11 +491,11 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
}
private boolean isMimetypeXml(String mimetype) {
return mimetype.toLowerCase(Locale.ROOT).startsWith("application/xml");
return mimetype.toLowerCase(Locale.ROOT).startsWith(xmlMimeType);
}
private boolean isMimetypePlainText(String mimetype) {
return mimetype.toLowerCase(Locale.ROOT).startsWith("text/plain");
return mimetype.toLowerCase(Locale.ROOT).startsWith(plainMimeType);
}
private boolean isMimetypeHtmlText(String mimetype) {
@ -505,7 +511,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
}
private boolean isMimetypeOctetStream(String mimetype) {
return mimetype.toLowerCase(Locale.ROOT).startsWith("application/octet-stream");
return mimetype.toLowerCase(Locale.ROOT).startsWith(octetMimeType);
}
private boolean isMimetypePlain(String mimetype) {
@ -563,36 +569,17 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
processParam(param, op);
}
List<String> consumes = new ArrayList<String>();
boolean consumesPlainText = false;
boolean consumesXml = false;
// if "consumes" is defined (per operation or using global definition)
if (consumes != null && !consumes.isEmpty()) {
consumes.addAll(getConsumesInfo(this.openAPI, operation));
List<Map<String, String>> c = new ArrayList<Map<String, String>>();
for (String mimeType : consumes) {
Map<String, String> mediaType = new HashMap<String, String>();
if (isMimetypeXml(mimeType)) {
additionalProperties.put("usesXml", true);
consumesXml = true;
} else if (isMimetypePlain(mimeType)) {
consumesPlainText = true;
} else if (isMimetypeWwwFormUrlEncoded(mimeType)) {
additionalProperties.put("usesUrlEncodedForm", true);
}
mediaType.put("mediaType", mimeType);
c.add(mediaType);
}
op.consumes = c;
op.hasConsumes = true;
}
List<String> produces = new ArrayList<String>(getProducesInfo(this.openAPI, operation));
// We keep track of the 'default' model type for this API. If there are
// *any* XML responses, then we set the default to XML, otherwise we
// let the default be JSON. It would be odd for an API to want to use
// both XML and JSON on a single operation, and if we don't know
// anything then JSON is a more modern (ergo reasonable) choice.
boolean defaultsToXml = false;
// Determine the types that this operation produces. `getProducesInfo`
// simply lists all the types, and then we add the correct imports to
// the generated library.
List<String> produces = new ArrayList<String>(getProducesInfo(openAPI, operation));
boolean producesXml = false;
boolean producesPlainText = false;
if (produces != null && !produces.isEmpty()) {
@ -602,6 +589,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
if (isMimetypeXml(mimeType)) {
additionalProperties.put("usesXml", true);
defaultsToXml = true;
producesXml = true;
} else if (isMimetypePlain(mimeType)) {
producesPlainText = true;
@ -621,32 +609,132 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
param.vendorExtensions.put("typeName", toModelName(param.baseName));
}
// Set for deduplication of response IDs
Set<String> responseIds = new HashSet();
for (CodegenResponse rsp : op.responses) {
// Get the original API response so we get process the schema
// directly.
ApiResponse original;
if (rsp.code == "0") {
original = operation.getResponses().get("default");
} else {
original = operation.getResponses().get(rsp.code);
}
String[] words = rsp.message.split("[^A-Za-z ]");
// Create a unique responseID for this response.
String responseId;
if (rsp.vendorExtensions.containsKey("x-responseId")) {
// If it's been specified directly, use that.
responseId = (String) rsp.vendorExtensions.get("x-responseId");
} else if (words.length != 0) {
// If there's a description, build it from the description.
responseId = camelize(words[0].replace(" ", "_"));
} else {
// Otherwise fall back to the http response code.
responseId = "Status" + rsp.code;
}
// Deduplicate response IDs that would otherwise contain the same
// text. We rely on the ID being unique, but since we form it from
// the raw description field we can't require that the spec writer
// provides unique descriptions.
int idTieBreaker = 2;
while (responseIds.contains(responseId)) {
String trial = String.format(Locale.ROOT, "%s_%d", responseId, idTieBreaker);
if (!responseIds.contains(trial)) {
responseId = trial;
} else {
idTieBreaker++;
}
}
responseIds.add(responseId);
rsp.vendorExtensions.put("x-responseId", responseId);
rsp.vendorExtensions.put("x-uppercaseResponseId", underscore(responseId).toUpperCase(Locale.ROOT));
rsp.vendorExtensions.put("uppercase_operation_id", underscore(op.operationId).toUpperCase(Locale.ROOT));
if (rsp.dataType != null) {
rsp.vendorExtensions.put("uppercase_data_type", (rsp.dataType.replace("models::", "")).toUpperCase(Locale.ROOT));
// Default to producing json if nothing else is specified
// Get the mimetype which is produced by this response. Note
// that although in general responses produces a set of
// different mimetypes currently we only support 1 per
// response.
String firstProduces = null;
if (original.getContent() != null) {
for (String mimetype : original.getContent().keySet()) {
firstProduces = mimetype;
break;
}
}
// The output mime type. This allows us to do sensible fallback
// to JSON/XML rather than using only the default operation
// mimetype.
String outputMime;
if (firstProduces == null) {
if (producesXml) {
outputMime = xmlMimeType;
} else if (producesPlainText) {
if (rsp.dataType.equals(bytesType)) {
outputMime = octetMimeType;
} else {
outputMime = plainMimeType;
}
} else {
outputMime = jsonMimeType;
}
} else {
// If we know exactly what mimetype this response is
// going to produce, then use that. If we have not found
// anything, then we'll fall back to the 'producesXXX'
// definitions we worked out above for the operation as a
// whole.
if (isMimetypeXml(firstProduces)) {
producesXml = true;
producesPlainText = false;
} else if (isMimetypePlain(firstProduces)) {
producesXml = false;
producesPlainText = true;
} else {
producesXml = false;
producesPlainText = false;
}
outputMime = firstProduces;
}
rsp.vendorExtensions.put("mimeType", outputMime);
// Write out the type of data we actually expect this response
// to make.
if (producesXml) {
rsp.vendorExtensions.put("producesXml", true);
} else if (producesPlainText && rsp.dataType.equals(bytesType)) {
} else if (producesPlainText) {
// Plain text means that there is not structured data in
// this response. So it'll either be a UTF-8 encoded string
// 'plainText' or some generic 'bytes'.
//
// Note that we don't yet distinguish between string/binary
// and string/bytes - that is we don't auto-detect whether
// base64 encoding should be done. They both look like
// 'producesBytes'.
if (rsp.dataType.equals(bytesType)) {
rsp.vendorExtensions.put("producesBytes", true);
} else {
rsp.vendorExtensions.put("producesPlainText", true);
}
} else {
rsp.vendorExtensions.put("producesJson", true);
// If the data type is just "object", then ensure that the Rust data type
// is "serde_json::Value". This allows us to define APIs that
// can return arbitrary JSON bodies.
// If the data type is just "object", then ensure that the
// Rust data type is "serde_json::Value". This allows us
// to define APIs that can return arbitrary JSON bodies.
if (rsp.dataType.equals("object")) {
rsp.dataType = "serde_json::Value";
}
@ -686,7 +774,6 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
List<CodegenOperation> operationList = (List<CodegenOperation>) operations.get("operation");
for (CodegenOperation op : operationList) {
boolean consumesPlainText = false;
boolean consumesXml = false;

View File

@ -937,7 +937,7 @@ public class ModelUtils {
if (s == null) {
LOGGER.error("Failed to obtain schema from {}", parentName);
return "UNKNOWN_PARENT_NAME";
} else if (s.getDiscriminator() != null && StringUtils.isNotEmpty(s.getDiscriminator().getPropertyName())) {
} else if (hasOrInheritsDiscriminator(s, allSchemas)) {
// discriminator.propertyName is used
return parentName;
} else {
@ -961,7 +961,7 @@ public class ModelUtils {
return null;
}
public static List<String> getAllParentsName(ComposedSchema composedSchema, Map<String, Schema> allSchemas) {
public static List<String> getAllParentsName(ComposedSchema composedSchema, Map<String, Schema> allSchemas, boolean includeAncestors) {
List<Schema> interfaces = getInterfaces(composedSchema);
List<String> names = new ArrayList<String>();
@ -974,9 +974,12 @@ public class ModelUtils {
if (s == null) {
LOGGER.error("Failed to obtain schema from {}", parentName);
names.add("UNKNOWN_PARENT_NAME");
} else if (s.getDiscriminator() != null && StringUtils.isNotEmpty(s.getDiscriminator().getPropertyName())) {
} else if (hasOrInheritsDiscriminator(s, allSchemas)) {
// discriminator.propertyName is used
names.add(parentName);
if (includeAncestors && s instanceof ComposedSchema) {
names.addAll(getAllParentsName((ComposedSchema) s, allSchemas, true));
}
} else {
LOGGER.debug("Not a parent since discriminator.propertyName is not set {}", s.get$ref());
// not a parent since discriminator.propertyName is not set
@ -990,6 +993,32 @@ public class ModelUtils {
return names;
}
private static boolean hasOrInheritsDiscriminator(Schema schema, Map<String, Schema> allSchemas) {
if (schema.getDiscriminator() != null && StringUtils.isNotEmpty(schema.getDiscriminator().getPropertyName())) {
return true;
}
else if (StringUtils.isNotEmpty(schema.get$ref())) {
String parentName = getSimpleRef(schema.get$ref());
Schema s = allSchemas.get(parentName);
if (s != null) {
return hasOrInheritsDiscriminator(s, allSchemas);
}
else {
LOGGER.error("Failed to obtain schema from {}", parentName);
}
}
else if (schema instanceof ComposedSchema) {
final ComposedSchema composed = (ComposedSchema) schema;
final List<Schema> interfaces = getInterfaces(composed);
for (Schema i : interfaces) {
if (hasOrInheritsDiscriminator(i, allSchemas)) {
return true;
}
}
}
return false;
}
public static boolean isNullable(Schema schema) {
if (schema == null) {
return false;

View File

@ -6,11 +6,12 @@
--
-- NOTE: Auto generated by OpenAPI Generator (https://openapi-generator.tech).
with "config";
with "util";
with "util_http";
with "utilada_sys";
with "utilada_xml";
with "utilada_http";
with "security";
with "swagger";{{#isServer}}
with "servlet";
with "servletada";
with "swagger_server";{{/isServer}}
project {{{projectName}}} is

View File

@ -2,17 +2,22 @@ with Ada.IO_Exceptions;
with AWS.Config.Set;
with Swagger.Servers.AWS;
with Swagger.Servers.Applications;
with Util.Strings;
with Util.Log.Loggers;
with Util.Properties;
with Util.Properties.Basic;
with {{package}}.Servers;
procedure {{package}}.Server is
procedure Configure (Config : in out AWS.Config.Object);
use Util.Properties.Basic;
CONFIG_PATH : constant String := "{{packageConfig}}.properties";
Port : Natural := 8080;
procedure Configure (Config : in out AWS.Config.Object) is
begin
AWS.Config.Set.Server_Port (Config, 8080);
AWS.Config.Set.Server_Port (Config, Port);
AWS.Config.Set.Max_Connection (Config, 8);
AWS.Config.Set.Accept_Queue_Size (Config, 512);
end Configure;
@ -25,13 +30,15 @@ begin
Props.Load_Properties (CONFIG_PATH);
Util.Log.Loggers.Initialize (Props);
Port := Integer_Property.Get (Props, "swagger.port", Port);
App.Configure (Props);
{{package}}.Servers.Server_Impl.Register (App);
WS.Configure (Configure'Access);
WS.Register_Application ("{{basePathWithoutHost}}", App'Unchecked_Access);
App.Dump_Routes (Util.Log.INFO_LEVEL);
Log.Info ("Connect you browser to: http://localhost:8080{{basePathWithoutHost}}/ui/index.html");
Log.Info ("Connect you browser to: http://localhost:{0}{{basePathWithoutHost}}/ui/index.html",
Util.Strings.Image (Port));
WS.Start;

View File

@ -22,7 +22,7 @@
//
{{/notes}}
{{#returnType}}{{#returnTypeIsPrimitive}}{{#returnSimpleType}}{{{.}}}*{{/returnSimpleType}}{{^returnSimpleType}}{{#isListContainer}}{{{.}}}_t*{{/isListContainer}}{{#isMapContainer}}{{{.}}}{{/isMapContainer}}{{/returnSimpleType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{{.}}}_t*{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void{{/returnType}}
{{{classname}}}_{{{operationId}}}(apiClient_t *apiClient{{#allParams}} ,{{#isPrimitiveType}}{{#isNumber}}{{{dataType}}}{{/isNumber}}{{#isLong}}{{{dataType}}}{{/isLong}}{{#isInteger}}{{{dataType}}}{{/isInteger}}{{#isDouble}}{{{dataType}}}{{/isDouble}}{{#isFloat}}{{{dataType}}}{{/isFloat}}{{#isBoolean}}{{dataType}}{{/isBoolean}}{{#isEnum}}{{#isString}}{{{baseName}}}_e{{/isString}}{{/isEnum}}{{^isEnum}}{{#isString}}{{{dataType}}} *{{/isString}}{{/isEnum}}{{#isByteArray}}{{{dataType}}}{{/isByteArray}}{{#isDate}}{{{dataType}}}{{/isDate}}{{#isDateTime}}{{{dataType}}}{{/isDateTime}}{{#isFile}}{{{dataType}}}{{/isFile}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isModel}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{^isEnum}}{{{dataType}}}_t *{{/isEnum}}{{/isModel}}{{^isModel}}{{^isListContainer}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{/isListContainer}}{{/isModel}}{{#isUuid}}{{dataType}} *{{/isUuid}}{{#isEmail}}{{dataType}}{{/isEmail}}{{/isPrimitiveType}}{{#isContainer}}{{#isListContainer}}{{dataType}}_t *{{/isListContainer}}{{#isMapContainer}}{{dataType}}{{/isMapContainer}}{{/isContainer}} {{{paramName}}}{{/allParams}})
{{{classname}}}_{{{operationId}}}(apiClient_t *apiClient{{#allParams}} ,{{#isPrimitiveType}}{{#isNumber}}{{{dataType}}}{{/isNumber}}{{#isLong}}{{{dataType}}}{{/isLong}}{{#isInteger}}{{{dataType}}}{{/isInteger}}{{#isDouble}}{{{dataType}}}{{/isDouble}}{{#isFloat}}{{{dataType}}}{{/isFloat}}{{#isBoolean}}{{dataType}}{{/isBoolean}}{{#isEnum}}{{#isString}}{{{baseName}}}_e{{/isString}}{{/isEnum}}{{^isEnum}}{{#isString}}{{{dataType}}} *{{/isString}}{{/isEnum}}{{#isByteArray}}{{{dataType}}}{{/isByteArray}}{{#isDate}}{{{dataType}}}{{/isDate}}{{#isDateTime}}{{{dataType}}}{{/isDateTime}}{{#isFile}}{{{dataType}}}{{/isFile}}{{#isFreeFormObject}}{{dataType}}_t *{{/isFreeFormObject}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isModel}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{^isEnum}}{{{dataType}}}_t *{{/isEnum}}{{/isModel}}{{^isModel}}{{^isListContainer}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{/isListContainer}}{{/isModel}}{{#isUuid}}{{dataType}} *{{/isUuid}}{{#isEmail}}{{dataType}}{{/isEmail}}{{/isPrimitiveType}}{{#isContainer}}{{#isListContainer}}{{dataType}}_t *{{/isListContainer}}{{#isMapContainer}}{{dataType}}{{/isMapContainer}}{{/isContainer}} {{{paramName}}}{{/allParams}})
{
list_t *localVarQueryParameters = {{#hasQueryParams}}list_create();{{/hasQueryParams}}{{^hasQueryParams}}NULL;{{/hasQueryParams}}
list_t *localVarHeaderParameters = {{#hasHeaderParams}}list_create();{{/hasHeaderParams}}{{^hasHeaderParams}}NULL;{{/hasHeaderParams}}
@ -39,7 +39,7 @@
{{#pathParams}}
// Path Params
long sizeOfPathParams_{{{paramName}}} = {{#pathParams}}{{#isLong}}sizeof({{paramName}})+3{{/isLong}}{{#isString}}strlen({{paramName}})+3{{/isString}}{{/pathParams}} + strlen("{ {{paramName}} }");
long sizeOfPathParams_{{{paramName}}} = {{#pathParams}}{{#isLong}}sizeof({{paramName}})+3{{/isLong}}{{#isString}}strlen({{paramName}})+3{{/isString}}{{#hasMore}} + {{/hasMore}}{{/pathParams}} + strlen("{ {{paramName}} }");
{{#isNumeric}}
if({{paramName}} == 0){
goto end;

View File

@ -19,7 +19,7 @@
//
{{/notes}}
{{#returnType}}{{#returnTypeIsPrimitive}}{{#returnSimpleType}}{{{.}}}*{{/returnSimpleType}}{{^returnSimpleType}}{{#isListContainer}}{{{.}}}_t*{{/isListContainer}}{{#isMapContainer}}{{{.}}}{{/isMapContainer}}{{/returnSimpleType}}{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}{{{.}}}_t*{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void{{/returnType}}
{{{classname}}}_{{{operationId}}}(apiClient_t *apiClient{{#allParams}} ,{{#isPrimitiveType}}{{#isNumber}}{{{dataType}}}{{/isNumber}}{{#isLong}}{{{dataType}}}{{/isLong}}{{#isInteger}}{{{dataType}}}{{/isInteger}}{{#isDouble}}{{{dataType}}}{{/isDouble}}{{#isFloat}}{{{dataType}}}{{/isFloat}}{{#isBoolean}}{{dataType}}{{/isBoolean}}{{#isEnum}}{{#isString}}{{{baseName}}}_e{{/isString}}{{/isEnum}}{{^isEnum}}{{#isString}}{{{dataType}}} *{{/isString}}{{/isEnum}}{{#isByteArray}}{{{dataType}}}{{/isByteArray}}{{#isDate}}{{{dataType}}}{{/isDate}}{{#isDateTime}}{{{dataType}}}{{/isDateTime}}{{#isFile}}{{{dataType}}}{{/isFile}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isModel}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{^isEnum}}{{{dataType}}}_t *{{/isEnum}}{{/isModel}}{{^isModel}}{{^isListContainer}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{/isListContainer}}{{/isModel}}{{#isUuid}}{{dataType}} *{{/isUuid}}{{#isEmail}}{{dataType}}{{/isEmail}}{{/isPrimitiveType}}{{#isContainer}}{{#isListContainer}}{{dataType}}_t *{{/isListContainer}}{{#isMapContainer}}{{dataType}}{{/isMapContainer}}{{/isContainer}} {{{paramName}}}{{/allParams}});
{{{classname}}}_{{{operationId}}}(apiClient_t *apiClient{{#allParams}} ,{{#isPrimitiveType}}{{#isNumber}}{{{dataType}}}{{/isNumber}}{{#isLong}}{{{dataType}}}{{/isLong}}{{#isInteger}}{{{dataType}}}{{/isInteger}}{{#isDouble}}{{{dataType}}}{{/isDouble}}{{#isFloat}}{{{dataType}}}{{/isFloat}}{{#isBoolean}}{{dataType}}{{/isBoolean}}{{#isEnum}}{{#isString}}{{{baseName}}}_e{{/isString}}{{/isEnum}}{{^isEnum}}{{#isString}}{{{dataType}}} *{{/isString}}{{/isEnum}}{{#isByteArray}}{{{dataType}}}{{/isByteArray}}{{#isDate}}{{{dataType}}}{{/isDate}}{{#isDateTime}}{{{dataType}}}{{/isDateTime}}{{#isFile}}{{{dataType}}}{{/isFile}}{{#isFreeFormObject}}{{dataType}}_t *{{/isFreeFormObject}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{#isModel}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{^isEnum}}{{{dataType}}}_t *{{/isEnum}}{{/isModel}}{{^isModel}}{{^isListContainer}}{{#isEnum}}{{datatypeWithEnum}}_e{{/isEnum}}{{/isListContainer}}{{/isModel}}{{#isUuid}}{{dataType}} *{{/isUuid}}{{#isEmail}}{{dataType}}{{/isEmail}}{{/isPrimitiveType}}{{#isContainer}}{{#isListContainer}}{{dataType}}_t *{{/isListContainer}}{{#isMapContainer}}{{dataType}}{{/isMapContainer}}{{/isContainer}} {{{paramName}}}{{/allParams}});
{{/operation}}

View File

@ -72,7 +72,7 @@ typedef struct {{classname}}_t {
{{datatype}}_e {{name}}; //enum model
{{/isEnum}}
{{^isEnum}}
{{datatype}}_t *{{name}}; //model
struct {{datatype}}_t *{{name}}; //model
{{/isEnum}}
{{/isModel}}
{{#isUuid}}

View File

@ -75,7 +75,7 @@ public class JSON {
{{/mappedModels}}
classByDiscriminatorValue.put("{{classname}}".toUpperCase(Locale.ROOT), {{classname}}.class);
return getClassByDiscriminator(classByDiscriminatorValue,
getDiscriminatorValue(readElement, "{{{propertyName}}}"));
getDiscriminatorValue(readElement, "{{{propertyBaseName}}}"));
}
})
{{/discriminator}}

View File

@ -715,6 +715,13 @@ public class ApiClient {
}
}
for (Entry<String, String> entry : defaultCookieMap.entrySet()) {
String value = entry.getValue();
if (value != null) {
invocationBuilder = invocationBuilder.cookie(entry.getKey(), value);
}
}
for (Entry<String, String> entry : defaultHeaderMap.entrySet()) {
String key = entry.getKey();
if (!headerParams.containsKey(key)) {

View File

@ -659,15 +659,21 @@ public class ApiClient {
}
}
/**
* Build cookie header. Keeps a single value per cookie (as per <a href="https://tools.ietf.org/html/rfc6265#section-5.3">
* RFC6265 section 5.3</a>).
*
* @param cookies map all cookies
* @return header string for cookies.
*/
private String buildCookieHeader(MultiValueMap<String, String> cookies) {
final StringBuilder cookieValue = new StringBuilder();
String delimiter = "";
for (final Map.Entry<String, List<String>> entry : cookies.entrySet()) {
for (String value : entry.getValue()) {
cookieValue.append(String.format("%s%s=%s", delimiter, entry.getKey(), entry.getValue()));
final String value = entry.getValue().get(entry.getValue().size() - 1);
cookieValue.append(String.format("%s%s=%s", delimiter, entry.getKey(), value));
delimiter = "; ";
}
}
return cookieValue.toString();
}

View File

@ -1,4 +1,5 @@
= {{{appName}}}
{{#headerAttributes}}
{{infoEmail}}
{{#version}}{{{version}}}{{/version}}
:toc: left
@ -8,9 +9,10 @@
:keywords: openapi, rest, {{appName}}
:specDir: {{specDir}}
:snippetDir: {{snippetDir}}
:generator-template: v1 2019-11-19
:generator-template: v1 2019-12-20
:info-url: {{infoUrl}}
:app-name: {{appName}}
{{/headerAttributes}}
[abstract]
.Abstract

View File

@ -10,8 +10,6 @@ abstract class {{classname}} implements Built<{{classname}}, {{classname}}Builde
{{#isNullable}}
@nullable
{{/isNullable}}
{{#description}}/* {{{description}}} */{{/description}}
@BuiltValueField(wireName: '{{baseName}}')
{{{dataType}}} get {{name}};
{{#allowableValues}}

View File

@ -1,6 +1,9 @@
name: {{pubName}}
version: {{pubVersion}}
description: {{pubDescription}}
authors:
- {{pubAuthor}} <{{pubAuthorEmail}}>
homepage: {{pubHomepage}}
environment:
sdk: '>=2.0.0 <3.0.0'
dependencies:

View File

@ -24,6 +24,9 @@ Install the following dependencies:
```shell
go get github.com/stretchr/testify/assert
go get golang.org/x/oauth2
{{#withAWSV4Signature}}
go get github.com/aws/aws-sdk-go/aws
{{/withAWSV4Signature}}
go get golang.org/x/net/context
go get github.com/antihax/optional
```
@ -113,6 +116,18 @@ r, err := client.Service.Operation(auth, args)
{{/isOAuth}}
{{/authMethods}}
{{#withAWSV4Signature}}
Example
```golang
auth := context.WithValue(context.Background(), sw.ContextAWSv4, sw.AWSv4{
AccessKey: "ACCESSKEYSTRING",
SecretKey: "SECRETKEYSTRING",
})
r, err := client.Service.Operation(auth, args)
```
{{/withAWSV4Signature}}
## Author
{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}}

View File

@ -24,6 +24,10 @@ import (
"unicode/utf8"
"golang.org/x/oauth2"
{{#withAWSV4Signature}}
awsv4 "github.com/aws/aws-sdk-go/aws/signer/v4"
awscredentials "github.com/aws/aws-sdk-go/aws/credentials"
{{/withAWSV4Signature}}
)
var (
@ -352,6 +356,25 @@ func (c *APIClient) prepareRequest(
if auth, ok := ctx.Value(ContextAccessToken).(string); ok {
localVarRequest.Header.Add("Authorization", "Bearer "+auth)
}
{{#withAWSV4Signature}}
// AWS Signature v4 Authentication
if auth, ok := ctx.Value(ContextAWSv4).(AWSv4); ok {
creds := awscredentials.NewStaticCredentials(auth.AccessKey, auth.SecretKey, "")
signer := awsv4.NewSigner(creds)
var reader *strings.Reader
if body == nil {
reader = strings.NewReader("")
} else {
reader = strings.NewReader(body.String())
}
timestamp := time.Now()
_, err := signer.Sign(localVarRequest, reader, "oapi", "eu-west-2", timestamp)
if err != nil {
return nil, err
}
}
{{/withAWSV4Signature}}
}
for header, value := range c.cfg.DefaultHeader {

View File

@ -29,6 +29,11 @@ var (
// ContextAPIKey takes an APIKey as authentication for the request
ContextAPIKey = contextKey("apikey")
{{#withAWSV4Signature}}
// ContextAWSv4 takes an Access Key and a Secret Key for signing AWS Signature v4.
ContextAWSv4 = contextKey("awsv4")
{{/withAWSV4Signature}}
)
// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth
@ -43,6 +48,15 @@ type APIKey struct {
Prefix string
}
{{#withAWSV4Signature}}
// AWSv4 provides AWS Signature to a request passed via context using ContextAWSv4
// https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
type AWSv4 struct {
AccessKey string
SecretKey string
}
{{/withAWSV4Signature}}
// ServerVariable stores the information about a server variable
type ServerVariable struct {
Description string

View File

@ -3,4 +3,5 @@ module {{gitHost}}/{{gitUserId}}/{{gitRepoId}}{{#isGoSubmodule}}/{{packageName}}
require (
github.com/antihax/optional v1.0.0
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
{{#withAWSV4Signature}}github.com/aws/aws-sdk-go v1.26.3{{/withAWSV4Signature}}
)

View File

@ -1,6 +1,8 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aws/aws-sdk-go v1.26.3 h1:szQdfJcUBAhQT0zZEx4sxoDuWb7iScoucxCiVxDmaBk=
github.com/aws/aws-sdk-go v1.26.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

View File

@ -5,7 +5,7 @@ composer.phar
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
composer.lock
# phplint tool creates cache file which is not necessary in a codebase
/.phplint-cache

View File

@ -43,11 +43,13 @@
"test": [
"@test-apis",
"@test-models",
"@test-mock"
"@test-mock",
"@test-utils"
],
"test-apis": "phpunit --testsuite Apis",
"test-models": "phpunit --testsuite Models",
"test-mock": "phpunit --testsuite Mock",
"test-utils": "phpunit --testsuite Utils",
"phpcs": "phpcs",
"phplint": "phplint ./ --exclude=vendor"
}

View File

@ -0,0 +1,132 @@
<?php
/**
* {{traitNamePrefix}}ModelUtils{{traitNameSuffix}}
*
* PHP version 7.1
*
* @package {{invokerPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**{{#apiInfo}}{{#appName}}
* {{{appName}}}
*
{{/appName}}
{{#appDescription}}
* {{{appDescription}}}
{{/appDescription}}
{{#version}}
* The version of the OpenAPI document: {{{version}}}
{{/version}}
{{#infoEmail}}
* Contact: {{{infoEmail}}}
{{/infoEmail}}
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace {{utilsPackage}};
use {{utilsPackage}}\{{traitNamePrefix}}StringUtils{{traitNameSuffix}};
/**
* {{traitNamePrefix}}ModelUtils{{traitNameSuffix}} Class Doc Comment
* This class duplicates functionality of ModelUtils.java and AbstractPhpCodegen.java classes.
*
* @package {{utilsPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
trait {{traitNamePrefix}}ModelUtils{{traitNameSuffix}}
{
use {{traitNamePrefix}}StringUtils{{traitNameSuffix}};
/**
* Parses model class name from provided ref.
* @link https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#reference-object
* This method doesn't check that class exists and autoloaded.
* This is recreated method of @link modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java class.
*
* @param string $ref Reference, eg. #/components/schemas/Pet
*
* @return string|null classname or null on fail
*/
public static function getSimpleRef($ref)
{
$model = null;
if (stripos($ref, '#/components/') === 0) {
// starts with #/components/
$model = substr($ref, strrpos($ref, '/') + 1);
} elseif (stripos($ref, '#/definitions/') === 0) {
// starts with #/definitions/
$model = substr($ref, strrpos($ref, '/') + 1);
}
return $model;
}
/**
* Output the proper model name (capitalized).
* In case the name belongs to the TypeSystem it won't be renamed.
* This is recreated method of @link modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java class.
*
* @param string $name the name of the model
* @param string|null $modelNamePrefix modelNamePrefix generator option
* @param string|null $modelNameSuffix modelNameSuffix generator option
*
* @return string capitalized model name
*/
public static function toModelName(
$name,
$modelNamePrefix = {{#modelNamePrefix}}'{{modelNamePrefix}}'{{/modelNamePrefix}}{{^modelNamePrefix}}null{{/modelNamePrefix}},
$modelNameSuffix = {{#modelNameSuffix}}'{{modelNameSuffix}}'{{/modelNameSuffix}}{{^modelNameSuffix}}null{{/modelNameSuffix}}
) {
if (is_string($name) === false || empty($name)) {
return null;
}
// remove [
$name = str_replace(']', '', $name);
// Note: backslash ("\\") is allowed for e.g. "\\DateTime"
$name = preg_replace('/[^\w\\\\]+/', '_', $name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
// remove underscores from start and end
$name = trim($name, '_');
// remove dollar sign
$name = str_replace('$', '', $name);
// model name cannot use reserved keyword
if (self::isReservedWord($name)) {
$name = 'model_' . $name; // e.g. return => ModelReturn (after camelize)
}
// model name starts with number
if (preg_match('/^\d.*/', $name) === 1) {
$name = 'model_' . $name; // e.g. 200Response => Model200Response (after camelize)
}
// add prefix and/or suffic only if name does not start wth \ (e.g. \DateTime)
if (preg_match('/^\\\\.*/', $name) !== 1) {
if (is_string($modelNamePrefix) && !empty($modelNamePrefix)) {
$name = $modelNamePrefix . '_' . $name;
}
if (is_string($modelNameSuffix) && !empty($modelNameSuffix)) {
$name = $name . '_' . $modelNameSuffix;
}
}
// camelize the model name
// phone_number => PhoneNumber
return self::camelize($name);
}
}
{{/apiInfo}}

View File

@ -0,0 +1,111 @@
<?php
/**
* {{traitNamePrefix}}ModelUtils{{traitNameSuffix}}Test
*
* PHP version 7.1
*
* @package {{invokerPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**{{#apiInfo}}{{#appName}}
* {{{appName}}}
*
{{/appName}}
{{#appDescription}}
* {{{appDescription}}}
{{/appDescription}}
{{#version}}
* The version of the OpenAPI document: {{{version}}}
{{/version}}
{{#infoEmail}}
* Contact: {{{infoEmail}}}
{{/infoEmail}}
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace {{utilsPackage}};
use {{utilsPackage}}\{{traitNamePrefix}}ModelUtils{{traitNameSuffix}} as ModelUtils;
use PHPUnit\Framework\TestCase;
/**
* {{traitNamePrefix}}ModelUtils{{traitNameSuffix}}Test Class Doc Comment
*
* @package {{utilsPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
* @coversDefaultClass \{{utilsPackage}}\{{traitNamePrefix}}ModelUtils{{traitNameSuffix}}
*/
class {{traitNamePrefix}}ModelUtils{{traitNameSuffix}}Test extends TestCase
{
/**
* @covers ::getSimpleRef
* @dataProvider provideRefs
*/
public function testGetSimpleRef($ref, $expectedRef)
{
$this->assertSame($expectedRef, ModelUtils::getSimpleRef($ref));
}
public function provideRefs()
{
return [
'Reference Object OAS 3.0' => [
'#/components/schemas/Pet', 'Pet',
],
'Reference Object Swagger 2.0' => [
'#/definitions/Pet', 'Pet',
],
'Underscored classname' => [
'#/components/schemas/_foobar_Objects', '_foobar_Objects',
],
'Relative Documents With Embedded Schema' => [
'definitions.json#/Pet', null,
],
'null as argument' => [
null, null,
],
'number as argument' => [
156, null,
],
];
}
/**
* @covers ::toModelName
* @dataProvider provideModelNames
*/
public function testToModelName($name, $prefix, $suffix, $expectedModel)
{
$this->assertSame($expectedModel, ModelUtils::toModelName($name, $prefix, $suffix));
}
public function provideModelNames()
{
return [
// fixtures from modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/StringUtilsTest.java
['abcd', null, null, 'Abcd'],
['some-value', null, null, 'SomeValue'],
['some_value', null, null, 'SomeValue'],
['$type', null, null, 'Type'],
['123', null, null, 'Model123'],
['$123', null, null, 'Model123'],
['return', null, null, 'ModelReturn'],
['200Response', null, null, 'Model200Response'],
['abcd', 'SuperModel', null, 'SuperModelAbcd'],
['abcd', null, 'WithEnd', 'AbcdWithEnd'],
['abcd', 'WithStart', 'AndEnd', 'WithStartAbcdAndEnd'],
['_foobar_Objects', null, null, 'FoobarObjects'],
[null, null, null, null],
];
}
}
{{/apiInfo}}

View File

@ -34,6 +34,7 @@
namespace {{mockPackage}};
use {{mockPackage}}\{{interfaceNamePrefix}}OpenApiDataMocker{{interfaceNameSuffix}} as IMocker;
use StdClass;
use InvalidArgumentException;
/**
@ -82,6 +83,13 @@ final class OpenApiDataMocker implements IMocker
$maxItems = $options['maxItems'] ?? null;
$uniqueItems = $options['uniqueItems'] ?? false;
return $this->mockArray($items, $minItems, $maxItems, $uniqueItems);
case IMocker::DATA_TYPE_OBJECT:
$properties = $options['properties'] ?? null;
$minProperties = $options['minProperties'] ?? 0;
$maxProperties = $options['maxProperties'] ?? null;
$additionalProperties = $options['additionalProperties'] ?? null;
$required = $options['required'] ?? null;
return $this->mockObject($properties, $minProperties, $maxProperties, $additionalProperties, $required);
default:
throw new InvalidArgumentException('"dataType" must be one of ' . implode(', ', [
IMocker::DATA_TYPE_INTEGER,
@ -89,6 +97,7 @@ final class OpenApiDataMocker implements IMocker
IMocker::DATA_TYPE_STRING,
IMocker::DATA_TYPE_BOOLEAN,
IMocker::DATA_TYPE_ARRAY,
IMocker::DATA_TYPE_OBJECT,
]));
}
}
@ -220,7 +229,7 @@ final class OpenApiDataMocker implements IMocker
* Shortcut to mock array type
* Equivalent to mockData(DATA_TYPE_ARRAY);
*
* @param array $items Array of described items
* @param object|array $items Object or assoc array of described items
* @param int|null $minItems (optional) An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
* @param int|null $maxItems (optional) An array instance is valid against "maxItems" if its size is less than, or equal to, the value of this keyword
* @param bool|null $uniqueItems (optional) If it has boolean value true, the instance validates successfully if all of its elements are unique
@ -239,8 +248,12 @@ final class OpenApiDataMocker implements IMocker
$minSize = 0;
$maxSize = \PHP_INT_MAX;
if (is_array($items) === false || array_key_exists('type', $items) === false) {
throw new InvalidArgumentException('"items" must be assoc array with "type" key');
if (
(is_array($items) === false && is_object($items) === false)
|| (is_array($items) && array_key_exists('type', $items) === false)
|| (is_object($items) && isset($items->type) === false)
) {
new InvalidArgumentException('"items" must be object or assoc array with "type" key');
}
if ($minItems !== null) {
@ -260,9 +273,9 @@ final class OpenApiDataMocker implements IMocker
$maxSize = $maxItems;
}
$dataType = $items['type'];
$dataFormat = $items['format'] ?? null;
$options = $this->extractSchemaProperties($items);
$dataType = $options['type'];
$dataFormat = $options['format'] ?? null;
// always genarate smallest possible array to avoid huge JSON responses
$arrSize = ($maxSize < 1) ? $maxSize : max($minSize, 1);
@ -273,17 +286,104 @@ final class OpenApiDataMocker implements IMocker
}
/**
* @internal Extract OAS properties from array or object.
* Shortcut to mock object type.
* Equivalent to mockData(DATA_TYPE_OBJECT);
*
* @param array $arr Processed array
* @param object|array $properties Object or array of described properties
* @param int|null $minProperties (optional) An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword.
* @param int|null $maxProperties (optional) An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.
* @param bool|object|array|null $additionalProperties (optional) If "additionalProperties" is true, validation always succeeds.
* If "additionalProperties" is false, validation succeeds only if the instance is an object and all properties on the instance were covered by "properties" and/or "patternProperties".
* If "additionalProperties" is an object, validate the value as a schema to all of the properties that weren't validated by "properties" nor "patternProperties".
* @param array|null $required (optional) This array MUST have at least one element. Elements of this array must be strings, and MUST be unique.
* An object instance is valid if its property set contains all elements in this array value.
*
* @throws \InvalidArgumentException when invalid arguments passed
*
* @return object
*/
public function mockObject(
$properties,
$minProperties = 0,
$maxProperties = null,
$additionalProperties = null,
$required = null
) {
$obj = new StdClass();
if (is_object($properties) === false && is_array($properties) === false) {
throw new InvalidArgumentException('The value of "properties" must be an array or object');
}
foreach ($properties as $propName => $propValue) {
if (is_object($propValue) === false && is_array($propValue) === false) {
throw new InvalidArgumentException('Each value of "properties" must be an array or object');
}
}
if ($minProperties !== null) {
if (is_integer($minProperties) === false || $minProperties < 0) {
throw new InvalidArgumentException('"minProperties" must be an integer. This integer must be greater than, or equal to, 0');
}
}
if ($maxProperties !== null) {
if (is_integer($maxProperties) === false || $maxProperties < 0) {
throw new InvalidArgumentException('"maxProperties" must be an integer. This integer must be greater than, or equal to, 0.');
}
if ($maxProperties < $minProperties) {
throw new InvalidArgumentException('"maxProperties" value cannot be less than "minProperties"');
}
}
if ($additionalProperties !== null) {
if (is_bool($additionalProperties) === false && is_object($additionalProperties) === false && is_array($additionalProperties) === false) {
throw new InvalidArgumentException('The value of "additionalProperties" must be a boolean or object or array.');
}
}
if ($required !== null) {
if (
is_array($required) === false
|| count($required) > count(array_unique($required))
) {
throw new InvalidArgumentException('The value of "required" must be an array. Elements of this array must be unique.');
}
foreach ($required as $requiredPropName) {
if (is_string($requiredPropName) === false) {
throw new InvalidArgumentException('Elements of "required" array must be strings');
}
}
}
foreach ($properties as $propName => $propValue) {
$options = $this->extractSchemaProperties($propValue);
$dataType = $options['type'];
$dataFormat = $options['dataFormat'] ?? null;
$obj->$propName = $this->mock($dataType, $dataFormat, $options);
}
return $obj;
}
/**
* @internal Extract OAS properties from array or object.
* @codeCoverageIgnore
*
* @param array|object $val Processed array or object
*
* @return array
*/
private function extractSchemaProperties($arr)
private function extractSchemaProperties($val)
{
$props = [];
$props = [
'type' => null,
'format' => null,
];
foreach (
[
'type',
'format',
'minimum',
'maximum',
'exclusiveMinimum',
@ -304,8 +404,10 @@ final class OpenApiDataMocker implements IMocker
'example',
] as $propName
) {
if (array_key_exists($propName, $arr)) {
$props[$propName] = $arr[$propName];
if (is_array($val) && array_key_exists($propName, $val)) {
$props[$propName] = $val[$propName];
} elseif (is_object($val) && isset($val->$propName)) {
$props[$propName] = $val->$propName;
}
}
return $props;
@ -313,6 +415,7 @@ final class OpenApiDataMocker implements IMocker
/**
* @internal
* @codeCoverageIgnore
*
* @return float|int
*/

View File

@ -63,6 +63,9 @@ interface {{interfaceNamePrefix}}OpenApiDataMocker{{interfaceNameSuffix}}
/** @var string DATA_TYPE_ARRAY */
public const DATA_TYPE_ARRAY = 'array';
/** @var string DATA_TYPE_OBJECT */
public const DATA_TYPE_OBJECT = 'object';
/** @var string DATA_FORMAT_INT32 Signed 32 bits */
public const DATA_FORMAT_INT32 = 'int32';
@ -194,7 +197,7 @@ interface {{interfaceNamePrefix}}OpenApiDataMocker{{interfaceNameSuffix}}
* Shortcut to mock array type
* Equivalent to mockData(DATA_TYPE_ARRAY);
*
* @param array $items Array of described items
* @param object|array $items Object or assoc array of described items
* @param int|null $minItems (optional) An array instance is valid against "minItems" if its size is greater than, or equal to, the value of this keyword.
* @param int|null $maxItems (optional) An array instance is valid against "maxItems" if its size is less than, or equal to, the value of this keyword
* @param bool|null $uniqueItems (optional) If it has boolean value true, the instance validates successfully if all of its elements are unique
@ -209,5 +212,30 @@ interface {{interfaceNamePrefix}}OpenApiDataMocker{{interfaceNameSuffix}}
$maxItems = null,
$uniqueItems = false
);
/**
* Shortcut to mock object type.
* Equivalent to mockData(DATA_TYPE_OBJECT);
*
* @param object|array $properties Object or array of described properties
* @param int|null $minProperties (optional) An object instance is valid against "minProperties" if its number of properties is greater than, or equal to, the value of this keyword.
* @param int|null $maxProperties (optional) An object instance is valid against "maxProperties" if its number of properties is less than, or equal to, the value of this keyword.
* @param bool|object|array|null $additionalProperties (optional) If "additionalProperties" is true, validation always succeeds.
* If "additionalProperties" is false, validation succeeds only if the instance is an object and all properties on the instance were covered by "properties" and/or "patternProperties".
* If "additionalProperties" is an object, validate the value as a schema to all of the properties that weren't validated by "properties" nor "patternProperties".
* @param array|null $required (optional) This array MUST have at least one element. Elements of this array must be strings, and MUST be unique.
* An object instance is valid if its property set contains all elements in this array value.
*
* @throws \InvalidArgumentException when invalid arguments passed
*
* @return object
*/
public function mockObject(
$properties,
$minProperties = 0,
$maxProperties = null,
$additionalProperties = null,
$required = null
);
}
{{/apiInfo}}

View File

@ -37,6 +37,7 @@ use {{mockPackage}}\OpenApiDataMocker;
use {{mockPackage}}\{{interfaceNamePrefix}}OpenApiDataMocker{{interfaceNameSuffix}} as IMocker;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Constraint\IsType;
use StdClass;
/**
* OpenApiDataMockerTest Class Doc Comment
@ -55,7 +56,8 @@ class OpenApiDataMockerTest extends TestCase
public function testMockCorrectArguments($dataType, $dataFormat, $options, $expectedType)
{
$mocker = new OpenApiDataMocker();
$this->assertInternalType($expectedType, $mocker->mock($dataType));
$data = $mocker->mock($dataType, $dataFormat, $options);
$this->assertInternalType($expectedType, $data);
}
public function provideMockCorrectArguments()
@ -65,6 +67,39 @@ class OpenApiDataMockerTest extends TestCase
[IMocker::DATA_TYPE_NUMBER, null, null, IsType::TYPE_FLOAT],
[IMocker::DATA_TYPE_STRING, null, null, IsType::TYPE_STRING],
[IMocker::DATA_TYPE_BOOLEAN, null, null, IsType::TYPE_BOOL],
[IMocker::DATA_TYPE_ARRAY, null, [
'items' => [
'type' => IMocker::DATA_TYPE_INTEGER,
],
], IsType::TYPE_ARRAY],
[IMocker::DATA_TYPE_OBJECT, null, [
'properties' => [
'username' => [
'type' => IMocker::DATA_TYPE_INTEGER,
],
],
], IsType::TYPE_OBJECT],
];
}
/**
* @covers ::mock
* @dataProvider provideMockInvalidArguments
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage "dataType" must be one of integer, number, string, boolean, array, object
*/
public function testMockInvalidArguments($dataType, $dataFormat, $options)
{
$mocker = new OpenApiDataMocker();
$data = $mocker->mock($dataType, $dataFormat, $options);
}
public function provideMockInvalidArguments()
{
return [
['foobar', null, null],
[3.14, null, null],
[null, null, null],
];
}
@ -440,6 +475,8 @@ class OpenApiDataMockerTest extends TestCase
$this->assertContainsOnly($expectedItemsType, $arr, true);
}
if (is_array($items)) {
$dataType = $items['type'];
$dataFormat = $items['dataFormat'] ?? null;
// items field numeric properties
@ -459,9 +496,33 @@ class OpenApiDataMockerTest extends TestCase
$subMinItems = $items['minItems'] ?? null;
$subMaxItems = $items['maxItems'] ?? null;
$subUniqueItems = $items['uniqueItems'] ?? null;
} else {
// is object
$dataType = $items->type;
$dataFormat = $items->dataFormat ?? null;
// items field numeric properties
$minimum = $items->minimum ?? null;
$maximum = $items->maximum ?? null;
$exclusiveMinimum = $items->exclusiveMinimum ?? null;
$exclusiveMaximum = $items->exclusiveMaximum ?? null;
// items field string properties
$minLength = $items->minLength ?? null;
$maxLength = $items->maxLength ?? null;
$enum = $items->enum ?? null;
$pattern = $items->pattern ?? null;
// items field array properties
$subItems = $items->items ?? null;
$subMinItems = $items->minItems ?? null;
$subMaxItems = $items->maxItems ?? null;
$subUniqueItems = $items->uniqueItems ?? null;
}
foreach ($arr as $item) {
switch ($items['type']) {
switch ($dataType) {
case IMocker::DATA_TYPE_INTEGER:
$this->internalAssertNumber($item, $minimum, $maximum, $exclusiveMinimum, $exclusiveMaximum);
break;
@ -486,13 +547,15 @@ class OpenApiDataMockerTest extends TestCase
$intItems = ['type' => IMocker::DATA_TYPE_INTEGER, 'minimum' => 5, 'maximum' => 10];
$floatItems = ['type' => IMocker::DATA_TYPE_NUMBER, 'minimum' => -32.4, 'maximum' => 88.6, 'exclusiveMinimum' => true, 'exclusiveMaximum' => true];
$strItems = ['type' => IMocker::DATA_TYPE_STRING, 'minLength' => 20, 'maxLength' => 50];
$boolItems = ['type' => IMocker::DATA_TYPE_BOOLEAN];
$arrayItems = ['type' => IMocker::DATA_TYPE_ARRAY, 'items' => ['type' => IMocker::DATA_TYPE_STRING, 'minItems' => 3, 'maxItems' => 10]];
$boolItems = (object) ['type' => IMocker::DATA_TYPE_BOOLEAN];
$arrayItems = (object) ['type' => IMocker::DATA_TYPE_ARRAY, 'items' => ['type' => IMocker::DATA_TYPE_STRING, 'minItems' => 3, 'maxItems' => 10]];
$objectItems = (object) ['type' => IMocker::DATA_TYPE_OBJECT, 'properties' => (object)['username' => ['type' => IMocker::DATA_TYPE_STRING]]];
$expectedInt = IsType::TYPE_INT;
$expectedFloat = IsType::TYPE_FLOAT;
$expectedStr = IsType::TYPE_STRING;
$expectedBool = IsType::TYPE_BOOL;
$expectedArray = IsType::TYPE_ARRAY;
$expectedObject = IsType::TYPE_OBJECT;
return [
'empty array' => [
@ -531,6 +594,9 @@ class OpenApiDataMockerTest extends TestCase
'array of one array of strings' => [
$arrayItems, null, null, false, $expectedArray, 1,
],
'array of one object' => [
$objectItems, null, null, false, $expectedObject, 1
],
];
}
@ -583,5 +649,125 @@ class OpenApiDataMockerTest extends TestCase
],
];
}
/**
* @dataProvider provideMockObjectCorrectArguments
* @covers ::mockObject
*/
public function testMockObjectWithCorrectArguments(
$properties,
$minProperties,
$maxProperties,
$additionalProperties,
$required,
$expectedKeys
) {
$mocker = new OpenApiDataMocker();
$obj = $mocker->mockObject(
$properties,
$minProperties,
$maxProperties,
$additionalProperties,
$required
);
$this->assertInternalType(IsType::TYPE_OBJECT, $obj);
$this->assertSame($expectedKeys, array_keys(get_object_vars($obj)));
}
public function provideMockObjectCorrectArguments()
{
$additionProps = [
'extra' => [
'type' => IMocker::DATA_TYPE_STRING,
],
];
return [
'empty object' => [
[], 1, 10, true, null, [],
],
'empty object from StdClass' => [
new StdClass(), 1, 5, false, null, [],
],
'object with username property' => [
[
'username' => [
'type' => IMocker::DATA_TYPE_STRING,
],
], 0, 5, $additionProps, null, ['username'],
],
'object with foobar property' => [
(object) [
'foobar' => [
'type' => IMocker::DATA_TYPE_INTEGER,
],
], 1, 1, (object) $additionProps, null, ['foobar'],
],
];
}
/**
* @dataProvider provideMockObjectInvalidArguments
* @expectedException \InvalidArgumentException
* @covers ::mockObject
*/
public function testMockObjectWithInvalidArguments(
$properties,
$minProperties,
$maxProperties,
$additionalProperties,
$required
) {
$mocker = new OpenApiDataMocker();
$obj = $mocker->mockObject($properties, $minProperties, $maxProperties, $additionalProperties, $required);
}
public function provideMockObjectInvalidArguments()
{
return [
'properties cannot be null' => [
null, 0, 10, false, null,
],
'properties cannot be a string' => [
'foobar', 0, 10, false, null,
],
'minProperties is not integer' => [
[], 3.12, null, false, null,
],
'minProperties is negative' => [
[], -10, null, false, null,
],
'minProperties is not number' => [
[], '1', null, false, null,
],
'maxProperties is not integer' => [
[], null, 3.12, false, null,
],
'maxProperties is negative' => [
[], null, -10, false, null,
],
'maxProperties is not number' => [
[], null, 'foobaz', false, null,
],
'maxProperties less than minProperties' => [
[], 5, 2, false, null,
],
'additionalProperties is not object|array|boolean' => [
[], null, null, 'foobar', null,
],
'required is object, not array' => [
[], null, null, null, new StdClass(),
],
'required is not array' => [
[], null, null, null, 'foobar',
],
'required array with duplicates' => [
[], null, null, null, ['username', 'username'],
],
'required array of non-strings' => [
[], null, null, null, [1, 2, 3],
],
];
}
}
{{/apiInfo}}

View File

@ -20,12 +20,16 @@
<testsuite name="Mock">
<directory>{{mockTestPath}}</directory>
</testsuite>
<testsuite name="Utils">
<directory>{{utilsTestPath}}</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">{{apiSrcPath}}</directory>
<directory suffix=".php">{{modelSrcPath}}</directory>
<directory suffix=".php">{{mockSrcPath}}</directory>
<directory suffix=".php">{{utilsSrcPath}}</directory>
</whitelist>
</filter>
<php>

View File

@ -0,0 +1,141 @@
<?php
/**
* {{traitNamePrefix}}StringUtils{{traitNameSuffix}}
*
* PHP version 7.1
*
* @package {{invokerPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**{{#apiInfo}}{{#appName}}
* {{{appName}}}
*
{{/appName}}
{{#appDescription}}
* {{{appDescription}}}
{{/appDescription}}
{{#version}}
* The version of the OpenAPI document: {{{version}}}
{{/version}}
{{#infoEmail}}
* Contact: {{{infoEmail}}}
{{/infoEmail}}
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace {{utilsPackage}};
/**
* {{traitNamePrefix}}StringUtils{{traitNameSuffix}} Class Doc Comment
* This class duplicates functionality of StringUtils.java and AbstractPhpCodegen.java classes.
*
* @package {{utilsPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
trait {{traitNamePrefix}}StringUtils{{traitNameSuffix}}
{
/**
* Camelize name (parameter, property, method, etc)
* This is recreated method of @link modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/StringUtils.java class.
*
* @param string $word string to be camelize
* @param bool $lowercaseFirstLetter lower case for first letter if set to true
*
* @return string camelized string
*/
public static function camelize($word, $lowercaseFirstLetter = false)
{
// Replace all slashes with dots (package separator)
$p = '/\/(.?)/';
$word = preg_replace($p, '.$1', $word);
// case out dots
$parts = explode('.', $word);
$str = '';
foreach ($parts as $z) {
if (strlen($z) > 0) {
$str .= strtoupper(substr($z, 0, 1)) . substr($z, 1);
}
}
$word = $str;
// Uppercase the class name.
$p = '/(\.?)(\w)([^\.]*)$/';
$word = preg_replace_callback($p, function ($matches) {
$rep = $matches[1] . strtoupper($matches[2]) . $matches[3];
$rep = preg_replace('/\$/', '\\\$', $rep);
return $rep;
}, $word);
// Remove all underscores (underscore_case to camelCase)
$p = '/(_)(.)/';
while (preg_match($p, $word, $matches) === 1) {
$original = $matches[2][0];
$upperCase = strtoupper($original);
if ($original === $upperCase) {
$word = preg_replace($p, '$2', $word);
} else {
$word = preg_replace($p, $upperCase, $word);
}
}
// Remove all hyphens (hyphen-case to camelCase)
$p = '/(-)(.)/';
while (preg_match($p, $word, $matches) === 1) {
$upperCase = strtoupper($matches[2][0]);
$word = preg_replace($p, $upperCase, $word);
}
if ($lowercaseFirstLetter === true && strlen($word) > 0) {
$i = 0;
$charAt = substr($word, $i, 1);
while (
$i + 1 < strlen($word)
&& !(
($charAt >= 'a' && $charAt <= 'z')
|| ($charAt >= 'A' && $charAt <= 'Z')
)
) {
$i++;
$charAt = substr($word, $i, 1);
}
$i++;
$word = strtolower(substr($word, 0, $i)) . substr($word, $i);
}
// remove all underscore
$word = str_replace('_', '', $word);
return $word;
}
/**
* Checks whether string is reserved php keyword.
* This is recreated method of @link modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPhpCodegen.java class.
*
* @param string $word Checked string
*
* @return bool
*/
public static function isReservedWord($word)
{
if (is_string($word) === false) {
return false;
}
// __halt_compiler is ommited because class names with underscores not allowed anyway
return in_array(
strtolower($word),
['abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', 'echo', 'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif', 'endswitch', 'endwhile', 'eval', 'exit', 'extends', 'final', 'for', 'foreach', 'function', 'global', 'goto', 'if', 'implements', 'include', 'include_once', 'instanceof', 'insteadof', 'interface', 'isset', 'list', 'namespace', 'new', 'or', 'print', 'private', 'protected', 'public', 'require', 'require_once', 'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use', 'var', 'while', 'xor']
);
}
}
{{/apiInfo}}

View File

@ -0,0 +1,108 @@
<?php
/**
* {{traitNamePrefix}}StringUtils{{traitNameSuffix}}Test
*
* PHP version 7.1
*
* @package {{invokerPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
*/
/**{{#apiInfo}}{{#appName}}
* {{{appName}}}
*
{{/appName}}
{{#appDescription}}
* {{{appDescription}}}
{{/appDescription}}
{{#version}}
* The version of the OpenAPI document: {{{version}}}
{{/version}}
{{#infoEmail}}
* Contact: {{{infoEmail}}}
{{/infoEmail}}
* Generated by: https://github.com/openapitools/openapi-generator.git
*/
/**
* NOTE: This class is auto generated by the openapi generator program.
* https://github.com/openapitools/openapi-generator
* Do not edit the class manually.
*/
namespace {{utilsPackage}};
use {{utilsPackage}}\{{traitNamePrefix}}StringUtils{{traitNameSuffix}} as StringUtils;
use PHPUnit\Framework\TestCase;
/**
* {{traitNamePrefix}}StringUtils{{traitNameSuffix}}Test Class Doc Comment
*
* @package {{utilsPackage}}
* @author OpenAPI Generator team
* @link https://github.com/openapitools/openapi-generator
* @coversDefaultClass \{{utilsPackage}}\{{traitNamePrefix}}StringUtils{{traitNameSuffix}}
*/
class {{traitNamePrefix}}StringUtils{{traitNameSuffix}}Test extends TestCase
{
/**
* @covers ::camelize
* @dataProvider provideWordsForCamelizeTest
*/
public function testCamelize($word, $lowercaseFirstLetter, $expectedWord)
{
$this->assertSame($expectedWord, StringUtils::camelize($word, $lowercaseFirstLetter));
}
public function provideWordsForCamelizeTest()
{
return [
// fixtures from modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/StringUtilsTest.java
['openApiServer/model/pet', null, 'OpenApiServerModelPet'],
['abcd', null, 'Abcd'],
['some-value', null, 'SomeValue'],
['some-Value', null, 'SomeValue'],
['some_value', null, 'SomeValue'],
['some_Value', null, 'SomeValue'],
['$type', null, '$Type'],
['abcd', true, 'abcd'],
['some-value', true, 'someValue'],
['some_value', true, 'someValue'],
['Abcd', true, 'abcd'],
['$type', true, '$type'],
['123', true, '123'],
['$123', true, '$123'],
];
}
/**
* @covers ::isReservedWord
* @dataProvider provideWordsForIsReservedTest
*/
public function testisReservedWord($word, $expected)
{
$this->assertSame($expected, StringUtils::isReservedWord($word));
}
public function provideWordsForIsReservedTest()
{
return [
['return', true],
['switch', true],
['class', true],
['interface', true],
['ABSTRACT', true],
['Trait', true],
['final', true],
['foobar', false],
['DateTime', false],
['Pet', false],
[123, false],
[null, false],
];
}
}
{{/apiInfo}}

View File

@ -0,0 +1,66 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
venv/
.venv/
.python-version
.pytest_cache
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
#Ipython Notebook
.ipynb_checkpoints

View File

@ -1,6 +1,13 @@
{{#useNose}}
coverage>=4.0.3
pytest>=1.3.7
nose>=1.3.7
pluggy>=0.3.1
py>=1.4.31
randomize>=0.13
{{/useNose}}
{{^useNose}}
pytest~=4.6.7 # needed for python 2.7+3.4
pytest-cov>=2.8.1
pytest-randomly==1.2.3 # needed for python 2.7+3.4
pytest-aiohttp>=0.3.0
{{/useNose}}

View File

@ -0,0 +1,10 @@
[tox]
envlist = py3
skipsdist=True
[testenv]
deps=-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands=
{{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}}

View File

@ -46,6 +46,7 @@ coverage.xml
.hypothesis/
venv/
.python-version
.pytest_cache
# Translations
*.mo

View File

@ -1,6 +1,13 @@
flask_testing==0.6.1
{{#useNose}}
coverage>=4.0.3
nose>=1.3.7
pluggy>=0.3.1
py>=1.4.31
randomize>=0.13
{{/useNose}}
{{^useNose}}
pytest~=4.6.7 # needed for python 2.7+3.4
pytest-cov>=2.8.1
pytest-randomly==1.2.3 # needed for python 2.7+3.4
{{/useNose}}
flask_testing==0.6.1

View File

@ -6,5 +6,4 @@ deps=-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands=
nosetests \
[]
{{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}}

View File

@ -45,7 +45,9 @@ coverage.xml
*,cover
.hypothesis/
venv/
.venv/
.python-version
.pytest_cache
# Translations
*.mo

View File

@ -1,4 +1,7 @@
connexion >= 2.0.2
connexion >= 2.5.0; python_version>="3.6"
connexion >= 2.3.0; python_version=="3.5"
connexion >= 2.3.0; python_version=="3.4"
connexion == 2.4.0; python_version<="2.7"
swagger-ui-bundle >= 0.0.2
python_dateutil >= 2.6.0
{{#supportPython2}}

View File

@ -1,6 +1,13 @@
flask_testing==0.6.1
{{#useNose}}
coverage>=4.0.3
nose>=1.3.7
pluggy>=0.3.1
py>=1.4.31
randomize>=0.13
{{/useNose}}
{{^useNose}}
pytest~=4.6.7 # needed for python 2.7+3.4
pytest-cov>=2.8.1
pytest-randomly==1.2.3 # needed for python 2.7+3.4
{{/useNose}}
flask_testing==0.6.1

View File

@ -6,5 +6,4 @@ deps=-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands=
nosetests \
[]
{{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}}

View File

@ -45,7 +45,9 @@ coverage.xml
*,cover
.hypothesis/
venv/
.venv/
.python-version
.pytest_cache
# Translations
*.mo

View File

@ -1,13 +1,13 @@
{{^asyncio}}
{{#useNose}}
coverage>=4.0.3
nose>=1.3.7
{{/asyncio}}
{{#asyncio}}
pytest>=3.6.0
pytest-cov>=2.6.1
{{/asyncio}}
pluggy>=0.3.1
py>=1.4.31
randomize>=0.13
{{/useNose}}
{{^useNose}}
pytest~=4.6.7 # needed for python 2.7+3.4
pytest-cov>=2.8.1
pytest-randomly==1.2.3 # needed for python 2.7+3.4
{{/useNose}}
mock; python_version<="2.7"

View File

@ -0,0 +1,13 @@
{{#useNose}}
[nosetests]
logging-clear-handlers=true
verbosity=2
randomize=true
exe=true
with-coverage=true
cover-package=petstore_api
cover-erase=true
{{/useNose}}
[flake8]
max-line-length=99

View File

@ -1,11 +1,12 @@
{{^asyncio}}
{{#useNose}}
coverage>=4.0.3
nose>=1.3.7
{{/asyncio}}
{{#asyncio}}
pytest>=3.6.0
pytest-cov>=2.6.1
{{/asyncio}}
pluggy>=0.3.1
py>=1.4.31
randomize>=0.13
{{/useNose}}
{{^useNose}}
pytest~=4.6.7 # needed for python 2.7+3.4
pytest-cov>=2.8.1
pytest-randomly==1.2.3 # needed for python 2.7+3.4
{{/useNose}}

View File

@ -11,10 +11,4 @@ deps=-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands=
{{^asyncio}}
nosetests \
[]
{{/asyncio}}
{{#asyncio}}
pytest -v --cov {{{packageName}}}
{{/asyncio}}
{{^useNose}}pytest --cov={{{packageName}}}{{/useNose}}{{#useNose}}nosetests{{/useNose}}

View File

@ -18,7 +18,7 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk-
chrono = { version = "0.4", features = ["serde"] }
futures = "0.1"
swagger = "2"
lazy_static = "0.2"
lazy_static = "1.4"
log = "0.3.0"
mime = "0.2.6"
multipart = "0.13.3"

View File

@ -86,7 +86,7 @@ fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<S
/// A client that implements the API by making HTTP calls out to a server.
pub struct Client<F> where
F: Future<Item=hyper::Response, Error=hyper::Error> + 'static {
client_service: Arc<Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=F>>>,
client_service: Arc<Box<dyn hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=F>>>,
base_path: String,
}
@ -197,7 +197,7 @@ impl Client<hyper::client::FutureResponse> {
handle: Handle,
base_path: &str,
protocol: Option<&'static str>,
connector_fn: Box<Fn(&Handle) -> C + Send + Sync>,
connector_fn: Box<dyn Fn(&Handle) -> C + Send + Sync>,
) -> Result<Client<hyper::client::FutureResponse>, ClientInitError>
where
C: hyper::client::Connect + hyper::client::Service,
@ -224,7 +224,7 @@ impl Client<hyper::client::FutureResponse> {
/// should be mentioned here.
#[deprecated(note="Use try_new_with_client_service instead")]
pub fn try_new_with_hyper_client(
hyper_client: Arc<Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>>>,
hyper_client: Arc<Box<dyn hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>>>,
handle: Handle,
base_path: &str
) -> Result<Client<hyper::client::FutureResponse>, ClientInitError>
@ -242,7 +242,7 @@ impl<F> Client<F> where
/// Constructor for creating a `Client` by passing in a pre-made `hyper` client Service.
///
/// This allows adding custom wrappers around the underlying transport, for example for logging.
pub fn try_new_with_client_service(client_service: Arc<Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=F>>>,
pub fn try_new_with_client_service(client_service: Arc<Box<dyn hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=F>>>,
handle: Handle,
base_path: &str)
-> Result<Client<F>, ClientInitError>
@ -258,7 +258,7 @@ impl<F, C> Api<C> for Client<F> where
F: Future<Item=hyper::Response, Error=hyper::Error> + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}}{
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{{operationId}}}Response, Error=ApiError>> {
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError>> {
let mut uri = format!(
"{}{{{basePathWithoutHost}}}{{path}}",
self.base_path{{#pathParams}}, {{{baseName}}}=utf8_percent_encode(&param_{{{paramName}}}.to_string(), ID_ENCODE_SET){{/pathParams}}
@ -269,7 +269,7 @@ impl<F, C> Api<C> for Client<F> where
{{^required}} if let Some({{{paramName}}}) = param_{{{paramName}}} {
query_string.append_pair("{{{baseName}}}", &{{{paramName}}}{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}});
}{{/required}}{{/queryParams}}
{{#authMethods}}{{#isApiKey}}{{#isKeyInQuery}} if let Some(auth_data) = (context as &Has<Option<AuthData>>).get().as_ref() {
{{#authMethods}}{{#isApiKey}}{{#isKeyInQuery}} if let Some(auth_data) = (context as &dyn Has<Option<AuthData>>).get().as_ref() {
if let AuthData::ApiKey(ref api_key) = *auth_data {
query_string.append_pair("{{keyParamName}}", api_key);
}
@ -416,10 +416,10 @@ impl<F, C> Api<C> for Client<F> where
{{/consumesMultipart}}
{{/vendorExtensions}}
request.headers_mut().set(XSpanId((context as &Has<XSpanIdString>).get().0.clone()));
request.headers_mut().set(XSpanId((context as &dyn Has<XSpanIdString>).get().0.clone()));
{{#vendorExtensions.hasHeaderAuthMethods}}
(context as &Has<Option<AuthData>>).get().as_ref().map(|auth_data| {
(context as &dyn Has<Option<AuthData>>).get().as_ref().map(|auth_data| {
// Currently only authentication with Basic, API Key, and Bearer are supported
match auth_data {
{{#authMethods}}
@ -480,7 +480,7 @@ impl<F, C> Api<C> for Client<F> where
{{#headers}} header! { (Response{{{nameInCamelCase}}}, "{{{baseName}}}") => [{{{datatype}}}] }
let response_{{{name}}} = match response.headers().get::<Response{{{nameInCamelCase}}}>() {
Some(response_{{{name}}}) => response_{{{name}}}.0.clone(),
None => return Box::new(future::err(ApiError(String::from("Required response header {{{baseName}}} for response {{{code}}} was not found.")))) as Box<Future<Item=_, Error=_>>,
None => return Box::new(future::err(ApiError(String::from("Required response header {{{baseName}}} for response {{{code}}} was not found.")))) as Box<dyn Future<Item=_, Error=_>>,
};
{{/headers}}
let body = response.body();
@ -491,9 +491,10 @@ impl<F, C> Api<C> for Client<F> where
.map_err(|e| ApiError(format!("Failed to read response: {}", e)))
.and_then(|body|
{{#vendorExtensions}}
{{#producesPlainText}}
{{#producesBytes}}
Ok(swagger::ByteArray(body.to_vec()))
{{/producesPlainText}}{{^producesPlainText}}
{{/producesBytes}}
{{^producesBytes}}
str::from_utf8(&body)
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
.and_then(|body|
@ -502,12 +503,17 @@ impl<F, C> Api<C> for Client<F> where
// once https://github.com/RReverser/serde-xml-rs/pull/45 is accepted upstream
serde_xml_rs::from_str::<{{{dataType}}}>(body)
.map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))
{{/producesXml}}{{#producesJson}}
{{/producesXml}}
{{#producesJson}}
serde_json::from_str::<{{{dataType}}}>(body)
.map_err(|e| e.into())
{{/producesJson}}
{{#producesPlainText}}
Ok(body.to_string())
{{/producesPlainText}}
)
{{/producesPlainText}}{{/vendorExtensions}}
{{/producesBytes}}
{{/vendorExtensions}}
)
.map(move |body| {
{{{operationId}}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}{{^headers}}(body){{/headers}}{{#headers}}{{#-first}}{ body: body, {{/-first}}{{{name}}}: response_{{{name}}}{{^-last}}, {{/-last}}{{#-last}} }{{/-last}}{{/headers}}
@ -526,7 +532,7 @@ impl<F, C> Api<C> for Client<F> where
{{/headers}}
)
{{/dataType}}
) as Box<Future<Item=_, Error=_>>
) as Box<dyn Future<Item=_, Error=_>>
},
{{/responses}}
code => {
@ -546,7 +552,7 @@ impl<F, C> Api<C> for Client<F> where
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
})))
)
) as Box<Future<Item=_, Error=_>>
) as Box<dyn Future<Item=_, Error=_>>
}
}
}))
@ -577,7 +583,7 @@ impl From<openssl::error::ErrorStack> for ClientInitError {
impl fmt::Display for ClientInitError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
(self as &fmt::Debug).fmt(f)
(self as &dyn fmt::Debug).fmt(f)
}
}

View File

@ -72,7 +72,7 @@ fn main() {
{{#vendorExtensions}}{{#noClientExample}}// Disabled because there's no example.
// {{/noClientExample}}Some("{{{operationId}}}") => {
{{#noClientExample}}// {{/noClientExample}} let result = core.run(client.{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}{{^-first}}, {{/-first}}{{#vendorExtensions}}{{{example}}}{{/vendorExtensions}}{{/allParams}}));
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has<XSpanIdString>).get().clone());
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has<XSpanIdString>).get().clone());
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} },
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
_ => {

View File

@ -31,7 +31,7 @@ impl<C> Server<C> {
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#summary}} /// {{{summary}}}{{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{{operationId}}}Response, Error=ApiError>> {
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError>> {
let context = context.clone();
println!("{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}, {{{paramName}}}{{/allParams}}, context.get().0.clone());{{#allParams}}{{/allParams}}
Box::new(futures::failed("Generic failure".into()))

View File

@ -92,7 +92,7 @@ pub enum {{{operationId}}}Response {
pub trait Api<C> {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#summary}} /// {{{summary}}}{{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{{operationId}}}Response, Error=ApiError>>;
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError>>;
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
}
@ -100,7 +100,7 @@ pub trait Api<C> {
pub trait ApiNoContext {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#summary}} /// {{{summary}}}{{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}) -> Box<Future<Item={{{operationId}}}Response, Error=ApiError>>;
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError>>;
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
}
@ -119,7 +119,7 @@ impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T {
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
{{#summary}} /// {{{summary}}}{{/summary}}
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}) -> Box<Future<Item={{{operationId}}}Response, Error=ApiError>> {
fn {{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}(&self{{#allParams}}, {{{paramName}}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}) -> Box<dyn Future<Item={{{operationId}}}Response, Error=ApiError>> {
self.api().{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}{{{paramName}}}, {{/allParams}}&self.context())
}
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}

View File

@ -7,7 +7,7 @@ pub mod responses {
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}{{#responses}}{{#produces}}{{#-first}}{{#dataType}}
lazy_static! {
/// Create Mime objects for the response content types for {{{operationId}}}
pub static ref {{#vendorExtensions}}{{{uppercase_operation_id}}}_{{x-uppercaseResponseId}}{{/vendorExtensions}}: Mime = "{{{mediaType}}}".parse().unwrap();
pub static ref {{#vendorExtensions}}{{{uppercase_operation_id}}}_{{x-uppercaseResponseId}}: Mime = "{{{mimeType}}}".parse().unwrap();{{/vendorExtensions}}
}
{{/dataType}}{{/-first}}{{/produces}}{{/responses}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
}

View File

@ -128,7 +128,7 @@ where
type Request = (Request, C);
type Response = Response;
type Error = Error;
type Future = Box<Future<Item=Response, Error=Error>>;
type Future = Box<dyn Future<Item=Response, Error=Error>>;
fn call(&self, (req, mut context): Self::Request) -> Self::Future {
let api_impl = self.api_impl.clone();
@ -143,7 +143,7 @@ where
&hyper::Method::{{vendorExtensions.HttpMethod}} if path.matched(paths::ID_{{vendorExtensions.PATH_ID}}) => {
{{#hasAuthMethods}}
{
let authorization = match (&context as &Has<Option<Authorization>>).get() {
let authorization = match (&context as &dyn Has<Option<Authorization>>).get() {
&Some(ref authorization) => authorization,
&None => return Box::new(future::ok(Response::new()
.with_status(StatusCode::Forbidden)
@ -257,7 +257,7 @@ where
// values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields.
Box::new(body.concat2()
.then(move |result| -> Box<Future<Item=Response, Error=Error>> {
.then(move |result| -> Box<dyn Future<Item=Response, Error=Error>> {
match result {
Ok(body) => {
{{#vendorExtensions}}
@ -313,7 +313,7 @@ where
// values, rather than causing a 400 response). Produce warning header and logs for
// any unused fields.
Box::new(body.concat2()
.then(move |result| -> Box<Future<Item=Response, Error=Error>> {
.then(move |result| -> Box<dyn Future<Item=Response, Error=Error>> {
match result {
Ok(body) => {
// Read Form Parameters from body
@ -404,7 +404,7 @@ where
Box::new(api_impl.{{#vendorExtensions}}{{{operation_id}}}{{/vendorExtensions}}({{#allParams}}param_{{{paramName}}}{{#isListContainer}}.as_ref(){{/isListContainer}}, {{/allParams}}&context)
.then(move |result| {
let mut response = Response::new();
response.headers_mut().set(XSpanId((&context as &Has<XSpanIdString>).get().0.to_string()));
response.headers_mut().set(XSpanId((&context as &dyn Has<XSpanIdString>).get().0.to_string()));
{{#bodyParams}}{{#vendorExtensions}}{{^consumesPlainText}}
if !unused_elements.is_empty() {
response.headers_mut().set(Warning(format!("Ignoring unknown fields in body: {:?}", unused_elements)));
@ -444,18 +444,28 @@ where
response.headers_mut().set(ContentType(mimetypes::responses::{{#vendorExtensions}}{{{uppercase_operation_id}}}_{{x-uppercaseResponseId}}{{/vendorExtensions}}.clone()));
{{/dataType}}{{/-first}}{{/produces}}
{{#dataType}}
{{#vendorExtensions}}{{#producesXml}}{{^has_namespace}}
{{#vendorExtensions}}
{{#producesXml}}
{{^has_namespace}}
let body = serde_xml_rs::to_string(&body).expect("impossible to fail to serialize");
{{/has_namespace}}{{#has_namespace}}
{{/has_namespace}}
{{#has_namespace}}
let mut namespaces = BTreeMap::new();
// An empty string is used to indicate a global namespace in xmltree.
namespaces.insert("".to_string(), {{{dataType}}}::NAMESPACE.to_string());
let body = serde_xml_rs::to_string_with_namespaces(&body, namespaces).expect("impossible to fail to serialize");
{{/has_namespace}}{{/producesXml}}{{#producesJson}}
{{/has_namespace}}
{{/producesXml}}
{{#producesJson}}
let body = serde_json::to_string(&body).expect("impossible to fail to serialize");
{{/producesJson}}{{#producesPlainText}}
{{/producesJson}}
{{#producesBytes}}
let body = body.0;
{{/producesPlainText}}{{/vendorExtensions}}
{{/producesBytes}}
{{#producesPlainText}}
let body = body;
{{/producesPlainText}}
{{/vendorExtensions}}
response.set_body(body);
{{/dataType}}
},
@ -476,7 +486,7 @@ where
{{^consumesMultipart}}
{{^bodyParams}}
}}
}) as Box<Future<Item=Response, Error=Error>>
}) as Box<dyn Future<Item=Response, Error=Error>>
{{/bodyParams}}
{{/consumesMultipart}}
{{/vendorExtensions}}
@ -486,13 +496,13 @@ where
Err(e) => Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't read body parameter {{{baseName}}}: {}", e)))),
}
})
) as Box<Future<Item=Response, Error=Error>>
) as Box<dyn Future<Item=Response, Error=Error>>
{{/-first}}
{{/bodyParams}}
{{#vendorExtensions}}
{{#consumesMultipart}}
{{^bodyParams}}
as Box<Future<Item=Response, Error=Error>>
as Box<dyn Future<Item=Response, Error=Error>>
},
Err(e) => Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't read multipart body")))),
}
@ -503,7 +513,7 @@ where
{{/vendorExtensions}}
},
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
_ => Box::new(future::ok(Response::new().with_status(StatusCode::NotFound))) as Box<Future<Item=Response, Error=Error>>,
_ => Box::new(future::ok(Response::new().with_status(StatusCode::NotFound))) as Box<dyn Future<Item=Response, Error=Error>>,
}
}
}

View File

@ -488,6 +488,7 @@ public class DefaultCodegenTest {
test.setPropertyBaseName("className");
test.getMappedModels().add(new CodegenDiscriminator.MappedModel("Dog", "Dog"));
test.getMappedModels().add(new CodegenDiscriminator.MappedModel("Cat", "Cat"));
test.getMappedModels().add(new CodegenDiscriminator.MappedModel("BigCat", "BigCat"));
Assert.assertEquals(discriminator, test);
}

View File

@ -22,7 +22,7 @@ import org.testng.annotations.Test;
import io.swagger.v3.oas.models.OpenAPI;
/** check against ping.yaml spec. */
/** unit test asciidoc markup generation against ping.yaml openapi spec. */
public class AsciidocGeneratorTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AsciidocGeneratorTest.class);
@ -114,4 +114,40 @@ public class AsciidocGeneratorTest {
}
@Test
public void testHeaderAttributesFlagRemovesAttributesFromMarkupHeaderSection() throws Exception {
File output = Files.createTempDirectory("test").toFile();
LOGGER.info("test: generating sample markup " + output.getAbsolutePath());
Map<String, Object> props = new TreeMap<String, Object>();
props.put("specDir", "spec");
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("asciidoc")
.setInputSpec("src/test/resources/3_0/ping.yaml")
.setOutputDir(output.getAbsolutePath())
.addAdditionalProperty(AsciidocDocumentationCodegen.HEADER_ATTRIBUTES_FLAG, "false") // option avoids generation of attributes
.addAdditionalProperty(AsciidocDocumentationCodegen.SPEC_DIR, "SPEC-DIR")
.addAdditionalProperty(AsciidocDocumentationCodegen.SNIPPET_DIR, "MY/SNIPPET/DIR");
DefaultGenerator generator = new DefaultGenerator();
boolean markupFileGenerated = false;
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
for (File file : files) {
if (file.getName().equals("index.adoc")) {
markupFileGenerated = true;
String markupContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
Assert.assertFalse(markupContent.contains(":specDir: SPEC-DIR"),
"not expected :specDir: in: " + markupContent.substring(0, 250));
Assert.assertFalse(markupContent.contains(":snippetDir: MY/SNIPPET/DIR"),
"not expected :snippetDir: in: " + markupContent.substring(0, 250));
Assert.assertFalse(markupContent.contains(":toc:"),
"not expected :toc: in: " + markupContent.substring(0, 250)); // typical attributes not found in markup.
}
}
Assert.assertTrue(markupFileGenerated, "index.adoc is not generated!");
}
}

View File

@ -52,6 +52,12 @@ public class DartClientOptionsTest extends AbstractOptionsTest {
times = 1;
clientCodegen.setPubDescription(DartClientOptionsProvider.PUB_DESCRIPTION_VALUE);
times = 1;
clientCodegen.setPubAuthor(DartClientOptionsProvider.PUB_AUTHOR_VALUE);
times = 1;
clientCodegen.setPubAuthorEmail(DartClientOptionsProvider.PUB_AUTHOR_EMAIL_VALUE);
times = 1;
clientCodegen.setPubHomepage(DartClientOptionsProvider.PUB_HOMEPAGE_VALUE);
times = 1;
clientCodegen.setSourceFolder(DartClientOptionsProvider.SOURCE_FOLDER_VALUE);
times = 1;
clientCodegen.setUseEnumExtension(Boolean.valueOf(DartClientOptionsProvider.USE_ENUM_EXTENSION));

View File

@ -21,10 +21,8 @@ import mockit.Expectations;
import mockit.Tested;
import org.openapitools.codegen.AbstractOptionsTest;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.languages.DartClientCodegen;
import org.openapitools.codegen.languages.DartDioClientCodegen;
import org.openapitools.codegen.options.DartDioClientOptionsProvider;
import org.openapitools.codegen.options.DartDioClientOptionsProvider;
public class DartDioClientOptionsTest extends AbstractOptionsTest {
@ -54,6 +52,12 @@ public class DartDioClientOptionsTest extends AbstractOptionsTest {
times = 1;
clientCodegen.setPubDescription(DartDioClientOptionsProvider.PUB_DESCRIPTION_VALUE);
times = 1;
//clientCodegen.setPubAuthor(DartDioClientOptionsProvider.PUB_AUTHOR_VALUE);
//times = 1;
//clientCodegen.setPubAuthorEmail(DartDioClientOptionsProvider.PUB_AUTHOR_EMAIL_VALUE);
//times = 1;
//clientCodegen.setPubHomepage(DartDioClientOptionsProvider.PUB_HOMEPAGE_VALUE);
//times = 1;
clientCodegen.setSourceFolder(DartDioClientOptionsProvider.SOURCE_FOLDER_VALUE);
times = 1;
clientCodegen.setUseEnumExtension(Boolean.valueOf(DartDioClientOptionsProvider.USE_ENUM_EXTENSION));

View File

@ -58,6 +58,8 @@ public class GoClientOptionsTest extends AbstractOptionsTest {
times = 1;
clientCodegen.setStructPrefix(Boolean.valueOf(GoClientOptionsProvider.STRUCT_PREFIX_VALUE));
times = 1;
clientCodegen.setWithAWSV4Signature(Boolean.valueOf(GoClientOptionsProvider.WITH_AWSV4_SIGNATURE));
times = 1;
}};
}
}

View File

@ -45,6 +45,7 @@ public abstract class JavaJaxrsBaseTest {
String jsonSubType = "@JsonSubTypes({\n" +
" @JsonSubTypes.Type(value = Dog.class, name = \"Dog\"),\n" +
" @JsonSubTypes.Type(value = Cat.class, name = \"Cat\"),\n" +
" @JsonSubTypes.Type(value = BigDog.class, name = \"BigDog\"),\n" +
"})";
assertFileContains(generator, outputPath + "/src/gen/java/org/openapitools/model/Animal.java", jsonTypeInfo, jsonSubType);
}

View File

@ -274,4 +274,24 @@ public class MysqlSchemaCodegenTest {
Assert.assertFalse(codegen.getJsonDataTypeEnabled());
}
@Test
public void testSetIdentifierNamingConvention() {
final MysqlSchemaCodegen codegen = new MysqlSchemaCodegen();
Assert.assertSame("original", codegen.getIdentifierNamingConvention());
codegen.setIdentifierNamingConvention("invalidValue");
Assert.assertSame("original", codegen.getIdentifierNamingConvention());
codegen.setIdentifierNamingConvention("snake_case");
Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention());
codegen.setIdentifierNamingConvention("anotherInvalid");
Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention());
}
@Test
public void testGetIdentifierNamingConvention() {
final MysqlSchemaCodegen codegen = new MysqlSchemaCodegen();
Assert.assertSame("original", codegen.getIdentifierNamingConvention());
codegen.setIdentifierNamingConvention("snake_case");
Assert.assertSame("snake_case", codegen.getIdentifierNamingConvention());
}
}

View File

@ -45,6 +45,8 @@ public class MysqlSchemaOptionsTest extends AbstractOptionsTest {
times = 1;
clientCodegen.setJsonDataTypeEnabled(Boolean.valueOf(MysqlSchemaOptionsProvider.JSON_DATA_TYPE_ENABLED_VALUE));
times = 1;
clientCodegen.setIdentifierNamingConvention(MysqlSchemaOptionsProvider.IDENTIFIER_NAMING_CONVENTION_VALUE);
times = 1;
}};
}
}

View File

@ -28,9 +28,12 @@ public class DartClientOptionsProvider implements OptionsProvider {
public static final String SORT_MODEL_PROPERTIES_VALUE = "false";
public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true";
public static final String BROWSER_CLIENT_VALUE = "true";
public static final String PUB_NAME_VALUE = "swagger";
public static final String PUB_NAME_VALUE = "openapi";
public static final String PUB_VERSION_VALUE = "1.0.0-SNAPSHOT";
public static final String PUB_DESCRIPTION_VALUE = "Swagger API client dart";
public static final String PUB_DESCRIPTION_VALUE = "OpenAPI API client dart";
public static final String PUB_AUTHOR_VALUE = "Author";
public static final String PUB_AUTHOR_EMAIL_VALUE = "author@homepage";
public static final String PUB_HOMEPAGE_VALUE = "Homepage";
public static final String SOURCE_FOLDER_VALUE = "src";
public static final String USE_ENUM_EXTENSION = "true";
public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false";
@ -51,6 +54,9 @@ public class DartClientOptionsProvider implements OptionsProvider {
.put(DartClientCodegen.PUB_NAME, PUB_NAME_VALUE)
.put(DartClientCodegen.PUB_VERSION, PUB_VERSION_VALUE)
.put(DartClientCodegen.PUB_DESCRIPTION, PUB_DESCRIPTION_VALUE)
.put(DartClientCodegen.PUB_AUTHOR, PUB_AUTHOR_VALUE)
.put(DartClientCodegen.PUB_AUTHOR_EMAIL, PUB_AUTHOR_EMAIL_VALUE)
.put(DartClientCodegen.PUB_HOMEPAGE, PUB_HOMEPAGE_VALUE)
.put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_VALUE)
.put(DartClientCodegen.USE_ENUM_EXTENSION, USE_ENUM_EXTENSION)
.put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE)

View File

@ -18,29 +18,33 @@
package org.openapitools.codegen.options;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.languages.DartDioClientCodegen;
import org.openapitools.codegen.languages.DartDioClientCodegen;
public class DartDioClientOptionsProvider implements OptionsProvider {
public static final String SORT_PARAMS_VALUE = "true";
public static final String SORT_MODEL_PROPERTIES_VALUE = "false";
public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true";
public static final String BROWSER_CLIENT_VALUE = "true";
public static final String PUB_NAME_VALUE = "swagger";
public static final String PUB_NAME_VALUE = "openapi";
public static final String PUB_VERSION_VALUE = "1.0.0-SNAPSHOT";
public static final String PUB_DESCRIPTION_VALUE = "Swagger API client dart";
public static final String PUB_DESCRIPTION_VALUE = "OpenAPI API client dart";
public static final String SOURCE_FOLDER_VALUE = "src";
public static final String USE_ENUM_EXTENSION = "true";
public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false";
public static final String PREPEND_FORM_OR_BODY_PARAMETERS_VALUE = "true";
public static final String DATE_LIBRARY = "core";
public static final String NULLABLE_FIELDS = "true";
public static final String PUB_AUTHOR_VALUE = "Author";
public static final String PUB_AUTHOR_EMAIL_VALUE = "author@homepage";
public static final String PUB_HOMEPAGE_VALUE = "Homepage";
@Override
public String getLanguage() {
return "dart";
return "dart-dio";
}
@Override
@ -53,6 +57,9 @@ public class DartDioClientOptionsProvider implements OptionsProvider {
.put(DartDioClientCodegen.PUB_NAME, PUB_NAME_VALUE)
.put(DartDioClientCodegen.PUB_VERSION, PUB_VERSION_VALUE)
.put(DartDioClientCodegen.PUB_DESCRIPTION, PUB_DESCRIPTION_VALUE)
.put(DartDioClientCodegen.PUB_AUTHOR, PUB_AUTHOR_VALUE)
.put(DartDioClientCodegen.PUB_AUTHOR_EMAIL, PUB_AUTHOR_EMAIL_VALUE)
.put(DartDioClientCodegen.PUB_HOMEPAGE, PUB_HOMEPAGE_VALUE)
.put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_VALUE)
.put(DartDioClientCodegen.USE_ENUM_EXTENSION, USE_ENUM_EXTENSION)
.put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE)
@ -60,7 +67,6 @@ public class DartDioClientOptionsProvider implements OptionsProvider {
.put(DartDioClientCodegen.SUPPORT_DART2, "false")
.put(DartDioClientCodegen.DATE_LIBRARY, DATE_LIBRARY)
.put(DartDioClientCodegen.NULLABLE_FIELDS, NULLABLE_FIELDS)
.build();
}

View File

@ -32,6 +32,7 @@ public class GoClientOptionsProvider implements OptionsProvider {
public static final Boolean PREPEND_FORM_OR_BODY_PARAMETERS_VALUE = true;
public static final boolean IS_GO_SUBMODULE_VALUE = true;
public static final boolean STRUCT_PREFIX_VALUE = true;
public static final boolean WITH_AWSV4_SIGNATURE = true;
@Override
public String getLanguage() {
@ -50,6 +51,7 @@ public class GoClientOptionsProvider implements OptionsProvider {
.put(CodegenConstants.ENUM_CLASS_PREFIX, "true")
.put(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS, "true")
.put(CodegenConstants.IS_GO_SUBMODULE, "true")
.put(CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT, "true")
.put("structPrefix", "true")
.build();
}

View File

@ -24,6 +24,7 @@ import java.util.Map;
public class MysqlSchemaOptionsProvider implements OptionsProvider {
public static final String DEFAULT_DATABASE_NAME_VALUE = "database_name";
public static final String JSON_DATA_TYPE_ENABLED_VALUE = "false";
public static final String IDENTIFIER_NAMING_CONVENTION_VALUE = "snake_case";
@Override
public String getLanguage() {
@ -35,6 +36,7 @@ public class MysqlSchemaOptionsProvider implements OptionsProvider {
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
return builder.put(MysqlSchemaCodegen.DEFAULT_DATABASE_NAME, DEFAULT_DATABASE_NAME_VALUE)
.put(MysqlSchemaCodegen.JSON_DATA_TYPE_ENABLED, JSON_DATA_TYPE_ENABLED_VALUE)
.put(MysqlSchemaCodegen.IDENTIFIER_NAMING_CONVENTION, IDENTIFIER_NAMING_CONVENTION_VALUE)
.build();
}

View File

@ -28,6 +28,7 @@ public class PythonClientOptionsProvider implements OptionsProvider {
public static final String PROJECT_NAME_VALUE = "swagger-client-python";
public static final String PACKAGE_VERSION_VALUE = "1.0.0-SNAPSHOT";
public static final String PACKAGE_URL_VALUE = "";
public static final String USE_NOSE_VALUE = "false";
@Override
public String getLanguage() {
@ -45,6 +46,7 @@ public class PythonClientOptionsProvider implements OptionsProvider {
.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "true")
.put(CodegenConstants.SOURCECODEONLY_GENERATION, "false")
.put(CodegenConstants.LIBRARY, "urllib3")
.put(PythonClientCodegen.USE_NOSE, USE_NOSE_VALUE)
.build();
}

View File

@ -56,6 +56,9 @@ public class PythonClientOptionsTest extends AbstractOptionsTest {
clientCodegen.setPackageUrl(PythonClientOptionsProvider.PACKAGE_URL_VALUE);
times = 1;
clientCodegen.setUseNose(PythonClientOptionsProvider.USE_NOSE_VALUE);
times = 1;
clientCodegen.packagePath();
result = PythonClientOptionsProvider.PACKAGE_NAME_VALUE.replace('.', File.separatorChar);
minTimes = 1;

View File

@ -1330,6 +1330,14 @@ definitions:
properties:
declawed:
type: boolean
BigCat:
allOf:
- $ref: '#/definitions/Cat'
- type: object
properties:
kind:
type: string
enum: [lions, tigers, leopards, jaguars]
Animal:
type: object
discriminator: className

View File

@ -0,0 +1,33 @@
openapi: 3.0.0
servers:
- url: 'http://petstore.swagger.io/v2'
info:
description: Test for issue 4062
version: 1.0.0
title: OpenAPI Petstore
license:
name: Apache-2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
paths:
/enum:
get:
tags:
- enum
summary: Get enums
description: ''
operationId: getEnum
responses:
'200':
description: success
content:
application/json:
schema:
$ref: '#/components/schemas/PetEnum'
components:
schemas:
PetEnum:
type: string
description: An enum with complex-ish naming
enum:
- myFirstValue
- MY_SECOND_VALUE

View File

@ -45,6 +45,56 @@ paths:
description: 'OK'
'400':
description: Bad Request
/multiget:
get:
summary: Get some stuff.
responses:
200:
description: JSON rsp
content:
application/json:
schema:
$ref: "#/components/schemas/anotherXmlObject"
201:
description: XML rsp
content:
application/xml:
schema:
type: object
properties:
foo:
type: string
202:
description: octet rsp
content:
application/octet-stream:
schema:
type: string
format: binary
203:
description: string rsp
content:
text/plain:
schema:
type: string
204:
description: Duplicate Response long text. One.
content:
application/json:
schema:
$ref: "#/components/schemas/anotherXmlObject"
205:
description: Duplicate Response long text. Two.
content:
application/json:
schema:
$ref: "#/components/schemas/anotherXmlObject"
206:
description: Duplicate Response long text. Three.
content:
application/json:
schema:
$ref: "#/components/schemas/anotherXmlObject"
/xml_other:
post:
requestBody:

Some files were not shown because too many files have changed in this diff Show More