Skip to content

Commit

Permalink
support for @deprecated directive on fields (fixes #64)
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed Mar 12, 2017
1 parent 93ddece commit 498fe39
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 10 deletions.
78 changes: 78 additions & 0 deletions graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,66 @@ func TestIncludeDirective(t *testing.T) {
})
}

type testDeprecatedDirectiveResolver struct{}

func (r *testDeprecatedDirectiveResolver) A() int32 {
return 0
}

func (r *testDeprecatedDirectiveResolver) B() int32 {
return 0
}

func (r *testDeprecatedDirectiveResolver) C() int32 {
return 0
}

func TestDeprecatedDirective(t *testing.T) {
graphql.RunTests(t, []*graphql.Test{
{
Schema: graphql.MustParseSchema(`
schema {
query: Query
}
type Query {
a: Int!
b: Int! @deprecated
c: Int! @deprecated(reason: "We don't like it")
}
`, &testDeprecatedDirectiveResolver{}),
Query: `
{
__type(name: "Query") {
fields {
name
}
allFields: fields(includeDeprecated: true) {
name
isDeprecated
deprecationReason
}
}
}
`,
ExpectedResult: `
{
"__type": {
"fields": [
{ "name": "a" }
],
"allFields": [
{ "name": "a", "isDeprecated": false, "deprecationReason": null },
{ "name": "b", "isDeprecated": true, "deprecationReason": "No longer supported" },
{ "name": "c", "isDeprecated": true, "deprecationReason": "We don't like it" }
]
}
}
`,
},
})
}

func TestInlineFragments(t *testing.T) {
graphql.RunTests(t, []*graphql.Test{
{
Expand Down Expand Up @@ -1134,6 +1194,24 @@ func TestIntrospection(t *testing.T) {
{
"__schema": {
"directives": [
{
"name": "deprecated",
"description": "Marks an element of a GraphQL schema as no longer supported.",
"locations": [
"FIELD_DEFINITION",
"ENUM_VALUE"
],
"args": [
{
"name": "reason",
"description": "Explains why this element was deprecated, usually also including a suggestion\nfor how to access supported similar data. Formatted in\n[Markdown](https://daringfireball.net/projects/markdown/).",
"type": {
"kind": "SCALAR",
"ofType": null
}
}
]
},
{
"name": "include",
"description": "Directs the executor to include this field or fragment only when the ` + "`" + `if` + "`" + ` argument is true.",
Expand Down
2 changes: 1 addition & 1 deletion internal/common/directive.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func ParseDirectives(l *lexer.Lexer) map[string]DirectiveArgs {
for l.Peek() == '@' {
l.ConsumeToken('@')
name := l.ConsumeIdent()
var args DirectiveArgs
args := make(DirectiveArgs)
if l.Peek() == '(' {
args = ParseArguments(l)
}
Expand Down
8 changes: 8 additions & 0 deletions internal/schema/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ var metaSrc = `
if: Boolean!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
# Marks an element of a GraphQL schema as no longer supported.
directive @deprecated(
# Explains why this element was deprecated, usually also including a suggestion
# for how to access supported similar data. Formatted in
# [Markdown](https://daringfireball.net/projects/markdown/).
reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE
# A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
#
# In some cases, you need to provide options to alter GraphQL's execution behavior
Expand Down
26 changes: 22 additions & 4 deletions internal/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,11 @@ func (t *Enum) Description() string { return t.Desc }
func (t *InputObject) Description() string { return t.Desc }

type Field struct {
Name string
Args common.InputMap
Type common.Type
Desc string
Name string
Args common.InputMap
Type common.Type
Directives map[string]common.DirectiveArgs
Desc string
}

func New() *Schema {
Expand Down Expand Up @@ -238,6 +239,22 @@ func resolveField(s *Schema, f *Field) error {
return err
}
f.Type = t
for name, args := range f.Directives {
d, ok := s.Directives[name]
if !ok {
return errors.Errorf("directive %q not found", name)
}
for argName := range args {
if _, ok := d.Args[argName]; !ok {
return errors.Errorf("invalid argument %q for directive %q", argName, name)
}
}
for argName, arg := range d.Args {
if _, ok := args[argName]; !ok {
args[argName] = arg.Default
}
}
}
return resolveInputObject(s, &f.Args)
}

Expand Down Expand Up @@ -410,6 +427,7 @@ func parseFields(l *lexer.Lexer) (map[string]*Field, []string) {
}
l.ConsumeToken(':')
f.Type = common.ParseType(l)
f.Directives = common.ParseDirectives(l)
fields[f.Name] = f
fieldOrder = append(fieldOrder, f.Name)
}
Expand Down
19 changes: 14 additions & 5 deletions introspection/introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,12 @@ func (r *Type) Fields(args *struct{ IncludeDeprecated bool }) *[]*Field {
return nil
}

l := make([]*Field, len(fieldOrder))
for i, name := range fieldOrder {
l[i] = &Field{fields[name]}
var l []*Field
for _, name := range fieldOrder {
f := fields[name]
if _, ok := f.Directives["deprecated"]; !ok || args.IncludeDeprecated {
l = append(l, &Field{f})
}
}
return &l
}
Expand Down Expand Up @@ -218,11 +221,17 @@ func (r *Field) Type() *Type {
}

func (r *Field) IsDeprecated() bool {
return false
_, ok := r.field.Directives["deprecated"]
return ok
}

func (r *Field) DeprecationReason() *string {
return nil
args, ok := r.field.Directives["deprecated"]
if !ok {
return nil
}
reason := args["reason"].(string)
return &reason
}

type InputValue struct {
Expand Down

0 comments on commit 498fe39

Please sign in to comment.