graphql 1.7.14 → 1.8.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/generators/graphql/function_generator.rb +1 -1
- data/lib/generators/graphql/loader_generator.rb +1 -1
- data/lib/generators/graphql/mutation_generator.rb +1 -6
- data/lib/generators/graphql/templates/function.erb +2 -2
- data/lib/generators/graphql/templates/loader.erb +2 -2
- data/lib/graphql.rb +2 -0
- data/lib/graphql/argument.rb +0 -1
- data/lib/graphql/backwards_compatibility.rb +2 -3
- data/lib/graphql/base_type.rb +18 -16
- data/lib/graphql/compatibility/query_parser_specification.rb +0 -117
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -14
- data/lib/graphql/define/assign_object_field.rb +5 -12
- data/lib/graphql/deprecated_dsl.rb +28 -0
- data/lib/graphql/directive.rb +0 -1
- data/lib/graphql/enum_type.rb +1 -3
- data/lib/graphql/execution.rb +0 -1
- data/lib/graphql/execution/multiplex.rb +29 -12
- data/lib/graphql/field.rb +5 -20
- data/lib/graphql/function.rb +12 -0
- data/lib/graphql/input_object_type.rb +1 -3
- data/lib/graphql/internal_representation/node.rb +14 -26
- data/lib/graphql/internal_representation/visit.rb +6 -3
- data/lib/graphql/introspection/arguments_field.rb +0 -1
- data/lib/graphql/introspection/enum_values_field.rb +0 -1
- data/lib/graphql/introspection/fields_field.rb +0 -1
- data/lib/graphql/introspection/input_fields_field.rb +0 -1
- data/lib/graphql/introspection/interfaces_field.rb +0 -1
- data/lib/graphql/introspection/of_type_field.rb +0 -1
- data/lib/graphql/introspection/possible_types_field.rb +0 -1
- data/lib/graphql/introspection/schema_field.rb +0 -1
- data/lib/graphql/introspection/type_by_name_field.rb +0 -1
- data/lib/graphql/introspection/typename_field.rb +0 -1
- data/lib/graphql/language.rb +0 -3
- data/lib/graphql/language/generation.rb +182 -3
- data/lib/graphql/language/lexer.rb +69 -144
- data/lib/graphql/language/lexer.rl +4 -15
- data/lib/graphql/language/nodes.rb +76 -136
- data/lib/graphql/language/parser.rb +621 -668
- data/lib/graphql/language/parser.y +11 -17
- data/lib/graphql/language/token.rb +3 -10
- data/lib/graphql/object_type.rb +6 -1
- data/lib/graphql/query.rb +13 -8
- data/lib/graphql/query/arguments.rb +33 -48
- data/lib/graphql/query/context.rb +1 -0
- data/lib/graphql/query/literal_input.rb +1 -4
- data/lib/graphql/relay/connection_resolve.rb +3 -0
- data/lib/graphql/relay/global_id_resolve.rb +5 -1
- data/lib/graphql/relay/relation_connection.rb +19 -14
- data/lib/graphql/schema.rb +219 -12
- data/lib/graphql/schema/argument.rb +33 -0
- data/lib/graphql/schema/build_from_definition.rb +18 -64
- data/lib/graphql/schema/enum.rb +76 -0
- data/lib/graphql/schema/field.rb +127 -0
- data/lib/graphql/schema/field/dynamic_resolve.rb +63 -0
- data/lib/graphql/schema/field/unwrapped_resolve.rb +20 -0
- data/lib/graphql/schema/input_object.rb +61 -0
- data/lib/graphql/schema/interface.rb +32 -0
- data/lib/graphql/schema/loader.rb +2 -2
- data/lib/graphql/schema/member.rb +97 -0
- data/lib/graphql/schema/member/build_type.rb +106 -0
- data/lib/graphql/schema/member/has_fields.rb +56 -0
- data/lib/graphql/schema/member/instrumentation.rb +113 -0
- data/lib/graphql/schema/member/list_type_proxy.rb +21 -0
- data/lib/graphql/schema/member/non_null_type_proxy.rb +21 -0
- data/lib/graphql/schema/object.rb +65 -0
- data/lib/graphql/schema/printer.rb +266 -33
- data/lib/graphql/schema/scalar.rb +25 -0
- data/lib/graphql/schema/traversal.rb +26 -17
- data/lib/graphql/schema/union.rb +48 -0
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -15
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -11
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -7
- data/lib/graphql/tracing.rb +0 -1
- data/lib/graphql/tracing/platform_tracing.rb +7 -20
- data/lib/graphql/tracing/scout_tracing.rb +2 -2
- data/lib/graphql/unresolved_type_error.rb +2 -3
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- data/spec/dummy/app/channels/graphql_channel.rb +1 -22
- data/spec/dummy/log/development.log +0 -239
- data/spec/dummy/log/test.log +0 -204
- data/spec/dummy/test/system/action_cable_subscription_test.rb +0 -4
- data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
- data/spec/generators/graphql/function_generator_spec.rb +0 -26
- data/spec/generators/graphql/loader_generator_spec.rb +0 -24
- data/spec/graphql/analysis/max_query_complexity_spec.rb +3 -3
- data/spec/graphql/analysis/max_query_depth_spec.rb +3 -3
- data/spec/graphql/backtrace_spec.rb +0 -10
- data/spec/graphql/base_type_spec.rb +5 -19
- data/spec/graphql/boolean_type_spec.rb +3 -3
- data/spec/graphql/directive_spec.rb +1 -3
- data/spec/graphql/enum_type_spec.rb +5 -18
- data/spec/graphql/execution/execute_spec.rb +1 -1
- data/spec/graphql/execution/multiplex_spec.rb +2 -2
- data/spec/graphql/float_type_spec.rb +2 -2
- data/spec/graphql/id_type_spec.rb +1 -1
- data/spec/graphql/input_object_type_spec.rb +2 -15
- data/spec/graphql/int_type_spec.rb +2 -2
- data/spec/graphql/internal_representation/rewrite_spec.rb +2 -2
- data/spec/graphql/introspection/schema_type_spec.rb +0 -1
- data/spec/graphql/language/generation_spec.rb +186 -21
- data/spec/graphql/language/lexer_spec.rb +1 -21
- data/spec/graphql/language/nodes_spec.rb +12 -21
- data/spec/graphql/language/parser_spec.rb +1 -1
- data/spec/graphql/query/arguments_spec.rb +15 -37
- data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -2
- data/spec/graphql/query/variables_spec.rb +1 -1
- data/spec/graphql/query_spec.rb +5 -31
- data/spec/graphql/rake_task_spec.rb +1 -3
- data/spec/graphql/relay/base_connection_spec.rb +1 -1
- data/spec/graphql/relay/connection_instrumentation_spec.rb +2 -2
- data/spec/graphql/relay/connection_resolve_spec.rb +1 -1
- data/spec/graphql/relay/connection_type_spec.rb +1 -1
- data/spec/graphql/relay/mutation_spec.rb +3 -3
- data/spec/graphql/relay/relation_connection_spec.rb +1 -65
- data/spec/graphql/schema/build_from_definition_spec.rb +4 -86
- data/spec/graphql/schema/enum_spec.rb +60 -0
- data/spec/graphql/schema/field_spec.rb +14 -0
- data/spec/graphql/schema/input_object_spec.rb +43 -0
- data/spec/graphql/schema/interface_spec.rb +98 -0
- data/spec/graphql/schema/object_spec.rb +119 -0
- data/spec/graphql/schema/printer_spec.rb +15 -92
- data/spec/graphql/schema/scalar_spec.rb +40 -0
- data/spec/graphql/schema/union_spec.rb +35 -0
- data/spec/graphql/schema/validation_spec.rb +1 -1
- data/spec/graphql/schema/warden_spec.rb +11 -11
- data/spec/graphql/schema_spec.rb +25 -23
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +2 -10
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
- data/spec/graphql/string_type_spec.rb +3 -3
- data/spec/graphql/subscriptions_spec.rb +1 -1
- data/spec/graphql/tracing/platform_tracing_spec.rb +1 -60
- data/spec/support/dummy/schema.rb +25 -39
- data/spec/support/jazz.rb +334 -0
- data/spec/support/lazy_helpers.rb +21 -23
- data/spec/support/star_wars/data.rb +7 -6
- data/spec/support/star_wars/schema.rb +109 -142
- metadata +39 -33
- data/lib/graphql/execution/instrumentation.rb +0 -82
- data/lib/graphql/language/block_string.rb +0 -47
- data/lib/graphql/language/document_from_schema_definition.rb +0 -277
- data/lib/graphql/language/printer.rb +0 -351
- data/lib/graphql/tracing/data_dog_tracing.rb +0 -49
- data/spec/graphql/execution/instrumentation_spec.rb +0 -165
- data/spec/graphql/language/block_string_spec.rb +0 -70
- data/spec/graphql/language/document_from_schema_definition_spec.rb +0 -770
- data/spec/graphql/language/printer_spec.rb +0 -203
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Member
|
5
|
+
# @api private
|
6
|
+
module BuildType
|
7
|
+
module_function
|
8
|
+
# @param type_expr [String, Class, GraphQL::BaseType]
|
9
|
+
# @return [GraphQL::BaseType]
|
10
|
+
def parse_type(type_expr, null:)
|
11
|
+
list_type = false
|
12
|
+
|
13
|
+
return_type = case type_expr
|
14
|
+
when String
|
15
|
+
case type_expr
|
16
|
+
when "String"
|
17
|
+
GraphQL::STRING_TYPE
|
18
|
+
when "Int", "Integer"
|
19
|
+
GraphQL::INT_TYPE
|
20
|
+
when "Float"
|
21
|
+
GraphQL::FLOAT_TYPE
|
22
|
+
when "Boolean"
|
23
|
+
GraphQL::BOOLEAN_TYPE
|
24
|
+
when "ID"
|
25
|
+
GraphQL::ID_TYPE
|
26
|
+
when /\A\[.*\]\Z/
|
27
|
+
list_type = true
|
28
|
+
# List members are required by default
|
29
|
+
parse_type(type_expr[1..-2], null: false)
|
30
|
+
when /.*!\Z/
|
31
|
+
null = false
|
32
|
+
parse_type(type_expr[0..-2], null: true)
|
33
|
+
else
|
34
|
+
maybe_type = Object.const_get(type_expr)
|
35
|
+
case maybe_type
|
36
|
+
when GraphQL::BaseType
|
37
|
+
maybe_type
|
38
|
+
when Class
|
39
|
+
if maybe_type < GraphQL::Schema::Member
|
40
|
+
maybe_type.graphql_definition
|
41
|
+
else
|
42
|
+
raise "Unexpected class found for GraphQL type: #{type_expr} (must be GraphQL::Object)"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
when GraphQL::BaseType
|
47
|
+
type_expr
|
48
|
+
when Array
|
49
|
+
if type_expr.length != 1
|
50
|
+
raise "Use an array of length = 1 for list types; other arrays are not supported"
|
51
|
+
end
|
52
|
+
list_type = true
|
53
|
+
# List members are required by default
|
54
|
+
parse_type(type_expr.first, null: false)
|
55
|
+
when Class
|
56
|
+
if Class < GraphQL::Schema::Member
|
57
|
+
type_expr.graphql_definition
|
58
|
+
else
|
59
|
+
# Eg `String` => GraphQL::STRING_TYPE
|
60
|
+
parse_type(type_expr.name, null: true)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Apply list_type first, that way the
|
65
|
+
# .to_non_null_type applies to the list type, not the inner type
|
66
|
+
if list_type
|
67
|
+
return_type = return_type.to_list_type
|
68
|
+
end
|
69
|
+
|
70
|
+
if !null
|
71
|
+
return_type = return_type.to_non_null_type
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
return_type
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_type_name(something)
|
79
|
+
case something
|
80
|
+
when GraphQL::BaseType
|
81
|
+
something.unwrap.name
|
82
|
+
when Array
|
83
|
+
to_type_name(something.first)
|
84
|
+
when Module
|
85
|
+
if something < GraphQL::Schema::Member
|
86
|
+
something.graphql_name
|
87
|
+
else
|
88
|
+
something.name.split("::").last
|
89
|
+
end
|
90
|
+
when String
|
91
|
+
something.gsub(/\]\[\!/, "").split("::").last
|
92
|
+
else
|
93
|
+
raise "Unhandled to_type_name input: #{something} (#{something.class})"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def underscore(string)
|
98
|
+
string
|
99
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2') # URLDecoder -> URL_Decoder
|
100
|
+
.gsub(/([a-z\d])([A-Z])/,'\1_\2') # someThing -> some_Thing
|
101
|
+
.downcase
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Member
|
5
|
+
# Shared code for Object and Interface
|
6
|
+
module HasFields
|
7
|
+
# Add a field to this object or interface with the given definition
|
8
|
+
# @see {GraphQL::Schema::Field#initialize} for method signature
|
9
|
+
# @return [void]
|
10
|
+
def field(*args, &block)
|
11
|
+
field_defn = field_class.new(*args, &block)
|
12
|
+
add_field(field_defn)
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Array<GraphQL::Schema::Field>] Fields on this object, including inherited fields
|
17
|
+
def fields
|
18
|
+
all_fields = own_fields
|
19
|
+
inherited_fields = (superclass.is_a?(HasFields) ? superclass.fields : [])
|
20
|
+
# Remove any inherited fields which were overridden on this class:
|
21
|
+
inherited_fields.each do |inherited_f|
|
22
|
+
if all_fields.none? {|f| f.name == inherited_f.name}
|
23
|
+
all_fields << inherited_f
|
24
|
+
end
|
25
|
+
end
|
26
|
+
all_fields
|
27
|
+
end
|
28
|
+
|
29
|
+
# Register this field with the class, overriding a previous one if needed
|
30
|
+
# @param field_defn [GraphQL::Schema::Field]
|
31
|
+
# @return [void]
|
32
|
+
def add_field(field_defn)
|
33
|
+
fields.reject! {|f| f.name == field_defn.name}
|
34
|
+
own_fields << field_defn
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Class] The class to initialize when adding fields to this kind of schema member
|
39
|
+
def field_class(new_field_class = nil)
|
40
|
+
if new_field_class
|
41
|
+
@field_class = new_field_class
|
42
|
+
else
|
43
|
+
@field_class || superclass.field_class
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# @return [Array<GraphQL::Schema::Field>] Fields defined on this class _specifically_, not parent classes
|
50
|
+
def own_fields
|
51
|
+
@own_fields ||= []
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# test_via: ../object.rb
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
class Schema
|
6
|
+
class Member
|
7
|
+
module Instrumentation
|
8
|
+
module_function
|
9
|
+
def instrument(type, field)
|
10
|
+
return_type = field.type.unwrap
|
11
|
+
if return_type.metadata[:object_class] ||
|
12
|
+
return_type.is_a?(GraphQL::InterfaceType) ||
|
13
|
+
(return_type.is_a?(GraphQL::UnionType) && return_type.possible_types.any? { |t| t.metadata[:object_class] })
|
14
|
+
field = apply_proxy(field)
|
15
|
+
end
|
16
|
+
|
17
|
+
field
|
18
|
+
end
|
19
|
+
|
20
|
+
def before_query(query)
|
21
|
+
# Get the root type for this query
|
22
|
+
root_node = query.irep_selection
|
23
|
+
if root_node.nil?
|
24
|
+
# It's an invalid query, nothing to do here
|
25
|
+
else
|
26
|
+
root_type = query.irep_selection.return_type
|
27
|
+
# If it has a wrapper, apply it
|
28
|
+
wrapper_class = root_type.metadata[:object_class]
|
29
|
+
if wrapper_class
|
30
|
+
new_root_value = wrapper_class.new(query.root_value, query.context)
|
31
|
+
query.root_value = new_root_value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def after_query(_query)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
module_function
|
42
|
+
|
43
|
+
def apply_proxy(field)
|
44
|
+
resolve_proc = field.resolve_proc
|
45
|
+
lazy_resolve_proc = field.lazy_resolve_proc
|
46
|
+
inner_return_type = field.type.unwrap
|
47
|
+
depth = list_depth(field.type)
|
48
|
+
|
49
|
+
field.redefine(
|
50
|
+
resolve: ProxiedResolve.new(inner_resolve: resolve_proc, list_depth: depth, inner_return_type: inner_return_type),
|
51
|
+
lazy_resolve: ProxiedResolve.new(inner_resolve: lazy_resolve_proc, list_depth: depth, inner_return_type: inner_return_type),
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def list_depth(type, starting_at = 0)
|
56
|
+
case type
|
57
|
+
when GraphQL::ListType
|
58
|
+
list_depth(type.of_type, starting_at + 1)
|
59
|
+
when GraphQL::NonNullType
|
60
|
+
list_depth(type.of_type, starting_at)
|
61
|
+
else
|
62
|
+
starting_at
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class ProxiedResolve
|
67
|
+
def initialize(inner_resolve:, list_depth:, inner_return_type:)
|
68
|
+
@inner_resolve = inner_resolve
|
69
|
+
@inner_return_type = inner_return_type
|
70
|
+
@list_depth = list_depth
|
71
|
+
end
|
72
|
+
|
73
|
+
def call(obj, args, ctx)
|
74
|
+
result = @inner_resolve.call(obj, args, ctx)
|
75
|
+
if ctx.schema.lazy?(result)
|
76
|
+
# Wrap it later
|
77
|
+
result
|
78
|
+
elsif ctx.skip == result
|
79
|
+
result
|
80
|
+
else
|
81
|
+
proxy_to_depth(result, @list_depth, @inner_return_type, ctx)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def proxy_to_depth(obj, depth, type, ctx)
|
88
|
+
if depth > 0
|
89
|
+
obj.map { |inner_obj| proxy_to_depth(inner_obj, depth - 1, type, ctx) }
|
90
|
+
elsif obj.nil?
|
91
|
+
obj
|
92
|
+
else
|
93
|
+
concrete_type = case type
|
94
|
+
when GraphQL::UnionType, GraphQL::InterfaceType
|
95
|
+
ctx.query.resolve_type(type, obj)
|
96
|
+
when GraphQL::ObjectType
|
97
|
+
type
|
98
|
+
else
|
99
|
+
raise "unexpected proxying type #{type} for #{obj} at #{ctx.owner_type}.#{ctx.field.name}"
|
100
|
+
end
|
101
|
+
|
102
|
+
if concrete_type && (object_class = concrete_type.metadata[:object_class])
|
103
|
+
object_class.new(obj, ctx)
|
104
|
+
else
|
105
|
+
obj
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Member
|
5
|
+
# Wraps a {Schema::Member} as a list type.
|
6
|
+
# @see {Schema::Member#to_list_type}
|
7
|
+
# @api private
|
8
|
+
class ListTypeProxy
|
9
|
+
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
10
|
+
|
11
|
+
def initialize(member)
|
12
|
+
@member = member
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_graphql
|
16
|
+
@member.graphql_definition.to_list_type
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Member
|
5
|
+
# Wraps a {Schema::Member} when it is required.
|
6
|
+
# @see {Schema::Member#to_non_null_type}
|
7
|
+
# @api private
|
8
|
+
class NonNullTypeProxy
|
9
|
+
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
10
|
+
|
11
|
+
def initialize(member)
|
12
|
+
@member = member
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_graphql
|
16
|
+
@member.graphql_definition.to_non_null_type
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Object < GraphQL::Schema::Member
|
6
|
+
attr_reader :object
|
7
|
+
|
8
|
+
def initialize(object, context)
|
9
|
+
@object = object
|
10
|
+
@context = context
|
11
|
+
end
|
12
|
+
extend GraphQL::Schema::Member::HasFields
|
13
|
+
field_class GraphQL::Schema::Field
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def implements(*new_interfaces)
|
17
|
+
new_interfaces.each do |int|
|
18
|
+
if int.is_a?(Class) && int < GraphQL::Schema::Interface
|
19
|
+
# Add the graphql field defns
|
20
|
+
int.fields.each do |field|
|
21
|
+
add_field(field)
|
22
|
+
end
|
23
|
+
# And call the implemented hook
|
24
|
+
int.apply_implemented(self)
|
25
|
+
else
|
26
|
+
int.all_fields.each do |f|
|
27
|
+
field(f.name, field: f)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
own_interfaces.concat(new_interfaces)
|
32
|
+
end
|
33
|
+
|
34
|
+
def interfaces
|
35
|
+
own_interfaces + (superclass <= GraphQL::Schema::Object ? superclass.interfaces : [])
|
36
|
+
end
|
37
|
+
|
38
|
+
def own_interfaces
|
39
|
+
@own_interfaces ||= []
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [GraphQL::ObjectType]
|
43
|
+
def to_graphql
|
44
|
+
obj_type = GraphQL::ObjectType.new
|
45
|
+
obj_type.name = graphql_name
|
46
|
+
obj_type.description = description
|
47
|
+
obj_type.interfaces = interfaces
|
48
|
+
|
49
|
+
fields.each do |field_inst|
|
50
|
+
field_defn = field_inst.graphql_definition
|
51
|
+
obj_type.fields[field_defn.name] = field_defn
|
52
|
+
end
|
53
|
+
|
54
|
+
obj_type.metadata[:object_class] = self
|
55
|
+
|
56
|
+
obj_type
|
57
|
+
end
|
58
|
+
|
59
|
+
def global_id_field(field_name)
|
60
|
+
field field_name, "ID", null: false, resolve: GraphQL::Relay::GlobalIdResolve.new(type: self)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
36
36
|
# printer = GraphQL::Schema::Printer.new(MySchema)
|
37
37
|
# puts printer.print_type(post_type)
|
38
38
|
#
|
39
|
-
class Printer
|
39
|
+
class Printer
|
40
40
|
attr_reader :schema, :warden
|
41
41
|
|
42
42
|
# @param schema [GraphQL::Schema]
|
@@ -45,32 +45,21 @@ module GraphQL
|
|
45
45
|
# @param except [<#call(member, ctx)>]
|
46
46
|
# @param introspection [Boolean] Should include the introspection types in the string?
|
47
47
|
def initialize(schema, context: nil, only: nil, except: nil, introspection: false)
|
48
|
-
@document_from_schema = GraphQL::Language::DocumentFromSchemaDefinition.new(
|
49
|
-
schema,
|
50
|
-
context: context,
|
51
|
-
only: only,
|
52
|
-
except: except,
|
53
|
-
include_introspection_types: introspection,
|
54
|
-
)
|
55
|
-
|
56
|
-
@document = @document_from_schema.document
|
57
|
-
|
58
48
|
@schema = schema
|
49
|
+
@context = context
|
50
|
+
|
51
|
+
blacklist = build_blacklist(only, except, introspection: introspection)
|
52
|
+
filter = GraphQL::Filter.new(except: blacklist)
|
53
|
+
@warden = GraphQL::Schema::Warden.new(filter, schema: @schema, context: @context)
|
59
54
|
end
|
60
55
|
|
61
56
|
# Return the GraphQL schema string for the introspection type system
|
62
57
|
def self.print_introspection_schema
|
63
58
|
query_root = ObjectType.define(name: "Root")
|
64
59
|
schema = GraphQL::Schema.define(query: query_root)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
except: ->(member, _) { member.name == "Root" },
|
69
|
-
include_introspection_types: true,
|
70
|
-
include_built_in_directives: true,
|
71
|
-
).document
|
72
|
-
|
73
|
-
introspection_schema_ast.to_query_string(printer: IntrospectionPrinter.new)
|
60
|
+
blacklist = ->(m, ctx) { m == query_root }
|
61
|
+
printer = new(schema, except: blacklist, introspection: true)
|
62
|
+
printer.print_schema
|
74
63
|
end
|
75
64
|
|
76
65
|
# Return a GraphQL schema string for the defined types in the schema
|
@@ -85,33 +74,277 @@ module GraphQL
|
|
85
74
|
|
86
75
|
# Return a GraphQL schema string for the defined types in the schema
|
87
76
|
def print_schema
|
88
|
-
|
77
|
+
directive_definitions = warden.directives.map { |directive| print_directive(directive) }
|
78
|
+
|
79
|
+
printable_types = warden.types.reject(&:default_scalar?)
|
80
|
+
|
81
|
+
type_definitions = printable_types
|
82
|
+
.sort_by(&:name)
|
83
|
+
.map { |type| print_type(type) }
|
84
|
+
|
85
|
+
[print_schema_definition].compact
|
86
|
+
.concat(directive_definitions)
|
87
|
+
.concat(type_definitions).join("\n\n")
|
89
88
|
end
|
90
89
|
|
91
90
|
def print_type(type)
|
92
|
-
|
93
|
-
print(node)
|
91
|
+
TypeKindPrinters::STRATEGIES.fetch(type.kind).print(warden, type)
|
94
92
|
end
|
95
93
|
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
private
|
95
|
+
|
96
|
+
# By default, these are included in a schema printout
|
97
|
+
IS_USER_DEFINED_MEMBER = ->(member) {
|
98
|
+
case member
|
99
|
+
when GraphQL::BaseType
|
100
|
+
!member.introspection?
|
101
|
+
when GraphQL::Directive
|
102
|
+
!member.default_directive?
|
103
|
+
else
|
104
|
+
true
|
105
|
+
end
|
106
|
+
}
|
107
|
+
|
108
|
+
private_constant :IS_USER_DEFINED_MEMBER
|
99
109
|
|
100
|
-
|
101
|
-
|
110
|
+
def build_blacklist(only, except, introspection:)
|
111
|
+
if introspection
|
112
|
+
if only
|
113
|
+
->(m, ctx) { !only.call(m, ctx) }
|
114
|
+
elsif except
|
115
|
+
except
|
102
116
|
else
|
103
|
-
|
117
|
+
->(m, ctx) { false }
|
104
118
|
end
|
105
119
|
else
|
106
|
-
|
120
|
+
if only
|
121
|
+
->(m, ctx) { !(IS_USER_DEFINED_MEMBER.call(m) && only.call(m, ctx)) }
|
122
|
+
elsif except
|
123
|
+
->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) || except.call(m, ctx) }
|
124
|
+
else
|
125
|
+
->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) }
|
126
|
+
end
|
107
127
|
end
|
108
128
|
end
|
109
129
|
|
110
|
-
|
111
|
-
|
112
|
-
|
130
|
+
def print_schema_definition
|
131
|
+
if (schema.query.nil? || schema.query.name == 'Query') &&
|
132
|
+
(schema.mutation.nil? || schema.mutation.name == 'Mutation') &&
|
133
|
+
(schema.subscription.nil? || schema.subscription.name == 'Subscription')
|
134
|
+
return
|
113
135
|
end
|
136
|
+
|
137
|
+
operations = [:query, :mutation, :subscription].map do |operation_type|
|
138
|
+
object_type = schema.public_send(operation_type)
|
139
|
+
# Special treatment for the introspection schema, which prints `{ query: "Root" }`
|
140
|
+
if object_type && (warden.get_type(object_type.name) || (object_type.name == "Root" && schema.query == object_type))
|
141
|
+
" #{operation_type}: #{object_type.name}\n"
|
142
|
+
else
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
end.compact.join
|
146
|
+
"schema {\n#{operations}}"
|
147
|
+
end
|
148
|
+
|
149
|
+
def print_directive(directive)
|
150
|
+
TypeKindPrinters::DirectivePrinter.print(warden, directive)
|
151
|
+
end
|
152
|
+
|
153
|
+
module TypeKindPrinters
|
154
|
+
module DeprecatedPrinter
|
155
|
+
def print_deprecated(field_or_enum_value)
|
156
|
+
return unless field_or_enum_value.deprecation_reason
|
157
|
+
|
158
|
+
case field_or_enum_value.deprecation_reason
|
159
|
+
when nil
|
160
|
+
''
|
161
|
+
when '', GraphQL::Directive::DEFAULT_DEPRECATION_REASON
|
162
|
+
' @deprecated'
|
163
|
+
else
|
164
|
+
" @deprecated(reason: #{field_or_enum_value.deprecation_reason.to_s.inspect})"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
module DescriptionPrinter
|
170
|
+
def print_description(definition, indentation='', first_in_block=true)
|
171
|
+
return '' unless definition.description
|
172
|
+
|
173
|
+
description = indentation != '' && !first_in_block ? "\n".dup : "".dup
|
174
|
+
description << GraphQL::Language::Comments.commentize(definition.description, indent: indentation)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
module ArgsPrinter
|
179
|
+
include DescriptionPrinter
|
180
|
+
def print_args(warden, field, indentation = '')
|
181
|
+
arguments = warden.arguments(field)
|
182
|
+
return if arguments.empty?
|
183
|
+
|
184
|
+
if arguments.all?{ |arg| !arg.description }
|
185
|
+
return "(#{arguments.map{ |arg| print_input_value(arg) }.join(", ")})"
|
186
|
+
end
|
187
|
+
|
188
|
+
out = "(\n".dup
|
189
|
+
out << arguments.sort_by(&:name).map.with_index{ |arg, i|
|
190
|
+
"#{print_description(arg, " #{indentation}", i == 0)} #{indentation}"\
|
191
|
+
"#{print_input_value(arg)}"
|
192
|
+
}.join("\n")
|
193
|
+
out << "\n#{indentation})"
|
194
|
+
end
|
195
|
+
|
196
|
+
def print_input_value(arg)
|
197
|
+
if arg.default_value?
|
198
|
+
default_string = " = #{print_value(arg.default_value, arg.type)}"
|
199
|
+
else
|
200
|
+
default_string = nil
|
201
|
+
end
|
202
|
+
|
203
|
+
"#{arg.name}: #{arg.type.to_s}#{default_string}"
|
204
|
+
end
|
205
|
+
|
206
|
+
def print_value(value, type)
|
207
|
+
case type
|
208
|
+
when FLOAT_TYPE
|
209
|
+
return 'null' if value.nil?
|
210
|
+
value.to_f.inspect
|
211
|
+
when INT_TYPE
|
212
|
+
return 'null' if value.nil?
|
213
|
+
value.to_i.inspect
|
214
|
+
when BOOLEAN_TYPE
|
215
|
+
return 'null' if value.nil?
|
216
|
+
(!!value).inspect
|
217
|
+
when ScalarType, ID_TYPE, STRING_TYPE
|
218
|
+
return 'null' if value.nil?
|
219
|
+
value.to_s.inspect
|
220
|
+
when EnumType
|
221
|
+
return 'null' if value.nil?
|
222
|
+
type.coerce_isolated_result(value)
|
223
|
+
when InputObjectType
|
224
|
+
return 'null' if value.nil?
|
225
|
+
fields = value.to_h.map{ |field_name, field_value|
|
226
|
+
field_type = type.input_fields.fetch(field_name.to_s).type
|
227
|
+
"#{field_name}: #{print_value(field_value, field_type)}"
|
228
|
+
}.join(", ")
|
229
|
+
"{#{fields}}"
|
230
|
+
when NonNullType
|
231
|
+
print_value(value, type.of_type)
|
232
|
+
when ListType
|
233
|
+
return 'null' if value.nil?
|
234
|
+
"[#{value.to_a.map{ |v| print_value(v, type.of_type) }.join(", ")}]"
|
235
|
+
else
|
236
|
+
raise NotImplementedError, "Unexpected value type #{type.inspect}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
module FieldPrinter
|
242
|
+
include DeprecatedPrinter
|
243
|
+
include ArgsPrinter
|
244
|
+
include DescriptionPrinter
|
245
|
+
def print_fields(warden, type)
|
246
|
+
fields = warden.fields(type)
|
247
|
+
fields.sort_by(&:name).map.with_index { |field, i|
|
248
|
+
"#{print_description(field, ' ', i == 0)}"\
|
249
|
+
" #{field.name}#{print_args(warden, field, ' ')}: #{field.type}#{print_deprecated(field)}"
|
250
|
+
}.join("\n")
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class DirectivePrinter
|
255
|
+
extend ArgsPrinter
|
256
|
+
extend DescriptionPrinter
|
257
|
+
def self.print(warden, directive)
|
258
|
+
"#{print_description(directive)}"\
|
259
|
+
"directive @#{directive.name}#{print_args(warden, directive)} "\
|
260
|
+
"on #{directive.locations.join(' | ')}"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class ScalarPrinter
|
265
|
+
extend DescriptionPrinter
|
266
|
+
def self.print(warden, type)
|
267
|
+
"#{print_description(type)}"\
|
268
|
+
"scalar #{type.name}"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
class ObjectPrinter
|
273
|
+
extend FieldPrinter
|
274
|
+
extend DescriptionPrinter
|
275
|
+
def self.print(warden, type)
|
276
|
+
interfaces = warden.interfaces(type)
|
277
|
+
if interfaces.any?
|
278
|
+
implementations = " implements #{interfaces.sort_by(&:name).map(&:to_s).join(", ")}"
|
279
|
+
else
|
280
|
+
implementations = nil
|
281
|
+
end
|
282
|
+
|
283
|
+
"#{print_description(type)}"\
|
284
|
+
"type #{type.name}#{implementations} {\n"\
|
285
|
+
"#{print_fields(warden, type)}\n"\
|
286
|
+
"}"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
class InterfacePrinter
|
291
|
+
extend FieldPrinter
|
292
|
+
extend DescriptionPrinter
|
293
|
+
def self.print(warden, type)
|
294
|
+
"#{print_description(type)}"\
|
295
|
+
"interface #{type.name} {\n#{print_fields(warden, type)}\n}"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class UnionPrinter
|
300
|
+
extend DescriptionPrinter
|
301
|
+
def self.print(warden, type)
|
302
|
+
possible_types = warden.possible_types(type)
|
303
|
+
"#{print_description(type)}"\
|
304
|
+
"union #{type.name} = #{possible_types.sort_by(&:name).map(&:to_s).join(" | ")}"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
class EnumPrinter
|
309
|
+
extend DeprecatedPrinter
|
310
|
+
extend DescriptionPrinter
|
311
|
+
def self.print(warden, type)
|
312
|
+
enum_values = warden.enum_values(type)
|
313
|
+
|
314
|
+
values = enum_values.sort_by(&:name).map.with_index { |v, i|
|
315
|
+
"#{print_description(v, ' ', i == 0)}"\
|
316
|
+
" #{v.name}#{print_deprecated(v)}"
|
317
|
+
}.join("\n")
|
318
|
+
|
319
|
+
"#{print_description(type)}"\
|
320
|
+
"enum #{type.name} {\n#{values}\n}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
class InputObjectPrinter
|
325
|
+
extend FieldPrinter
|
326
|
+
extend DescriptionPrinter
|
327
|
+
def self.print(warden, type)
|
328
|
+
arguments = warden.arguments(type)
|
329
|
+
fields = arguments.sort_by(&:name).map.with_index{ |field, i|
|
330
|
+
"#{print_description(field, " ", i == 0)}"\
|
331
|
+
" #{print_input_value(field)}"
|
332
|
+
}.join("\n")
|
333
|
+
"#{print_description(type)}"\
|
334
|
+
"input #{type.name} {\n#{fields}\n}"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
STRATEGIES = {
|
339
|
+
GraphQL::TypeKinds::SCALAR => ScalarPrinter,
|
340
|
+
GraphQL::TypeKinds::OBJECT => ObjectPrinter,
|
341
|
+
GraphQL::TypeKinds::INTERFACE => InterfacePrinter,
|
342
|
+
GraphQL::TypeKinds::UNION => UnionPrinter,
|
343
|
+
GraphQL::TypeKinds::ENUM => EnumPrinter,
|
344
|
+
GraphQL::TypeKinds::INPUT_OBJECT => InputObjectPrinter,
|
345
|
+
}
|
114
346
|
end
|
347
|
+
private_constant :TypeKindPrinters
|
115
348
|
end
|
116
349
|
end
|
117
350
|
end
|