私が持っています:
私の意図は、flaskアプリ(すべての生成されたコード)が実際のREST apiおよびパラメーター解析をマッピングして、swaggerでコーディングされたAPI仕様に一致する)のみを処理することです。パラメータの解析(再度、生成されたコード)の後で、(生成されていない)バックエンドを直接呼び出す必要があります。
私の質問は、生成されたpython/flaskコードを手動で編集せずにこれらを接続するのに最適な方法ですか? (私のデザインへのフィードバック、またはこれを実現する正式なデザインパターンの詳細も素晴らしいです。私はこの分野に不慣れです)。
ジェネレータから新しく、python関数のようになります:
_def create_task(myTaskDefinition):
"""
comment as specified in swagger.json
:param myTaskDefinition: json blah blah blah
:type myTaskDefinition: dict | bytes
:rtype: ApiResponse
"""
if connexion.request.is_json:
myTaskDefinition = MyTaskTypeFromSwagger.from_dict(connexion.request.get_json())
return 'do some magic!' # swagger codegen inserts this string :)
_
バックエンドには私の実際のロジックがあります:
_def create_task_backend(myTaskDefinition):
# hand-coded, checked into git: do all the things
return APIResponse(...)
_
create_task()
を呼び出してcreate_task_backend()
を呼び出す正しい方法は何ですか?
もちろん、swagger仕様に重大な変更を加えた場合は、生成されていないコードを手動で更新する必要があります。ただし、APIを再生成する必要がある理由はたくさんあります(たとえば、MyTaskTypeFromSwagger
クラスを追加/修正するか、生成されたコードをgitにチェックインしないでください)。生成されたコードを手動で編集する必要がある場合APIコードの場合、これらの編集はすべて、再生成のたびに吹き飛ばされます。
もちろん、たとえば〜の簡単な文法でこれをスクリプト化することもできます。 pyparsing;しかし、この問題は初めてですが、すでに広く解決されているようです。
次のアプローチは私のために働きました:
3つのディレクトリを作成しました:
src
-私のコードの場合、src-gen
はswagger生成コードの場合、codegen
では、サーバーを生成するスクリプトといくつかのトリックを配置しました。すべてのテンプレート(swaggerビルドで使用可能)をcodegen/templates
にコピーし、controller.mustache
がsrc/server_impl
を参照するように編集して、独自のコードを使用できるようにしました。編集はテンプレート言語を使用しているため、汎用的です。それでも完璧ではありません(いくつかの命名規則を変更します)が、それでうまくいきます。したがって、最初にcontroller.mustache
に追加します。
from {{packageName}}.server_impl.controllers_impl import {{classname}}_impl
次に、return 'do some magic!'
の代わりに以下を追加します。
return {{classname}}_impl.{{operationId}}({{#allParams}}{{paramName}}{{^required}}=None{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
src
にはserver_impl
ディレクトリがあります。server_impl
をpythonモジュールとしてインポートできるように、シンボリックリンクを作成しますcd ../src-gen/swagger_server/
ln -s ../../src/server_impl/
cd ../../codegen
Java -jar swagger-codegen-cli.jar generate \
-i /path_to_your_swagger definition.yaml \
-l python-flask \
-o ../src-gen \
-t ./templates
cd ../src-gen/
python3 -m swagger_server
以前swagger-codegen
を使いたくて、同じ難問に遭遇しました。仕様を更新するまではすべて問題ありません。カスタムテンプレートを使用することもできますが、これは多くのオーバーヘッドとメンテナンスのように思えました。
結局 connexion を使用することになりました。これは、swagger仕様を使用してルーティング、マーシャリング、検証などを自動的に処理します。Connexionはフラスコ上に構築されているため、フレームワークの切り替えや何かについて心配する必要はありません。自動生成されたコードを維持する必要がなく、アプリケーションの一部がswaggerから自動的に処理されるという利点が得られます。
私がたどり着いたワークフロー。
アイデアは、コードを生成してから、swagger_server
パッケージをプロジェクトディレクトリに抽出することです。ただし、個別に、別のディレクトリまたは(私が行うように)プロジェクトルートでコーディングしているコントローラーを保持し、mergeを使用して、各世代の後にgit merge-files
を使用して生成したコントローラーを作成します。次に、注入swagger_server/controllers
に、つまりサーバーを起動する前に、新しいコントローラーコードを挿入する必要があります。
project
+-- swagger_server
| +-- controllers
| +-- controller.py <- this is generated
+-- controller.py <- this is you are typing your code in
+-- controller.py.common <- common ancestor, see below
+-- server.py <- your server code, if any
したがって、ワークフローは次のとおりです。
swagger_server
をプロジェクトディレクトリにコピーして、既存のものを完全に上書きしますcontroller.py
およびcontroller.py.common
をバックアップgit merge-file controller.py controller.py.common swagger_server/controllers/controller.py
swagger_server/controllers/controller.py
を新しい共通の祖先にして、controller.py.common
にコピーし、既存のものを上書きしますシェルスクリプトを使用して、これらすべてを自由に自動化してください。
#!/bin/bash
# Swagger generate server and client stub based on specification, them merge it into the project.
# Use carefully! Commit always before using this script!
# The following structure is assumed:
# .
# +-- my_client
# | +-- swagger_client
# +-- my_server
# | +-- swagger_server
# +-- merge.sh <- this script
read -p "Have you commited the project??? " -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo 'Commit first!'; exit 1; fi
rm -rf swagger-python-client
rm -rf swagger-python-server
Java -jar swagger-codegen-cli.jar generate -i swagger.yaml -l python -o swagger-python-client
Java -jar swagger-codegen-cli.jar generate -i swagger.yaml -l python-flask -o swagger-python-server
# Client - it's easy, just replace swagger_client package
rm -rf my_client/swagger_client
cp -rf swagger-python-client/swagger_client/ my_client
# Server - replace swagger_server package and merge with controllers
rm -rf my_server/.backup
mkdir -p my_server/.backup
cp -rf my_server/swagger_server my_server/.backup
rm -rf my_server/swagger_server
cp -rf swagger-python-server/swagger_server my_server
cd my_server/swagger_server/controllers/
files=$( ls * )
cd ../../..
for f in $files; do
# skip __init__.py
if [ -z "$flag" ]; then flag=1; continue; fi
echo "======== $f"
# initialization
cp -n my_server/swagger_server/controllers/$f my_server/$f.common
cp -n my_server/swagger_server/controllers/$f my_server/$f
# real merge
cp -f my_server/$f my_server/.backup/
cp -f my_server/$f.common my_server/.backup/
git merge-file my_server/$f my_server/$f.common my_server/swagger_server/controllers/$f
cp -f my_server/swagger_server/controllers/$f otmini-repo/$f.common
done
rm -rf swagger-python-client
rm -rf swagger-python-server
今のところ、これらの手順でビルドを行うことでこれに対処しています
'do some magic'
(生成されたすべてのコントローラーエンドポイントが返す文字列)を返す代わりに、「バックエンド」で対応する関数を呼び出すだけgit format-patch
を使用して、先行する変更のパッチを作成します。これにより、コードを再生成したときに、ビルドが自動的に変更を適用できるようになります。したがって、新しいエンドポイントを追加でき、バックエンドへの呼び出しを1回だけ手動でコーディングする必要があります。パッチファイルを使用する代わりに、生成されたコードのpy解析文法を記述し、解析された生成コードを使用してバックエンドへの呼び出しを作成することで、これを直接行うことができます。ハック。
これは最適とはほど遠いです。誰かが本当の解決策を提供してくれることを期待しているので、これを承認済みとしてマークするつもりはありません。
@MrNameが示唆するように connexion を使用します。
私は最初にこれをcodegenと一緒に使い始めました。
openapi-generator generate -i ../myapi.yaml -g python-flask -o .
これにより、openapiサーバーを含むディレクトリが生成されます。
|- openapi_server\
|--controllers\
|--mytag._controller.py\
|--openapi\
|--my-api.yaml\
API仕様のパスにタグを追加すると、タグごとに個別のtagname-controller.pyが作成されます。各operationIdについて、関数が生成されます。
ただし、これがセットアップされると、connexionはAPI仕様の更新を処理できます。 openapi/my-api.yamlに新しいパスを追加し、operationId = new_funcを指定すると、既存のコントローラーにnew_func()を追加できます。既存のサーバーロジックは失われません(ただし、念のためにバックアップしておきます)。既存のパスに根本的な変更を加えたことはまだありません。