Самый чистый способ склеивания созданного кода приложения Flask (Swagger-Codegen) для реализации бэкэнд

У меня есть:

  • библиотека, которая делает [материал]
  • определение API swagger, которое примерно равно # 1 с незначительными отличиями, чтобы четко отобразить службу REST
  • приложение с флягой, созданное с использованием Swagger-Codegen, например, приводит к функциям контроллера python примерно один к одному С# 1.

Мое намерение заключается в том, что приложение флеши (весь сгенерированный код) должно обрабатывать отображение только фактического REST api и синтаксического анализа параметров для соответствия спецификации API, закодированной в swagger. После любого синтаксического анализа параметров (опять же, сгенерированного кода) он должен обращаться непосредственно к моему (не созданному) бэкэнду.

Мой вопрос в том, как лучше всего подключить их с помощью ручного редактирования созданного кода 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 кода, то все эти изменения удаляются с каждым воссозданием.

Конечно, я мог бы script с помощью простой грамматики, например. Pyparsing; но пока это мой первый раз с этой проблемой, кажется, что она уже широко решена!

Ответ 1

У меня сработал следующий подход:

  • создал три каталога:

    • src - для моего кода,
    • src-gen для сгенерированного кода,
    • 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

Ответ 2

У меня возникло соблазн использовать swagger-codegen раньше и столкнулся с той же загадкой. Все нормально, пока вы не обновите спецификацию. Хотя вы можете использовать пользовательские шаблоны, это просто показалось большим количеством накладных расходов и обслуживания, когда все, что я хочу, это первый API проектирования.

В итоге я использовал connexion, который использует спецификацию swagger для автоматической обработки маршрутизации, маршалинга, проверки и т.д. Connexion is построенный на фляже, поэтому вам не нужно беспокоиться о переключении фреймворков или чего-то еще, вы просто получите преимущество от того, что части вашего приложения будут автоматически обрабатываться из swagger вместо того, чтобы поддерживать автоматически сгенерированный код.

Ответ 3

Пока я работаю над этим, выполняя сборку в этих шагах

  • запустите codegen
  • sed- script сгенерированный код для исправления тривиальных вещей, таких как пространства имен
  • вручную отредактируйте файлы, чтобы вместо возврата 'do some magic' (thats the string все возвращаемые конечные точки контроллера) они просто вызывают соответствующую функцию в моем "backend"
  • используйте git format-patch, чтобы сделать патч предыдущих изменений, так что, когда я снова сгенерировал код, сборка может автоматически применить изменения.

Таким образом, я могу добавить новые конечные точки, и мне нужно только вручную обработать вызовы на моем сервере ~ один раз. Вместо использования файлов патчей я мог бы сделать это напрямую, написав грамматику py-parsing для сгенерированного кода и используя обработанный сгенерированный код для создания вызовов на моем бэкэнд... это займет больше времени, поэтому я сделал это все как быстро взломать.

Это далеко не оптимально, я не буду отмечать это как принятое, поскольку я надеюсь, что кто-то предложит реальное решение.

Ответ 4

Рабочий процесс, к которому я пришел.

Идея состоит в том, чтобы сгенерировать код, а затем извлечь пакет swagger_server в каталог проекта. Но отдельно держите контроллеры, которые вы кодируете, в отдельном каталоге или (как я) в корне проекта и объединяйте их с сгенерированными после каждого поколения, используя 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 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
    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