diff --git a/graphql/admin/schema.go b/graphql/admin/schema.go index 0ebf5192d75..7a206692be4 100644 --- a/graphql/admin/schema.go +++ b/graphql/admin/schema.go @@ -20,8 +20,8 @@ import ( "bytes" "context" "encoding/json" - dgoapi "github.com/dgraph-io/dgo/v200/protos/api" + "github.com/dgraph-io/dgraph/edgraph" "github.com/dgraph-io/dgraph/gql" "github.com/dgraph-io/dgraph/graphql/resolve" diff --git a/graphql/schema/gqlschema.go b/graphql/schema/gqlschema.go index c587db2e968..f885c16506b 100644 --- a/graphql/schema/gqlschema.go +++ b/graphql/schema/gqlschema.go @@ -577,11 +577,88 @@ func completeSchema(sch *ast.Schema, definitions []string) { } } +func cleanupInput(sch *ast.Schema, def *ast.Definition, seen map[string]bool) { + // seen helps us avoid cycles + if def == nil || seen[def.Name] { + return + } + + i := 0 + // recursively walk over fields for an input type and delete those which are themselves empty. + for _, f := range def.Fields { + nt := f.Type.Name() + enum := sch.Types[nt] != nil && sch.Types[nt].Kind == "ENUM" + // Lets skip scalar types and enums. + if _, ok := scalarToDgraph[nt]; ok || enum { + def.Fields[i] = f + i++ + continue + } + + seen[def.Name] = true + cleanupInput(sch, sch.Types[nt], seen) + seen[def.Name] = false + + // If after calling cleanup on an input type, it got deleted then it doesn't need to be + // in the fields for this type anymore. + if sch.Types[nt] == nil { + continue + } + def.Fields[i] = f + i++ + } + def.Fields = def.Fields[:i] + + if len(def.Fields) == 0 { + delete(sch.Types, def.Name) + } +} + +func cleanSchema(sch *ast.Schema) { + // Let's go over inputs of the type TRef, TPatch and AddTInput and delete the ones which + // don't have field inside them. + for k := range sch.Types { + if strings.HasSuffix(k, "Ref") || strings.HasSuffix(k, "Patch") || + (strings.HasPrefix(k, "Add") && strings.HasSuffix(k, "Input")) { + cleanupInput(sch, sch.Types[k], map[string]bool{}) + } + } + + // Let's go over mutations and cleanup those which don't have AddTInput defined in the schema + // anymore. + i := 0 // helps us overwrite the array with valid entries. + for _, field := range sch.Mutation.Fields { + custom := field.Directives.ForName("custom") + // We would only modify add type queries. + if custom != nil || !strings.HasPrefix(field.Name, "add") { + sch.Mutation.Fields[i] = field + i++ + continue + } + + // addT type mutations have an input which is AddTInput so if that doesn't exist anymore, + // we can delete the AddTPayload and also skip this mutation. + typ := field.Name[3:] + input := sch.Types["Add"+typ+"Input"] + if input == nil { + delete(sch.Types, "Add"+typ+"Payload") + continue + } + sch.Mutation.Fields[i] = field + i++ + + } + sch.Mutation.Fields = sch.Mutation.Fields[:i] +} + func addInputType(schema *ast.Schema, defn *ast.Definition) { - schema.Types["Add"+defn.Name+"Input"] = &ast.Definition{ - Kind: ast.InputObject, - Name: "Add" + defn.Name + "Input", - Fields: getFieldsWithoutIDType(schema, defn), + field := getFieldsWithoutIDType(schema, defn) + if len(field) != 0 { + schema.Types["Add"+defn.Name+"Input"] = &ast.Definition{ + Kind: ast.InputObject, + Name: "Add" + defn.Name + "Input", + Fields: field, + } } } @@ -604,10 +681,12 @@ func addReferenceType(schema *ast.Schema, defn *ast.Definition) { } } - schema.Types[defn.Name+"Ref"] = &ast.Definition{ - Kind: ast.InputObject, - Name: defn.Name + "Ref", - Fields: flds, + if len(flds) != 0 { + schema.Types[defn.Name+"Ref"] = &ast.Definition{ + Kind: ast.InputObject, + Name: defn.Name + "Ref", + Fields: flds, + } } } @@ -1040,11 +1119,12 @@ func addAddPayloadType(schema *ast.Schema, defn *ast.Definition) { addFilterArgument(schema, qry) addOrderArgument(schema, qry) addPaginationArguments(qry) - - schema.Types["Add"+defn.Name+"Payload"] = &ast.Definition{ - Kind: ast.Object, - Name: "Add" + defn.Name + "Payload", - Fields: []*ast.FieldDefinition{qry, numUids}, + if schema.Types["Add"+defn.Name+"Input"] != nil { + schema.Types["Add"+defn.Name+"Payload"] = &ast.Definition{ + Kind: ast.Object, + Name: "Add" + defn.Name + "Payload", + Fields: []*ast.FieldDefinition{qry, numUids}, + } } } @@ -1235,6 +1315,7 @@ func addAddMutation(schema *ast.Schema, defn *ast.Definition) { }, } schema.Mutation.Fields = append(schema.Mutation.Fields, add) + } func addUpdateMutation(schema *ast.Schema, defn *ast.Definition) { @@ -1285,6 +1366,9 @@ func addDeleteMutation(schema *ast.Schema, defn *ast.Definition) { } func addMutations(schema *ast.Schema, defn *ast.Definition) { + if schema.Types["Add"+defn.Name+"Input"] == nil { + return + } addAddMutation(schema, defn) addUpdateMutation(schema, defn) addDeleteMutation(schema, defn) @@ -1385,7 +1469,6 @@ func getFieldsWithoutIDType(schema *ast.Schema, defn *ast.Definition) ast.FieldL (!hasID(schema.Types[fld.Type.Name()]) && !hasXID(schema.Types[fld.Type.Name()])) { continue } - fldList = append(fldList, createField(schema, fld)) } diff --git a/graphql/schema/schemagen.go b/graphql/schema/schemagen.go index 97f6d793a6b..5213a88ab2e 100644 --- a/graphql/schema/schemagen.go +++ b/graphql/schema/schemagen.go @@ -214,6 +214,7 @@ func NewHandler(input string, validateOnly bool) (Handler, error) { headers := getAllowedHeaders(sch, defns, authHeader) dgSchema := genDgSchema(sch, typesToComplete) completeSchema(sch, typesToComplete) + cleanSchema(sch) if len(sch.Query.Fields) == 0 && len(sch.Mutation.Fields) == 0 { return nil, gqlerror.Errorf("No query or mutation found in the generated schema") diff --git a/graphql/schema/schemagen_test.go b/graphql/schema/schemagen_test.go index dbcbebc2ec6..914eae7a69e 100644 --- a/graphql/schema/schemagen_test.go +++ b/graphql/schema/schemagen_test.go @@ -87,7 +87,6 @@ func TestSchemaString(t *testing.T) { _, err = FromString(newSchemaStr) require.NoError(t, err) - outputFileName := outputDir + testFile.Name() str2, err := ioutil.ReadFile(outputFileName) require.NoError(t, err) diff --git a/graphql/schema/testdata/schemagen/input/filter-cleanSchema-all-empty.graphql b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-all-empty.graphql new file mode 100644 index 00000000000..9c489b6aa88 --- /dev/null +++ b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-all-empty.graphql @@ -0,0 +1,12 @@ +type X { + name: [Y] + f1: [Y] @dgraph(pred: "f1") +} + +type Y { + f1: [X] @dgraph(pred: "~f1") +} + +type Z { + add:[X] +} diff --git a/graphql/schema/testdata/schemagen/input/filter-cleanSchema-circular.graphql b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-circular.graphql new file mode 100644 index 00000000000..560f7b855da --- /dev/null +++ b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-circular.graphql @@ -0,0 +1,14 @@ +type X{ + f1: [Y] @dgraph(pred: "f1") + f3: [Z] @dgraph(pred: "~f3") +} + +type Y{ + f1: [X] @dgraph(pred: "~f1") + f2: [Z] @dgraph(pred: "f2") +} + +type Z{ + f2: [Y] @dgraph(pred: "~f2") + f3: [X] @dgraph(pred: "f3") +} \ No newline at end of file diff --git a/graphql/schema/testdata/schemagen/input/filter-cleanSchema-custom-mutation.graphql b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-custom-mutation.graphql new file mode 100644 index 00000000000..c3704e34625 --- /dev/null +++ b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-custom-mutation.graphql @@ -0,0 +1,16 @@ +type User { + id: ID! + name: String! +} + +input UserInput { + name: String! +} + +type Mutation { + addMyFavouriteUsers(input: [UserInput!]!): [User] @custom(http: { + url: "http://my-api.com", + method: "POST", + body: "{ data: $input }" + }) +} \ No newline at end of file diff --git a/graphql/schema/testdata/schemagen/input/filter-cleanSchema-directLink.graphql b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-directLink.graphql new file mode 100644 index 00000000000..e66f9a3cf86 --- /dev/null +++ b/graphql/schema/testdata/schemagen/input/filter-cleanSchema-directLink.graphql @@ -0,0 +1,14 @@ +type X { + f1: [Y] @dgraph(pred: "f1") + name: String + id: ID +} + +type Y { + f2: [Z] @dgraph(pred: "~f2") + f1: [X] @dgraph(pred: "~f1") +} + +type Z { + f2: [Y] @dgraph(pred: "f2") +} \ No newline at end of file diff --git a/graphql/schema/testdata/schemagen/input/hasInverse-with-interface-having-directive.graphql b/graphql/schema/testdata/schemagen/input/hasInverse-with-interface-having-directive.graphql index e0d8dcd006b..3f2c1edc3ae 100644 --- a/graphql/schema/testdata/schemagen/input/hasInverse-with-interface-having-directive.graphql +++ b/graphql/schema/testdata/schemagen/input/hasInverse-with-interface-having-directive.graphql @@ -1,7 +1,7 @@ type Author { id: ID! name: String! @search(by: [hash]) - posts: [Post] + posts: [Post] } interface Post { @@ -12,7 +12,7 @@ interface Post { } type Question implements Post { - answered: Boolean + answered: Boolean } type Answer implements Post { diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-all-empty.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-all-empty.graphql new file mode 100644 index 00000000000..4fe1e985fef --- /dev/null +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-all-empty.graphql @@ -0,0 +1,204 @@ +####################### +# Input Schema +####################### + +type X { + name(first: Int, offset: Int): [Y] + f1(first: Int, offset: Int): [Y] @dgraph(pred: "f1") +} + +type Y { + f1(first: Int, offset: Int): [X] @dgraph(pred: "~f1") +} + +type Z { + add(first: Int, offset: Int): [X] +} + +####################### +# 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 + +enum DgraphIndex { + int + int64 + float + bool + hash + exact + term + fulltext + trigram + regexp + year + month + day + hour +} + +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 +} + +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 @withSubscription on OBJECT | INTERFACE +directive @secret(field: String!, pred: String) on OBJECT | INTERFACE +directive @auth( + query: AuthRule, + add: AuthRule, + update: AuthRule, + delete:AuthRule) on OBJECT +directive @custom(http: CustomHTTP, dql: String) on FIELD_DEFINITION +directive @remote on OBJECT | INTERFACE +directive @cascade(fields: [String]) on FIELD + +input IntFilter { + eq: Int + le: Int + lt: Int + ge: Int + gt: Int +} + +input Int64Filter { + eq: Int64 + le: Int64 + lt: Int64 + ge: Int64 + gt: Int64 +} + +input FloatFilter { + eq: Float + le: Float + lt: Float + ge: Float + gt: Float +} + +input DateTimeFilter { + eq: DateTime + le: DateTime + lt: DateTime + ge: DateTime + gt: DateTime +} + +input StringTermFilter { + allofterms: String + anyofterms: String +} + +input StringRegExpFilter { + regexp: String +} + +input StringFullTextFilter { + alloftext: String + anyoftext: String +} + +input StringExactFilter { + eq: String + le: String + lt: String + ge: String + gt: String +} + +input StringHashFilter { + eq: String +} + +####################### +# Generated Enums +####################### + +enum XHasFilter { + name + f1 +} + +enum YHasFilter { + f1 +} + +enum ZHasFilter { + add +} + +####################### +# Generated Inputs +####################### + +input XFilter { + has: XHasFilter + and: XFilter + or: XFilter + not: XFilter +} + +input YFilter { + not: YFilter +} + +input ZFilter { + has: ZHasFilter + and: ZFilter + or: ZFilter + not: ZFilter +} + +####################### +# Generated Query +####################### + +type Query { + queryX(first: Int, offset: Int): [X] + queryY(first: Int, offset: Int): [Y] + queryZ(first: Int, offset: Int): [Z] +} + diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-circular.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-circular.graphql new file mode 100644 index 00000000000..a2dc284a013 --- /dev/null +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-circular.graphql @@ -0,0 +1,264 @@ +####################### +# Input Schema +####################### + +type X { + f1(first: Int, offset: Int): [Y] @dgraph(pred: "f1") + f3(first: Int, offset: Int): [Z] @dgraph(pred: "~f3") +} + +type Y { + f1(first: Int, offset: Int): [X] @dgraph(pred: "~f1") + f2(first: Int, offset: Int): [Z] @dgraph(pred: "f2") +} + +type Z { + f2(first: Int, offset: Int): [Y] @dgraph(pred: "~f2") + f3(first: Int, offset: Int): [X] @dgraph(pred: "f3") +} + +####################### +# 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 + +enum DgraphIndex { + int + int64 + float + bool + hash + exact + term + fulltext + trigram + regexp + year + month + day + hour +} + +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 +} + +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 @withSubscription on OBJECT | INTERFACE +directive @secret(field: String!, pred: String) on OBJECT | INTERFACE +directive @auth( + query: AuthRule, + add: AuthRule, + update: AuthRule, + delete:AuthRule) on OBJECT +directive @custom(http: CustomHTTP, dql: String) on FIELD_DEFINITION +directive @remote on OBJECT | INTERFACE +directive @cascade(fields: [String]) on FIELD + +input IntFilter { + eq: Int + le: Int + lt: Int + ge: Int + gt: Int +} + +input Int64Filter { + eq: Int64 + le: Int64 + lt: Int64 + ge: Int64 + gt: Int64 +} + +input FloatFilter { + eq: Float + le: Float + lt: Float + ge: Float + gt: Float +} + +input DateTimeFilter { + eq: DateTime + le: DateTime + lt: DateTime + ge: DateTime + gt: DateTime +} + +input StringTermFilter { + allofterms: String + anyofterms: String +} + +input StringRegExpFilter { + regexp: String +} + +input StringFullTextFilter { + alloftext: String + anyoftext: String +} + +input StringExactFilter { + eq: String + le: String + lt: String + ge: String + gt: String +} + +input StringHashFilter { + eq: String +} + +####################### +# Generated Types +####################### + +type AddXPayload { + x(first: Int, offset: Int): [X] + numUids: Int +} + +type AddYPayload { + y(first: Int, offset: Int): [Y] + numUids: Int +} + +type AddZPayload { + z(first: Int, offset: Int): [Z] + numUids: Int +} + +####################### +# Generated Enums +####################### + +enum XHasFilter { + f1 + f3 +} + +enum YHasFilter { + f1 + f2 +} + +enum ZHasFilter { + f2 + f3 +} + +####################### +# Generated Inputs +####################### + +input AddXInput { + f1: [YRef] +} + +input AddYInput { + f2: [ZRef] +} + +input AddZInput { + f3: [XRef] +} + +input XFilter { + has: XHasFilter + and: XFilter + or: XFilter + not: XFilter +} + +input XRef { + f1: [YRef] +} + +input YFilter { + has: YHasFilter + and: YFilter + or: YFilter + not: YFilter +} + +input YRef { + f2: [ZRef] +} + +input ZFilter { + has: ZHasFilter + and: ZFilter + or: ZFilter + not: ZFilter +} + +input ZRef { + f3: [XRef] +} + +####################### +# Generated Query +####################### + +type Query { + queryX(first: Int, offset: Int): [X] + queryY(first: Int, offset: Int): [Y] + queryZ(first: Int, offset: Int): [Z] +} + +####################### +# Generated Mutations +####################### + +type Mutation { + addX(input: [AddXInput!]!): AddXPayload + addY(input: [AddYInput!]!): AddYPayload + addZ(input: [AddZInput!]!): AddZPayload +} + diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-custom-mutation.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-custom-mutation.graphql new file mode 100644 index 00000000000..0d3a24f74be --- /dev/null +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-custom-mutation.graphql @@ -0,0 +1,240 @@ +####################### +# Input Schema +####################### + +type User { + id: ID! + name: String! +} + +input UserInput { + name: String! +} + +####################### +# 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 + +enum DgraphIndex { + int + int64 + float + bool + hash + exact + term + fulltext + trigram + regexp + year + month + day + hour +} + +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 +} + +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 @withSubscription on OBJECT | INTERFACE +directive @secret(field: String!, pred: String) on OBJECT | INTERFACE +directive @auth( + query: AuthRule, + add: AuthRule, + update: AuthRule, + delete:AuthRule) on OBJECT +directive @custom(http: CustomHTTP, dql: String) on FIELD_DEFINITION +directive @remote on OBJECT | INTERFACE +directive @cascade(fields: [String]) on FIELD + +input IntFilter { + eq: Int + le: Int + lt: Int + ge: Int + gt: Int +} + +input Int64Filter { + eq: Int64 + le: Int64 + lt: Int64 + ge: Int64 + gt: Int64 +} + +input FloatFilter { + eq: Float + le: Float + lt: Float + ge: Float + gt: Float +} + +input DateTimeFilter { + eq: DateTime + le: DateTime + lt: DateTime + ge: DateTime + gt: DateTime +} + +input StringTermFilter { + allofterms: String + anyofterms: String +} + +input StringRegExpFilter { + regexp: String +} + +input StringFullTextFilter { + alloftext: String + anyoftext: String +} + +input StringExactFilter { + eq: String + le: String + lt: String + ge: String + gt: String +} + +input StringHashFilter { + eq: String +} + +####################### +# Generated Types +####################### + +type AddUserPayload { + user(filter: UserFilter, order: UserOrder, first: Int, offset: Int): [User] + numUids: Int +} + +type DeleteUserPayload { + user(filter: UserFilter, order: UserOrder, first: Int, offset: Int): [User] + msg: String + numUids: Int +} + +type UpdateUserPayload { + user(filter: UserFilter, order: UserOrder, first: Int, offset: Int): [User] + numUids: Int +} + +####################### +# Generated Enums +####################### + +enum UserHasFilter { + name +} + +enum UserOrderable { + name +} + +####################### +# Generated Inputs +####################### + +input AddUserInput { + name: String! +} + +input UpdateUserInput { + filter: UserFilter! + set: UserPatch + remove: UserPatch +} + +input UserFilter { + id: [ID!] + has: UserHasFilter + and: UserFilter + or: UserFilter + not: UserFilter +} + +input UserOrder { + asc: UserOrderable + desc: UserOrderable + then: UserOrder +} + +input UserPatch { + name: String +} + +input UserRef { + id: ID + name: String +} + +####################### +# Generated Query +####################### + +type Query { + getUser(id: ID!): User + queryUser(filter: UserFilter, order: UserOrder, first: Int, offset: Int): [User] +} + +####################### +# Generated Mutations +####################### + +type Mutation { + addMyFavouriteUsers(input: [UserInput!]!): [User] @custom(http: {url:"http://my-api.com",method:"POST",body:"{ data: $input }"}) + addUser(input: [AddUserInput!]!): AddUserPayload + updateUser(input: UpdateUserInput!): UpdateUserPayload + deleteUser(filter: UserFilter!): DeleteUserPayload +} + diff --git a/graphql/schema/testdata/schemagen/output/filter-cleanSchema-directLink.graphql b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-directLink.graphql new file mode 100644 index 00000000000..ca25f13d823 --- /dev/null +++ b/graphql/schema/testdata/schemagen/output/filter-cleanSchema-directLink.graphql @@ -0,0 +1,268 @@ +####################### +# Input Schema +####################### + +type X { + f1(first: Int, offset: Int): [Y] @dgraph(pred: "f1") + name: String + id: ID +} + +type Y { + f2(first: Int, offset: Int): [Z] @dgraph(pred: "~f2") + f1(filter: XFilter, order: XOrder, first: Int, offset: Int): [X] @dgraph(pred: "~f1") +} + +type Z { + f2(first: Int, offset: Int): [Y] @dgraph(pred: "f2") +} + +####################### +# 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 + +enum DgraphIndex { + int + int64 + float + bool + hash + exact + term + fulltext + trigram + regexp + year + month + day + hour +} + +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 +} + +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 @withSubscription on OBJECT | INTERFACE +directive @secret(field: String!, pred: String) on OBJECT | INTERFACE +directive @auth( + query: AuthRule, + add: AuthRule, + update: AuthRule, + delete:AuthRule) on OBJECT +directive @custom(http: CustomHTTP, dql: String) on FIELD_DEFINITION +directive @remote on OBJECT | INTERFACE +directive @cascade(fields: [String]) on FIELD + +input IntFilter { + eq: Int + le: Int + lt: Int + ge: Int + gt: Int +} + +input Int64Filter { + eq: Int64 + le: Int64 + lt: Int64 + ge: Int64 + gt: Int64 +} + +input FloatFilter { + eq: Float + le: Float + lt: Float + ge: Float + gt: Float +} + +input DateTimeFilter { + eq: DateTime + le: DateTime + lt: DateTime + ge: DateTime + gt: DateTime +} + +input StringTermFilter { + allofterms: String + anyofterms: String +} + +input StringRegExpFilter { + regexp: String +} + +input StringFullTextFilter { + alloftext: String + anyoftext: String +} + +input StringExactFilter { + eq: String + le: String + lt: String + ge: String + gt: String +} + +input StringHashFilter { + eq: String +} + +####################### +# Generated Types +####################### + +type AddXPayload { + x(filter: XFilter, order: XOrder, first: Int, offset: Int): [X] + numUids: Int +} + +type DeleteXPayload { + x(filter: XFilter, order: XOrder, first: Int, offset: Int): [X] + msg: String + numUids: Int +} + +type UpdateXPayload { + x(filter: XFilter, order: XOrder, first: Int, offset: Int): [X] + numUids: Int +} + +####################### +# Generated Enums +####################### + +enum XHasFilter { + f1 + name +} + +enum XOrderable { + name +} + +enum YHasFilter { + f2 + f1 +} + +enum ZHasFilter { + f2 +} + +####################### +# Generated Inputs +####################### + +input AddXInput { + name: String +} + +input UpdateXInput { + filter: XFilter! + set: XPatch + remove: XPatch +} + +input XFilter { + id: [ID!] + has: XHasFilter + and: XFilter + or: XFilter + not: XFilter +} + +input XOrder { + asc: XOrderable + desc: XOrderable + then: XOrder +} + +input XPatch { + name: String +} + +input XRef { + id: ID + name: String +} + +input YFilter { + not: YFilter +} + +input ZFilter { + has: ZHasFilter + and: ZFilter + or: ZFilter + not: ZFilter +} + +####################### +# Generated Query +####################### + +type Query { + getX(id: ID!): X + queryX(filter: XFilter, order: XOrder, first: Int, offset: Int): [X] + queryY(first: Int, offset: Int): [Y] + queryZ(first: Int, offset: Int): [Z] +} + +####################### +# Generated Mutations +####################### + +type Mutation { + addX(input: [AddXInput!]!): AddXPayload + updateX(input: UpdateXInput!): UpdateXPayload + deleteX(filter: XFilter!): DeleteXPayload +} +