Русский
Русский
English
Статистика
Реклама

Автоматическая документация для Flask с использованием OpenAPI

image alt


Техническая документация, как известно, крайне важная часть любого проекта. До недавнего времени мы прекрасно жили с таким генератором документаций как Sphinx. Но наступил момент переходить на технологии с бОльшим набором возможностей, поэтому мы приняли решение переписать нашу документацию на более современный стандарт: OpenAPI Specification. Эта статья является скромным гайдом по такому переезду. Она будет интересна Python-разработчикам, особенно тем, которые используют Flask. После ее прочтения вы узнаете, как создать статическую OpenAPI документацию для Flask приложения и развернуть ее в GitLab Pages.




apispec + marshmallow


В качестве веб-фреймворка у нас используется Flask. Документацию для API, созданного с помощью него, мы и хотим создать. Спецификация по стандарту OpenAPI описывается форматом YAML(или JSON). Чтобы преобразовать докстринги нашего API в необходимый формат, будем использовать такой инструмент, как apispec, и его плагины. Например, MarshmallowPlugin, с помощью которого (и самой библиотеки marshmallow) можно удобно за счет возможности наследования и переиспользования описать входные и выходные данные эндпоинтов в виде python классов, а также провалидировать их.

Используя библиотеку marshmallow, создадим класс, описывающий параметры API:

from marshmallow import Schema, fieldsclass InputSchema(Schema):   number = fields.Int(description="Число", required=True, example=5)   power = fields.Int(description="Степень", required=True, example=2)

Аналогично сделаем для выходных параметров:

class OutputSchema(Schema):   result = fields.Int(description="Результат", required=True, example=25)

Для группировки запросов в OpenAPI используются теги. Создадим тег и добавим его в объект APISpec:

def create_tags(spec):   """ Создаем теги.   :param spec: объект APISpec для сохранения тегов   """   tags = [{'name': 'math', 'description': 'Математические функции'}]   for tag in tags:       print(f"Добавляем тег: {tag['name']}")       spec.tag(tag)

Далее нам нужно интегрировать параметры в докстринг так, чтобы это соответствовало OpenAPI спецификации.

Пример:

from flask import Blueprint, current_app, json, requestblueprint_power = Blueprint(name="power", import_name=__name__)@blueprint_power.route('/power')def power():   """   ---   get:     summary: Возводит число в степень     parameters:       - in: query         schema: InputSchema     responses:       '200':         description: Результат возведения в степень         content:           application/json:             schema: OutputSchema       '400':         description: Не передан обязательный параметр         content:           application/json:             schema: ErrorSchema     tags:       - math   """   args = request.args   number = args.get('number')   if number is None:       return current_app.response_class(           response=json.dumps(               {'error': 'Не передан параметр number'}           ),           status=400,           mimetype='application/json'       )   power = args.get('power')   if power is None:       return current_app.response_class(           response=json.dumps(               {'error': 'Не передан параметр power'}           ),           status=400,           mimetype='application/json'       )   return current_app.response_class(       response=json.dumps(           {'response': int(number)**int(power)}       ),       status=200,       mimetype='application/json'   )

Эта функция пример реализации метода GET в нашем API.

Блок summary. Краткое описание функции. Для более подробного описания можно добавить блок description.

Блок parameters. Описание параметров запроса. У параметра указывается, откуда он берется:

  • path, для /power/{number}
  • query, для /power?number=5
  • header, для X-MyHeader: Value
  • cookie, для параметров переданных в cookie файле

и schema, в которую передается python класс, описывающий данный параметр.

Блок responses. Описание вариантов ответа команды и их структура.

Блок tags. Описание тегов, которые используются для логической группировки эндпоинтов.

Для POST запроса, например, можно указать еще requestBody, в котором описываются параметры, передаваемые в теле. Подробнее можно почитать в официальной документации.

После того, как мы описали методы API, можем загрузить их описание в объект APISpec:

def load_docstrings(spec, app):   """ Загружаем описание API.   :param spec: объект APISpec, куда загружаем описание функций   :param app: экземпляр Flask приложения, откуда берем описание функций   """   for fn_name in app.view_functions:       if fn_name == 'static':           continue       print(f'Загружаем описание для функции: {fn_name}')       view_fn = app.view_functions[fn_name]       spec.path(view=view_fn)

Создаем метод get_apispec, который будет возвращать объект APISpec, в нем добавляем общую информацию о проекте и вызываем описанные ранее методы load_docstrings и create_tags:

from apispec import APISpecfrom apispec.ext.marshmallow import MarshmallowPluginfrom apispec_webframeworks.flask import FlaskPlugindef get_apispec(app):   """ Формируем объект APISpec.   :param app: объект Flask приложения   """   spec = APISpec(       title="My App",       version="1.0.0",       openapi_version="3.0.3",       plugins=[FlaskPlugin(), MarshmallowPlugin()],   )   spec.components.schema("Input", schema=InputSchema)   spec.components.schema("Output", schema=OutputSchema)   spec.components.schema("Error", schema=ErrorSchema)   create_tags(spec)   load_docstrings(spec, app)   return spec


image alt


Swagger UI


Swagger UI позволяет создать интерактивную страницу с документацией.

Создадим эндпоинт, который будет возвращать спецификацию в json формате, и вызываем в нем get_apispec:

@app.route('/swagger')def create_swagger_spec():   return json.dumps(get_apispec(app).to_dict())

Теперь, когда мы получили json спецификацию, нам нужно сформировать из неё html документ. Для этого воспользуемся пакетом flask_swagger_ui, с помощью которого можно встроить интерактивную страницу с документацией на базе Swagger UI в наше Flask приложение:

from flask_swagger_ui import get_swaggerui_blueprintSWAGGER_URL = '/docs'API_URL = '/swagger'swagger_ui_blueprint = get_swaggerui_blueprint(   SWAGGER_URL,   API_URL,   config={       'app_name': 'My App'   })


Таким образом, мы сделали эндпоинт /docs, при обращении по которому получаем документацию следующего вида:

image alt


image alt

GitLab Pages + ReDoc



Если мы не хотим формировать документацию во время обращения по эндпоинту, то можно собрать статичный документ.

Использование GitLab позволяет сгенерировать такую статическую страницу с документацией в CI/CD процессах.

Таким образом, мы один раз соберем документацию, и при обращении по заданному адресу она будет просто отображаться без какой-либо дополнительной обработки.

Для этого сохраним APISpec в YAML файл:

DOCS_FILENAME = 'docs.yaml'def write_yaml_file(spec: APISpec):   """ Экспортируем объект APISpec в YAML файл.   :param spec: объект APISpec   """   with open(DOCS_FILENAME, 'w') as file:       file.write(spec.to_yaml())   print(f'Сохранили документацию в {DOCS_FILENAME})

Теперь, когда мы получили YAML файл по спецификации OpenAPI, нужно сформировать HTML документ. Для этого будем использовать ReDoc, так как он позволяет сгенерировать документ в gitlab-ci с красивой и удобной структурой. Публиковать его будем с помощью GitLab Pages.

Добавим следующие строки в файл gitlab-ci.yml:

pages: stage: docs image: alpine:latest script:   - apk add --update nodejs npm   - npm install -g redoc-cli   - redoc-cli bundle -o public/index.html docs.yaml artifacts:   paths:     - public

Стоит отметить, что index.html нужно сохранять в папку public, так как она зарезервирована GitLabом.

Теперь, если мы запушим изменения в репозиторий, по адресу namespace.gitlab.com/project появится документация:

image alt


Также путь до документации можно посмотреть в Settings/Pages

Пример документации с использованием ReDoc: ivi-ru.github.io/hydra

Заключение


Таким образом, мы научились собирать OpenAPI документацию с использованием ReDoc и хостить ее на GitLab Pages. В эту статью не попало еще несколько возможностей этих инструментов, например, валидация параметров с помощью marshmallow. Но основной ее целью было показать непосредственно процесс создания документации.

Полезные ссылки


Источник: habr.com
К списку статей
Опубликовано: 15.02.2021 18:22:10
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании онлайн-кинотеатр ivi

Python

Flask

Документация

Openapi

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru