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.
  • Loading branch information
pawanrawal committed Aug 27, 2020
1 parent 079a0de commit 12b2716
Show file tree
Hide file tree
Showing 10 changed files with 1,684 additions and 831 deletions.
1 change: 1 addition & 0 deletions graphql/e2e/common/common.go
Expand Up @@ -305,6 +305,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
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

0 comments on commit 12b2716

Please sign in to comment.