web-dev-qa-db-ja.com

生成されたFlaskアプリコード(Swagger-Codegen)をバックエンド実装に接着するための最もクリーンな方法

私が持っています:

  1. [スタッフ]を行うライブラリ
  2. swagger API定義。これは、大まかに#1ですが、RESTサービスにきれいにマッピングするための小さな違いがあります。
  3. a flaskアプリがSwagger-Codegenを使用して#2を生成-たとえば、結果はpythonコントローラー関数は#1とほぼ1対1になります。

私の意図は、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;しかし、この問題は初めてですが、すでに広く解決されているようです。

17
mike

次のアプローチは私のために働きました:

  • 3つのディレクトリを作成しました:

    • src-私のコードの場合、
    • src-genはswagger生成コードの場合、
    • codegenでは、サーバーを生成するスクリプトといくつかのトリックを配置しました。
  • すべてのテンプレート(swaggerビルドで使用可能)をcodegen/templatesにコピーし、controller.mustachesrc/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
7
Dudi

以前swagger-codegenを使いたくて、同じ難問に遭遇しました。仕様を更新するまではすべて問題ありません。カスタムテンプレートを使用することもできますが、これは多くのオーバーヘッドとメンテナンスのように思えました。

結局 connexion を使用することになりました。これは、swagger仕様を使用してルーティング、マーシャリング、検証などを自動的に処理します。Connexionはフラスコ上に構築されているため、フレームワークの切り替えや何かについて心配する必要はありません。自動生成されたコードを維持する必要がなく、アプリケーションの一部がswaggerから自動的に処理されるという利点が得られます。

5
MrName

私がたどり着いたワークフロー。

アイデアは、コードを生成してから、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

したがって、ワークフローは次のとおりです。

  1. コードを生成し、swagger_serverをプロジェクトディレクトリにコピーして、既存のものを完全に上書きします
  2. プロジェクトルートからcontroller.pyおよびcontroller.py.commonをバックアップ
  3. git merge-file controller.py controller.py.common swagger_server/controllers/controller.py
  4. 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

1

今のところ、これらの手順でビルドを行うことでこれに対処しています

  1. codegenを実行する
  2. 名前空間のような些細なことを修正するために生成されたコードをsed-script
  3. ファイルを手動で編集して、'do some magic'(生成されたすべてのコントローラーエンドポイントが返す文字列)を返す代わりに、「バックエンド」で対応する関数を呼び出すだけ
  4. git format-patchを使用して、先行する変更のパッチを作成します。これにより、コードを再生成したときに、ビルドが自動的に変更を適用できるようになります。

したがって、新しいエンドポイントを追加でき、バックエンドへの呼び出しを1回だけ手動でコーディングする必要があります。パッチファイルを使用する代わりに、生成されたコードのpy解析文法を記述し、解析された生成コードを使用してバックエンドへの呼び出しを作成することで、これを直接行うことができます。ハック。

これは最適とはほど遠いです。誰かが本当の解決策を提供してくれることを期待しているので、これを承認済みとしてマークするつもりはありません。

1
mike

@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()を追加できます。既存のサーバーロジックは失われません(ただし、念のためにバックアップしておきます)。既存のパスに根本的な変更を加えたことはまだありません。

0
intotecho