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;
@ -159,7 +161,7 @@ public class AsciidocDocumentationCodegen extends DefaultCodegen implements Code
static String sanitize(final String name) {
String sanitized = name == null ? "" : name.trim();
sanitized = sanitized.replace("//", "/"); // rest paths may or may not end with slashes, leading to redundant
// path separators.
// path separators.
return sanitized.startsWith(File.separator) || sanitized.startsWith("/") ? sanitized.substring(1) : sanitized;
}
@ -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: .")
.defaultValue("."));
"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: .")
.defaultValue(".."));
"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,8 +122,9 @@ public class DartDioClientCodegen extends DartClientCodegen {
return "Generates a Dart Dio client library.";
}
@Override public void setBrowserClient(boolean browserClient) {
super.browserClient = browserClient;
@Override
public void setBrowserClient(boolean browserClient) {
super.browserClient = browserClient;
}
@Override
@ -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;
@ -127,7 +130,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
"and", "del", "from", "not", "while", "as", "elif", "global", "or", "with",
"assert", "else", "if", "pass", "yield", "break", "except", "import",
"print", "class", "exec", "in", "raise", "continue", "finally", "is",
"return", "def", "for", "lambda", "try", "self", "nonlocal", "None", "True",
"return", "def", "for", "lambda", "try", "self", "nonlocal", "None", "True",
"False", "async", "await"));
regexModifiers = new HashMap<Character, String>();
@ -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+)");
@ -190,7 +195,7 @@ public class PythonClientCodegen extends DefaultCodegen implements CodegenConfig
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
}
}
Boolean generateSourceCodeOnly = false;
if (additionalProperties.containsKey(CodegenConstants.SOURCECODEONLY_GENERATION)) {
@ -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)) {
rsp.vendorExtensions.put("producesPlainText", true);
} 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,14 +659,20 @@ 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()));
delimiter = "; ";
}
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}}}
= {{{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,10 +229,10 @@ final class OpenApiDataMocker implements IMocker
* Shortcut to mock array type
* Equivalent to mockData(DATA_TYPE_ARRAY);
*
* @param array $items 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
* @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
*
* @throws \InvalidArgumentException when invalid arguments passed
*
@ -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,10 +197,10 @@ interface {{interfaceNamePrefix}}OpenApiDataMocker{{interfaceNameSuffix}}
* Shortcut to mock array type
* Equivalent to mockData(DATA_TYPE_ARRAY);
*
* @param array $items 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
* @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
*
* @throws \InvalidArgumentException when invalid arguments passed
*
@ -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,28 +475,54 @@ class OpenApiDataMockerTest extends TestCase
$this->assertContainsOnly($expectedItemsType, $arr, true);
}
$dataFormat = $items['dataFormat'] ?? null;
if (is_array($items)) {
$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 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 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;
// items field array properties
$subItems = $items['items'] ?? null;
$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
mock; python_version<="2.7"
{{/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,24 +491,30 @@ impl<F, C> Api<C> for Client<F> where
.map_err(|e| ApiError(format!("Failed to read response: {}", e)))
.and_then(|body|
{{#vendorExtensions}}
{{#producesPlainText}}
Ok(swagger::ByteArray(body.to_vec()))
{{/producesPlainText}}{{^producesPlainText}}
str::from_utf8(&body)
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
.and_then(|body|
{{#producesXml}}
// ToDo: this will move to swagger-rs and become a standard From conversion trait
// 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}}
serde_json::from_str::<{{{dataType}}}>(body)
.map_err(|e| e.into())
{{/producesJson}}
)
{{/producesPlainText}}{{/vendorExtensions}}
)
{{#producesBytes}}
Ok(swagger::ByteArray(body.to_vec()))
{{/producesBytes}}
{{^producesBytes}}
str::from_utf8(&body)
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
.and_then(|body|
{{#producesXml}}
// ToDo: this will move to swagger-rs and become a standard From conversion trait
// 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}}
serde_json::from_str::<{{{dataType}}}>(body)
.map_err(|e| e.into())
{{/producesJson}}
{{#producesPlainText}}
Ok(body.to_string())
{{/producesPlainText}}
)
{{/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);
@ -113,5 +113,41 @@ public class AsciidocGeneratorTest {
Assert.assertTrue(markupFileGenerated, "index.adoc is not generated!");
}
@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,8 +67,7 @@ public class DartDioClientOptionsProvider implements OptionsProvider {
.put(DartDioClientCodegen.SUPPORT_DART2, "false")
.put(DartDioClientCodegen.DATE_LIBRARY, DATE_LIBRARY)
.put(DartDioClientCodegen.NULLABLE_FIELDS, NULLABLE_FIELDS)
.build();
.build();
}
@Override

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