Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

graphql: Allow user to define and pass arguments to fields. #5562

Merged
merged 8 commits into from
Jun 10, 2020
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