Skip to content

Commit

Permalink
fix(GraphQL): fix bug in custom resolver, now body need not have all …
Browse files Browse the repository at this point in the history
…the fields. (#6054)

This PR fix bug in custom resolver, now body need not have all the fields(both nullable and non-nullable).
  • Loading branch information
JatinDev543 authored Jul 29, 2020
1 parent 27ddeca commit 72e6a47
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 14 deletions.
81 changes: 80 additions & 1 deletion graphql/e2e/custom_logic/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,20 @@ func postFavMoviesHandler(w http.ResponseWriter, r *http.Request) {
check2(w.Write(getDefaultResponse()))
}

func postFavMoviesWithBodyHandler(w http.ResponseWriter, r *http.Request) {
err := verifyRequest(r, expectedRequest{
method: http.MethodPost,
urlSuffix: "/0x123?name=Author",
body: `{"id":"0x123","name":"Author"}`,
headers: nil,
})
if err != nil {
check2(w.Write([]byte(err.Error())))
return
}
check2(w.Write(getDefaultResponse()))
}

func verifyHeadersHandler(w http.ResponseWriter, r *http.Request) {
err := verifyRequest(r, expectedRequest{
method: http.MethodGet,
Expand Down Expand Up @@ -384,6 +398,36 @@ func favMoviesCreateHandler(w http.ResponseWriter, r *http.Request) {
]`)))
}

func favMoviesCreateWithNullBodyHandler(w http.ResponseWriter, r *http.Request) {
err := verifyRequest(r, expectedRequest{
method: http.MethodPost,
urlSuffix: "/favMoviesCreateWithNullBody",
body: `{"movies":[{"director":[{"name":"Dir1"}],"name":"Mov1"},{"name":null}]}`,
headers: nil,
})
if err != nil {
check2(w.Write([]byte(err.Error())))
return
}

check2(w.Write([]byte(`[
{
"id": "0x1",
"name": "Mov1",
"director": [
{
"id": "0x2",
"name": "Dir1"
}
]
},
{
"id": "0x3",
"name": null
}
]`)))
}

func favMoviesUpdateHandler(w http.ResponseWriter, r *http.Request) {
err := verifyRequest(r, expectedRequest{
method: http.MethodPatch,
Expand Down Expand Up @@ -792,6 +836,39 @@ func userNameHandler(w http.ResponseWriter, r *http.Request) {
nameHandler(w, r, &inputBody)
}

func userNameWithoutAddressHandler(w http.ResponseWriter, r *http.Request) {
expectedRequest := expectedRequest{
body: `{"uid":"0x5"}`,
}

b, err := ioutil.ReadAll(r.Body)
fmt.Println(b, err)
if err != nil {
err = getError("Unable to read request body", err.Error())
check2(w.Write([]byte(err.Error())))
return
}

if string(b) != expectedRequest.body {
err = getError("Unexpected value for request body", string(b))
}
if err != nil {
check2(w.Write([]byte(err.Error())))
return
}

var inputBody input
if err := json.Unmarshal(b, &inputBody); err != nil {
fmt.Println("while doing JSON unmarshal: ", err)
check2(w.Write([]byte(err.Error())))
return
}

n := fmt.Sprintf(`"%s"`, inputBody.Name())
check2(fmt.Fprint(w, n))

}

func carHandler(w http.ResponseWriter, r *http.Request) {
var inputBody input
err := getInput(r, &inputBody)
Expand Down Expand Up @@ -1134,6 +1211,7 @@ func main() {
// for queries
http.HandleFunc("/favMovies/", getFavMoviesHandler)
http.HandleFunc("/favMoviesPost/", postFavMoviesHandler)
http.HandleFunc("/favMoviesPostWithBody/", postFavMoviesWithBodyHandler)
http.HandleFunc("/verifyHeaders", verifyHeadersHandler)
http.HandleFunc("/verifyCustomNameHeaders", verifyCustomNameHeadersHandler)
http.HandleFunc("/twitterfollowers", twitterFollwerHandler)
Expand All @@ -1142,7 +1220,7 @@ func main() {
http.HandleFunc("/favMoviesCreate", favMoviesCreateHandler)
http.HandleFunc("/favMoviesUpdate/", favMoviesUpdateHandler)
http.HandleFunc("/favMoviesDelete/", favMoviesDeleteHandler)

http.HandleFunc("/favMoviesCreateWithNullBody", favMoviesCreateWithNullBodyHandler)
// The endpoints below are for testing custom resolution of fields within type definitions.
// for testing batch mode
http.HandleFunc("/userNames", userNamesHandler)
Expand All @@ -1154,6 +1232,7 @@ func main() {

// for testing single mode
http.HandleFunc("/userName", userNameHandler)
http.HandleFunc("/userNameWithoutAddress", userNameWithoutAddressHandler)
http.HandleFunc("/checkHeadersForUserName", userNameHandlerWithHeaders)
http.HandleFunc("/car", carHandler)
http.HandleFunc("/class", classHandler)
Expand Down
213 changes: 213 additions & 0 deletions graphql/e2e/custom_logic/custom_logic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (
alphaURL = "http://localhost:8180/graphql"
alphaAdminURL = "http://localhost:8180/admin"
subscriptionEndpoint = "ws://localhost:8180/graphql"
groupOnegRPC = "localhost:9180"
customTypes = `type MovieDirector @remote {
id: ID!
name: String!
Expand Down Expand Up @@ -158,6 +159,42 @@ func TestCustomPostQuery(t *testing.T) {
require.JSONEq(t, expected, string(result.Data))
}

func TestCustomPostQueryWithBody(t *testing.T) {
schema := customTypes + `
type Query {
myFavoriteMoviesPost(id: ID!, name: String!, num: Int): [Movie] @custom(http: {
url: "http://mock:8888/favMoviesPostWithBody/$id?name=$name",
body:"{id:$id,name:$name,num:$num}"
method: "POST"
})
}`
updateSchemaRequireNoGQLErrors(t, schema)
time.Sleep(2 * time.Second)

query := `
query {
myFavoriteMoviesPost(id: "0x123", name: "Author") {
id
name
director {
id
name
}
}
}`
params := &common.GraphQLParams{
Query: query,
}

result := params.ExecuteAsPost(t, alphaURL)
require.Nil(t, result.Errors)

expected := `{"myFavoriteMoviesPost":[{"id":"0x3","name":"Star Wars","director":
[{"id":"0x4","name":"George Lucas"}]},{"id":"0x5","name":"Star Trek","director":
[{"id":"0x6","name":"J.J. Abrams"}]}]}`
require.JSONEq(t, expected, string(result.Data))
}

func TestCustomQueryShouldForwardHeaders(t *testing.T) {
schema := customTypes + `
type Query {
Expand Down Expand Up @@ -1012,6 +1049,103 @@ func TestCustomFieldsShouldForwardHeaders(t *testing.T) {
require.Nilf(t, result.Errors, "%+v", result.Errors)
}

func TestCustomFieldsShouldSkipNonEmptyVariable(t *testing.T) {
schema := `
type User {
id: ID!
address:String
name: String
@custom(
http: {
url: "http://mock:8888/userName"
method: "GET"
body: "{uid: $id,address:$address}"
mode: SINGLE,
secretHeaders: ["GITHUB-API-TOKEN"]
}
)
age: Int! @search
}
# Dgraph.Secret GITHUB-API-TOKEN "some-api-token"
`
updateSchemaRequireNoGQLErrors(t, schema)
time.Sleep(2 * time.Second)

users := addUsers(t)
queryUser := `
query ($id: [ID!]){
queryUser(filter: {id: $id}, order: {asc: age}) {
name
age
}
}`
params := &common.GraphQLParams{
Query: queryUser,
Variables: map[string]interface{}{"id": []interface{}{
users[0].ID, users[1].ID, users[2].ID}},
}

result := params.ExecuteAsPost(t, alphaURL)
require.Nilf(t, result.Errors, "%+v", result.Errors)
}

func TestCustomFieldsShouldPassBody(t *testing.T) {
dg, err := testutil.DgraphClient(groupOnegRPC)
require.NoError(t, err)
testutil.DropAll(t, dg)
schema := `
type User {
id: String! @id @search(by: [hash, regexp])
address:String
name: String
@custom(
http: {
url: "http://mock:8888/userNameWithoutAddress"
method: "GET"
body: "{uid: $id,address:$address}"
mode: SINGLE,
secretHeaders: ["GITHUB-API-TOKEN"]
}
)
age: Int! @search
}
# Dgraph.Secret GITHUB-API-TOKEN "some-api-token"
`
updateSchemaRequireNoGQLErrors(t, schema)
time.Sleep(2 * time.Second)

params := &common.GraphQLParams{
Query: `mutation addUser {
addUser(input: [{ id:"0x5", age: 10 }]) {
user {
id
age
}
}
}`,
}

result := params.ExecuteAsPost(t, alphaURL)
common.RequireNoGQLErrors(t, result)

queryUser := `
query ($id: String!){
queryUser(filter: {id: {eq: $id}}) {
name
age
}
}`

params = &common.GraphQLParams{
Query: queryUser,
Variables: map[string]interface{}{"id": "0x5"},
}

result = params.ExecuteAsPost(t, alphaURL)
require.Nilf(t, result.Errors, "%+v", result.Errors)
}

func TestCustomFieldsShouldBeResolved(t *testing.T) {
// This test adds data, modifies the schema multiple times and fetches the data.
// It has the following modes.
Expand Down Expand Up @@ -1732,6 +1866,85 @@ func TestCustomPostMutation(t *testing.T) {
require.JSONEq(t, expected, string(result.Data))
}

func TestCustomPostMutationNullInBody(t *testing.T) {
schema := `type MovieDirector @remote {
id: ID!
name: String!
directed: [Movie]
}
type Movie @remote {
id: ID!
name: String
director: [MovieDirector]
}
input MovieDirectorInput {
id: ID
name: String
directed: [MovieInput]
}
input MovieInput {
id: ID
name: String
director: [MovieDirectorInput]
}
type Mutation {
createMyFavouriteMovies(input: [MovieInput!]): [Movie] @custom(http: {
url: "http://mock:8888/favMoviesCreateWithNullBody",
method: "POST",
body: "{ movies: $input}"
})
}`
updateSchemaRequireNoGQLErrors(t, schema)
time.Sleep(2 * time.Second)

params := &common.GraphQLParams{
Query: `
mutation createMovies($movs: [MovieInput!]) {
createMyFavouriteMovies(input: $movs) {
id
name
director {
id
name
}
}
}`,
Variables: map[string]interface{}{
"movs": []interface{}{
map[string]interface{}{
"name": "Mov1",
"director": []interface{}{map[string]interface{}{"name": "Dir1"}},
},
map[string]interface{}{"name": nil},
}},
}

result := params.ExecuteAsPost(t, alphaURL)
common.RequireNoGQLErrors(t, result)

expected := `
{
"createMyFavouriteMovies": [
{
"id": "0x1",
"name": "Mov1",
"director": [
{
"id": "0x2",
"name": "Dir1"
}
]
},
{
"id": "0x3",
"name": null,
"director": []
}
]
}`
require.JSONEq(t, expected, string(result.Data))
}

func TestCustomPatchMutation(t *testing.T) {
schema := customTypes + `
input MovieDirectorInput {
Expand Down
1 change: 1 addition & 0 deletions graphql/resolve/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,7 @@ func (hr *httpResolver) rewriteAndExecute(ctx context.Context, field schema.Fiel
}
body = string(b)
}

b, err := makeRequest(hr.Client, hrc.Method, hrc.URL, body, hrc.ForwardHeaders)
if err != nil {
return emptyResult(externalRequestError(err, field))
Expand Down
Loading

0 comments on commit 72e6a47

Please sign in to comment.