Skip to content

Commit

Permalink
graphql: Allow user to define and pass arguments to fields. (#5562)
Browse files Browse the repository at this point in the history
This would allow users to pass in arguments to fields of remote endpoints.
  • Loading branch information
pawanrawal authored Jun 10, 2020
1 parent 476c7aa commit b29a977
Show file tree
Hide file tree
Showing 10 changed files with 487 additions and 24 deletions.
28 changes: 28 additions & 0 deletions graphql/e2e/custom_logic/cmd/graphqlresponse.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,34 @@
country(code: ID!): Country!
}
- name: "argsonfields"
description: "Test case to check args on fields can be passed by Dgraph"
schema: |
type Country {
code(size: Int!): String
name: String
}
type Query {
country(code: ID!): Country!
}
request: |
query($id: ID!) { country(code: $id) {
code(size: 100)
name
}}
variables: |
{"id":"BI"}
response: |
{
"data":{
"country":{
"name":"Burundi",
"code":"BI"
}
}
}
- name: "validcountrywitherror"
description: "Test case to validate dgraph can handle both valid data and error"
schema: |
Expand Down
1 change: 1 addition & 0 deletions graphql/e2e/custom_logic/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,7 @@ func main() {
// for queries
vsch := graphql.MustParseSchema(graphqlResponses["validcountry"].Schema, &query{})
http.Handle("/validcountry", &relay.Handler{Schema: vsch})
http.HandleFunc("/argsonfields", commonGraphqlHandler("argsonfields"))
http.HandleFunc("/validcountrywitherror", commonGraphqlHandler("validcountrywitherror"))
http.HandleFunc("/graphqlerr", commonGraphqlHandler("graphqlerr"))
http.Handle("/validcountries", &relay.Handler{
Expand Down
33 changes: 32 additions & 1 deletion graphql/e2e/custom_logic/custom_logic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const (
director: [MovieDirector]
}
type Country @remote {
code: String
code(size: Int): String
name: String
states: [State]
std: Int
Expand Down Expand Up @@ -1209,6 +1209,37 @@ func TestCustomLogicGraphql(t *testing.T) {
require.JSONEq(t, string(result.Data), `{"getCountry1":{"code":"BI","name":"Burundi"}}`)
}

func TestCustomLogicGraphqlWithArgumentsOnFields(t *testing.T) {
schema := customTypes + `
type Query {
getCountry2(id: ID!): Country!
@custom(
http: {
url: "http://mock:8888/argsonfields"
method: "POST"
forwardHeaders: ["Content-Type"]
graphql: "query($id: ID!) { country(code: $id) }"
}
)
}`
updateSchemaRequireNoGQLErrors(t, schema)
time.Sleep(2 * time.Second)
query := `
query {
getCountry2(id: "BI"){
code(size: 100)
name
}
}`
params := &common.GraphQLParams{
Query: query,
}

result := params.ExecuteAsPost(t, alphaURL)
common.RequireNoGQLErrors(t, result)
require.JSONEq(t, string(result.Data), `{"getCountry2":{"code":"BI","name":"Burundi"}}`)
}

func TestCustomLogicGraphqlWithError(t *testing.T) {
schema := customTypes + `
type Query {
Expand Down
93 changes: 88 additions & 5 deletions graphql/schema/custom_http_config_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,85 @@
{ "id": "0x1" }
-
name: "custom mutation"
name: "custom query with arguments on fields"
type: "query"
gqlschema: |
input ExactFilter {
eq: String
}
input MyFilter {
ids: ID
name: ExactFilter
}
type Country @remote {
code(first: Int!, filter: MyFilter): String
name: String
}
type Query {
getCountry1(id: ID!): Country! @custom(http: {
url: "http://google.com/validcountry",
method: "POST",
forwardHeaders: ["Content-Type"],
graphql: "query($id: ID!) { country(code: $id) }",
skipIntrospection: true
})
}
gqlquery: |
query {
getCountry1(id: "0x1") {
name
code(first: 10, filter: {ids: "0x123", name: { eq: "github" }})
}
}
remoteschema: |
input ExactFilter {
eq: String
}
input MyFilter {
ids: ID
name: ExactFilter
}
type Country @remote {
code(first: Int!, filter: MyFilter): String
name: String
}
type Query {
country(code: ID!): Country! @custom(http: {
url: "http://google.com/validcountry",
method: "POST",
graphql: "query($code: ID!) { country(code: $code) }",
skipIntrospection: true
})
}
remotequery: |-
query($id: ID!) { country(code: $id) {
name
code(first: 10, filter: {ids:"0x123",name:{eq:"github"}})
}}
remotevariables: |-
{ "id": "0x1" }
-
name: "custom mutation with arguments on field"
type: "mutation"
gqlschema: |
input ExactFilter {
eq: String
}
input MyFilter {
ids: ID
name: ExactFilter
}
type Country @remote {
code: String
code(get: Int!, choose: MyFilter): String
name: String
states: [State]
std: Int
Expand Down Expand Up @@ -84,7 +158,7 @@
gqlquery: |
mutation addCountry1($input: CountryInput!) {
addCountry1(input: $input) {
code
code(get: 10, choose: {ids: "0x123", name: { eq: "github" }})
name
states {
code
Expand All @@ -110,8 +184,17 @@
}
}
remoteschema: |
input ExactFilter {
eq: String
}
input MyFilter {
ids: ID
name: ExactFilter
}
type Country {
code: String
code(get: Int!, choose: MyFilter): String
name: String
states: [State]
std: Int
Expand Down Expand Up @@ -144,7 +227,7 @@
}
remotequery: |-
mutation($input: CountryInput!) { setCountry(country: $input) {
code
code(get: 10, choose: {ids:"0x123",name:{eq:"github"}})
name
states{
code
Expand Down
5 changes: 3 additions & 2 deletions graphql/schema/gqlschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,13 +326,13 @@ func copyAstFieldDef(src *ast.FieldDefinition) *ast.FieldDefinition {
var dirs ast.DirectiveList
dirs = append(dirs, src.Directives...)

// Lets leave out copying the arguments as types in input schemas are not supposed to contain
// them. We add arguments for filters and order statements later.
// We add arguments for filters and order statements later.
dst := &ast.FieldDefinition{
Name: src.Name,
DefaultValue: src.DefaultValue,
Type: src.Type,
Directives: dirs,
Arguments: src.Arguments,
Position: src.Position,
}
return dst
Expand Down Expand Up @@ -1240,6 +1240,7 @@ func createField(schema *ast.Schema, fld *ast.FieldDefinition) *ast.FieldDefinit
newFldType := *fld.Type
newFld.Type = &newFldType
newFld.Directives = nil
newFld.Arguments = nil
return &newFld
}

Expand Down
19 changes: 9 additions & 10 deletions graphql/schema/gqlschema_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,6 @@ invalid_schemas:
{"message": "Type A; Field f: Nested lists are invalid.", "locations": [{"line":2, "column": 3}]}
]

-
name: "There shoudnt be arguments on any field"
input: |
type T {
f(a: Int): String
}
errlist: [
{"message": "Type T; Field f: You can't give arguments to fields.", "locations": [{"line": 2, "column": 3}]}
]

-
name: "Enum indexes clash trigram and regexp"
input: |
Expand Down Expand Up @@ -1903,6 +1893,15 @@ invalid_schemas:
"locations":[{"line":1, "column":11}]},
]


- name: "There shoudnt be any reserved arguments on any field"
input: |
type T {
f(first: Int): String
}
errlist: [
{"message": "Type T; Field f: can't have first as an argument because it is a reserved argument.", "locations": [{"line": 2, "column": 3}]}]

- name: "remote type with @custom directives on fields shouldn't be allowed."
description: "Remote types are not resolved further currently, hence they shouldn't have
fields with @custom directive on them."
Expand Down
30 changes: 24 additions & 6 deletions graphql/schema/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,12 +694,22 @@ func fieldArgumentCheck(typ *ast.Definition, field *ast.FieldDefinition) *gqlerr
if isQueryOrMutationType(typ) {
return nil
}
if field.Arguments != nil {
return gqlerror.ErrorPosf(
field.Position,
"Type %s; Field %s: You can't give arguments to fields.",
typ.Name, field.Name,
)
// We don't need to verify the argument names for fields which are part of a remote type as
// we don't add any of our own arguments to them.
remote := typ.Directives.ForName(remoteDirective)
if remote != nil {
return nil
}
for _, arg := range field.Arguments {
if isReservedArgument(arg.Name) {
return gqlerror.ErrorPosf(
field.Position,
"Type %s; Field %s: can't have %s as an argument because it is a reserved "+
"argument.",
typ.Name, field.Name, arg.Name,
)

}
}
return nil
}
Expand Down Expand Up @@ -1649,6 +1659,14 @@ func isScalar(s string) bool {
return ok
}

func isReservedArgument(name string) bool {
switch name {
case "first", "offset", "filter", "order":
return true
}
return false
}

func isReservedKeyWord(name string) bool {
if isScalar(name) || isQueryOrMutation(name) || name == "uid" {
return true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
interface Abstract {
id: ID!
name(random: Int!, size: String): String!
}

type Message implements Abstract {
content(pick: Int!, name: String): String!
author: String
datePosted: DateTime
}
Loading

0 comments on commit b29a977

Please sign in to comment.