Skip to content

Commit

Permalink
fix(GraphQL): Link xids properly if there are duplicate xids within t…
Browse files Browse the repository at this point in the history
…he same add request. (#6265)

Fixes GRAPHQL-641

See https://discuss.dgraph.io/t/residual-issue-linking-grandchild-in-mutation-with-custom-ids/9613 for the issue details.

If the same nested xid was used as a leaf node within an addType mutation, it was only linked the first time that it was used and not subsequently for later objects. This change fixes that by adding the linkage mutations and only removing the mutation which was creating the object with the xid again.

(cherry picked from commit 12b2716)
  • Loading branch information
pawanrawal committed Aug 27, 2020
1 parent f422913 commit cc31249
Show file tree
Hide file tree
Showing 11 changed files with 1,665 additions and 817 deletions.
1 change: 1 addition & 0 deletions graphql/e2e/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ func RunAll(t *testing.T) {
t.Run("deep mutations", deepMutations)
t.Run("add multiple mutations", testMultipleMutations)
t.Run("deep XID mutations", deepXIDMutations)
t.Run("three level xid", testThreeLevelXID)
t.Run("error in multiple mutations", addMultipleMutationWithOneError)
t.Run("dgraph directive with reverse edge adds data correctly",
addMutationWithReverseDgraphEdge)
Expand Down
254 changes: 254 additions & 0 deletions graphql/e2e/common/mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,260 @@ func deepXIDMutations(t *testing.T) {
deepXIDTest(t, postExecutor)
}

func addComments(t *testing.T, ids []string) {
input := []map[string]interface{}{}
for _, id := range ids {
input = append(input, map[string]interface{}{"id": id})
}

params := &GraphQLParams{
Query: `mutation($input: [AddComment1Input!]!) {
addComment1(input: $input) {
comment1 {
id
}
}
}`,
Variables: map[string]interface{}{
"input": input,
},
}

gqlResponse := postExecutor(t, graphqlURL, params)
RequireNoGQLErrors(t, gqlResponse)
}

func testThreeLevelXID(t *testing.T) {

input := `{
"input": [
{
"id": "post1",
"comments": [
{
"id": "comment1",
"replies": [
{
"id": "reply1"
}
]
}
]
},
{
"id": "post2",
"comments": [
{
"id": "comment2",
"replies": [
{
"id": "reply1"
}
]
}
]
}
]
}`

qinput := make(map[string]interface{})
err := json.Unmarshal([]byte(input), &qinput)
require.NoError(t, err)

addPostParams := &GraphQLParams{
Query: ` mutation($input: [AddPost1Input!]!) {
addPost1(input: $input) {
post1(order: { asc: id }) {
id
comments {
id
replies {
id
}
}
}
}
}`,
Variables: qinput,
}

bothCommentsLinkedToReply := `{
"addPost1": {
"post1": [
{
"id": "post1",
"comments": [
{
"id": "comment1",
"replies": [
{
"id": "reply1"
}
]
}
]
},
{
"id": "post2",
"comments": [
{
"id": "comment2",
"replies": [
{
"id": "reply1"
}
]
}
]
}
]
}
}`

firstCommentLinkedToReply := `{
"addPost1": {
"post1": [
{
"id": "post1",
"comments": [
{
"id": "comment1",
"replies": [
{
"id": "reply1"
}
]
}
]
},
{
"id": "post2",
"comments": [
{
"id": "comment2",
"replies": []
}
]
}
]
}
}`

secondCommentLinkedToReply := `{
"addPost1": {
"post1": [
{
"id": "post1",
"comments": [
{
"id": "comment1",
"replies": []
}
]
},
{
"id": "post2",
"comments": [
{
"id": "comment2",
"replies": [
{
"id": "reply1"
}
]
}
]
}
]
}
}`

noCommentsLinkedToReply := `{
"addPost1": {
"post1": [
{
"id": "post1",
"comments": [
{
"id": "comment1",
"replies": []
}
]
},
{
"id": "post2",
"comments": [
{
"id": "comment2",
"replies": []
}
]
}
]
}
}`

cases := map[string]struct {
Comments []string
Expected string
ExpectedNumDeletedComments int
}{
"2nd level nodes don't exist but third level does": {
[]string{"reply1"},
bothCommentsLinkedToReply,
3,
},
"2nd level and third level nodes don't exist": {
[]string{},
bothCommentsLinkedToReply,
3,
},
"2nd level node exists but third level doesn't": {
[]string{"comment1", "comment2"},
noCommentsLinkedToReply,
2,
},
"2nd level and third level nodes exist": {
[]string{"comment1", "comment2", "reply1"},
noCommentsLinkedToReply,
3,
},
"one 2nd level node exists and third level node exists": {
[]string{"comment1", "reply1"},
secondCommentLinkedToReply,
3,
},
"the other 2nd level node exists and third level node exists": {
[]string{"comment2", "reply1"},
firstCommentLinkedToReply,
3,
},
"one 2nd level node exists and third level node doesn't exist": {
[]string{"comment1"},
secondCommentLinkedToReply,
3,
},
"other 2nd level node exists and third level node doesn't exist": {
[]string{"comment2", "reply1"},
firstCommentLinkedToReply,
3,
},
}

for name, tc := range cases {
t.Run(name, func(t *testing.T) {
addComments(t, tc.Comments)
gqlResponse := postExecutor(t, graphqlURL, addPostParams)
RequireNoGQLErrors(t, gqlResponse)
testutil.CompareJSON(t, tc.Expected, string(gqlResponse.Data))

deleteGqlType(t, "Post1", map[string]interface{}{}, 2, nil)
deleteGqlType(t, "Comment1", map[string]interface{}{}, tc.ExpectedNumDeletedComments,
nil)
})
}
}

func deepXIDTest(t *testing.T, executeRequest requestExecutor) {
newCountry := &country{
Name: "A Country",
Expand Down
2 changes: 2 additions & 0 deletions graphql/e2e/common/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package common

import (
"context"
"fmt"
"testing"

"github.com/dgraph-io/dgo/v200"
Expand Down Expand Up @@ -112,6 +113,7 @@ func SchemaTest(t *testing.T, expectedDgraphSchema string) {
resp, err := client.NewReadOnlyTxn().Query(context.Background(), "schema {}")
require.NoError(t, err)

fmt.Println(string(resp.GetJson()))
testutil.CompareJSON(t, expectedDgraphSchema, string(resp.GetJson()))
}

Expand Down
Loading

0 comments on commit cc31249

Please sign in to comment.