#json-schema #openapi #schema-file #schema-validation #template #codegen #process

app schematools-cli

用于代码生成、预处理和验证 json-schema 和 openapi 规范的工具

3 个版本

0.18.2 2024 年 6 月 3 日
0.18.1 2024 年 1 月 17 日
0.18.0 2023 年 12 月 17 日

#647 in 编码

MIT 许可证

395KB
11K SLoC

Schema Tools

build tests

简介

这只是另一种 openapi/jsonschema 代码生成器的方法。它是一个用 Rust 编写的家庭项目,一个简单的一站式控制台工具,具有以下功能:

  • openapi/json 模式验证
  • 模式预处理
    • 解除引用
    • merge-allof
    • 修补(应用/生成 json-patch)
    • 名称
    • merge-openapi
  • Tera (jinja2) 代码生成器,支持自定义模板

它旨在加快开发大量使用 json 对象的微服务(在 api 层面上作为事件以及 json 模式)。

与其他解决方案(如 openapi-generator)在方法上的主要区别

  • 更健壮的模板语言,如 jinja2
  • 将特定于语言的逻辑移动到模板中,包括类型映射、保留字
  • 无需区分器即可包装混合类型
  • 一个工具用于客户端、服务器、json-schema 队列消费者以及 openapi 的处理
  • 每个项目的构建中使用的相对较小的二进制文件
  • 按微服务方法执行代码生成(而不是作为独立的通用客户端库)
  • 支持 json-schema 注册表,待办事项:共享模型 - 创建一个模型以在客户端/服务器中共享结构,以避免映射相同的结构

一般规则

  • 所有命令都支持 yaml 和 json 文件。
  • 使用帮助以获取可用参数列表 schema-tools process --help
  • -v-vv-vvv-vvvv详细程度级别

验证

验证 openapi 规范

schematools validate openapi openapi.yaml

验证 json 模式定义

schematools validate openapi schema.yaml

这两个命令在失败的情况下都会返回非零退出码。错误报告不是很清晰,但它显示了json模式不符合的地方。待办事项:解决此问题 问题

过程

常见CLI参数

<file>                      Path to json/yaml file with openapi specification
-o, --output <output>       Returned format [default: json] [possible values: json,  yaml]
--to-file <to-file>         Path of output file, default output to stdout

命名

如果你的openapi规范遵循RESTFUL openapi规则,你可以创建缺失的json-schema标题或尝试重命名现有端点的operationId

schematools process name schema.yaml

其他选项

--overwrite                  Should overwrite existing titles
--overwrite-ambiguous         Should overwrite ambiguous titles
--resource-method-version    Reverts order of operationId generator to resource+method+version

引用

要替换openapi中所有出现的$ref,你可能需要输入

schematools process dereference schema.yaml

几乎从不执行openapi的完全引用。你可以使用以下选项部分引用模式

  • skip-references - 如果你在使用公共模式的注册表,则非常有用。在许多情况下,你不想引用这样的$ref
  • skip-root-internal-references - 非常有用,可以跳过/components.* openapi模式引用,这些引用通常存储在根openapi文件中。
  • create-internal-references - 节省空间,仅引用每个指针一次,后续出现都替换为第一个指针的引用
 --create-internal-references             Creates internal references if refs where pointing to same place
--skip-root-internal-references           Leaves internal references intact in root schema file
--skip-references <skip-references>...    List of hostnames to skip dereference

合并所有

allOf合并到对象类型中

schematools process merge-all-of openapi.yaml

在代码生成之前进行此类操作非常有用,考虑到json模式更多地代表验证而不是数据结构本身。在许多语言中,联合体是一个复杂的问题,但如果你使用allOf提取结构体的公共部分,这可能是非常有帮助的功能。

修补

如果你收到的openapi文件似乎损坏,你可以修复它并创建json-patch文件

schematools process patch <file> create <original-file> 

然后你可以在处理过程中将此类补丁应用于原始openapi文件

schematools process patch <file> apply <patch-file> 

合并openapi并升级

如果你的微服务分为多个服务(并且在同一入口下公开),创建一个openapi定义可能很有用

schematools process merge-openapi <file> --with <with>

可能需要的某些有用的选项,用于版本控制合并的openapi

--add-version <add-version>    Should add info.x-version- attribute to openapi specification
--retag <retag>                Should change tags of all endpoints of merged openapi

要升级合并的openapi版本,你可以使用此命令

schematools process bump-openapi <file> --original <previous-version-file>

它应该正确更改openapi版本,根据所有子openapi semversions。

代码生成openapi

代码生成本身是通过处理模板目录来执行的。在完成之前,必须提取和处理openapi/json-schema文件中的所有数据。有两种执行代码生成的方法

  • schematools codegen json-schema json-schema.json [...]用于处理json模式文件。如果json模式的标题缺失,则需要额外的属性--base-name <base-name>。一个json模式并不一定意味着此类代码生成运行的结果正好是一个结构体/对象/类 - 在复杂json模式的情况下,可能会有许多模型。
  • schematools codegen openapi openapi.json [...]用于处理openapi规范 - 提取端点和模型。

简单用法

schema-tools codegen openapi openapi.json --template templates/  --target-dir pkg/client/
  • openapi.json - openapi规范文件
  • --template templates/ - 存储jinja2文件的目录
  • --target-dir pkg/client/ - 应在此处生成代码

代码生成选项

  • --nested-arrays-as-models - 一些语言允许创建内联类型 Vec<HashMap<Vec<HashMap>>>> / [][][]int,对于这种情况可能需要创建包装类型
  • --optional-and-nullable-as-models - OpenAPI 允许创建两级的“可空性”,一些语言不区分 null 和 undefined。此选项将所有可空和可选字段包装在单独的类型中
  • --wrappers - 选项将混合类型(oneOf)包装为具有自定义反序列化逻辑的自定义对象
  • -o <options> - 选项将选项(字符串或 JSON)传递给所有模板文件,例如 -o 'name=ordersClient' -o 'usedEndpoints=["/orders", "/orders/{id}/items"]'
  • --format - 在代码生成后执行语言格式化器,例如 --format "gofmt -w"

代码生成模板

使用 --template templates/ 选项指定代码生成模板目录。所有来自此目录的简单文件都会复制到 --target-dir 中,除了 .j2 模板。其中最重要的 .j2 模板是它的头部(文件的第一行)。

models.j2 的示例

{# type=models,filename=models.go #}

/* options: {{ options | json_encode(pretty=true) }} */
/* models: {{ models | json_encode(pretty=true) }} */

endpoints.j2 的示例

{# type=endpoints,filename=endpoints.go #}

/* options: {{ options | json_encode(pretty=true) }} */
/* models: {{ endpoints | json_encode(pretty=true) }} */

头部 决定了如何处理模板文件,何时以及如何生成文件。头部选项

  • type=? - 可能的值:endpointsmodels
  • filename=? - 要创建的目标文件路径。可以与选项混合使用,例如 filename=clients/%options.name%/endpoints.go
  • if=foo:bar - 使用模板文件的条件。应与选项混合使用,例如 if=%options.type%:server

有关如何编写模板文件的更多信息,请参阅 Tera 文档。要获取我们创建的附加过滤器的列表,请访问 filters.rs

代码生成模板继承

代码生成允许定义多个 --template 选项。

schematools codegen openapi.json --template dir1/ --template2 dir2/ --target-dir output/

逐个加载所有目录的文件,并在发生冲突时覆盖它们。还有一个选项指向注册表,目前可能仅是 git 仓库

schematools codegen openapi.json --template REGISTRY::dir1/ --template2 dir2/ --target-dir output/

可用的代码生成模板

TODO:将代码生成模板推送到单独的仓库,并在那里编写示例

这就是这个工具的核心所在。它将所有现有功能整合在一起,并为openapi处理添加全局应用上下文。

schematools chain -vvvv \
   -c 'process merge-all-of --leave-invalid-properties https://domain.com/openapi/orders/api.yaml' \
   -c 'process name - --resource-method-version --overwrite' \
   -c 'validate openapi - --continue-on-error' \
   -c 'codegen openapi - \
        --template codegen/client/ \
        --format "gofmt -w" \
        --target-dir pkg/client/ \
        -o namespace=orders \
        -o clientName=OrdersClient'

所有命令都接受与单独执行时相同的参数。唯一的区别是,第一次执行必须以实际架构文件作为-f参数。后续执行应使用-来使用之前生成的架构文件。

schematools chain -vvvv \
   -c 'process merge-all-of --leave-invalid-properties specifications/api.yaml' \
   -c 'process name - --resource-method-version --overwrite' \
   -c 'validate openapi - ' 
   -c 'codegen openapi - \
        --template codegen/server/ \
        --format "gofmt -w" \
        --target-dir internal/http/ \
        -o namespace=myservice'

在链式操作过程中,可以使用output命令将处理后的架构导出到文件。

schematools chain -vvvv \
   -c 'process merge-all-of --leave-invalid-properties specifications/api.yaml' \
   -c 'process name - --resource-method-version --overwrite' \
   -c 'validate openapi - ' \
   -c 'output --to-file=test.json -o json' \
   -c 'codegen openapi - \
        --template codegen/server/ \
        --format "gofmt -w" \
        --target-dir internal/http/ \
        -o namespace=myservice'

注册表

可以选择将单独的git仓库作为模板的来源。

schematools chain -vvvv \
   -c 'registry add common git://github.com/kstasik/schema-tools --tag v0.0.1' \
   -c 'process merge-all-of --leave-invalid-properties clients/client1.yaml' \
   -c 'process name - --resource-method-version --overwrite' \
   -c 'validate openapi - --continue-on-error' \
   -c 'codegen openapi - --template common::resources/openapi/ --target-dir pkg/client1/ -o namespace=client1 -o clientName=Client1'

要针对此类注册表,只需使用:--template REGISTRY_NAME::path/

使用示例

此示例展示了具有两个外部openapi客户端依赖项的openapi http服务器。

schematools chain -vv \
  # 0. Register external repository with templates and fix it to tag
  -c 'registry add default https://codegen-templates/templates.git --tag v0.5.0' \
  # 1. Load local openapi specification from file and dereference
  -c 'process dereference spec/api.yaml --skip-root-internal-references --create-internal-references' \
  # 1. Convert allOf to structs
  -c 'process merge-all-of - --leave-invalid-properties' \
  # 1. Overwrite titles of schemas and operationIds of endpoints in openapi
  -c 'process name - --overwrite --resource-method-version' \
  # 1. Perform validation of our openapi specification - interrupt build on error
  -c 'validate openapi - ' \
  # 1. Create models and routers
  -c 'codegen openapi - --template default::rust-actix-server/ --format "rustfmt --edition 2018" --target-dir src/app/ -o name=ShippingApp'  \
  \
  # 2. Load remote openapi definition of external service
  -c 'process dereference https://schemas.com/openapi/orders/v0.1.0.json --skip-root-internal-references' \
  # 2. Convert allOf to structs
  -c 'process merge-all-of - --leave-invalid-properties' \
  # 2. Overwrite titles of schemas and operationIds of endpoints in openapi because it follow restful standards
  -c 'process name - --overwrite --resource-method-version' \
  # 2. Patch openapi specification because it has an error and we don't want to wait for a fix to be published by other project
  -c 'patch - apply specs/fixes/orders.yaml' \
  # 2. Validate openapi definition but continue on failure because it's an external client not owned by project
  -c 'validate openapi - --continue-on-error' \
  # 2. Create client
  -c "codegen openapi - --optional-and-nullable-as-models --template default::rust-reqwest-http/ --format 'rustfmt --edition 2018' \
    -o 'usedEndpoints=~[\"ordersListV3\",\"ordersCreateV3\"]' \
    --target-dir src/clients/ -o name=OrdersClient" \
  \
  # 3. Load remote openapi definition of external service
  -c 'process dereference https://schemas.com/openapi/users/v0.1.0.json --skip-root-internal-references' \
  # 3. Convert allOf to structs
  -c 'process merge-all-of - --leave-invalid-properties' \
  # 3. Overwrite titles of schemas and operationIds of endpoints in openapi because it follow restful standards
  -c 'process name - --overwrite --resource-method-version' \
  # 3. Validate openapi definition but continue on failure because it's an external client not owned by project
  -c 'validate openapi - --continue-on-error' \
  # 3. Create client
  -c "codegen openapi - --optional-and-nullable-as-models --template default::rust-reqwest-http/ --format 'rustfmt --edition 2018' \
    -o 'usedEndpoints=~[\"usersListV3\",\"usersCreateV3\"]' \
    --target-dir src/clients/ -o name=UsersClient"

依赖项

~20–31MB
~556K SLoC