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.