如何从protobuf (.proto)文件生成swagger3 (OpenAPI3)规范(.json/.yaml)?

11

我的原始用例:

我正在使用gRPC服务器(使用protobuf)构建GO应用程序,并将其包装在HTTPS服务器(使用gin)中。仅HTTPS服务器被发布供客户使用(我的意思是,我的应用程序可以通过REST API访问,实际上然后拨号gRPC端点),并且我使用Swagger OpenAPI3(版本3是主要要求)规范进行发布。需要gRPC和HTTPS,任何解决方案都应符合这种架构。

我不想在两个位置维护我的服务器规范,也就是说,我不想维护proto文件(.proto)和swagger规范(.json/.yaml)。由于我必须编写proto文件来生成gRPC服务器,因此我希望自动化生成swagger规范(OpenAPI3)。

我的进展情况:

我能够使用grpc-gateway库从protobuf文件(.proto)生成swagger OpenAPI2规范,类似于grpc-rest-go-example。但是我的要求是OpenAPI3;更具体地说,我想使用OpenAPI3中的oneOf功能,并从proto的oneof特性进行映射。这在OpenAPI2中不可能,因为它不允许API具有多个类型定义的请求/响应正文,这是通过启用oneOf、anyOf和allOf结构在OpenAPI3中添加的一项功能。

在尝试这样做时,我偶然发现了GoogleAPIs的googleapis/gnostic库,它的描述是:

此存储库包含一个Go命令行工具,该工具可以将JSON和YAML OpenAPI描述转换为等效的Protocol Buffer表示形式,并从中间进行转换。

乍一看,这似乎完全解决了我的问题,但事实证明,此库仅在协议缓冲区(protobuf)二进制文件(.pb)和swagger OpenAPI2/OpenAPI3(.json/.yaml)文件之间进行互相转换,这使我遇到了新问题。

例如,对于以下.pb文件:


�3.0.1�…�
�Example service��Example service description*�
�Example contact2=

Apache 2.0�/http://www.apache.org/licenses/LICENSE-2.0.html:�1.0�!
�//localhost:9999/example/api/v1"â�
�
�/exampleResource��"���Example API��Example API description*�example-operation2B
@
example-query��query��example-query description �R�
    Ê��stringBÇ��œ�
�200�”�
‘�
�OK�Š�
C
�application/json�/
-�+
)#/components/schemas/common.StatusMessage
C
�application/yaml�/
-�+
)#/components/schemas/common.StatusMessage�¥�
�400���
š�
�Bad Request�Š�
C
�application/json�/
-�+
)#/components/schemas/common.StatusMessage
C
�application/yaml�/
-�+
)#/components/schemas/common.StatusMessage*Y
W
U
�common.StatusMessage�=
;Ê��objectú�/
�
�message��
    ��string
�
�status��
    ��string

它会生成以下Swagger文件:

openapi: 3.0.1
info:
  title: Example service
  description: Example service description
  contact:
    name: Example contact
  license:
    name: Apache 2.0
    url: http://www.apache.org/licenses/LICENSE-2.0.html
  version: "1.0"
servers:
- url: //localhost:9999/example/api/v1
paths:
  /exampleResource:
    get:
      summary: Example API
      description: Example API description
      operationId: example-operation
      parameters:
      - name: example-query
        in: query
        description: example-query description
        required: true
        schema:
          type: string
      responses:
        200:
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
            application/yaml:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
        400:
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
            application/yaml:
              schema:
                $ref: '#/components/schemas/common.StatusMessage'
components:
  schemas:
    common.StatusMessage:
      type: object
      properties:
        message:
          type: string
        status:
          type: string

如果.pb文件无法正常查看,请前往这里。因此可以像这样处理:

�status��
    ��string

看起来像:

<0x06>status<0x12><0x0b>
    Ê<0x01><0x06>string
对于上述例子,我先编写了Swagger规范,然后生成了.pb,但也可以反过来做同样的事情。

当前状态:

如果我有一种方法在(.pb)和(.proto)文件之间进行转换,那么转换循环将被关闭并完成(.proto-> .pb-> .json/.yaml-> .pb-> .proto)。

我确定必须有一种方法实现这一点,因此存在解决我的原始问题的解决方案。 但是我找不到任何可以实现它的文章或代码片段。 是否有理智的方法在.pb.proto文件之间互相转换?

如果您对我的原始用例有完全不同的解决方案,请随意分享。 这将非常有帮助。

提前致谢!

编辑:

(1)由于最近的评论,很明显首先在.pb.proto之间进行“转换”是一个荒谬的要求。 但是原始问题仍然相同,即如何从protobuf文件(.proto)生成swagger3(OpenAPI3)规范,无论是使用注释,标签还是其他方式。 相应地更改问题标题。

(2)就在我发布这篇文章的第二天,我遇到了gnostic-grpc存储库,其中的描述说:

此工具将OpenAPI v3.0 API描述转换为可以用于实现该API的gRPC服务的描述,并使用gRPC-JSON Transcoding。

再次,这让我非常兴奋。 实际上,这是一个GSOC项目,尽管这个存储库的想法如此惊人,但它不能满足要求。 此外,这不是一个互相转换的库,对于任何生产使用都非常不成熟。 实际上,它未能提供OpenAPI3规范的一些基本基本功能。

但是这个仓库朝着正确的方向前进。 我的结论是使用自定义插件完成这项工作,主要是通过扩展GO中的注释库来完成。

(3)显然没有很好的候选项可以从.proto转换为OpenAPI3规范(.yaml/.json),除了gnostic-grpc之外,该存储库非常不成熟并高度工作正在进行中,适用于任何实际用途。

但是对于相反的转换,即OpenAPI3规范(.yaml/.json)到.proto,有一个名为openapi-generator的良好库在OpenAPITools下,它将OpenAPI v2 / 3规范转换为几乎所有平台的客户端/服务器存根。 但由于这不是最初的要求,问题仍然悬而未决。


你根本不需要将protobuf有效负载转换为.proto格式;那没有意义。有两种protobuf格式:(a)二进制protobuf编码-这是你在问题中所称的.pb,和(b)一种具有见解的JSON变体,用JSON表达相同的意图; 在这个背景下(即.proto有完全不同的含义),你能澄清你想在这里做什么吗? - Marc Gravell
哦,好的!我的确切用例是编写proto文件->生成gRPC服务器->生成HTTPS服务器,并生成服务器的swagger规范。我知道使用grpc-gateway库(它使用.proto文件中的注释)可以实现openAPI2,但找不到任何关于OpenAPI3的内容。我想使用注释、标签或其他方式将.proto转换为OpenAPI3规范。 - Krishna Birla
grpc-gateway 已经有一个支持 openAPI v3 的问题开放了几年:https://github.com/grpc-ecosystem/grpc-gateway/issues/441 我也需要这个。也许我们可以一起合作解决它。由于它已经生成了 openAPIv2,所以似乎比从头开始编写要容易些。 - Clint
我很乐意处理它。目前我正在开发Python脚本来完成这种互转,当然我会将其开源,但更通用的解决方案在一个更易达的仓库中将会更加惊人,而且Python脚本只是一种解决方法。至于第二部分,我认为它肯定比从头开始编写要少些工作,但仍然需要大量努力,这很好!这是因为OpenAPIv3高度不兼容旧版本。 - Krishna Birla
你的问题有任何更新吗?我有完全相同的情况,我们目前使用的是openApiv2,希望能迁移到openApiv3。 - undefined
4个回答

1

尽管您的要求似乎是从PROTO获取YAML(OpenAPNv3)规范,但您可以查看此插件gnostic-grpc - 用于gnostic,它执行反向操作,即将YAML / JSON规范转换为PROTO以及gRPC服务调用。


是的,就在我发布这个问题的那天之后,我遇到了这个问题。尽管这个存储库的想法很棒,但它还没有准备好供使用。这是一个GSOC项目,存在着一些bug。而且它的转换是单向的,正如你所说的,并且不完整。它不支持大多数openAPI结构,而且它支持的那些也没有利用openAPI 3的特性。我得出的结论是编写一个插件是最好的解决方案,这并不难。除此之外,我决定为gnostic做出贡献,因为我喜欢这个想法。这是一个真正的用例,令人惊讶的是,竟然没有解决方案存在。 - Krishna Birla
绝对正确。事实上,我们一直在尝试使用它,但由于OpenAPI似乎不完整或部分支持而遇到了多种限制。但令人惊讶的是,除了这个实现之外,没有其他实现可以如此接近从YAML获取proto(特别是gRPC)。 - Anupama Deshmukh
我在 GitHub 上还有一个关于相同问题的开放式问题 - Krishna Birla
@AnupamaDeshmukh 我是gnostic-grpc的主要贡献者之一。您能详细说明哪些方面出了问题吗?请随意在存储库中开启问题。 - LorenzHW

0

我们未能找到将.proto文件直接转换为OpenAPI3/Swagger3的方法。这里有一个解决方法:

  • 通过protoc-gen-openapiv2.proto文件转换为Swagger2
  • 然后通过this link将Swagger2文件转换为Swagger3
    • Swagger Editor
    • Swagger Converter
    • Swagger Codegen版本3.x

示例

protoc --proto_path=${PROTO_PATH} --swagger_out=logtostderr=true:./swagger.json 

swagger-codegen generate -i ./swagger.json -l openapi-yaml -o swaggerv3.yaml

但是在这个翻译中,Swagger3的特定信息丢失了。 - Krishna Birla
@KrishnaBirla,你能否帮忙查明丢失的swagger3特定信息的细节?在我的情况下,转换后的swaggerv3文件可以很好地与工具“openapi-python-client”配合使用。 - zangw
@KrishnaBirla,您能否尝试另一个模块api-spec-converter?它可以将Swagger2.0转换为OpenAPI 3。使用命令api-spec-converter --from=swagger_2 --to=openapi_3 --syntax=yaml ./swagger2.json > openapi3.yaml进行转换。 - zangw

0

你需要稍微修改一下你的 "buf.gen.yaml" 文件:

version: v1
plugins:
  - plugin: go
    out: proto
    opt: paths=source_relative
  - plugin: go-grpc
    out: proto
    opt: paths=source_relative,require_unimplemented_servers=true
  - plugin: grpc-gateway
    out: proto
    opt: paths=source_relative
  - plugin: openapiv2
    out: proto

接下来,执行以下操作: buf mod update

然后,每当您使用buf generate时,它都会为您生成swagger.json文件。


0

protoc-gen-openapi似乎可用。它将.proto转换为OpenAPIv3 YAML,并支持oneOf等功能。请参阅examples获取使用示例。

为了方便起见,这里有一些代码片段:

安装

go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest

buf.yaml

version: v1
deps:
  - buf.build/googleapis/googleapis
  - buf.build/gnostic/gnostic

buf.gen.yaml

version: v1
plugins:
  - plugin: openapi
    out: .

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接