Skip to content

Commit d476e2a

Browse files
authored
Fix: path item objects become block scalars [#3557] (#3566)
* fix * implement toYAMLNode * add comment * check len * add test * add comment * rename * update * generate
1 parent 042abde commit d476e2a

File tree

5 files changed

+144
-4
lines changed

5 files changed

+144
-4
lines changed

protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ go_test(
4949
"template_test.go",
5050
"types_test.go",
5151
],
52+
data = glob(["testdata/**"]),
5253
embed = [":genopenapi"],
5354
deps = [
5455
"//internal/descriptor",

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,20 +150,45 @@ func (po openapiPathsObject) MarshalYAML() (interface{}, error) {
150150
pathObjectNode.Kind = yaml.MappingNode
151151

152152
for _, pathData := range po {
153-
var pathNode, pathItemObjectNode yaml.Node
153+
var pathNode yaml.Node
154154

155155
pathNode.SetString(pathData.Path)
156-
b, err := yaml.Marshal(pathData.PathItemObject)
156+
pathItemObjectNode, err := pathData.PathItemObject.toYAMLNode()
157157
if err != nil {
158158
return nil, err
159159
}
160-
pathItemObjectNode.SetString(string(b))
161-
pathObjectNode.Content = append(pathObjectNode.Content, &pathNode, &pathItemObjectNode)
160+
pathObjectNode.Content = append(pathObjectNode.Content, &pathNode, pathItemObjectNode)
162161
}
163162

164163
return pathObjectNode, nil
165164
}
166165

166+
// We can simplify this implementation once the go-yaml bug is resolved. See: https://github.com/go-yaml/yaml/issues/643.
167+
//
168+
// func (pio *openapiPathItemObject) toYAMLNode() (*yaml.Node, error) {
169+
// var node yaml.Node
170+
// if err := node.Encode(pio); err != nil {
171+
// return nil, err
172+
// }
173+
// return &node, nil
174+
// }
175+
func (pio *openapiPathItemObject) toYAMLNode() (*yaml.Node, error) {
176+
var doc yaml.Node
177+
var buf bytes.Buffer
178+
ec := yaml.NewEncoder(&buf)
179+
ec.SetIndent(2)
180+
if err := ec.Encode(pio); err != nil {
181+
return nil, err
182+
}
183+
if err := yaml.Unmarshal(buf.Bytes(), &doc); err != nil {
184+
return nil, err
185+
}
186+
if len(doc.Content) == 0 {
187+
return nil, errors.New("unexpected number of yaml nodes")
188+
}
189+
return doc.Content[0], nil
190+
}
191+
167192
func (so openapiInfoObject) MarshalJSON() ([]byte, error) {
168193
type alias openapiInfoObject
169194
return extensionMarshalJSON(alias(so), so.extensions)

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package genopenapi_test
22

33
import (
4+
"os"
45
"reflect"
56
"sort"
67
"strings"
78
"testing"
89

10+
"github.com/google/go-cmp/cmp"
911
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
1012
"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/internal/genopenapi"
1113
"gopkg.in/yaml.v3"
@@ -120,6 +122,54 @@ func TestGenerateExtension(t *testing.T) {
120122
}
121123
}
122124

125+
func TestGenerateYAML(t *testing.T) {
126+
t.Parallel()
127+
128+
tests := []struct {
129+
name string
130+
inputProtoText string
131+
wantYAML string
132+
}{
133+
{
134+
// It tests https://github.com/grpc-ecosystem/grpc-gateway/issues/3557.
135+
name: "path item object",
136+
inputProtoText: "testdata/generator/path_item_object.prototext",
137+
wantYAML: "testdata/generator/path_item_object.swagger.yaml",
138+
},
139+
}
140+
141+
for _, tt := range tests {
142+
tt := tt
143+
t.Run(tt.name, func(t *testing.T) {
144+
t.Parallel()
145+
146+
b, err := os.ReadFile(tt.inputProtoText)
147+
if err != nil {
148+
t.Fatal(err)
149+
}
150+
var req pluginpb.CodeGeneratorRequest
151+
if err := prototext.Unmarshal(b, &req); err != nil {
152+
t.Fatal(err)
153+
}
154+
155+
resp := requireGenerate(t, &req, genopenapi.FormatYAML, false, true)
156+
if len(resp) != 1 {
157+
t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
158+
}
159+
got := resp[0].GetContent()
160+
161+
want, err := os.ReadFile(tt.wantYAML)
162+
if err != nil {
163+
t.Fatal(err)
164+
}
165+
diff := cmp.Diff(string(want), got)
166+
if diff != "" {
167+
t.Fatalf("content not match\n%s", diff)
168+
}
169+
})
170+
}
171+
}
172+
123173
func requireGenerate(
124174
tb testing.TB,
125175
req *pluginpb.CodeGeneratorRequest,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
file_to_generate: "your/service/v1/your_service.proto"
2+
proto_file: {
3+
name: "your/service/v1/your_service.proto"
4+
package: "your.service.v1"
5+
message_type: {
6+
name: "StringMessage"
7+
field: {
8+
name: "value"
9+
number: 1
10+
label: LABEL_OPTIONAL
11+
type: TYPE_STRING
12+
json_name: "value"
13+
}
14+
}
15+
service: {
16+
name: "YourService"
17+
method: {
18+
name: "Echo"
19+
input_type: ".your.service.v1.StringMessage"
20+
output_type: ".your.service.v1.StringMessage"
21+
options: {
22+
[google.api.http]: {
23+
post: "/api/echo"
24+
}
25+
}
26+
}
27+
}
28+
options: {
29+
go_package: "github.com/yourorg/yourprotos/gen/go/your/service/v1"
30+
}
31+
syntax: "proto3"
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
swagger: "2.0"
2+
info:
3+
title: your/service/v1/your_service.proto
4+
version: version not set
5+
tags:
6+
- name: YourService
7+
consumes:
8+
- application/json
9+
produces:
10+
- application/json
11+
paths:
12+
/api/echo:
13+
post:
14+
operationId: YourService_Echo
15+
responses:
16+
"200":
17+
description: A successful response.
18+
schema:
19+
$ref: '#/definitions/StringMessage'
20+
parameters:
21+
- name: value
22+
in: query
23+
required: false
24+
type: string
25+
tags:
26+
- YourService
27+
definitions:
28+
StringMessage:
29+
type: object
30+
properties:
31+
value:
32+
type: string

0 commit comments

Comments
 (0)