Skip to content

Commit f13518d

Browse files
feat: responsebody generation (default) and remove path params from request body
1 parent 48b4c88 commit f13518d

File tree

4 files changed

+74
-18
lines changed

4 files changed

+74
-18
lines changed

protoc-gen-openapiv3/internal/genopenapiv3/file_generator.go

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,17 @@ func (fg *fileGenerator) generateFileDoc(file *descriptor.File) *openapi3.T {
3030

3131
fg.doc.Components = new(openapi3.Components)
3232
fg.doc.Components.Schemas = make(openapi3.Schemas)
33+
fg.doc.Components.RequestBodies = make(openapi3.RequestBodies)
3334

3435
if fg.doc.Paths == nil {
3536
fg.doc.Paths = &openapi3.Paths{}
3637
}
3738

3839
for _, svc := range file.Services {
39-
fg.generateServiceDoc(svc)
40+
err := fg.generateServiceDoc(svc)
41+
if err != nil {
42+
grpclog.Errorf("could not generate service document: %v", err)
43+
}
4044
}
4145

4246
for _, msg := range file.Messages {
@@ -77,6 +81,10 @@ func (fg *fileGenerator) generateMessageSchema(msg *descriptor.Message, excludeF
7781
properties := make(openapi3.Schemas)
7882
tempOneOfsProperties := make(map[int32]openapi3.Schemas)
7983
for _, field := range msg.Fields {
84+
if slices.Contains(excludeFields, field.FQFN()) {
85+
continue
86+
}
87+
8088
fieldDoc := fg.generateFieldDoc(field)
8189
if field.OneofIndex != nil {
8290
if tempOneOfsProperties[*field.OneofIndex] == nil {
@@ -254,10 +262,15 @@ func (fg *fileGenerator) generateEnumSchema(enum *descriptor.Enum) *openapi3.Sch
254262
}
255263
}
256264

257-
func (fg *fileGenerator) generateServiceDoc(svc *descriptor.Service) {
265+
func (fg *fileGenerator) generateServiceDoc(svc *descriptor.Service) error {
258266
for _, meth := range svc.Methods {
259-
fg.generateMethodDoc(meth)
267+
err := fg.generateMethodDoc(meth)
268+
if err != nil {
269+
return fmt.Errorf("could not generate method %s doc: %w", meth.GetName(), err)
270+
}
260271
}
272+
273+
return nil
261274
}
262275

263276
func (fg *fileGenerator) generateMethodDoc(meth *descriptor.Method) error {
@@ -279,26 +292,52 @@ func (fg *fileGenerator) generateMethodDoc(meth *descriptor.Method) error {
279292
params = append(params, tmpParams...)
280293
}
281294

295+
pathParamsFQFNs := make([]string, len(binding.PathParams))
296+
for i, param := range binding.PathParams {
297+
pathParamsFQFNs[i] = param.Target.FQFN()
298+
}
299+
282300
switch binding.HTTPMethod {
283301
case "POST", "PUT", "PATCH":
284302
// For POST, PUT, PATCH, add request body
285-
requestBody = &openapi3.RequestBodyRef{Value: openapi3.NewRequestBody().WithContent(
286-
openapi3.NewContentWithJSONSchemaRef(fg.getMessageSchemaRef(meth.RequestType)))}
303+
if len(pathParamsFQFNs) > 0 {
304+
messageSchema := fg.generateMessageSchema(meth.RequestType, pathParamsFQFNs)
305+
306+
name := fg.resolveName(meth.RequestType.FQMN())
307+
resultRef := fmt.Sprintf("#/components/requestBodies/%s", name)
287308

309+
fg.doc.Components.RequestBodies[name] = &openapi3.RequestBodyRef{Value: openapi3.NewRequestBody().WithContent(
310+
openapi3.NewContentWithJSONSchemaRef(messageSchema.NewRef()))}
311+
312+
requestBody = &openapi3.RequestBodyRef{Ref: resultRef}
313+
} else {
314+
requestBody = &openapi3.RequestBodyRef{Value: openapi3.NewRequestBody().
315+
WithJSONSchemaRef(fg.getMessageSchemaRef(meth.RequestType))}
316+
}
288317
}
289318
}
290319

291-
responseSchema := &openapi3.ResponseRef{
292-
Ref: fg.getMessageSchemaRef(meth.ResponseType).RefString(),
320+
defaultResponse, err := fg.defaultResponse()
321+
if err != nil {
322+
return fmt.Errorf("could not get default response: %w", err)
293323
}
294324

325+
successResponseSchema := openapi3.NewResponse().
326+
WithJSONSchemaRef(fg.getMessageSchemaRef(meth.ResponseType))
327+
328+
defaultResponseSchema := openapi3.NewResponse().
329+
WithJSONSchemaRef(fg.getMessageSchemaRef(defaultResponse))
330+
331+
responses := openapi3.NewResponses(openapi3.WithStatus(200, &openapi3.ResponseRef{Value: successResponseSchema}),
332+
openapi3.WithName("default", defaultResponseSchema))
333+
295334
operation := &openapi3.Operation{
296335
Tags: []string{meth.Service.GetName()},
297336
Summary: opOpts.GetSummary(),
298337
Description: opOpts.GetDescription(),
299338
OperationID: fg.getOperationName(meth.Service.GetName(), meth.GetName(), bindingIdx),
300339
RequestBody: requestBody,
301-
Responses: openapi3.NewResponses(openapi3.WithStatus(200, responseSchema)),
340+
Responses: responses,
302341
Parameters: params,
303342
}
304343

@@ -370,13 +409,13 @@ func (fg *fileGenerator) messageToParameters(message *descriptor.Message,
370409

371410
switch paramType {
372411
case openapi3.ParameterInPath:
373-
param := openapi3.NewPathParameter(field.GetJsonName())
412+
param := openapi3.NewPathParameter(field.GetName())
374413
param.Schema = schema
375414
params = append(params, &openapi3.ParameterRef{
376415
Value: param,
377416
})
378417
case openapi3.ParameterInQuery:
379-
param := openapi3.NewQueryParameter(field.GetJsonName())
418+
param := openapi3.NewQueryParameter(field.GetName())
380419
param.Schema = schema
381420
params = append(params, &openapi3.ParameterRef{
382421
Value: param,

protoc-gen-openapiv3/internal/genopenapiv3/generator.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import (
99
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
1010
gen "github.com/grpc-ecosystem/grpc-gateway/v2/internal/generator"
1111
"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options"
12-
"google.golang.org/grpc/grpclog"
12+
statuspb "google.golang.org/genproto/googleapis/rpc/status"
1313
"google.golang.org/protobuf/proto"
14+
"google.golang.org/protobuf/reflect/protodesc"
1415
"google.golang.org/protobuf/types/descriptorpb"
16+
"google.golang.org/protobuf/types/known/anypb"
1517
"google.golang.org/protobuf/types/pluginpb"
1618
)
1719

@@ -28,6 +30,11 @@ func NewGenerator(reg *descriptor.Registry, format Format) gen.Generator {
2830
}
2931

3032
func (g *generator) Generate(targets []*descriptor.File) ([]*descriptor.ResponseFile, error) {
33+
err := g.loadPrequisiteProtos()
34+
if err != nil {
35+
return nil, fmt.Errorf("could not load prequisite proto files in registry: %w", err)
36+
}
37+
3138
respFiles := make([]*descriptor.ResponseFile, len(targets))
3239
for i, t := range targets {
3340
fileGenerator := &fileGenerator{generator: g, doc: &openapi3.T{}}
@@ -70,15 +77,30 @@ func (g *generator) fqmnToLocation(fqmn string) string {
7077
}
7178

7279
func (g *generator) resolveName(fqmn string) string {
73-
grpclog.Infof("resolveFQMN: %s", fqmn)
7480
return fqmn
7581
}
7682

7783
func (g *generator) resolveType(typeName string) string {
78-
grpclog.Infof("resolveType: %s", typeName)
7984
return typeName
8085
}
8186

87+
func (g *generator) loadPrequisiteProtos() error {
88+
any := protodesc.ToFileDescriptorProto((&anypb.Any{}).ProtoReflect().Descriptor().ParentFile())
89+
any.SourceCodeInfo = new(descriptorpb.SourceCodeInfo)
90+
status := protodesc.ToFileDescriptorProto((&statuspb.Status{}).ProtoReflect().Descriptor().ParentFile())
91+
status.SourceCodeInfo = new(descriptorpb.SourceCodeInfo)
92+
return g.reg.Load(&pluginpb.CodeGeneratorRequest{
93+
ProtoFile: []*descriptorpb.FileDescriptorProto{
94+
any,
95+
status,
96+
},
97+
})
98+
}
99+
100+
func (g *generator) defaultResponse() (*descriptor.Message, error) {
101+
return g.reg.LookupMsg("", ".google.rpc.Status")
102+
}
103+
82104
func extractOperationOptionFromMethodDescriptor(meth *descriptorpb.MethodDescriptorProto) (*options.Operation, error) {
83105
if meth.Options == nil {
84106
return &options.Operation{}, nil

protoc-gen-openapiv3/internal/genopenapiv3/types.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,3 @@ const (
4040
OneOfStrategyOneOf = "oneof"
4141
OneOfStrategyAllOf = "allof"
4242
)
43-
44-
type methodDoc struct{
45-
operation *openapi3.Operation
46-
externalSchemas []*openapi3.Schema
47-
}
-14 MB
Binary file not shown.

0 commit comments

Comments
 (0)