Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate swagger3 (OpenAPI3) spec in (.json/.yaml) from protobuf (.proto) files?

My original use-case:

I am building an application in GO with a gRPC server (using protobuf), and wrapping it inside an HTTPS server (using gin). Only the HTTPS server is being published to the clients for use (by which I mean that my application can be accessed via REST API, that actually then dials on the gRPC endpoint), and I am publishing it using Swagger OpenAPI3 (version 3 is the main requirement here) specification. Both gRPC and HTTPS is required, and any solution should adhere to this architecture.

I don't want to maintain my server specification at two places, that is I don't want to maintain both proto files (.proto) and swagger spec (.json/.yaml). Since I absolutely have to write proto files to generate gRPC server, I want to automate the swagger spec generation (OpenAPI3).

Where I am:

I am able to generate swagger OpenAPI2 spec from protobuf files (.proto) using grpc-gateway library something like so: grpc-rest-go-example. But my requirement is OpenAPI3; more specifically I want to use the oneOf feature in OpenAPI3 and map to it from oneof feature of proto. This is not possible with OpenAPI2, as it does not allow an API to have a request/response body of multiple type definitions, which was a feature added in OpenAPI3 by enabling oneOf, anyOf and allOf constructs.

While trying to do so, I stumbled upon this library by GoogleAPIs googleapis/gnostic, the description for which is:

This repository contains a Go command line tool which converts JSON and YAML OpenAPI descriptions to and from equivalent Protocol Buffer representations.

At first look, this seems to exactly solve my problem, but as it turns out, this library only interconverts between protocol buffer (protobuf) binary (.pb) and swagger OpenAPI2/OpenAPI3 (.json/.yaml) files, which brings me to my new problem.

So for example for the following .pb file:


�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

it generates the following swagger file:

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

The .pb may not view properly, access it here. So something like:

�status��
    ��string

looks like:

<0x06>status<0x12><0x0b>
    Ê<0x01><0x06>string

For above example, I first wrote the swagger spec and then generated the .pb but same can be done the other way around.

Current state:

If I have a way to convert between (.pb) and (.proto) files, the conversion loop will be closed and complete (.proto -> .pb -> .json/.yaml -> .pb -> .proto).

I am sure there has to be a way to achieve this, and hence there exists a solution to my original problem. But I could not find any article or piece of code that does it. Are there sane ways to interconvert between .pb and .proto files?

If you have an entirely different solution to my original use-case, please feel free to share that as well. It would help a lot.

Thanks in advance!

Edits:

(1) Thanks to recent comments, it is clear that a "conversion" between .pb and .proto is an absurd ask in the first place. But the original problem remains the same i.e. how to generate swagger3 (OpenAPI3) spec from protobuf file (.proto), either using annotations, tags or otherwise. Changing question title accordingly.

(2) Just the next day after I posted this, I bumped into gnostic-grpc repository, the description of which says:

This tool converts an OpenAPI v3.0 API description into a description of a gRPC service that can be used to implement that API using gRPC-JSON Transcoding.

Again, this got me exited too soon. Actually, this was a GSOC project, and as amazing as the idea of this repository is, it does not fulfill the requirements. Moreover, this is not an interconversion library, and very immature for any production use. In fact, it fails to deliver some of the basic fundamental features of the OpenAPI3 spec.

But this repository is headed in the right direction. My conclusion is to have a custom plugin that does this, mostly by extending the annotations library in GO.

(3) Apparently there are no good and obvious candidates for converting from .proto to OpenAPI3 spec (.yaml/.json), except gnostic-grpc which is very immature and highly work in progress for any kind of real use.

But for the reverse conversion, i.e. OpenAPI3 spec (.yaml/.json) to .proto, there is a good library called openapi-generator under OpenAPITools, which converts OpenAPI v2/3 spec to client/server stubs for almost all platforms. But since this is not the original ask, the question still remains open.

like image 603
Krishna Birla Avatar asked Apr 21 '20 18:04

Krishna Birla


People also ask

What does Protobuf generate?

Protobuf 2.0 provides a code generator for C++, Java, C#, and Python. Protobuf 3.0 provides a code generator for C++, Java (including JavaNano, a dialect intended for low-resource environments), Python, Go, Ruby, Objective-C, C#. It also supports JavaScript since 3.0.

Is repeated optional Protobuf?

A repeated field is inherently optional : you just don't add any values. As for com. google. protobuf.

Is Protobuf binary format?

A protocol buffer message is a series of key-value pairs. The binary version of a message just uses the field's number as the key – the name and declared type for each field can only be determined on the decoding end by referencing the message type's definition (i.e. the . proto file).

How do you use Protobuf?

To use the Any type, you must import the google/protobuf/any. proto definition. In the C# code, the Any class provides methods for setting the field, extracting the message, and checking the type. Protobuf's internal reflection code uses the Descriptor static field on each generated type to resolve Any field types.

What version of OpenAPI is in a swagger field?

(OAS 2.0 documents contain a top-level version field named swagger and value "2.0" .) An OpenAPI document that conforms to the OpenAPI Specification is itself a JSON object, which may be represented either in JSON or YAML format.

Is there a Protobuf example for OpenAPI V3?

It contains examples for OpenAPI v3 (and v2) on how the equivalent protobuf code should look like. Here is a medium article that explains end to end gRPC + HTTP (using protobuf and swagger respectively) server implementation and documentation, in a step by step fashion. It is an OpenAPI v2 example, but similar concepts apply to OpenAPI v3.

How to convert OpenAPI V2/3 spec to client stubs?

But for the reverse conversion, i.e. OpenAPI3 spec ( .yaml/.json) to .proto, there is a good library called openapi-generator under OpenAPITools, which converts OpenAPI v2/3 spec to client/server stubs for almost all platforms. But since this is not the original ask, the question still remains open.

How to write Swagger file in Visual Studio Code?

I will recommend using Visual Studio Code as your editor for writing Swagger file with the below-mentioned extension as it helps in autocompletion: Let's start by creating a new file you can name it whatever you want but I will call it swagger.json. Now open that file in Visual Studio Code and put below text inside of it:


1 Answers

Although, your requirement seems to be to get a YAML (OpenAPNv3) specification from a PROTO, you may checkout this plugin - gnostic-grpc - for gnostic which does the reverse i.e. converts from a YAML/JSON spec to a proto along with gRPC service calls.

like image 74
Anupama Deshmukh Avatar answered Oct 09 '22 14:10

Anupama Deshmukh