繁体   English   中英

将生成的Flask应用程序代码(Swagger-Codegen)粘合到后端实现的最简洁方法

[英]cleanest way to glue generated Flask app code (Swagger-Codegen) to backend implementation

我有:

  1. 有[资料]的图书馆
  2. 醒目的API定义,大约是#1,有一些细微差别,可以清晰地映射到REST服务
  3. 一个使用Swagger-Codegen生成的#2烧瓶应用程序-例如,导致#1大致与Python控制器功能一对一。

我的意图是,flask应用程序(所有生成的代码)应仅处理实际REST api和参数解析的映射,以匹配以大方的方式编码的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()的正确方法?

当然,如果我对摇摇欲坠的规格进行了重大更改,则无论如何我都必须手动更新未生成的代码。 但是,出于多种原因,我可能想重新生成我的API(例如,添加/优化MyTaskTypeFromSwagger类,或者完全跳过git生成的代码),如果我必须手动编辑生成的API代码,则所有每次重新生成时,这些编辑都会被删除。

当然,我可以使用例如〜的简单语法编写脚本。 剖析 但是,尽管这是我第一次遇到此问题,但似乎已经被广泛解决了!

以下方法对我有用:

  • 创建了三个目录:

    • 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

以前我很想使用swagger-codegen并遇到相同的难题。 一切正常,直到您更新规格。 尽管您可以使用自定义模板,但是当我想要的只是设计优先的API时,这似乎需要大量的开销和维护。

我最终使用了connexion ,它使用了swagger规范来自动处理路由,封送处理,验证等。Connexion构建在flask上,因此您不必担心切换框架或其他任何事情,您将获得部分收益可以自动处理所有应用程序,而不必维护自动生成的代码。

现在,我正在按以下步骤进行构建,以解决此问题

  1. 运行代码
  2. sed-script生成的代码可修复诸如名称空间之类的琐碎内容
  3. 手动编辑文件,以使它们无需返回'do some magic' (即所有生成的控制器端点返回的字符串),而只需在我的“后端”中调用相应的函数
  4. 使用git format-patch对先前的更改进行修补,以便在我重新生成代码时,构建可以自动应用更改。

因此,我可以添加新的端点,并且只需要手动将调用编码到后端即可。 无需使用补丁文件,我可以直接通过为生成的代码编写py解析语法并使用解析的生成的代码来创建对我的后端的调用来直接执行此操作……这将花费更长的时间,因此我很快就做了所有这些骇客。

这远非最佳,我不会将其标记为已接受,因为我希望有人会提供真正的解决方案。

我参加的工作流程。

这个想法是生成代码,然后将swagger_server包解压缩到项目目录。 但是要分开使用,将您正在编写的控制器保留在单独的目录中,或者(如我所做的那样)在项目根目录中,并在每代之后使用git merge-files 它们与生成的控制器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.pycontroller.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 ,覆盖现有的

随意使用shell脚本自动化所有这些操作,即

#!/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

建议使用connexion作为@MrName。

我首先开始将其与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规范的更新。 如果我使用operationId = new_func将新路径添加到openapi / my-api.yaml,则可以将new_func()添加到现有控制器。 我不会丢失现有的服务器逻辑(但是为了以防万一,我还是会备份它)。 我还没有尝试对现有路径进行重大更改。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM