forked from loafle/openapi-generator-original
Python AIOHTTP server generator (#1470)
* Astract factory for generators based on connexion * Add aiohttp server generator * Fix flask tests * Normalize python-flask folder names
This commit is contained in:
parent
4652023b7c
commit
80ca67cfda
@ -26,9 +26,25 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# if you've executed sbt assembly previously it will use that instead.
|
# if you've executed sbt assembly previously it will use that instead.
|
||||||
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
|
input=modules/openapi-generator/src/test/resources/3_0/petstore.yaml
|
||||||
#ags="generate -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -DsupportPython2=true $@"
|
out_folder=samples/server/openapi3/petstore/python-flask-python2
|
||||||
ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -c bin/supportPython2.json -D service $@"
|
resources=modules/openapi-generator/src/main/resources/python-flask
|
||||||
|
|
||||||
|
# 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 $resources -i $input -g python-flask -o $out_folder -c bin/supportPython2.json -D service $@"
|
||||||
|
|
||||||
|
rm -rf $out_folder/.openapi*
|
||||||
|
rm -rf $out_folder/openapi_server
|
||||||
|
rm $out_folder/.dockerignore
|
||||||
|
rm $out_folder/.gitignore
|
||||||
|
rm $out_folder/.travis.yml
|
||||||
|
rm $out_folder/Dockerfile
|
||||||
|
rm $out_folder/git_push.sh
|
||||||
|
rm $out_folder/README.md
|
||||||
|
rm $out_folder/requirements.txt
|
||||||
|
rm $out_folder/setup.py
|
||||||
|
rm $out_folder/test-requirements.txt
|
||||||
|
rm $out_folder/tox.ini
|
||||||
|
|
||||||
rm -rf samples/server/petstore/flaskConnexion-python2/*
|
|
||||||
java $JAVA_OPTS -jar $executable $ags
|
java $JAVA_OPTS -jar $executable $ags
|
||||||
|
@ -26,8 +26,25 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# if you've executed sbt assembly previously it will use that instead.
|
# if you've executed sbt assembly previously it will use that instead.
|
||||||
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
|
input=modules/openapi-generator/src/test/resources/3_0/petstore.yaml
|
||||||
ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion -Dservice $@"
|
out_folder=samples/server/openapi3/petstore/python-flask
|
||||||
|
resources=modules/openapi-generator/src/main/resources/python-flask
|
||||||
|
|
||||||
|
# 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 $resources -i $input -g python-flask -o $out_folder -Dservice $@"
|
||||||
|
|
||||||
|
rm -rf $out_folder/.openapi*
|
||||||
|
rm -rf $out_folder/openapi_server
|
||||||
|
rm $out_folder/.dockerignore
|
||||||
|
rm $out_folder/.gitignore
|
||||||
|
rm $out_folder/.travis.yml
|
||||||
|
rm $out_folder/Dockerfile
|
||||||
|
rm $out_folder/git_push.sh
|
||||||
|
rm $out_folder/README.md
|
||||||
|
rm $out_folder/requirements.txt
|
||||||
|
rm $out_folder/setup.py
|
||||||
|
rm $out_folder/test-requirements.txt
|
||||||
|
rm $out_folder/tox.ini
|
||||||
|
|
||||||
rm -rf samples/server/petstore/flaskConnexion/*
|
|
||||||
java $JAVA_OPTS -jar $executable $ags
|
java $JAVA_OPTS -jar $executable $ags
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
./bin/python-flask-petstore.sh
|
|
||||||
./bin/python-flask-petstore-python2.sh
|
|
@ -1,34 +0,0 @@
|
|||||||
#!/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} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
|
|
||||||
#ags="generate -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -DsupportPython2=true $@"
|
|
||||||
ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion-python2 -c bin/supportPython2.json -D service $@"
|
|
||||||
|
|
||||||
rm -rf samples/server/petstore/flaskConnexion-python2/*
|
|
||||||
java $JAVA_OPTS -jar $executable $ags
|
|
@ -25,9 +25,20 @@ then
|
|||||||
mvn -B clean package
|
mvn -B clean package
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# if you've executed sbt assembly previously it will use that instead.
|
generator=python-aiohttp
|
||||||
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
|
input=modules/openapi-generator/src/test/resources/2_0/petstore.yaml
|
||||||
ags="generate -t modules/openapi-generator/src/main/resources/flaskConnexion -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g python-flask -o samples/server/petstore/flaskConnexion -Dservice $@"
|
out_folder=samples/server/petstore/$generator
|
||||||
|
resources=modules/openapi-generator/src/main/resources/$generator
|
||||||
|
|
||||||
|
# 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 $resources -i $input -g $generator -o $out_folder -Dservice $@"
|
||||||
|
|
||||||
|
rm -rf $out_folder/.openapi*
|
||||||
|
rm -rf $out_folder/openapi_server
|
||||||
|
rm -rf $out_folder/tests*
|
||||||
|
rm $out_folder/README.md
|
||||||
|
rm $out_folder/requirements.txt
|
||||||
|
rm $out_folder/test-requirements.txt
|
||||||
|
|
||||||
rm -rf samples/server/petstore/flaskConnexion/*
|
|
||||||
java $JAVA_OPTS -jar $executable $ags
|
java $JAVA_OPTS -jar $executable $ags
|
5
bin/python-server-all.sh
Executable file
5
bin/python-server-all.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
./bin/python-server-aiohttp-petstore.sh
|
||||||
|
./bin/python-server-flask-petstore.sh
|
||||||
|
./bin/python-server-flask-petstore-python2.sh
|
50
bin/python-server-flask-petstore-python2.sh
Executable file
50
bin/python-server-flask-petstore-python2.sh
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
generator=python-flask
|
||||||
|
input=modules/openapi-generator/src/test/resources/2_0/petstore.yaml
|
||||||
|
out_folder=samples/server/petstore/$generator-python2
|
||||||
|
resources=modules/openapi-generator/src/main/resources/$generator
|
||||||
|
|
||||||
|
# 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 $resources -i $input -g $generator -o $out_folder -c bin/supportPython2.json -D service $@"
|
||||||
|
|
||||||
|
rm -rf $out_folder/.openapi*
|
||||||
|
rm -rf $out_folder/openapi_server
|
||||||
|
rm $out_folder/.dockerignore
|
||||||
|
rm $out_folder/.gitignore
|
||||||
|
rm $out_folder/.travis.yml
|
||||||
|
rm $out_folder/Dockerfile
|
||||||
|
rm $out_folder/git_push.sh
|
||||||
|
rm $out_folder/README.md
|
||||||
|
rm $out_folder/requirements.txt
|
||||||
|
rm $out_folder/setup.py
|
||||||
|
rm $out_folder/test-requirements.txt
|
||||||
|
rm $out_folder/tox.ini
|
||||||
|
|
||||||
|
java $JAVA_OPTS -jar $executable $ags
|
50
bin/python-server-flask-petstore.sh
Executable file
50
bin/python-server-flask-petstore.sh
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
generator=python-flask
|
||||||
|
input=modules/openapi-generator/src/test/resources/2_0/petstore.yaml
|
||||||
|
out_folder=samples/server/petstore/$generator
|
||||||
|
resources=modules/openapi-generator/src/main/resources/$generator
|
||||||
|
|
||||||
|
# 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 $resources -i $input -g $generator -o $out_folder -Dservice $@"
|
||||||
|
|
||||||
|
rm -rf $out_folder/.openapi*
|
||||||
|
rm -rf $out_folder/openapi_server
|
||||||
|
rm $out_folder/.dockerignore
|
||||||
|
rm $out_folder/.gitignore
|
||||||
|
rm $out_folder/.travis.yml
|
||||||
|
rm $out_folder/Dockerfile
|
||||||
|
rm $out_folder/git_push.sh
|
||||||
|
rm $out_folder/README.md
|
||||||
|
rm $out_folder/requirements.txt
|
||||||
|
rm $out_folder/setup.py
|
||||||
|
rm $out_folder/test-requirements.txt
|
||||||
|
rm $out_folder/tox.ini
|
||||||
|
|
||||||
|
java $JAVA_OPTS -jar $executable $ags
|
@ -5,6 +5,6 @@ If Not Exist %executable% (
|
|||||||
)
|
)
|
||||||
|
|
||||||
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
|
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
|
||||||
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\flaskConnexion-python2 -c bin\supportPython2.json -D service
|
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\python-flask-python2 -c bin\supportPython2.json -D service
|
||||||
|
|
||||||
java %JAVA_OPTS% -jar %executable% %ags%
|
java %JAVA_OPTS% -jar %executable% %ags%
|
||||||
|
@ -5,6 +5,6 @@ If Not Exist %executable% (
|
|||||||
)
|
)
|
||||||
|
|
||||||
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
|
REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
|
||||||
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\flaskConnexion -D service
|
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g python-flask -o samples\server\petstore\python-flask -D service
|
||||||
|
|
||||||
java %JAVA_OPTS% -jar %executable% %ags%
|
java %JAVA_OPTS% -jar %executable% %ags%
|
||||||
|
@ -27,7 +27,10 @@ import java.util.Map;
|
|||||||
public class CodegenSecurity {
|
public class CodegenSecurity {
|
||||||
public String name;
|
public String name;
|
||||||
public String type;
|
public String type;
|
||||||
|
public String scheme;
|
||||||
public Boolean hasMore, isBasic, isOAuth, isApiKey;
|
public Boolean hasMore, isBasic, isOAuth, isApiKey;
|
||||||
|
// is Basic is true for all http authentication type. Those are to differentiate basic and bearer authentication
|
||||||
|
public Boolean isBasicBasic, isBasicBearer;
|
||||||
public Map<String, Object> vendorExtensions = new HashMap<String, Object>();
|
public Map<String, Object> vendorExtensions = new HashMap<String, Object>();
|
||||||
// ApiKey specific
|
// ApiKey specific
|
||||||
public String keyParamName;
|
public String keyParamName;
|
||||||
|
@ -3230,6 +3230,8 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
cs.name = key;
|
cs.name = key;
|
||||||
cs.type = securityScheme.getType().toString();
|
cs.type = securityScheme.getType().toString();
|
||||||
cs.isCode = cs.isPassword = cs.isApplication = cs.isImplicit = false;
|
cs.isCode = cs.isPassword = cs.isApplication = cs.isImplicit = false;
|
||||||
|
cs.isBasicBasic = cs.isBasicBearer = false;
|
||||||
|
cs.scheme = securityScheme.getScheme();
|
||||||
|
|
||||||
if (SecurityScheme.Type.APIKEY.equals(securityScheme.getType())) {
|
if (SecurityScheme.Type.APIKEY.equals(securityScheme.getType())) {
|
||||||
cs.isBasic = cs.isOAuth = false;
|
cs.isBasic = cs.isOAuth = false;
|
||||||
@ -3241,6 +3243,12 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
} else if (SecurityScheme.Type.HTTP.equals(securityScheme.getType())) {
|
} else if (SecurityScheme.Type.HTTP.equals(securityScheme.getType())) {
|
||||||
cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isOAuth = false;
|
cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isOAuth = false;
|
||||||
cs.isBasic = true;
|
cs.isBasic = true;
|
||||||
|
if ("basic".equals(securityScheme.getScheme())) {
|
||||||
|
cs.isBasicBasic = true;
|
||||||
|
}
|
||||||
|
else if ("bearer".equals(securityScheme.getScheme())) {
|
||||||
|
cs.isBasicBearer = true;
|
||||||
|
}
|
||||||
} else if (SecurityScheme.Type.OAUTH2.equals(securityScheme.getType())) {
|
} else if (SecurityScheme.Type.OAUTH2.equals(securityScheme.getType())) {
|
||||||
cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isBasic = false;
|
cs.isKeyInHeader = cs.isKeyInQuery = cs.isKeyInCookie = cs.isApiKey = cs.isBasic = false;
|
||||||
cs.isOAuth = true;
|
cs.isOAuth = true;
|
||||||
|
@ -0,0 +1,908 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openapitools.codegen.languages;
|
||||||
|
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import io.swagger.v3.oas.models.Components;
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import io.swagger.v3.oas.models.PathItem;
|
||||||
|
import io.swagger.v3.oas.models.PathItem.HttpMethod;
|
||||||
|
import io.swagger.v3.oas.models.media.ArraySchema;
|
||||||
|
import io.swagger.v3.oas.models.media.Schema;
|
||||||
|
import io.swagger.v3.oas.models.parameters.Parameter;
|
||||||
|
import io.swagger.v3.oas.models.parameters.RequestBody;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.openapitools.codegen.*;
|
||||||
|
import org.openapitools.codegen.utils.ModelUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.openapitools.codegen.utils.StringUtils.camelize;
|
||||||
|
import static org.openapitools.codegen.utils.StringUtils.underscore;
|
||||||
|
|
||||||
|
public class PythonAbstractConnexionServerCodegen extends DefaultCodegen implements CodegenConfig {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(PythonAbstractConnexionServerCodegen.class);
|
||||||
|
|
||||||
|
public static final String CONTROLLER_PACKAGE = "controllerPackage";
|
||||||
|
public static final String DEFAULT_CONTROLLER = "defaultController";
|
||||||
|
public static final String SUPPORT_PYTHON2 = "supportPython2";
|
||||||
|
static final String MEDIA_TYPE = "mediaType";
|
||||||
|
|
||||||
|
protected int serverPort = 8080;
|
||||||
|
protected String packageName;
|
||||||
|
protected String packageVersion;
|
||||||
|
protected String controllerPackage;
|
||||||
|
protected String defaultController;
|
||||||
|
protected Map<Character, String> regexModifiers;
|
||||||
|
protected boolean fixBodyName;
|
||||||
|
|
||||||
|
public PythonAbstractConnexionServerCodegen(String templateDirectory, boolean fixBodyNameValue) {
|
||||||
|
super();
|
||||||
|
fixBodyName = fixBodyNameValue;
|
||||||
|
modelPackage = "models";
|
||||||
|
testPackage = "test";
|
||||||
|
|
||||||
|
languageSpecificPrimitives.clear();
|
||||||
|
languageSpecificPrimitives.add("int");
|
||||||
|
languageSpecificPrimitives.add("float");
|
||||||
|
languageSpecificPrimitives.add("List");
|
||||||
|
languageSpecificPrimitives.add("Dict");
|
||||||
|
languageSpecificPrimitives.add("bool");
|
||||||
|
languageSpecificPrimitives.add("str");
|
||||||
|
languageSpecificPrimitives.add("datetime");
|
||||||
|
languageSpecificPrimitives.add("date");
|
||||||
|
languageSpecificPrimitives.add("file");
|
||||||
|
languageSpecificPrimitives.add("object");
|
||||||
|
languageSpecificPrimitives.add("byte");
|
||||||
|
languageSpecificPrimitives.add("bytearray");
|
||||||
|
|
||||||
|
typeMapping.clear();
|
||||||
|
typeMapping.put("integer", "int");
|
||||||
|
typeMapping.put("float", "float");
|
||||||
|
typeMapping.put("number", "float");
|
||||||
|
typeMapping.put("long", "int");
|
||||||
|
typeMapping.put("double", "float");
|
||||||
|
typeMapping.put("array", "List");
|
||||||
|
typeMapping.put("map", "Dict");
|
||||||
|
typeMapping.put("boolean", "bool");
|
||||||
|
typeMapping.put("string", "str");
|
||||||
|
typeMapping.put("date", "date");
|
||||||
|
typeMapping.put("DateTime", "datetime");
|
||||||
|
typeMapping.put("object", "object");
|
||||||
|
typeMapping.put("file", "file");
|
||||||
|
typeMapping.put("UUID", "str");
|
||||||
|
typeMapping.put("byte", "bytearray");
|
||||||
|
typeMapping.put("ByteArray", "bytearray");
|
||||||
|
|
||||||
|
// from https://docs.python.org/3/reference/lexical_analysis.html#keywords
|
||||||
|
setReservedWordsLowerCase(
|
||||||
|
Arrays.asList(
|
||||||
|
// @property
|
||||||
|
"property",
|
||||||
|
// python reserved words
|
||||||
|
"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", "None", "True", "False", "nonlocal"));
|
||||||
|
|
||||||
|
// set the output folder here
|
||||||
|
outputFolder = "generated-code/connexion";
|
||||||
|
|
||||||
|
apiTemplateFiles.put("controller.mustache", ".py");
|
||||||
|
modelTemplateFiles.put("model.mustache", ".py");
|
||||||
|
apiTestTemplateFiles().put("controller_test.mustache", ".py");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Template Location. This is the location which templates will be read from. The generator
|
||||||
|
* will use the resource stream to attempt to read the templates.
|
||||||
|
*/
|
||||||
|
embeddedTemplateDir = templateDir = templateDirectory;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Additional Properties. These values can be passed to the templates and
|
||||||
|
* are available in models, apis, and supporting files
|
||||||
|
*/
|
||||||
|
additionalProperties.put("serverPort", serverPort);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supporting Files. You can write single files for the generator with the
|
||||||
|
* entire object tree available. If the input file has a suffix of `.mustache
|
||||||
|
* it will be processed by the template engine. Otherwise, it will be copied
|
||||||
|
*/
|
||||||
|
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
|
||||||
|
supportingFiles.add(new SupportingFile("test-requirements.mustache", "", "test-requirements.txt"));
|
||||||
|
supportingFiles.add(new SupportingFile("requirements.mustache", "", "requirements.txt"));
|
||||||
|
|
||||||
|
regexModifiers = new HashMap<Character, String>();
|
||||||
|
regexModifiers.put('i', "IGNORECASE");
|
||||||
|
regexModifiers.put('l', "LOCALE");
|
||||||
|
regexModifiers.put('m', "MULTILINE");
|
||||||
|
regexModifiers.put('s', "DOTALL");
|
||||||
|
regexModifiers.put('u', "UNICODE");
|
||||||
|
regexModifiers.put('x', "VERBOSE");
|
||||||
|
|
||||||
|
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "python package name (convention: snake_case).")
|
||||||
|
.defaultValue("openapi_server"));
|
||||||
|
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "python package version.")
|
||||||
|
.defaultValue("1.0.0"));
|
||||||
|
cliOptions.add(new CliOption(CONTROLLER_PACKAGE, "controller package").
|
||||||
|
defaultValue("controllers"));
|
||||||
|
cliOptions.add(new CliOption(DEFAULT_CONTROLLER, "default controller").
|
||||||
|
defaultValue("default_controller"));
|
||||||
|
cliOptions.add(new CliOption(SUPPORT_PYTHON2, "support python2").
|
||||||
|
defaultValue("false"));
|
||||||
|
cliOptions.add(new CliOption("serverPort", "TCP port to listen to in app.run").
|
||||||
|
defaultValue("8080"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addSupportingFiles() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processOpts() {
|
||||||
|
super.processOpts();
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(System.getenv("PYTHON_POST_PROCESS_FILE"))) {
|
||||||
|
LOGGER.info("Environment variable PYTHON_POST_PROCESS_FILE not defined so the Python code may not be properly formatted. To define it, try 'export PYTHON_POST_PROCESS_FILE=\"/usr/local/bin/yapf -i\"' (Linux/Mac)");
|
||||||
|
LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
|
||||||
|
}
|
||||||
|
|
||||||
|
//apiTemplateFiles.clear();
|
||||||
|
|
||||||
|
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
|
||||||
|
setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
|
||||||
|
} else {
|
||||||
|
setPackageName("openapi_server");
|
||||||
|
additionalProperties.put(CodegenConstants.PACKAGE_NAME, this.packageName);
|
||||||
|
}
|
||||||
|
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
|
||||||
|
setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
|
||||||
|
} else {
|
||||||
|
setPackageVersion("1.0.0");
|
||||||
|
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, this.packageVersion);
|
||||||
|
}
|
||||||
|
if (additionalProperties.containsKey(CONTROLLER_PACKAGE)) {
|
||||||
|
this.controllerPackage = additionalProperties.get(CONTROLLER_PACKAGE).toString();
|
||||||
|
} else {
|
||||||
|
this.controllerPackage = "controllers";
|
||||||
|
additionalProperties.put(CONTROLLER_PACKAGE, this.controllerPackage);
|
||||||
|
}
|
||||||
|
if (additionalProperties.containsKey(DEFAULT_CONTROLLER)) {
|
||||||
|
this.defaultController = additionalProperties.get(DEFAULT_CONTROLLER).toString();
|
||||||
|
} else {
|
||||||
|
this.defaultController = "default_controller";
|
||||||
|
additionalProperties.put(DEFAULT_CONTROLLER, this.defaultController);
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(additionalProperties.get(SUPPORT_PYTHON2))) {
|
||||||
|
additionalProperties.put(SUPPORT_PYTHON2, Boolean.TRUE);
|
||||||
|
typeMapping.put("long", "long");
|
||||||
|
}
|
||||||
|
supportingFiles.add(new SupportingFile("__main__.mustache", packageName, "__main__.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("util.mustache", packageName, "util.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("__init__.mustache", packageName + File.separatorChar + controllerPackage, "__init__.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("security_controller_.mustache", packageName + File.separatorChar + controllerPackage, "security_controller_.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("__init__model.mustache", packageName + File.separatorChar + modelPackage, "__init__.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("base_model_.mustache", packageName + File.separatorChar + modelPackage, "base_model_.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("openapi.mustache", packageName + File.separatorChar + "openapi", "openapi.yaml"));
|
||||||
|
addSupportingFiles();
|
||||||
|
|
||||||
|
modelPackage = packageName + "." + modelPackage;
|
||||||
|
controllerPackage = packageName + "." + controllerPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String dropDots(String str) {
|
||||||
|
return str.replaceAll("\\.", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String apiPackage() {
|
||||||
|
return controllerPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the type of generator.
|
||||||
|
*
|
||||||
|
* @return the CodegenType for this generator
|
||||||
|
* @see org.openapitools.codegen.CodegenType
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public CodegenType getTag() {
|
||||||
|
return CodegenType.SERVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns human-friendly help for the generator. Provide the consumer with help
|
||||||
|
* tips, parameters here
|
||||||
|
*
|
||||||
|
* @return A string value for the help message
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getHelp() {
|
||||||
|
return "Generates a Python server library using the Connexion project. By default, " +
|
||||||
|
"it will also generate service classes -- which you can disable with the `-Dnoservice` environment variable.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toApiName(String name) {
|
||||||
|
if (name == null || name.length() == 0) {
|
||||||
|
return "DefaultController";
|
||||||
|
}
|
||||||
|
return camelize(name, false) + "Controller";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toApiFilename(String name) {
|
||||||
|
return underscore(toApiName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toApiTestFilename(String name) {
|
||||||
|
return "test_" + toApiFilename(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
|
||||||
|
* those terms here. This logic is only called if a variable matches the reserved words
|
||||||
|
*
|
||||||
|
* @return the escaped term
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String escapeReservedWord(String name) {
|
||||||
|
if (this.reservedWordsMappings().containsKey(name)) {
|
||||||
|
return this.reservedWordsMappings().get(name);
|
||||||
|
}
|
||||||
|
return "_" + name; // add an underscore to the name
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location to write api files. You can use the apiPackage() as defined when the class is
|
||||||
|
* instantiated
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String apiFileFolder() {
|
||||||
|
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTypeDeclaration(Schema p) {
|
||||||
|
if (ModelUtils.isArraySchema(p)) {
|
||||||
|
ArraySchema ap = (ArraySchema) p;
|
||||||
|
Schema inner = ap.getItems();
|
||||||
|
return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]";
|
||||||
|
} else if (ModelUtils.isMapSchema(p)) {
|
||||||
|
Schema inner = ModelUtils.getAdditionalProperties(p);
|
||||||
|
return getSchemaType(p) + "[str, " + getTypeDeclaration(inner) + "]";
|
||||||
|
}
|
||||||
|
return super.getTypeDeclaration(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSchemaType(Schema p) {
|
||||||
|
String schemaType = super.getSchemaType(p);
|
||||||
|
String type = null;
|
||||||
|
if (typeMapping.containsKey(schemaType)) {
|
||||||
|
type = typeMapping.get(schemaType);
|
||||||
|
if (languageSpecificPrimitives.contains(type)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type = toModelName(schemaType);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preprocessOpenAPI(OpenAPI openAPI) {
|
||||||
|
// need vendor extensions for x-openapi-router-controller
|
||||||
|
Map<String, PathItem> paths = openAPI.getPaths();
|
||||||
|
if (paths != null) {
|
||||||
|
List<String> pathnames = new ArrayList(paths.keySet());
|
||||||
|
for (String pathname : pathnames) {
|
||||||
|
PathItem path = paths.get(pathname);
|
||||||
|
// Fix path parameters to be in snake_case
|
||||||
|
if (pathname.contains("{")) {
|
||||||
|
String fixedPath = new String();
|
||||||
|
for (String token: pathname.substring(1).split("/")) {
|
||||||
|
if (token.startsWith("{")) {
|
||||||
|
String snake_case_token = "{" + this.toParamName(token.substring(1, token.length()-1)) + "}";
|
||||||
|
if(token != snake_case_token) {
|
||||||
|
token = snake_case_token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixedPath += "/" + token;
|
||||||
|
}
|
||||||
|
if (!fixedPath.equals(pathname)) {
|
||||||
|
LOGGER.warn("Path '" + pathname + "' is not consistant with Python variable names. It will be replaced by '" + fixedPath + "'");
|
||||||
|
paths.remove(pathname);
|
||||||
|
paths.put(fixedPath, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<HttpMethod, Operation> operationMap = path.readOperationsMap();
|
||||||
|
if (operationMap != null) {
|
||||||
|
for (HttpMethod method : operationMap.keySet()) {
|
||||||
|
Operation operation = operationMap.get(method);
|
||||||
|
String tag = "default";
|
||||||
|
if (operation.getTags() != null && operation.getTags().size() > 0) {
|
||||||
|
tag = operation.getTags().get(0);
|
||||||
|
}
|
||||||
|
String operationId = operation.getOperationId();
|
||||||
|
if (operationId == null) {
|
||||||
|
operationId = getOrGenerateOperationId(operation, pathname, method.toString());
|
||||||
|
}
|
||||||
|
operation.setOperationId(toOperationId(operationId));
|
||||||
|
if (operation.getExtensions() == null || operation.getExtensions().get("x-openapi-router-controller") == null) {
|
||||||
|
operation.addExtension(
|
||||||
|
"x-openapi-router-controller",
|
||||||
|
controllerPackage + "." + toApiFilename(tag)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (operation.getParameters() != null) {
|
||||||
|
for (Parameter parameter: operation.getParameters()) {
|
||||||
|
String swaggerParameterName = parameter.getName();
|
||||||
|
String pythonParameterName = this.toParamName(swaggerParameterName);
|
||||||
|
if (!swaggerParameterName.equals(pythonParameterName)) {
|
||||||
|
LOGGER.warn("Parameter name '" + swaggerParameterName + "' is not consistant with Python variable names. It will be replaced by '" + pythonParameterName + "'");
|
||||||
|
parameter.setName(pythonParameterName);
|
||||||
|
}
|
||||||
|
if (swaggerParameterName.isEmpty()) {
|
||||||
|
LOGGER.error("Missing parameter name in " + pathname + "." + parameter.getIn());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RequestBody body = operation.getRequestBody();
|
||||||
|
if (fixBodyName && body != null) {
|
||||||
|
if (body.getExtensions() == null || !body.getExtensions().containsKey("x-body-name")) {
|
||||||
|
String bodyParameterName = "body";
|
||||||
|
if (operation.getExtensions() != null && operation.getExtensions().containsKey("x-codegen-request-body-name")) {
|
||||||
|
bodyParameterName = (String) operation.getExtensions().get("x-codegen-request-body-name");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Used by code generator
|
||||||
|
operation.addExtension("x-codegen-request-body-name", bodyParameterName);
|
||||||
|
}
|
||||||
|
// Used by connexion
|
||||||
|
body.addExtension("x-body-name", bodyParameterName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort path names after variable name fix
|
||||||
|
List<String> fixedPathnames = new ArrayList(paths.keySet());
|
||||||
|
Collections.sort(fixedPathnames);
|
||||||
|
for (String pathname: fixedPathnames) {
|
||||||
|
PathItem pathItem = paths.remove(pathname);
|
||||||
|
paths.put(pathname, pathItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addSecurityExtensions(openAPI);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSecurityExtension(SecurityScheme securityScheme, String extensionName, String functionName) {
|
||||||
|
if (securityScheme.getExtensions() == null || ! securityScheme.getExtensions().containsKey(extensionName)) {
|
||||||
|
securityScheme.addExtension(extensionName, functionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSecurityExtensions(OpenAPI openAPI) {
|
||||||
|
Components components = openAPI.getComponents();
|
||||||
|
if (components != null && components.getSecuritySchemes() != null) {
|
||||||
|
Map<String, SecurityScheme> securitySchemes = components.getSecuritySchemes();
|
||||||
|
for(String securityName: securitySchemes.keySet()) {
|
||||||
|
SecurityScheme securityScheme = securitySchemes.get(securityName);
|
||||||
|
String baseFunctionName = controllerPackage + ".security_controller_.";
|
||||||
|
switch(securityScheme.getType()) {
|
||||||
|
case APIKEY:
|
||||||
|
addSecurityExtension(securityScheme, "x-apikeyInfoFunc", baseFunctionName + "info_from_" + securityName);
|
||||||
|
break;
|
||||||
|
case HTTP:
|
||||||
|
if ("basic".equals(securityScheme.getScheme())) {
|
||||||
|
addSecurityExtension(securityScheme, "x-basicInfoFunc", baseFunctionName + "info_from_" + securityName);
|
||||||
|
}
|
||||||
|
else if ("bearer".equals(securityScheme.getScheme())) {
|
||||||
|
addSecurityExtension(securityScheme, "x-bearerInfoFunc", baseFunctionName + "info_from_" + securityName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OPENIDCONNECT:
|
||||||
|
LOGGER.warn("Security type " + securityScheme.getType().toString() + " is not supported by connextion yet");
|
||||||
|
case OAUTH2:
|
||||||
|
addSecurityExtension(securityScheme, "x-tokenInfoFunc", baseFunctionName + "info_from_" + securityName);
|
||||||
|
addSecurityExtension(securityScheme, "x-scopeValidateFunc", baseFunctionName + "validate_scope_" + securityName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOGGER.warn("Unknown security type " + securityScheme.getType().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static List<Map<String, Object>> getOperations(Map<String, Object> objs) {
|
||||||
|
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
|
||||||
|
Map<String, Object> apiInfo = (Map<String, Object>) objs.get("apiInfo");
|
||||||
|
List<Map<String, Object>> apis = (List<Map<String, Object>>) apiInfo.get("apis");
|
||||||
|
for (Map<String, Object> api : apis) {
|
||||||
|
result.add((Map<String, Object>) api.get("operations"));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Map<String, Object>> sortOperationsByPath(List<CodegenOperation> ops) {
|
||||||
|
Multimap<String, CodegenOperation> opsByPath = ArrayListMultimap.create();
|
||||||
|
|
||||||
|
for (CodegenOperation op : ops) {
|
||||||
|
opsByPath.put(op.path, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object>> opsByPathList = new ArrayList<Map<String, Object>>();
|
||||||
|
for (Map.Entry<String, Collection<CodegenOperation>> entry : opsByPath.asMap().entrySet()) {
|
||||||
|
Map<String, Object> opsByPathEntry = new HashMap<String, Object>();
|
||||||
|
opsByPathList.add(opsByPathEntry);
|
||||||
|
opsByPathEntry.put("path", entry.getKey());
|
||||||
|
opsByPathEntry.put("operation", entry.getValue());
|
||||||
|
List<CodegenOperation> operationsForThisPath = Lists.newArrayList(entry.getValue());
|
||||||
|
operationsForThisPath.get(operationsForThisPath.size() - 1).hasMore = false;
|
||||||
|
if (opsByPathList.size() < opsByPath.asMap().size()) {
|
||||||
|
opsByPathEntry.put("hasMore", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opsByPathList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
|
||||||
|
generateYAMLSpecFile(objs);
|
||||||
|
|
||||||
|
for (Map<String, Object> operations : getOperations(objs)) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<CodegenOperation> ops = (List<CodegenOperation>) operations.get("operation");
|
||||||
|
|
||||||
|
List<Map<String, Object>> opsByPathList = sortOperationsByPath(ops);
|
||||||
|
operations.put("operationsByPath", opsByPathList);
|
||||||
|
}
|
||||||
|
return super.postProcessSupportingFileData(objs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toVarName(String name) {
|
||||||
|
// sanitize name
|
||||||
|
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
|
||||||
|
|
||||||
|
// remove dollar sign
|
||||||
|
name = name.replaceAll("$", "");
|
||||||
|
|
||||||
|
// if it's all uppper case, convert to lower case
|
||||||
|
if (name.matches("^[A-Z_]*$")) {
|
||||||
|
name = name.toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// underscore the variable name
|
||||||
|
// petId => pet_id
|
||||||
|
name = underscore(name);
|
||||||
|
|
||||||
|
// remove leading underscore
|
||||||
|
name = name.replaceAll("^_*", "");
|
||||||
|
|
||||||
|
// for reserved word or word starting with number, append _
|
||||||
|
if (isReservedWord(name) || name.matches("^\\d.*")) {
|
||||||
|
name = escapeReservedWord(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toParamName(String name) {
|
||||||
|
// to avoid conflicts with 'callback' parameter for async call
|
||||||
|
if ("callback".equals(name)) {
|
||||||
|
return "param_callback";
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be the same as variable name
|
||||||
|
return toVarName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toModelFilename(String name) {
|
||||||
|
// underscore the model file name
|
||||||
|
// PhoneNumber => phone_number
|
||||||
|
return underscore(dropDots(toModelName(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toModelName(String name) {
|
||||||
|
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
|
||||||
|
// remove dollar sign
|
||||||
|
name = name.replaceAll("$", "");
|
||||||
|
|
||||||
|
// model name cannot use reserved keyword, e.g. return
|
||||||
|
if (isReservedWord(name)) {
|
||||||
|
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + camelize("model_" + name));
|
||||||
|
name = "model_" + name; // e.g. return => ModelReturn (after camelize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// model name starts with number
|
||||||
|
if (name.matches("^\\d.*")) {
|
||||||
|
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + name));
|
||||||
|
name = "model_" + name; // e.g. 200Response => Model200Response (after camelize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(modelNamePrefix)) {
|
||||||
|
name = modelNamePrefix + "_" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(modelNameSuffix)) {
|
||||||
|
name = name + "_" + modelNameSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// camelize the model name
|
||||||
|
// phone_number => PhoneNumber
|
||||||
|
return camelize(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toOperationId(String operationId) {
|
||||||
|
// throw exception if method name is empty (should not occur as an auto-generated method name will be used)
|
||||||
|
if (StringUtils.isEmpty(operationId)) {
|
||||||
|
throw new RuntimeException("Empty method name (operationId) not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// method name cannot use reserved keyword, e.g. return
|
||||||
|
if (isReservedWord(operationId)) {
|
||||||
|
LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore(sanitizeName("call_" + operationId)));
|
||||||
|
operationId = "call_" + operationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return underscore(sanitizeName(operationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the default value of the property
|
||||||
|
*
|
||||||
|
* @param p OpenAPI property object
|
||||||
|
* @return string presentation of the default value of the property
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toDefaultValue(Schema p) {
|
||||||
|
if (ModelUtils.isBooleanSchema(p)) {
|
||||||
|
if (p.getDefault() != null) {
|
||||||
|
if (p.getDefault().toString().equalsIgnoreCase("false"))
|
||||||
|
return "False";
|
||||||
|
else
|
||||||
|
return "True";
|
||||||
|
}
|
||||||
|
} else if (ModelUtils.isDateSchema(p)) {
|
||||||
|
// TODO
|
||||||
|
} else if (ModelUtils.isDateTimeSchema(p)) {
|
||||||
|
// TODO
|
||||||
|
} else if (ModelUtils.isNumberSchema(p)) {
|
||||||
|
if (p.getDefault() != null) {
|
||||||
|
return p.getDefault().toString();
|
||||||
|
}
|
||||||
|
} else if (ModelUtils.isIntegerSchema(p)) {
|
||||||
|
if (p.getDefault() != null) {
|
||||||
|
return p.getDefault().toString();
|
||||||
|
}
|
||||||
|
} else if (ModelUtils.isStringSchema(p)) {
|
||||||
|
if (p.getDefault() != null) {
|
||||||
|
return "'" + (String) p.getDefault() + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParameterExampleValue(CodegenParameter p) {
|
||||||
|
String example;
|
||||||
|
|
||||||
|
if (p.defaultValue == null) {
|
||||||
|
example = p.example;
|
||||||
|
} else {
|
||||||
|
p.example = p.defaultValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String type = p.baseType;
|
||||||
|
if (type == null) {
|
||||||
|
type = p.dataType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("String".equalsIgnoreCase(type) || "str".equalsIgnoreCase(type)) {
|
||||||
|
if (example == null) {
|
||||||
|
example = p.paramName + "_example";
|
||||||
|
}
|
||||||
|
example = "'" + escapeText(example) + "'";
|
||||||
|
} else if ("Integer".equals(type) || "int".equals(type)) {
|
||||||
|
if (p.minimum != null) {
|
||||||
|
example = "" + (Integer.valueOf(p.minimum) + 1);
|
||||||
|
}
|
||||||
|
if (p.maximum != null) {
|
||||||
|
example = "" + p.maximum;
|
||||||
|
} else if (example == null) {
|
||||||
|
example = "56";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ("Long".equalsIgnoreCase(type)) {
|
||||||
|
if (p.minimum != null) {
|
||||||
|
example = "" + (Long.valueOf(p.minimum) + 1);
|
||||||
|
}
|
||||||
|
if (p.maximum != null) {
|
||||||
|
example = "" + p.maximum;
|
||||||
|
} else if (example == null) {
|
||||||
|
example = "789";
|
||||||
|
}
|
||||||
|
} else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) {
|
||||||
|
if (p.minimum != null) {
|
||||||
|
example = "" + p.minimum;
|
||||||
|
} else if (p.maximum != null) {
|
||||||
|
example = "" + p.maximum;
|
||||||
|
} else if (example == null) {
|
||||||
|
example = "3.4";
|
||||||
|
}
|
||||||
|
} else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) {
|
||||||
|
if (example == null) {
|
||||||
|
example = "True";
|
||||||
|
}
|
||||||
|
} else if ("file".equalsIgnoreCase(type)) {
|
||||||
|
example = "(BytesIO(b'some file data'), 'file.txt')";
|
||||||
|
} else if ("Date".equalsIgnoreCase(type)) {
|
||||||
|
if (example == null) {
|
||||||
|
example = "2013-10-20";
|
||||||
|
}
|
||||||
|
example = "'" + escapeText(example) + "'";
|
||||||
|
} else if ("DateTime".equalsIgnoreCase(type)) {
|
||||||
|
if (example == null) {
|
||||||
|
example = "2013-10-20T19:20:30+01:00";
|
||||||
|
}
|
||||||
|
example = "'" + escapeText(example) + "'";
|
||||||
|
} else if (!languageSpecificPrimitives.contains(type)) {
|
||||||
|
// type is a model class, e.g. User
|
||||||
|
example = "{}";
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.items != null && p.items.defaultValue != null) {
|
||||||
|
example = p.items.defaultValue;
|
||||||
|
}
|
||||||
|
if (example == null) {
|
||||||
|
if (Boolean.TRUE.equals(p.isListContainer)) {
|
||||||
|
example = "[]";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
example = "None";
|
||||||
|
}
|
||||||
|
} else if (Boolean.TRUE.equals(p.isListContainer)) {
|
||||||
|
if (Boolean.TRUE.equals(p.isBodyParam)) {
|
||||||
|
example = "[" + example + "]";
|
||||||
|
}
|
||||||
|
} else if (Boolean.TRUE.equals(p.isMapContainer)) {
|
||||||
|
example = "{'key': " + example + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
p.example = example;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPackageName(String packageName) {
|
||||||
|
this.packageName = packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPackageVersion(String packageVersion) {
|
||||||
|
this.packageVersion = packageVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escapeQuotationMark(String input) {
|
||||||
|
// remove ' to avoid code injection
|
||||||
|
return input.replace("'", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escapeUnsafeCharacters(String input) {
|
||||||
|
// remove multiline comment
|
||||||
|
return input.replace("'''", "'_'_'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toModelImport(String name) {
|
||||||
|
String modelImport;
|
||||||
|
if (StringUtils.startsWithAny(name, "import", "from")) {
|
||||||
|
modelImport = name;
|
||||||
|
} else {
|
||||||
|
modelImport = "from ";
|
||||||
|
if (!"".equals(modelPackage())) {
|
||||||
|
modelImport += modelPackage() + ".";
|
||||||
|
}
|
||||||
|
modelImport += toModelFilename(name) + " import " + name;
|
||||||
|
}
|
||||||
|
return modelImport;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
|
||||||
|
if (StringUtils.isNotEmpty(property.pattern)) {
|
||||||
|
addImport(model, "import re");
|
||||||
|
}
|
||||||
|
postProcessPattern(property.pattern, property.vendorExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
||||||
|
// process enum in models
|
||||||
|
return postProcessModelsEnum(objs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
|
||||||
|
Map<String, Object> result = super.postProcessAllModels(objs);
|
||||||
|
for (Map.Entry<String, Object> entry : result.entrySet()) {
|
||||||
|
Map<String, Object> inner = (Map<String, Object>) entry.getValue();
|
||||||
|
List<Map<String, Object>> models = (List<Map<String, Object>>) inner.get("models");
|
||||||
|
for (Map<String, Object> mo : models) {
|
||||||
|
CodegenModel cm = (CodegenModel) mo.get("model");
|
||||||
|
// Add additional filename information for imports
|
||||||
|
mo.put("pyImports", toPyImports(cm, cm.imports));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, String>> toPyImports(CodegenModel cm, Set<String> imports) {
|
||||||
|
List<Map<String, String>> pyImports = new ArrayList<>();
|
||||||
|
for (String im : imports) {
|
||||||
|
if (!im.equals(cm.classname)) {
|
||||||
|
HashMap<String, String> pyImport = new HashMap<>();
|
||||||
|
pyImport.put("import", toModelImport(im));
|
||||||
|
pyImports.add(pyImport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pyImports;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessParameter(CodegenParameter parameter) {
|
||||||
|
postProcessPattern(parameter.pattern, parameter.vendorExtensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
|
||||||
|
Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
|
||||||
|
List<CodegenOperation> operationList = (List<CodegenOperation>) operations.get("operation");
|
||||||
|
|
||||||
|
for (CodegenOperation operation : operationList) {
|
||||||
|
Map<String, String> skipTests = new HashMap<>();
|
||||||
|
// Set flag to deactivate tests due to connexion issue.
|
||||||
|
if (operation.consumes != null ) {
|
||||||
|
if (operation.consumes.size() == 1) {
|
||||||
|
Map<String, String> consume = operation.consumes.get(0);
|
||||||
|
if (! "application/json".equals(consume.get(MEDIA_TYPE))) {
|
||||||
|
skipTests.put("reason", consume.get(MEDIA_TYPE) + " not supported by Connexion");
|
||||||
|
if ("multipart/form-data".equals(consume.get(MEDIA_TYPE))) {
|
||||||
|
operation.isMultipart = Boolean.TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operation.vendorExtensions.put("x-prefered-consume", consume);
|
||||||
|
}
|
||||||
|
else if (operation.consumes.size() > 1) {
|
||||||
|
Map<String, String> consume = operation.consumes.get(0);
|
||||||
|
skipTests.put("reason", "Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760");
|
||||||
|
operation.vendorExtensions.put("x-prefered-consume", consume);
|
||||||
|
if ("multipart/form-data".equals(consume.get(MEDIA_TYPE))) {
|
||||||
|
operation.isMultipart = Boolean.TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// A body without consumes means '*/*' has been used instead of application/json
|
||||||
|
if (operation.bodyParam != null) {
|
||||||
|
Map<String, String> consume = new HashMap<>();
|
||||||
|
consume.put(MEDIA_TYPE, "application/json");
|
||||||
|
operation.vendorExtensions.put("x-prefered-consume", consume);
|
||||||
|
skipTests.put("reason", "*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Choose to consume 'application/json' if available, else choose the last one.
|
||||||
|
if (operation.produces != null ) {
|
||||||
|
for (Map<String, String> produce: operation.produces) {
|
||||||
|
operation.vendorExtensions.put("x-prefered-produce", produce);
|
||||||
|
if (produce.get(MEDIA_TYPE).equals("application/json")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! skipTests.isEmpty()) {
|
||||||
|
operation.vendorExtensions.put("x-skip-test", skipTests);
|
||||||
|
}
|
||||||
|
if (operation.requestBodyExamples != null) {
|
||||||
|
for (Map<String, String> example: operation.requestBodyExamples) {
|
||||||
|
if (example.get("contentType") != null && example.get("contentType").equals("application/json")) {
|
||||||
|
operation.bodyParam.example = example.get("example");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The openapi pattern spec follows the Perl convention and style of modifiers. Python
|
||||||
|
* does not support this in as natural a way so it needs to convert it. See
|
||||||
|
* https://docs.python.org/2/howto/regex.html#compilation-flags for details.
|
||||||
|
*/
|
||||||
|
public void postProcessPattern(String pattern, Map<String, Object> vendorExtensions) {
|
||||||
|
if (pattern != null) {
|
||||||
|
int i = pattern.lastIndexOf('/');
|
||||||
|
|
||||||
|
//Must follow Perl /pattern/modifiers convention
|
||||||
|
if (pattern.charAt(0) != '/' || i < 2) {
|
||||||
|
throw new IllegalArgumentException("Pattern must follow the Perl "
|
||||||
|
+ "/pattern/modifiers convention. " + pattern + " is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String regex = pattern.substring(1, i).replace("'", "\\'");
|
||||||
|
List<String> modifiers = new ArrayList<String>();
|
||||||
|
|
||||||
|
for (char c : pattern.substring(i).toCharArray()) {
|
||||||
|
if (regexModifiers.containsKey(c)) {
|
||||||
|
String modifier = regexModifiers.get(c);
|
||||||
|
modifiers.add(modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vendorExtensions.put("x-regex", regex);
|
||||||
|
vendorExtensions.put("x-modifiers", modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessFile(File file, String fileType) {
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String pythonPostProcessFile = System.getenv("PYTHON_POST_PROCESS_FILE");
|
||||||
|
if (StringUtils.isEmpty(pythonPostProcessFile)) {
|
||||||
|
return; // skip if PYTHON_POST_PROCESS_FILE env variable is not defined
|
||||||
|
}
|
||||||
|
|
||||||
|
// only process files with py extension
|
||||||
|
if ("py".equals(FilenameUtils.getExtension(file.toString()))) {
|
||||||
|
String command = pythonPostProcessFile + " " + file.toString();
|
||||||
|
try {
|
||||||
|
Process p = Runtime.getRuntime().exec(command);
|
||||||
|
int exitValue = p.waitFor();
|
||||||
|
if (exitValue != 0) {
|
||||||
|
LOGGER.error("Error running the command ({}). Exit value: {}", command, exitValue);
|
||||||
|
} else {
|
||||||
|
LOGGER.info("Successfully executed: " + command);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.openapitools.codegen.languages;
|
||||||
|
|
||||||
|
import org.openapitools.codegen.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class PythonAiohttpConnexionServerCodegen extends PythonAbstractConnexionServerCodegen {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(PythonAiohttpConnexionServerCodegen.class);
|
||||||
|
|
||||||
|
public PythonAiohttpConnexionServerCodegen() {
|
||||||
|
super("python-aiohttp", true);
|
||||||
|
testPackage = "tests";
|
||||||
|
embeddedTemplateDir = templateDir = "python-aiohttp";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures a friendly name for the generator. This will be used by the generator
|
||||||
|
* to select the library with the -g flag.
|
||||||
|
*
|
||||||
|
* @return the friendly name for the generator
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "python-aiohttp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void addSupportingFiles() {
|
||||||
|
supportingFiles.add(new SupportingFile("conftest.mustache", testPackage, "conftest.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("__init__test.mustache", testPackage, "__init__.py"));
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
|
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
|
||||||
* Copyright 2018 SmartBear Software
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,19 +16,7 @@
|
|||||||
|
|
||||||
package org.openapitools.codegen.languages;
|
package org.openapitools.codegen.languages;
|
||||||
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import io.swagger.v3.oas.models.OpenAPI;
|
|
||||||
import io.swagger.v3.oas.models.Operation;
|
|
||||||
import io.swagger.v3.oas.models.PathItem;
|
|
||||||
import io.swagger.v3.oas.models.PathItem.HttpMethod;
|
|
||||||
import io.swagger.v3.oas.models.media.ArraySchema;
|
|
||||||
import io.swagger.v3.oas.models.media.Schema;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
import org.openapitools.codegen.*;
|
import org.openapitools.codegen.*;
|
||||||
import org.openapitools.codegen.utils.ModelUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -38,193 +25,11 @@ import java.util.*;
|
|||||||
import static org.openapitools.codegen.utils.StringUtils.camelize;
|
import static org.openapitools.codegen.utils.StringUtils.camelize;
|
||||||
import static org.openapitools.codegen.utils.StringUtils.underscore;
|
import static org.openapitools.codegen.utils.StringUtils.underscore;
|
||||||
|
|
||||||
public class PythonFlaskConnexionServerCodegen extends DefaultCodegen implements CodegenConfig {
|
public class PythonFlaskConnexionServerCodegen extends PythonAbstractConnexionServerCodegen {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(PythonFlaskConnexionServerCodegen.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(PythonFlaskConnexionServerCodegen.class);
|
||||||
|
|
||||||
public static final String CONTROLLER_PACKAGE = "controllerPackage";
|
|
||||||
public static final String DEFAULT_CONTROLLER = "defaultController";
|
|
||||||
public static final String SUPPORT_PYTHON2 = "supportPython2";
|
|
||||||
|
|
||||||
protected int serverPort = 8080;
|
|
||||||
protected String packageName;
|
|
||||||
protected String packageVersion;
|
|
||||||
protected String controllerPackage;
|
|
||||||
protected String defaultController;
|
|
||||||
protected Map<Character, String> regexModifiers;
|
|
||||||
|
|
||||||
public PythonFlaskConnexionServerCodegen() {
|
public PythonFlaskConnexionServerCodegen() {
|
||||||
super();
|
super("python-flask", false);
|
||||||
modelPackage = "models";
|
|
||||||
testPackage = "test";
|
|
||||||
|
|
||||||
languageSpecificPrimitives.clear();
|
|
||||||
languageSpecificPrimitives.add("int");
|
|
||||||
languageSpecificPrimitives.add("float");
|
|
||||||
languageSpecificPrimitives.add("List");
|
|
||||||
languageSpecificPrimitives.add("Dict");
|
|
||||||
languageSpecificPrimitives.add("bool");
|
|
||||||
languageSpecificPrimitives.add("str");
|
|
||||||
languageSpecificPrimitives.add("datetime");
|
|
||||||
languageSpecificPrimitives.add("date");
|
|
||||||
languageSpecificPrimitives.add("file");
|
|
||||||
languageSpecificPrimitives.add("object");
|
|
||||||
|
|
||||||
typeMapping.clear();
|
|
||||||
typeMapping.put("integer", "int");
|
|
||||||
typeMapping.put("float", "float");
|
|
||||||
typeMapping.put("number", "float");
|
|
||||||
typeMapping.put("long", "int");
|
|
||||||
typeMapping.put("double", "float");
|
|
||||||
typeMapping.put("array", "List");
|
|
||||||
typeMapping.put("map", "Dict");
|
|
||||||
typeMapping.put("boolean", "bool");
|
|
||||||
typeMapping.put("string", "str");
|
|
||||||
typeMapping.put("date", "date");
|
|
||||||
typeMapping.put("DateTime", "datetime");
|
|
||||||
typeMapping.put("object", "object");
|
|
||||||
typeMapping.put("file", "file");
|
|
||||||
typeMapping.put("UUID", "str");
|
|
||||||
|
|
||||||
// from https://docs.python.org/3/reference/lexical_analysis.html#keywords
|
|
||||||
setReservedWordsLowerCase(
|
|
||||||
Arrays.asList(
|
|
||||||
// @property
|
|
||||||
"property",
|
|
||||||
// python reserved words
|
|
||||||
"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", "None", "True", "False", "nonlocal"));
|
|
||||||
|
|
||||||
// set the output folder here
|
|
||||||
outputFolder = "generated-code/connexion";
|
|
||||||
|
|
||||||
apiTemplateFiles.put("controller.mustache", ".py");
|
|
||||||
modelTemplateFiles.put("model.mustache", ".py");
|
|
||||||
apiTestTemplateFiles().put("controller_test.mustache", ".py");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Template Location. This is the location which templates will be read from. The generator
|
|
||||||
* will use the resource stream to attempt to read the templates.
|
|
||||||
*/
|
|
||||||
embeddedTemplateDir = templateDir = "flaskConnexion";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Additional Properties. These values can be passed to the templates and
|
|
||||||
* are available in models, apis, and supporting files
|
|
||||||
*/
|
|
||||||
additionalProperties.put("serverPort", serverPort);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Supporting Files. You can write single files for the generator with the
|
|
||||||
* entire object tree available. If the input file has a suffix of `.mustache
|
|
||||||
* it will be processed by the template engine. Otherwise, it will be copied
|
|
||||||
*/
|
|
||||||
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
|
|
||||||
supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py"));
|
|
||||||
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("git_push.sh.mustache", "", "git_push.sh"));
|
|
||||||
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
|
|
||||||
supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml"));
|
|
||||||
supportingFiles.add(new SupportingFile("Dockerfile.mustache", "", "Dockerfile"));
|
|
||||||
supportingFiles.add(new SupportingFile("dockerignore.mustache", "", ".dockerignore"));
|
|
||||||
|
|
||||||
regexModifiers = new HashMap<Character, String>();
|
|
||||||
regexModifiers.put('i', "IGNORECASE");
|
|
||||||
regexModifiers.put('l', "LOCALE");
|
|
||||||
regexModifiers.put('m', "MULTILINE");
|
|
||||||
regexModifiers.put('s', "DOTALL");
|
|
||||||
regexModifiers.put('u', "UNICODE");
|
|
||||||
regexModifiers.put('x', "VERBOSE");
|
|
||||||
|
|
||||||
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "python package name (convention: snake_case).")
|
|
||||||
.defaultValue("openapi_server"));
|
|
||||||
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_VERSION, "python package version.")
|
|
||||||
.defaultValue("1.0.0"));
|
|
||||||
cliOptions.add(new CliOption(CONTROLLER_PACKAGE, "controller package").
|
|
||||||
defaultValue("controllers"));
|
|
||||||
cliOptions.add(new CliOption(DEFAULT_CONTROLLER, "default controller").
|
|
||||||
defaultValue("default_controller"));
|
|
||||||
cliOptions.add(new CliOption(SUPPORT_PYTHON2, "support python2").
|
|
||||||
defaultValue("false"));
|
|
||||||
cliOptions.add(new CliOption("serverPort", "TCP port to listen to in app.run").
|
|
||||||
defaultValue("8080"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void processOpts() {
|
|
||||||
super.processOpts();
|
|
||||||
|
|
||||||
if (StringUtils.isEmpty(System.getenv("PYTHON_POST_PROCESS_FILE"))) {
|
|
||||||
LOGGER.info("Environment variable PYTHON_POST_PROCESS_FILE not defined so the Python code may not be properly formatted. To define it, try 'export PYTHON_POST_PROCESS_FILE=\"/usr/local/bin/yapf -i\"' (Linux/Mac)");
|
|
||||||
LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
|
|
||||||
}
|
|
||||||
|
|
||||||
//apiTemplateFiles.clear();
|
|
||||||
|
|
||||||
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
|
|
||||||
setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
|
|
||||||
} else {
|
|
||||||
setPackageName("openapi_server");
|
|
||||||
additionalProperties.put(CodegenConstants.PACKAGE_NAME, this.packageName);
|
|
||||||
}
|
|
||||||
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_VERSION)) {
|
|
||||||
setPackageVersion((String) additionalProperties.get(CodegenConstants.PACKAGE_VERSION));
|
|
||||||
} else {
|
|
||||||
setPackageVersion("1.0.0");
|
|
||||||
additionalProperties.put(CodegenConstants.PACKAGE_VERSION, this.packageVersion);
|
|
||||||
}
|
|
||||||
if (additionalProperties.containsKey(CONTROLLER_PACKAGE)) {
|
|
||||||
this.controllerPackage = additionalProperties.get(CONTROLLER_PACKAGE).toString();
|
|
||||||
} else {
|
|
||||||
this.controllerPackage = "controllers";
|
|
||||||
additionalProperties.put(CONTROLLER_PACKAGE, this.controllerPackage);
|
|
||||||
}
|
|
||||||
if (additionalProperties.containsKey(DEFAULT_CONTROLLER)) {
|
|
||||||
this.defaultController = additionalProperties.get(DEFAULT_CONTROLLER).toString();
|
|
||||||
} else {
|
|
||||||
this.defaultController = "default_controller";
|
|
||||||
additionalProperties.put(DEFAULT_CONTROLLER, this.defaultController);
|
|
||||||
}
|
|
||||||
if (Boolean.TRUE.equals(additionalProperties.get(SUPPORT_PYTHON2))) {
|
|
||||||
additionalProperties.put(SUPPORT_PYTHON2, Boolean.TRUE);
|
|
||||||
typeMapping.put("long", "long");
|
|
||||||
}
|
|
||||||
supportingFiles.add(new SupportingFile("__init__.mustache", packageName, "__init__.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("__main__.mustache", packageName, "__main__.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("encoder.mustache", packageName, "encoder.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("util.mustache", packageName, "util.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("__init__.mustache", packageName + File.separatorChar + controllerPackage, "__init__.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("__init__model.mustache", packageName + File.separatorChar + modelPackage, "__init__.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("base_model_.mustache", packageName + File.separatorChar + modelPackage, "base_model_.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("__init__test.mustache", packageName + File.separatorChar + testPackage, "__init__.py"));
|
|
||||||
supportingFiles.add(new SupportingFile("openapi.mustache", packageName + File.separatorChar + "openapi", "openapi.yaml"));
|
|
||||||
|
|
||||||
modelPackage = packageName + "." + modelPackage;
|
|
||||||
controllerPackage = packageName + "." + controllerPackage;
|
|
||||||
testPackage = packageName + "." + testPackage;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String dropDots(String str) {
|
|
||||||
return str.replaceAll("\\.", "_");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String apiPackage() {
|
|
||||||
return controllerPackage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the type of generator.
|
|
||||||
*
|
|
||||||
* @return the CodegenType for this generator
|
|
||||||
* @see org.openapitools.codegen.CodegenType
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public CodegenType getTag() {
|
|
||||||
return CodegenType.SERVER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,493 +43,18 @@ public class PythonFlaskConnexionServerCodegen extends DefaultCodegen implements
|
|||||||
return "python-flask";
|
return "python-flask";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns human-friendly help for the generator. Provide the consumer with help
|
|
||||||
* tips, parameters here
|
|
||||||
*
|
|
||||||
* @return A string value for the help message
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getHelp() {
|
protected void addSupportingFiles() {
|
||||||
return "Generates a Python server library using the Connexion project. By default, " +
|
supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore"));
|
||||||
"it will also generate service classes -- which you can disable with the `-Dnoservice` environment variable.";
|
supportingFiles.add(new SupportingFile("Dockerfile.mustache", "", "Dockerfile"));
|
||||||
|
supportingFiles.add(new SupportingFile("dockerignore.mustache", "", ".dockerignore"));
|
||||||
|
supportingFiles.add(new SupportingFile("setup.mustache", "", "setup.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("tox.mustache", "", "tox.ini"));
|
||||||
|
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
|
||||||
|
supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml"));
|
||||||
|
supportingFiles.add(new SupportingFile("encoder.mustache", packageName, "encoder.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("__init__test.mustache", packageName + File.separatorChar + testPackage, "__init__.py"));
|
||||||
|
supportingFiles.add(new SupportingFile("__init__.mustache", packageName, "__init__.py"));
|
||||||
|
testPackage = packageName + "." + testPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toApiName(String name) {
|
|
||||||
if (name == null || name.length() == 0) {
|
|
||||||
return "DefaultController";
|
|
||||||
}
|
|
||||||
return camelize(name, false) + "Controller";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toApiFilename(String name) {
|
|
||||||
return underscore(toApiName(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toApiTestFilename(String name) {
|
|
||||||
return "test_" + toApiFilename(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escapes a reserved word as defined in the `reservedWords` array. Handle escaping
|
|
||||||
* those terms here. This logic is only called if a variable matches the reserved words
|
|
||||||
*
|
|
||||||
* @return the escaped term
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String escapeReservedWord(String name) {
|
|
||||||
if (this.reservedWordsMappings().containsKey(name)) {
|
|
||||||
return this.reservedWordsMappings().get(name);
|
|
||||||
}
|
|
||||||
return "_" + name; // add an underscore to the name
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Location to write api files. You can use the apiPackage() as defined when the class is
|
|
||||||
* instantiated
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String apiFileFolder() {
|
|
||||||
return outputFolder + File.separator + apiPackage().replace('.', File.separatorChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTypeDeclaration(Schema p) {
|
|
||||||
if (ModelUtils.isArraySchema(p)) {
|
|
||||||
ArraySchema ap = (ArraySchema) p;
|
|
||||||
Schema inner = ap.getItems();
|
|
||||||
return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]";
|
|
||||||
} else if (ModelUtils.isMapSchema(p)) {
|
|
||||||
Schema inner = ModelUtils.getAdditionalProperties(p);
|
|
||||||
return getSchemaType(p) + "[str, " + getTypeDeclaration(inner) + "]";
|
|
||||||
}
|
|
||||||
return super.getTypeDeclaration(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSchemaType(Schema p) {
|
|
||||||
String schemaType = super.getSchemaType(p);
|
|
||||||
String type = null;
|
|
||||||
if (typeMapping.containsKey(schemaType)) {
|
|
||||||
type = typeMapping.get(schemaType);
|
|
||||||
if (languageSpecificPrimitives.contains(type)) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type = toModelName(schemaType);
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void preprocessOpenAPI(OpenAPI openAPI) {
|
|
||||||
// need vendor extensions for x-openapi-router-controller
|
|
||||||
Map<String, PathItem> paths = openAPI.getPaths();
|
|
||||||
if (paths != null) {
|
|
||||||
for (String pathname : paths.keySet()) {
|
|
||||||
PathItem path = paths.get(pathname);
|
|
||||||
Map<HttpMethod, Operation> operationMap = path.readOperationsMap();
|
|
||||||
if (operationMap != null) {
|
|
||||||
for (HttpMethod method : operationMap.keySet()) {
|
|
||||||
Operation operation = operationMap.get(method);
|
|
||||||
String tag = "default";
|
|
||||||
if (operation.getTags() != null && operation.getTags().size() > 0) {
|
|
||||||
tag = operation.getTags().get(0);
|
|
||||||
}
|
|
||||||
String operationId = operation.getOperationId();
|
|
||||||
if (operationId == null) {
|
|
||||||
operationId = getOrGenerateOperationId(operation, pathname, method.toString());
|
|
||||||
}
|
|
||||||
operation.setOperationId(toOperationId(operationId));
|
|
||||||
if (operation.getExtensions() == null || operation.getExtensions().get("x-openapi-router-controller") == null) {
|
|
||||||
operation.addExtension(
|
|
||||||
"x-openapi-router-controller",
|
|
||||||
controllerPackage + "." + toApiFilename(tag)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static List<Map<String, Object>> getOperations(Map<String, Object> objs) {
|
|
||||||
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
|
|
||||||
Map<String, Object> apiInfo = (Map<String, Object>) objs.get("apiInfo");
|
|
||||||
List<Map<String, Object>> apis = (List<Map<String, Object>>) apiInfo.get("apis");
|
|
||||||
for (Map<String, Object> api : apis) {
|
|
||||||
result.add((Map<String, Object>) api.get("operations"));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Map<String, Object>> sortOperationsByPath(List<CodegenOperation> ops) {
|
|
||||||
Multimap<String, CodegenOperation> opsByPath = ArrayListMultimap.create();
|
|
||||||
|
|
||||||
for (CodegenOperation op : ops) {
|
|
||||||
opsByPath.put(op.path, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Map<String, Object>> opsByPathList = new ArrayList<Map<String, Object>>();
|
|
||||||
for (Map.Entry<String, Collection<CodegenOperation>> entry : opsByPath.asMap().entrySet()) {
|
|
||||||
Map<String, Object> opsByPathEntry = new HashMap<String, Object>();
|
|
||||||
opsByPathList.add(opsByPathEntry);
|
|
||||||
opsByPathEntry.put("path", entry.getKey());
|
|
||||||
opsByPathEntry.put("operation", entry.getValue());
|
|
||||||
List<CodegenOperation> operationsForThisPath = Lists.newArrayList(entry.getValue());
|
|
||||||
operationsForThisPath.get(operationsForThisPath.size() - 1).hasMore = false;
|
|
||||||
if (opsByPathList.size() < opsByPath.asMap().size()) {
|
|
||||||
opsByPathEntry.put("hasMore", "true");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return opsByPathList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
|
|
||||||
generateYAMLSpecFile(objs);
|
|
||||||
|
|
||||||
for (Map<String, Object> operations : getOperations(objs)) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<CodegenOperation> ops = (List<CodegenOperation>) operations.get("operation");
|
|
||||||
|
|
||||||
List<Map<String, Object>> opsByPathList = sortOperationsByPath(ops);
|
|
||||||
operations.put("operationsByPath", opsByPathList);
|
|
||||||
}
|
|
||||||
return super.postProcessSupportingFileData(objs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toVarName(String name) {
|
|
||||||
// sanitize name
|
|
||||||
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
|
|
||||||
|
|
||||||
// remove dollar sign
|
|
||||||
name = name.replaceAll("$", "");
|
|
||||||
|
|
||||||
// if it's all uppper case, convert to lower case
|
|
||||||
if (name.matches("^[A-Z_]*$")) {
|
|
||||||
name = name.toLowerCase(Locale.ROOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// underscore the variable name
|
|
||||||
// petId => pet_id
|
|
||||||
name = underscore(name);
|
|
||||||
|
|
||||||
// remove leading underscore
|
|
||||||
name = name.replaceAll("^_*", "");
|
|
||||||
|
|
||||||
// for reserved word or word starting with number, append _
|
|
||||||
if (isReservedWord(name) || name.matches("^\\d.*")) {
|
|
||||||
name = escapeReservedWord(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toParamName(String name) {
|
|
||||||
// to avoid conflicts with 'callback' parameter for async call
|
|
||||||
if ("callback".equals(name)) {
|
|
||||||
return "param_callback";
|
|
||||||
}
|
|
||||||
|
|
||||||
// should be the same as variable name
|
|
||||||
return toVarName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toModelFilename(String name) {
|
|
||||||
// underscore the model file name
|
|
||||||
// PhoneNumber => phone_number
|
|
||||||
return underscore(dropDots(toModelName(name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toModelName(String name) {
|
|
||||||
name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
|
|
||||||
// remove dollar sign
|
|
||||||
name = name.replaceAll("$", "");
|
|
||||||
|
|
||||||
// model name cannot use reserved keyword, e.g. return
|
|
||||||
if (isReservedWord(name)) {
|
|
||||||
LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + camelize("model_" + name));
|
|
||||||
name = "model_" + name; // e.g. return => ModelReturn (after camelize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// model name starts with number
|
|
||||||
if (name.matches("^\\d.*")) {
|
|
||||||
LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + name));
|
|
||||||
name = "model_" + name; // e.g. 200Response => Model200Response (after camelize)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(modelNamePrefix)) {
|
|
||||||
name = modelNamePrefix + "_" + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(modelNameSuffix)) {
|
|
||||||
name = name + "_" + modelNameSuffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
// camelize the model name
|
|
||||||
// phone_number => PhoneNumber
|
|
||||||
return camelize(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toOperationId(String operationId) {
|
|
||||||
// throw exception if method name is empty (should not occur as an auto-generated method name will be used)
|
|
||||||
if (StringUtils.isEmpty(operationId)) {
|
|
||||||
throw new RuntimeException("Empty method name (operationId) not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// method name cannot use reserved keyword, e.g. return
|
|
||||||
if (isReservedWord(operationId)) {
|
|
||||||
LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + underscore(sanitizeName("call_" + operationId)));
|
|
||||||
operationId = "call_" + operationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return underscore(sanitizeName(operationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the default value of the property
|
|
||||||
*
|
|
||||||
* @param p OpenAPI property object
|
|
||||||
* @return string presentation of the default value of the property
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toDefaultValue(Schema p) {
|
|
||||||
if (ModelUtils.isBooleanSchema(p)) {
|
|
||||||
if (p.getDefault() != null) {
|
|
||||||
if (p.getDefault().toString().equalsIgnoreCase("false"))
|
|
||||||
return "False";
|
|
||||||
else
|
|
||||||
return "True";
|
|
||||||
}
|
|
||||||
} else if (ModelUtils.isDateSchema(p)) {
|
|
||||||
// TODO
|
|
||||||
} else if (ModelUtils.isDateTimeSchema(p)) {
|
|
||||||
// TODO
|
|
||||||
} else if (ModelUtils.isNumberSchema(p)) {
|
|
||||||
if (p.getDefault() != null) {
|
|
||||||
return p.getDefault().toString();
|
|
||||||
}
|
|
||||||
} else if (ModelUtils.isIntegerSchema(p)) {
|
|
||||||
if (p.getDefault() != null) {
|
|
||||||
return p.getDefault().toString();
|
|
||||||
}
|
|
||||||
} else if (ModelUtils.isStringSchema(p)) {
|
|
||||||
if (p.getDefault() != null) {
|
|
||||||
return "'" + (String) p.getDefault() + "'";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setParameterExampleValue(CodegenParameter p) {
|
|
||||||
String example;
|
|
||||||
|
|
||||||
if (p.defaultValue == null) {
|
|
||||||
example = p.example;
|
|
||||||
} else {
|
|
||||||
p.example = p.defaultValue;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String type = p.baseType;
|
|
||||||
if (type == null) {
|
|
||||||
type = p.dataType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("String".equalsIgnoreCase(type) || "str".equalsIgnoreCase(type)) {
|
|
||||||
if (example == null) {
|
|
||||||
example = p.paramName + "_example";
|
|
||||||
}
|
|
||||||
example = "'" + escapeText(example) + "'";
|
|
||||||
} else if ("Integer".equals(type) || "int".equals(type)) {
|
|
||||||
if (p.minimum != null) {
|
|
||||||
example = "" + (Integer.valueOf(p.minimum) + 1);
|
|
||||||
}
|
|
||||||
if (p.maximum != null) {
|
|
||||||
example = "" + p.maximum;
|
|
||||||
} else if (example == null) {
|
|
||||||
example = "56";
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ("Long".equalsIgnoreCase(type)) {
|
|
||||||
if (p.minimum != null) {
|
|
||||||
example = "" + (Long.valueOf(p.minimum) + 1);
|
|
||||||
}
|
|
||||||
if (p.maximum != null) {
|
|
||||||
example = "" + p.maximum;
|
|
||||||
} else if (example == null) {
|
|
||||||
example = "789";
|
|
||||||
}
|
|
||||||
} else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) {
|
|
||||||
if (p.minimum != null) {
|
|
||||||
example = "" + p.minimum;
|
|
||||||
} else if (p.maximum != null) {
|
|
||||||
example = "" + p.maximum;
|
|
||||||
} else if (example == null) {
|
|
||||||
example = "3.4";
|
|
||||||
}
|
|
||||||
} else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) {
|
|
||||||
if (example == null) {
|
|
||||||
example = "True";
|
|
||||||
}
|
|
||||||
} else if ("file".equalsIgnoreCase(type)) {
|
|
||||||
example = "(BytesIO(b'some file data'), 'file.txt')";
|
|
||||||
} else if ("Date".equalsIgnoreCase(type)) {
|
|
||||||
if (example == null) {
|
|
||||||
example = "2013-10-20";
|
|
||||||
}
|
|
||||||
example = "'" + escapeText(example) + "'";
|
|
||||||
} else if ("DateTime".equalsIgnoreCase(type)) {
|
|
||||||
if (example == null) {
|
|
||||||
example = "2013-10-20T19:20:30+01:00";
|
|
||||||
}
|
|
||||||
example = "'" + escapeText(example) + "'";
|
|
||||||
} else if (!languageSpecificPrimitives.contains(type)) {
|
|
||||||
// type is a model class, e.g. User
|
|
||||||
example = type + "()";
|
|
||||||
} else {
|
|
||||||
LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.items != null && p.items.defaultValue != null) {
|
|
||||||
example = p.items.defaultValue;
|
|
||||||
}
|
|
||||||
if (example == null) {
|
|
||||||
example = "None";
|
|
||||||
} else if (Boolean.TRUE.equals(p.isListContainer)) {
|
|
||||||
if (Boolean.TRUE.equals(p.isBodyParam)) {
|
|
||||||
example = "[" + example + "]";
|
|
||||||
}
|
|
||||||
} else if (Boolean.TRUE.equals(p.isMapContainer)) {
|
|
||||||
example = "{'key': " + example + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
p.example = example;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPackageName(String packageName) {
|
|
||||||
this.packageName = packageName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPackageVersion(String packageVersion) {
|
|
||||||
this.packageVersion = packageVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String escapeQuotationMark(String input) {
|
|
||||||
// remove ' to avoid code injection
|
|
||||||
return input.replace("'", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String escapeUnsafeCharacters(String input) {
|
|
||||||
// remove multiline comment
|
|
||||||
return input.replace("'''", "'_'_'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toModelImport(String name) {
|
|
||||||
String modelImport;
|
|
||||||
if (StringUtils.startsWithAny(name, "import", "from")) {
|
|
||||||
modelImport = name;
|
|
||||||
} else {
|
|
||||||
modelImport = "from ";
|
|
||||||
if (!"".equals(modelPackage())) {
|
|
||||||
modelImport += modelPackage() + ".";
|
|
||||||
}
|
|
||||||
modelImport += toModelFilename(name) + " import " + name;
|
|
||||||
}
|
|
||||||
return modelImport;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
|
|
||||||
if (StringUtils.isNotEmpty(property.pattern)) {
|
|
||||||
addImport(model, "import re");
|
|
||||||
}
|
|
||||||
postProcessPattern(property.pattern, property.vendorExtensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
|
|
||||||
// process enum in models
|
|
||||||
return postProcessModelsEnum(objs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postProcessParameter(CodegenParameter parameter) {
|
|
||||||
postProcessPattern(parameter.pattern, parameter.vendorExtensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The openapi pattern spec follows the Perl convention and style of modifiers. Python
|
|
||||||
* does not support this in as natural a way so it needs to convert it. See
|
|
||||||
* https://docs.python.org/2/howto/regex.html#compilation-flags for details.
|
|
||||||
*/
|
|
||||||
public void postProcessPattern(String pattern, Map<String, Object> vendorExtensions) {
|
|
||||||
if (pattern != null) {
|
|
||||||
int i = pattern.lastIndexOf('/');
|
|
||||||
|
|
||||||
//Must follow Perl /pattern/modifiers convention
|
|
||||||
if (pattern.charAt(0) != '/' || i < 2) {
|
|
||||||
throw new IllegalArgumentException("Pattern must follow the Perl "
|
|
||||||
+ "/pattern/modifiers convention. " + pattern + " is not valid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
String regex = pattern.substring(1, i).replace("'", "\\'");
|
|
||||||
List<String> modifiers = new ArrayList<String>();
|
|
||||||
|
|
||||||
for (char c : pattern.substring(i).toCharArray()) {
|
|
||||||
if (regexModifiers.containsKey(c)) {
|
|
||||||
String modifier = regexModifiers.get(c);
|
|
||||||
modifiers.add(modifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vendorExtensions.put("x-regex", regex);
|
|
||||||
vendorExtensions.put("x-modifiers", modifiers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postProcessFile(File file, String fileType) {
|
|
||||||
if (file == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String pythonPostProcessFile = System.getenv("PYTHON_POST_PROCESS_FILE");
|
|
||||||
if (StringUtils.isEmpty(pythonPostProcessFile)) {
|
|
||||||
return; // skip if PYTHON_POST_PROCESS_FILE env variable is not defined
|
|
||||||
}
|
|
||||||
|
|
||||||
// only process files with py extension
|
|
||||||
if ("py".equals(FilenameUtils.getExtension(file.toString()))) {
|
|
||||||
String command = pythonPostProcessFile + " " + file.toString();
|
|
||||||
try {
|
|
||||||
Process p = Runtime.getRuntime().exec(command);
|
|
||||||
int exitValue = p.waitFor();
|
|
||||||
if (exitValue != 0) {
|
|
||||||
LOGGER.error("Error running the command ({}). Exit value: {}", command, exitValue);
|
|
||||||
} else {
|
|
||||||
LOGGER.info("Successfully executed: " + command);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.error("Error running the command ({}). Exception: {}", command, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ org.openapitools.codegen.languages.PhpZendExpressivePathHandlerServerCodegen
|
|||||||
org.openapitools.codegen.languages.PowerShellClientCodegen
|
org.openapitools.codegen.languages.PowerShellClientCodegen
|
||||||
org.openapitools.codegen.languages.PythonClientCodegen
|
org.openapitools.codegen.languages.PythonClientCodegen
|
||||||
org.openapitools.codegen.languages.PythonFlaskConnexionServerCodegen
|
org.openapitools.codegen.languages.PythonFlaskConnexionServerCodegen
|
||||||
|
org.openapitools.codegen.languages.PythonAiohttpConnexionServerCodegen
|
||||||
org.openapitools.codegen.languages.RClientCodegen
|
org.openapitools.codegen.languages.RClientCodegen
|
||||||
org.openapitools.codegen.languages.RubyClientCodegen
|
org.openapitools.codegen.languages.RubyClientCodegen
|
||||||
org.openapitools.codegen.languages.RubyOnRailsServerCodegen
|
org.openapitools.codegen.languages.RubyOnRailsServerCodegen
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
# OpenAPI generated server
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the
|
||||||
|
[OpenAPI-Spec](https://openapis.org) from a remote server, you can easily generate a server stub. This
|
||||||
|
is an example of building a OpenAPI-enabled aiohtpp server.
|
||||||
|
|
||||||
|
This example uses the [Connexion](https://github.com/zalando/connexion) library on top of aiohtpp.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
Python 3.5.2+
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
To run the server, please execute the following from the root directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
python3 -m {{packageName}}
|
||||||
|
```
|
||||||
|
|
||||||
|
and open your browser to here:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:{{serverPort}}{{contextPath}}/ui/
|
||||||
|
```
|
||||||
|
|
||||||
|
Your OpenAPI definition lives here:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:{{serverPort}}{{contextPath}}/openapi.json
|
||||||
|
```
|
||||||
|
|
||||||
|
To launch the integration tests, use pytest:
|
||||||
|
```
|
||||||
|
sudo pip install -r test-requirements.txt
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prevent file overriding
|
||||||
|
|
||||||
|
After first generation, add edited files to _.openapi-generator-ignore_ to prevent generator to overwrite them. Typically:
|
||||||
|
```
|
||||||
|
server/controllers/*
|
||||||
|
test/*
|
||||||
|
*.txt
|
||||||
|
```
|
@ -0,0 +1,12 @@
|
|||||||
|
import os
|
||||||
|
import connexion
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
options = {
|
||||||
|
"swagger_ui": True
|
||||||
|
}
|
||||||
|
specification_dir = os.path.join(os.path.dirname(__file__), 'openapi')
|
||||||
|
app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, options=options)
|
||||||
|
app.add_api('openapi.yaml', arguments={'title': '{{appName}}'}, pass_context_arg_name='request')
|
||||||
|
app.run(port={{serverPort}})
|
@ -0,0 +1,5 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
# import models into model package
|
||||||
|
{{#models}}{{#model}}from {{modelPackage}}.{{classFilename}} import {{classname}}{{/model}}
|
||||||
|
{{/models}}
|
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from . import main
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,66 @@
|
|||||||
|
import pprint
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from {{packageName}} import util
|
||||||
|
|
||||||
|
T = typing.TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
|
class Model(object):
|
||||||
|
# openapiTypes: The key is attribute name and the
|
||||||
|
# value is attribute type.
|
||||||
|
openapi_types = {}
|
||||||
|
|
||||||
|
# attributeMap: The key is attribute name and the
|
||||||
|
# value is json key in definition.
|
||||||
|
attribute_map = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: T, dikt: dict) -> T:
|
||||||
|
"""Returns the dict as a model"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""Returns the model properties as a dict
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for attr_key, json_key in self.attribute_map.items():
|
||||||
|
value = getattr(self, attr_key)
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
if isinstance(value, list):
|
||||||
|
result[json_key] = list(map(
|
||||||
|
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
||||||
|
value
|
||||||
|
))
|
||||||
|
elif hasattr(value, "to_dict"):
|
||||||
|
result[json_key] = value.to_dict()
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
result[json_key] = dict(map(
|
||||||
|
lambda item: (item[0], item[1].to_dict())
|
||||||
|
if hasattr(item[1], "to_dict") else item,
|
||||||
|
value.items()
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
result[json_key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def to_str(self) -> str:
|
||||||
|
"""Returns the string representation of the model
|
||||||
|
"""
|
||||||
|
return pprint.pformat(self.to_dict())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""For `print` and `pprint`"""
|
||||||
|
return self.to_str()
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Returns true if both objects are equal"""
|
||||||
|
return self.__dict__ == other.__dict__
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
"""Returns true if both objects are not equal"""
|
||||||
|
return not self == other
|
@ -0,0 +1,17 @@
|
|||||||
|
import logging
|
||||||
|
import pytest
|
||||||
|
import os
|
||||||
|
|
||||||
|
import connexion
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client(loop, aiohttp_client):
|
||||||
|
logging.getLogger('connexion.operation').setLevel('ERROR')
|
||||||
|
options = {
|
||||||
|
"swagger_ui": True
|
||||||
|
}
|
||||||
|
specification_dir = os.path.join(os.path.dirname(__file__), '..', '{{packageName}}', 'openapi')
|
||||||
|
app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, options=options)
|
||||||
|
app.add_api('openapi.yaml', pass_context_arg_name='request')
|
||||||
|
return loop.run_until_complete(aiohttp_client(app.app))
|
@ -0,0 +1,104 @@
|
|||||||
|
from typing import List, Dict
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
{{#imports}}{{import}}
|
||||||
|
{{/imports}}
|
||||||
|
from {{packageName}} import util
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
|
||||||
|
|
||||||
|
async def {{operationId}}(request: web.Request, {{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> web.Response:
|
||||||
|
"""{{#summary}}{{.}}{{/summary}}{{^summary}}{{operationId}}{{/summary}}
|
||||||
|
|
||||||
|
{{#notes}}{{.}}{{/notes}}
|
||||||
|
|
||||||
|
{{#allParams}}
|
||||||
|
:param {{paramName}}: {{description}}
|
||||||
|
{{^isContainer}}
|
||||||
|
{{#isPrimitiveType}}
|
||||||
|
:type {{paramName}}: {{>param_type}}
|
||||||
|
{{/isPrimitiveType}}
|
||||||
|
{{#isUuid}}
|
||||||
|
:type {{paramName}}: {{>param_type}}
|
||||||
|
{{/isUuid}}
|
||||||
|
{{^isPrimitiveType}}
|
||||||
|
{{#isFile}}
|
||||||
|
:type {{paramName}}: werkzeug.datastructures.FileStorage
|
||||||
|
{{/isFile}}
|
||||||
|
{{^isFile}}
|
||||||
|
{{^isUuid}}
|
||||||
|
:type {{paramName}}: dict | bytes
|
||||||
|
{{/isUuid}}
|
||||||
|
{{/isFile}}
|
||||||
|
{{/isPrimitiveType}}
|
||||||
|
{{/isContainer}}
|
||||||
|
{{#isListContainer}}
|
||||||
|
{{#items}}
|
||||||
|
{{#isPrimitiveType}}
|
||||||
|
:type {{paramName}}: List[{{>param_type}}]
|
||||||
|
{{/isPrimitiveType}}
|
||||||
|
{{^isPrimitiveType}}
|
||||||
|
:type {{paramName}}: list | bytes
|
||||||
|
{{/isPrimitiveType}}
|
||||||
|
{{/items}}
|
||||||
|
{{/isListContainer}}
|
||||||
|
{{#isMapContainer}}
|
||||||
|
{{#items}}
|
||||||
|
{{#isPrimitiveType}}
|
||||||
|
:type {{paramName}}: Dict[str, {{>param_type}}]
|
||||||
|
{{/isPrimitiveType}}
|
||||||
|
{{^isPrimitiveType}}
|
||||||
|
:type {{paramName}}: dict | bytes
|
||||||
|
{{/isPrimitiveType}}
|
||||||
|
{{/items}}
|
||||||
|
{{/isMapContainer}}
|
||||||
|
{{/allParams}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
{{#allParams}}
|
||||||
|
{{^isContainer}}
|
||||||
|
{{#isDate}}
|
||||||
|
{{paramName}} = util.deserialize_date({{paramName}})
|
||||||
|
{{/isDate}}
|
||||||
|
{{#isDateTime}}
|
||||||
|
{{paramName}} = util.deserialize_datetime({{paramName}})
|
||||||
|
{{/isDateTime}}
|
||||||
|
{{^isPrimitiveType}}
|
||||||
|
{{^isFile}}
|
||||||
|
{{^isUuid}}
|
||||||
|
{{paramName}} = {{baseType}}.from_dict({{paramName}})
|
||||||
|
{{/isUuid}}
|
||||||
|
{{/isFile}}
|
||||||
|
{{/isPrimitiveType}}
|
||||||
|
{{/isContainer}}
|
||||||
|
{{#isListContainer}}
|
||||||
|
{{#items}}
|
||||||
|
{{#isDate}}
|
||||||
|
{{paramName}} = [util.deserialize_date(s) for s in {{paramName}}]
|
||||||
|
{{/isDate}}
|
||||||
|
{{#isDateTime}}
|
||||||
|
{{paramName}} = [util.deserialize_datetime(s) for s in {{paramName}}]
|
||||||
|
{{/isDateTime}}
|
||||||
|
{{#complexType}}
|
||||||
|
{{paramName}} = [{{complexType}}.from_dict(d) for d in {{paramName}}]
|
||||||
|
{{/complexType}}
|
||||||
|
{{/items}}
|
||||||
|
{{/isListContainer}}
|
||||||
|
{{#isMapContainer}}
|
||||||
|
{{#items}}
|
||||||
|
{{#isDate}}
|
||||||
|
{{paramName}} = {k: util.deserialize_date(v) for k, v in {{paramName}}}
|
||||||
|
{{/isDate}}
|
||||||
|
{{#isDateTime}}
|
||||||
|
{{paramName}} = {k: util.deserialize_datetime(v) for k, v in {{paramName}}}
|
||||||
|
{{/isDateTime}}
|
||||||
|
{{#complexType}}
|
||||||
|
{{paramName}} = {k: {{baseType}}.from_dict(v) for k, v in {{paramName}}}
|
||||||
|
{{/complexType}}
|
||||||
|
{{/items}}
|
||||||
|
{{/isMapContainer}}
|
||||||
|
{{/allParams}}
|
||||||
|
return web.Response(status=200)
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
@ -0,0 +1,68 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import json
|
||||||
|
from aiohttp import web
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
{{#isMultipart}}
|
||||||
|
from aiohttp import FormData
|
||||||
|
{{/isMultipart}}
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
||||||
|
|
||||||
|
{{#imports}}{{import}}
|
||||||
|
{{/imports}}
|
||||||
|
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
|
||||||
|
{{#vendorExtensions.x-skip-test}}
|
||||||
|
@pytest.mark.skip("{{reason}}")
|
||||||
|
{{/vendorExtensions.x-skip-test}}
|
||||||
|
async def test_{{operationId}}(client):
|
||||||
|
"""Test case for {{{operationId}}}
|
||||||
|
|
||||||
|
{{{summary}}}
|
||||||
|
"""
|
||||||
|
{{#bodyParam}}
|
||||||
|
{{paramName}} = {{{example}}}
|
||||||
|
{{/bodyParam}}
|
||||||
|
{{#queryParams}}
|
||||||
|
{{#-first}}params = [{{/-first}}{{^-first}} {{/-first}}('{{paramName}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}}
|
||||||
|
{{/queryParams}}
|
||||||
|
headers = { {{#vendorExtensions.x-prefered-produce}}
|
||||||
|
'Accept': '{{mediaType}}',{{/vendorExtensions.x-prefered-produce}}{{#vendorExtensions.x-prefered-consume}}
|
||||||
|
'Content-Type': '{{mediaType}}',{{/vendorExtensions.x-prefered-consume}}{{#headerParams}}
|
||||||
|
'{{paramName}}': {{{example}}},{{/headerParams}}{{#authMethods}}
|
||||||
|
{{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}'{{name}}': 'special-key',{{/isApiKey}}{{#isBasicBasic}}'Authorization': 'BasicZm9vOmJhcg==',{{/isBasicBasic}}{{#isBasicBearer}}'Authorization': 'Bearer special-key',{{/isBasicBearer}}{{/authMethods}}
|
||||||
|
}
|
||||||
|
{{#formParams}}
|
||||||
|
{{#isMultipart}}
|
||||||
|
{{#-first}}
|
||||||
|
data = FormData()
|
||||||
|
{{/-first}}
|
||||||
|
data.add_field('{{paramName}}', {{{example}}})
|
||||||
|
{{/isMultipart}}
|
||||||
|
{{^isMultipart}}
|
||||||
|
{{#-first}}
|
||||||
|
data = {
|
||||||
|
{{/-first}}
|
||||||
|
'{{paramName}}': {{{example}}}{{#hasMore}},{{/hasMore}}
|
||||||
|
{{^hasMore}}
|
||||||
|
}
|
||||||
|
{{/hasMore}}
|
||||||
|
{{/isMultipart}}
|
||||||
|
{{/formParams}}
|
||||||
|
response = await client.request(
|
||||||
|
method='{{httpMethod}}',
|
||||||
|
path='{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}},
|
||||||
|
headers=headers,{{#bodyParam}}
|
||||||
|
json={{paramName}},{{/bodyParam}}{{#formParams}}{{#-first}}
|
||||||
|
data=data,{{/-first}}{{/formParams}}{{#queryParams}}{{#-first}}
|
||||||
|
params=params,{{/-first}}{{/queryParams}}
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
@ -0,0 +1,162 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from typing import List, Dict, Type
|
||||||
|
|
||||||
|
from {{modelPackage}}.base_model_ import Model
|
||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
{{#pyImports}}
|
||||||
|
{{import}}
|
||||||
|
{{/pyImports}}
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
||||||
|
from {{packageName}} import util
|
||||||
|
|
||||||
|
|
||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
class {{classname}}(Model):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
"""{{#allowableValues}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
allowed enum values
|
||||||
|
"""
|
||||||
|
{{#enumVars}}
|
||||||
|
{{name}} = {{{value}}}{{^-last}}
|
||||||
|
{{/-last}}
|
||||||
|
{{/enumVars}}{{/allowableValues}}
|
||||||
|
|
||||||
|
def __init__(self{{#vars}}, {{name}}: {{dataType}}={{#defaultValue}}{{{defaultValue}}}{{/defaultValue}}{{^defaultValue}}None{{/defaultValue}}{{/vars}}):
|
||||||
|
"""{{classname}} - a model defined in OpenAPI
|
||||||
|
|
||||||
|
{{#vars}}
|
||||||
|
:param {{name}}: The {{name}} of this {{classname}}.
|
||||||
|
{{/vars}}
|
||||||
|
"""
|
||||||
|
self.openapi_types = {
|
||||||
|
{{#vars}}
|
||||||
|
'{{name}}': {{dataType}}{{#hasMore}},{{/hasMore}}
|
||||||
|
{{/vars}}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attribute_map = {
|
||||||
|
{{#vars}}
|
||||||
|
'{{name}}': '{{baseName}}'{{#hasMore}},{{/hasMore}}
|
||||||
|
{{/vars}}
|
||||||
|
}
|
||||||
|
{{#vars}}{{#-first}}
|
||||||
|
{{/-first}}
|
||||||
|
self._{{name}} = {{name}}
|
||||||
|
{{/vars}}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dikt: dict) -> '{{classname}}':
|
||||||
|
"""Returns the dict as a model
|
||||||
|
|
||||||
|
:param dikt: A dict.
|
||||||
|
:return: The {{name}} of this {{classname}}.
|
||||||
|
"""
|
||||||
|
return util.deserialize_model(dikt, cls){{#vars}}{{#-first}}
|
||||||
|
|
||||||
|
{{/-first}}
|
||||||
|
@property
|
||||||
|
def {{name}}(self):
|
||||||
|
"""Gets the {{name}} of this {{classname}}.
|
||||||
|
|
||||||
|
{{#description}}
|
||||||
|
{{{description}}}
|
||||||
|
{{/description}}
|
||||||
|
|
||||||
|
:return: The {{name}} of this {{classname}}.
|
||||||
|
:rtype: {{dataType}}
|
||||||
|
"""
|
||||||
|
return self._{{name}}
|
||||||
|
|
||||||
|
@{{name}}.setter
|
||||||
|
def {{name}}(self, {{name}}):
|
||||||
|
"""Sets the {{name}} of this {{classname}}.
|
||||||
|
|
||||||
|
{{#description}}
|
||||||
|
{{{description}}}
|
||||||
|
{{/description}}
|
||||||
|
|
||||||
|
:param {{name}}: The {{name}} of this {{classname}}.
|
||||||
|
:type {{name}}: {{dataType}}
|
||||||
|
"""
|
||||||
|
{{#isEnum}}
|
||||||
|
allowed_values = [{{#allowableValues}}{{#values}}"{{{this}}}"{{^-last}}, {{/-last}}{{/values}}{{/allowableValues}}]
|
||||||
|
{{#isContainer}}
|
||||||
|
{{#isListContainer}}
|
||||||
|
if not set({{{name}}}).issubset(set(allowed_values)):
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid values for `{{{name}}}` [{0}], must be a subset of [{1}]"
|
||||||
|
.format(", ".join(map(str, set({{{name}}}) - set(allowed_values))),
|
||||||
|
", ".join(map(str, allowed_values)))
|
||||||
|
)
|
||||||
|
{{/isListContainer}}
|
||||||
|
{{#isMapContainer}}
|
||||||
|
if not set({{{name}}}.keys()).issubset(set(allowed_values)):
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid keys in `{{{name}}}` [{0}], must be a subset of [{1}]"
|
||||||
|
.format(", ".join(map(str, set({{{name}}}.keys()) - set(allowed_values))),
|
||||||
|
", ".join(map(str, allowed_values)))
|
||||||
|
)
|
||||||
|
{{/isMapContainer}}
|
||||||
|
{{/isContainer}}
|
||||||
|
{{^isContainer}}
|
||||||
|
if {{{name}}} not in allowed_values:
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid value for `{{{name}}}` ({0}), must be one of {1}"
|
||||||
|
.format({{{name}}}, allowed_values)
|
||||||
|
)
|
||||||
|
{{/isContainer}}
|
||||||
|
{{/isEnum}}
|
||||||
|
{{^isEnum}}
|
||||||
|
{{#required}}
|
||||||
|
if {{name}} is None:
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, must not be `None`")
|
||||||
|
{{/required}}
|
||||||
|
{{#hasValidation}}
|
||||||
|
{{#maxLength}}
|
||||||
|
if {{name}} is not None and len({{name}}) > {{maxLength}}:
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, length must be less than or equal to `{{maxLength}}`")
|
||||||
|
{{/maxLength}}
|
||||||
|
{{#minLength}}
|
||||||
|
if {{name}} is not None and len({{name}}) < {{minLength}}:
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, length must be greater than or equal to `{{minLength}}`")
|
||||||
|
{{/minLength}}
|
||||||
|
{{#maximum}}
|
||||||
|
if {{name}} is not None and {{name}} >{{#exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}:
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}`{{maximum}}`")
|
||||||
|
{{/maximum}}
|
||||||
|
{{#minimum}}
|
||||||
|
if {{name}} is not None and {{name}} <{{#exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}:
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}`{{minimum}}`")
|
||||||
|
{{/minimum}}
|
||||||
|
{{#pattern}}
|
||||||
|
if {{name}} is not None and not re.search(r'{{{vendorExtensions.x-regex}}}', {{name}}{{#vendorExtensions.x-modifiers}}{{#-first}}, flags={{/-first}}re.{{.}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}):
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, must be a follow pattern or equal to `{{{pattern}}}`")
|
||||||
|
{{/pattern}}
|
||||||
|
{{#maxItems}}
|
||||||
|
if {{name}} is not None and len({{name}}) > {{maxItems}}:
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, number of items must be less than or equal to `{{maxItems}}`")
|
||||||
|
{{/maxItems}}
|
||||||
|
{{#minItems}}
|
||||||
|
if {{name}} is not None and len({{name}}) < {{minItems}}:
|
||||||
|
raise ValueError("Invalid value for `{{name}}`, number of items must be greater than or equal to `{{minItems}}`")
|
||||||
|
{{/minItems}}
|
||||||
|
{{/hasValidation}}
|
||||||
|
{{/isEnum}}
|
||||||
|
|
||||||
|
self._{{name}} = {{name}}{{^-last}}
|
||||||
|
|
||||||
|
{{/-last}}
|
||||||
|
{{/vars}}
|
||||||
|
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
@ -0,0 +1,3 @@
|
|||||||
|
connexion[aiohttp,swagger-ui] == 2.0.2
|
||||||
|
swagger-ui-bundle == 0.0.2
|
||||||
|
aiohttp_jinja2 == 1.1.0
|
@ -0,0 +1,59 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
{{#authMethods}}
|
||||||
|
{{#isOAuth}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(token: str) -> dict:
|
||||||
|
"""
|
||||||
|
Validate and decode token.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
'scope' or 'scopes' will be passed to scope validation function.
|
||||||
|
Should return None if token is invalid or does not allow access to called API.
|
||||||
|
"""
|
||||||
|
return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_scope_{{name}}(required_scopes: List[str], token_scopes: List[str]) -> bool:
|
||||||
|
""" Validate required scopes are included in token scope """
|
||||||
|
return set(required_scopes).issubset(set(token_scopes))
|
||||||
|
|
||||||
|
{{/isOAuth}}
|
||||||
|
{{#isApiKey}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(api_key: str, required_scopes: None) -> dict:
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from api_key.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
Should return None if api_key is invalid or does not allow access to called API.
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
{{/isApiKey}}
|
||||||
|
{{#isBasicBasic}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(username: str, password: str, required_scopes: None) -> dict:
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from basic auth.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
Should return None if auth is invalid or does not allow access to called API.
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
{{/isBasicBasic}}
|
||||||
|
{{#isBasicBearer}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(token: str) -> dict:
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from custom bearer token.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
Should return None if auth is invalid or does not allow access to called API.
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
{{/isBasicBearer}}
|
||||||
|
{{/authMethods}}
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
coverage>=4.0.3
|
||||||
|
pytest>=1.3.7
|
||||||
|
pluggy>=0.3.1
|
||||||
|
py>=1.4.31
|
||||||
|
randomize>=0.13
|
||||||
|
pytest-aiohttp>=0.3.0
|
@ -0,0 +1,130 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
import typing
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
T = typing.TypeVar('T')
|
||||||
|
Class = typing.Type[T]
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize(data: Union[dict, list, str], klass: Union[Class, str]) -> Union[dict, list, Class, int, float, str, bool, datetime.date, datetime.datetime]:
|
||||||
|
"""Deserializes dict, list, str into an object.
|
||||||
|
|
||||||
|
:param data: dict, list or str.
|
||||||
|
:param klass: class literal, or string of class name.
|
||||||
|
|
||||||
|
:return: object.
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if klass in (int, float, str, bool):
|
||||||
|
return _deserialize_primitive(data, klass)
|
||||||
|
elif klass == object:
|
||||||
|
return _deserialize_object(data)
|
||||||
|
elif klass == datetime.date:
|
||||||
|
return deserialize_date(data)
|
||||||
|
elif klass == datetime.datetime:
|
||||||
|
return deserialize_datetime(data)
|
||||||
|
elif type(klass) == typing.GenericMeta:
|
||||||
|
if klass.__extra__ == list:
|
||||||
|
return _deserialize_list(data, klass.__args__[0])
|
||||||
|
if klass.__extra__ == dict:
|
||||||
|
return _deserialize_dict(data, klass.__args__[1])
|
||||||
|
else:
|
||||||
|
return deserialize_model(data, klass)
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_primitive(data, klass: Class) -> Union[Class, int, float, str, bool]:
|
||||||
|
"""Deserializes to primitive type.
|
||||||
|
|
||||||
|
:param data: data to deserialize.
|
||||||
|
:param klass: class literal.
|
||||||
|
|
||||||
|
:return: int, float, str, bool.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
value = klass(data)
|
||||||
|
except (UnicodeEncodeError, TypeError):
|
||||||
|
value = data
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_object(value: T) -> T:
|
||||||
|
"""Return an original value.
|
||||||
|
|
||||||
|
:return: object.
|
||||||
|
"""
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_date(string: str) -> datetime.date:
|
||||||
|
"""Deserializes string to date.
|
||||||
|
|
||||||
|
:param string: str.
|
||||||
|
:return: date.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from dateutil.parser import parse
|
||||||
|
return parse(string).date()
|
||||||
|
except ImportError:
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_datetime(string: str) -> datetime.datetime:
|
||||||
|
"""Deserializes string to datetime.
|
||||||
|
|
||||||
|
The string should be in iso8601 datetime format.
|
||||||
|
|
||||||
|
:param string: str.
|
||||||
|
:return: datetime.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from dateutil.parser import parse
|
||||||
|
return parse(string)
|
||||||
|
except ImportError:
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_model(data: Union[dict, list], klass: T) -> T:
|
||||||
|
"""Deserializes list or dict to model.
|
||||||
|
|
||||||
|
:param data: dict, list.
|
||||||
|
:param klass: class literal.
|
||||||
|
:return: model object.
|
||||||
|
"""
|
||||||
|
instance = klass()
|
||||||
|
|
||||||
|
if not instance.openapi_types:
|
||||||
|
return data
|
||||||
|
|
||||||
|
if data is not None and isinstance(data, (list, dict)):
|
||||||
|
for attr, attr_type in instance.openapi_types.items():
|
||||||
|
attr_key = instance.attribute_map[attr]
|
||||||
|
if attr_key in data:
|
||||||
|
value = data[attr_key]
|
||||||
|
setattr(instance, attr, _deserialize(value, attr_type))
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_list(data: list, boxed_type) -> list:
|
||||||
|
"""Deserializes a list and its elements.
|
||||||
|
|
||||||
|
:param data: list to deserialize.
|
||||||
|
:param boxed_type: class literal.
|
||||||
|
|
||||||
|
:return: deserialized list.
|
||||||
|
"""
|
||||||
|
return [_deserialize(sub_data, boxed_type) for sub_data in data]
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_dict(data: dict, boxed_type) -> dict:
|
||||||
|
"""Deserializes a dict and its elements.
|
||||||
|
|
||||||
|
:param data: dict to deserialize.
|
||||||
|
:param boxed_type: class literal.
|
||||||
|
|
||||||
|
:return: deserialized dict.
|
||||||
|
"""
|
||||||
|
return {k: _deserialize(v, boxed_type) for k, v in data.items()}
|
@ -1,6 +1,7 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
import unittest
|
||||||
|
|
||||||
from flask import json
|
from flask import json
|
||||||
from six import BytesIO
|
from six import BytesIO
|
||||||
@ -14,6 +15,9 @@ class {{#operations}}Test{{classname}}(BaseTestCase):
|
|||||||
"""{{classname}} integration test stubs"""
|
"""{{classname}} integration test stubs"""
|
||||||
|
|
||||||
{{#operation}}
|
{{#operation}}
|
||||||
|
{{#vendorExtensions.x-skip-test}}
|
||||||
|
@unittest.skip("{{reason}}")
|
||||||
|
{{/vendorExtensions.x-skip-test}}
|
||||||
def test_{{operationId}}(self):
|
def test_{{operationId}}(self):
|
||||||
"""Test case for {{{operationId}}}
|
"""Test case for {{{operationId}}}
|
||||||
|
|
||||||
@ -25,18 +29,21 @@ class {{#operations}}Test{{classname}}(BaseTestCase):
|
|||||||
{{#queryParams}}
|
{{#queryParams}}
|
||||||
{{#-first}}query_string = [{{/-first}}{{^-first}} {{/-first}}('{{paramName}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}}
|
{{#-first}}query_string = [{{/-first}}{{^-first}} {{/-first}}('{{paramName}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}}
|
||||||
{{/queryParams}}
|
{{/queryParams}}
|
||||||
{{#headerParams}}
|
headers = { {{#vendorExtensions.x-prefered-produce}}
|
||||||
{{#-first}}headers = [{{/-first}}{{^-first}} {{/-first}}('{{paramName}}', {{{example}}}){{#hasMore}},{{/hasMore}}{{#-last}}]{{/-last}}
|
'Accept': '{{mediaType}}',{{/vendorExtensions.x-prefered-produce}}{{#vendorExtensions.x-prefered-consume}}
|
||||||
{{/headerParams}}
|
'Content-Type': '{{mediaType}}',{{/vendorExtensions.x-prefered-consume}}{{#headerParams}}
|
||||||
|
'{{paramName}}': {{{example}}},{{/headerParams}}{{#authMethods}}
|
||||||
|
{{#isOAuth}}'Authorization': 'Bearer special-key',{{/isOAuth}}{{#isApiKey}}'{{name}}': 'special-key',{{/isApiKey}}{{#isBasicBasic}}'Authorization': 'BasicZm9vOmJhcg==',{{/isBasicBasic}}{{#isBasicBearer}}'Authorization': 'Bearer special-key',{{/isBasicBearer}}{{/authMethods}}
|
||||||
|
}
|
||||||
{{#formParams}}
|
{{#formParams}}
|
||||||
{{#-first}}data = dict({{/-first}}{{^-first}} {{/-first}}{{paramName}}={{{example}}}{{#hasMore}},{{/hasMore}}{{#-last}}){{/-last}}
|
{{#-first}}data = dict({{/-first}}{{^-first}} {{/-first}}{{paramName}}={{{example}}}{{#hasMore}},{{/hasMore}}{{#-last}}){{/-last}}
|
||||||
{{/formParams}}
|
{{/formParams}}
|
||||||
response = self.client.open(
|
response = self.client.open(
|
||||||
'{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}},
|
'{{#contextPath}}{{{.}}}{{/contextPath}}{{{path}}}'{{#pathParams}}{{#-first}}.format({{/-first}}{{paramName}}={{{example}}}{{#hasMore}}, {{/hasMore}}{{^hasMore}}){{/hasMore}}{{/pathParams}},
|
||||||
method='{{httpMethod}}'{{#bodyParam}},
|
method='{{httpMethod}}',
|
||||||
|
headers=headers{{#bodyParam}},
|
||||||
data=json.dumps({{paramName}}){{^consumes}},
|
data=json.dumps({{paramName}}){{^consumes}},
|
||||||
content_type='application/json'{{/consumes}}{{/bodyParam}}{{#headerParams}}{{#-first}},
|
content_type='application/json'{{/consumes}}{{/bodyParam}}{{#formParams}}{{#-first}},
|
||||||
headers=headers{{/-first}}{{/headerParams}}{{#formParams}}{{#-first}},
|
|
||||||
data=data{{/-first}}{{/formParams}}{{#consumes}}{{#-first}},
|
data=data{{/-first}}{{/formParams}}{{#consumes}}{{#-first}},
|
||||||
content_type='{{{mediaType}}}'{{/-first}}{{/consumes}}{{#queryParams}}{{#-first}},
|
content_type='{{{mediaType}}}'{{/-first}}{{/consumes}}{{#queryParams}}{{#-first}},
|
||||||
query_string=query_string{{/-first}}{{/queryParams}})
|
query_string=query_string{{/-first}}{{/queryParams}})
|
||||||
@ -47,5 +54,4 @@ class {{#operations}}Test{{classname}}(BaseTestCase):
|
|||||||
{{/operations}}
|
{{/operations}}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import unittest
|
|
||||||
unittest.main()
|
unittest.main()
|
@ -6,6 +6,13 @@ from datetime import date, datetime # noqa: F401
|
|||||||
from typing import List, Dict # noqa: F401
|
from typing import List, Dict # noqa: F401
|
||||||
|
|
||||||
from {{modelPackage}}.base_model_ import Model
|
from {{modelPackage}}.base_model_ import Model
|
||||||
|
{{#models}}
|
||||||
|
{{#model}}
|
||||||
|
{{#pyImports}}
|
||||||
|
{{import}}
|
||||||
|
{{/pyImports}}
|
||||||
|
{{/model}}
|
||||||
|
{{/models}}
|
||||||
from {{packageName}} import util
|
from {{packageName}} import util
|
||||||
|
|
||||||
{{#imports}}
|
{{#imports}}
|
@ -0,0 +1 @@
|
|||||||
|
{{{openapi-yaml}}}
|
@ -0,0 +1 @@
|
|||||||
|
{{#isString}}str{{/isString}}{{#isInteger}}int{{/isInteger}}{{#isLong}}int{{/isLong}}{{#isFloat}}float{{/isFloat}}{{#isDouble}}float{{/isDouble}}{{#isByteArray}}str{{/isByteArray}}{{#isBinary}}str{{/isBinary}}{{#isBoolean}}bool{{/isBoolean}}{{#isDate}}str{{/isDate}}{{#isDateTime}}str{{/isDateTime}}
|
@ -1,4 +1,4 @@
|
|||||||
connexion == 2.0.0
|
connexion == 2.0.2
|
||||||
swagger-ui-bundle == 0.0.2
|
swagger-ui-bundle == 0.0.2
|
||||||
python_dateutil == 2.6.0
|
python_dateutil == 2.6.0
|
||||||
{{#supportPython2}}
|
{{#supportPython2}}
|
@ -0,0 +1,90 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
{{#authMethods}}
|
||||||
|
{{#isOAuth}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(token):
|
||||||
|
"""
|
||||||
|
Validate and decode token.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
'scope' or 'scopes' will be passed to scope validation function.
|
||||||
|
|
||||||
|
:param token Token provided by Authorization header
|
||||||
|
:type token: str
|
||||||
|
:return: Decoded token information or None if token is invalid
|
||||||
|
:rtype: dict | None
|
||||||
|
"""
|
||||||
|
return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_scope_{{name}}(required_scopes, token_scopes):
|
||||||
|
"""
|
||||||
|
Validate required scopes are included in token scope
|
||||||
|
|
||||||
|
:param required_scopes Required scope to access called API
|
||||||
|
:type required_scopes: List[str]
|
||||||
|
:param token_scopes Scope present in token
|
||||||
|
:type token_scopes: List[str]
|
||||||
|
:return: True if access to called API is allowed
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return set(required_scopes).issubset(set(token_scopes))
|
||||||
|
|
||||||
|
{{/isOAuth}}
|
||||||
|
{{#isApiKey}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(api_key, required_scopes):
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from api_key.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
|
||||||
|
:param api_key API key provided by Authorization header
|
||||||
|
:type api_key: str
|
||||||
|
:param required_scopes Always None. Used for other authentication method
|
||||||
|
:type required_scopes: None
|
||||||
|
:return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API
|
||||||
|
:rtype: dict | None
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
{{/isApiKey}}
|
||||||
|
{{#isBasicBasic}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(username, password, required_scopes):
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from basic auth.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
|
||||||
|
:param username login provided by Authorization header
|
||||||
|
:type username: str
|
||||||
|
:param password password provided by Authorization header
|
||||||
|
:type password: str
|
||||||
|
:param required_scopes Always None. Used for other authentication method
|
||||||
|
:type required_scopes: None
|
||||||
|
:return: Information attached to user or None if credentials are invalid or does not allow access to called API
|
||||||
|
:rtype: dict | None
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
{{/isBasicBasic}}
|
||||||
|
{{#isBasicBearer}}
|
||||||
|
|
||||||
|
def info_from_{{name}}(token):
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from custom bearer token.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
|
||||||
|
:param token Token provided by Authorization header
|
||||||
|
:type token: str
|
||||||
|
:return: Decoded token information or None if token is invalid
|
||||||
|
:rtype: dict | None
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
{{/isBasicBearer}}
|
||||||
|
{{/authMethods}}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = {{#supportPython2}}py27, {{/supportPython2}}py35
|
envlist = {{#supportPython2}}py27, {{/supportPython2}}py3
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps=-r{toxinidir}/requirements.txt
|
deps=-r{toxinidir}/requirements.txt
|
3
pom.xml
3
pom.xml
@ -1056,6 +1056,9 @@
|
|||||||
<module>samples/client/petstore/typescript-angular-v6-provided-in-root</module>
|
<module>samples/client/petstore/typescript-angular-v6-provided-in-root</module>
|
||||||
<!--<module>samples/client/petstore/bash</module>-->
|
<!--<module>samples/client/petstore/bash</module>-->
|
||||||
<module>samples/server/petstore/rust-server</module>
|
<module>samples/server/petstore/rust-server</module>
|
||||||
|
<module>samples/server/petstore/python-aiohttp</module>
|
||||||
|
<module>samples/server/petstore/python-flask</module>
|
||||||
|
<module>samples/server/petstore/python-flask-python2</module>
|
||||||
</modules>
|
</modules>
|
||||||
</profile>
|
</profile>
|
||||||
<!-- test with JDK8 in CircleCI -->
|
<!-- test with JDK8 in CircleCI -->
|
||||||
|
18
samples/server/petstore/python-aiohttp/Makefile
Normal file
18
samples/server/petstore/python-aiohttp/Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
REQUIREMENTS_OUT=requirements.txt.log
|
||||||
|
VENV=.venv
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(REQUIREMENTS_OUT)
|
||||||
|
rm -rf $(VENV)
|
||||||
|
rm -rf .pytest_cache
|
||||||
|
rm -rf .coverage
|
||||||
|
find . -name "*.py[oc]" -delete
|
||||||
|
find . -name "__pycache__" -delete
|
||||||
|
|
||||||
|
test: clean
|
||||||
|
bash ./test_python3.sh
|
||||||
|
|
||||||
|
test-all: clean
|
||||||
|
bash ./test_python3.sh
|
46
samples/server/petstore/python-aiohttp/README.md
Normal file
46
samples/server/petstore/python-aiohttp/README.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# OpenAPI generated server
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the
|
||||||
|
[OpenAPI-Spec](https://openapis.org) from a remote server, you can easily generate a server stub. This
|
||||||
|
is an example of building a OpenAPI-enabled aiohtpp server.
|
||||||
|
|
||||||
|
This example uses the [Connexion](https://github.com/zalando/connexion) library on top of aiohtpp.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
Python 3.5.2+
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
To run the server, please execute the following from the root directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
python3 -m openapi_server
|
||||||
|
```
|
||||||
|
|
||||||
|
and open your browser to here:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/v2/ui/
|
||||||
|
```
|
||||||
|
|
||||||
|
Your OpenAPI definition lives here:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/v2/openapi.json
|
||||||
|
```
|
||||||
|
|
||||||
|
To launch the integration tests, use pytest:
|
||||||
|
```
|
||||||
|
sudo pip install -r test-requirements.txt
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prevent file overriding
|
||||||
|
|
||||||
|
After first generation, add edited files to _.openapi-generator-ignore_ to prevent generator to overwrite them. Typically:
|
||||||
|
```
|
||||||
|
server/controllers/*
|
||||||
|
test/*
|
||||||
|
*.txt
|
||||||
|
```
|
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from . import main
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,114 @@
|
|||||||
|
from typing import List, Dict
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from openapi_server.models.api_response import ApiResponse
|
||||||
|
from openapi_server.models.pet import Pet
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
async def add_pet(request: web.Request, body) -> web.Response:
|
||||||
|
"""Add a new pet to the store
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param body: Pet object that needs to be added to the store
|
||||||
|
:type body: dict | bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = Pet.from_dict(body)
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_pet(request: web.Request, pet_id, api_key=None) -> web.Response:
|
||||||
|
"""Deletes a pet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param pet_id: Pet id to delete
|
||||||
|
:type pet_id: int
|
||||||
|
:param api_key:
|
||||||
|
:type api_key: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def find_pets_by_status(request: web.Request, status) -> web.Response:
|
||||||
|
"""Finds Pets by status
|
||||||
|
|
||||||
|
Multiple status values can be provided with comma separated strings
|
||||||
|
|
||||||
|
:param status: Status values that need to be considered for filter
|
||||||
|
:type status: List[str]
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def find_pets_by_tags(request: web.Request, tags) -> web.Response:
|
||||||
|
"""Finds Pets by tags
|
||||||
|
|
||||||
|
Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
|
||||||
|
|
||||||
|
:param tags: Tags to filter by
|
||||||
|
:type tags: List[str]
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_pet_by_id(request: web.Request, pet_id) -> web.Response:
|
||||||
|
"""Find pet by ID
|
||||||
|
|
||||||
|
Returns a single pet
|
||||||
|
|
||||||
|
:param pet_id: ID of pet to return
|
||||||
|
:type pet_id: int
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def update_pet(request: web.Request, body) -> web.Response:
|
||||||
|
"""Update an existing pet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param body: Pet object that needs to be added to the store
|
||||||
|
:type body: dict | bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = Pet.from_dict(body)
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def update_pet_with_form(request: web.Request, pet_id, name=None, status=None) -> web.Response:
|
||||||
|
"""Updates a pet in the store with form data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param pet_id: ID of pet that needs to be updated
|
||||||
|
:type pet_id: int
|
||||||
|
:param name: Updated name of the pet
|
||||||
|
:type name: str
|
||||||
|
:param status: Updated status of the pet
|
||||||
|
:type status: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def upload_file(request: web.Request, pet_id, additional_metadata=None, file=None) -> web.Response:
|
||||||
|
"""uploads an image
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param pet_id: ID of pet to update
|
||||||
|
:type pet_id: int
|
||||||
|
:param additional_metadata: Additional data to pass to server
|
||||||
|
:type additional_metadata: str
|
||||||
|
:param file: file to upload
|
||||||
|
:type file: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
@ -0,0 +1,29 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def info_from_api_key(api_key: str, required_scopes: None) -> dict:
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from api_key.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
Should return None if api_key is invalid or does not allow access to called API.
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
|
||||||
|
def info_from_petstore_auth(token: str) -> dict:
|
||||||
|
"""
|
||||||
|
Validate and decode token.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
'scope' or 'scopes' will be passed to scope validation function.
|
||||||
|
Should return None if token is invalid or does not allow access to called API.
|
||||||
|
"""
|
||||||
|
return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_scope_petstore_auth(required_scopes: List[str], token_scopes: List[str]) -> bool:
|
||||||
|
""" Validate required scopes are included in token scope """
|
||||||
|
return set(required_scopes).issubset(set(token_scopes))
|
||||||
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
|||||||
|
from typing import List, Dict
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from openapi_server.models.order import Order
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_order(request: web.Request, order_id) -> web.Response:
|
||||||
|
"""Delete purchase order by ID
|
||||||
|
|
||||||
|
For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
|
||||||
|
|
||||||
|
:param order_id: ID of the order that needs to be deleted
|
||||||
|
:type order_id: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_inventory(request: web.Request, ) -> web.Response:
|
||||||
|
"""Returns pet inventories by status
|
||||||
|
|
||||||
|
Returns a map of status codes to quantities
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_order_by_id(request: web.Request, order_id) -> web.Response:
|
||||||
|
"""Find purchase order by ID
|
||||||
|
|
||||||
|
For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
|
||||||
|
|
||||||
|
:param order_id: ID of pet that needs to be fetched
|
||||||
|
:type order_id: int
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def place_order(request: web.Request, body) -> web.Response:
|
||||||
|
"""Place an order for a pet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param body: order placed for purchasing the pet
|
||||||
|
:type body: dict | bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = Order.from_dict(body)
|
||||||
|
return web.Response(status=200)
|
@ -0,0 +1,107 @@
|
|||||||
|
from typing import List, Dict
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from openapi_server.models.user import User
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
async def create_user(request: web.Request, body) -> web.Response:
|
||||||
|
"""Create user
|
||||||
|
|
||||||
|
This can only be done by the logged in user.
|
||||||
|
|
||||||
|
:param body: Created user object
|
||||||
|
:type body: dict | bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = User.from_dict(body)
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_users_with_array_input(request: web.Request, body) -> web.Response:
|
||||||
|
"""Creates list of users with given input array
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param body: List of user object
|
||||||
|
:type body: list | bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = [User.from_dict(d) for d in body]
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def create_users_with_list_input(request: web.Request, body) -> web.Response:
|
||||||
|
"""Creates list of users with given input array
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param body: List of user object
|
||||||
|
:type body: list | bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = [User.from_dict(d) for d in body]
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def delete_user(request: web.Request, username) -> web.Response:
|
||||||
|
"""Delete user
|
||||||
|
|
||||||
|
This can only be done by the logged in user.
|
||||||
|
|
||||||
|
:param username: The name that needs to be deleted
|
||||||
|
:type username: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_user_by_name(request: web.Request, username) -> web.Response:
|
||||||
|
"""Get user by user name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param username: The name that needs to be fetched. Use user1 for testing.
|
||||||
|
:type username: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def login_user(request: web.Request, username, password) -> web.Response:
|
||||||
|
"""Logs user into the system
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:param username: The user name for login
|
||||||
|
:type username: str
|
||||||
|
:param password: The password for login in clear text
|
||||||
|
:type password: str
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def logout_user(request: web.Request, ) -> web.Response:
|
||||||
|
"""Logs out current logged in user session
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def update_user(request: web.Request, username, body) -> web.Response:
|
||||||
|
"""Updated user
|
||||||
|
|
||||||
|
This can only be done by the logged in user.
|
||||||
|
|
||||||
|
:param username: name that need to be deleted
|
||||||
|
:type username: str
|
||||||
|
:param body: Updated user object
|
||||||
|
:type body: dict | bytes
|
||||||
|
|
||||||
|
"""
|
||||||
|
body = User.from_dict(body)
|
||||||
|
return web.Response(status=200)
|
@ -0,0 +1,9 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
# import models into model package
|
||||||
|
from openapi_server.models.api_response import ApiResponse
|
||||||
|
from openapi_server.models.category import Category
|
||||||
|
from openapi_server.models.order import Order
|
||||||
|
from openapi_server.models.pet import Pet
|
||||||
|
from openapi_server.models.tag import Tag
|
||||||
|
from openapi_server.models.user import User
|
@ -0,0 +1,110 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from typing import List, Dict, Type
|
||||||
|
|
||||||
|
from openapi_server.models.base_model_ import Model
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
class ApiResponse(Model):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, code: int=None, type: str=None, message: str=None):
|
||||||
|
"""ApiResponse - a model defined in OpenAPI
|
||||||
|
|
||||||
|
:param code: The code of this ApiResponse.
|
||||||
|
:param type: The type of this ApiResponse.
|
||||||
|
:param message: The message of this ApiResponse.
|
||||||
|
"""
|
||||||
|
self.openapi_types = {
|
||||||
|
'code': int,
|
||||||
|
'type': str,
|
||||||
|
'message': str
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attribute_map = {
|
||||||
|
'code': 'code',
|
||||||
|
'type': 'type',
|
||||||
|
'message': 'message'
|
||||||
|
}
|
||||||
|
|
||||||
|
self._code = code
|
||||||
|
self._type = type
|
||||||
|
self._message = message
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dikt: dict) -> 'ApiResponse':
|
||||||
|
"""Returns the dict as a model
|
||||||
|
|
||||||
|
:param dikt: A dict.
|
||||||
|
:return: The ApiResponse of this ApiResponse.
|
||||||
|
"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def code(self):
|
||||||
|
"""Gets the code of this ApiResponse.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The code of this ApiResponse.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._code
|
||||||
|
|
||||||
|
@code.setter
|
||||||
|
def code(self, code):
|
||||||
|
"""Sets the code of this ApiResponse.
|
||||||
|
|
||||||
|
|
||||||
|
:param code: The code of this ApiResponse.
|
||||||
|
:type code: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._code = code
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""Gets the type of this ApiResponse.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The type of this ApiResponse.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@type.setter
|
||||||
|
def type(self, type):
|
||||||
|
"""Sets the type of this ApiResponse.
|
||||||
|
|
||||||
|
|
||||||
|
:param type: The type of this ApiResponse.
|
||||||
|
:type type: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._type = type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
"""Gets the message of this ApiResponse.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The message of this ApiResponse.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._message
|
||||||
|
|
||||||
|
@message.setter
|
||||||
|
def message(self, message):
|
||||||
|
"""Sets the message of this ApiResponse.
|
||||||
|
|
||||||
|
|
||||||
|
:param message: The message of this ApiResponse.
|
||||||
|
:type message: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._message = message
|
@ -0,0 +1,66 @@
|
|||||||
|
import pprint
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
T = typing.TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
|
class Model(object):
|
||||||
|
# openapiTypes: The key is attribute name and the
|
||||||
|
# value is attribute type.
|
||||||
|
openapi_types = {}
|
||||||
|
|
||||||
|
# attributeMap: The key is attribute name and the
|
||||||
|
# value is json key in definition.
|
||||||
|
attribute_map = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls: T, dikt: dict) -> T:
|
||||||
|
"""Returns the dict as a model"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict:
|
||||||
|
"""Returns the model properties as a dict
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for attr_key, json_key in self.attribute_map.items():
|
||||||
|
value = getattr(self, attr_key)
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
if isinstance(value, list):
|
||||||
|
result[json_key] = list(map(
|
||||||
|
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
||||||
|
value
|
||||||
|
))
|
||||||
|
elif hasattr(value, "to_dict"):
|
||||||
|
result[json_key] = value.to_dict()
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
result[json_key] = dict(map(
|
||||||
|
lambda item: (item[0], item[1].to_dict())
|
||||||
|
if hasattr(item[1], "to_dict") else item,
|
||||||
|
value.items()
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
result[json_key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def to_str(self) -> str:
|
||||||
|
"""Returns the string representation of the model
|
||||||
|
"""
|
||||||
|
return pprint.pformat(self.to_dict())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""For `print` and `pprint`"""
|
||||||
|
return self.to_str()
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Returns true if both objects are equal"""
|
||||||
|
return self.__dict__ == other.__dict__
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
"""Returns true if both objects are not equal"""
|
||||||
|
return not self == other
|
@ -0,0 +1,85 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from typing import List, Dict, Type
|
||||||
|
|
||||||
|
from openapi_server.models.base_model_ import Model
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
class Category(Model):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, id: int=None, name: str=None):
|
||||||
|
"""Category - a model defined in OpenAPI
|
||||||
|
|
||||||
|
:param id: The id of this Category.
|
||||||
|
:param name: The name of this Category.
|
||||||
|
"""
|
||||||
|
self.openapi_types = {
|
||||||
|
'id': int,
|
||||||
|
'name': str
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attribute_map = {
|
||||||
|
'id': 'id',
|
||||||
|
'name': 'name'
|
||||||
|
}
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dikt: dict) -> 'Category':
|
||||||
|
"""Returns the dict as a model
|
||||||
|
|
||||||
|
:param dikt: A dict.
|
||||||
|
:return: The Category of this Category.
|
||||||
|
"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""Gets the id of this Category.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The id of this Category.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@id.setter
|
||||||
|
def id(self, id):
|
||||||
|
"""Sets the id of this Category.
|
||||||
|
|
||||||
|
|
||||||
|
:param id: The id of this Category.
|
||||||
|
:type id: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Gets the name of this Category.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The name of this Category.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
"""Sets the name of this Category.
|
||||||
|
|
||||||
|
|
||||||
|
:param name: The name of this Category.
|
||||||
|
:type name: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._name = name
|
@ -0,0 +1,193 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from typing import List, Dict, Type
|
||||||
|
|
||||||
|
from openapi_server.models.base_model_ import Model
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
class Order(Model):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, id: int=None, pet_id: int=None, quantity: int=None, ship_date: datetime=None, status: str=None, complete: bool=False):
|
||||||
|
"""Order - a model defined in OpenAPI
|
||||||
|
|
||||||
|
:param id: The id of this Order.
|
||||||
|
:param pet_id: The pet_id of this Order.
|
||||||
|
:param quantity: The quantity of this Order.
|
||||||
|
:param ship_date: The ship_date of this Order.
|
||||||
|
:param status: The status of this Order.
|
||||||
|
:param complete: The complete of this Order.
|
||||||
|
"""
|
||||||
|
self.openapi_types = {
|
||||||
|
'id': int,
|
||||||
|
'pet_id': int,
|
||||||
|
'quantity': int,
|
||||||
|
'ship_date': datetime,
|
||||||
|
'status': str,
|
||||||
|
'complete': bool
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attribute_map = {
|
||||||
|
'id': 'id',
|
||||||
|
'pet_id': 'petId',
|
||||||
|
'quantity': 'quantity',
|
||||||
|
'ship_date': 'shipDate',
|
||||||
|
'status': 'status',
|
||||||
|
'complete': 'complete'
|
||||||
|
}
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
self._pet_id = pet_id
|
||||||
|
self._quantity = quantity
|
||||||
|
self._ship_date = ship_date
|
||||||
|
self._status = status
|
||||||
|
self._complete = complete
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dikt: dict) -> 'Order':
|
||||||
|
"""Returns the dict as a model
|
||||||
|
|
||||||
|
:param dikt: A dict.
|
||||||
|
:return: The Order of this Order.
|
||||||
|
"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""Gets the id of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The id of this Order.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@id.setter
|
||||||
|
def id(self, id):
|
||||||
|
"""Sets the id of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:param id: The id of this Order.
|
||||||
|
:type id: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pet_id(self):
|
||||||
|
"""Gets the pet_id of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The pet_id of this Order.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._pet_id
|
||||||
|
|
||||||
|
@pet_id.setter
|
||||||
|
def pet_id(self, pet_id):
|
||||||
|
"""Sets the pet_id of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:param pet_id: The pet_id of this Order.
|
||||||
|
:type pet_id: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._pet_id = pet_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def quantity(self):
|
||||||
|
"""Gets the quantity of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The quantity of this Order.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._quantity
|
||||||
|
|
||||||
|
@quantity.setter
|
||||||
|
def quantity(self, quantity):
|
||||||
|
"""Sets the quantity of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:param quantity: The quantity of this Order.
|
||||||
|
:type quantity: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._quantity = quantity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ship_date(self):
|
||||||
|
"""Gets the ship_date of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The ship_date of this Order.
|
||||||
|
:rtype: datetime
|
||||||
|
"""
|
||||||
|
return self._ship_date
|
||||||
|
|
||||||
|
@ship_date.setter
|
||||||
|
def ship_date(self, ship_date):
|
||||||
|
"""Sets the ship_date of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:param ship_date: The ship_date of this Order.
|
||||||
|
:type ship_date: datetime
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._ship_date = ship_date
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status(self):
|
||||||
|
"""Gets the status of this Order.
|
||||||
|
|
||||||
|
Order Status
|
||||||
|
|
||||||
|
:return: The status of this Order.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._status
|
||||||
|
|
||||||
|
@status.setter
|
||||||
|
def status(self, status):
|
||||||
|
"""Sets the status of this Order.
|
||||||
|
|
||||||
|
Order Status
|
||||||
|
|
||||||
|
:param status: The status of this Order.
|
||||||
|
:type status: str
|
||||||
|
"""
|
||||||
|
allowed_values = ["placed", "approved", "delivered"]
|
||||||
|
if status not in allowed_values:
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid value for `status` ({0}), must be one of {1}"
|
||||||
|
.format(status, allowed_values)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
@property
|
||||||
|
def complete(self):
|
||||||
|
"""Gets the complete of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The complete of this Order.
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._complete
|
||||||
|
|
||||||
|
@complete.setter
|
||||||
|
def complete(self, complete):
|
||||||
|
"""Sets the complete of this Order.
|
||||||
|
|
||||||
|
|
||||||
|
:param complete: The complete of this Order.
|
||||||
|
:type complete: bool
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._complete = complete
|
@ -0,0 +1,199 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from typing import List, Dict, Type
|
||||||
|
|
||||||
|
from openapi_server.models.base_model_ import Model
|
||||||
|
from openapi_server.models.category import Category
|
||||||
|
from openapi_server.models.tag import Tag
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
class Pet(Model):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, id: int=None, category: Category=None, name: str=None, photo_urls: List[str]=None, tags: List[Tag]=None, status: str=None):
|
||||||
|
"""Pet - a model defined in OpenAPI
|
||||||
|
|
||||||
|
:param id: The id of this Pet.
|
||||||
|
:param category: The category of this Pet.
|
||||||
|
:param name: The name of this Pet.
|
||||||
|
:param photo_urls: The photo_urls of this Pet.
|
||||||
|
:param tags: The tags of this Pet.
|
||||||
|
:param status: The status of this Pet.
|
||||||
|
"""
|
||||||
|
self.openapi_types = {
|
||||||
|
'id': int,
|
||||||
|
'category': Category,
|
||||||
|
'name': str,
|
||||||
|
'photo_urls': List[str],
|
||||||
|
'tags': List[Tag],
|
||||||
|
'status': str
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attribute_map = {
|
||||||
|
'id': 'id',
|
||||||
|
'category': 'category',
|
||||||
|
'name': 'name',
|
||||||
|
'photo_urls': 'photoUrls',
|
||||||
|
'tags': 'tags',
|
||||||
|
'status': 'status'
|
||||||
|
}
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
self._category = category
|
||||||
|
self._name = name
|
||||||
|
self._photo_urls = photo_urls
|
||||||
|
self._tags = tags
|
||||||
|
self._status = status
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dikt: dict) -> 'Pet':
|
||||||
|
"""Returns the dict as a model
|
||||||
|
|
||||||
|
:param dikt: A dict.
|
||||||
|
:return: The Pet of this Pet.
|
||||||
|
"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""Gets the id of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The id of this Pet.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@id.setter
|
||||||
|
def id(self, id):
|
||||||
|
"""Sets the id of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:param id: The id of this Pet.
|
||||||
|
:type id: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def category(self):
|
||||||
|
"""Gets the category of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The category of this Pet.
|
||||||
|
:rtype: Category
|
||||||
|
"""
|
||||||
|
return self._category
|
||||||
|
|
||||||
|
@category.setter
|
||||||
|
def category(self, category):
|
||||||
|
"""Sets the category of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:param category: The category of this Pet.
|
||||||
|
:type category: Category
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._category = category
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Gets the name of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The name of this Pet.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
"""Sets the name of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:param name: The name of this Pet.
|
||||||
|
:type name: str
|
||||||
|
"""
|
||||||
|
if name is None:
|
||||||
|
raise ValueError("Invalid value for `name`, must not be `None`")
|
||||||
|
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def photo_urls(self):
|
||||||
|
"""Gets the photo_urls of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The photo_urls of this Pet.
|
||||||
|
:rtype: List[str]
|
||||||
|
"""
|
||||||
|
return self._photo_urls
|
||||||
|
|
||||||
|
@photo_urls.setter
|
||||||
|
def photo_urls(self, photo_urls):
|
||||||
|
"""Sets the photo_urls of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:param photo_urls: The photo_urls of this Pet.
|
||||||
|
:type photo_urls: List[str]
|
||||||
|
"""
|
||||||
|
if photo_urls is None:
|
||||||
|
raise ValueError("Invalid value for `photo_urls`, must not be `None`")
|
||||||
|
|
||||||
|
self._photo_urls = photo_urls
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tags(self):
|
||||||
|
"""Gets the tags of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The tags of this Pet.
|
||||||
|
:rtype: List[Tag]
|
||||||
|
"""
|
||||||
|
return self._tags
|
||||||
|
|
||||||
|
@tags.setter
|
||||||
|
def tags(self, tags):
|
||||||
|
"""Sets the tags of this Pet.
|
||||||
|
|
||||||
|
|
||||||
|
:param tags: The tags of this Pet.
|
||||||
|
:type tags: List[Tag]
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._tags = tags
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status(self):
|
||||||
|
"""Gets the status of this Pet.
|
||||||
|
|
||||||
|
pet status in the store
|
||||||
|
|
||||||
|
:return: The status of this Pet.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._status
|
||||||
|
|
||||||
|
@status.setter
|
||||||
|
def status(self, status):
|
||||||
|
"""Sets the status of this Pet.
|
||||||
|
|
||||||
|
pet status in the store
|
||||||
|
|
||||||
|
:param status: The status of this Pet.
|
||||||
|
:type status: str
|
||||||
|
"""
|
||||||
|
allowed_values = ["available", "pending", "sold"]
|
||||||
|
if status not in allowed_values:
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid value for `status` ({0}), must be one of {1}"
|
||||||
|
.format(status, allowed_values)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._status = status
|
@ -0,0 +1,85 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from typing import List, Dict, Type
|
||||||
|
|
||||||
|
from openapi_server.models.base_model_ import Model
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
class Tag(Model):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, id: int=None, name: str=None):
|
||||||
|
"""Tag - a model defined in OpenAPI
|
||||||
|
|
||||||
|
:param id: The id of this Tag.
|
||||||
|
:param name: The name of this Tag.
|
||||||
|
"""
|
||||||
|
self.openapi_types = {
|
||||||
|
'id': int,
|
||||||
|
'name': str
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attribute_map = {
|
||||||
|
'id': 'id',
|
||||||
|
'name': 'name'
|
||||||
|
}
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dikt: dict) -> 'Tag':
|
||||||
|
"""Returns the dict as a model
|
||||||
|
|
||||||
|
:param dikt: A dict.
|
||||||
|
:return: The Tag of this Tag.
|
||||||
|
"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""Gets the id of this Tag.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The id of this Tag.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@id.setter
|
||||||
|
def id(self, id):
|
||||||
|
"""Sets the id of this Tag.
|
||||||
|
|
||||||
|
|
||||||
|
:param id: The id of this Tag.
|
||||||
|
:type id: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Gets the name of this Tag.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The name of this Tag.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name):
|
||||||
|
"""Sets the name of this Tag.
|
||||||
|
|
||||||
|
|
||||||
|
:param name: The name of this Tag.
|
||||||
|
:type name: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._name = name
|
@ -0,0 +1,237 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
|
||||||
|
from typing import List, Dict, Type
|
||||||
|
|
||||||
|
from openapi_server.models.base_model_ import Model
|
||||||
|
from openapi_server import util
|
||||||
|
|
||||||
|
|
||||||
|
class User(Model):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, id: int=None, username: str=None, first_name: str=None, last_name: str=None, email: str=None, password: str=None, phone: str=None, user_status: int=None):
|
||||||
|
"""User - a model defined in OpenAPI
|
||||||
|
|
||||||
|
:param id: The id of this User.
|
||||||
|
:param username: The username of this User.
|
||||||
|
:param first_name: The first_name of this User.
|
||||||
|
:param last_name: The last_name of this User.
|
||||||
|
:param email: The email of this User.
|
||||||
|
:param password: The password of this User.
|
||||||
|
:param phone: The phone of this User.
|
||||||
|
:param user_status: The user_status of this User.
|
||||||
|
"""
|
||||||
|
self.openapi_types = {
|
||||||
|
'id': int,
|
||||||
|
'username': str,
|
||||||
|
'first_name': str,
|
||||||
|
'last_name': str,
|
||||||
|
'email': str,
|
||||||
|
'password': str,
|
||||||
|
'phone': str,
|
||||||
|
'user_status': int
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attribute_map = {
|
||||||
|
'id': 'id',
|
||||||
|
'username': 'username',
|
||||||
|
'first_name': 'firstName',
|
||||||
|
'last_name': 'lastName',
|
||||||
|
'email': 'email',
|
||||||
|
'password': 'password',
|
||||||
|
'phone': 'phone',
|
||||||
|
'user_status': 'userStatus'
|
||||||
|
}
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
self._username = username
|
||||||
|
self._first_name = first_name
|
||||||
|
self._last_name = last_name
|
||||||
|
self._email = email
|
||||||
|
self._password = password
|
||||||
|
self._phone = phone
|
||||||
|
self._user_status = user_status
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dikt: dict) -> 'User':
|
||||||
|
"""Returns the dict as a model
|
||||||
|
|
||||||
|
:param dikt: A dict.
|
||||||
|
:return: The User of this User.
|
||||||
|
"""
|
||||||
|
return util.deserialize_model(dikt, cls)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""Gets the id of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The id of this User.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@id.setter
|
||||||
|
def id(self, id):
|
||||||
|
"""Sets the id of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:param id: The id of this User.
|
||||||
|
:type id: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def username(self):
|
||||||
|
"""Gets the username of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The username of this User.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._username
|
||||||
|
|
||||||
|
@username.setter
|
||||||
|
def username(self, username):
|
||||||
|
"""Sets the username of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:param username: The username of this User.
|
||||||
|
:type username: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._username = username
|
||||||
|
|
||||||
|
@property
|
||||||
|
def first_name(self):
|
||||||
|
"""Gets the first_name of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The first_name of this User.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._first_name
|
||||||
|
|
||||||
|
@first_name.setter
|
||||||
|
def first_name(self, first_name):
|
||||||
|
"""Sets the first_name of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:param first_name: The first_name of this User.
|
||||||
|
:type first_name: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._first_name = first_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_name(self):
|
||||||
|
"""Gets the last_name of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The last_name of this User.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._last_name
|
||||||
|
|
||||||
|
@last_name.setter
|
||||||
|
def last_name(self, last_name):
|
||||||
|
"""Sets the last_name of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:param last_name: The last_name of this User.
|
||||||
|
:type last_name: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._last_name = last_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def email(self):
|
||||||
|
"""Gets the email of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The email of this User.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._email
|
||||||
|
|
||||||
|
@email.setter
|
||||||
|
def email(self, email):
|
||||||
|
"""Sets the email of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:param email: The email of this User.
|
||||||
|
:type email: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._email = email
|
||||||
|
|
||||||
|
@property
|
||||||
|
def password(self):
|
||||||
|
"""Gets the password of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The password of this User.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._password
|
||||||
|
|
||||||
|
@password.setter
|
||||||
|
def password(self, password):
|
||||||
|
"""Sets the password of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:param password: The password of this User.
|
||||||
|
:type password: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._password = password
|
||||||
|
|
||||||
|
@property
|
||||||
|
def phone(self):
|
||||||
|
"""Gets the phone of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:return: The phone of this User.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return self._phone
|
||||||
|
|
||||||
|
@phone.setter
|
||||||
|
def phone(self, phone):
|
||||||
|
"""Sets the phone of this User.
|
||||||
|
|
||||||
|
|
||||||
|
:param phone: The phone of this User.
|
||||||
|
:type phone: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._phone = phone
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_status(self):
|
||||||
|
"""Gets the user_status of this User.
|
||||||
|
|
||||||
|
User Status
|
||||||
|
|
||||||
|
:return: The user_status of this User.
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
return self._user_status
|
||||||
|
|
||||||
|
@user_status.setter
|
||||||
|
def user_status(self, user_status):
|
||||||
|
"""Sets the user_status of this User.
|
||||||
|
|
||||||
|
User Status
|
||||||
|
|
||||||
|
:param user_status: The user_status of this User.
|
||||||
|
:type user_status: int
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._user_status = user_status
|
@ -0,0 +1,792 @@
|
|||||||
|
openapi: 3.0.1
|
||||||
|
info:
|
||||||
|
description: This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
|
license:
|
||||||
|
name: Apache-2.0
|
||||||
|
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
title: OpenAPI Petstore
|
||||||
|
version: 1.0.0
|
||||||
|
servers:
|
||||||
|
- url: http://petstore.swagger.io/v2
|
||||||
|
tags:
|
||||||
|
- description: Everything about your Pets
|
||||||
|
name: pet
|
||||||
|
- description: Access to Petstore orders
|
||||||
|
name: store
|
||||||
|
- description: Operations about user
|
||||||
|
name: user
|
||||||
|
paths:
|
||||||
|
/pet:
|
||||||
|
post:
|
||||||
|
operationId: add_pet
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
description: Pet object that needs to be added to the store
|
||||||
|
required: true
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
405:
|
||||||
|
content: {}
|
||||||
|
description: Invalid input
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- write:pets
|
||||||
|
- read:pets
|
||||||
|
summary: Add a new pet to the store
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
put:
|
||||||
|
operationId: update_pet
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
description: Pet object that needs to be added to the store
|
||||||
|
required: true
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid ID supplied
|
||||||
|
404:
|
||||||
|
content: {}
|
||||||
|
description: Pet not found
|
||||||
|
405:
|
||||||
|
content: {}
|
||||||
|
description: Validation exception
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- write:pets
|
||||||
|
- read:pets
|
||||||
|
summary: Update an existing pet
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
/pet/findByStatus:
|
||||||
|
get:
|
||||||
|
description: Multiple status values can be provided with comma separated strings
|
||||||
|
operationId: find_pets_by_status
|
||||||
|
parameters:
|
||||||
|
- description: Status values that need to be considered for filter
|
||||||
|
explode: false
|
||||||
|
in: query
|
||||||
|
name: status
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
default: available
|
||||||
|
enum:
|
||||||
|
- available
|
||||||
|
- pending
|
||||||
|
- sold
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
style: form
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
type: array
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
type: array
|
||||||
|
description: successful operation
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid status value
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- write:pets
|
||||||
|
- read:pets
|
||||||
|
summary: Finds Pets by status
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
/pet/findByTags:
|
||||||
|
get:
|
||||||
|
deprecated: true
|
||||||
|
description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
|
||||||
|
operationId: find_pets_by_tags
|
||||||
|
parameters:
|
||||||
|
- description: Tags to filter by
|
||||||
|
explode: false
|
||||||
|
in: query
|
||||||
|
name: tags
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
style: form
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
type: array
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
type: array
|
||||||
|
description: successful operation
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid tag value
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- write:pets
|
||||||
|
- read:pets
|
||||||
|
summary: Finds Pets by tags
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
/pet/{pet_id}:
|
||||||
|
delete:
|
||||||
|
operationId: delete_pet
|
||||||
|
parameters:
|
||||||
|
- in: header
|
||||||
|
name: api_key
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: Pet id to delete
|
||||||
|
in: path
|
||||||
|
name: pet_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid pet value
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- write:pets
|
||||||
|
- read:pets
|
||||||
|
summary: Deletes a pet
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
get:
|
||||||
|
description: Returns a single pet
|
||||||
|
operationId: get_pet_by_id
|
||||||
|
parameters:
|
||||||
|
- description: ID of pet to return
|
||||||
|
in: path
|
||||||
|
name: pet_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'
|
||||||
|
description: successful operation
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid ID supplied
|
||||||
|
404:
|
||||||
|
content: {}
|
||||||
|
description: Pet not found
|
||||||
|
security:
|
||||||
|
- api_key: []
|
||||||
|
summary: Find pet by ID
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
post:
|
||||||
|
operationId: update_pet_with_form
|
||||||
|
parameters:
|
||||||
|
- description: ID of pet that needs to be updated
|
||||||
|
in: path
|
||||||
|
name: pet_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/x-www-form-urlencoded:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Updated name of the pet
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
description: Updated status of the pet
|
||||||
|
type: string
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
405:
|
||||||
|
content: {}
|
||||||
|
description: Invalid input
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- write:pets
|
||||||
|
- read:pets
|
||||||
|
summary: Updates a pet in the store with form data
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
/pet/{pet_id}/uploadImage:
|
||||||
|
post:
|
||||||
|
operationId: upload_file
|
||||||
|
parameters:
|
||||||
|
- description: ID of pet to update
|
||||||
|
in: path
|
||||||
|
name: pet_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
properties:
|
||||||
|
additionalMetadata:
|
||||||
|
description: Additional data to pass to server
|
||||||
|
type: string
|
||||||
|
file:
|
||||||
|
description: file to upload
|
||||||
|
format: binary
|
||||||
|
type: string
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ApiResponse'
|
||||||
|
description: successful operation
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- write:pets
|
||||||
|
- read:pets
|
||||||
|
summary: uploads an image
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.pet_controller
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
/store/inventory:
|
||||||
|
get:
|
||||||
|
description: Returns a map of status codes to quantities
|
||||||
|
operationId: get_inventory
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
format: int32
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
description: successful operation
|
||||||
|
security:
|
||||||
|
- api_key: []
|
||||||
|
summary: Returns pet inventories by status
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.store_controller
|
||||||
|
/store/order:
|
||||||
|
post:
|
||||||
|
operationId: place_order
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Order'
|
||||||
|
description: order placed for purchasing the pet
|
||||||
|
required: true
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Order'
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Order'
|
||||||
|
description: successful operation
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid Order
|
||||||
|
summary: Place an order for a pet
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.store_controller
|
||||||
|
/store/order/{order_id}:
|
||||||
|
delete:
|
||||||
|
description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
|
||||||
|
operationId: delete_order
|
||||||
|
parameters:
|
||||||
|
- description: ID of the order that needs to be deleted
|
||||||
|
in: path
|
||||||
|
name: order_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid ID supplied
|
||||||
|
404:
|
||||||
|
content: {}
|
||||||
|
description: Order not found
|
||||||
|
summary: Delete purchase order by ID
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.store_controller
|
||||||
|
get:
|
||||||
|
description: For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions
|
||||||
|
operationId: get_order_by_id
|
||||||
|
parameters:
|
||||||
|
- description: ID of pet that needs to be fetched
|
||||||
|
in: path
|
||||||
|
name: order_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
format: int64
|
||||||
|
maximum: 5
|
||||||
|
minimum: 1
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Order'
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Order'
|
||||||
|
description: successful operation
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid ID supplied
|
||||||
|
404:
|
||||||
|
content: {}
|
||||||
|
description: Order not found
|
||||||
|
summary: Find purchase order by ID
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.store_controller
|
||||||
|
/user:
|
||||||
|
post:
|
||||||
|
description: This can only be done by the logged in user.
|
||||||
|
operationId: create_user
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
description: Created user object
|
||||||
|
required: true
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
content: {}
|
||||||
|
description: successful operation
|
||||||
|
summary: Create user
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
/user/createWithArray:
|
||||||
|
post:
|
||||||
|
operationId: create_users_with_array_input
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
type: array
|
||||||
|
description: List of user object
|
||||||
|
required: true
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
content: {}
|
||||||
|
description: successful operation
|
||||||
|
summary: Creates list of users with given input array
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
/user/createWithList:
|
||||||
|
post:
|
||||||
|
operationId: create_users_with_list_input
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
type: array
|
||||||
|
description: List of user object
|
||||||
|
required: true
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
content: {}
|
||||||
|
description: successful operation
|
||||||
|
summary: Creates list of users with given input array
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
/user/login:
|
||||||
|
get:
|
||||||
|
operationId: login_user
|
||||||
|
parameters:
|
||||||
|
- description: The user name for login
|
||||||
|
in: query
|
||||||
|
name: username
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: The password for login in clear text
|
||||||
|
in: query
|
||||||
|
name: password
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: successful operation
|
||||||
|
headers:
|
||||||
|
X-Rate-Limit:
|
||||||
|
description: calls per hour allowed by the user
|
||||||
|
schema:
|
||||||
|
format: int32
|
||||||
|
type: integer
|
||||||
|
X-Expires-After:
|
||||||
|
description: date in UTC when toekn expires
|
||||||
|
schema:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid username/password supplied
|
||||||
|
summary: Logs user into the system
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
/user/logout:
|
||||||
|
get:
|
||||||
|
operationId: logout_user
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
content: {}
|
||||||
|
description: successful operation
|
||||||
|
summary: Logs out current logged in user session
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
/user/{username}:
|
||||||
|
delete:
|
||||||
|
description: This can only be done by the logged in user.
|
||||||
|
operationId: delete_user
|
||||||
|
parameters:
|
||||||
|
- description: The name that needs to be deleted
|
||||||
|
in: path
|
||||||
|
name: username
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid username supplied
|
||||||
|
404:
|
||||||
|
content: {}
|
||||||
|
description: User not found
|
||||||
|
summary: Delete user
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
get:
|
||||||
|
operationId: get_user_by_name
|
||||||
|
parameters:
|
||||||
|
- description: The name that needs to be fetched. Use user1 for testing.
|
||||||
|
in: path
|
||||||
|
name: username
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
content:
|
||||||
|
application/xml:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
description: successful operation
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid username supplied
|
||||||
|
404:
|
||||||
|
content: {}
|
||||||
|
description: User not found
|
||||||
|
summary: Get user by user name
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
put:
|
||||||
|
description: This can only be done by the logged in user.
|
||||||
|
operationId: update_user
|
||||||
|
parameters:
|
||||||
|
- description: name that need to be deleted
|
||||||
|
in: path
|
||||||
|
name: username
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
'*/*':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/User'
|
||||||
|
description: Updated user object
|
||||||
|
required: true
|
||||||
|
x-body-name: body
|
||||||
|
responses:
|
||||||
|
400:
|
||||||
|
content: {}
|
||||||
|
description: Invalid user supplied
|
||||||
|
404:
|
||||||
|
content: {}
|
||||||
|
description: User not found
|
||||||
|
summary: Updated user
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
x-codegen-request-body-name: body
|
||||||
|
x-openapi-router-controller: openapi_server.controllers.user_controller
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
Order:
|
||||||
|
description: An order for a pets from the pet store
|
||||||
|
example:
|
||||||
|
petId: 6
|
||||||
|
quantity: 1
|
||||||
|
id: 0
|
||||||
|
shipDate: 2000-01-23T04:56:07.000+00:00
|
||||||
|
complete: false
|
||||||
|
status: placed
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
petId:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
quantity:
|
||||||
|
format: int32
|
||||||
|
type: integer
|
||||||
|
shipDate:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
description: Order Status
|
||||||
|
enum:
|
||||||
|
- placed
|
||||||
|
- approved
|
||||||
|
- delivered
|
||||||
|
type: string
|
||||||
|
complete:
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
title: Pet Order
|
||||||
|
type: object
|
||||||
|
xml:
|
||||||
|
name: Order
|
||||||
|
Category:
|
||||||
|
description: A category for a pet
|
||||||
|
example:
|
||||||
|
name: name
|
||||||
|
id: 6
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
title: Pet category
|
||||||
|
type: object
|
||||||
|
xml:
|
||||||
|
name: Category
|
||||||
|
User:
|
||||||
|
description: A User who is purchasing from the pet store
|
||||||
|
example:
|
||||||
|
firstName: firstName
|
||||||
|
lastName: lastName
|
||||||
|
password: password
|
||||||
|
userStatus: 6
|
||||||
|
phone: phone
|
||||||
|
id: 0
|
||||||
|
email: email
|
||||||
|
username: username
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
firstName:
|
||||||
|
type: string
|
||||||
|
lastName:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
phone:
|
||||||
|
type: string
|
||||||
|
userStatus:
|
||||||
|
description: User Status
|
||||||
|
format: int32
|
||||||
|
type: integer
|
||||||
|
title: a User
|
||||||
|
type: object
|
||||||
|
xml:
|
||||||
|
name: User
|
||||||
|
Tag:
|
||||||
|
description: A tag for a pet
|
||||||
|
example:
|
||||||
|
name: name
|
||||||
|
id: 1
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
title: Pet Tag
|
||||||
|
type: object
|
||||||
|
xml:
|
||||||
|
name: Tag
|
||||||
|
Pet:
|
||||||
|
description: A pet for sale in the pet store
|
||||||
|
example:
|
||||||
|
photoUrls:
|
||||||
|
- photoUrls
|
||||||
|
- photoUrls
|
||||||
|
name: doggie
|
||||||
|
id: 0
|
||||||
|
category:
|
||||||
|
name: name
|
||||||
|
id: 6
|
||||||
|
tags:
|
||||||
|
- name: name
|
||||||
|
id: 1
|
||||||
|
- name: name
|
||||||
|
id: 1
|
||||||
|
status: available
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
category:
|
||||||
|
$ref: '#/components/schemas/Category'
|
||||||
|
name:
|
||||||
|
example: doggie
|
||||||
|
type: string
|
||||||
|
photoUrls:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
xml:
|
||||||
|
name: photoUrl
|
||||||
|
wrapped: true
|
||||||
|
tags:
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Tag'
|
||||||
|
type: array
|
||||||
|
xml:
|
||||||
|
name: tag
|
||||||
|
wrapped: true
|
||||||
|
status:
|
||||||
|
description: pet status in the store
|
||||||
|
enum:
|
||||||
|
- available
|
||||||
|
- pending
|
||||||
|
- sold
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- photoUrls
|
||||||
|
title: a Pet
|
||||||
|
type: object
|
||||||
|
xml:
|
||||||
|
name: Pet
|
||||||
|
ApiResponse:
|
||||||
|
description: Describes the result of uploading an image resource
|
||||||
|
example:
|
||||||
|
code: 0
|
||||||
|
type: type
|
||||||
|
message: message
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
format: int32
|
||||||
|
type: integer
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
title: An uploaded response
|
||||||
|
type: object
|
||||||
|
securitySchemes:
|
||||||
|
petstore_auth:
|
||||||
|
flows:
|
||||||
|
implicit:
|
||||||
|
authorizationUrl: http://petstore.swagger.io/api/oauth/dialog
|
||||||
|
scopes:
|
||||||
|
write:pets: modify pets in your account
|
||||||
|
read:pets: read your pets
|
||||||
|
type: oauth2
|
||||||
|
x-tokenInfoFunc: openapi_server.controllers.security_controller_.info_from_petstore_auth
|
||||||
|
x-scopeValidateFunc: openapi_server.controllers.security_controller_.validate_scope_petstore_auth
|
||||||
|
api_key:
|
||||||
|
in: header
|
||||||
|
name: api_key
|
||||||
|
type: apiKey
|
||||||
|
x-apikeyInfoFunc: openapi_server.controllers.security_controller_.info_from_api_key
|
130
samples/server/petstore/python-aiohttp/openapi_server/util.py
Normal file
130
samples/server/petstore/python-aiohttp/openapi_server/util.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
import typing
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
T = typing.TypeVar('T')
|
||||||
|
Class = typing.Type[T]
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize(data: Union[dict, list, str], klass: Union[Class, str]) -> Union[dict, list, Class, int, float, str, bool, datetime.date, datetime.datetime]:
|
||||||
|
"""Deserializes dict, list, str into an object.
|
||||||
|
|
||||||
|
:param data: dict, list or str.
|
||||||
|
:param klass: class literal, or string of class name.
|
||||||
|
|
||||||
|
:return: object.
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if klass in (int, float, str, bool):
|
||||||
|
return _deserialize_primitive(data, klass)
|
||||||
|
elif klass == object:
|
||||||
|
return _deserialize_object(data)
|
||||||
|
elif klass == datetime.date:
|
||||||
|
return deserialize_date(data)
|
||||||
|
elif klass == datetime.datetime:
|
||||||
|
return deserialize_datetime(data)
|
||||||
|
elif type(klass) == typing.GenericMeta:
|
||||||
|
if klass.__extra__ == list:
|
||||||
|
return _deserialize_list(data, klass.__args__[0])
|
||||||
|
if klass.__extra__ == dict:
|
||||||
|
return _deserialize_dict(data, klass.__args__[1])
|
||||||
|
else:
|
||||||
|
return deserialize_model(data, klass)
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_primitive(data, klass: Class) -> Union[Class, int, float, str, bool]:
|
||||||
|
"""Deserializes to primitive type.
|
||||||
|
|
||||||
|
:param data: data to deserialize.
|
||||||
|
:param klass: class literal.
|
||||||
|
|
||||||
|
:return: int, float, str, bool.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
value = klass(data)
|
||||||
|
except (UnicodeEncodeError, TypeError):
|
||||||
|
value = data
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_object(value: T) -> T:
|
||||||
|
"""Return an original value.
|
||||||
|
|
||||||
|
:return: object.
|
||||||
|
"""
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_date(string: str) -> datetime.date:
|
||||||
|
"""Deserializes string to date.
|
||||||
|
|
||||||
|
:param string: str.
|
||||||
|
:return: date.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from dateutil.parser import parse
|
||||||
|
return parse(string).date()
|
||||||
|
except ImportError:
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_datetime(string: str) -> datetime.datetime:
|
||||||
|
"""Deserializes string to datetime.
|
||||||
|
|
||||||
|
The string should be in iso8601 datetime format.
|
||||||
|
|
||||||
|
:param string: str.
|
||||||
|
:return: datetime.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from dateutil.parser import parse
|
||||||
|
return parse(string)
|
||||||
|
except ImportError:
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_model(data: Union[dict, list], klass: T) -> T:
|
||||||
|
"""Deserializes list or dict to model.
|
||||||
|
|
||||||
|
:param data: dict, list.
|
||||||
|
:param klass: class literal.
|
||||||
|
:return: model object.
|
||||||
|
"""
|
||||||
|
instance = klass()
|
||||||
|
|
||||||
|
if not instance.openapi_types:
|
||||||
|
return data
|
||||||
|
|
||||||
|
if data is not None and isinstance(data, (list, dict)):
|
||||||
|
for attr, attr_type in instance.openapi_types.items():
|
||||||
|
attr_key = instance.attribute_map[attr]
|
||||||
|
if attr_key in data:
|
||||||
|
value = data[attr_key]
|
||||||
|
setattr(instance, attr, _deserialize(value, attr_type))
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_list(data: list, boxed_type) -> list:
|
||||||
|
"""Deserializes a list and its elements.
|
||||||
|
|
||||||
|
:param data: list to deserialize.
|
||||||
|
:param boxed_type: class literal.
|
||||||
|
|
||||||
|
:return: deserialized list.
|
||||||
|
"""
|
||||||
|
return [_deserialize(sub_data, boxed_type) for sub_data in data]
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize_dict(data: dict, boxed_type) -> dict:
|
||||||
|
"""Deserializes a dict and its elements.
|
||||||
|
|
||||||
|
:param data: dict to deserialize.
|
||||||
|
:param boxed_type: class literal.
|
||||||
|
|
||||||
|
:return: deserialized dict.
|
||||||
|
"""
|
||||||
|
return {k: _deserialize(v, boxed_type) for k, v in data.items()}
|
46
samples/server/petstore/python-aiohttp/pom.xml
Normal file
46
samples/server/petstore/python-aiohttp/pom.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<project>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.openapitools</groupId>
|
||||||
|
<artifactId>PythonAiohttpServer</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<name>Python Aiohttp Server</name>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>1.2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>pytest-test</id>
|
||||||
|
<phase>integration-test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>exec</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<executable>make</executable>
|
||||||
|
<arguments>
|
||||||
|
<argument>test-all</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
3
samples/server/petstore/python-aiohttp/requirements.txt
Normal file
3
samples/server/petstore/python-aiohttp/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
connexion[aiohttp,swagger-ui] == 2.0.2
|
||||||
|
swagger-ui-bundle == 0.0.2
|
||||||
|
aiohttp_jinja2 == 1.1.0
|
@ -0,0 +1,6 @@
|
|||||||
|
coverage>=4.0.3
|
||||||
|
pytest>=1.3.7
|
||||||
|
pluggy>=0.3.1
|
||||||
|
py>=1.4.31
|
||||||
|
randomize>=0.13
|
||||||
|
pytest-aiohttp>=0.3.0
|
32
samples/server/petstore/python-aiohttp/test_python3.sh
Executable file
32
samples/server/petstore/python-aiohttp/test_python3.sh
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
REQUIREMENTS_FILE=requirements.txt
|
||||||
|
TEST_REQUIREMENTS_FILE=test-requirements.txt
|
||||||
|
REQUIREMENTS_OUT=requirements.txt.log
|
||||||
|
SETUP_OUT=*.egg-info
|
||||||
|
VENV=.venv
|
||||||
|
DEACTIVE=false
|
||||||
|
|
||||||
|
export LC_ALL=en_US.UTF-8
|
||||||
|
export LANG=en_US.UTF-8
|
||||||
|
|
||||||
|
### set virtualenv
|
||||||
|
if [ -z "$VIRTUAL_ENV" ]; then
|
||||||
|
virtualenv $VENV --no-site-packages --always-copy --python python3
|
||||||
|
source $VENV/bin/activate
|
||||||
|
DEACTIVE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
### install dependencies
|
||||||
|
pip install -r $REQUIREMENTS_FILE -r $TEST_REQUIREMENTS_FILE | tee -a $REQUIREMENTS_OUT
|
||||||
|
|
||||||
|
### run tests
|
||||||
|
pytest || exit 1
|
||||||
|
|
||||||
|
### static analysis of code
|
||||||
|
flake8 --show-source petstore_api/
|
||||||
|
|
||||||
|
### deactivate virtualenv
|
||||||
|
if [ $DEACTIVE == true ]; then
|
||||||
|
deactivate
|
||||||
|
fi
|
17
samples/server/petstore/python-aiohttp/tests/conftest.py
Normal file
17
samples/server/petstore/python-aiohttp/tests/conftest.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import logging
|
||||||
|
import pytest
|
||||||
|
import os
|
||||||
|
|
||||||
|
import connexion
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client(loop, aiohttp_client):
|
||||||
|
logging.getLogger('connexion.operation').setLevel('ERROR')
|
||||||
|
options = {
|
||||||
|
"swagger_ui": True
|
||||||
|
}
|
||||||
|
specification_dir = os.path.join(os.path.dirname(__file__), '..', 'openapi_server', 'openapi')
|
||||||
|
app = connexion.AioHttpApp(__name__, specification_dir=specification_dir, options=options)
|
||||||
|
app.add_api('openapi.yaml', pass_context_arg_name='request')
|
||||||
|
return loop.run_until_complete(aiohttp_client(app.app))
|
@ -0,0 +1,200 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import json
|
||||||
|
from aiohttp import web
|
||||||
|
from aiohttp import FormData
|
||||||
|
|
||||||
|
from openapi_server.models.api_response import ApiResponse
|
||||||
|
from openapi_server.models.pet import Pet
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760")
|
||||||
|
async def test_add_pet(client):
|
||||||
|
"""Test case for add_pet
|
||||||
|
|
||||||
|
Add a new pet to the store
|
||||||
|
"""
|
||||||
|
body = {
|
||||||
|
"photoUrls" : [ "photoUrls", "photoUrls" ],
|
||||||
|
"name" : "doggie",
|
||||||
|
"id" : 0,
|
||||||
|
"category" : {
|
||||||
|
"name" : "name",
|
||||||
|
"id" : 6
|
||||||
|
},
|
||||||
|
"tags" : [ {
|
||||||
|
"name" : "name",
|
||||||
|
"id" : 1
|
||||||
|
}, {
|
||||||
|
"name" : "name",
|
||||||
|
"id" : 1
|
||||||
|
} ],
|
||||||
|
"status" : "available"
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer special-key',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='POST',
|
||||||
|
path='/v2/pet',
|
||||||
|
headers=headers,
|
||||||
|
json=body,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_delete_pet(client):
|
||||||
|
"""Test case for delete_pet
|
||||||
|
|
||||||
|
Deletes a pet
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'api_key': 'api_key_example',
|
||||||
|
'Authorization': 'Bearer special-key',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='DELETE',
|
||||||
|
path='/v2/pet/{pet_id}'.format(pet_id=56),
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_find_pets_by_status(client):
|
||||||
|
"""Test case for find_pets_by_status
|
||||||
|
|
||||||
|
Finds Pets by status
|
||||||
|
"""
|
||||||
|
params = [('status', 'available')]
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': 'Bearer special-key',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/pet/findByStatus',
|
||||||
|
headers=headers,
|
||||||
|
params=params,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_find_pets_by_tags(client):
|
||||||
|
"""Test case for find_pets_by_tags
|
||||||
|
|
||||||
|
Finds Pets by tags
|
||||||
|
"""
|
||||||
|
params = [('tags', 'tags_example')]
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': 'Bearer special-key',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/pet/findByTags',
|
||||||
|
headers=headers,
|
||||||
|
params=params,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_pet_by_id(client):
|
||||||
|
"""Test case for get_pet_by_id
|
||||||
|
|
||||||
|
Find pet by ID
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'api_key': 'special-key',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/pet/{pet_id}'.format(pet_id=56),
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("Connexion does not support multiple consummes. See https://github.com/zalando/connexion/pull/760")
|
||||||
|
async def test_update_pet(client):
|
||||||
|
"""Test case for update_pet
|
||||||
|
|
||||||
|
Update an existing pet
|
||||||
|
"""
|
||||||
|
body = {
|
||||||
|
"photoUrls" : [ "photoUrls", "photoUrls" ],
|
||||||
|
"name" : "doggie",
|
||||||
|
"id" : 0,
|
||||||
|
"category" : {
|
||||||
|
"name" : "name",
|
||||||
|
"id" : 6
|
||||||
|
},
|
||||||
|
"tags" : [ {
|
||||||
|
"name" : "name",
|
||||||
|
"id" : 1
|
||||||
|
}, {
|
||||||
|
"name" : "name",
|
||||||
|
"id" : 1
|
||||||
|
} ],
|
||||||
|
"status" : "available"
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer special-key',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='PUT',
|
||||||
|
path='/v2/pet',
|
||||||
|
headers=headers,
|
||||||
|
json=body,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("application/x-www-form-urlencoded not supported by Connexion")
|
||||||
|
async def test_update_pet_with_form(client):
|
||||||
|
"""Test case for update_pet_with_form
|
||||||
|
|
||||||
|
Updates a pet in the store with form data
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'Authorization': 'Bearer special-key',
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
'name': 'name_example',
|
||||||
|
'status': 'status_example'
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='POST',
|
||||||
|
path='/v2/pet/{pet_id}'.format(pet_id=56),
|
||||||
|
headers=headers,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("multipart/form-data not supported by Connexion")
|
||||||
|
async def test_upload_file(client):
|
||||||
|
"""Test case for upload_file
|
||||||
|
|
||||||
|
uploads an image
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
'Authorization': 'Bearer special-key',
|
||||||
|
}
|
||||||
|
data = FormData()
|
||||||
|
data.add_field('additional_metadata', 'additional_metadata_example')
|
||||||
|
data.add_field('file', (BytesIO(b'some file data'), 'file.txt'))
|
||||||
|
response = await client.request(
|
||||||
|
method='POST',
|
||||||
|
path='/v2/pet/{pet_id}/uploadImage'.format(pet_id=56),
|
||||||
|
headers=headers,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
@ -0,0 +1,76 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import json
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from openapi_server.models.order import Order
|
||||||
|
|
||||||
|
|
||||||
|
async def test_delete_order(client):
|
||||||
|
"""Test case for delete_order
|
||||||
|
|
||||||
|
Delete purchase order by ID
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='DELETE',
|
||||||
|
path='/v2/store/order/{order_id}'.format(order_id='order_id_example'),
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_inventory(client):
|
||||||
|
"""Test case for get_inventory
|
||||||
|
|
||||||
|
Returns pet inventories by status
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'api_key': 'special-key',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/store/inventory',
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_order_by_id(client):
|
||||||
|
"""Test case for get_order_by_id
|
||||||
|
|
||||||
|
Find purchase order by ID
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/store/order/{order_id}'.format(order_id=5),
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760")
|
||||||
|
async def test_place_order(client):
|
||||||
|
"""Test case for place_order
|
||||||
|
|
||||||
|
Place an order for a pet
|
||||||
|
"""
|
||||||
|
body = {}
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='POST',
|
||||||
|
path='/v2/store/order',
|
||||||
|
headers=headers,
|
||||||
|
json=body,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
@ -0,0 +1,149 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import json
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from openapi_server.models.user import User
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760")
|
||||||
|
async def test_create_user(client):
|
||||||
|
"""Test case for create_user
|
||||||
|
|
||||||
|
Create user
|
||||||
|
"""
|
||||||
|
body = {}
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='POST',
|
||||||
|
path='/v2/user',
|
||||||
|
headers=headers,
|
||||||
|
json=body,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760")
|
||||||
|
async def test_create_users_with_array_input(client):
|
||||||
|
"""Test case for create_users_with_array_input
|
||||||
|
|
||||||
|
Creates list of users with given input array
|
||||||
|
"""
|
||||||
|
body = []
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='POST',
|
||||||
|
path='/v2/user/createWithArray',
|
||||||
|
headers=headers,
|
||||||
|
json=body,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760")
|
||||||
|
async def test_create_users_with_list_input(client):
|
||||||
|
"""Test case for create_users_with_list_input
|
||||||
|
|
||||||
|
Creates list of users with given input array
|
||||||
|
"""
|
||||||
|
body = []
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='POST',
|
||||||
|
path='/v2/user/createWithList',
|
||||||
|
headers=headers,
|
||||||
|
json=body,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_delete_user(client):
|
||||||
|
"""Test case for delete_user
|
||||||
|
|
||||||
|
Delete user
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='DELETE',
|
||||||
|
path='/v2/user/{username}'.format(username='username_example'),
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_user_by_name(client):
|
||||||
|
"""Test case for get_user_by_name
|
||||||
|
|
||||||
|
Get user by user name
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/user/{username}'.format(username='username_example'),
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_login_user(client):
|
||||||
|
"""Test case for login_user
|
||||||
|
|
||||||
|
Logs user into the system
|
||||||
|
"""
|
||||||
|
params = [('username', 'username_example'),
|
||||||
|
('password', 'password_example')]
|
||||||
|
headers = {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/user/login',
|
||||||
|
headers=headers,
|
||||||
|
params=params,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
async def test_logout_user(client):
|
||||||
|
"""Test case for logout_user
|
||||||
|
|
||||||
|
Logs out current logged in user session
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='GET',
|
||||||
|
path='/v2/user/logout',
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("*/* not supported by Connexion. Use application/json instead. See https://github.com/zalando/connexion/pull/760")
|
||||||
|
async def test_update_user(client):
|
||||||
|
"""Test case for update_user
|
||||||
|
|
||||||
|
Updated user
|
||||||
|
"""
|
||||||
|
body = {}
|
||||||
|
headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
response = await client.request(
|
||||||
|
method='PUT',
|
||||||
|
path='/v2/user/{username}'.format(username='username_example'),
|
||||||
|
headers=headers,
|
||||||
|
json=body,
|
||||||
|
)
|
||||||
|
assert response.status == 200, 'Response body is : ' + (await response.read()).decode('utf-8')
|
||||||
|
|
20
samples/server/petstore/python-flask-python2/Makefile
Normal file
20
samples/server/petstore/python-flask-python2/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
REQUIREMENTS_OUT=test-requirements.txt.log
|
||||||
|
SETUP_OUT=*.egg-info
|
||||||
|
VENV=.venv
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(REQUIREMENTS_OUT)
|
||||||
|
rm -rf $(SETUP_OUT)
|
||||||
|
rm -rf $(VENV)
|
||||||
|
rm -rf .tox
|
||||||
|
rm -rf .coverage
|
||||||
|
find . -name "*.py[oc]" -delete
|
||||||
|
find . -name "__pycache__" -delete
|
||||||
|
|
||||||
|
test: clean
|
||||||
|
bash ./test_python2.sh
|
||||||
|
|
||||||
|
test-all: clean
|
||||||
|
bash ./test_python2.sh
|
@ -0,0 +1,48 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def info_from_api_key(api_key, required_scopes):
|
||||||
|
"""
|
||||||
|
Check and retrieve authentication information from api_key.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
|
||||||
|
:param api_key API key provided by Authorization header
|
||||||
|
:type api_key: str
|
||||||
|
:param required_scopes Always None. Used for other authentication method
|
||||||
|
:type required_scopes: None
|
||||||
|
:return: Information attached to provided api_key or None if api_key is invalid or does not allow access to called API
|
||||||
|
:rtype: dict | None
|
||||||
|
"""
|
||||||
|
return {'uid': 'user_id'}
|
||||||
|
|
||||||
|
|
||||||
|
def info_from_petstore_auth(token):
|
||||||
|
"""
|
||||||
|
Validate and decode token.
|
||||||
|
Returned value will be passed in 'token_info' parameter of your operation function, if there is one.
|
||||||
|
'sub' or 'uid' will be set in 'user' parameter of your operation function, if there is one.
|
||||||
|
'scope' or 'scopes' will be passed to scope validation function.
|
||||||
|
|
||||||
|
:param token Token provided by Authorization header
|
||||||
|
:type token: str
|
||||||
|
:return: Decoded token information or None if token is invalid
|
||||||
|
:rtype: dict | None
|
||||||
|
"""
|
||||||
|
return {'scopes': ['read:pets', 'write:pets'], 'uid': 'user_id'}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_scope_petstore_auth(required_scopes, token_scopes):
|
||||||
|
"""
|
||||||
|
Validate required scopes are included in token scope
|
||||||
|
|
||||||
|
:param required_scopes Required scope to access called API
|
||||||
|
:type required_scopes: List[str]
|
||||||
|
:param token_scopes Scope present in token
|
||||||
|
:type token_scopes: List[str]
|
||||||
|
:return: True if access to called API is allowed
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return set(required_scopes).issubset(set(token_scopes))
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user