Skip to content

Commit 82da9d7

Browse files
authored
Support disabling default response rendering (#3006)
* feat(protoc-gen-openapiv2): support disabling default response rendering * docs: add example output for disable_default_responses * docs: note for alterting HTTP status codes for disabled default responses
1 parent 191aea6 commit 82da9d7

File tree

5 files changed

+132
-10
lines changed

5 files changed

+132
-10
lines changed

docs/docs/mapping/customizing_openapi_output.md

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -631,14 +631,107 @@ info:
631631
title: helloproto/v1/example.proto
632632
version: version not set
633633
consumes:
634-
- application/json
634+
- application/json
635635
produces:
636-
- application/json
636+
- application/json
637+
paths:
638+
/api/hello:
639+
get:
640+
operationId: EchoService_Hello
641+
```
642+
643+
### Disable default responses
644+
645+
By default a 200 OK response is rendered for each service operation. But it is possible to disable this and explicitly define your service's responses, using the `disable_default_responses` option. Allowed values are: `true`, `false`.
646+
647+
**Note**: This does not alter the behavior of the gateway itself and should be coupled with a `ForwardResponseWriter` when altering status codes, see [Controlling HTTP Response Codes](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/customizing_your_gateway/#controlling-http-response-status-codes).
648+
649+
For example, if you are using `buf`:
650+
651+
```yaml
652+
version: v1
653+
plugins:
654+
- name: openapiv2
655+
out: .
656+
opt:
657+
- disable_default_responses=true
658+
```
659+
660+
or with `protoc`
661+
662+
```sh
663+
protoc --openapiv2_out=. --openapiv2_opt=disable_default_responses=true ./path/to/file.proto
664+
```
665+
666+
Input example:
667+
668+
```protobuf
669+
syntax = "proto3";
670+
671+
package helloproto.v1;
672+
673+
import "google/api/annotations.proto";
674+
import "protoc-gen-openapiv2/options/annotations.proto";
675+
676+
option go_package = "helloproto/v1;helloproto";
677+
678+
service EchoService {
679+
rpc Hello(HelloReq) returns (HelloResp) {
680+
option (google.api.http) = {get: "/api/hello"};
681+
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
682+
responses: {
683+
key: "201",
684+
value: {
685+
description: "Created";
686+
schema: {
687+
json_schema: {ref: ".helloproto.v1.HelloResp"}
688+
}
689+
}
690+
};
691+
};
692+
}
693+
}
694+
695+
message HelloReq {
696+
string name = 1;
697+
}
698+
699+
message HelloResp {
700+
string message = 1;
701+
}
702+
```
703+
704+
Output (default response not generated):
705+
706+
```yaml
707+
swagger: "2.0"
708+
info:
709+
title: helloproto/v1/hello.proto
710+
version: version not set
711+
consumes:
712+
- application/json
713+
produces:
714+
- application/json
637715
paths:
638716
/api/hello:
639717
get:
640718
operationId: EchoService_Hello
641-
...
719+
responses:
720+
"201":
721+
description: Created
722+
schema:
723+
$ref: "#/definitions/v1HelloResp"
724+
parameters:
725+
- name: name
726+
in: query
727+
required: false
728+
type: string
729+
definitions:
730+
v1HelloResp:
731+
type: object
732+
properties:
733+
message:
734+
type: string
642735
```
643736

644737
{% endraw %}

internal/descriptor/registry.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ type Registry struct {
132132
// disableServiceTags disables the generation of service tags.
133133
// This is useful if you do not want to expose the names of your backend grpc services.
134134
disableServiceTags bool
135+
136+
// disableDefaultResponses disables the generation of default responses.
137+
// Useful if you have to support custom response codes that are not 200.
138+
disableDefaultResponses bool
135139
}
136140

137141
type repeatedFieldSeparator struct {
@@ -758,3 +762,13 @@ func (r *Registry) SetDisableServiceTags(use bool) {
758762
func (r *Registry) GetDisableServiceTags() bool {
759763
return r.disableServiceTags
760764
}
765+
766+
// SetDisableDefaultResponses setsdisableDefaultResponses
767+
func (r *Registry) SetDisableDefaultResponses(use bool) {
768+
r.disableDefaultResponses = use
769+
}
770+
771+
// GetDisableDefaultResponses returns disableDefaultResponses
772+
func (r *Registry) GetDisableDefaultResponses() bool {
773+
return r.disableDefaultResponses
774+
}

protoc-gen-openapiv2/internal/genopenapi/template.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,13 +1366,15 @@ func renderServices(services []*descriptor.Service, paths openapiPathsObject, re
13661366

13671367
operationObject := &openapiOperationObject{
13681368
Parameters: parameters,
1369-
Responses: openapiResponsesObject{
1370-
"200": openapiResponseObject{
1371-
Description: desc,
1372-
Schema: responseSchema,
1373-
Headers: openapiHeadersObject{},
1374-
},
1375-
},
1369+
Responses: openapiResponsesObject{},
1370+
}
1371+
1372+
if !reg.GetDisableDefaultResponses() {
1373+
operationObject.Responses["200"] = openapiResponseObject{
1374+
Description: desc,
1375+
Schema: responseSchema,
1376+
Headers: openapiHeadersObject{},
1377+
}
13761378
}
13771379

13781380
if !reg.GetDisableServiceTags() {

protoc-gen-openapiv2/internal/genopenapi/template_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,6 +3316,17 @@ func TestApplyTemplateProtobufAny(t *testing.T) {
33163316
},
33173317
wantNumDefinitions: 3,
33183318
},
3319+
{
3320+
// we have a protobufAny in a message but with automatic rendering of responses disabled
3321+
name: "protobufAny_referenced_in_message_with_default_responses_disabled",
3322+
args: args{
3323+
msgContainsAny: true,
3324+
regConfig: func(reg *descriptor.Registry) {
3325+
reg.SetDisableDefaultResponses(true)
3326+
},
3327+
},
3328+
wantNumDefinitions: 4,
3329+
},
33193330
}
33203331

33213332
for _, tt := range tests {

protoc-gen-openapiv2/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var (
4141
outputFormat = flag.String("output_format", string(genopenapi.FormatJSON), fmt.Sprintf("output content format. Allowed values are: `%s`, `%s`", genopenapi.FormatJSON, genopenapi.FormatYAML))
4242
visibilityRestrictionSelectors = utilities.StringArrayFlag(flag.CommandLine, "visibility_restriction_selectors", "list of `google.api.VisibilityRule` visibility labels to include in the generated output when a visibility annotation is defined. Repeat this option to supply multiple values. Elements without visibility annotations are unaffected by this setting.")
4343
disableServiceTags = flag.Bool("disable_service_tags", false, "if set, disables generation of service tags. This is useful if you do not want to expose the names of your backend grpc services.")
44+
disableDefaultResponses = flag.Bool("disable_default_responses", false, "if set, disables generation of default responses. Useful if you have to support custom response codes that are not 200.")
4445
)
4546

4647
// Variables set by goreleaser at build time
@@ -125,6 +126,7 @@ func main() {
125126
reg.SetOmitEnumDefaultValue(*omitEnumDefaultValue)
126127
reg.SetVisibilityRestrictionSelectors(*visibilityRestrictionSelectors)
127128
reg.SetDisableServiceTags(*disableServiceTags)
129+
reg.SetDisableDefaultResponses(*disableDefaultResponses)
128130
if err := reg.SetRepeatedPathParamSeparator(*repeatedPathParamSeparator); err != nil {
129131
emitError(err)
130132
return

0 commit comments

Comments
 (0)