From d87b1cf912b9b52d1a21a1973f953ddeb7c86e51 Mon Sep 17 00:00:00 2001 From: Nicolas Maquet Date: Tue, 26 Apr 2022 17:12:52 +1200 Subject: [PATCH 1/3] Add WrapGraphQLClientTransport to Plugin API --- client.go | 5 ++++- client_test.go | 8 ++++---- config.go | 2 +- execution.go | 2 +- gateway_test.go | 2 +- plugin.go | 6 ++++++ 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/client.go b/client.go index 984e52e2..d1787f5e 100644 --- a/client.go +++ b/client.go @@ -26,7 +26,7 @@ type GraphQLClient struct { type ClientOpt func(*GraphQLClient) // NewClient creates a new GraphQLClient from the given options. -func NewClient(opts ...ClientOpt) *GraphQLClient { +func NewClient(plugins []Plugin, opts ...ClientOpt) *GraphQLClient { c := &GraphQLClient{ HTTPClient: &http.Client{ Timeout: 5 * time.Second, @@ -38,6 +38,9 @@ func NewClient(opts ...ClientOpt) *GraphQLClient { opt(c) } + for _, plugin := range plugins { + c.HTTPClient.Transport = plugin.WrapGraphQLClientTransport(c.HTTPClient.Transport) + } return c } diff --git a/client_test.go b/client_test.go index e4d3b68e..8cd7d324 100644 --- a/client_test.go +++ b/client_test.go @@ -24,7 +24,7 @@ func TestGraphqlClient(t *testing.T) { }`)) })) - c := NewClient() + c := NewClient([]Plugin{}) var res struct { Root struct { Test string @@ -75,7 +75,7 @@ func TestGraphqlClient(t *testing.T) { }) httpClient := &http.Client{Jar: jar} - c := NewClient(WithHTTPClient(httpClient)) + c := NewClient([]Plugin{}, WithHTTPClient(httpClient)) var res interface{} _ = c.Request(context.Background(), srv.URL, &Request{}, &res) }) @@ -85,7 +85,7 @@ func TestGraphqlClient(t *testing.T) { assert.Equal(t, "My User Agent", r.Header.Get("User-Agent")) })) - c := NewClient(WithUserAgent("My User Agent")) + c := NewClient([]Plugin{}, WithUserAgent("My User Agent")) var res interface{} _ = c.Request(context.Background(), srv.URL, &Request{}, &res) }) @@ -95,7 +95,7 @@ func TestGraphqlClient(t *testing.T) { w.Write([]byte(`{ "data": "long response" }`)) })) - c := NewClient(WithMaxResponseSize(1)) + c := NewClient([]Plugin{}, WithMaxResponseSize(1)) var res interface{} err := c.Request(context.Background(), srv.URL, &Request{}, &res) require.Error(t, err) diff --git a/config.go b/config.go index f3861880..b598f3be 100644 --- a/config.go +++ b/config.go @@ -271,7 +271,7 @@ func (c *Config) Init() error { if c.QueryHTTPClient != nil { queryClientOptions = append(queryClientOptions, WithHTTPClient(c.QueryHTTPClient)) } - queryClient := NewClient(queryClientOptions...) + queryClient := NewClient(c.plugins, queryClientOptions...) es := NewExecutableSchema(c.plugins, c.MaxRequestsPerQuery, queryClient, services...) err = es.UpdateSchema(true) if err != nil { diff --git a/execution.go b/execution.go index 38b08de2..adb0dd08 100644 --- a/execution.go +++ b/execution.go @@ -21,7 +21,7 @@ func NewExecutableSchema(plugins []Plugin, maxRequestsPerQuery int64, client *Gr } if client == nil { - client = NewClient() + client = NewClient(plugins) } return &ExecutableSchema{ diff --git a/gateway_test.go b/gateway_test.go index 63e58778..9c896645 100644 --- a/gateway_test.go +++ b/gateway_test.go @@ -52,7 +52,7 @@ func TestGatewayQuery(t *testing.T) { assert.Equal(t, "Bramble/dev (query)", r.Header.Get("User-Agent")) } })) - client := NewClient(WithUserAgent(GenerateUserAgent("query"))) + client := NewClient([]Plugin{}, WithUserAgent(GenerateUserAgent("query"))) executableSchema := NewExecutableSchema(nil, 50, client, NewService(server.URL)) err := executableSchema.UpdateSchema(true) require.NoError(t, err) diff --git a/plugin.go b/plugin.go index d86c4e85..0c82fdef 100644 --- a/plugin.go +++ b/plugin.go @@ -24,6 +24,7 @@ type Plugin interface { GraphqlQueryPath() (bool, string) ApplyMiddlewarePublicMux(http.Handler) http.Handler ApplyMiddlewarePrivateMux(http.Handler) http.Handler + WrapGraphQLClientTransport(http.RoundTripper) http.RoundTripper } // BasePlugin is an empty plugin. It can be embedded by any plugin as a way to avoid @@ -59,6 +60,11 @@ func (p *BasePlugin) ApplyMiddlewarePrivateMux(h http.Handler) http.Handler { return h } +// WrapGraphQLClientTransport wraps the http.RoundTripper used for GraphQL requests. +func (p *BasePlugin) WrapGraphQLClientTransport(transport http.RoundTripper) http.RoundTripper { + return transport +} + var registeredPlugins = map[string]Plugin{} // RegisterPlugin register a plugin so that it can be enabled via the configuration. From a1e312a1e65e0f89fae200d6422e3e1e9f206b4b Mon Sep 17 00:00:00 2001 From: Adam Sven Johnson Date: Mon, 2 May 2022 10:09:00 +1200 Subject: [PATCH 2/3] Add new client constructor for plugins Changing the signature of NewClient broke several existing Movio library users. --- client.go | 6 +++++- client_test.go | 8 ++++---- config.go | 2 +- execution.go | 2 +- gateway_test.go | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/client.go b/client.go index d1787f5e..aa249217 100644 --- a/client.go +++ b/client.go @@ -26,7 +26,11 @@ type GraphQLClient struct { type ClientOpt func(*GraphQLClient) // NewClient creates a new GraphQLClient from the given options. -func NewClient(plugins []Plugin, opts ...ClientOpt) *GraphQLClient { +func NewClient(opts ...ClientOpt) *GraphQLClient { + return NewClientWithPlugins(nil, opts...) +} + +func NewClientWithPlugins(plugins []Plugin, opts ...ClientOpt) *GraphQLClient { c := &GraphQLClient{ HTTPClient: &http.Client{ Timeout: 5 * time.Second, diff --git a/client_test.go b/client_test.go index 8cd7d324..e4d3b68e 100644 --- a/client_test.go +++ b/client_test.go @@ -24,7 +24,7 @@ func TestGraphqlClient(t *testing.T) { }`)) })) - c := NewClient([]Plugin{}) + c := NewClient() var res struct { Root struct { Test string @@ -75,7 +75,7 @@ func TestGraphqlClient(t *testing.T) { }) httpClient := &http.Client{Jar: jar} - c := NewClient([]Plugin{}, WithHTTPClient(httpClient)) + c := NewClient(WithHTTPClient(httpClient)) var res interface{} _ = c.Request(context.Background(), srv.URL, &Request{}, &res) }) @@ -85,7 +85,7 @@ func TestGraphqlClient(t *testing.T) { assert.Equal(t, "My User Agent", r.Header.Get("User-Agent")) })) - c := NewClient([]Plugin{}, WithUserAgent("My User Agent")) + c := NewClient(WithUserAgent("My User Agent")) var res interface{} _ = c.Request(context.Background(), srv.URL, &Request{}, &res) }) @@ -95,7 +95,7 @@ func TestGraphqlClient(t *testing.T) { w.Write([]byte(`{ "data": "long response" }`)) })) - c := NewClient([]Plugin{}, WithMaxResponseSize(1)) + c := NewClient(WithMaxResponseSize(1)) var res interface{} err := c.Request(context.Background(), srv.URL, &Request{}, &res) require.Error(t, err) diff --git a/config.go b/config.go index b598f3be..25e3df0f 100644 --- a/config.go +++ b/config.go @@ -271,7 +271,7 @@ func (c *Config) Init() error { if c.QueryHTTPClient != nil { queryClientOptions = append(queryClientOptions, WithHTTPClient(c.QueryHTTPClient)) } - queryClient := NewClient(c.plugins, queryClientOptions...) + queryClient := NewClientWithPlugins(c.plugins, queryClientOptions...) es := NewExecutableSchema(c.plugins, c.MaxRequestsPerQuery, queryClient, services...) err = es.UpdateSchema(true) if err != nil { diff --git a/execution.go b/execution.go index adb0dd08..24c2bb10 100644 --- a/execution.go +++ b/execution.go @@ -21,7 +21,7 @@ func NewExecutableSchema(plugins []Plugin, maxRequestsPerQuery int64, client *Gr } if client == nil { - client = NewClient(plugins) + client = NewClientWithPlugins(plugins) } return &ExecutableSchema{ diff --git a/gateway_test.go b/gateway_test.go index 9c896645..63e58778 100644 --- a/gateway_test.go +++ b/gateway_test.go @@ -52,7 +52,7 @@ func TestGatewayQuery(t *testing.T) { assert.Equal(t, "Bramble/dev (query)", r.Header.Get("User-Agent")) } })) - client := NewClient([]Plugin{}, WithUserAgent(GenerateUserAgent("query"))) + client := NewClient(WithUserAgent(GenerateUserAgent("query"))) executableSchema := NewExecutableSchema(nil, 50, client, NewService(server.URL)) err := executableSchema.UpdateSchema(true) require.NoError(t, err) From 5b7e016100c941a87969535a53f8dbbf081d9f77 Mon Sep 17 00:00:00 2001 From: Lucian Jones Date: Mon, 2 May 2022 11:05:40 +1200 Subject: [PATCH 3/3] Add InterceptRequest plugin interception point --- execution.go | 4 ++++ plugin.go | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/execution.go b/execution.go index 24c2bb10..d79c4fb2 100644 --- a/execution.go +++ b/execution.go @@ -138,6 +138,10 @@ func (s *ExecutableSchema) ExecuteQuery(ctx context.Context) *graphql.Response { operation := operationCtx.Operation variables := operationCtx.Variables + for _, plugin := range s.plugins { + plugin.InterceptRequest(ctx, operation.Name, operationCtx.RawQuery, variables) + } + AddField(ctx, "operation.name", operation.Name) AddField(ctx, "operation.type", operation.Operation) diff --git a/plugin.go b/plugin.go index 0c82fdef..5f3242f8 100644 --- a/plugin.go +++ b/plugin.go @@ -1,6 +1,7 @@ package bramble import ( + "context" "encoding/json" "net/http" @@ -25,6 +26,8 @@ type Plugin interface { ApplyMiddlewarePublicMux(http.Handler) http.Handler ApplyMiddlewarePrivateMux(http.Handler) http.Handler WrapGraphQLClientTransport(http.RoundTripper) http.RoundTripper + + InterceptRequest(ctx context.Context, operationName, rawQuery string, variables map[string]interface{}) } // BasePlugin is an empty plugin. It can be embedded by any plugin as a way to avoid @@ -50,6 +53,11 @@ func (p *BasePlugin) GraphqlQueryPath() (bool, string) { return false, "" } +// InterceptRequest is called before bramble starts executing a request. +// It can be used to inspect the unmarshalled GraphQL request bramble receives. +func (p *BasePlugin) InterceptRequest(ctx context.Context, operationName, rawQuery string, variables map[string]interface{}) { +} + // ApplyMiddlewarePublicMux ... func (p *BasePlugin) ApplyMiddlewarePublicMux(h http.Handler) http.Handler { return h