From ed302d5b1465eb2d8b45b9e22bce19978480c389 Mon Sep 17 00:00:00 2001 From: Jatin Dev <64803093+JatinDevDG@users.noreply.github.com> Date: Sun, 18 Apr 2021 02:47:27 +0530 Subject: [PATCH] Feat(GraphQL): This PR allows @id field in interface to be unique across all the implementing types. (#7710) Currently, @id fields in the interface are unique only in one implementing type. But there are several use cases that require @id field to be unique across all the implementation types. Also currently. get a query on the interface can result in unexpected errors as we can have multiple implementing types have the same value for that @id field. Now we are allowing the @id field in the interface to be unique across all the implementing types. In order to do this, we have added a new argument interface of boolean type in the @id field. Whenever a @id field in interface type has an interface argument set then its value will be unique across all the implementing types. Users will get errors if they try to add a node with such a field and there is already a node with the same value of that field even in some other implementing types. This is true for other scenarios like adding nested value or while using upserts. If the interface argument is not present or its value is false then that field will be unique only for one implementing type. But such fields won't be allowed in argument to get query on interface in the future, see this PR also #7602 Example Schema, interface LibraryItem { refID: String! @id // This field is unique only for one implementing type itemID: String! @id(interface:true) // This field will be unique over all the implementing types inheriting this interface } type Book implements LibraryItem { title: String author: String } Related discuss Post: https://discuss.dgraph.io/t/uniqueness-for-id-fields-on-interface/13294 --- graphql/e2e/auth/add_mutation_test.go | 47 +- graphql/e2e/auth/auth_test.go | 2 +- graphql/e2e/auth/debug_off/debugoff_test.go | 61 ++ graphql/e2e/auth/schema.graphql | 95 ++-- graphql/e2e/common/common.go | 2 + graphql/e2e/common/mutation.go | 208 ++++++- graphql/e2e/common/query.go | 85 ++- graphql/e2e/directives/schema.graphql | 32 ++ graphql/e2e/directives/schema_response.json | 192 +++++++ graphql/e2e/normal/schema.graphql | 33 ++ graphql/e2e/normal/schema_response.json | 194 ++++++- .../schema/apollo_service_response.graphql | 2 +- graphql/e2e/schema/generatedSchema.graphql | 2 +- graphql/resolve/add_mutation_test.yaml | 531 ++++++++++++++++++ graphql/resolve/mutation_rewriter.go | 139 ++++- graphql/resolve/query_test.yaml | 423 ++++++-------- graphql/resolve/schema.graphql | 298 +++++----- graphql/schema/gqlschema.go | 29 +- graphql/schema/gqlschema_test.yml | 46 ++ graphql/schema/rules.go | 14 + .../output/auth-directive.graphql | 2 +- .../output/custom-directive.graphql | 2 +- .../output/extended-types.graphql | 2 +- .../output/generate-directive.graphql | 2 +- .../output/single-extended-type.graphql | 2 +- .../input/interface-with-id-directive.graphql | 3 +- .../output/apollo-federation.graphql | 2 +- .../output/auth-on-interfaces.graphql | 2 +- .../schemagen/output/authorization.graphql | 2 +- .../output/comments-and-descriptions.graphql | 2 +- ...custom-dql-query-with-subscription.graphql | 2 +- .../schemagen/output/custom-mutation.graphql | 2 +- .../output/custom-nested-types.graphql | 2 +- .../output/custom-query-mixed-types.graphql | 2 +- .../custom-query-not-dgraph-type.graphql | 2 +- .../custom-query-with-dgraph-type.graphql | 2 +- .../schemagen/output/deprecated.graphql | 2 +- ...e-on-concrete-type-with-interfaces.graphql | 2 +- ...-reverse-directive-with-interfaces.graphql | 2 +- .../output/field-with-id-directive.graphql | 2 +- .../field-with-multiple-@id-fields.graphql | 2 +- ...erse-predicate-in-dgraph-directive.graphql | 2 +- .../filter-cleanSchema-all-empty.graphql | 2 +- .../filter-cleanSchema-circular.graphql | 2 +- ...filter-cleanSchema-custom-mutation.graphql | 2 +- .../filter-cleanSchema-directLink.graphql | 2 +- .../output/generate-directive.graphql | 2 +- .../schemagen/output/geo-type.graphql | 2 +- ...se-with-interface-having-directive.graphql | 2 +- .../output/hasInverse-with-interface.graphql | 2 +- ...Inverse-with-type-having-directive.graphql | 2 +- .../schemagen/output/hasInverse.graphql | 2 +- .../hasInverse_withSubscription.graphql | 2 +- .../schemagen/output/hasfilter.graphql | 2 +- .../ignore-unsupported-directive.graphql | 2 +- .../output/interface-with-dgraph-pred.graphql | 2 +- .../interface-with-id-directive.graphql | 24 +- .../output/interface-with-no-ids.graphql | 2 +- ...interfaces-with-types-and-password.graphql | 2 +- .../output/interfaces-with-types.graphql | 2 +- .../schemagen/output/lambda-directive.graphql | 2 +- .../schemagen/output/language-tags.graphql | 517 +++++++++++++++++ .../no-id-field-with-searchables.graphql | 2 +- .../schemagen/output/no-id-field.graphql | 2 +- .../schemagen/output/password-type.graphql | 2 +- .../testdata/schemagen/output/random.graphql | 2 +- .../output/searchables-references.graphql | 2 +- .../schemagen/output/searchables.graphql | 2 +- .../output/single-type-with-enum.graphql | 2 +- .../schemagen/output/single-type.graphql | 2 +- ...ype-implements-multiple-interfaces.graphql | 2 +- .../schemagen/output/type-reference.graphql | 2 +- .../type-with-arguments-on-field.graphql | 2 +- ...e-with-custom-field-on-dgraph-type.graphql | 2 +- ...-with-custom-fields-on-remote-type.graphql | 2 +- .../output/type-without-orderables.graphql | 2 +- .../testdata/schemagen/output/union.graphql | 2 +- graphql/schema/wrappers.go | 56 +- 78 files changed, 2581 insertions(+), 562 deletions(-) create mode 100755 graphql/schema/testdata/schemagen/output/language-tags.graphql diff --git a/graphql/e2e/auth/add_mutation_test.go b/graphql/e2e/auth/add_mutation_test.go index 8abe0d763c7..70be41f6b32 100644 --- a/graphql/e2e/auth/add_mutation_test.go +++ b/graphql/e2e/auth/add_mutation_test.go @@ -1002,7 +1002,7 @@ func TestUpsertMutationsWithRBAC(t *testing.T) { require.Error(t, gqlResponse.Errors) require.Equal(t, len(gqlResponse.Errors), 1) require.Contains(t, gqlResponse.Errors[0].Error(), - "GraphQL debug: id tweet1 already exists for field id inside type Tweets") + " GraphQL debug: id Tweets already exists for field id inside type tweet1") } else { common.RequireNoGQLErrors(t, gqlResponse) require.JSONEq(t, tcase.result, string(gqlResponse.Data)) @@ -1119,3 +1119,48 @@ func TestUpsertWithDeepAuth(t *testing.T) { filter = map[string]interface{}{"code": map[string]interface{}{"eq": "UK"}} common.DeleteGqlType(t, "State", filter, 1, nil) } + +func TestAddMutationWithAuthOnIDFieldHavingInterfaceArg(t *testing.T) { + // add Library Member + addLibraryMemberParams := &common.GraphQLParams{ + Query: `mutation addLibraryMember($input: [AddLibraryMemberInput!]!) { + addLibraryMember(input: $input, upsert: false) { + numUids + } + }`, + Variables: map[string]interface{}{"input": []interface{}{ + map[string]interface{}{ + "refID": "101", + "name": "Alice", + "readHours": "4d2hr", + }}, + }, + } + + gqlResponse := addLibraryMemberParams.ExecuteAsPost(t, common.GraphqlURL) + common.RequireNoGQLErrors(t, gqlResponse) + // add sports member should return error but in debug mode + // because interface type have auth rules defined on it + addSportsMemberParams := &common.GraphQLParams{ + Query: `mutation addSportsMember($input: [AddSportsMemberInput!]!) { + addSportsMember(input: $input, upsert: false) { + numUids + } + }`, + Variables: map[string]interface{}{"input": []interface{}{ + map[string]interface{}{ + "refID": "101", + "name": "Bob", + "plays": "football and cricket", + }}, + }, + } + + gqlResponse = addSportsMemberParams.ExecuteAsPost(t, common.GraphqlURL) + require.Contains(t, gqlResponse.Errors[0].Error(), + " GraphQL debug: id 101 already exists for field refID in some other"+ + " implementing type of interface Member") + + // cleanup + common.DeleteGqlType(t, "LibraryMember", map[string]interface{}{}, 1, nil) +} diff --git a/graphql/e2e/auth/auth_test.go b/graphql/e2e/auth/auth_test.go index f89bbbd510b..6d47eba7b47 100644 --- a/graphql/e2e/auth/auth_test.go +++ b/graphql/e2e/auth/auth_test.go @@ -354,7 +354,7 @@ func TestAddMutationWithXid(t *testing.T) { require.Error(t, gqlResponse.Errors) require.Equal(t, len(gqlResponse.Errors), 1) require.Contains(t, gqlResponse.Errors[0].Error(), - "GraphQL debug: id tweet1 already exists for field id inside type Tweets") + "GraphQL debug: id Tweets already exists for field id inside type tweet1") // Clear the tweet. tweet.DeleteByID(t, user, metaInfo) diff --git a/graphql/e2e/auth/debug_off/debugoff_test.go b/graphql/e2e/auth/debug_off/debugoff_test.go index 2ed6bdb9282..aaa824830dd 100644 --- a/graphql/e2e/auth/debug_off/debugoff_test.go +++ b/graphql/e2e/auth/debug_off/debugoff_test.go @@ -122,6 +122,67 @@ func TestAddMutationWithXid(t *testing.T) { tweet.DeleteByID(t, user, metaInfo) } +func TestAddMutationWithAuthOnIDFieldHavingInterfaceArg(t *testing.T) { + // add Library Member + addLibraryMemberParams := &common.GraphQLParams{ + Query: `mutation addLibraryMember($input: [AddLibraryMemberInput!]!) { + addLibraryMember(input: $input, upsert: false) { + numUids + } + }`, + Variables: map[string]interface{}{"input": []interface{}{ + map[string]interface{}{ + "refID": "101", + "name": "Alice", + "readHours": "4d2hr", + }}, + }, + } + + gqlResponse := addLibraryMemberParams.ExecuteAsPost(t, common.GraphqlURL) + common.RequireNoGQLErrors(t, gqlResponse) + + // add SportsMember should return error but in debug mode + // because interface type have auth rules defined on it + var resultLibraryMember struct { + AddLibraryMember struct { + NumUids int + } + } + err := json.Unmarshal(gqlResponse.Data, &resultLibraryMember) + require.NoError(t, err) + require.Equal(t, 1, resultLibraryMember.AddLibraryMember.NumUids) + + addSportsMemberParams := &common.GraphQLParams{ + Query: `mutation addSportsMember($input: [AddSportsMemberInput!]!) { + addSportsMember(input: $input, upsert: false) { + numUids + } + }`, + Variables: map[string]interface{}{"input": []interface{}{ + map[string]interface{}{ + "refID": "101", + "name": "Bob", + "plays": "football and cricket", + }}, + }, + } + + gqlResponse = addSportsMemberParams.ExecuteAsPost(t, common.GraphqlURL) + common.RequireNoGQLErrors(t, gqlResponse) + var resultSportsMember struct { + AddSportsMember struct { + NumUids int + } + } + err = json.Unmarshal(gqlResponse.Data, &resultSportsMember) + require.NoError(t, err) + require.Equal(t, 0, resultSportsMember.AddSportsMember.NumUids) + + // cleanup + common.DeleteGqlType(t, "LibraryMember", map[string]interface{}{}, 1, nil) +} + func TestMain(m *testing.M) { schemaFile := "../schema.graphql" schema, err := os.ReadFile(schemaFile) diff --git a/graphql/e2e/auth/schema.graphql b/graphql/e2e/auth/schema.graphql index 69a6146df7b..b9b928f655b 100644 --- a/graphql/e2e/auth/schema.graphql +++ b/graphql/e2e/auth/schema.graphql @@ -620,43 +620,43 @@ type Author { interface Post @secret(field: "pwd") @auth( password: { rule: "{$ROLE: { eq: \"Admin\" } }"}, query: { rule: """ - query($USER: String!) { + query($USER: String!) { queryPost{ author(filter: {name: {eq: $USER}}){ name } - } + } }""" }, add: { rule: """ - query($USER: String!) { + query($USER: String!) { queryPost{ author(filter: {name: {eq: $USER}}){ name } - } + } }""" }, delete: { rule: """ - query($USER: String!) { + query($USER: String!) { queryPost{ author(filter: {name: {eq: $USER}}){ name } - } + } }""" }, update: { rule: """ - query($USER: String!) { + query($USER: String!) { queryPost{ author(filter: {name: {eq: $USER}}){ name } - } + } }""" } ){ id: ID! text: String! @search(by: [exact]) topic: String datePublished: DateTime @search - author: Author! + author: Author! } interface MsgPost @auth( @@ -678,28 +678,28 @@ type Question implements Post @auth( } }""" }, query:{ rule: """ - query($ANS: Boolean!) { - queryQuestion(filter: { answered: $ANS } ) { - id - } + query($ANS: Boolean!) { + queryQuestion(filter: { answered: $ANS } ) { + id + } }""" }, add:{ rule: """ - query($ANS: Boolean!) { - queryQuestion(filter: { answered: $ANS } ) { - id - } + query($ANS: Boolean!) { + queryQuestion(filter: { answered: $ANS } ) { + id + } }""" }, delete:{ rule: """ - query($ANS: Boolean!) { - queryQuestion(filter: { answered: $ANS } ) { - id - } + query($ANS: Boolean!) { + queryQuestion(filter: { answered: $ANS } ) { + id + } }""" }, update:{ rule: """ - query($ANS: Boolean!) { - queryQuestion(filter: { answered: $ANS } ) { - id - } + query($ANS: Boolean!) { + queryQuestion(filter: { answered: $ANS } ) { + id + } }""" }, ){ answered: Boolean @search @@ -726,7 +726,7 @@ type Answer implements Post { interface A { id: ID! fieldA: String @search(by: [exact]) - random: String + random: String } type B implements A { @@ -735,16 +735,16 @@ type B implements A { type C implements A @auth( query:{ rule: """ - query($ANS: Boolean!) { - queryC(filter: { fieldC: $ANS } ) { - id - } + query($ANS: Boolean!) { + queryC(filter: { fieldC: $ANS } ) { + id + } }""" }, delete:{ rule: """ - query($ANS: Boolean!) { - queryC(filter: { fieldC: $ANS } ) { - id - } + query($ANS: Boolean!) { + queryC(filter: { fieldC: $ANS } ) { + id + } }""" } ){ fieldC: Boolean @search @@ -780,10 +780,10 @@ type Book @auth( type Mission @key(fields: "id") @auth( query:{ rule: """ - query($USER: String!) { - queryMission(filter: { supervisorName: {eq: $USER} } ) { - id - } + query($USER: String!) { + queryMission(filter: { supervisorName: {eq: $USER} } ) { + id + } }""" } ){ id: String! @id @@ -864,6 +864,25 @@ type Person name: String! } +interface Member @auth( + query: { rule: "{$ROLE: { eq: \"ADMIN\" } }" }, +){ + refID: String! @id (interface:true) + name: String! @id (interface:false) +} + +type SportsMember implements Member { + plays: String + playerRating: Int +} + +type LibraryMember implements Member { + interests: [String] + readHours: String +} + + + # union testing - start enum AnimalCategory { Fish diff --git a/graphql/e2e/common/common.go b/graphql/e2e/common/common.go index 7c50cb58ef6..a2884bcf4e4 100644 --- a/graphql/e2e/common/common.go +++ b/graphql/e2e/common/common.go @@ -868,6 +868,7 @@ func RunAll(t *testing.T) { t.Run("query id directive with int", idDirectiveWithInt) t.Run("query id directive with int64", idDirectiveWithInt64) t.Run("query filter ID values coercion to List", queryFilterWithIDInputCoercion) + t.Run("query @id field with interface arg on interface", queryWithIDFieldAndInterfaceArg) // mutation tests t.Run("add mutation", addMutation) @@ -927,6 +928,7 @@ func RunAll(t *testing.T) { t.Run("input coercion to list", inputCoerciontoList) t.Run("multiple external Id's tests", multipleXidsTests) t.Run("Upsert Mutation Tests", upsertMutationTests) + t.Run("add mutation with @id field and interface arg", addMutationWithIDFieldHavingInterfaceArg) // error tests t.Run("graphql completion on", graphQLCompletionOn) diff --git a/graphql/e2e/common/mutation.go b/graphql/e2e/common/mutation.go index a0df3104846..b468c858f97 100644 --- a/graphql/e2e/common/mutation.go +++ b/graphql/e2e/common/mutation.go @@ -4820,7 +4820,7 @@ func filterInMutationsWithArrayForAndOr(t *testing.T) { query: `mutation { addpost1(input: [{title: "Dgraph", numLikes: 100}]) { post1(filter:{and:[{title:{eq: "Dgraph"}},{or:{numLikes:{eq: 100}}}]}) { - title + title numLikes } } @@ -5228,7 +5228,7 @@ func threeLevelDoubleXID(t *testing.T) { name region { id - name + name district { id name @@ -5692,7 +5692,7 @@ func multipleXidsTests(t *testing.T) { query: `mutation { addEmployer(input: [{ company: "Slash", worker: { reg_No: 3, emp_Id: "E04" } }]) { employer { - company + company worker { name reg_No @@ -6037,3 +6037,205 @@ func upsertMutationTests(t *testing.T) { filter = GetXidFilter("xcode", []interface{}{"S1", "S10"}) deleteState(t, filter, 2, nil) } + +func addMutationWithIDFieldHavingInterfaceArg(t *testing.T) { + // add data successfully for different implementing types + tcases := []struct { + name string + query string + variables string + error string + }{ + { + name: "adding new Library member shouldn't return any error", + query: `mutation addLibraryMember($input: [AddLibraryMemberInput!]!) { + addLibraryMember(input: $input, upsert: false) { + libraryMember { + refID + } + } + }`, + variables: `{ + "input": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + }`, + }, { + name: "update existing library member using upsert shouldn't return any error", + query: `mutation addLibraryMember($input: [AddLibraryMemberInput!]!) { + addLibraryMember(input: $input, upsert: true) { + libraryMember { + refID + } + } + }`, + variables: `{ + "input": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming", + "Computer Architecture" + ], + "readHours": "5d3hr" + } + }`, + }, { + name: "adding new Sports Member shouldn't return any error", + query: `mutation addSportsMember($input: [AddSportsMemberInput!]!) { + addSportsMember(input: $input, upsert: false) { + sportsMember { + refID + } + } + }`, + variables: `{ + "input": { + "refID": "102", + "name": "Bob", + "teamID": "T01", + "teamName": "GraphQL", + "itemsIssued": [ + "2-Bats", + "1-football" + ], + "plays": "football and cricket" + } + }`, + }, { + name: "adding new Cricket Team shouldn't return any error", + query: `mutation addCricketTeam($input: [AddCricketTeamInput!]!) { + addCricketTeam(input: $input, upsert: false) { + cricketTeam { + teamID + } + } + }`, + variables: `{ + "input": { + "teamID": "T02", + "teamName": "Dgraph", + "numOfBatsmen": 5, + "numOfBowlers": 3 + } + }`, + }, { + name: "add new LibraryManager,linking to existing library Member", + query: `mutation addLibraryManager($input: [AddLibraryManagerInput!]!) { + addLibraryManager(input: $input, upsert: false) { + libraryManager { + name + } + } + }`, + variables: `{ + "input": { + "name": "Juliet", + "manages": { + "refID": "101" + } + } + }`, + }, { + name: "adding new Library member returns error as given id already exist in other node of type" + + " SportsMember which implements same interface", + query: `mutation addLibraryMember($input: [AddLibraryMemberInput!]!) { + addLibraryMember(input: $input, upsert: false) { + libraryMember { + refID + } + } + }`, + variables: `{ + "input": { + "refID": "102", + "name": "James", + "itemsIssued": [ + "Intro to C" + ], + "readHours": "1d2hr" + } + }`, + error: "couldn't rewrite mutation addLibraryMember because failed to rewrite mutation" + + " payload because id 102 already exists for field refID in some other implementing" + + " type of interface Member", + }, { + name: "adding new Cricket Team with upsert returns error as given id already exist" + + " in other node of type SportsMember which implements same interface", + query: `mutation addCricketTeam($input: [AddCricketTeamInput!]!) { + addCricketTeam(input: $input, upsert: true) { + cricketTeam { + teamID + } + } + }`, + variables: `{ + "input": { + "teamID": "T01", + "teamName": "Slash", + "numOfBatsmen": 5, + "numOfBowlers": 4 + } + }`, + error: "couldn't rewrite mutation addCricketTeam because failed to rewrite mutation" + + " payload because id T01 already exists for field teamID in some other" + + " implementing type of interface Team", + }, { + name: "adding new Library manager returns error when it try to links to LibraryMember" + + " but got id of some other implementing type which implements " + + "same interface as LibraryMember", + query: `mutation addLibraryManager($input: [AddLibraryManagerInput!]!) { + addLibraryManager(input: $input, upsert: false) { + libraryManager { + name + } + } + }`, + variables: `{ + "input": { + "name": "John", + "manages": { + "refID": "102" + } + } + }`, + error: "couldn't rewrite mutation addLibraryManager because failed to rewrite mutation" + + " payload because id 102 already exists for field refID in some other implementing" + + " type of interface Member", + }, + } + + for _, tcase := range tcases { + t.Run(tcase.name, func(t *testing.T) { + var vars map[string]interface{} + if tcase.variables != "" { + err := json.Unmarshal([]byte(tcase.variables), &vars) + require.NoError(t, err) + } + params := &GraphQLParams{ + Query: tcase.query, + Variables: vars, + } + resp := params.ExecuteAsPost(t, GraphqlURL) + if tcase.error != "" { + require.Equal(t, tcase.error, resp.Errors[0].Error()) + } else { + RequireNoGQLErrors(t, resp) + } + + }) + } + + // Cleanup + DeleteGqlType(t, "LibraryMember", map[string]interface{}{}, 1, nil) + DeleteGqlType(t, "SportsMember", map[string]interface{}{}, 1, nil) + DeleteGqlType(t, "CricketTeam", map[string]interface{}{}, 1, nil) + DeleteGqlType(t, "LibraryManager", map[string]interface{}{}, 1, nil) +} diff --git a/graphql/e2e/common/query.go b/graphql/e2e/common/query.go index f6def8f3ec6..f1704408d1d 100644 --- a/graphql/e2e/common/query.go +++ b/graphql/e2e/common/query.go @@ -522,10 +522,10 @@ func inFilterOnString(t *testing.T) { updateStateParams := &GraphQLParams{ Query: `mutation{ - updateState(input: { + updateState(input: { filter: { xcode: { in: ["abc", "def"]}}, - set: { + set: { capital: "Common Capital"} }){ state{ xcode @@ -2597,10 +2597,10 @@ func queryWithCascade(t *testing.T) { { name: "parameterized cascade on interface ", query: `query { - queryCharacter (filter: { appearsIn: { in: [EMPIRE] } }) @cascade(fields:["appearsIn"]){ + queryCharacter (filter: { appearsIn: { in: [EMPIRE] } }) @cascade(fields:["appearsIn"]){ name appearsIn - } + } }`, respData: `{ "queryCharacter": [ @@ -3238,7 +3238,7 @@ func queryAggregateOnEmptyData3(t *testing.T) { { "queryCountry": [{ "name": "India", - "ag": { + "ag": { "count" : 3, "nameMin": "Gujarat", "capitalMax": null, @@ -3289,7 +3289,7 @@ func queryAggregateWithAlias(t *testing.T) { cnt: count tmin : titleMin tmax: titleMax - navg : numLikesAvg + navg : numLikesAvg } }`, } @@ -3365,7 +3365,7 @@ func queryAggregateAtChildLevel(t *testing.T) { { "queryCountry": [{ "name": "India", - "ag": { + "ag": { "count" : 3, "__typename": "StateAggregateResult", "nameMin": "Gujarat" @@ -3377,7 +3377,7 @@ func queryAggregateAtChildLevel(t *testing.T) { func queryAggregateAtChildLevelWithFilter(t *testing.T) { queryNumberOfIndianStates := &GraphQLParams{ - Query: `query + Query: `query { queryCountry(filter: { name: { eq: "India" } }) { name @@ -3395,7 +3395,7 @@ func queryAggregateAtChildLevelWithFilter(t *testing.T) { { "queryCountry": [{ "name": "India", - "ag": { + "ag": { "count" : 2, "nameMin" : "Karnataka" } @@ -3406,7 +3406,7 @@ func queryAggregateAtChildLevelWithFilter(t *testing.T) { func queryAggregateAtChildLevelWithEmptyData(t *testing.T) { queryNumberOfIndianStates := &GraphQLParams{ - Query: `query + Query: `query { queryCountry(filter: { name: { eq: "India" } }) { name @@ -3461,7 +3461,7 @@ func queryAggregateAtChildLevelWithMultipleAlias(t *testing.T) { { "queryCountry": [{ "name": "India", - "ag1": { + "ag1": { "count" : 2, "nameMax" : "Maharashtra" }, @@ -3509,7 +3509,7 @@ func queryAggregateAtChildLevelWithRepeatedFields(t *testing.T) { func queryAggregateAndOtherFieldsAtChildLevel(t *testing.T) { queryNumberOfIndianStates := &GraphQLParams{ - Query: `query + Query: `query { queryCountry(filter: { name: { eq: "India" } }) { name @@ -3530,14 +3530,14 @@ func queryAggregateAndOtherFieldsAtChildLevel(t *testing.T) { { "queryCountry": [{ "name": "India", - "ag": { + "ag": { "count" : 3, "nameMin" : "Gujarat" }, "states": [ { "name": "Maharashtra" - }, + }, { "name": "Gujarat" }, @@ -3927,3 +3927,60 @@ func idDirectiveWithInt(t *testing.T) { }` require.JSONEq(t, expected, string(response.Data)) } + +func queryWithIDFieldAndInterfaceArg(t *testing.T) { + // add library member + addLibraryMemberParams := &GraphQLParams{ + Query: `mutation addLibraryMember($input: [AddLibraryMemberInput!]!) { + addLibraryMember(input: $input, upsert: false) { + libraryMember { + refID + } + } + }`, + Variables: map[string]interface{}{"input": []interface{}{ + map[string]interface{}{ + "refID": "101", + "name": "Alice", + "itemsIssued": []string{"Intro to Go", "Parallel Programming"}, + "readHours": "4d2hr", + }}, + }, + } + + gqlResponse := addLibraryMemberParams.ExecuteAsPost(t, GraphqlURL) + RequireNoGQLErrors(t, gqlResponse) + + queryMember := &GraphQLParams{ + Query: ` + query { + getMember(refID: "101") { + refID + name + itemsIssued + ... on LibraryMember { + readHours + } + } + }`, + } + + gqlResponse = queryMember.ExecuteAsPost(t, GraphqlURL) + RequireNoGQLErrors(t, gqlResponse) + queryPersonExpected := ` + { + "getMember": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Parallel Programming", + "Intro to Go" + ], + "readHours": "4d2hr" + } + }` + + require.JSONEq(t, queryPersonExpected, string(gqlResponse.Data)) + // Cleanup + DeleteGqlType(t, "LibraryMember", map[string]interface{}{}, 1, nil) +} diff --git a/graphql/e2e/directives/schema.graphql b/graphql/e2e/directives/schema.graphql index 64af79b7c9a..01529ca4e6c 100644 --- a/graphql/e2e/directives/schema.graphql +++ b/graphql/e2e/directives/schema.graphql @@ -377,3 +377,35 @@ type Employer { company: String! @id worker: [Worker] } + +interface Member { + refID: String! @id (interface:true) + name: String! @id + itemsIssued: [String] + fineAccumulated: Int +} + +interface Team { + teamID: String! @id (interface:true) + teamName: String! @id +} + +type LibraryMember implements Member { + interests: [String] + readHours: String +} + +type SportsMember implements Member & Team { + plays: String + playerRating: Int +} + +type CricketTeam implements Team { + numOfBatsmen: Int + numOfBowlers: Int +} + +type LibraryManager { + name: String! @id + manages: [LibraryMember] +} \ No newline at end of file diff --git a/graphql/e2e/directives/schema_response.json b/graphql/e2e/directives/schema_response.json index b1377b73c3f..edda361e792 100644 --- a/graphql/e2e/directives/schema_response.json +++ b/graphql/e2e/directives/schema_response.json @@ -67,6 +67,90 @@ "predicate": "author1.posts", "type": "uid" }, + { + "list": true, + "predicate": "LibraryManager.manages", + "type": "uid" + }, + { + "predicate": "LibraryMember.readHours", + "type": "string" + }, + { + "index": true, + "predicate": "Team.teamName", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "predicate": "SportsMember.playerRating", + "type": "int" + }, + { + "list": true, + "predicate": "LibraryMember.interests", + "type": "string" + }, + { + "index": true, + "predicate": "Team.teamID", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "index": true, + "predicate": "Member.name", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "predicate": "CricketTeam.numOfBowlers", + "type": "int" + }, + { + "predicate": "CricketTeam.numOfBatsmen", + "type": "int" + }, + { + "index": true, + "predicate": "LibraryManager.name", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "index": true, + "predicate": "Member.refID", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "list": true, + "predicate": "Member.itemsIssued", + "type": "string" + }, + { + "predicate": "Member.fineAccumulated", + "type": "int" + }, + { + "predicate": "SportsMember.plays", + "type": "string" + }, { "predicate": "Astronaut.name", "type": "string" @@ -1427,6 +1511,114 @@ } ], "name": "Employer" + }, + { + "fields": [ + { + "name": "Member.name" + }, + { + "name": "Member.refID" + }, + { + "name": "Member.itemsIssued" + }, + { + "name": "Member.fineAccumulated" + } + ], + "name": "Member" + }, + { + "fields": [ + { + "name": "Team.teamName" + }, + { + "name": "Team.teamID" + }, + { + "name": "CricketTeam.numOfBowlers" + }, + { + "name": "CricketTeam.numOfBatsmen" + } + ], + "name": "CricketTeam" + }, + { + "fields": [ + { + "name": "LibraryManager.manages" + }, + { + "name": "LibraryManager.name" + } + ], + "name": "LibraryManager" + }, + { + "fields": [ + { + "name": "Team.teamName" + }, + { + "name": "Team.teamID" + } + ], + "name": "Team" + }, + { + "fields": [ + { + "name": "Team.teamName" + }, + { + "name": "SportsMember.playerRating" + }, + { + "name": "Team.teamID" + }, + { + "name": "Member.name" + }, + { + "name": "Member.refID" + }, + { + "name": "Member.itemsIssued" + }, + { + "name": "SportsMember.plays" + }, + { + "name": "Member.fineAccumulated" + } + ], + "name": "SportsMember" + }, + { + "fields": [ + { + "name": "LibraryMember.readHours" + }, + { + "name": "LibraryMember.interests" + }, + { + "name": "Member.name" + }, + { + "name": "Member.refID" + }, + { + "name": "Member.itemsIssued" + }, + { + "name": "Member.fineAccumulated" + } + ], + "name": "LibraryMember" } ] } diff --git a/graphql/e2e/normal/schema.graphql b/graphql/e2e/normal/schema.graphql index 347a282c4f4..1876dd77f6c 100644 --- a/graphql/e2e/normal/schema.graphql +++ b/graphql/e2e/normal/schema.graphql @@ -380,3 +380,36 @@ type Dataset { project: Project! name: String! @search(by: [hash]) } + + +interface Member { + refID: String! @id (interface:true) + name: String! @id + itemsIssued: [String] + fineAccumulated: Int +} + +interface Team { + teamID: String! @id (interface:true) + teamName: String! @id +} + +type LibraryMember implements Member { + interests: [String] + readHours: String +} + +type SportsMember implements Member & Team { + plays: String + playerRating: Int +} + +type CricketTeam implements Team { + numOfBatsmen: Int + numOfBowlers: Int +} + +type LibraryManager { + name: String! @id + manages: [LibraryMember] +} \ No newline at end of file diff --git a/graphql/e2e/normal/schema_response.json b/graphql/e2e/normal/schema_response.json index 5d6b0c5708b..defb3113383 100644 --- a/graphql/e2e/normal/schema_response.json +++ b/graphql/e2e/normal/schema_response.json @@ -17,6 +17,90 @@ "type": "string", "upsert": true }, + { + "list": true, + "predicate": "LibraryManager.manages", + "type": "uid" + }, + { + "predicate": "LibraryMember.readHours", + "type": "string" + }, + { + "index": true, + "predicate": "Team.teamName", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "predicate": "SportsMember.playerRating", + "type": "int" + }, + { + "list": true, + "predicate": "LibraryMember.interests", + "type": "string" + }, + { + "index": true, + "predicate": "Team.teamID", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "index": true, + "predicate": "Member.name", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "predicate": "CricketTeam.numOfBowlers", + "type": "int" + }, + { + "predicate": "CricketTeam.numOfBatsmen", + "type": "int" + }, + { + "index": true, + "predicate": "LibraryManager.name", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "index": true, + "predicate": "Member.refID", + "tokenizer": [ + "hash" + ], + "type": "string", + "upsert": true + }, + { + "list": true, + "predicate": "Member.itemsIssued", + "type": "string" + }, + { + "predicate": "Member.fineAccumulated", + "type": "int" + }, + { + "predicate": "SportsMember.plays", + "type": "string" + }, { "predicate": "post1.author", "type": "uid" @@ -1427,6 +1511,114 @@ } ], "name": "Employer" + }, + { + "fields": [ + { + "name": "Member.name" + }, + { + "name": "Member.refID" + }, + { + "name": "Member.itemsIssued" + }, + { + "name": "Member.fineAccumulated" + } + ], + "name": "Member" + }, + { + "fields": [ + { + "name": "Team.teamName" + }, + { + "name": "Team.teamID" + }, + { + "name": "CricketTeam.numOfBowlers" + }, + { + "name": "CricketTeam.numOfBatsmen" + } + ], + "name": "CricketTeam" + }, + { + "fields": [ + { + "name": "LibraryManager.manages" + }, + { + "name": "LibraryManager.name" + } + ], + "name": "LibraryManager" + }, + { + "fields": [ + { + "name": "Team.teamName" + }, + { + "name": "Team.teamID" + } + ], + "name": "Team" + }, + { + "fields": [ + { + "name": "Team.teamName" + }, + { + "name": "SportsMember.playerRating" + }, + { + "name": "Team.teamID" + }, + { + "name": "Member.name" + }, + { + "name": "Member.refID" + }, + { + "name": "Member.itemsIssued" + }, + { + "name": "SportsMember.plays" + }, + { + "name": "Member.fineAccumulated" + } + ], + "name": "SportsMember" + }, + { + "fields": [ + { + "name": "LibraryMember.readHours" + }, + { + "name": "LibraryMember.interests" + }, + { + "name": "Member.name" + }, + { + "name": "Member.refID" + }, + { + "name": "Member.itemsIssued" + }, + { + "name": "Member.fineAccumulated" + } + ], + "name": "LibraryMember" } ] -} \ No newline at end of file +} diff --git a/graphql/e2e/schema/apollo_service_response.graphql b/graphql/e2e/schema/apollo_service_response.graphql index 4c176d478e7..c0383cdeec5 100644 --- a/graphql/e2e/schema/apollo_service_response.graphql +++ b/graphql/e2e/schema/apollo_service_response.graphql @@ -198,7 +198,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM diff --git a/graphql/e2e/schema/generatedSchema.graphql b/graphql/e2e/schema/generatedSchema.graphql index f4a15622aaf..6b9c37d5f31 100644 --- a/graphql/e2e/schema/generatedSchema.graphql +++ b/graphql/e2e/schema/generatedSchema.graphql @@ -179,7 +179,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/resolve/add_mutation_test.yaml b/graphql/resolve/add_mutation_test.yaml index cfaeffc3d3f..68959e4ee28 100644 --- a/graphql/resolve/add_mutation_test.yaml +++ b/graphql/resolve/add_mutation_test.yaml @@ -1127,6 +1127,537 @@ } } +- + name: "Add mutation on implementation type which have inherited @id field with interface argument -1" + explanation: "This mutation will generate three existence queries two for xid - refID (one for interface and one + for implementing type) and one for xid - name" + gqlmutation: | + mutation addLibraryMember($input: AddLibraryMemberInput!) { + addLibraryMember(input: [$input], upsert: false) { + libraryMember { + refID + } + } + } + gqlvariables: | + { + "input": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + } + dgquery: |- + query { + LibraryMember_1(func: eq(Member.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + dgmutations: + - setjson: | + { + "LibraryMember.readHours": "4d2hr", + "Member.itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "Member.name": "Alice", + "Member.refID": "101", + "dgraph.type": [ + "LibraryMember", + "Member" + ], + "uid": "_:LibraryMember_2" + } + +- + name: "Add mutation on implementation type which have inherited @id field with interface argument -2" + explanation: "Node with refID:101 already exist in other implementing type of interface, mutation not allowed + in this case and we will return error" + gqlmutation: | + mutation addLibraryMember($input: AddLibraryMemberInput!) { + addLibraryMember(input: [$input], upsert: false) { + libraryMember { + refID + } + } + } + gqlvariables: | + { + "input": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + } + dgquery: |- + query { + LibraryMember_1(func: eq(Member.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + qnametouid: |- + { + "LibraryMember_3": "0x11" + } + error2: + { + "message": "failed to rewrite mutation payload because id 101 already exists for field refID + in some other implementing type of interface Member" + } + +- + name: "Add mutation on implementation type which have inherited @id field with interface argument -3" + explanation: "Node with refID:101 already exist in same mutated type, returns error " + gqlmutation: | + mutation addLibraryMember($input: AddLibraryMemberInput!) { + addLibraryMember(input: [$input], upsert: false) { + libraryMember { + refID + } + } + } + gqlvariables: | + { + "input": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + } + dgquery: |- + query { + LibraryMember_1(func: eq(Member.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + qnametouid: |- + { + "LibraryMember_2": "0x11" + } + error2: + { + "message": "failed to rewrite mutation payload because id 101 already exists for field + refID inside type LibraryMember" + } + +- + name: "Add upsert mutation on implementation type which have inherited @id field with interface argument -1" + explanation: "node with @id field doesn't exist in any of the implementing type, we will add the node" + gqlmutation: | + mutation addLibraryMember($input: AddLibraryMemberInput!) { + addLibraryMember(input: [$input], upsert: true) { + libraryMember { + refID + } + } + } + gqlvariables: | + { + "input": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + } + dgquery: |- + query { + LibraryMember_1(func: eq(Member.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + qnametouid: |- + { + "LibraryMember_1": "0x11" + } + dgquerysec: |- + query { + LibraryMember_1 as LibraryMember_1(func: uid(0x11)) @filter(type(LibraryMember)) { + uid + } + } + dgmutations: + - setjson: | + { + "LibraryMember.readHours": "4d2hr", + "Member.itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "uid": "uid(LibraryMember_1)" + } + cond: "@if(gt(len(LibraryMember_1), 0))" +- + name: "Add upsert mutation on implementation type which have inherited @id field with interface argument -2" + explanation: "node with @id field already exist in one of the implementing type, returns error" + gqlmutation: | + mutation addLibraryMember($input: AddLibraryMemberInput!) { + addLibraryMember(input: [$input], upsert: true) { + libraryMember { + refID + } + } + } + gqlvariables: | + { + "input": { + "refID": "101", + "name": "Alice", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + } + dgquery: |- + query { + LibraryMember_1(func: eq(Member.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + qnametouid: |- + { + "LibraryMember_3": "0x11" + } + error2: + { + "message": "failed to rewrite mutation payload because id 101 already exists for + field refID in some other implementing type of interface Member" + } + +- + name: "Add mutation with nested object which have inherited @id field with interface argument -1" + explanation: "There is no node with refID 101 of interface type or it's implementation type,hence will wii add + nested object and link that to parent object" + gqlmutation: | + mutation addLibraryManager($input: AddLibraryManagerInput!) { + addLibraryManager(input: [$input], upsert: false) { + libraryManager { + name + } + } + } + gqlvariables: | + { + "input": { + "name": "Alice", + "manages": [ + { + "refID": "101", + "name": "Bob", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + ] + } + } + dgquery: |- + query { + LibraryManager_1(func: eq(LibraryManager.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.name, "Bob")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_4(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + dgmutations: + - setjson: | + { + "LibraryManager.manages": [ + { + "LibraryMember.readHours": "4d2hr", + "Member.itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "Member.name": "Bob", + "Member.refID": "101", + "dgraph.type": [ + "LibraryMember", + "Member" + ], + "uid": "_:LibraryMember_3" + } + ], + "LibraryManager.name": "Alice", + "dgraph.type": [ + "LibraryManager" + ], + "uid": "_:LibraryManager_1" + } + +- + name: "Add mutation with nested object which have inherited @id field with interface argument -2" + explanation: "node with refID 101 already exist in one of the implementing type other than mutated type,returns error" + gqlmutation: | + mutation addLibraryManager($input: AddLibraryManagerInput!) { + addLibraryManager(input: [$input], upsert: false) { + libraryManager { + name + } + } + } + gqlvariables: | + { + "input": { + "name": "Alice", + "manages": [ + { + "refID": "101", + "name": "Bob", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + ] + } + } + dgquery: |- + query { + LibraryManager_1(func: eq(LibraryManager.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.name, "Bob")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_4(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + qnametouid: |- + { + "LibraryMember_4": "0x11" + } + error2: + { + "message": "failed to rewrite mutation payload because id 101 already exists for field + refID in some other implementing type of interface Member" + } + +- + name: "Add mutation with nested object which have inherited @id field with interface argument -3" + explanation: "node with refID 101 already exist for mutated type,link child node to parent" + gqlmutation: | + mutation addLibraryManager($input: AddLibraryManagerInput!) { + addLibraryManager(input: [$input], upsert: false) { + libraryManager { + name + } + } + } + gqlvariables: | + { + "input": { + "name": "Alice", + "manages": [ + { + "refID": "101", + "name": "Bob", + "itemsIssued": [ + "Intro to Go", + "Parallel Programming" + ], + "readHours": "4d2hr" + } + ] + } + } + dgquery: |- + query { + LibraryManager_1(func: eq(LibraryManager.name, "Alice")) { + uid + dgraph.type + } + LibraryMember_2(func: eq(Member.name, "Bob")) { + uid + dgraph.type + } + LibraryMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + LibraryMember_4(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + } + qnametouid: |- + { + "LibraryMember_3": "0x11", + "LibraryMember_4": "0x11" + } + dgmutations: + - setjson: | + { + "LibraryManager.manages": [ + { + "uid":"0x11" + } + ], + "LibraryManager.name": "Alice", + "dgraph.type": [ + "LibraryManager" + ], + "uid": "_:LibraryManager_1" + } + +- + name: "Add mutation on implementation type which have inherited @id fields with interface argument from multiple interfaces" + explanation: "This mutation will generate six existence queries, 2 existence queries for each of the inherited @id fields + with interface arg and one for each @id field,none of the existence query return uid,so we successfully add the object in this case" + gqlmutation: | + mutation addSportsMember($input: AddSportsMemberInput!) { + addSportsMember(input: [$input], upsert: false) { + sportsMember { + refID + } + } + } + gqlvariables: | + { + "input": { + "refID": "101", + "name": "Alice", + "teamID": "T01", + "teamName": "GraphQL", + "itemsIssued": [ + "2-Bats", + "1-football" + ], + "plays": "football and cricket" + } + } + dgquery: |- + query { + SportsMember_1(func: eq(Member.name, "Alice")) { + uid + dgraph.type + } + SportsMember_2(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + SportsMember_3(func: eq(Member.refID, "101")) { + uid + dgraph.type + } + SportsMember_4(func: eq(Team.teamID, "T01")) { + uid + dgraph.type + } + SportsMember_5(func: eq(Team.teamID, "T01")) { + uid + dgraph.type + } + SportsMember_6(func: eq(Team.teamName, "GraphQL")) { + uid + dgraph.type + } + } + dgmutations: + - setjson: | + { + "Member.itemsIssued": [ + "2-Bats", + "1-football" + ], + "Member.name": "Alice", + "Member.refID": "101", + "SportsMember.plays": "football and cricket", + "Team.teamID": "T01", + "Team.teamName": "GraphQL", + "dgraph.type": [ + "SportsMember", + "Member", + "Team" + ], + "uid": "_:SportsMember_6" + } + - name: "Add mutation using code on type which also has an ID field" gqlmutation: | diff --git a/graphql/resolve/mutation_rewriter.go b/graphql/resolve/mutation_rewriter.go index 4439e9a5727..3afd2942e4c 100644 --- a/graphql/resolve/mutation_rewriter.go +++ b/graphql/resolve/mutation_rewriter.go @@ -131,9 +131,19 @@ func NewVariableGenerator() *VariableGenerator { func (v *VariableGenerator) Next(typ schema.Type, xidName, xidVal string, auth bool) string { // return previously allocated variable name for repeating xidVal var key string + flagAndXidName := xidName + + // We pass the xidName as "Int.xidName" to generate variable for existence query + // of interface type when id filed is inherited from interface and have interface Argument set + // Here we handle that case + if strings.Contains(flagAndXidName, ".") { + xidName = strings.Split(flagAndXidName, ".")[1] + } + if xidName == "" || xidVal == "" { key = typ.Name() } else { + // here we are using the assertion that field name or type name can't have "." in them // We add "." between values while generating key to removes duplicate xidError from below type of cases // mutation { // addABC(input: [{ ab: "cd", abc: "d" }]) { @@ -147,7 +157,8 @@ func (v *VariableGenerator) Next(typ schema.Type, xidName, xidVal string, auth b // ABC.ab.cd and ABC.abc.d // It also ensures that xids from different types gets different variable names // here we are using the assertion that field name or type name can't have "." in them - key = typ.FieldOriginatedFrom(xidName) + "." + xidName + "." + xidVal + xidType, _ := typ.FieldOriginatedFrom(xidName) + key = fmt.Sprintf("%s.%s.%s", xidType.Name(), flagAndXidName, xidVal) } if varName, ok := v.xidVarNameMap[key]; ok { @@ -1236,8 +1247,8 @@ func mutationFromFragment( } -func checkXIDExistsQuery( - xidVariable, xidString, xidPredicate string, typ schema.Type) *dql.GraphQuery { +func checkXIDExistsQuery(xidVariable, xidString, xidPredicate string, typ schema.Type, + interfaceType schema.Type) *dql.GraphQuery { qry := &dql.GraphQuery{ Attr: xidVariable, Func: &dql.Function{ @@ -1249,6 +1260,7 @@ func checkXIDExistsQuery( }, Children: []*dql.GraphQuery{{Attr: "uid"}, {Attr: "dgraph.type"}}, } + return qry } @@ -1406,46 +1418,79 @@ func rewriteObject( xidString, _ = extractVal(xidVal, xid.Name(), xid.Type().Name()) variable = varGen.Next(typ, xid.Name(), xidString, false) - // Three cases: - // 1. If the queryResult UID exists. Add a reference. + // If this xid field is inherited from interface and have interface argument set, we also + // have existence query for interface to make sure that this xid is unique across all + // implementation types of the interface. + // We have following cases + // 1. If the queryResult UID exists for any of existence query (type or interface), + // then add a reference. // 2. If the queryResult UID does not exist and this is the first time we are seeing // this. Then, return error. // 3. The queryResult UID does not exist. But, this could be a reference to an XID // node added during the mutation rewriting. This is handled by adding the new blank UID // to existenceQueryResult. - // Get whether node with XID exists or not from existenceQueriesResult - if uid, ok := idExistence[variable]; ok { + interfaceTyp, interfaceVar := interfaceVariable(typ, varGen, xid.Name(), xidString) + + // Get whether node with XID exists or not from existenceQueriesResults + _, interfaceUidExist := idExistence[interfaceVar] + typUid, typUidExist := idExistence[variable] + + if interfaceUidExist || typUidExist { // node with XID exists. This is a reference. - // We return an error if this is at toplevel. Else, we return the ID reference + // We return an error if this is at toplevel. Else, we return the ID reference if + // found node is of same type as xid field type. Because that node can be of some other + // type in case xidField is inherited from interface. if atTopLevel { if mutationType == AddWithUpsert { - // This means we are in Add Mutation with upsert: true. - // In this case, we don't return an error and continue updating this node. - // upsertVar is set to variable and srcUID is set to uid(variable) to continue - // updating this node. - upsertVar = variable - srcUID = fmt.Sprintf("uid(%s)", variable) + if typUidExist { + // This means we are in Add Mutation with upsert: true and node belong to + // same type as of the xid field. + // In this case, we don't return an error and continue updating this node. + // upsertVar is set to variable and srcUID is set to uid(variable) to continue + // updating this node. + upsertVar = variable + srcUID = fmt.Sprintf("uid(%s)", variable) + } else { + // if node is some other type as of xid Field then we can't upsert that + // and we returns error + retErrors = append(retErrors, xidErrorForInterfaceType(typ, xidString, xid, + interfaceTyp.Name())) + return nil, "", retErrors + } } else { // We return an error as we are at top level of non-upsert mutation and the XID exists. // We need to conceal the error because we might be leaking information to the user if it // tries to add duplicate data to the field with @id. var err error - if queryAuthSelector(typ) == nil { - err = x.GqlErrorf("id %s already exists "+ - "for field %s inside type %s", xidString, xid.Name(), typ.Name()) - } else { - // This error will only be reported in debug mode. - err = x.GqlErrorf("GraphQL debug: id %s already exists for "+ - "field %s inside type %s", xidString, xid.Name(), typ.Name()) + if typUidExist { + if queryAuthSelector(typ) == nil { + err = x.GqlErrorf("id %s already exists for field %s inside type %s", + xidString, xid.Name(), typ.Name()) + } else { + // This error will only be reported in debug mode. + err = x.GqlErrorf("GraphQL debug: id %s already exists for field %s"+ + " inside type %s", typ.Name(), xid.Name(), xidString) + } + retErrors = append(retErrors, err) + return nil, upsertVar, retErrors } - retErrors = append(retErrors, err) + + retErrors = append(retErrors, xidErrorForInterfaceType(typ, xidString, xid, + interfaceTyp.Name())) return nil, upsertVar, retErrors + } } else { // As we are not at top level, we return the XID reference. We don't update this node // further. - return asIDReference(ctx, uid, srcField, srcUID, varGen, mutationType == UpdateWithRemove), upsertVar, nil + if typUidExist { + return asIDReference(ctx, typUid, srcField, srcUID, varGen, + mutationType == UpdateWithRemove), upsertVar, nil + } + retErrors = append(retErrors, xidErrorForInterfaceType(typ, xidString, xid, + interfaceTyp.Name())) + return nil, upsertVar, retErrors } } else { @@ -1498,7 +1543,7 @@ func rewriteObject( // This is handled in the for loop above continue } else if mutationType == Add || mutationType == AddWithUpsert || !atTopLevel { - // When we reach this stage we are absoulutely sure that this is not a reference and is + // When we reach this stage we are absolutely sure that this is not a reference and is // a new node and one of the XIDs is missing. // There are two possibilities here: // 1. This is an Add Mutation or we are at some deeper level inside Update Mutation: @@ -1715,6 +1760,20 @@ func rewriteObject( return frag, upsertVar, retErrors } +func xidErrorForInterfaceType(typ schema.Type, xidString string, xid schema.FieldDefinition, + interfaceName string) error { + // TODO(GRAPHQL): currently we are checking typ of the mutated field for auth rules, + // But we need to check auth rule on implementing type for which we found existing node + // with same @id. + if queryAuthSelector(typ) == nil { + return x.GqlErrorf("id %s already exists for field %s in some other"+ + " implementing type of interface %s", xidString, xid.Name(), interfaceName) + } + // This error will only be reported in debug mode. + return x.GqlErrorf("GraphQL debug: id %s already exists for field %s in some other"+ + " implementing type of interface %s", xidString, xid.Name(), interfaceName) +} + // existenceQueries takes a GraphQL JSON object as obj and creates queries to find // out if referenced nodes by XID and UID exist or not. // This is done in recursive fashion using a dfs. @@ -1794,7 +1853,13 @@ func existenceQueries( if xidMetadata.variableObjMap[variable] != nil { // if we already encountered an object with same xid earlier, and this object is // considered a duplicate of the existing object, then return error. + if xidMetadata.isDuplicateXid(atTopLevel, variable, obj, srcField) { + // TODO(GRAPHQL): Add this error for inherited @id field with interface arg. + // Currently we don't return this error for the nested case when + // at both root and nested level we have same value of @id fields + // which have interface arg set and are inherited from same interface + // but are in different implementing type, we currently treat that as reference. err := errors.Errorf("duplicate XID found: %s", xidString) retErrors = append(retErrors, err) return nil, nil, retErrors @@ -1824,9 +1889,21 @@ func existenceQueries( // Add the corresponding existence query. As this is the first time we have // encountered this variable, the query is added only once per variable. - query := checkXIDExistsQuery(variable, xidString, xid.Name(), typ) + query := checkXIDExistsQuery(variable, xidString, xid.Name(), typ, nil) ret = append(ret, query) retTypes = append(retTypes, typ.DgraphName()) + + // Add one more existence query if given xid field is inherited from interface and has + // interface argument set. This is added to ensure that this xid is unique across all the + // implementation of the interface. + interfaceTyp, varInterface := interfaceVariable(typ, varGen, + xid.Name(), xidString) + if interfaceTyp != nil { + queryInterface := checkXIDExistsQuery(varInterface, xidString, xid.Name(), + typ, interfaceTyp) + ret = append(ret, queryInterface) + retTypes = append(retTypes, interfaceTyp.DgraphName()) + } // Don't return just over here as there maybe more nodes in the children tree. } } @@ -2376,3 +2453,15 @@ func extractVal(xidVal interface{}, xidName, typeName string) (string, error) { "allowed as Xid", xidName, typeName) } } + +// This function will return interface type and variable for existence query on interface, +// if given xid is inherited from interface, otherwise it will return nil and empty string +func interfaceVariable(typ schema.Type, varGen *VariableGenerator, xidName string, + xidString string) (schema.Type, string) { + interfaceType, isInherited := typ.FieldOriginatedFrom(xidName) + fieldDef := typ.Field(xidName) + if isInherited && fieldDef.HasInterfaceArg() { + return interfaceType, varGen.Next(typ, "Int."+xidName, xidString, false) + } + return nil, "" +} diff --git a/graphql/resolve/query_test.yaml b/graphql/resolve/query_test.yaml index d5410eff615..20f983722db 100644 --- a/graphql/resolve/query_test.yaml +++ b/graphql/resolve/query_test.yaml @@ -30,8 +30,7 @@ } } -- - name: "in filter on string type" +- name: "in filter on string type" gqlquery: | query { queryState(filter: {code: {in: ["abc", "def", "ghi"]}}) { @@ -48,8 +47,7 @@ } } -- - name: "in filter on float type" +- name: "in filter on float type" gqlquery: | query { queryAuthor(filter: {reputation: {in: [10.3, 12.6, 13.6]}}) { @@ -66,8 +64,7 @@ } } -- - name: "in filter on datetime type" +- name: "in filter on datetime type" gqlquery: | query { queryAuthor(filter: {dob: {in: ["2001-01-01", "2002-02-01"]}}) { @@ -84,8 +81,7 @@ } } -- - name: "in filter on int type" +- name: "in filter on int type" gqlquery: | query { queryPost(filter: {numLikes: {in: [10, 15, 100]}}) { @@ -99,8 +95,7 @@ dgraph.uid : uid } } -- - name: "in filter on field which is of enum type" +- name: "in filter on field which is of enum type" gqlquery: | query{ queryVerification(filter: {prevStatus: {in: [ACTIVE, DEACTIVATED]}}){ @@ -117,8 +112,7 @@ } } -- - name: "in filter on field which is a List of enum type" +- name: "in filter on field which is a List of enum type" gqlquery: | query{ queryVerification(filter: {status: {in: [ACTIVE, DEACTIVATED]}}){ @@ -168,8 +162,7 @@ dgraph.uid : uid } } -- - name: "Point query near filter" +- name: "Point query near filter" gqlquery: | query { queryHotel(filter: { location: { near: { distance: 33.33, coordinate: { latitude: 11.11, longitude: 22.22} } } }) { @@ -189,8 +182,7 @@ } } -- - name: "Point query within filter" +- name: "Point query within filter" gqlquery: | query { queryHotel(filter: { location: { within: { polygon: { coordinates: [ { points: [{ latitude: 11.11, longitude: 22.22}, { latitude: 15.15, longitude: 16.16} , { latitude: 20.20, longitude: 21.21} ]}, { points: [{ latitude: 11.18, longitude: 22.28}, { latitude: 15.18, longitude: 16.18} , { latitude: 20.28, longitude: 21.28}]} ] } } } }) { @@ -210,8 +202,7 @@ } } -- - name: "Polygon query near filter" +- name: "Polygon query near filter" gqlquery: | query { queryHotel(filter: { area: { near: { distance: 33.33, coordinate: { latitude: 11.11, longitude: 22.22} } } }) { @@ -235,8 +226,7 @@ } } -- - name: "Polygon query within filter" +- name: "Polygon query within filter" gqlquery: | query { queryHotel(filter: { area: { within: { polygon: { coordinates: [ { points: [{ latitude: 11.11, longitude: 22.22}, { latitude: 15.15, longitude: 16.16} , { latitude: 20.20, longitude: 21.21} ]}, { points: [{ latitude: 11.18, longitude: 22.28}, { latitude: 15.18, longitude: 16.18} , { latitude: 20.28, longitude: 21.28}]} ] } } } }) { @@ -260,8 +250,7 @@ } } -- - name: "Polygon query contains polygon filter" +- name: "Polygon query contains polygon filter" gqlquery: | query { queryHotel(filter: { area: { contains: { polygon: { coordinates: [ { points: [{ latitude: 11.11, longitude: 22.22}, { latitude: 15.15, longitude: 16.16} , { latitude: 20.20, longitude: 21.21} ]}, { points: [{ latitude: 11.18, longitude: 22.28}, { latitude: 15.18, longitude: 16.18} , { latitude: 20.28, longitude: 21.28}]} ] } } } }) { @@ -285,8 +274,7 @@ } } -- - name: "Polygon query contains point filter" +- name: "Polygon query contains point filter" gqlquery: | query { queryHotel(filter: { area: { contains: { point: { latitude: 11.11, longitude: 22.22}} } }) { @@ -310,8 +298,7 @@ } } -- - name: "Polygon query intersect polygon filter" +- name: "Polygon query intersect polygon filter" gqlquery: | query { queryHotel(filter: { @@ -365,8 +352,7 @@ } } -- - name: "Polygon query intersect multi-polygon filter" +- name: "Polygon query intersect multi-polygon filter" gqlquery: | query { queryHotel(filter: { @@ -446,8 +432,7 @@ } } -- - name: "MultiPolygon query near filter" +- name: "MultiPolygon query near filter" gqlquery: | query { queryHotel(filter: { branches: { near: { distance: 33.33, coordinate: { latitude: 11.11, longitude: 22.22} } } }) { @@ -473,8 +458,7 @@ } } -- - name: "MultiPolygon query within filter" +- name: "MultiPolygon query within filter" gqlquery: | query { queryHotel(filter: { branches: { within: { polygon: { coordinates: [ { points: [{ latitude: 11.11, longitude: 22.22}, { latitude: 15.15, longitude: 16.16} , { latitude: 20.20, longitude: 21.21} ]}, { points: [{ latitude: 11.18, longitude: 22.28}, { latitude: 15.18, longitude: 16.18} , { latitude: 20.28, longitude: 21.28}]} ] } } } }) { @@ -500,8 +484,7 @@ } } -- - name: "MultiPolygon query contains polygon filter" +- name: "MultiPolygon query contains polygon filter" gqlquery: | query { queryHotel(filter: { branches: { contains: { polygon: { coordinates: [ { points: [{ latitude: 11.11, longitude: 22.22}, { latitude: 15.15, longitude: 16.16} , { latitude: 20.20, longitude: 21.21} ]}, { points: [{ latitude: 11.18, longitude: 22.28}, { latitude: 15.18, longitude: 16.18} , { latitude: 20.28, longitude: 21.28}]} ] } } } }) { @@ -527,8 +510,7 @@ } } -- - name: "MultiPolygon query contains point filter" +- name: "MultiPolygon query contains point filter" gqlquery: | query { queryHotel(filter: { branches: { contains: { point: { latitude: 11.11, longitude: 22.22}} } }) { @@ -554,8 +536,7 @@ } } -- - name: "MultiPolygon query intersect polygon filter" +- name: "MultiPolygon query intersect polygon filter" gqlquery: | query { queryHotel(filter: { @@ -611,8 +592,7 @@ } } -- - name: "MultiPolygon query intersect multi-polygon filter" +- name: "MultiPolygon query intersect multi-polygon filter" gqlquery: | query { queryHotel(filter: { @@ -694,8 +674,7 @@ } } -- - name: "ID query" +- name: "ID query" gqlquery: | query { getAuthor(id: "0x1") { @@ -710,8 +689,7 @@ } } -- - name: "Alias isn't ignored in query rewriting - get" +- name: "Alias isn't ignored in query rewriting - get" gqlquery: | query { author : getAuthor(id: "0x1") { @@ -733,8 +711,7 @@ } } -- - name: "Alias isn't ignored in query rewriting - query" +- name: "Alias isn't ignored in query rewriting - query" gqlquery: | query { author : queryAuthor { @@ -756,8 +733,7 @@ } } -- - name: "ID field gets transformed to uid" +- name: "ID field gets transformed to uid" gqlquery: | query { getAuthor(id: "0x1") { @@ -773,8 +749,7 @@ } } -- - name: "ID query with depth" +- name: "ID query with depth" gqlquery: | query { getAuthor(id: "0x1") { @@ -798,8 +773,7 @@ } } -- - name: "ID query deep" +- name: "ID query deep" gqlquery: | query { getAuthor(id: "0x1") { @@ -831,8 +805,7 @@ } } -- - name: "Query with no args is query for everything of that type" +- name: "Query with no args is query for everything of that type" gqlquery: | query { queryAuthor { @@ -863,8 +836,7 @@ } } -- - name: "Filter connectives with null values gets skipped " +- name: "Filter connectives with null values gets skipped " gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" },not:null }) { @@ -879,8 +851,7 @@ } } -- - name: "Query with has Filter" +- name: "Query with has Filter" gqlquery: | query { queryTeacher(filter: {has: subject}) { @@ -895,8 +866,7 @@ } } -- - name: "has Filter with not" +- name: "has Filter with not" gqlquery: | query { queryTeacher(filter: { not : {has: subject } }) { @@ -911,8 +881,7 @@ } } -- - name: "has Filter with and" +- name: "has Filter with and" gqlquery: | query { queryTeacher(filter: {has: subject, and: {has: teaches } } ) { @@ -927,8 +896,7 @@ } } -- - name: "has Filter on list of fields" +- name: "has Filter on list of fields" gqlquery: | query { queryTeacher(filter: {has: [subject, teaches ] } ) { @@ -956,8 +924,7 @@ dgraph.uid : uid } } -- - name: "Filters in same input object implies AND" +- name: "Filters in same input object implies AND" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, dob: { le: "2001-01-01" }, reputation: { gt: 2.5 } } ) { @@ -972,8 +939,7 @@ } } -- - name: "Filter with nested 'and'" +- name: "Filter with nested 'and'" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, and: { dob: { le: "2001-01-01" }, and: { reputation: { gt: 2.5 } } } } ) { @@ -988,8 +954,7 @@ } } -- - name: "has Filter with nested 'and'" +- name: "has Filter with nested 'and'" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, and: { dob: { le: "2001-01-01" }, and: { has: country } } } ) { @@ -1004,8 +969,7 @@ } } -- - name: "Filter with 'or'" +- name: "Filter with 'or'" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, or: { dob: { le: "2001-01-01" } } } ) { @@ -1020,8 +984,7 @@ } } -- - name: "Filter with 'or' array" +- name: "Filter with 'or' array" gqlquery: | query { queryAuthor(filter: { or: [ { name: { eq: "A. N. Author" } }, { dob: { le: "2001-01-01" } }] } ) { @@ -1036,8 +999,7 @@ } } -- - name: "Filter with 'or' object" +- name: "Filter with 'or' object" gqlquery: | query { queryAuthor(filter: { or: { name: { eq: "A. N. Author" } }} ) { @@ -1053,8 +1015,7 @@ } -- - name: "Filter with implied and as well as 'or'" +- name: "Filter with implied and as well as 'or'" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, reputation: { gt: 2.5 }, or: { dob: { le: "2001-01-01" } } } ) { @@ -1069,8 +1030,7 @@ } } -- - name: "Filter with implied and nested in 'or'" +- name: "Filter with implied and nested in 'or'" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, or: { reputation: { gt: 2.5 }, dob: { le: "2001-01-01" } } } ) { @@ -1085,8 +1045,7 @@ } } -- - name: "Filter nested 'or'" +- name: "Filter nested 'or'" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, or: { reputation: { gt: 2.5 }, or: { dob: { le: "2001-01-01" } } } } ) { @@ -1101,8 +1060,7 @@ } } -- - name: "Filter with 'not" +- name: "Filter with 'not" gqlquery: | query { queryAuthor(filter: { not: { reputation: { gt: 2.5 } } } ) { @@ -1117,8 +1075,7 @@ } } -- - name: "Filter with first" +- name: "Filter with first" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" } }, first: 10) { @@ -1133,8 +1090,7 @@ } } -- - name: "Filter with first and offset" +- name: "Filter with first and offset" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" } }, first: 10, offset: 10) { @@ -1149,8 +1105,7 @@ } } -- - name: "Filter with order asc" +- name: "Filter with order asc" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" } }, order: { asc: reputation }) { @@ -1165,8 +1120,7 @@ } } -- - name: "Filter with order desc" +- name: "Filter with order desc" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" } }, order: { desc: reputation }) { @@ -1182,8 +1136,7 @@ } -- - name: "Filter with nested order" +- name: "Filter with nested order" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" } }, order: { desc: reputation, then: { asc: dob } }) { @@ -1198,8 +1151,7 @@ } } -- - name: "Filter with order, first and offset" +- name: "Filter with order, first and offset" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" } }, order: { desc: reputation }, first: 10, offset: 10) { @@ -1214,8 +1166,7 @@ } } -- - name: "Deep filter" +- name: "Deep filter" gqlquery: | query { queryAuthor { @@ -1238,8 +1189,7 @@ } -- - name: "Deep filter with has filter" +- name: "Deep filter with has filter" gqlquery: | query { queryAuthor { @@ -1260,8 +1210,7 @@ dgraph.uid : uid } } -- - name: "Deep filter with has filter on list of fields" +- name: "Deep filter with has filter on list of fields" gqlquery: | query { queryAuthor { @@ -1283,8 +1232,7 @@ } } -- - name: "Deep filter with has and other filters" +- name: "Deep filter with has and other filters" gqlquery: | query { queryAuthor { @@ -1305,8 +1253,7 @@ dgraph.uid : uid } } -- - name: "Deep filter with first" +- name: "Deep filter with first" gqlquery: | query { queryAuthor { @@ -1328,8 +1275,7 @@ } } -- - name: "Deep filter with order, first and offset" +- name: "Deep filter with order, first and offset" gqlquery: | query { queryAuthor { @@ -1351,8 +1297,7 @@ } } -- - name: "Deep filter with multiple order, first and offset" +- name: "Deep filter with multiple order, first and offset" gqlquery: | query { queryAuthor { @@ -1374,8 +1319,7 @@ } } -- - name: "Float with large exponentiation" +- name: "Float with large exponentiation" gqlquery: | query { queryAuthor(filter:{ reputation: { gt: 123456789.113 } }) { @@ -1390,8 +1334,7 @@ } } -- - name: "All Float filters work" +- name: "All Float filters work" gqlquery: | query { queryAuthor(filter: { reputation: { gt: 1.1 }, or: { reputation: { ge: 1.1 }, or: { reputation: { lt: 1.1 }, or: { reputation: { le: 1.1 }, or: { reputation: { eq: 1.1 } } } } } } ) { @@ -1406,8 +1349,7 @@ } } -- - name: "All DateTime filters work" +- name: "All DateTime filters work" gqlquery: | query { queryAuthor(filter: { dob: { gt: "2000-01-01" }, or: { dob: { ge: "2000-01-01" }, or: { dob: { lt: "2000-01-01" }, or: { dob: { le: "2000-01-01" }, or: { dob: { eq: "2000-01-01" } } } } } } ) { @@ -1422,8 +1364,7 @@ } } -- - name: "All Int filters work" +- name: "All Int filters work" gqlquery: | query { queryPost(filter: { numLikes: { gt: 10 }, or: { numLikes: { ge: 10 }, or: { numLikes: { lt: 10 }, or: { numLikes: { le: 10 }, or: { numLikes: { eq: 10 } } } } } } ) { @@ -1438,8 +1379,7 @@ } } -- - name: "All String hash filters work" +- name: "All String hash filters work" gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" } } ) { @@ -1454,8 +1394,7 @@ } } -- - name: "All String exact filters work" +- name: "All String exact filters work" gqlquery: | query { queryCountry(filter: { name: { gt: "AAA" }, or: { name: { ge: "AAA" }, or: { name: { lt: "AAA" }, or: { name: { le: "AAA" }, or: { name: { eq: "AAA" } } } } } } ) { @@ -1470,8 +1409,7 @@ } } -- - name: "All String exact filters work with an array for OR" +- name: "All String exact filters work with an array for OR" gqlquery: | query { queryCountry(filter: { name: { gt: "AAA" }, or: [{ name: { ge: "AAA" }}, { name: { lt: "AAA" }}, { name: { le: "AAA" }}, { name: { eq: "AAA" } }] }) { @@ -1486,8 +1424,7 @@ } } -- - name: "All String exact filters work with an array for AND" +- name: "All String exact filters work with an array for AND" gqlquery: | query { queryCountry(filter: { name: { gt: "AAA" }, and: [{ name: { ge: "AAA" }}, { name: { lt: "AAA" }}, { name: { le: "AAA" }}, { name: { eq: "AAA" } }] }) { @@ -1503,8 +1440,7 @@ } -- - name: "Represent (A OR B) AND (C OR D)" +- name: "Represent (A OR B) AND (C OR D)" gqlquery: | query { queryCountry(filter: { and: [{ name: { gt: "AAA" }, or: { name: { lt: "XXX" }}}, { name: { gt : "CCC" }, or: { name: { lt: "MMM" }}}] }) { @@ -1519,8 +1455,7 @@ } } -- - name: "All String term filters work" +- name: "All String term filters work" gqlquery: | query { queryPost(filter: { title: { anyofterms: "GraphQL"}, or: { title: { allofterms: "GraphQL" } } } ) { @@ -1536,8 +1471,7 @@ } -- - name: "All String fulltext filters work" +- name: "All String fulltext filters work" gqlquery: | query { queryPost(filter: { text: { anyoftext: "GraphQL"}, or: { text: { alloftext: "GraphQL" } } } ) { @@ -1552,8 +1486,7 @@ } } -- - name: "All String regexp filters work" +- name: "All String regexp filters work" gqlquery: | query { queryCountry(filter: { name: { regexp: "/.*ust.*/" }}) { @@ -1568,8 +1501,7 @@ } } -- - name: "Aggregate Query" +- name: "Aggregate Query" gqlquery: | query { aggregateCountry(filter: { name: { regexp: "/.*ust.*/" }}) { @@ -1595,8 +1527,7 @@ } } -- - name: "Skip directive" +- name: "Skip directive" gqlquery: | query ($skipTrue: Boolean!, $skipFalse: Boolean!) { getAuthor(id: "0x1") { @@ -1620,8 +1551,7 @@ } } -- - name: "Include directive" +- name: "Include directive" gqlquery: | query ($includeTrue: Boolean!, $includeFalse: Boolean!) { queryAuthor { @@ -1645,8 +1575,7 @@ } } -- - name: "Include only fields for which skip is !false or include is true" +- name: "Include only fields for which skip is !false or include is true" variables: includeFalse: false includeTrue: true @@ -1681,8 +1610,7 @@ } } -- - name: "Cascade directive on get query" +- name: "Cascade directive on get query" gqlquery: | query { getAuthor(id: "0x1") @cascade { @@ -1704,8 +1632,7 @@ } } -- - name: "Cascade directive on filter query" +- name: "Cascade directive on filter query" gqlquery: | query { queryAuthor @cascade { @@ -1727,8 +1654,7 @@ } } -- - name: "Cascade directive on query field" +- name: "Cascade directive on query field" gqlquery: | query { queryAuthor { @@ -1750,8 +1676,7 @@ } } -- - name: "Cascade directive on root query and query field" +- name: "Cascade directive on root query and query field" gqlquery: | query { queryAuthor @cascade { @@ -1773,8 +1698,7 @@ } } -- - name: "Parameterized Cascade directive on filter query" +- name: "Parameterized Cascade directive on filter query" gqlquery: | query { queryAuthor @cascade(fields:["dob"]) { @@ -1798,8 +1722,7 @@ } } -- - name: "Parameterized Cascade directive on get query" +- name: "Parameterized Cascade directive on get query" gqlquery: | query { getAuthor(id: "0x1") @cascade(fields:["dob"]) { @@ -1823,8 +1746,7 @@ } } -- - name: "Parameterized Cascade directive on query field" +- name: "Parameterized Cascade directive on query field" gqlquery: | query { queryAuthor { @@ -1848,8 +1770,7 @@ } } -- - name: "Parameterized Cascade directive on root and query field" +- name: "Parameterized Cascade directive on root and query field" gqlquery: | query { queryAuthor @cascade(fields:["dob"]) { @@ -1875,8 +1796,7 @@ } } -- - name: "Parameterized Cascade directive with multiple parameters on root and query field" +- name: "Parameterized Cascade directive with multiple parameters on root and query field" gqlquery: | query { queryAuthor @cascade(fields:["dob","reputation","id"]) { @@ -1902,8 +1822,7 @@ } } -- - name: "Parameterized Cascade directive with argument at outer level which is not present in inner level " +- name: "Parameterized Cascade directive with argument at outer level which is not present in inner level " gqlquery: | query { queryAuthor @cascade(fields:["dob"]) { @@ -1929,8 +1848,7 @@ } } -- - name: "parameterized cascade with interface implementation Human" +- name: "parameterized cascade with interface implementation Human" gqlquery: | query { queryHuman @cascade(fields:["id","name","ename","dob"]) { @@ -1952,8 +1870,7 @@ } } -- - name: "parameterized cascade with interface Character" +- name: "parameterized cascade with interface Character" gqlquery: | query { queryCharacter @cascade(fields:["id","name"]) { @@ -1970,8 +1887,7 @@ } } -- - name: "Parameterized Cascade directive on root and nested field using variables" +- name: "Parameterized Cascade directive on root and nested field using variables" gqlquery: | query($fieldsRoot:[String],$fieldsDeep:[String]) { queryAuthor @cascade(fields: $fieldsRoot) { @@ -2007,8 +1923,7 @@ } } -- - name: "getHuman which implements an interface" +- name: "getHuman which implements an interface" gqlquery: | query { getHuman(id: "0x1") { @@ -2030,8 +1945,7 @@ } } -- - name: "queryHuman which implements an interface" +- name: "queryHuman which implements an interface" gqlquery: | query { queryHuman { @@ -2053,8 +1967,7 @@ } } -- - name: "Get Query on interface whose implementation contains Auth rules." +- name: "Get Query on interface whose implementation contains Auth rules." gqlquery: | query { getX(id: "0x1") { @@ -2067,8 +1980,7 @@ getX() } -- - name: "Query on interface whose implementation contains Auth rules." +- name: "Query on interface whose implementation contains Auth rules." gqlquery: | query { queryX { @@ -2081,8 +1993,7 @@ queryX() } -- - name: "filter with order for type which implements an interface" +- name: "filter with order for type which implements an interface" gqlquery: | query { queryHuman (filter: { name: { anyofterms: "GraphQL" } }, order: { asc: ename }) { @@ -2102,8 +2013,7 @@ } } -- - name: "queryCharacter with fragment for human" +- name: "queryCharacter with fragment for human" gqlquery: | query { queryCharacter { @@ -2126,8 +2036,7 @@ } } -- - name: "queryCharacter with fragment on multiple types" +- name: "queryCharacter with fragment on multiple types" gqlquery: | query { queryCharacter { @@ -2154,8 +2063,7 @@ } } -- - name: "fragment on interface implemented by type which implements multiple interfaces in query on some other interface" +- name: "fragment on interface implemented by type which implements multiple interfaces in query on some other interface" gqlquery: | query { queryCharacter { @@ -2180,8 +2088,7 @@ } } -- - name: "Filter with id uses uid func at root." +- name: "Filter with id uses uid func at root." gqlquery: | query { queryAuthor(filter: { id: ["0x1", "0x2"], and: { name: { eq: "A. N. Author" } }}) { @@ -2196,8 +2103,7 @@ } } -- - name: "Between filter" +- name: "Between filter" gqlquery: | query { queryPost(filter: { numLikes: { between : { min :10, max: 20 }}}) { @@ -2214,8 +2120,7 @@ } } -- - name: "deep Between filter" +- name: "deep Between filter" gqlquery: | query{ queryAuthor(filter: {reputation: {between: {min:6.0, max: 7.2}}}){ @@ -2241,8 +2146,7 @@ } } -- - name: "Filter with id inside and argument doesn't use uid func at root." +- name: "Filter with id inside and argument doesn't use uid func at root." gqlquery: | query { queryAuthor(filter: { name: { eq: "A. N. Author" }, and: { id: ["0x1", "0x2"] }}) { @@ -2257,8 +2161,7 @@ } } -- - name: "Filter with id and not translates correctly.." +- name: "Filter with id and not translates correctly.." gqlquery: | query { queryAuthor(filter: { not: { id: ["0x1", "0x2"] }}) { @@ -2273,8 +2176,7 @@ } } -- - name: "Deep filter with id" +- name: "Deep filter with id" gqlquery: | query { queryAuthor { @@ -2296,8 +2198,7 @@ } } -- - name: "Deep filter with id in not key" +- name: "Deep filter with id in not key" gqlquery: | query { queryAuthor { @@ -2319,8 +2220,7 @@ } } -- - name: "Pagination and Order at root node with UID." +- name: "Pagination and Order at root node with UID." gqlquery: | query { queryAuthor(filter: { id: ["0x1", "0x2"] }, order: {asc: name}, first: 0, offset: 1 ) { @@ -2335,8 +2235,7 @@ } } -- - name: "Order at root node with UID." +- name: "Order at root node with UID." gqlquery: | query { queryAuthor(filter: { id: ["0x1", "0x2"] }, order: {asc: name}) { @@ -2351,8 +2250,7 @@ } } -- - name: "Order at root node without UID." +- name: "Order at root node without UID." gqlquery: | query { queryAuthor(order: {asc: name}) { @@ -2367,8 +2265,7 @@ } } -- - name: "Order and Pagination at root node without UID." +- name: "Order and Pagination at root node without UID." gqlquery: | query { queryAuthor(order: {asc: name}, first: 2, offset: 3) { @@ -2384,8 +2281,7 @@ } -- - name: "Filter with no valid id construct the right query with type func at root." +- name: "Filter with no valid id construct the right query with type func at root." gqlquery: | query { queryAuthor(filter: { id: ["alice", "bob"], and: { name: { eq: "A. N. Author" } }}) { @@ -2400,8 +2296,7 @@ } } -- - name: "Filter with id only includes valid id in dgquery." +- name: "Filter with id only includes valid id in dgquery." gqlquery: | query { queryAuthor(filter: { id: ["0x1", "bob"], and: { name: { eq: "A. N. Author" } }}) { @@ -2416,8 +2311,7 @@ } } -- - name: "Get editor without supplying anything" +- name: "Get editor without supplying anything" gqlquery: | query { getEditor { @@ -2432,8 +2326,7 @@ } } -- - name: "Get editor using code" +- name: "Get editor using code" gqlquery: | query { getEditor(code: "tolstoy") { @@ -2448,8 +2341,7 @@ } } -- - name: "Get editor using both code and id" +- name: "Get editor using both code and id" gqlquery: | query { getEditor(code: "tolstoy", id: "0x1") { @@ -2464,8 +2356,7 @@ } } -- - name: "Get with XID where no ID in type" +- name: "Get with XID where no ID in type" gqlquery: | query { getState(code: "NSW") { @@ -2480,8 +2371,7 @@ } } -- - name: "Query editor using code" +- name: "Query editor using code" gqlquery: | query { queryEditor(filter: { code: { eq: "editor" }, and: { name: { eq: "A. N. Editor" }}}) { @@ -2496,8 +2386,7 @@ } } -- - name: "Query editor using code and uid" +- name: "Query editor using code and uid" gqlquery: | query { queryEditor(filter: { id: ["0x1"], and: { code: { eq: "editor"}}}) { @@ -2512,8 +2401,7 @@ } } -- - name: "Query along reverse edge is converted appropriately" +- name: "Query along reverse edge is converted appropriately" gqlquery: | query { queryMovie { @@ -2535,8 +2423,7 @@ } } -- - name: "deprecated fields can be queried" +- name: "deprecated fields can be queried" gqlquery: | query { queryCategory { @@ -2552,8 +2439,7 @@ } } -- - name: "Password query" +- name: "Password query" gqlquery: | query { checkUserPassword(name: "user1", pwd: "Password") { @@ -2571,8 +2457,7 @@ } } -- - name: "Password query with alias" +- name: "Password query with alias" gqlquery: | query { verify : checkUserPassword(name: "user1", pwd: "Password") { @@ -2749,8 +2634,7 @@ dgraph.uid : uid } } -- - name: "querying a non-inbuiltType field multiple times with different aliases should reflect in rewriting" +- name: "querying a non-inbuiltType field multiple times with different aliases should reflect in rewriting" gqlquery: |- query { queryAuthor { @@ -2783,8 +2667,7 @@ } } -- - name: "querying field multiple times with different aliases and same filters" +- name: "querying field multiple times with different aliases and same filters" gqlquery: |- query { queryAuthor { @@ -2816,8 +2699,7 @@ dgraph.uid : uid } } -- - name: "Query with Same Alias" +- name: "Query with Same Alias" gqlquery: |- query { queryAuthor { @@ -2844,8 +2726,7 @@ dgraph.uid : uid } } -- - name: "Aggregate Query with multiple aliases" +- name: "Aggregate Query with multiple aliases" gqlquery: | query{ queryAuthor{ @@ -3124,8 +3005,7 @@ } } -- - name: "Count query at child level" +- name: "Count query at child level" gqlquery: | query { queryCountry { @@ -3144,8 +3024,7 @@ } } -- - name: "Aggregate query at child level with filter and multiple aggregate fields" +- name: "Aggregate query at child level with filter and multiple aggregate fields" gqlquery: | query { queryCountry { @@ -3189,8 +3068,7 @@ } } -- - name: "Count query at child level with filter" +- name: "Count query at child level with filter" gqlquery: | query { queryCountry { @@ -3216,8 +3094,7 @@ } } -- - name: "Deep child level get query with count" +- name: "Deep child level get query with count" gqlquery: | query { getAuthor(id: "0x1") { @@ -3241,8 +3118,7 @@ } } -- - name: "Aggregate Query with Sum and Avg" +- name: "Aggregate Query with Sum and Avg" gqlquery: | query { aggregateTweets() { @@ -3268,14 +3144,13 @@ } } -- - name: "query using single ID in filter" +- name: "query using single ID in filter" gqlquery: | - query { - queryAuthor(filter:{id: "0x1"}) { - name - } + query { + queryAuthor(filter:{id: "0x1"}) { + name } + } dgquery: |- query { queryAuthor(func: uid(0x1)) @filter(type(Author)) { @@ -3284,8 +3159,7 @@ } } -- - name: "entities query for extended type having @key field of ID type" +- name: "entities query for extended type having @key field of ID type" gqlquery: | query { _entities(representations: [{__typename: "Astronaut", id: "0x1" },{__typename: "Astronaut", id: "0x2" }]) { @@ -3296,7 +3170,7 @@ } } } - dgquery: |- + dgquery: |- query { _entities(func: eq(Astronaut.id, "0x1", "0x2"), orderasc: Astronaut.id) @filter(type(Astronaut)) { dgraph.type @@ -3308,8 +3182,7 @@ } } -- - name: "entities query for extended type having @key field of string type with @id directive" +- name: "entities query for extended type having @key field of string type with @id directive" gqlquery: | query { _entities(representations: [{__typename: "SpaceShip", id: "0x1" },{__typename: "SpaceShip", id: "0x2" }]) { @@ -3320,7 +3193,7 @@ } } } - dgquery: |- + dgquery: |- query { _entities(func: eq(SpaceShip.id, "0x1", "0x2"), orderasc: SpaceShip.id) @filter(type(SpaceShip)) { dgraph.type @@ -3332,8 +3205,7 @@ } } -- - name: "get query with multiple @id and an ID field" +- name: "get query with multiple @id and an ID field" gqlquery: | query { getBook(id: "0x1", title: "GraphQL", ISBN: "001HB") { @@ -3358,8 +3230,7 @@ } } -- - name: "get query with multiple @id fields " +- name: "get query with multiple @id fields " gqlquery: | query { getBook(title: "GraphQL", ISBN: "001HB") { @@ -3431,3 +3302,27 @@ dgraph.uid : uid } } + +- name: "get query on interface with @id field having interface argument set" + gqlquery: | + query { + getMember(refID: "101") { + refID + name + fineAccumulated + ... on SportsMember { + plays + } + } + } + dgquery: |- + query { + getMember(func: eq(Member.refID, "101")) @filter(type(Member)) { + dgraph.type + Member.refID : Member.refID + Member.name : Member.name + Member.fineAccumulated : Member.fineAccumulated + SportsMember.plays : SportsMember.plays + dgraph.uid : uid + } + } diff --git a/graphql/resolve/schema.graphql b/graphql/resolve/schema.graphql index 986010dff8c..cc5ffa25db9 100644 --- a/graphql/resolve/schema.graphql +++ b/graphql/resolve/schema.graphql @@ -10,169 +10,169 @@ type Hotel { } type Country { - id: ID! - name: String! @search(by: [trigram, exact]) - states: [State] @hasInverse(field: country) + id: ID! + name: String! @search(by: [trigram, exact]) + states: [State] @hasInverse(field: country) } type State { - code: String! @id - country: Country - name: String! - capital: String + code: String! @id + country: Country + name: String! + capital: String } type Author { - id: ID! - name: String! @search(by: [hash]) - dob: DateTime @search - reputation: Float @search - country: Country - posts: [Post!] @hasInverse(field: author) + id: ID! + name: String! @search(by: [hash]) + dob: DateTime @search + reputation: Float @search + country: Country + posts: [Post!] @hasInverse(field: author) } type Editor { - id: ID! - code: String! @id - name: String! @search(by: [hash]) + id: ID! + code: String! @id + name: String! @search(by: [hash]) } type Post { - postID: ID! - title: String! @search(by: [term]) - text: String @search(by: [fulltext]) - tags: [String] @search(by: [exact]) - numLikes: Int @search - isPublished: Boolean @search - postType: [PostType] @search - author: Author! - category: Category @hasInverse(field: posts) - comments: [Comment] - ps: PostSecret + postID: ID! + title: String! @search(by: [term]) + text: String @search(by: [fulltext]) + tags: [String] @search(by: [exact]) + numLikes: Int @search + isPublished: Boolean @search + postType: [PostType] @search + author: Author! + category: Category @hasInverse(field: posts) + comments: [Comment] + ps: PostSecret } type PostSecret { - title: String! @id + title: String! @id } type Category { - id: ID - name: String - posts: [Post] - iAmDeprecated: String @deprecated(reason: "because") + id: ID + name: String + posts: [Post] + iAmDeprecated: String @deprecated(reason: "because") } enum PostType { - Fact - Question - Opinion + Fact + Question + Opinion } interface Character { - id: ID! - name: String! @search + id: ID! + name: String! @search } interface Employee { - ename: String! + ename: String! } type Director implements Character { - movies: [String!] + movies: [String!] } type Human implements Character & Employee { - dob: DateTime - female: Boolean + dob: DateTime + female: Boolean } # just for testing filters on enum types type Verification { - name: String @search(by: [exact]) - status: [Status!]! @search(by: [exact]) - prevStatus: Status! @search + name: String @search(by: [exact]) + status: [Status!]! @search(by: [exact]) + prevStatus: Status! @search } enum Status { - ACTIVE - INACTIVE - DEACTIVATED + ACTIVE + INACTIVE + DEACTIVATED } # just for testing singluar (non-list) edges in both directions type House { - id: ID! - owner: Owner @hasInverse(field: house) + id: ID! + owner: Owner @hasInverse(field: house) } type Owner { - id: ID! - house: House + id: ID! + house: House } # for testing ~reverse predicate in @dgraph directive type Movie { - id: ID! - name: String! - director: [MovieDirector] @dgraph(pred: "~directed.movies") + id: ID! + name: String! + director: [MovieDirector] @dgraph(pred: "~directed.movies") } type MovieDirector { - id: ID! - name: String! - directed: [Movie] @dgraph(pred: "directed.movies") + id: ID! + name: String! + directed: [Movie] @dgraph(pred: "directed.movies") } type Lab { - name: String! @id - computers: [Computer] + name: String! @id + computers: [Computer] } # just for testing XID remove in list type Computer { - owners: [ComputerOwner!] - name: String! @id + owners: [ComputerOwner!] + name: String! @id } type ComputerOwner { - name: String! @id - nickName: String - computers: Computer! @hasInverse(field: owners) + name: String! @id + nickName: String + computers: Computer! @hasInverse(field: owners) } type User @secret(field: "pwd") { - name: String! @id + name: String! @id } # For testing duplicate XID in single mutation type District { - code: String! @id - name: String! - cities: [City] @hasInverse(field: district) + code: String! @id + name: String! + cities: [City] @hasInverse(field: district) } type City { - id: ID! - name: String! - district: District + id: ID! + name: String! + district: District } # For testing duplicate XID in single mutation for interface interface People { - id: ID! - xid: String! @id - name: String! + id: ID! + xid: String! @id + name: String! } type Teacher implements People { - subject: String - teaches: [Student] + subject: String + teaches: [Student] } type Student implements People { - taughtBy: [Teacher] @hasInverse(field: "teaches") + taughtBy: [Teacher] @hasInverse(field: "teaches") } type Comment { @@ -180,64 +180,64 @@ type Comment { author: String! title: String content: String @custom(http: { - url: "http://api-gateway.com/post/", - method: "GET", - operation: "batch", - body: "{ myId: $id, theAuthor: $author}", - forwardHeaders: ["X-App-Token"]}) + url: "http://api-gateway.com/post/", + method: "GET", + operation: "batch", + body: "{ myId: $id, theAuthor: $author}", + forwardHeaders: ["X-App-Token"]}) url: String! @id ups: Int! downs: Int relatedUsers: [User] @custom(http: { - url: "http://api-gateway.com/relatedPosts", - method: "POST", - operation: "single", - body: "{ myId: $url }"}) + url: "http://api-gateway.com/relatedPosts", + method: "POST", + operation: "single", + body: "{ myId: $url }"}) } type Query { - myFavoriteMovies(id: ID!, name: String!, num: Int): [Movie] @custom(http: { - url: "http://myapi.com/favMovies/$id?name=$name&num=$num", - method: "GET" - }) + myFavoriteMovies(id: ID!, name: String!, num: Int): [Movie] @custom(http: { + url: "http://myapi.com/favMovies/$id?name=$name&num=$num", + method: "GET" + }) - myFavoriteMoviesPart2(id: ID!, name: String!, num: Int): [Movie] @custom(http: { - url: "http://myapi.com/favMovies/$id?name=$name&num=$num", - method: "POST", - body: "{ id: $id, name: $name, director: { number: $num }}", - forwardHeaders: ["X-App-Token", "Auth0-token"] - }) + myFavoriteMoviesPart2(id: ID!, name: String!, num: Int): [Movie] @custom(http: { + url: "http://myapi.com/favMovies/$id?name=$name&num=$num", + method: "POST", + body: "{ id: $id, name: $name, director: { number: $num }}", + forwardHeaders: ["X-App-Token", "Auth0-token"] + }) } input MovieDirectorInput { - id: ID - name: String - directed: [MovieInput] + id: ID + name: String + directed: [MovieInput] } input MovieInput { - id: ID - name: String - director: [MovieDirectorInput] + id: ID + name: String + director: [MovieDirectorInput] } type Mutation { - createMyFavouriteMovies(input: [MovieInput!]): [Movie] @custom(http: { - url: "http://myapi.com/favMovies", - method: "POST", - body: "{ movies: $input}", - forwardHeaders: ["X-App-Token", "Auth0-token"] - }) - updateMyFavouriteMovie(id: ID!, input: MovieInput!): Movie @custom(http: { - url: "http://myapi.com/favMovies/$id", - method: "PATCH", - body: "{ movie: $input}" - }) - deleteMyFavouriteMovie(id: ID!): Movie @custom(http: { - url: "http://myapi.com/favMovies/$id", - method: "DELETE" - }) + createMyFavouriteMovies(input: [MovieInput!]): [Movie] @custom(http: { + url: "http://myapi.com/favMovies", + method: "POST", + body: "{ movies: $input}", + forwardHeaders: ["X-App-Token", "Auth0-token"] + }) + updateMyFavouriteMovie(id: ID!, input: MovieInput!): Movie @custom(http: { + url: "http://myapi.com/favMovies/$id", + method: "PATCH", + body: "{ movie: $input}" + }) + deleteMyFavouriteMovie(id: ID!): Movie @custom(http: { + url: "http://myapi.com/favMovies/$id", + method: "DELETE" + }) } type Message { @@ -252,33 +252,33 @@ interface X { } type Y implements X @auth( query: { rule: """ - query($USER: String!) { - queryY(filter: { username: { eq: $USER } }) { - __typename - } - } + query($USER: String!) { + queryY(filter: { username: { eq: $USER } }) { + __typename + } + } """ } ){ - userRole: String @search(by: [hash]) + userRole: String @search(by: [hash]) } type Post1 { - id: String! @id - content: String - comments: [Comment1] + id: String! @id + content: String + comments: [Comment1] } type Person1 { - id: String! @id - friends: [Person1] @hasInverse(field: friends) - closeFriends: [Person1] @hasInverse(field: closeFriends) - name: String! @id + id: String! @id + friends: [Person1] @hasInverse(field: friends) + closeFriends: [Person1] @hasInverse(field: closeFriends) + name: String! @id } type Comment1 { - id: String! @id - message: String - replies: [Comment1] + id: String! @id + message: String + replies: [Comment1] } type Tweets { @@ -460,3 +460,35 @@ type LinkZ { f7: String! @id f4: [LinkX] @dgraph(pred: "link") } + +interface Member { + refID: String! @id (interface:true) + name: String! @id + itemsIssued: [String] + fineAccumulated: Int +} + +interface Team { + teamID: String! @id (interface:true) + teamName: String! @id +} + +type LibraryMember implements Member { + interests: [String] + readHours: String +} + +type SportsMember implements Member & Team { + plays: String + playerRating: Int +} + +type CricketTeam implements Team { + numOfBatsmans: Int + numOfBowlers: Int +} + +type LibraryManager { + name: String! @id + manages: [LibraryMember] +} \ No newline at end of file diff --git a/graphql/schema/gqlschema.go b/graphql/schema/gqlschema.go index 920096aacd5..c2398514260 100644 --- a/graphql/schema/gqlschema.go +++ b/graphql/schema/gqlschema.go @@ -39,6 +39,7 @@ const ( dgraphPredArg = "pred" idDirective = "id" + idDirectiveInterfaceArg = "interface" subscriptionDirective = "withSubscription" secretDirective = "secret" authDirective = "auth" @@ -276,7 +277,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( @@ -307,7 +308,7 @@ directive @generate( directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM @@ -1968,18 +1969,14 @@ func addGetQuery(schema *ast.Schema, defn *ast.Definition, providesTypeMap map[s }, }) } + if hasXIDField { - if defn.Kind == "INTERFACE" { - qry.Directives = append( - qry.Directives, &ast.Directive{Name: deprecatedDirective, - Arguments: ast.ArgumentList{&ast.Argument{Name: "reason", - Value: &ast.Value{Raw: "@id argument for get query on interface is being deprecated, " + - "it will be removed in v21.11.0, " + - "please update your query to not use that argument", - Kind: ast.StringValue}}}}) - } + var idWithoutUniqueArgExists bool for _, fld := range defn.Fields { if hasIDDirective(fld) { + if !hasInterfaceArg(fld) { + idWithoutUniqueArgExists = true + } qry.Arguments = append(qry.Arguments, &ast.ArgumentDefinition{ Name: fld.Name, Type: &ast.Type{ @@ -1989,6 +1986,16 @@ func addGetQuery(schema *ast.Schema, defn *ast.Definition, providesTypeMap map[s }) } } + if defn.Kind == "INTERFACE" && idWithoutUniqueArgExists { + qry.Directives = append( + qry.Directives, &ast.Directive{Name: deprecatedDirective, + Arguments: ast.ArgumentList{&ast.Argument{Name: "reason", + Value: &ast.Value{Raw: "@id argument for get query on interface is being" + + " deprecated. Only those @id fields which have interface argument" + + " set to true will be available in getQuery argument on interface" + + " post v21.11.0, please update your schema accordingly.", + Kind: ast.StringValue}}}}) + } } schema.Query.Fields = append(schema.Query.Fields, qry) subs := defn.Directives.ForName(subscriptionDirective) diff --git a/graphql/schema/gqlschema_test.yml b/graphql/schema/gqlschema_test.yml index ba435b69366..a7458e1d616 100644 --- a/graphql/schema/gqlschema_test.yml +++ b/graphql/schema/gqlschema_test.yml @@ -2889,6 +2889,18 @@ invalid_schemas: { "message": "Type TwitterUser; @lambdaOnMutate directive not allowed along with @remote directive.", "locations": [{"line": 1, "column": 27}]} ] + - name: "@id field can't have interface argument when it's defined inside a type" + input: | + type Person { + name: String! @id(interface:true) + age: Int + } + errlist: [ + { "message": "Type Person; Field name: @id field with interface argument + can only be defined in interface,not in Type", + "locations": [ { "line": 2, "column": 18 } ] }, + ] + valid_schemas: - name: "Multiple fields with @id directive should be allowed" input: | @@ -3379,3 +3391,37 @@ valid_schemas: type Z { f4: [X] @dgraph(pred: "link") } + + - name: "valid schema with @id directive having interface argument in interface" + input: | + interface Member { + refID: String! @id(interface: true) + name: String! @id + itemsIssued: [String] + fineAccumulated: Int + } + + interface Team { + teamID: String! @id(interface: true) + teamName: String! @id + } + + type LibraryMember implements Member { + interests: [String] + readHours: String + } + + type SportsMember implements Member & Team { + plays: String + playerRating: Int + } + + type CricketTeam implements Team { + numOfBatsmans: Int + numOfBowlers: Int + } + + type LibraryManager { + name: String! @id + manages: [LibraryMember] + } diff --git a/graphql/schema/rules.go b/graphql/schema/rules.go index f835a2e4d8e..c6979234b6e 100644 --- a/graphql/schema/rules.go +++ b/graphql/schema/rules.go @@ -2074,12 +2074,26 @@ func idValidation(sch *ast.Schema, if field.Type.String() == "String!" || field.Type.String() == "Int!" || field.Type.String() == "Int64!" { + + var inherited bool + for _, implements := range sch.Implements[typ.Name] { + if implements.Fields.ForName(field.Name) != nil { + inherited = true + } + } + if typ.Kind != "INTERFACE" && hasInterfaceArg(field) && !inherited { + return []*gqlerror.Error{gqlerror.ErrorPosf( + dir.Position, + "Type %s; Field %s: @id field with interface argument can only be defined"+ + " in interface,not in Type", typ.Name, field.Name)} + } return nil } return []*gqlerror.Error{gqlerror.ErrorPosf( dir.Position, "Type %s; Field %s: with @id directive must be of type String!, Int! or Int64!, not %s", typ.Name, field.Name, field.Type.String())} + } func apolloKeyValidation(sch *ast.Schema, typ *ast.Definition) gqlerror.List { diff --git a/graphql/schema/testdata/apolloservice/output/auth-directive.graphql b/graphql/schema/testdata/apolloservice/output/auth-directive.graphql index e92aef33b21..1d3488c0b27 100644 --- a/graphql/schema/testdata/apolloservice/output/auth-directive.graphql +++ b/graphql/schema/testdata/apolloservice/output/auth-directive.graphql @@ -192,7 +192,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM diff --git a/graphql/schema/testdata/apolloservice/output/custom-directive.graphql b/graphql/schema/testdata/apolloservice/output/custom-directive.graphql index 137953258e2..0ffe521b151 100644 --- a/graphql/schema/testdata/apolloservice/output/custom-directive.graphql +++ b/graphql/schema/testdata/apolloservice/output/custom-directive.graphql @@ -184,7 +184,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM diff --git a/graphql/schema/testdata/apolloservice/output/extended-types.graphql b/graphql/schema/testdata/apolloservice/output/extended-types.graphql index 852620a45fb..31024cd9323 100644 --- a/graphql/schema/testdata/apolloservice/output/extended-types.graphql +++ b/graphql/schema/testdata/apolloservice/output/extended-types.graphql @@ -198,7 +198,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM diff --git a/graphql/schema/testdata/apolloservice/output/generate-directive.graphql b/graphql/schema/testdata/apolloservice/output/generate-directive.graphql index 9330e483219..fd38dab07b9 100644 --- a/graphql/schema/testdata/apolloservice/output/generate-directive.graphql +++ b/graphql/schema/testdata/apolloservice/output/generate-directive.graphql @@ -194,7 +194,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM diff --git a/graphql/schema/testdata/apolloservice/output/single-extended-type.graphql b/graphql/schema/testdata/apolloservice/output/single-extended-type.graphql index acf1c5611e8..0704e8fd6f3 100644 --- a/graphql/schema/testdata/apolloservice/output/single-extended-type.graphql +++ b/graphql/schema/testdata/apolloservice/output/single-extended-type.graphql @@ -179,7 +179,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM diff --git a/graphql/schema/testdata/schemagen/input/interface-with-id-directive.graphql b/graphql/schema/testdata/schemagen/input/interface-with-id-directive.graphql index 13cfc0547c8..6520d123ec7 100644 --- a/graphql/schema/testdata/schemagen/input/interface-with-id-directive.graphql +++ b/graphql/schema/testdata/schemagen/input/interface-with-id-directive.graphql @@ -1,5 +1,6 @@ interface LibraryItem { - refID: String! @id + refID: String! @id(interface:false) + itemID: String! @id(interface:true) } type Book implements LibraryItem { diff --git a/graphql/schema/testdata/schemagen/output/apollo-federation.graphql b/graphql/schema/testdata/schemagen/output/apollo-federation.graphql index 9b2891e8d03..91642aebb01 100644 --- a/graphql/schema/testdata/schemagen/output/apollo-federation.graphql +++ b/graphql/schema/testdata/schemagen/output/apollo-federation.graphql @@ -214,7 +214,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/auth-on-interfaces.graphql b/graphql/schema/testdata/schemagen/output/auth-on-interfaces.graphql index f8451784789..ea87104b74f 100644 --- a/graphql/schema/testdata/schemagen/output/auth-on-interfaces.graphql +++ b/graphql/schema/testdata/schemagen/output/auth-on-interfaces.graphql @@ -196,7 +196,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/authorization.graphql b/graphql/schema/testdata/schemagen/output/authorization.graphql index 97ec90740b4..8d8a2da3d46 100644 --- a/graphql/schema/testdata/schemagen/output/authorization.graphql +++ b/graphql/schema/testdata/schemagen/output/authorization.graphql @@ -192,7 +192,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/comments-and-descriptions.graphql b/graphql/schema/testdata/schemagen/output/comments-and-descriptions.graphql index 08987e9adb8..cf11ecc3856 100755 --- a/graphql/schema/testdata/schemagen/output/comments-and-descriptions.graphql +++ b/graphql/schema/testdata/schemagen/output/comments-and-descriptions.graphql @@ -205,7 +205,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/custom-dql-query-with-subscription.graphql b/graphql/schema/testdata/schemagen/output/custom-dql-query-with-subscription.graphql index a3fc18824ff..a6c89246ef5 100755 --- a/graphql/schema/testdata/schemagen/output/custom-dql-query-with-subscription.graphql +++ b/graphql/schema/testdata/schemagen/output/custom-dql-query-with-subscription.graphql @@ -193,7 +193,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/custom-mutation.graphql b/graphql/schema/testdata/schemagen/output/custom-mutation.graphql index cf2bc952534..1b48735de16 100644 --- a/graphql/schema/testdata/schemagen/output/custom-mutation.graphql +++ b/graphql/schema/testdata/schemagen/output/custom-mutation.graphql @@ -183,7 +183,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/custom-nested-types.graphql b/graphql/schema/testdata/schemagen/output/custom-nested-types.graphql index 33695d23be3..7184d90e5a8 100755 --- a/graphql/schema/testdata/schemagen/output/custom-nested-types.graphql +++ b/graphql/schema/testdata/schemagen/output/custom-nested-types.graphql @@ -200,7 +200,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/custom-query-mixed-types.graphql b/graphql/schema/testdata/schemagen/output/custom-query-mixed-types.graphql index a38f3ea8bbf..d441721cf27 100644 --- a/graphql/schema/testdata/schemagen/output/custom-query-mixed-types.graphql +++ b/graphql/schema/testdata/schemagen/output/custom-query-mixed-types.graphql @@ -184,7 +184,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/custom-query-not-dgraph-type.graphql b/graphql/schema/testdata/schemagen/output/custom-query-not-dgraph-type.graphql index c605b43a8e7..bbc666a87e1 100755 --- a/graphql/schema/testdata/schemagen/output/custom-query-not-dgraph-type.graphql +++ b/graphql/schema/testdata/schemagen/output/custom-query-not-dgraph-type.graphql @@ -183,7 +183,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/custom-query-with-dgraph-type.graphql b/graphql/schema/testdata/schemagen/output/custom-query-with-dgraph-type.graphql index c19a37615cd..a38c50561d8 100755 --- a/graphql/schema/testdata/schemagen/output/custom-query-with-dgraph-type.graphql +++ b/graphql/schema/testdata/schemagen/output/custom-query-with-dgraph-type.graphql @@ -179,7 +179,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/deprecated.graphql b/graphql/schema/testdata/schemagen/output/deprecated.graphql index df19d26c92a..52cb4ea15d4 100755 --- a/graphql/schema/testdata/schemagen/output/deprecated.graphql +++ b/graphql/schema/testdata/schemagen/output/deprecated.graphql @@ -179,7 +179,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-on-concrete-type-with-interfaces.graphql b/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-on-concrete-type-with-interfaces.graphql index 718771ade43..b1af0955121 100755 --- a/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-on-concrete-type-with-interfaces.graphql +++ b/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-on-concrete-type-with-interfaces.graphql @@ -196,7 +196,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-with-interfaces.graphql b/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-with-interfaces.graphql index 86c2966c11f..44da4eec782 100755 --- a/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-with-interfaces.graphql +++ b/graphql/schema/testdata/schemagen/output/dgraph-reverse-directive-with-interfaces.graphql @@ -196,7 +196,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/field-with-id-directive.graphql b/graphql/schema/testdata/schemagen/output/field-with-id-directive.graphql index 9157745c0b7..47653c74222 100755 --- a/graphql/schema/testdata/schemagen/output/field-with-id-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/field-with-id-directive.graphql @@ -193,7 +193,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/field-with-multiple-@id-fields.graphql b/graphql/schema/testdata/schemagen/output/field-with-multiple-@id-fields.graphql index 0ede01e438a..d8b93ef7b08 100755 --- a/graphql/schema/testdata/schemagen/output/field-with-multiple-@id-fields.graphql +++ b/graphql/schema/testdata/schemagen/output/field-with-multiple-@id-fields.graphql @@ -193,7 +193,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/field-with-reverse-predicate-in-dgraph-directive.graphql b/graphql/schema/testdata/schemagen/output/field-with-reverse-predicate-in-dgraph-directive.graphql index 3316b65d97b..f2145184ee6 100755 --- a/graphql/schema/testdata/schemagen/output/field-with-reverse-predicate-in-dgraph-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/field-with-reverse-predicate-in-dgraph-directive.graphql @@ -188,7 +188,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-all-empty.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-all-empty.graphql index 3650c045774..64d88e15dd9 100644 --- a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-all-empty.graphql +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-all-empty.graphql @@ -191,7 +191,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-circular.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-circular.graphql index 001091f2c1f..fd2ae029d78 100644 --- a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-circular.graphql +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-circular.graphql @@ -195,7 +195,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-custom-mutation.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-custom-mutation.graphql index b72b0056457..480d42cc906 100644 --- a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-custom-mutation.graphql +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-custom-mutation.graphql @@ -183,7 +183,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-directLink.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-directLink.graphql index a6848b2e499..ce22f674d5a 100644 --- a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-directLink.graphql +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-directLink.graphql @@ -193,7 +193,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/generate-directive.graphql b/graphql/schema/testdata/schemagen/output/generate-directive.graphql index e5ed4c5b2ba..046c376fd08 100644 --- a/graphql/schema/testdata/schemagen/output/generate-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/generate-directive.graphql @@ -194,7 +194,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/geo-type.graphql b/graphql/schema/testdata/schemagen/output/geo-type.graphql index 858da149138..7b007dbea08 100644 --- a/graphql/schema/testdata/schemagen/output/geo-type.graphql +++ b/graphql/schema/testdata/schemagen/output/geo-type.graphql @@ -185,7 +185,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/hasInverse-with-interface-having-directive.graphql b/graphql/schema/testdata/schemagen/output/hasInverse-with-interface-having-directive.graphql index 724e975f5e3..2a6e74d672c 100755 --- a/graphql/schema/testdata/schemagen/output/hasInverse-with-interface-having-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/hasInverse-with-interface-having-directive.graphql @@ -204,7 +204,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/hasInverse-with-interface.graphql b/graphql/schema/testdata/schemagen/output/hasInverse-with-interface.graphql index 68d762315a4..7262833b462 100755 --- a/graphql/schema/testdata/schemagen/output/hasInverse-with-interface.graphql +++ b/graphql/schema/testdata/schemagen/output/hasInverse-with-interface.graphql @@ -206,7 +206,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/hasInverse-with-type-having-directive.graphql b/graphql/schema/testdata/schemagen/output/hasInverse-with-type-having-directive.graphql index 724e975f5e3..2a6e74d672c 100755 --- a/graphql/schema/testdata/schemagen/output/hasInverse-with-type-having-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/hasInverse-with-type-having-directive.graphql @@ -204,7 +204,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/hasInverse.graphql b/graphql/schema/testdata/schemagen/output/hasInverse.graphql index ebf0fac6ef3..e550998e55a 100755 --- a/graphql/schema/testdata/schemagen/output/hasInverse.graphql +++ b/graphql/schema/testdata/schemagen/output/hasInverse.graphql @@ -185,7 +185,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/hasInverse_withSubscription.graphql b/graphql/schema/testdata/schemagen/output/hasInverse_withSubscription.graphql index 8106fa755a4..08cebd56299 100755 --- a/graphql/schema/testdata/schemagen/output/hasInverse_withSubscription.graphql +++ b/graphql/schema/testdata/schemagen/output/hasInverse_withSubscription.graphql @@ -185,7 +185,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/hasfilter.graphql b/graphql/schema/testdata/schemagen/output/hasfilter.graphql index 07084830a4c..e5adcc1acc6 100644 --- a/graphql/schema/testdata/schemagen/output/hasfilter.graphql +++ b/graphql/schema/testdata/schemagen/output/hasfilter.graphql @@ -187,7 +187,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/ignore-unsupported-directive.graphql b/graphql/schema/testdata/schemagen/output/ignore-unsupported-directive.graphql index 8b401ca912b..0d50f4fcb38 100755 --- a/graphql/schema/testdata/schemagen/output/ignore-unsupported-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/ignore-unsupported-directive.graphql @@ -186,7 +186,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/interface-with-dgraph-pred.graphql b/graphql/schema/testdata/schemagen/output/interface-with-dgraph-pred.graphql index 1bd51e0ccb5..a604dd070c5 100644 --- a/graphql/schema/testdata/schemagen/output/interface-with-dgraph-pred.graphql +++ b/graphql/schema/testdata/schemagen/output/interface-with-dgraph-pred.graphql @@ -195,7 +195,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/interface-with-id-directive.graphql b/graphql/schema/testdata/schemagen/output/interface-with-id-directive.graphql index 7efd61c5558..db2e3d3be50 100755 --- a/graphql/schema/testdata/schemagen/output/interface-with-id-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/interface-with-id-directive.graphql @@ -3,11 +3,13 @@ ####################### interface LibraryItem { - refID: String! @id + refID: String! @id(interface: false) + itemID: String! @id(interface: true) } type Book implements LibraryItem { - refID: String! @id + refID: String! @id(interface: false) + itemID: String! @id(interface: true) title: String author: String } @@ -189,7 +191,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( @@ -297,6 +299,8 @@ type BookAggregateResult { count: Int refIDMin: String refIDMax: String + itemIDMin: String + itemIDMax: String titleMin: String titleMax: String authorMin: String @@ -329,6 +333,8 @@ type LibraryItemAggregateResult { count: Int refIDMin: String refIDMax: String + itemIDMin: String + itemIDMax: String } type UpdateBookPayload { @@ -347,12 +353,14 @@ type UpdateLibraryPayload { enum BookHasFilter { refID + itemID title author } enum BookOrderable { refID + itemID title author } @@ -363,10 +371,12 @@ enum LibraryHasFilter { enum LibraryItemHasFilter { refID + itemID } enum LibraryItemOrderable { refID + itemID } ####################### @@ -375,6 +385,7 @@ enum LibraryItemOrderable { input AddBookInput { refID: String! + itemID: String! title: String author: String } @@ -385,6 +396,7 @@ input AddLibraryInput { input BookFilter { refID: StringHashFilter + itemID: StringHashFilter has: [BookHasFilter] and: [BookFilter] or: [BookFilter] @@ -404,6 +416,7 @@ input BookPatch { input BookRef { refID: String + itemID: String title: String author: String } @@ -417,6 +430,7 @@ input LibraryFilter { input LibraryItemFilter { refID: StringHashFilter + itemID: StringHashFilter has: [LibraryItemHasFilter] and: [LibraryItemFilter] or: [LibraryItemFilter] @@ -458,10 +472,10 @@ input UpdateLibraryInput { ####################### type Query { - getLibraryItem(refID: String!): LibraryItem @deprecated(reason: "@id argument for get query on interface is being deprecated, it will be removed in v21.11.0, please update your query to not use that argument") + getLibraryItem(refID: String, itemID: String): LibraryItem @deprecated(reason: "@id argument for get query on interface is being deprecated. Only those @id fields which have interface argument set to true will be available in getQuery argument on interface post v21.11.0, please update your schema accordingly.") queryLibraryItem(filter: LibraryItemFilter, order: LibraryItemOrder, first: Int, offset: Int): [LibraryItem] aggregateLibraryItem(filter: LibraryItemFilter): LibraryItemAggregateResult - getBook(refID: String!): Book + getBook(refID: String, itemID: String): Book queryBook(filter: BookFilter, order: BookOrder, first: Int, offset: Int): [Book] aggregateBook(filter: BookFilter): BookAggregateResult queryLibrary(filter: LibraryFilter, first: Int, offset: Int): [Library] diff --git a/graphql/schema/testdata/schemagen/output/interface-with-no-ids.graphql b/graphql/schema/testdata/schemagen/output/interface-with-no-ids.graphql index e50390d6bc7..90161b27860 100755 --- a/graphql/schema/testdata/schemagen/output/interface-with-no-ids.graphql +++ b/graphql/schema/testdata/schemagen/output/interface-with-no-ids.graphql @@ -189,7 +189,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/interfaces-with-types-and-password.graphql b/graphql/schema/testdata/schemagen/output/interfaces-with-types-and-password.graphql index 615784ab6d6..2314846daa9 100755 --- a/graphql/schema/testdata/schemagen/output/interfaces-with-types-and-password.graphql +++ b/graphql/schema/testdata/schemagen/output/interfaces-with-types-and-password.graphql @@ -214,7 +214,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/interfaces-with-types.graphql b/graphql/schema/testdata/schemagen/output/interfaces-with-types.graphql index 224f014b327..53da87263b2 100755 --- a/graphql/schema/testdata/schemagen/output/interfaces-with-types.graphql +++ b/graphql/schema/testdata/schemagen/output/interfaces-with-types.graphql @@ -214,7 +214,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/lambda-directive.graphql b/graphql/schema/testdata/schemagen/output/lambda-directive.graphql index 41747a6769a..8df251f64a7 100644 --- a/graphql/schema/testdata/schemagen/output/lambda-directive.graphql +++ b/graphql/schema/testdata/schemagen/output/lambda-directive.graphql @@ -181,7 +181,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/language-tags.graphql b/graphql/schema/testdata/schemagen/output/language-tags.graphql new file mode 100755 index 00000000000..e217582cce7 --- /dev/null +++ b/graphql/schema/testdata/schemagen/output/language-tags.graphql @@ -0,0 +1,517 @@ +####################### +# Input Schema +####################### + +interface Node { + f1: String +} + +type Person implements Node { + f1: String + f1Hi: String @dgraph(pred: "Node.f1@hi") + f2: String @dgraph(pred: "T.f@no") + f3: String @dgraph(pred: "f3@en") + name: String! @id + nameHi: String @dgraph(pred: "Person.name@hi") @search(by: [term,exact]) + nameEn: String @dgraph(pred: "Person.name@en") @search(by: [regexp]) + nameHiEn: String @dgraph(pred: "Person.name@hi:en") + nameHi_En_Untag: String @dgraph(pred: "Person.name@hi:en:.") + name_Untag_AnyLang: String @dgraph(pred: "Person.name@.") + address: String @search(by: [fulltext]) + addressHi: String @dgraph(pred: "Person.address@hi") + professionEn: String @dgraph(pred: "Person.profession@en") +} + +####################### +# Extended Definitions +####################### + +""" +The Int64 scalar type represents a signed 64‐bit numeric non‐fractional value. +Int64 can represent values in range [-(2^63),(2^63 - 1)]. +""" +scalar Int64 + +""" +The DateTime scalar type represents date and time as a string in RFC3339 format. +For example: "1985-04-12T23:20:50.52Z" represents 20 minutes and 50.52 seconds after the 23rd hour of April 12th, 1985 in UTC. +""" +scalar DateTime + +input IntRange{ + min: Int! + max: Int! +} + +input FloatRange{ + min: Float! + max: Float! +} + +input Int64Range{ + min: Int64! + max: Int64! +} + +input DateTimeRange{ + min: DateTime! + max: DateTime! +} + +input StringRange{ + min: String! + max: String! +} + +enum DgraphIndex { + int + int64 + float + bool + hash + exact + term + fulltext + trigram + regexp + year + month + day + hour + geo +} + +input AuthRule { + and: [AuthRule] + or: [AuthRule] + not: AuthRule + rule: String +} + +enum HTTPMethod { + GET + POST + PUT + PATCH + DELETE +} + +enum Mode { + BATCH + SINGLE +} + +input CustomHTTP { + url: String! + method: HTTPMethod! + body: String + graphql: String + mode: Mode + forwardHeaders: [String!] + secretHeaders: [String!] + introspectionHeaders: [String!] + skipIntrospection: Boolean +} + +type Point { + longitude: Float! + latitude: Float! +} + +input PointRef { + longitude: Float! + latitude: Float! +} + +input NearFilter { + distance: Float! + coordinate: PointRef! +} + +input PointGeoFilter { + near: NearFilter + within: WithinFilter +} + +type PointList { + points: [Point!]! +} + +input PointListRef { + points: [PointRef!]! +} + +type Polygon { + coordinates: [PointList!]! +} + +input PolygonRef { + coordinates: [PointListRef!]! +} + +type MultiPolygon { + polygons: [Polygon!]! +} + +input MultiPolygonRef { + polygons: [PolygonRef!]! +} + +input WithinFilter { + polygon: PolygonRef! +} + +input ContainsFilter { + point: PointRef + polygon: PolygonRef +} + +input IntersectsFilter { + polygon: PolygonRef + multiPolygon: MultiPolygonRef +} + +input PolygonGeoFilter { + near: NearFilter + within: WithinFilter + contains: ContainsFilter + intersects: IntersectsFilter +} + +input GenerateQueryParams { + get: Boolean + query: Boolean + password: Boolean + aggregate: Boolean +} + +input GenerateMutationParams { + add: Boolean + update: Boolean + delete: Boolean +} + +directive @hasInverse(field: String!) on FIELD_DEFINITION +directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION +directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION +directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION +directive @secret(field: String!, pred: String) on OBJECT | INTERFACE +directive @auth( + password: AuthRule + query: AuthRule, + add: AuthRule, + update: AuthRule, + delete: AuthRule) on OBJECT | INTERFACE +directive @custom(http: CustomHTTP, dql: String) on FIELD_DEFINITION +directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM +directive @remoteResponse(name: String) on FIELD_DEFINITION +directive @cascade(fields: [String]) on FIELD +directive @lambda on FIELD_DEFINITION +directive @lambdaOnMutate(add: Boolean, update: Boolean, delete: Boolean) on OBJECT | INTERFACE +directive @cacheControl(maxAge: Int!) on QUERY +directive @generate( + query: GenerateQueryParams, + mutation: GenerateMutationParams, + subscription: Boolean) on OBJECT | INTERFACE + +input IntFilter { + eq: Int + in: [Int] + le: Int + lt: Int + ge: Int + gt: Int + between: IntRange +} + +input Int64Filter { + eq: Int64 + in: [Int64] + le: Int64 + lt: Int64 + ge: Int64 + gt: Int64 + between: Int64Range +} + +input FloatFilter { + eq: Float + in: [Float] + le: Float + lt: Float + ge: Float + gt: Float + between: FloatRange +} + +input DateTimeFilter { + eq: DateTime + in: [DateTime] + le: DateTime + lt: DateTime + ge: DateTime + gt: DateTime + between: DateTimeRange +} + +input StringTermFilter { + allofterms: String + anyofterms: String +} + +input StringRegExpFilter { + regexp: String +} + +input StringFullTextFilter { + alloftext: String + anyoftext: String +} + +input StringExactFilter { + eq: String + in: [String] + le: String + lt: String + ge: String + gt: String + between: StringRange +} + +input StringHashFilter { + eq: String + in: [String] +} + +####################### +# Generated Types +####################### + +type AddPersonPayload { + person(filter: PersonFilter, order: PersonOrder, first: Int, offset: Int): [Person] + numUids: Int +} + +type DeleteNodePayload { + node(filter: NodeFilter, order: NodeOrder, first: Int, offset: Int): [Node] + msg: String + numUids: Int +} + +type DeletePersonPayload { + person(filter: PersonFilter, order: PersonOrder, first: Int, offset: Int): [Person] + msg: String + numUids: Int +} + +type NodeAggregateResult { + count: Int + f1Min: String + f1Max: String +} + +type PersonAggregateResult { + count: Int + f1Min: String + f1Max: String + f1HiMin: String + f1HiMax: String + f2Min: String + f2Max: String + f3Min: String + f3Max: String + nameMin: String + nameMax: String + nameHiMin: String + nameHiMax: String + nameEnMin: String + nameEnMax: String + nameHiEnMin: String + nameHiEnMax: String + nameHi_En_UntagMin: String + nameHi_En_UntagMax: String + name_Untag_AnyLangMin: String + name_Untag_AnyLangMax: String + addressMin: String + addressMax: String + addressHiMin: String + addressHiMax: String + professionEnMin: String + professionEnMax: String +} + +type UpdateNodePayload { + node(filter: NodeFilter, order: NodeOrder, first: Int, offset: Int): [Node] + numUids: Int +} + +type UpdatePersonPayload { + person(filter: PersonFilter, order: PersonOrder, first: Int, offset: Int): [Person] + numUids: Int +} + +####################### +# Generated Enums +####################### + +enum NodeHasFilter { + f1 +} + +enum NodeOrderable { + f1 +} + +enum PersonHasFilter { + f1 + f1Hi + f2 + f3 + name + nameHi + nameEn + name_Untag_AnyLang + address + addressHi + professionEn +} + +enum PersonOrderable { + f1 + f1Hi + f2 + f3 + name + nameHi + nameEn + name_Untag_AnyLang + address + addressHi + professionEn +} + +####################### +# Generated Inputs +####################### + +input AddPersonInput { + f1: String + f1Hi: String + f2: String + f3: String + name: String! + nameHi: String + nameEn: String + address: String + addressHi: String + professionEn: String +} + +input NodeFilter { + has: [NodeHasFilter] + and: [NodeFilter] + or: [NodeFilter] + not: NodeFilter +} + +input NodeOrder { + asc: NodeOrderable + desc: NodeOrderable + then: NodeOrder +} + +input NodePatch { + f1: String +} + +input PersonFilter { + name: StringHashFilter + nameHi: StringExactFilter_StringTermFilter + nameEn: StringRegExpFilter + address: StringFullTextFilter + has: [PersonHasFilter] + and: [PersonFilter] + or: [PersonFilter] + not: PersonFilter +} + +input PersonOrder { + asc: PersonOrderable + desc: PersonOrderable + then: PersonOrder +} + +input PersonPatch { + f1: String + f1Hi: String + f2: String + f3: String + nameHi: String + nameEn: String + address: String + addressHi: String + professionEn: String +} + +input PersonRef { + f1: String + f1Hi: String + f2: String + f3: String + name: String + nameHi: String + nameEn: String + address: String + addressHi: String + professionEn: String +} + +input StringExactFilter_StringTermFilter { + eq: String + in: [String] + le: String + lt: String + ge: String + gt: String + between: StringRange + allofterms: String + anyofterms: String +} + +input UpdateNodeInput { + filter: NodeFilter! + set: NodePatch + remove: NodePatch +} + +input UpdatePersonInput { + filter: PersonFilter! + set: PersonPatch + remove: PersonPatch +} + +####################### +# Generated Query +####################### + +type Query { + queryNode(filter: NodeFilter, order: NodeOrder, first: Int, offset: Int): [Node] + aggregateNode(filter: NodeFilter): NodeAggregateResult + getPerson(name: String!): Person + queryPerson(filter: PersonFilter, order: PersonOrder, first: Int, offset: Int): [Person] + aggregatePerson(filter: PersonFilter): PersonAggregateResult +} + +####################### +# Generated Mutations +####################### + +type Mutation { + updateNode(input: UpdateNodeInput!): UpdateNodePayload + deleteNode(filter: NodeFilter!): DeleteNodePayload + addPerson(input: [AddPersonInput!]!, upsert: Boolean): AddPersonPayload + updatePerson(input: UpdatePersonInput!): UpdatePersonPayload + deletePerson(filter: PersonFilter!): DeletePersonPayload +} + diff --git a/graphql/schema/testdata/schemagen/output/no-id-field-with-searchables.graphql b/graphql/schema/testdata/schemagen/output/no-id-field-with-searchables.graphql index 41a8008c643..5430dbf9e17 100755 --- a/graphql/schema/testdata/schemagen/output/no-id-field-with-searchables.graphql +++ b/graphql/schema/testdata/schemagen/output/no-id-field-with-searchables.graphql @@ -178,7 +178,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/no-id-field.graphql b/graphql/schema/testdata/schemagen/output/no-id-field.graphql index 68e809c5a0b..1976d50b845 100755 --- a/graphql/schema/testdata/schemagen/output/no-id-field.graphql +++ b/graphql/schema/testdata/schemagen/output/no-id-field.graphql @@ -191,7 +191,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/password-type.graphql b/graphql/schema/testdata/schemagen/output/password-type.graphql index a994bbf1f83..cba6c170459 100755 --- a/graphql/schema/testdata/schemagen/output/password-type.graphql +++ b/graphql/schema/testdata/schemagen/output/password-type.graphql @@ -179,7 +179,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/random.graphql b/graphql/schema/testdata/schemagen/output/random.graphql index 8b365c84d9d..117b334faef 100644 --- a/graphql/schema/testdata/schemagen/output/random.graphql +++ b/graphql/schema/testdata/schemagen/output/random.graphql @@ -197,7 +197,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @remote on OBJECT | INTERFACE | UNION | INPUT_OBJECT | ENUM diff --git a/graphql/schema/testdata/schemagen/output/searchables-references.graphql b/graphql/schema/testdata/schemagen/output/searchables-references.graphql index d98e8468a98..a1d33610f5a 100755 --- a/graphql/schema/testdata/schemagen/output/searchables-references.graphql +++ b/graphql/schema/testdata/schemagen/output/searchables-references.graphql @@ -189,7 +189,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/searchables.graphql b/graphql/schema/testdata/schemagen/output/searchables.graphql index 71764a8ff6d..8e21af2ddcd 100755 --- a/graphql/schema/testdata/schemagen/output/searchables.graphql +++ b/graphql/schema/testdata/schemagen/output/searchables.graphql @@ -209,7 +209,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/single-type-with-enum.graphql b/graphql/schema/testdata/schemagen/output/single-type-with-enum.graphql index bd33c6a0166..dc67bd55967 100755 --- a/graphql/schema/testdata/schemagen/output/single-type-with-enum.graphql +++ b/graphql/schema/testdata/schemagen/output/single-type-with-enum.graphql @@ -187,7 +187,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/single-type.graphql b/graphql/schema/testdata/schemagen/output/single-type.graphql index c24c2a07ca4..ffd93575abf 100755 --- a/graphql/schema/testdata/schemagen/output/single-type.graphql +++ b/graphql/schema/testdata/schemagen/output/single-type.graphql @@ -182,7 +182,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/type-implements-multiple-interfaces.graphql b/graphql/schema/testdata/schemagen/output/type-implements-multiple-interfaces.graphql index ae26f7520a4..b789d8551f7 100755 --- a/graphql/schema/testdata/schemagen/output/type-implements-multiple-interfaces.graphql +++ b/graphql/schema/testdata/schemagen/output/type-implements-multiple-interfaces.graphql @@ -196,7 +196,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/type-reference.graphql b/graphql/schema/testdata/schemagen/output/type-reference.graphql index c61259d84f0..721497a18fd 100755 --- a/graphql/schema/testdata/schemagen/output/type-reference.graphql +++ b/graphql/schema/testdata/schemagen/output/type-reference.graphql @@ -186,7 +186,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/type-with-arguments-on-field.graphql b/graphql/schema/testdata/schemagen/output/type-with-arguments-on-field.graphql index c4e9d509fe9..192482c12fa 100644 --- a/graphql/schema/testdata/schemagen/output/type-with-arguments-on-field.graphql +++ b/graphql/schema/testdata/schemagen/output/type-with-arguments-on-field.graphql @@ -187,7 +187,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/type-with-custom-field-on-dgraph-type.graphql b/graphql/schema/testdata/schemagen/output/type-with-custom-field-on-dgraph-type.graphql index fdd9e835668..7e4c71914f7 100644 --- a/graphql/schema/testdata/schemagen/output/type-with-custom-field-on-dgraph-type.graphql +++ b/graphql/schema/testdata/schemagen/output/type-with-custom-field-on-dgraph-type.graphql @@ -186,7 +186,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/type-with-custom-fields-on-remote-type.graphql b/graphql/schema/testdata/schemagen/output/type-with-custom-fields-on-remote-type.graphql index fcc386231e2..e85ea68b041 100644 --- a/graphql/schema/testdata/schemagen/output/type-with-custom-fields-on-remote-type.graphql +++ b/graphql/schema/testdata/schemagen/output/type-with-custom-fields-on-remote-type.graphql @@ -186,7 +186,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/type-without-orderables.graphql b/graphql/schema/testdata/schemagen/output/type-without-orderables.graphql index eedaf08b447..ff78a20fbb1 100644 --- a/graphql/schema/testdata/schemagen/output/type-without-orderables.graphql +++ b/graphql/schema/testdata/schemagen/output/type-without-orderables.graphql @@ -181,7 +181,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/testdata/schemagen/output/union.graphql b/graphql/schema/testdata/schemagen/output/union.graphql index 3a02426ae4d..33eaa4c6c54 100644 --- a/graphql/schema/testdata/schemagen/output/union.graphql +++ b/graphql/schema/testdata/schemagen/output/union.graphql @@ -228,7 +228,7 @@ input GenerateMutationParams { directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION -directive @id on FIELD_DEFINITION +directive @id(interface: Boolean) on FIELD_DEFINITION directive @withSubscription on OBJECT | INTERFACE | FIELD_DEFINITION directive @secret(field: String!, pred: String) on OBJECT | INTERFACE directive @auth( diff --git a/graphql/schema/wrappers.go b/graphql/schema/wrappers.go index d07d85b73ff..40239982a55 100644 --- a/graphql/schema/wrappers.go +++ b/graphql/schema/wrappers.go @@ -249,7 +249,7 @@ type Type interface { Interfaces() []string ImplementingTypes() []Type EnsureNonNulls(map[string]interface{}, string) error - FieldOriginatedFrom(fieldName string) string + FieldOriginatedFrom(fieldName string) (Type, bool) AuthRules() *TypeAuth IsGeo() bool IsAggregateResult() bool @@ -269,6 +269,7 @@ type FieldDefinition interface { IsID() bool IsExternal() bool HasIDDirective() bool + HasInterfaceArg() bool Inverse() FieldDefinition WithMemberType(string) FieldDefinition // TODO - It might be possible to get rid of ForwardEdge and just use Inverse() always. @@ -2320,10 +2321,34 @@ func (fd *fieldDefinition) HasIDDirective() bool { } func hasIDDirective(fd *ast.FieldDefinition) bool { - id := fd.Directives.ForName("id") + id := fd.Directives.ForName(idDirective) return id != nil } +func (fd *fieldDefinition) HasInterfaceArg() bool { + if fd.fieldDef == nil { + return false + } + return hasInterfaceArg(fd.fieldDef) +} + +func hasInterfaceArg(fd *ast.FieldDefinition) bool { + if !hasIDDirective(fd) { + return false + } + interfaceArg := fd.Directives.ForName(idDirective).Arguments.ForName(idDirectiveInterfaceArg) + if interfaceArg == nil { + return false + } + + value, _ := interfaceArg.Value.Value(nil) + if val, ok := value.(bool); ok && val { + return true + } + + return false +} + func isID(fd *ast.FieldDefinition) bool { return fd.Type.Name() == "ID" } @@ -3014,21 +3039,34 @@ func SubstituteVarsInBody(jsonTemplate interface{}, variables map[string]interfa return jsonTemplate } -// FieldOriginatedFrom returns the name of the interface from which given field was inherited. -// If the field wasn't inherited, but belonged to this type, this type's name is returned. -// Otherwise, empty string is returned. -func (t *astType) FieldOriginatedFrom(fieldName string) string { +// FieldOriginatedFrom returns the interface from which given field was inherited. +// If the field wasn't inherited, but belonged to this type,then type is returned. +// Otherwise, nil is returned. Along with type definition we return boolean flag true if field +// is inherited from interface. +func (t *astType) FieldOriginatedFrom(fieldName string) (Type, bool) { + + astTyp := &astType{ + inSchema: t.inSchema, + dgraphPredicate: t.dgraphPredicate, + } + for _, implements := range t.inSchema.schema.Implements[t.Name()] { if implements.Fields.ForName(fieldName) != nil { - return implements.Name + astTyp.typ = &ast.Type{ + NamedType: implements.Name, + } + return astTyp, true } } if t.inSchema.schema.Types[t.Name()].Fields.ForName(fieldName) != nil { - return t.Name() + astTyp.typ = &ast.Type{ + NamedType: t.inSchema.schema.Types[t.Name()].Name, + } + return astTyp, false } - return "" + return nil, false } // buildGraphqlRequestFields will build graphql request body from ast.