Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial interceptors implementation #3616

Open
wants to merge 6 commits into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions codegen/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type (
// PkgName is the service HTTP client package import name,
// e.g. "storagec".
PkgName string
// Interceptors contains the data for client interceptors if any.
Interceptors *InterceptorData
}

// SubcommandData contains the data needed to render a sub-command.
Expand All @@ -58,6 +60,14 @@ type (
Example string
}

// InterceptorData contains the data needed to generate interceptor code.
InterceptorData struct {
// VarName is the name of the interceptor variable.
VarName string
// PkgName is the package name containing the interceptor type.
PkgName string
}

// FlagData contains the data needed to render a command-line flag.
FlagData struct {
// Name is the name of the flag, e.g. "list-vintage"
Expand Down Expand Up @@ -151,11 +161,21 @@ func BuildCommandData(data *service.Data) *CommandData {
if description == "" {
description = fmt.Sprintf("Make requests to the %q service", data.Name)
}

var interceptors *InterceptorData
if len(data.ClientInterceptors) > 0 {
interceptors = &InterceptorData{
VarName: "inter",
PkgName: data.PkgName,
}
}

return &CommandData{
Name: codegen.KebabCase(data.Name),
VarName: codegen.Goify(data.Name, false),
Description: description,
PkgName: data.PkgName + "c",
Name: codegen.KebabCase(data.Name),
VarName: codegen.Goify(data.Name, false),
Description: description,
PkgName: data.PkgName + "c",
Interceptors: interceptors,
}
}

Expand Down
20 changes: 19 additions & 1 deletion codegen/example/example_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,17 @@ func exampleSvrMain(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *c
// Iterate through services listed in the server expression.
svcData := make([]*service.Data, len(svr.Services))
scope := codegen.NewNameScope()
hasInterceptors := false
for i, svc := range svr.Services {
sd := service.Services.Get(svc)
svcData[i] = sd
specs = append(specs, &codegen.ImportSpec{
Path: path.Join(genpkg, sd.PathName),
Name: scope.Unique(sd.PkgName),
Name: scope.Unique(sd.PkgName, "svc"),
})
hasInterceptors = hasInterceptors || len(sd.ServerInterceptors) > 0
}
interPkg := scope.Unique("interceptors", "ex")

var (
rootPath string
Expand All @@ -73,6 +76,9 @@ func exampleSvrMain(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *c
apiPkg = scope.Unique(strings.ToLower(codegen.Goify(root.API.Name, false)), "api")
}
specs = append(specs, &codegen.ImportSpec{Path: rootPath, Name: apiPkg})
if hasInterceptors {
specs = append(specs, &codegen.ImportSpec{Path: path.Join(rootPath, "interceptors"), Name: interPkg})
}

sections := []*codegen.SectionTemplate{
codegen.Header("", "main", specs),
Expand Down Expand Up @@ -101,6 +107,18 @@ func exampleSvrMain(genpkg string, root *expr.RootExpr, svr *expr.ServerExpr) *c
FuncMap: map[string]any{
"mustInitServices": mustInitServices,
},
}, {
Name: "server-main-interceptors",
Source: readTemplate("server_interceptors"),
Data: map[string]any{
"APIPkg": apiPkg,
"InterPkg": interPkg,
"Services": svcData,
"HasInterceptors": hasInterceptors,
},
FuncMap: map[string]any{
"mustInitServices": mustInitServices,
},
}, {
Name: "server-main-endpoints",
Source: readTemplate("server_endpoints"),
Expand Down
2 changes: 1 addition & 1 deletion codegen/example/templates/server_endpoints.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{
{{- range .Services }}
{{- if .Methods }}
{{ .VarName }}Endpoints = {{ .PkgName }}.NewEndpoints({{ .VarName }}Svc)
{{ .VarName }}Endpoints = {{ .PkgName }}.NewEndpoints({{ .VarName }}Svc{{ if .ServerInterceptors }}, {{ .VarName }}Interceptors{{ end }})
{{ .VarName }}Endpoints.Use(debug.LogPayloads())
{{ .VarName }}Endpoints.Use(log.Endpoint)
{{- end }}
Expand Down
23 changes: 23 additions & 0 deletions codegen/example/templates/server_interceptors.go.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{- if mustInitServices .Services }}
{{- if .HasInterceptors }}
{{ comment "Initialize the interceptors." }}
var (
{{- range .Services }}
{{- if and .Methods .ServerInterceptors }}
{{ .VarName }}Interceptors {{ .PkgName }}.ServerInterceptors
{{- end }}
{{- end }}
)
{{- end }}
{{- if .HasInterceptors }}
{
{{- end }}
{{- range .Services }}
{{- if and .Methods .ServerInterceptors }}
{{ .VarName }}Interceptors = {{ $.InterPkg }}.New{{ .StructName }}ServerInterceptors()
{{- end }}
{{- end }}
{{- if .HasInterceptors }}
}
{{- end }}
{{- end }}
7 changes: 7 additions & 0 deletions codegen/generator/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func Example(genpkg string, roots []eval.Root) ([]*codegen.File, error) {
files = append(files, fs...)
}

// example interceptors implementation
if fs := service.ExampleInterceptorsFiles(genpkg, r); len(fs) != 0 {
files = append(files, fs...)
}

// server main
if fs := example.ServerFiles(genpkg, r); len(fs) != 0 {
files = append(files, fs...)
Expand Down Expand Up @@ -54,6 +59,8 @@ func Example(genpkg string, roots []eval.Root) ([]*codegen.File, error) {
files = append(files, fs...)
}
}

// Add imports defined via struct:field:type
for _, f := range files {
if len(f.SectionTemplates) > 0 {
for _, s := range r.Services {
Expand Down
1 change: 1 addition & 0 deletions codegen/service/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func TestClient(t *testing.T) {
{"client-streaming-payload-no-result", testdata.StreamingPayloadNoResultMethodDSL, testdata.StreamingPayloadNoResultMethodClient},
{"client-bidirectional-streaming", testdata.BidirectionalStreamingMethodDSL, testdata.BidirectionalStreamingMethodClient},
{"client-bidirectional-streaming-no-payload", testdata.BidirectionalStreamingNoPayloadMethodDSL, testdata.BidirectionalStreamingNoPayloadMethodClient},
{"client-interceptor", testdata.EndpointWithClientInterceptorDSL, testdata.InterceptorClient},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
Expand Down
24 changes: 0 additions & 24 deletions codegen/service/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"goa.design/goa/v3/codegen"
"goa.design/goa/v3/codegen/service/testdata"
"goa.design/goa/v3/dsl"
"goa.design/goa/v3/eval"
"goa.design/goa/v3/expr"
)

Expand Down Expand Up @@ -257,30 +256,7 @@ func TestConvertFile(t *testing.T) {
}
}

// runDSL returns the DSL root resulting from running the given DSL.
func runDSL(t *testing.T, dsl func()) *expr.RootExpr {
// reset all roots and codegen data structures
Services = make(ServicesData)
eval.Reset()
expr.Root = new(expr.RootExpr)
expr.GeneratedResultTypes = new(expr.ResultTypesRoot)
require.NoError(t, eval.Register(expr.Root))
require.NoError(t, eval.Register(expr.GeneratedResultTypes))
expr.Root.API = expr.NewAPIExpr("test api", func() {})
expr.Root.API.Servers = []*expr.ServerExpr{expr.Root.API.DefaultServer()}

// run DSL (first pass)
require.True(t, eval.Execute(dsl, nil))

// run DSL (second pass)
require.NoError(t, eval.RunDSL())

// return generated root
return expr.Root
}

// Test fixtures

var obj = &expr.UserTypeExpr{
AttributeExpr: &expr.AttributeExpr{
Type: &expr.Object{
Expand Down
40 changes: 27 additions & 13 deletions codegen/service/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type (
// Schemes contains the security schemes types used by the
// all the endpoints.
Schemes SchemesData
// HasServerInterceptors indicates that the service has server-side
// interceptors.
HasServerInterceptors bool
// HasClientInterceptors indicates that the service has client-side
// interceptors.
HasClientInterceptors bool
}

// EndpointMethodData describes a single endpoint method.
Expand All @@ -44,6 +50,10 @@ type (
ServiceName string
// ServiceVarName is the name of the owner service Go interface.
ServiceVarName string
// ServerInterceptors contains the server-side interceptors for this method
ServerInterceptors []*InterceptorData
// ClientInterceptors contains the client-side interceptors for this method
ClientInterceptors []*InterceptorData
}
)

Expand Down Expand Up @@ -134,24 +144,28 @@ func endpointData(service *expr.ServiceExpr) *EndpointsData {
names := make([]string, len(svc.Methods))
for i, m := range svc.Methods {
methods[i] = &EndpointMethodData{
MethodData: m,
ArgName: codegen.Goify(m.VarName, false),
ServiceName: svc.Name,
ServiceVarName: serviceInterfaceName,
ClientVarName: clientStructName,
MethodData: m,
ArgName: codegen.Goify(m.VarName, false),
ServiceName: svc.Name,
ServiceVarName: serviceInterfaceName,
ClientVarName: clientStructName,
ServerInterceptors: m.ServerInterceptors,
ClientInterceptors: m.ClientInterceptors,
}
names[i] = codegen.Goify(m.VarName, false)
}
desc := fmt.Sprintf("%s wraps the %q service endpoints.", endpointsStructName, service.Name)
return &EndpointsData{
Name: service.Name,
Description: desc,
VarName: endpointsStructName,
ClientVarName: clientStructName,
ServiceVarName: serviceInterfaceName,
ClientInitArgs: strings.Join(names, ", "),
Methods: methods,
Schemes: svc.Schemes,
Name: service.Name,
Description: desc,
VarName: endpointsStructName,
ClientVarName: clientStructName,
ServiceVarName: serviceInterfaceName,
ClientInitArgs: strings.Join(names, ", "),
Methods: methods,
Schemes: svc.Schemes,
HasServerInterceptors: len(svc.ServerInterceptors) > 0,
HasClientInterceptors: len(svc.ClientInterceptors) > 0,
}
}

Expand Down
2 changes: 2 additions & 0 deletions codegen/service/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ func TestEndpoint(t *testing.T) {
{"endpoint-streaming-payload-no-result", testdata.StreamingPayloadNoResultMethodDSL, testdata.StreamingPayloadNoResultMethodEndpoint},
{"endpoint-bidirectional-streaming", testdata.BidirectionalStreamingEndpointDSL, testdata.BidirectionalStreamingMethodEndpoint},
{"endpoint-bidirectional-streaming-no-payload", testdata.BidirectionalStreamingNoPayloadMethodDSL, testdata.BidirectionalStreamingNoPayloadMethodEndpoint},
{"endpoint-with-server-interceptor", testdata.EndpointWithServerInterceptorDSL, testdata.EndpointWithServerInterceptor},
{"endpoint-with-multiple-interceptors", testdata.EndpointWithMultipleInterceptorsDSL, testdata.EndpointWithMultipleInterceptors},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
Expand Down
86 changes: 86 additions & 0 deletions codegen/service/example_interceptors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package service

import (
"fmt"
"os"
"path"
"path/filepath"

"goa.design/goa/v3/codegen"
"goa.design/goa/v3/expr"
)

// ExampleInterceptorsFiles returns the files for the example server and client interceptors.
func ExampleInterceptorsFiles(genpkg string, r *expr.RootExpr) []*codegen.File {
var fw []*codegen.File
for _, svc := range r.Services {
if f := exampleInterceptorsFile(genpkg, svc); f != nil {
fw = append(fw, f...)
}
}
return fw
}

// exampleInterceptorsFile returns the example interceptors for the given service.
func exampleInterceptorsFile(genpkg string, svc *expr.ServiceExpr) []*codegen.File {
sdata := Services.Get(svc.Name)
data := map[string]any{
"ServiceName": sdata.Name,
"StructName": sdata.StructName,
"PkgName": "interceptors",
"ServerInterceptors": sdata.ServerInterceptors,
"ClientInterceptors": sdata.ClientInterceptors,
}

var files []*codegen.File

// Generate server interceptor if needed and file doesn't exist
if len(sdata.ServerInterceptors) > 0 {
serverPath := filepath.Join("interceptors", sdata.PathName+"_server.go")
if _, err := os.Stat(serverPath); os.IsNotExist(err) {
files = append(files, &codegen.File{
Path: serverPath,
SectionTemplates: []*codegen.SectionTemplate{
codegen.Header(fmt.Sprintf("%s example server interceptors", sdata.Name), "interceptors", []*codegen.ImportSpec{
{Path: "context"},
{Path: "fmt"},
{Path: "goa.design/clue/log"},
codegen.GoaImport(""),
{Path: path.Join(genpkg, sdata.PathName), Name: sdata.PkgName},
}),
{
Name: "server-interceptor",
Source: readTemplate("example_server_interceptor"),
Data: data,
},
},
})
}
}

// Generate client interceptor if needed and file doesn't exist
if len(sdata.ClientInterceptors) > 0 {
clientPath := filepath.Join("interceptors", sdata.PathName+"_client.go")
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
files = append(files, &codegen.File{
Path: clientPath,
SectionTemplates: []*codegen.SectionTemplate{
codegen.Header(fmt.Sprintf("%s example client interceptors", sdata.Name), "interceptors", []*codegen.ImportSpec{
{Path: "context"},
{Path: "fmt"},
{Path: "goa.design/clue/log"},
codegen.GoaImport(""),
{Path: path.Join(genpkg, sdata.PathName), Name: sdata.PkgName},
}),
{
Name: "client-interceptor",
Source: readTemplate("example_client_interceptor"),
Data: data,
},
},
})
}
}

return files
}
Loading