Skip to content

Commit 2553953

Browse files
committed
Add deprecations to IDL
1 parent cb103a7 commit 2553953

File tree

6 files changed

+88
-12
lines changed

6 files changed

+88
-12
lines changed

lib/graphql/directive.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ class Directive
1616
MUTATION = :MUTATION,
1717
SUBSCRIPTION = :SUBSCRIPTION,
1818
FIELD = :FIELD,
19+
FIELD_DEFINITION = :FIELD_DEFINITION,
1920
FRAGMENT_DEFINITION = :FRAGMENT_DEFINITION,
2021
FRAGMENT_SPREAD = :FRAGMENT_SPREAD,
2122
INLINE_FRAGMENT = :INLINE_FRAGMENT,
23+
ENUM_VALUE = :ENUM_VALUE,
2224
]
2325

2426
def initialize
2527
@arguments = {}
2628
end
2729

2830
def include?(arguments)
31+
return true if include_proc.nil?
2932
include_proc.call(arguments)
3033
end
3134

@@ -49,3 +52,4 @@ def on_operation?
4952

5053
require "graphql/directive/include_directive"
5154
require "graphql/directive/skip_directive"
55+
require "graphql/directive/deprecated_directive"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
GraphQL::Directive::DeprecatedDirective = GraphQL::Directive.define do
2+
name "deprecated"
3+
description "Marks an element of a GraphQL schema as no longer supported."
4+
locations([GraphQL::Directive::FIELD_DEFINITION, GraphQL::Directive::ENUM_VALUE])
5+
6+
reason_description = "Explains why this element was deprecated, usually also including a "\
7+
"suggestion for how to access supported similar data. Formatted "\
8+
"in [Markdown](https://daringfireball.net/projects/markdown/)."
9+
10+
argument :reason, !GraphQL::STRING_TYPE, reason_description, default_value: "No longer supported"
11+
end

lib/graphql/schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class Schema
6161
:query_analyzers, :middleware
6262

6363

64-
DIRECTIVES = [GraphQL::Directive::SkipDirective, GraphQL::Directive::IncludeDirective]
64+
DIRECTIVES = [GraphQL::Directive::SkipDirective, GraphQL::Directive::IncludeDirective, GraphQL::Directive::DeprecatedDirective]
6565
DYNAMIC_FIELDS = ["__type", "__typename", "__schema"]
6666
RESOLVE_TYPE_PROC_REQUIRED = -> (obj, ctx) { raise("Schema.resolve_type is undefined, can't resolve type for #{obj.inspect}") }
6767

lib/graphql/schema/printer.rb

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module Printer
1212
# Return a GraphQL schema string for the defined types in the schema
1313
# @param schema [GraphQL::Schema]
1414
def print_schema(schema)
15-
print_filtered_schema(schema, method(:is_defined_type))
15+
print_filtered_schema(schema, lambda { |n| !is_spec_directive(n) }, method(:is_defined_type))
1616
end
1717

1818
# Return the GraphQL schema string for the introspection type system
@@ -21,16 +21,20 @@ def print_introspection_schema
2121
name "Query"
2222
end
2323
schema = GraphQL::Schema.define(query: query_root)
24-
print_filtered_schema(schema, method(:is_introspection_type))
24+
print_filtered_schema(schema, method(:is_spec_directive), method(:is_introspection_type))
2525
end
2626

2727
private
2828

29-
def print_filtered_schema(schema, type_filter)
29+
def print_filtered_schema(schema, directive_filter, type_filter)
30+
directives = schema.directives.values.select{ |directive| directive_filter.call(directive) }
31+
directive_definitions = directives.map{ |directive| print_directive(directive) }
32+
3033
types = schema.types.values.select{ |type| type_filter.call(type) }.sort_by(&:name)
3134
type_definitions = types.map{ |type| print_type(type) }
3235

33-
[print_schema_definition(schema)].concat(type_definitions).join("\n\n")
36+
[print_schema_definition(schema)].concat(directive_definitions)
37+
.concat(type_definitions).join("\n\n")
3438
end
3539

3640
def print_schema_definition(schema)
@@ -44,6 +48,10 @@ def print_schema_definition(schema)
4448
BUILTIN_SCALARS = Set.new(["String", "Boolean", "Int", "Float", "ID"])
4549
private_constant :BUILTIN_SCALARS
4650

51+
def is_spec_directive(directive)
52+
['skip', 'include', 'deprecated'].include?(directive.name)
53+
end
54+
4755
def is_introspection_type(type)
4856
type.name.start_with?("__")
4957
end
@@ -56,12 +64,24 @@ def print_type(type)
5664
TypeKindPrinters::STRATEGIES.fetch(type.kind).print(type)
5765
end
5866

67+
def print_directive(directive)
68+
TypeKindPrinters::DirectivePrinter.print(directive)
69+
end
70+
5971
module TypeKindPrinters
60-
module FieldPrinter
61-
def print_fields(type)
62-
type.all_fields.map{ |field| " #{field.name}#{print_args(field)}: #{field.type}" }.join("\n")
72+
module DeprecatedPrinter
73+
def print_deprecated(field_or_enum_value)
74+
return unless field_or_enum_value.deprecation_reason
75+
76+
if field_or_enum_value.deprecation_reason == ''
77+
' @deprecated'
78+
else
79+
" @deprecated(reason: #{field_or_enum_value.deprecation_reason.to_s.inspect})"
80+
end
6381
end
82+
end
6483

84+
module ArgsPrinter
6585
def print_args(field)
6686
return if field.arguments.empty?
6787
"(#{field.arguments.values.map{ |arg| print_input_value(arg) }.join(", ")})"
@@ -105,6 +125,24 @@ def print_value(value, type)
105125
end
106126
end
107127

128+
module FieldPrinter
129+
include DeprecatedPrinter
130+
include ArgsPrinter
131+
def print_fields(type)
132+
type.all_fields.map{ |field|
133+
" #{field.name}#{print_args(field)}: #{field.type}#{print_deprecated(field)}"
134+
}.join("\n")
135+
end
136+
end
137+
138+
class DirectivePrinter
139+
extend ArgsPrinter
140+
def self.print(directive)
141+
"directive @#{directive.name}#{print_args(directive)} "\
142+
"on #{directive.locations.join(' | ')}"
143+
end
144+
end
145+
108146
class ScalarPrinter
109147
def self.print(type)
110148
"scalar #{type.name}"
@@ -137,8 +175,9 @@ def self.print(type)
137175
end
138176

139177
class EnumPrinter
178+
extend DeprecatedPrinter
140179
def self.print(type)
141-
values = type.values.values.map{ |v| " #{v.name}" }.join("\n")
180+
values = type.values.values.map{ |v| " #{v.name}#{print_deprecated(v)}" }.join("\n")
142181
"enum #{type.name} {\n#{values}\n}"
143182
end
144183
end

spec/graphql/introspection/directive_type_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@
4242
"onFragment" => true,
4343
"onOperation" => false,
4444
},
45+
{
46+
"name" => "deprecated",
47+
"args" => [
48+
{"name"=>"reason", "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"String"}}}
49+
],
50+
"locations"=>["FIELD_DEFINITION", "ENUM_VALUE"],
51+
"onField" => false,
52+
"onFragment" => false,
53+
"onOperation" => false,
54+
},
4555
]
4656
}
4757
}}

spec/graphql/schema/printer_spec.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
value "FOO"
1515
value "BAR"
16+
value "BAZ", deprecation_reason: 'Use "BAR".'
1617
end
1718

1819
sub_input_type = GraphQL::InputObjectType.define do
@@ -46,6 +47,7 @@
4647
field :title, !types.String
4748
field :body, !types.String
4849
field :comments, types[!comment_type]
50+
field :comments_count, !types.Int, deprecation_reason: 'Use "comments".'
4951
end
5052

5153
query_root = GraphQL::ObjectType.define do
@@ -70,24 +72,32 @@
7072
query: Query
7173
}
7274
75+
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
76+
77+
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
78+
79+
directive @deprecated(reason: String! = \"No longer supported\") on FIELD_DEFINITION | ENUM_VALUE
80+
7381
type __Directive {
7482
name: String!
7583
description: String
7684
args: [__InputValue!]!
7785
locations: [__DirectiveLocation!]!
78-
onOperation: Boolean!
79-
onFragment: Boolean!
80-
onField: Boolean!
86+
onOperation: Boolean! @deprecated(reason: \"Moved to 'locations' field\")
87+
onFragment: Boolean! @deprecated(reason: \"Moved to 'locations' field\")
88+
onField: Boolean! @deprecated(reason: \"Moved to 'locations' field\")
8189
}
8290
8391
enum __DirectiveLocation {
8492
QUERY
8593
MUTATION
8694
SUBSCRIPTION
8795
FIELD
96+
FIELD_DEFINITION
8897
FRAGMENT_DEFINITION
8998
FRAGMENT_SPREAD
9099
INLINE_FRAGMENT
100+
ENUM_VALUE
91101
}
92102
93103
type __EnumValue {
@@ -158,6 +168,7 @@
158168
enum Choice {
159169
FOO
160170
BAR
171+
BAZ @deprecated(reason: "Use \\\"BAR\\\".")
161172
}
162173
163174
type Comment implements Node {
@@ -173,6 +184,7 @@
173184
title: String!
174185
body: String!
175186
comments: [Comment!]
187+
comments_count: Int! @deprecated(reason: \"Use \\\"comments\\\".\")
176188
}
177189
178190
type Query {

0 commit comments

Comments
 (0)