Skip to content

Commit

Permalink
Fix/tt 1540 graphql headers (TykTechnologies#3458)
Browse files Browse the repository at this point in the history
* pass original headers with graphql request

* vendor latests graphl-go-tools with fixes for arguments, headers, field validation

* add test for graphql headers injection

* extend default api with graphql headers query
extend test server with headers handler
  • Loading branch information
Sergey Petrunin authored Feb 18, 2021
1 parent d418a7d commit a5a2fb9
Show file tree
Hide file tree
Showing 83 changed files with 347 additions and 15,518 deletions.
1 change: 1 addition & 0 deletions gateway/reverse_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ func (p *ReverseProxy) handleGraphQL(roundTripper *TykRoundTripper, outreq *http
err = errors.New("graphql request is nil")
return
}
gqlRequest.SetHeader(outreq.Header)

var isIntrospection bool
isIntrospection, err = gqlRequest.IsIntrospectionQuery()
Expand Down
37 changes: 37 additions & 0 deletions gateway/reverse_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/jensneuse/graphql-go-tools/pkg/execution/datasource"
"github.com/jensneuse/graphql-go-tools/pkg/graphql"
"github.com/stretchr/testify/require"

"github.com/TykTechnologies/tyk/apidef"
"github.com/TykTechnologies/tyk/config"
Expand Down Expand Up @@ -683,6 +684,42 @@ func TestNopCloseResponseBody(t *testing.T) {
}
}

func TestGraphQL_HeadersInjection(t *testing.T) {
g := StartTest()
defer g.Close()

composedAPI := BuildAPI(func(spec *APISpec) {
spec.Proxy.ListenPath = "/"
spec.GraphQL.Enabled = true
spec.GraphQL.ExecutionMode = apidef.GraphQLExecutionModeExecutionEngine
spec.GraphQL.Version = apidef.GraphQLConfigVersion2

spec.GraphQL.Engine.DataSources = []apidef.GraphQLEngineDataSource{
generateRESTDataSourceV2(func(ds *apidef.GraphQLEngineDataSource, restConfig *apidef.GraphQLEngineDataSourceConfigREST) {
require.NoError(t, json.Unmarshal([]byte(testRESTHeadersDataSourceConfigurationV2), ds))
require.NoError(t, json.Unmarshal(ds.Config, restConfig))
}),
}

spec.GraphQL.TypeFieldConfigurations = nil
})[0]

LoadAPI(composedAPI)

headers := graphql.Request{
Query: "query Query { headers { name value } }",
}

_, _ = g.Run(t, []test.TestCase{
{
Data: headers,
Headers: map[string]string{"injected": "FOO"},
BodyMatch: `"headers":.*{"name":"Injected","value":"FOO"},{"name":"Static","value":"barbaz"}.*`,
Code: http.StatusOK,
},
}...)
}

func TestGraphQL_InternalDataSource(t *testing.T) {
g := StartTest()
defer g.Close()
Expand Down
80 changes: 66 additions & 14 deletions gateway/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,21 +462,26 @@ func ProxyHandler(p *ReverseProxy, apiSpec *APISpec) http.Handler {
}

const (
handlerPathRestDataSource = "/rest-data-source"
handlerPathGraphQLDataSource = "/graphql-data-source"
handlerPathHeadersRestDataSource = "/rest-headers-data-source"

// We need a static port so that the urls can be used in static
// test data, and to prevent the requests from being randomized
// for checksums. Port 16500 should be obscure and unused.
testHttpListen = "127.0.0.1:16500"
// Accepts any http requests on /, only allows GET on /get, etc.
// All return a JSON with request info.
TestHttpAny = "http://" + testHttpListen
TestHttpGet = TestHttpAny + "/get"
testHttpPost = TestHttpAny + "/post"
testGraphQLDataSource = TestHttpAny + "/graphql-data-source"
testRESTDataSource = TestHttpAny + "/rest-data-source"
testHttpJWK = TestHttpAny + "/jwk.json"
testHttpJWKLegacy = TestHttpAny + "/jwk-legacy.json"
testHttpBundles = TestHttpAny + "/bundles/"
testReloadGroup = TestHttpAny + "/groupReload"
TestHttpAny = "http://" + testHttpListen
TestHttpGet = TestHttpAny + "/get"
testHttpPost = TestHttpAny + "/post"
testGraphQLDataSource = TestHttpAny + handlerPathGraphQLDataSource
testRESTDataSource = TestHttpAny + handlerPathRestDataSource
testRESTHeadersDataSource = TestHttpAny + handlerPathHeadersRestDataSource
testHttpJWK = TestHttpAny + "/jwk.json"
testHttpJWKLegacy = TestHttpAny + "/jwk-legacy.json"
testHttpBundles = TestHttpAny + "/bundles/"
testReloadGroup = TestHttpAny + "/groupReload"

// Nothing should be listening on port 16501 - useful for
// testing TCP and HTTP failures.
Expand Down Expand Up @@ -550,8 +555,11 @@ func testHttpHandler() *mux.Router {

r.HandleFunc("/get", handleMethod("GET"))
r.HandleFunc("/post", handleMethod("POST"))
r.HandleFunc("/graphql-data-source", graphqlDataSourceHandler)
r.HandleFunc("/rest-data-source", restDataSourceHandler)

r.HandleFunc(handlerPathGraphQLDataSource, graphqlDataSourceHandler)
r.HandleFunc(handlerPathRestDataSource, restDataSourceHandler)
r.HandleFunc(handlerPathHeadersRestDataSource, restHeadersDataSourceHandler)

r.HandleFunc("/ws", wsHandler)
r.HandleFunc("/jwk.json", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, jwkTestJson)
Expand Down Expand Up @@ -603,6 +611,21 @@ func graphqlDataSourceHandler(w http.ResponseWriter, r *http.Request) {
}`))
}

func restHeadersDataSourceHandler(w http.ResponseWriter, r *http.Request) {
type KeyVal struct {
Name string `json:"name"`
Value string `json:"value"`
}

var headers []KeyVal
for name, values := range r.Header {
for _, value := range values {
headers = append(headers, KeyVal{name, value})
}
}
json.NewEncoder(w).Encode(headers)
}

func restDataSourceHandler(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`[
{
Expand Down Expand Up @@ -1067,11 +1090,18 @@ const sampleAPI = `{
"field_name": "people",
"disable_default_mapping": true,
"path": [""]
},
{
"type_name": "Query",
"field_name": "headers",
"disable_default_mapping": true,
"path": [""]
}
],
"data_sources": [
` + testRESTDataSourceConfigurationV2 + `,
` + testGraphQLDataSourceConfigurationV2 + `
` + testGraphQLDataSourceConfigurationV2 + `,
` + testRESTHeadersDataSourceConfigurationV2 + `
]
},
"playground": {
Expand All @@ -1081,8 +1111,10 @@ const sampleAPI = `{
}
}`

const testComposedSchema = "type Query {people: [Person] countries: [Country]} type Person {name: String country: Country} " +
"type Country {code: String name: String}"
const testComposedSchema = "type Query {people: [Person] countries: [Country] headers: [Header]} " +
"type Person {name: String country: Country} " +
"type Country {code: String name: String} " +
"type Header {name:String value: String}"

const testGraphQLDataSourceConfigurationV2 = `
{
Expand Down Expand Up @@ -1116,6 +1148,26 @@ const testGraphQLDataSourceConfiguration = `
}
`

const testRESTHeadersDataSourceConfigurationV2 = `
{
"kind": "REST",
"name": "headers",
"internal": true,
"root_fields": [
{ "type": "Query", "fields": ["headers"] }
],
"config": {
"url": "` + testRESTHeadersDataSource + `",
"method": "GET",
"headers": {
"static": "barbaz",
"injected": "{{ .request.header.injected }}"
},
"query": [],
"body": ""
}
}`

const testRESTDataSourceConfigurationV2 = `
{
"kind": "REST",
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,6 @@ require (
rsc.io/letsencrypt v0.0.2
)

replace github.com/jensneuse/graphql-go-tools => github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20210128132753-3d71d69ea680
replace github.com/jensneuse/graphql-go-tools => github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20210211151733-c8f2c4de645d

//replace github.com/jensneuse/graphql-go-tools => ../graphql-go-tools
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20201123105516-733fb8df3c8a
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20201123105516-733fb8df3c8a/go.mod h1:T6j+5gC7NkmZOMLcxR4qk+nK81LMWW7FTsj/hP3X/Ds=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20210128132753-3d71d69ea680 h1:EmNGfB/0FH7F46dRZgiik29Sl9FgVoL61bvpcLrmdxA=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20210128132753-3d71d69ea680/go.mod h1:zm0OEQyZ8F2jzX1Rqm6lOjt6u8hCPW86YOjGvPMpVe0=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20210211151733-c8f2c4de645d h1:zbxw5ZzqcZxPzpPAdHQUNtTxi2SCwhjpHfzX22fH5TU=
github.com/TykTechnologies/graphql-go-tools v1.6.2-0.20210211151733-c8f2c4de645d/go.mod h1:zm0OEQyZ8F2jzX1Rqm6lOjt6u8hCPW86YOjGvPMpVe0=
github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c h1:j6fd0Fz1R4oSWOmcooGjrdahqrML+btQ+PfEJw8SzbA=
github.com/TykTechnologies/leakybucket v0.0.0-20170301023702-71692c943e3c/go.mod h1:GnHUbsQx+ysI10osPhUdTmsxcE7ef64cVp38Fdyd7e0=
github.com/TykTechnologies/murmur3 v0.0.0-20180602122059-1915e687e465 h1:A2gBjoX8aF0G3GHEpHyj2f0ixuPkCgcGqmPdKHSkW+0=
Expand Down
72 changes: 0 additions & 72 deletions vendor/github.com/certifi/gocertifi/gen.go

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a5a2fb9

Please sign in to comment.