Skip to content

Commit 498fe39

Browse files
committed
support for @deprecated directive on fields (fixes #64)
1 parent 93ddece commit 498fe39

File tree

5 files changed

+123
-10
lines changed

5 files changed

+123
-10
lines changed

graphql_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,66 @@ func TestIncludeDirective(t *testing.T) {
462462
})
463463
}
464464

465+
type testDeprecatedDirectiveResolver struct{}
466+
467+
func (r *testDeprecatedDirectiveResolver) A() int32 {
468+
return 0
469+
}
470+
471+
func (r *testDeprecatedDirectiveResolver) B() int32 {
472+
return 0
473+
}
474+
475+
func (r *testDeprecatedDirectiveResolver) C() int32 {
476+
return 0
477+
}
478+
479+
func TestDeprecatedDirective(t *testing.T) {
480+
graphql.RunTests(t, []*graphql.Test{
481+
{
482+
Schema: graphql.MustParseSchema(`
483+
schema {
484+
query: Query
485+
}
486+
487+
type Query {
488+
a: Int!
489+
b: Int! @deprecated
490+
c: Int! @deprecated(reason: "We don't like it")
491+
}
492+
`, &testDeprecatedDirectiveResolver{}),
493+
Query: `
494+
{
495+
__type(name: "Query") {
496+
fields {
497+
name
498+
}
499+
allFields: fields(includeDeprecated: true) {
500+
name
501+
isDeprecated
502+
deprecationReason
503+
}
504+
}
505+
}
506+
`,
507+
ExpectedResult: `
508+
{
509+
"__type": {
510+
"fields": [
511+
{ "name": "a" }
512+
],
513+
"allFields": [
514+
{ "name": "a", "isDeprecated": false, "deprecationReason": null },
515+
{ "name": "b", "isDeprecated": true, "deprecationReason": "No longer supported" },
516+
{ "name": "c", "isDeprecated": true, "deprecationReason": "We don't like it" }
517+
]
518+
}
519+
}
520+
`,
521+
},
522+
})
523+
}
524+
465525
func TestInlineFragments(t *testing.T) {
466526
graphql.RunTests(t, []*graphql.Test{
467527
{
@@ -1134,6 +1194,24 @@ func TestIntrospection(t *testing.T) {
11341194
{
11351195
"__schema": {
11361196
"directives": [
1197+
{
1198+
"name": "deprecated",
1199+
"description": "Marks an element of a GraphQL schema as no longer supported.",
1200+
"locations": [
1201+
"FIELD_DEFINITION",
1202+
"ENUM_VALUE"
1203+
],
1204+
"args": [
1205+
{
1206+
"name": "reason",
1207+
"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/).",
1208+
"type": {
1209+
"kind": "SCALAR",
1210+
"ofType": null
1211+
}
1212+
}
1213+
]
1214+
},
11371215
{
11381216
"name": "include",
11391217
"description": "Directs the executor to include this field or fragment only when the ` + "`" + `if` + "`" + ` argument is true.",

internal/common/directive.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func ParseDirectives(l *lexer.Lexer) map[string]DirectiveArgs {
1111
for l.Peek() == '@' {
1212
l.ConsumeToken('@')
1313
name := l.ConsumeIdent()
14-
var args DirectiveArgs
14+
args := make(DirectiveArgs)
1515
if l.Peek() == '(' {
1616
args = ParseArguments(l)
1717
}

internal/schema/meta.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ var metaSrc = `
3838
if: Boolean!
3939
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
4040
41+
# Marks an element of a GraphQL schema as no longer supported.
42+
directive @deprecated(
43+
# Explains why this element was deprecated, usually also including a suggestion
44+
# for how to access supported similar data. Formatted in
45+
# [Markdown](https://daringfireball.net/projects/markdown/).
46+
reason: String = "No longer supported"
47+
) on FIELD_DEFINITION | ENUM_VALUE
48+
4149
# A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
4250
#
4351
# In some cases, you need to provide options to alter GraphQL's execution behavior

internal/schema/schema.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,11 @@ func (t *Enum) Description() string { return t.Desc }
115115
func (t *InputObject) Description() string { return t.Desc }
116116

117117
type Field struct {
118-
Name string
119-
Args common.InputMap
120-
Type common.Type
121-
Desc string
118+
Name string
119+
Args common.InputMap
120+
Type common.Type
121+
Directives map[string]common.DirectiveArgs
122+
Desc string
122123
}
123124

124125
func New() *Schema {
@@ -238,6 +239,22 @@ func resolveField(s *Schema, f *Field) error {
238239
return err
239240
}
240241
f.Type = t
242+
for name, args := range f.Directives {
243+
d, ok := s.Directives[name]
244+
if !ok {
245+
return errors.Errorf("directive %q not found", name)
246+
}
247+
for argName := range args {
248+
if _, ok := d.Args[argName]; !ok {
249+
return errors.Errorf("invalid argument %q for directive %q", argName, name)
250+
}
251+
}
252+
for argName, arg := range d.Args {
253+
if _, ok := args[argName]; !ok {
254+
args[argName] = arg.Default
255+
}
256+
}
257+
}
241258
return resolveInputObject(s, &f.Args)
242259
}
243260

@@ -410,6 +427,7 @@ func parseFields(l *lexer.Lexer) (map[string]*Field, []string) {
410427
}
411428
l.ConsumeToken(':')
412429
f.Type = common.ParseType(l)
430+
f.Directives = common.ParseDirectives(l)
413431
fields[f.Name] = f
414432
fieldOrder = append(fieldOrder, f.Name)
415433
}

introspection/introspection.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,12 @@ func (r *Type) Fields(args *struct{ IncludeDeprecated bool }) *[]*Field {
115115
return nil
116116
}
117117

118-
l := make([]*Field, len(fieldOrder))
119-
for i, name := range fieldOrder {
120-
l[i] = &Field{fields[name]}
118+
var l []*Field
119+
for _, name := range fieldOrder {
120+
f := fields[name]
121+
if _, ok := f.Directives["deprecated"]; !ok || args.IncludeDeprecated {
122+
l = append(l, &Field{f})
123+
}
121124
}
122125
return &l
123126
}
@@ -218,11 +221,17 @@ func (r *Field) Type() *Type {
218221
}
219222

220223
func (r *Field) IsDeprecated() bool {
221-
return false
224+
_, ok := r.field.Directives["deprecated"]
225+
return ok
222226
}
223227

224228
func (r *Field) DeprecationReason() *string {
225-
return nil
229+
args, ok := r.field.Directives["deprecated"]
230+
if !ok {
231+
return nil
232+
}
233+
reason := args["reason"].(string)
234+
return &reason
226235
}
227236

228237
type InputValue struct {

0 commit comments

Comments
 (0)