Skip to content

Commit

Permalink
fix: Add support for operationName and variables in HTTP GET (#3292)
Browse files Browse the repository at this point in the history
## Relevant issue(s)

Resolves #3153

## Description

I have made it so that operationName and variables are supported by HTTP
GET requests. I did this by modifying the `ExecRequest` function inside
`http/handler_store.go` such that `operationName` and `variables`
parameters are extracted if they are present.

I added two new tests to `http/handler_store_test.go` which test this
functionality. See:

`TestExecRequest_WithValidQuery_HttpGet_WithOperationName_OmitsErrors`
and
`TestExecRequest_HttpGet_WithVariables_OmitsErrors`

## Tasks

- [x] I made sure the code is well commented, particularly
hard-to-understand areas.
- [x] I made sure the repository-held documentation is changed
accordingly.
- [x] I made sure the pull request title adheres to the conventional
commit style (the subset used in the project can be found in
[tools/configs/chglog/config.yml](tools/configs/chglog/config.yml)).

## How has this been tested?

The platform(s) on which this was tested:
- Windows
  • Loading branch information
ChrisBQu authored Dec 5, 2024
1 parent e4599e8 commit 89f9f41
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 3 deletions.
10 changes: 8 additions & 2 deletions http/handler_ccip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func TestCCIPGet_WithValidData(t *testing.T) {
resHex, err := hex.DecodeString(strings.TrimPrefix(ccipRes.Data, "0x"))
require.NoError(t, err)

assert.JSONEq(t, `{"data": {"User": [{"name": "bob"}]}}`, string(resHex))
assert.JSONEq(t, `{"data": {"User": [{"name": "bob"}, {"name": "adam"}]}}`, string(resHex))
}

func TestCCIPGet_WithSubscription(t *testing.T) {
Expand Down Expand Up @@ -153,7 +153,7 @@ func TestCCIPPost_WithValidData(t *testing.T) {
resHex, err := hex.DecodeString(strings.TrimPrefix(ccipRes.Data, "0x"))
require.NoError(t, err)

assert.JSONEq(t, `{"data": {"User": [{"name": "bob"}]}}`, string(resHex))
assert.JSONEq(t, `{"data": {"User": [{"name": "bob"}, {"name": "adam"}]}}`, string(resHex))
}

func TestCCIPPost_WithInvalidGraphQLRequest(t *testing.T) {
Expand Down Expand Up @@ -210,5 +210,11 @@ func setupDatabase(t *testing.T) client.DB {
err = col.Create(ctx, doc)
require.NoError(t, err)

doc2, err := client.NewDocFromJSON([]byte(`{"name": "adam"}`), col.Definition())
require.NoError(t, err)

err = col.Create(ctx, doc2)
require.NoError(t, err)

return cdb
}
15 changes: 14 additions & 1 deletion http/handler_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,21 @@ func (s *storeHandler) ExecRequest(rw http.ResponseWriter, req *http.Request) {
var request GraphQLRequest
switch {
case req.URL.Query().Get("query") != "":

request.Query = req.URL.Query().Get("query")

request.OperationName = req.URL.Query().Get("operationName")

variablesFromQuery := req.URL.Query().Get("variables")
if variablesFromQuery != "" {
var variables map[string]any
if err := json.Unmarshal([]byte(variablesFromQuery), &variables); err != nil {
responseJSON(rw, http.StatusBadRequest, errorResponse{err})
return
}
request.Variables = variables
}

case req.Body != nil:
if err := requestJSON(req, &request); err != nil {
responseJSON(rw, http.StatusBadRequest, errorResponse{err})
Expand All @@ -294,7 +308,6 @@ func (s *storeHandler) ExecRequest(rw http.ResponseWriter, req *http.Request) {
responseJSON(rw, http.StatusBadRequest, errorResponse{ErrMissingRequest})
return
}

var options []client.RequestOption
if request.OperationName != "" {
options = append(options, client.WithOperationName(request.OperationName))
Expand Down
98 changes: 98 additions & 0 deletions http/handler_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -93,3 +94,100 @@ func TestExecRequest_WithInvalidQuery_HasSpecCompliantErrors(t *testing.T) {
"message": "Cannot query field \"invalid\" on type \"User\".",
}})
}

func TestExecRequest_HttpGet_WithOperationName(t *testing.T) {
cdb := setupDatabase(t)

query := `
query UserQuery {
User {
name
}
}
query UserQueryWithDocID {
User {
_docID
name
}
}
`
operationName := "UserQuery"

encodedQuery := url.QueryEscape(query)
encodedOperationName := url.QueryEscape(operationName)

endpointURL := "http://localhost:9181/api/v0/graphql?query=" + encodedQuery + "&operationName=" + encodedOperationName

req := httptest.NewRequest(http.MethodGet, endpointURL, nil)
rec := httptest.NewRecorder()

handler, err := NewHandler(cdb)
require.NoError(t, err)
handler.ServeHTTP(rec, req)

res := rec.Result()
require.NotNil(t, res.Body)

resData, err := io.ReadAll(res.Body)
require.NoError(t, err)

var gqlResponse map[string]any
err = json.Unmarshal(resData, &gqlResponse)
require.NoError(t, err)

// Ensure the response data contains names, but not the _docID field
expectedJSON := `{
"data": {
"User": [
{"name": "bob"},
{"name": "adam"}
]
}
}`
assert.JSONEq(t, expectedJSON, string(resData))
}

func TestExecRequest_HttpGet_WithVariables(t *testing.T) {
cdb := setupDatabase(t)

query := `query getUser($filter: UserFilterArg) {
User(filter: $filter) {
name
}
}`
operationName := "getUser"
variables := `{"filter":{"name":{"_eq":"bob"}}}`

encodedQuery := url.QueryEscape(query)
encodedOperationName := url.QueryEscape(operationName)
encodedVariables := url.QueryEscape(variables)

endpointURL := "http://localhost:9181/api/v0/graphql?query=" + encodedQuery + "&operationName=" + encodedOperationName + "&variables=" + encodedVariables

req := httptest.NewRequest(http.MethodGet, endpointURL, nil)
rec := httptest.NewRecorder()

handler, err := NewHandler(cdb)
require.NoError(t, err)
handler.ServeHTTP(rec, req)

res := rec.Result()
require.NotNil(t, res.Body)

resData, err := io.ReadAll(res.Body)
require.NoError(t, err)

var gqlResponse map[string]any
err = json.Unmarshal(resData, &gqlResponse)
require.NoError(t, err)

// Ensure only bob is returned, because of the filter variable
expectedJSON := `{
"data": {
"User": [
{"name": "bob"}
]
}
}`
assert.JSONEq(t, expectedJSON, string(resData))
}

0 comments on commit 89f9f41

Please sign in to comment.