Flask-RESTX で作成した API を Postman のコレクション形式でエクスポートする


Flask-RESTX には API を Postman のコレクションとしてエクスポートする機能がありますが、出力するのに苦労したので、調査の内容を記録しておきます。(出力したファイルを Postman にインポートするのにも変換が必要だったので、インポート手順もついでに記載します。)

Flask-RESTX で作成したアプリケーションは別記事で紹介しております。こちらのアプリケーションで作成した API を Postman のコレクションとして出力してみます。

Postman 用のコードを記載

公式の doc に Postman のコレクションとして出力するコードが紹介されていますが、コードをそのままコピーしてもエラーが出て動きません。。

This has to be executed when application context is available.とでて application contextで実行されていないのでエラーが発生しているっぽいです。

    return url_for(self.endpoint("root"), _scheme=self.url_scheme, _external=True)
  File "C:\Users\kiyo27\.pyenv\pyenv-win\versions\3.8.10\lib\site-packages\flask\helpers.py", line 274, in url_for 
    raise RuntimeError(
RuntimeError: Attempted to generate a URL without the application context being pushed. This has to be executed when application context is available.

ネットで調べてみるとこちらの issue が出てきました。with app.app_context(): を追加して実行してみます。

from flask import Flask, json
from apis import api

app = Flask(__name__)

def see_json():
    urlvars = False  # Build query strings in URLs
    swagger = True  # Export Swagger specifications
    data = api.as_postman(urlvars=urlvars, swagger=swagger)
    # print(json.dumps(data))
    f = open('postman_import.json', 'w')
    f.write(json.dumps(data))

with app.app_context():
    see_json()

url_for でエラーが起きたりします。

    return url_for(self.endpoint("root"), _scheme=self.url_scheme, _external=True)
  File "C:\Users\kiyo27\.pyenv\pyenv-win\versions\3.8.10\lib\site-packages\flask\helpers.py", line 300, in url_for 
    raise RuntimeError(
RuntimeError: Application was not able to create a URL adapter for request independent URL generation. You might be able to fix this by setting the SERVER_NAME config variable.

エラーが発生しているコードを調べると、

    def endpoint(self, name):
        if self.blueprint:
            return "{0}.{1}".format(self.blueprint.name, name)
        else:
            return name

...

    @property
    def base_path(self):
        """
        The API path

        :rtype: str
        """
        return url_for(self.endpoint("root"), _external=False)

うーーん、api を分割して記述している場合は、 blueprint を使って書き直す必要があるかもしれません。

色々調査して直したコードが書きになります。

postman.py
from flask import Flask, json, url_for, Blueprint
from flask_restx import Api
from apis import api
from apis.todos import api as todos

app = Flask(__name__)
app.config['SERVER_NAME'] = 'localhost'
blueprint = Blueprint('api', __name__, url_prefix='/api')
api = Api(blueprint, doc='/doc/')
api.add_namespace(todos, path='/todos')
app.register_blueprint(blueprint)

def see_json():
    # print(url_for('api.doc'))
    urlvars = False  # Build query strings in URLs
    swagger = True  # Export Swagger specifications
    data = api.as_postman(urlvars=urlvars, swagger=swagger)
    # print(json.dumps(data))
    f = open('postman_import.json', 'w')
    f.write(json.dumps(data))

with app.app_context():
    see_json()

api.todosapi は、

api/todos.py
from flask_restx import Namespace, Resource, fields

api = Namespace('todos', description='TODO operations')

こちらで実行したら、無事に出力されました。

Postman にインポートする

flask-restx で出力した postman のコレクションは v1 なので、インポートできませんでした。ですが幸いにも v1 から v2 に変換する方法が公式の doc に書いてありましたのでそちらを実行します。

Postman collection transformer をインストール:

sudo npm install -g postman-collection-transformer

v1 から v2 に変換:

postman-collection-transformer convert -i postman_import.json -o postman_v2.json -j 1.0.0 -p 2.0.0 -P

これで postman にインポートできるようなります。