Skip to content

Commit 1936476

Browse files
committed
feat(gen): generate ogen handler -> gRPC wrapper
1 parent 4ad5adc commit 1936476

File tree

13 files changed

+828
-12
lines changed

13 files changed

+828
-12
lines changed

cmd/protoc-gen-oas/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func run() error {
2020
title := set.String("title", "", "Title")
2121
description := set.String("description", "", "Description")
2222
version := set.String("version", "", "Version")
23+
proxy := set.Bool("proxy", false, "Generate generate gRPC proxy based on ogen")
2324

2425
if err := set.Parse(os.Args[1:]); err != nil {
2526
return errors.Wrap(err, "parse args")
@@ -47,12 +48,17 @@ func run() error {
4748
if err != nil {
4849
return err
4950
}
50-
5151
gf := plugin.NewGeneratedFile("openapi.yaml", "")
5252
if _, err := gf.Write(bytes); err != nil {
5353
return err
5454
}
5555

56+
if *proxy {
57+
if err := g.WriteProxy(plugin); err != nil {
58+
return errors.Wrap(err, "write proxy")
59+
}
60+
}
61+
5662
return nil
5763
}
5864

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/go-faster/errors v0.6.1
77
github.com/go-faster/sdk v0.6.0
88
github.com/go-faster/yaml v0.4.6
9-
github.com/ogen-go/ogen v0.67.0
9+
github.com/ogen-go/ogen v0.67.1-0.20230519074038-98e343624ecb
1010
github.com/stretchr/testify v1.8.3
1111
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53
1212
golang.org/x/tools v0.9.1
@@ -16,7 +16,7 @@ require (
1616

1717
require (
1818
github.com/davecgh/go-spew v1.1.1 // indirect
19-
github.com/dlclark/regexp2 v1.9.0 // indirect
19+
github.com/dlclark/regexp2 v1.10.0 // indirect
2020
github.com/fatih/color v1.15.0 // indirect
2121
github.com/ghodss/yaml v1.0.0 // indirect
2222
github.com/go-faster/jx v1.0.0 // indirect
@@ -31,6 +31,7 @@ require (
3131
go.uber.org/zap v1.24.0 // indirect
3232
golang.org/x/mod v0.10.0 // indirect
3333
golang.org/x/net v0.10.0 // indirect
34+
golang.org/x/sync v0.2.0 // indirect
3435
golang.org/x/sys v0.8.0 // indirect
3536
golang.org/x/text v0.9.0 // indirect
3637
gopkg.in/yaml.v2 v2.4.0 // indirect

go.sum

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
22
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
33
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4-
github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI=
5-
github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
4+
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
5+
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
66
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
77
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
88
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
@@ -28,8 +28,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
2828
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
2929
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
3030
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
31-
github.com/ogen-go/ogen v0.67.0 h1:mCWKEsxmDqQMN8aw+tv0+qGKeYj/yRhC0d38+XfdN1E=
32-
github.com/ogen-go/ogen v0.67.0/go.mod h1:2xVW08apG9D+lwRmqR2THu4pYXe1XnZ8AhBpQ0arbtE=
31+
github.com/ogen-go/ogen v0.67.1-0.20230519074038-98e343624ecb h1:CjPI5xCFynAra3/0QcVe8l3Jn9cPwFfd7htVeWxUvc8=
32+
github.com/ogen-go/ogen v0.67.1-0.20230519074038-98e343624ecb/go.mod h1:1rkAJFvflHFR8be4TsJkzyUlP25TguYo3HHIHd84cg0=
3333
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
3434
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3535
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -52,6 +52,7 @@ golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
5252
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
5353
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
5454
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
55+
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
5556
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
5657
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
5758
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{{ define "handler" }}
2+
{{- template "header" $ }}
3+
4+
// WrapperHandler is an oas.Handler implementation that maps request to gRPC server.
5+
type WrapperHandler struct {
6+
{{- range $s := $.Services }}
7+
{{ $s.ProtoName }} {{ $s.ProtoServer }}
8+
{{- end }}
9+
}
10+
11+
var _ oas.Handler = (*WrapperHandler)(nil)
12+
13+
{{- range $s := $.Services }}{{- range $m := $s.Methods }}
14+
// {{ $m.OgenName }} maps request to {{ $s.ProtoName }}.{{ $m.ProtoName }}.
15+
func (h *WrapperHandler) {{ $m.OgenName }}(ctx context.Context,
16+
{{- if $m.Input.HasParams }}params {{ $m.ParamsType }},{{- end -}}
17+
{{- if $m.Input.Body }}req {{ $m.Input.Body.OgenType }},{{- end -}}
18+
)(resp {{ $m.Output.OgenType }}, _ error) {
19+
input := new({{ $m.Input.ProtoType }})
20+
{{- if $m.Input.HasParams }}
21+
h.map{{ $m.OgenName }}Params(input, params)
22+
{{- end }}
23+
{{- if $m.Input.Body }}
24+
h.map{{ $m.OgenName }}BodyInput(input, req)
25+
{{- end }}
26+
output, err := h.{{ $s.ProtoName }}.{{ $m.ProtoName }}(ctx, input)
27+
if err != nil {
28+
return resp, err
29+
}
30+
return h.map{{ $m.OgenName }}Output(output), nil
31+
}
32+
33+
{{ if $m.Input.HasParams -}}
34+
func (h *WrapperHandler) map{{ $m.OgenName }}Params(pm *{{ $m.Input.ProtoType }}, om {{ $m.ParamsType }}) {
35+
{{- range $p := $m.Input.Path }}
36+
{{- template "map_value_from" $p.Elem }}
37+
{{- end }}
38+
}
39+
{{- end }}
40+
41+
{{ with $body := $m.Input.Body -}}
42+
func (h *WrapperHandler) map{{ $m.OgenName }}BodyInput(pm *{{ $m.Input.ProtoType }}, om {{ $body.OgenType }}) {
43+
{{- if $m.Input.AllInBody }}
44+
{{- if $body.PassByPointer }}
45+
pm.FromOpenAPI(*om)
46+
{{- else }}
47+
pm.FromOpenAPI(om)
48+
{{- end }}
49+
{{- else if $body.Field -}}
50+
{{- template "map_value_from" $body.Field.Elem }}
51+
{{- end }}
52+
}
53+
{{- end }}
54+
55+
func (h *WrapperHandler) map{{ $m.OgenName }}Output(pm *{{ $m.Output.ProtoType }}) (om {{ $m.Output.OgenType }}) {
56+
{{- if $m.Output.AllInBody }}
57+
respVal := pm.ToOpenAPI()
58+
{{- if $m.Output.PassByPointer }}
59+
return &respVal
60+
{{- else }}
61+
return respVal
62+
{{- end }}
63+
{{- else }}
64+
{{ template "map_value_to" $m.Output.Field.Elem }}
65+
return resp
66+
{{- end }}
67+
}
68+
69+
{{ end }}{{- end }}
70+
71+
{{- end }}

internal/gen/_template/header.tmpl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{ define "header" }}
2+
// Code generated by protoc-gen-oas, DO NOT EDIT.
3+
package {{ $.PackageName }}
4+
5+
import (
6+
{{- range $i := $.Imports }}
7+
{{ $i.Name }} {{ $i.Path }}
8+
{{- end }}
9+
)
10+
{{ end }}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
{{ define "messages" }}
2+
{{- template "header" $ }}
3+
4+
func indirectOf[P ~*T, T any](p P) (zero T) {
5+
return zero
6+
}
7+
8+
func elemOf[S ~[]T, T any](s S) (zero T) {
9+
return zero
10+
}
11+
12+
func valueOf[M ~map[K]V, K comparable, V any](m M) (zero V) {
13+
return zero
14+
}
15+
16+
func initPtr[P ~*T, T any](p *P) {
17+
var zero T
18+
*p = &zero
19+
}
20+
21+
func insertMap[M ~map[K]V, K comparable, V any](m *M, k K, v V) {
22+
if *m == nil {
23+
*m = make(M)
24+
}
25+
(*m)[k] = v
26+
}
27+
28+
{{ range $m := $.Messages }}
29+
func (pm *{{ $m.ProtoType }}) FromOpenAPI(om {{ $m.OgenType }}) {
30+
{{- range $f := $m.Fields -}}
31+
{{ template "map_value_from" $f.Elem }}
32+
{{ end -}}
33+
}
34+
35+
func (pm *{{ $m.ProtoType }}) ToOpenAPI() (om {{ $m.OgenType }}) {
36+
{{- range $f := $m.Fields -}}
37+
{{ template "map_value_to" $f.Elem }}
38+
{{ end -}}
39+
return om
40+
}
41+
{{ end }}
42+
43+
{{ range $e := $.Enums }}
44+
func (pm *{{ $e.ProtoType }}) FromOpenAPI(om {{ $e.OgenType }}) {
45+
idx := {{ $e.EnumValueMap }}[string(om)]
46+
*pm = {{ $e.ProtoType }}(idx)
47+
}
48+
49+
func (pm {{ $e.ProtoType }}) ToOpenAPI() {{ $e.OgenType }} {
50+
name := {{ $e.EnumNameMap }}[int32(pm)]
51+
return {{ $e.OgenType }}(name)
52+
}
53+
{{ end }}
54+
55+
{{- end }}
56+
57+
58+
{{ define "map_value_to" }}
59+
{{ $t := $.Ogen }}
60+
{{- if $.CheckProtoForNil -}} if {{ $.ProtoSel }} != nil {{- end }} {
61+
{{- if $t.IsPointer }}
62+
ogenVal := indirectOf({{ $.OgenSel }})
63+
{{- $elem := field_elem $t.PointerTo $.Proto "ogenVal" $.ProtoSel -}}
64+
{{ template "map_value_to" $elem }}
65+
{{ $.OgenSel }} = &ogenVal
66+
{{- else if $t.IsGeneric }}
67+
ogenVal := {{ $.OgenSel }}.Value
68+
{{- $elem := field_elem $t.GenericOf $.Proto "ogenVal" $.ProtoSel -}}
69+
{{ template "map_value_to" $elem }}
70+
{{ $.OgenSel }}.SetTo(ogenVal)
71+
{{- else if $t.IsArray }}
72+
for _, protoVal := range {{ $.ProtoSel }} {
73+
ogenElem := elemOf({{ $.OgenSel }})
74+
75+
{{- $elem := field_elem $t.Item $.Proto "ogenElem" "protoVal" -}}
76+
{{ template "map_value_to" $elem }}
77+
78+
{{ $.OgenSel }} = append({{ $.OgenSel }}, ogenElem)
79+
}
80+
{{- else if $t.IsMap }}
81+
for key, protoVal := range {{ $.ProtoSel }} {
82+
ogenElem := valueOf({{ $.OgenSel }})
83+
84+
{{- $elem := field_elem $t.Item $.Proto "ogenElem" "protoVal" -}}
85+
{{ template "map_value_to" $elem }}
86+
87+
insertMap(&{{ $.OgenSel }}, key, ogenElem)
88+
}
89+
{{- else if or ($t.IsStruct) ($t.IsAlias) }}
90+
{{ $.OgenSel }} = {{ $.ProtoSel }}.ToOpenAPI()
91+
{{- else if $t.IsEnum }}
92+
{{ $.OgenSel }} = {{ $.ProtoSel }}.ToOpenAPI()
93+
{{- else if $t.IsPrimitive }}
94+
{{- if $.Ptr }}
95+
{{ $.OgenSel }} = *{{ $.ProtoSel }}
96+
{{- else }}
97+
{{ $.OgenSel }} = {{ $.ProtoSel }}
98+
{{- end }}
99+
{{- else }}
100+
{{ errorf "unsupported kind: %s" $t.Kind }}
101+
{{- end }}
102+
}
103+
{{- end }}
104+
105+
{{ define "map_value_from" }}
106+
{{- $t := $.Ogen }}
107+
{{- if $t.IsPointer }}
108+
if {{ $.OgenSel }} != nil {
109+
ogenVal := *{{ $.OgenSel }}
110+
{{- $elem := field_elem $t.PointerTo $.Proto "ogenVal" $.ProtoSel -}}
111+
{{ template "map_value_from" $elem }}
112+
}
113+
{{- else if $t.IsGeneric }}
114+
if ogenVal, ok := {{ $.OgenSel }}.Get(); ok {
115+
{{- $elem := field_elem $t.GenericOf $.Proto "ogenVal" $.ProtoSel -}}
116+
{{ template "map_value_from" $elem }}
117+
}
118+
{{- else if $t.IsArray }}
119+
for _, ogenVal := range {{ $.OgenSel }} {
120+
protoVal := elemOf({{ $.ProtoSel }})
121+
122+
{{- $elem := field_elem $t.Item $.Proto "ogenVal" "protoVal" -}}
123+
{{ template "map_value_from" $elem }}
124+
125+
{{ $.ProtoSel }} = append({{ $.ProtoSel }}, protoVal)
126+
}
127+
{{- else if $t.IsMap }}
128+
for key, ogenVal := range {{ $.OgenSel }} {
129+
protoVal := valueOf({{ $.ProtoSel }})
130+
131+
{{- $elem := field_elem $t.Item $.Proto "ogenVal" "protoVal" -}}
132+
{{ template "map_value_from" $elem }}
133+
134+
insertMap(&{{ $.ProtoSel }}, key, protoVal)
135+
}
136+
{{- else if or ($t.IsStruct) ($t.IsAlias) }}
137+
{{ $.ProtoSel }} = new({{ $.ProtoType }})
138+
{{ $.ProtoSel }}.FromOpenAPI({{ $.OgenSel }})
139+
{{- else if $t.IsEnum }}
140+
{{ $.ProtoSel }} = new({{ $.ProtoType }})
141+
{{ $.ProtoSel }}.FromOpenAPI({{ $.OgenSel }})
142+
{{- else if $t.IsPrimitive }}
143+
{{- if $.Ptr }}
144+
{{ $.ProtoSel }} = &{{ $.OgenSel }}
145+
{{- else }}
146+
{{ $.ProtoSel }} = {{ $.OgenSel }}
147+
{{- end }}
148+
{{- else }}
149+
{{ errorf "unsupported kind: %s" $t.Kind }}
150+
{{- end }}
151+
{{- end }}

internal/gen/generator.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@ func NewGenerator(files []*protogen.File, opts ...GeneratorOption) (*Generator,
6464
g.spec.AddPathItem(tmpl, pi)
6565
}
6666

67+
ms, ok := g.ops[tmpl]
68+
if !ok {
69+
ms = &methodSet{
70+
Methods: map[string]methodRule{},
71+
}
72+
g.ops[tmpl] = ms
73+
}
74+
ms.Methods[rule.Method] = methodRule{
75+
Rule: rule,
76+
Method: m,
77+
Service: s,
78+
}
79+
6780
var to **ogen.Operation
6881
switch rule.Method {
6982
case http.MethodGet:
@@ -90,9 +103,25 @@ func NewGenerator(files []*protogen.File, opts ...GeneratorOption) (*Generator,
90103
return g, nil
91104
}
92105

106+
type methodSet struct {
107+
Template pathTemplate
108+
// HTTP Method -> RPC Method.
109+
Methods map[string]methodRule
110+
}
111+
112+
type methodRule struct {
113+
Rule HTTPRule
114+
Method *protogen.Method
115+
Service *protogen.Service
116+
}
117+
93118
// Generator instance.
94119
type Generator struct {
95120
spec *ogen.Spec
121+
122+
messages map[string]*protogen.Message
123+
enums map[string]*protogen.Enum
124+
ops map[string]*methodSet
96125
}
97126

98127
// YAML returns OpenAPI specification bytes.
@@ -108,6 +137,10 @@ func (g *Generator) JSON() ([]byte, error) {
108137
func (g *Generator) init() {
109138
g.spec = ogen.NewSpec()
110139
g.spec.Init()
140+
141+
g.messages = map[string]*protogen.Message{}
142+
g.enums = map[string]*protogen.Enum{}
143+
g.ops = map[string]*methodSet{}
111144
}
112145

113146
func (g *Generator) mkMethod(rule HTTPRule, m *protogen.Method) (string, *ogen.Operation, error) {

internal/gen/generator_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"google.golang.org/protobuf/types/pluginpb"
1313

1414
"github.com/go-faster/sdk/gold"
15+
1516
"github.com/ogen-go/ogen/openapi/parser"
1617
)
1718

0 commit comments

Comments
 (0)