Skip to content

Commit f336fbc

Browse files
mechinnjohanbrandhorst
authored andcommitted
make generated swagger json match gateway behavior for server streams (#850)
* #579 fixing protoc-gen-swagger to wrap server stream messages with "result" object to match gateway behavior * #579 fixing test example swagger json * change streamdefinitions to x-stream-definitions and add test * move runtime internal to root of repo for use in protoc-gen-swagger, adding error to stream wrapper * adding comment explaining AddStreamError * fix bazel
1 parent 9221822 commit f336fbc

File tree

14 files changed

+586
-45
lines changed

14 files changed

+586
-45
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ SWAGGER_PLUGIN_FLAGS?=
4545
GOOGLEAPIS_DIR=third_party/googleapis
4646
OUTPUT_DIR=_output
4747

48-
RUNTIME_PROTO=runtime/internal/stream_chunk.proto
48+
RUNTIME_PROTO=internal/stream_chunk.proto
4949
RUNTIME_GO=$(RUNTIME_PROTO:.proto=.pb.go)
5050

5151
OPENAPIV2_PROTO=protoc-gen-swagger/options/openapiv2.proto protoc-gen-swagger/options/annotations.proto
@@ -63,6 +63,7 @@ endif
6363
SWAGGER_EXAMPLES=examples/proto/examplepb/echo_service.proto \
6464
examples/proto/examplepb/a_bit_of_everything.proto \
6565
examples/proto/examplepb/wrappers.proto \
66+
examples/proto/examplepb/stream.proto \
6667
examples/proto/examplepb/unannotated_echo_service.proto \
6768
examples/proto/examplepb/response_body_service.proto
6869

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
{
2+
"swagger": "2.0",
3+
"info": {
4+
"title": "examples/proto/examplepb/stream.proto",
5+
"version": "version not set"
6+
},
7+
"schemes": [
8+
"http",
9+
"https"
10+
],
11+
"consumes": [
12+
"application/json"
13+
],
14+
"produces": [
15+
"application/json"
16+
],
17+
"paths": {
18+
"/v1/example/a_bit_of_everything": {
19+
"get": {
20+
"operationId": "List",
21+
"responses": {
22+
"200": {
23+
"description": "A successful response.(streaming responses)",
24+
"schema": {
25+
"$ref": "#/x-stream-definitions/examplepbABitOfEverything"
26+
}
27+
}
28+
},
29+
"tags": [
30+
"StreamService"
31+
]
32+
}
33+
},
34+
"/v1/example/a_bit_of_everything/bulk": {
35+
"post": {
36+
"operationId": "BulkCreate",
37+
"responses": {
38+
"200": {
39+
"description": "A successful response.",
40+
"schema": {
41+
"properties": {}
42+
}
43+
}
44+
},
45+
"parameters": [
46+
{
47+
"name": "body",
48+
"description": " (streaming inputs)",
49+
"in": "body",
50+
"required": true,
51+
"schema": {
52+
"$ref": "#/definitions/examplepbABitOfEverything"
53+
}
54+
}
55+
],
56+
"tags": [
57+
"StreamService"
58+
]
59+
}
60+
},
61+
"/v1/example/a_bit_of_everything/echo": {
62+
"post": {
63+
"operationId": "BulkEcho",
64+
"responses": {
65+
"200": {
66+
"description": "A successful response.(streaming responses)",
67+
"schema": {
68+
"$ref": "#/x-stream-definitions/subStringMessage"
69+
}
70+
}
71+
},
72+
"parameters": [
73+
{
74+
"name": "body",
75+
"description": " (streaming inputs)",
76+
"in": "body",
77+
"required": true,
78+
"schema": {
79+
"$ref": "#/definitions/subStringMessage"
80+
}
81+
}
82+
],
83+
"tags": [
84+
"StreamService"
85+
]
86+
}
87+
}
88+
},
89+
"definitions": {
90+
"ABitOfEverythingNested": {
91+
"type": "object",
92+
"example": {
93+
"ok": "TRUE"
94+
},
95+
"properties": {
96+
"name": {
97+
"type": "string",
98+
"description": "name is nested field."
99+
},
100+
"amount": {
101+
"type": "integer",
102+
"format": "int64"
103+
},
104+
"ok": {
105+
"$ref": "#/definitions/NestedDeepEnum"
106+
}
107+
},
108+
"description": "Nested is nested type."
109+
},
110+
"MessagePathEnumNestedPathEnum": {
111+
"type": "string",
112+
"enum": [
113+
"GHI",
114+
"JKL"
115+
],
116+
"default": "GHI"
117+
},
118+
"NestedDeepEnum": {
119+
"type": "string",
120+
"enum": [
121+
"FALSE",
122+
"TRUE"
123+
],
124+
"default": "FALSE",
125+
"description": "DeepEnum is one or zero.\n\n - FALSE: FALSE is false.\n - TRUE: TRUE is true."
126+
},
127+
"examplepbABitOfEverything": {
128+
"type": "object",
129+
"example": {
130+
"uuid": "0cf361e1-4b44-483d-a159-54dabdf7e814"
131+
},
132+
"properties": {
133+
"single_nested": {
134+
"$ref": "#/definitions/ABitOfEverythingNested"
135+
},
136+
"uuid": {
137+
"type": "string",
138+
"minLength": 1,
139+
"pattern": "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
140+
},
141+
"nested": {
142+
"type": "array",
143+
"items": {
144+
"$ref": "#/definitions/ABitOfEverythingNested"
145+
}
146+
},
147+
"float_value": {
148+
"type": "number",
149+
"format": "float"
150+
},
151+
"double_value": {
152+
"type": "number",
153+
"format": "double"
154+
},
155+
"int64_value": {
156+
"type": "string",
157+
"format": "int64"
158+
},
159+
"uint64_value": {
160+
"type": "string",
161+
"format": "uint64"
162+
},
163+
"int32_value": {
164+
"type": "integer",
165+
"format": "int32"
166+
},
167+
"fixed64_value": {
168+
"type": "string",
169+
"format": "uint64"
170+
},
171+
"fixed32_value": {
172+
"type": "integer",
173+
"format": "int64"
174+
},
175+
"bool_value": {
176+
"type": "boolean",
177+
"format": "boolean"
178+
},
179+
"string_value": {
180+
"type": "string"
181+
},
182+
"bytes_value": {
183+
"type": "string",
184+
"format": "byte"
185+
},
186+
"uint32_value": {
187+
"type": "integer",
188+
"format": "int64"
189+
},
190+
"enum_value": {
191+
"$ref": "#/definitions/examplepbNumericEnum"
192+
},
193+
"path_enum_value": {
194+
"$ref": "#/definitions/pathenumPathEnum"
195+
},
196+
"nested_path_enum_value": {
197+
"$ref": "#/definitions/MessagePathEnumNestedPathEnum"
198+
},
199+
"sfixed32_value": {
200+
"type": "integer",
201+
"format": "int32"
202+
},
203+
"sfixed64_value": {
204+
"type": "string",
205+
"format": "int64"
206+
},
207+
"sint32_value": {
208+
"type": "integer",
209+
"format": "int32"
210+
},
211+
"sint64_value": {
212+
"type": "string",
213+
"format": "int64"
214+
},
215+
"repeated_string_value": {
216+
"type": "array",
217+
"items": {
218+
"type": "string"
219+
}
220+
},
221+
"oneof_empty": {
222+
"properties": {}
223+
},
224+
"oneof_string": {
225+
"type": "string"
226+
},
227+
"map_value": {
228+
"type": "object",
229+
"additionalProperties": {
230+
"$ref": "#/definitions/examplepbNumericEnum"
231+
}
232+
},
233+
"mapped_string_value": {
234+
"type": "object",
235+
"additionalProperties": {
236+
"type": "string"
237+
}
238+
},
239+
"mapped_nested_value": {
240+
"type": "object",
241+
"additionalProperties": {
242+
"$ref": "#/definitions/ABitOfEverythingNested"
243+
}
244+
},
245+
"nonConventionalNameValue": {
246+
"type": "string"
247+
},
248+
"timestamp_value": {
249+
"type": "string",
250+
"format": "date-time"
251+
},
252+
"repeated_enum_value": {
253+
"type": "array",
254+
"items": {
255+
"$ref": "#/definitions/examplepbNumericEnum"
256+
},
257+
"title": "repeated enum value. it is comma-separated in query"
258+
}
259+
},
260+
"description": "Intentionaly complicated message type to cover many features of Protobuf.",
261+
"title": "A bit of everything",
262+
"externalDocs": {
263+
"description": "Find out more about ABitOfEverything",
264+
"url": "https://github.com/grpc-ecosystem/grpc-gateway"
265+
},
266+
"required": [
267+
"uuid"
268+
]
269+
},
270+
"examplepbNumericEnum": {
271+
"type": "string",
272+
"enum": [
273+
"ZERO",
274+
"ONE"
275+
],
276+
"default": "ZERO",
277+
"description": "NumericEnum is one or zero.\n\n - ZERO: ZERO means 0\n - ONE: ONE means 1"
278+
},
279+
"pathenumPathEnum": {
280+
"type": "string",
281+
"enum": [
282+
"ABC",
283+
"DEF"
284+
],
285+
"default": "ABC"
286+
},
287+
"protobufAny": {
288+
"type": "object",
289+
"properties": {
290+
"type_url": {
291+
"type": "string",
292+
"description": "A URL/resource name whose content describes the type of the\nserialized protocol buffer message.\n\nFor URLs which use the scheme `http`, `https`, or no scheme, the\nfollowing restrictions and interpretations apply:\n\n* If no scheme is provided, `https` is assumed.\n* The last segment of the URL's path must represent the fully\n qualified name of the type (as in `path/google.protobuf.Duration`).\n The name should be in a canonical form (e.g., leading \".\" is\n not accepted).\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics."
293+
},
294+
"value": {
295+
"type": "string",
296+
"format": "byte",
297+
"description": "Must be a valid serialized protocol buffer of the above specified type."
298+
}
299+
},
300+
"description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }"
301+
},
302+
"runtimeStreamError": {
303+
"type": "object",
304+
"properties": {
305+
"grpc_code": {
306+
"type": "integer",
307+
"format": "int32"
308+
},
309+
"http_code": {
310+
"type": "integer",
311+
"format": "int32"
312+
},
313+
"message": {
314+
"type": "string"
315+
},
316+
"http_status": {
317+
"type": "string"
318+
},
319+
"details": {
320+
"type": "array",
321+
"items": {
322+
"$ref": "#/definitions/protobufAny"
323+
}
324+
}
325+
}
326+
},
327+
"subStringMessage": {
328+
"type": "object",
329+
"properties": {
330+
"value": {
331+
"type": "string"
332+
}
333+
}
334+
}
335+
},
336+
"x-stream-definitions": {
337+
"examplepbABitOfEverything": {
338+
"type": "object",
339+
"properties": {
340+
"result": {
341+
"$ref": "#/definitions/examplepbABitOfEverything"
342+
},
343+
"error": {
344+
"$ref": "#/definitions/runtimeStreamError"
345+
}
346+
},
347+
"title": "Stream result of examplepbABitOfEverything"
348+
},
349+
"subStringMessage": {
350+
"type": "object",
351+
"properties": {
352+
"result": {
353+
"$ref": "#/definitions/subStringMessage"
354+
},
355+
"error": {
356+
"$ref": "#/definitions/runtimeStreamError"
357+
}
358+
},
359+
"title": "Stream result of subStringMessage"
360+
}
361+
}
362+
}

0 commit comments

Comments
 (0)