graphql 1.13.25 → 2.0.0
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast/query_complexity.rb +1 -1
- data/lib/graphql/analysis/ast/query_depth.rb +0 -1
- data/lib/graphql/analysis/ast/visitor.rb +1 -1
- data/lib/graphql/analysis/ast.rb +0 -10
- data/lib/graphql/analysis.rb +0 -7
- data/lib/graphql/backtrace/table.rb +0 -18
- data/lib/graphql/backtrace/tracer.rb +1 -2
- data/lib/graphql/backtrace.rb +2 -8
- data/lib/graphql/dig.rb +1 -1
- data/lib/graphql/execution/errors.rb +1 -9
- data/lib/graphql/execution/interpreter/runtime.rb +6 -13
- data/lib/graphql/execution/interpreter.rb +0 -22
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/lookahead.rb +6 -13
- data/lib/graphql/execution/multiplex.rb +50 -107
- data/lib/graphql/execution.rb +11 -3
- data/lib/graphql/introspection/directive_type.rb +2 -2
- data/lib/graphql/introspection/dynamic_fields.rb +3 -8
- data/lib/graphql/introspection/entry_points.rb +2 -15
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language/document_from_schema_definition.rb +0 -17
- data/lib/graphql/language/nodes.rb +0 -3
- data/lib/graphql/pagination/connections.rb +2 -28
- data/lib/graphql/pagination/relation_connection.rb +0 -2
- data/lib/graphql/query/context.rb +1 -185
- data/lib/graphql/query/input_validation_result.rb +0 -9
- data/lib/graphql/query/literal_input.rb +8 -13
- data/lib/graphql/query/validation_pipeline.rb +6 -34
- data/lib/graphql/query/variable_validation_error.rb +2 -2
- data/lib/graphql/query/variables.rb +8 -31
- data/lib/graphql/query.rb +5 -34
- data/lib/graphql/railtie.rb +0 -104
- data/lib/graphql/relay/range_add.rb +0 -4
- data/lib/graphql/relay.rb +0 -15
- data/lib/graphql/schema/addition.rb +1 -8
- data/lib/graphql/schema/argument.rb +6 -28
- data/lib/graphql/schema/build_from_definition.rb +7 -9
- data/lib/graphql/schema/directive.rb +1 -22
- data/lib/graphql/schema/enum.rb +3 -19
- data/lib/graphql/schema/enum_value.rb +1 -23
- data/lib/graphql/schema/field.rb +22 -221
- data/lib/graphql/schema/input_object.rb +17 -65
- data/lib/graphql/schema/interface.rb +1 -30
- data/lib/graphql/schema/introspection_system.rb +3 -8
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/list.rb +3 -24
- data/lib/graphql/schema/loader.rb +0 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -6
- data/lib/graphql/schema/member/build_type.rb +4 -6
- data/lib/graphql/schema/member/has_arguments.rb +16 -20
- data/lib/graphql/schema/member/has_fields.rb +3 -3
- data/lib/graphql/schema/member/has_interfaces.rb +1 -13
- data/lib/graphql/schema/member/validates_input.rb +2 -2
- data/lib/graphql/schema/member.rb +0 -6
- data/lib/graphql/schema/mutation.rb +0 -9
- data/lib/graphql/schema/non_null.rb +3 -9
- data/lib/graphql/schema/object.rb +0 -40
- data/lib/graphql/schema/relay_classic_mutation.rb +17 -28
- data/lib/graphql/schema/scalar.rb +1 -16
- data/lib/graphql/schema/union.rb +0 -16
- data/lib/graphql/schema/warden.rb +3 -12
- data/lib/graphql/schema/wrapper.rb +0 -5
- data/lib/graphql/schema.rb +106 -945
- data/lib/graphql/static_validation/base_visitor.rb +4 -21
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +12 -12
- data/lib/graphql/static_validation/validator.rb +2 -24
- data/lib/graphql/static_validation.rb +0 -2
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +38 -1
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions/instrumentation.rb +0 -51
- data/lib/graphql/subscriptions.rb +4 -13
- data/lib/graphql/tracing/data_dog_tracing.rb +16 -20
- data/lib/graphql/tracing/platform_tracing.rb +4 -32
- data/lib/graphql/tracing.rb +0 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +2 -6
- data/lib/graphql/types/relay/default_relay.rb +0 -10
- data/lib/graphql/types/relay/node_behaviors.rb +5 -1
- data/lib/graphql/types/relay.rb +0 -2
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -66
- metadata +28 -164
- data/lib/graphql/analysis/analyze_query.rb +0 -98
- data/lib/graphql/analysis/field_usage.rb +0 -45
- data/lib/graphql/analysis/max_query_complexity.rb +0 -26
- data/lib/graphql/analysis/max_query_depth.rb +0 -26
- data/lib/graphql/analysis/query_complexity.rb +0 -88
- data/lib/graphql/analysis/query_depth.rb +0 -43
- data/lib/graphql/analysis/reducer_state.rb +0 -48
- data/lib/graphql/argument.rb +0 -131
- data/lib/graphql/authorization.rb +0 -82
- data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
- data/lib/graphql/backwards_compatibility.rb +0 -61
- data/lib/graphql/base_type.rb +0 -232
- data/lib/graphql/boolean_type.rb +0 -2
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
- data/lib/graphql/compatibility/execution_specification.rb +0 -436
- data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
- data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
- data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
- data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
- data/lib/graphql/compatibility.rb +0 -5
- data/lib/graphql/define/assign_argument.rb +0 -12
- data/lib/graphql/define/assign_connection.rb +0 -13
- data/lib/graphql/define/assign_enum_value.rb +0 -18
- data/lib/graphql/define/assign_global_id_field.rb +0 -11
- data/lib/graphql/define/assign_mutation_function.rb +0 -34
- data/lib/graphql/define/assign_object_field.rb +0 -42
- data/lib/graphql/define/defined_object_proxy.rb +0 -53
- data/lib/graphql/define/instance_definable.rb +0 -255
- data/lib/graphql/define/no_definition_error.rb +0 -7
- data/lib/graphql/define/non_null_with_bang.rb +0 -16
- data/lib/graphql/define/type_definer.rb +0 -31
- data/lib/graphql/define.rb +0 -31
- data/lib/graphql/deprecated_dsl.rb +0 -55
- data/lib/graphql/directive/deprecated_directive.rb +0 -2
- data/lib/graphql/directive/include_directive.rb +0 -2
- data/lib/graphql/directive/skip_directive.rb +0 -2
- data/lib/graphql/directive.rb +0 -107
- data/lib/graphql/enum_type.rb +0 -133
- data/lib/graphql/execution/execute.rb +0 -333
- data/lib/graphql/execution/flatten.rb +0 -40
- data/lib/graphql/execution/typecast.rb +0 -50
- data/lib/graphql/field/resolve.rb +0 -59
- data/lib/graphql/field.rb +0 -226
- data/lib/graphql/float_type.rb +0 -2
- data/lib/graphql/function.rb +0 -128
- data/lib/graphql/id_type.rb +0 -2
- data/lib/graphql/input_object_type.rb +0 -138
- data/lib/graphql/int_type.rb +0 -2
- data/lib/graphql/interface_type.rb +0 -72
- data/lib/graphql/internal_representation/document.rb +0 -27
- data/lib/graphql/internal_representation/node.rb +0 -206
- data/lib/graphql/internal_representation/print.rb +0 -51
- data/lib/graphql/internal_representation/rewrite.rb +0 -184
- data/lib/graphql/internal_representation/scope.rb +0 -88
- data/lib/graphql/internal_representation/visit.rb +0 -36
- data/lib/graphql/internal_representation.rb +0 -7
- data/lib/graphql/list_type.rb +0 -80
- data/lib/graphql/non_null_type.rb +0 -71
- data/lib/graphql/object_type.rb +0 -130
- data/lib/graphql/query/arguments.rb +0 -189
- data/lib/graphql/query/arguments_cache.rb +0 -24
- data/lib/graphql/query/executor.rb +0 -52
- data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
- data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
- data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
- data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
- data/lib/graphql/query/serial_execution.rb +0 -40
- data/lib/graphql/relay/array_connection.rb +0 -83
- data/lib/graphql/relay/base_connection.rb +0 -189
- data/lib/graphql/relay/connection_instrumentation.rb +0 -54
- data/lib/graphql/relay/connection_resolve.rb +0 -43
- data/lib/graphql/relay/connection_type.rb +0 -54
- data/lib/graphql/relay/edge.rb +0 -27
- data/lib/graphql/relay/edge_type.rb +0 -19
- data/lib/graphql/relay/edges_instrumentation.rb +0 -39
- data/lib/graphql/relay/global_id_resolve.rb +0 -17
- data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
- data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
- data/lib/graphql/relay/mutation/resolve.rb +0 -56
- data/lib/graphql/relay/mutation/result.rb +0 -38
- data/lib/graphql/relay/mutation.rb +0 -106
- data/lib/graphql/relay/node.rb +0 -39
- data/lib/graphql/relay/page_info.rb +0 -7
- data/lib/graphql/relay/relation_connection.rb +0 -188
- data/lib/graphql/relay/type_extensions.rb +0 -32
- data/lib/graphql/scalar_type.rb +0 -91
- data/lib/graphql/schema/catchall_middleware.rb +0 -35
- data/lib/graphql/schema/default_parse_error.rb +0 -10
- data/lib/graphql/schema/default_type_error.rb +0 -17
- data/lib/graphql/schema/member/accepts_definition.rb +0 -164
- data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -58
- data/lib/graphql/schema/member/instrumentation.rb +0 -131
- data/lib/graphql/schema/middleware_chain.rb +0 -82
- data/lib/graphql/schema/possible_types.rb +0 -44
- data/lib/graphql/schema/rescue_middleware.rb +0 -60
- data/lib/graphql/schema/timeout_middleware.rb +0 -88
- data/lib/graphql/schema/traversal.rb +0 -228
- data/lib/graphql/schema/validation.rb +0 -313
- data/lib/graphql/static_validation/default_visitor.rb +0 -15
- data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
- data/lib/graphql/string_type.rb +0 -2
- data/lib/graphql/subscriptions/subscription_root.rb +0 -76
- data/lib/graphql/tracing/skylight_tracing.rb +0 -70
- data/lib/graphql/types/relay/node_field.rb +0 -24
- data/lib/graphql/types/relay/nodes_field.rb +0 -43
- data/lib/graphql/union_type.rb +0 -115
- data/lib/graphql/upgrader/member.rb +0 -937
- data/lib/graphql/upgrader/schema.rb +0 -38
@@ -1,228 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
class Schema
|
4
|
-
# Visit the members of this schema and build up artifacts for runtime.
|
5
|
-
# @api private
|
6
|
-
class Traversal
|
7
|
-
# @return [Hash<String => GraphQL::BaseType]
|
8
|
-
attr_reader :type_map
|
9
|
-
|
10
|
-
# @return [Hash<String => Hash<String => GraphQL::Field>>]
|
11
|
-
attr_reader :instrumented_field_map
|
12
|
-
|
13
|
-
# @return [Hash<String => Array<GraphQL::Field || GraphQL::Argument || GraphQL::Directive>]
|
14
|
-
attr_reader :type_reference_map
|
15
|
-
|
16
|
-
# @return [Hash<String => Array<GraphQL::BaseType>]
|
17
|
-
attr_reader :union_memberships
|
18
|
-
|
19
|
-
|
20
|
-
# @param schema [GraphQL::Schema]
|
21
|
-
def initialize(schema, introspection: true)
|
22
|
-
@schema = schema
|
23
|
-
@introspection = introspection
|
24
|
-
built_in_insts = [
|
25
|
-
GraphQL::Relay::ConnectionInstrumentation,
|
26
|
-
GraphQL::Relay::EdgesInstrumentation,
|
27
|
-
GraphQL::Relay::Mutation::Instrumentation,
|
28
|
-
]
|
29
|
-
|
30
|
-
if schema.query_execution_strategy != GraphQL::Execution::Interpreter
|
31
|
-
built_in_insts << GraphQL::Schema::Member::Instrumentation
|
32
|
-
end
|
33
|
-
|
34
|
-
@field_instrumenters =
|
35
|
-
schema.instrumenters[:field] +
|
36
|
-
built_in_insts +
|
37
|
-
schema.instrumenters[:field_after_built_ins]
|
38
|
-
|
39
|
-
# These fields have types specified by _name_,
|
40
|
-
# So we need to inspect the schema and find those types,
|
41
|
-
# then update their references.
|
42
|
-
@late_bound_fields = []
|
43
|
-
@type_map = {}
|
44
|
-
@instrumented_field_map = Hash.new { |h, k| h[k] = {} }
|
45
|
-
@type_reference_map = Hash.new { |h, k| h[k] = [] }
|
46
|
-
@union_memberships = Hash.new { |h, k| h[k] = [] }
|
47
|
-
visit(schema, schema, nil)
|
48
|
-
resolve_late_bound_fields
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
# A brute-force appraoch to late binding.
|
54
|
-
# Just keep trying the whole list, hoping that they
|
55
|
-
# eventually all resolve.
|
56
|
-
# This could be replaced with proper dependency tracking.
|
57
|
-
def resolve_late_bound_fields
|
58
|
-
# This is a bit tricky, with the writes going to internal state.
|
59
|
-
prev_late_bound_fields = @late_bound_fields
|
60
|
-
# Things might get added here during `visit...`
|
61
|
-
# or they might be added manually if we can't find them by hand
|
62
|
-
@late_bound_fields = []
|
63
|
-
prev_late_bound_fields.each do |(owner_type, field_defn, dynamic_field)|
|
64
|
-
if @type_map.key?(field_defn.type.unwrap.name)
|
65
|
-
late_bound_return_type = field_defn.type
|
66
|
-
resolved_type = @type_map.fetch(late_bound_return_type.unwrap.name)
|
67
|
-
wrapped_resolved_type = rewrap_resolved_type(late_bound_return_type, resolved_type)
|
68
|
-
# Update the field definition in place? :thinking_face:
|
69
|
-
field_defn.type = wrapped_resolved_type
|
70
|
-
visit_field_on_type(@schema, owner_type, field_defn, dynamic_field: dynamic_field)
|
71
|
-
else
|
72
|
-
@late_bound_fields << [owner_type, field_defn, dynamic_field]
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
if @late_bound_fields.any?
|
77
|
-
# If we visited each field and failed to resolve _any_,
|
78
|
-
# then we're stuck.
|
79
|
-
if @late_bound_fields == prev_late_bound_fields
|
80
|
-
type_names = prev_late_bound_fields.map { |f| f[1] }.map(&:type).map(&:unwrap).map(&:name).uniq
|
81
|
-
raise <<-ERR
|
82
|
-
Some late-bound types couldn't be resolved:
|
83
|
-
|
84
|
-
- #{type_names}
|
85
|
-
- Found __* types: #{@type_map.keys.select { |k| k.start_with?("__") }}
|
86
|
-
ERR
|
87
|
-
else
|
88
|
-
resolve_late_bound_fields
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# The late-bound type may be wrapped with list or non-null types.
|
94
|
-
# Apply the same wrapping to the resolve type and
|
95
|
-
# return the maybe-wrapped type
|
96
|
-
def rewrap_resolved_type(late_bound_type, resolved_inner_type)
|
97
|
-
case late_bound_type
|
98
|
-
when GraphQL::NonNullType
|
99
|
-
rewrap_resolved_type(late_bound_type.of_type, resolved_inner_type).to_non_null_type
|
100
|
-
when GraphQL::ListType
|
101
|
-
rewrap_resolved_type(late_bound_type.of_type, resolved_inner_type).to_list_type
|
102
|
-
when GraphQL::Schema::LateBoundType
|
103
|
-
resolved_inner_type
|
104
|
-
else
|
105
|
-
raise "Unexpected late_bound_type: #{late_bound_type.inspect} (#{late_bound_type.class})"
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def visit(schema, member, context_description)
|
110
|
-
case member
|
111
|
-
when GraphQL::Schema
|
112
|
-
member.directives.each { |name, directive| visit(schema, directive, "Directive #{name}") }
|
113
|
-
# Find the starting points, then visit them
|
114
|
-
visit_roots = [member.query, member.mutation, member.subscription]
|
115
|
-
if @introspection
|
116
|
-
introspection_types = schema.introspection_system.types.values
|
117
|
-
visit_roots.concat(introspection_types)
|
118
|
-
if member.query
|
119
|
-
member.introspection_system.entry_points.each do |introspection_field|
|
120
|
-
# Visit this so that arguments class is preconstructed
|
121
|
-
# Skip validation since it begins with "__"
|
122
|
-
visit_field_on_type(schema, member.query, introspection_field, dynamic_field: true)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
visit_roots.concat(member.orphan_types)
|
127
|
-
visit_roots.compact!
|
128
|
-
visit_roots.each { |t| visit(schema, t, t.name) }
|
129
|
-
when GraphQL::Directive
|
130
|
-
member.arguments.each do |name, argument|
|
131
|
-
@type_reference_map[argument.type.unwrap.to_s] << argument
|
132
|
-
visit(schema, argument.type, "Directive argument #{member.name}.#{name}")
|
133
|
-
end
|
134
|
-
# Construct arguments class here, which is later used to generate GraphQL::Query::Arguments
|
135
|
-
# to be passed to a resolver proc
|
136
|
-
GraphQL::Query::Arguments.construct_arguments_class(member)
|
137
|
-
when GraphQL::BaseType
|
138
|
-
type_defn = member.unwrap
|
139
|
-
prev_type = @type_map[type_defn.name]
|
140
|
-
# Continue to visit this type if it's the first time we've seen it:
|
141
|
-
if prev_type.nil?
|
142
|
-
validate_type(type_defn, context_description)
|
143
|
-
@type_map[type_defn.name] = type_defn
|
144
|
-
case type_defn
|
145
|
-
when GraphQL::ObjectType
|
146
|
-
type_defn.interfaces.each { |i| visit(schema, i, "Interface on #{type_defn.name}") }
|
147
|
-
visit_fields(schema, type_defn)
|
148
|
-
when GraphQL::InterfaceType
|
149
|
-
visit_fields(schema, type_defn)
|
150
|
-
type_defn.orphan_types.each do |t|
|
151
|
-
visit(schema, t, "Orphan type for #{type_defn.name}")
|
152
|
-
end
|
153
|
-
when GraphQL::UnionType
|
154
|
-
type_defn.possible_types.each do |t|
|
155
|
-
@union_memberships[t.name] << type_defn
|
156
|
-
visit(schema, t, "Possible type for #{type_defn.name}")
|
157
|
-
end
|
158
|
-
when GraphQL::InputObjectType
|
159
|
-
type_defn.arguments.each do |name, arg|
|
160
|
-
@type_reference_map[arg.type.unwrap.to_s] << arg
|
161
|
-
visit(schema, arg.type, "Input field #{type_defn.name}.#{name}")
|
162
|
-
end
|
163
|
-
|
164
|
-
# Construct arguments class here, which is later used to generate GraphQL::Query::Arguments
|
165
|
-
# to be passed to a resolver proc
|
166
|
-
if type_defn.arguments_class.nil?
|
167
|
-
GraphQL::Query::Arguments.construct_arguments_class(type_defn)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
elsif !prev_type.equal?(type_defn)
|
171
|
-
# If the previous entry in the map isn't the same object we just found, raise.
|
172
|
-
raise("Duplicate type definition found for name '#{type_defn.name}' at '#{context_description}' (#{prev_type.metadata[:type_class] || prev_type}, #{type_defn.metadata[:type_class] || type_defn})")
|
173
|
-
end
|
174
|
-
when Class
|
175
|
-
if member.respond_to?(:graphql_definition)
|
176
|
-
graphql_member = member.graphql_definition(silence_deprecation_warning: true)
|
177
|
-
visit(schema, graphql_member, context_description)
|
178
|
-
else
|
179
|
-
raise GraphQL::Schema::InvalidTypeError.new("Unexpected traversal member: #{member} (#{member.class.name})")
|
180
|
-
end
|
181
|
-
else
|
182
|
-
message = "Unexpected schema traversal member: #{member} (#{member.class.name})"
|
183
|
-
raise GraphQL::Schema::InvalidTypeError.new(message)
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
def visit_fields(schema, type_defn)
|
188
|
-
type_defn.all_fields.each do |field_defn|
|
189
|
-
visit_field_on_type(schema, type_defn, field_defn)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def visit_field_on_type(schema, type_defn, field_defn, dynamic_field: false)
|
194
|
-
base_return_type = field_defn.type.unwrap
|
195
|
-
if base_return_type.is_a?(GraphQL::Schema::LateBoundType)
|
196
|
-
@late_bound_fields << [type_defn, field_defn, dynamic_field]
|
197
|
-
return
|
198
|
-
end
|
199
|
-
if dynamic_field
|
200
|
-
# Don't apply instrumentation to dynamic fields since they're shared constants
|
201
|
-
instrumented_field_defn = field_defn
|
202
|
-
else
|
203
|
-
instrumented_field_defn = @field_instrumenters.reduce(field_defn) do |defn, inst|
|
204
|
-
inst.instrument(type_defn, defn)
|
205
|
-
end
|
206
|
-
@instrumented_field_map[type_defn.name][instrumented_field_defn.name] = instrumented_field_defn
|
207
|
-
end
|
208
|
-
@type_reference_map[instrumented_field_defn.type.unwrap.name] << instrumented_field_defn
|
209
|
-
visit(schema, instrumented_field_defn.type, "Field #{type_defn.name}.#{instrumented_field_defn.name}'s return type")
|
210
|
-
instrumented_field_defn.arguments.each do |name, arg|
|
211
|
-
@type_reference_map[arg.type.unwrap.to_s] << arg
|
212
|
-
visit(schema, arg.type, "Argument #{name} on #{type_defn.name}.#{instrumented_field_defn.name}")
|
213
|
-
end
|
214
|
-
|
215
|
-
# Construct arguments class here, which is later used to generate GraphQL::Query::Arguments
|
216
|
-
# to be passed to a resolver proc
|
217
|
-
GraphQL::Query::Arguments.construct_arguments_class(instrumented_field_defn)
|
218
|
-
end
|
219
|
-
|
220
|
-
def validate_type(member, context_description)
|
221
|
-
error_message = GraphQL::Schema::Validation.validate(member)
|
222
|
-
if error_message
|
223
|
-
raise GraphQL::Schema::InvalidTypeError.new("#{context_description} is invalid: #{error_message}")
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
@@ -1,313 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
class Schema
|
4
|
-
# This module provides a function for validating GraphQL types.
|
5
|
-
#
|
6
|
-
# Its {RULES} contain objects that respond to `#call(type)`. Rules are
|
7
|
-
# looked up for given types (by class ancestry), then applied to
|
8
|
-
# the object until an error is returned.
|
9
|
-
#
|
10
|
-
# Remove this in GraphQL-Ruby 2.0 when schema instances are removed.
|
11
|
-
class Validation
|
12
|
-
# Lookup the rules for `object` based on its class,
|
13
|
-
# Then returns an error message or `nil`
|
14
|
-
# @param object [Object] something to be validated
|
15
|
-
# @return [String, Nil] error message, if there was one
|
16
|
-
def self.validate(object)
|
17
|
-
RULES.each do |parent_class, validations|
|
18
|
-
if object.is_a?(parent_class)
|
19
|
-
validations.each do |rule|
|
20
|
-
if error = rule.call(object)
|
21
|
-
return error
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
nil
|
27
|
-
end
|
28
|
-
|
29
|
-
module Rules
|
30
|
-
# @param property_name [Symbol] The method to validate
|
31
|
-
# @param allowed_classes [Class] Classes which the return value may be an instance of
|
32
|
-
# @return [Proc] A proc which will validate the input by calling `property_name` and asserting it is an instance of one of `allowed_classes`
|
33
|
-
def self.assert_property(property_name, *allowed_classes)
|
34
|
-
# Hide LateBoundType from user-facing errors
|
35
|
-
allowed_classes_message = allowed_classes.map(&:name).reject {|n| n.include?("LateBoundType") }.join(" or ")
|
36
|
-
->(obj) {
|
37
|
-
property_value = obj.public_send(property_name)
|
38
|
-
is_valid_value = allowed_classes.any? { |allowed_class| property_value.is_a?(allowed_class) }
|
39
|
-
is_valid_value ? nil : "#{property_name} must return #{allowed_classes_message}, not #{property_value.class.name} (#{property_value.inspect})"
|
40
|
-
}
|
41
|
-
end
|
42
|
-
|
43
|
-
# @param property_name [Symbol] The method whose return value will be validated
|
44
|
-
# @param from_class [Class] The class for keys in the return value
|
45
|
-
# @param to_class [Class] The class for values in the return value
|
46
|
-
# @return [Proc] A proc to validate that validates the input by calling `property_name` and asserting that the return value is a Hash of `{from_class => to_class}` pairs
|
47
|
-
def self.assert_property_mapping(property_name, from_class, to_class)
|
48
|
-
->(obj) {
|
49
|
-
property_value = obj.public_send(property_name)
|
50
|
-
if !property_value.is_a?(Hash)
|
51
|
-
"#{property_name} must be a hash of {#{from_class.name} => #{to_class.name}}, not a #{property_value.class.name} (#{property_value.inspect})"
|
52
|
-
else
|
53
|
-
invalid_key, invalid_value = property_value.find { |key, value| !key.is_a?(from_class) || !value.is_a?(to_class) }
|
54
|
-
if invalid_key
|
55
|
-
"#{property_name} must map #{from_class} => #{to_class}, not #{invalid_key.class.name} => #{invalid_value.class.name} (#{invalid_key.inspect} => #{invalid_value.inspect})"
|
56
|
-
else
|
57
|
-
nil # OK
|
58
|
-
end
|
59
|
-
end
|
60
|
-
}
|
61
|
-
end
|
62
|
-
|
63
|
-
# @param property_name [Symbol] The method whose return value will be validated
|
64
|
-
# @param list_member_class [Class] The class which each member of the returned array should be an instance of
|
65
|
-
# @return [Proc] A proc to validate the input by calling `property_name` and asserting that the return is an Array of `list_member_class` instances
|
66
|
-
def self.assert_property_list_of(property_name, list_member_class)
|
67
|
-
->(obj) {
|
68
|
-
property_value = obj.public_send(property_name)
|
69
|
-
if !property_value.is_a?(Array)
|
70
|
-
"#{property_name} must be an Array of #{list_member_class.name}, not a #{property_value.class.name} (#{property_value.inspect})"
|
71
|
-
else
|
72
|
-
invalid_member = property_value.find { |value| !value.is_a?(list_member_class) }
|
73
|
-
if invalid_member
|
74
|
-
"#{property_name} must contain #{list_member_class.name}, not #{invalid_member.class.name} (#{invalid_member.inspect})"
|
75
|
-
else
|
76
|
-
nil # OK
|
77
|
-
end
|
78
|
-
end
|
79
|
-
}
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.count_at_least(item_name, minimum_count, get_items_proc)
|
83
|
-
->(type) {
|
84
|
-
items = get_items_proc.call(type)
|
85
|
-
|
86
|
-
if items.size < minimum_count
|
87
|
-
"#{type.name} must define at least #{minimum_count} #{item_name}. #{items.size} defined."
|
88
|
-
else
|
89
|
-
nil
|
90
|
-
end
|
91
|
-
}
|
92
|
-
end
|
93
|
-
|
94
|
-
def self.assert_named_items_are_valid(item_name, get_items_proc)
|
95
|
-
->(type) {
|
96
|
-
items = get_items_proc.call(type)
|
97
|
-
error_message = nil
|
98
|
-
items.each do |item|
|
99
|
-
item_message = GraphQL::Schema::Validation.validate(item)
|
100
|
-
if item_message
|
101
|
-
error_message = "#{item_name} #{item.name.inspect} #{item_message}"
|
102
|
-
break
|
103
|
-
end
|
104
|
-
end
|
105
|
-
error_message
|
106
|
-
}
|
107
|
-
end
|
108
|
-
|
109
|
-
HAS_AT_LEAST_ONE_FIELD = Rules.count_at_least("field", 1, ->(type) { type.all_fields })
|
110
|
-
FIELDS_ARE_VALID = Rules.assert_named_items_are_valid("field", ->(type) { type.all_fields })
|
111
|
-
HAS_AT_LEAST_ONE_ARGUMENT = Rules.count_at_least("argument", 1, ->(type) { type.arguments })
|
112
|
-
|
113
|
-
HAS_ONE_OR_MORE_POSSIBLE_TYPES = ->(type) {
|
114
|
-
type.possible_types.length >= 1 ? nil : "must have at least one possible type"
|
115
|
-
}
|
116
|
-
|
117
|
-
NAME_IS_STRING = Rules.assert_property(:name, String)
|
118
|
-
DESCRIPTION_IS_STRING_OR_NIL = Rules.assert_property(:description, String, NilClass)
|
119
|
-
ARGUMENTS_ARE_STRING_TO_ARGUMENT = Rules.assert_property_mapping(:arguments, String, GraphQL::Argument)
|
120
|
-
ARGUMENTS_ARE_VALID = Rules.assert_named_items_are_valid("argument", ->(type) { type.arguments.values })
|
121
|
-
|
122
|
-
DEFAULT_VALUE_IS_VALID_FOR_TYPE = ->(type) {
|
123
|
-
if !type.default_value.nil?
|
124
|
-
coerced_value = begin
|
125
|
-
type.type.coerce_isolated_result(type.default_value)
|
126
|
-
rescue => ex
|
127
|
-
ex
|
128
|
-
end
|
129
|
-
|
130
|
-
if coerced_value.nil? || coerced_value.is_a?(StandardError)
|
131
|
-
msg = "default value #{type.default_value.inspect} is not valid for type #{type.type}"
|
132
|
-
msg += " (#{coerced_value})" if coerced_value.is_a?(StandardError)
|
133
|
-
msg
|
134
|
-
end
|
135
|
-
end
|
136
|
-
}
|
137
|
-
|
138
|
-
DEPRECATED_ARGUMENTS_ARE_OPTIONAL = ->(argument) {
|
139
|
-
if argument.deprecation_reason && argument.type.non_null?
|
140
|
-
"must be optional because it's deprecated"
|
141
|
-
end
|
142
|
-
}
|
143
|
-
|
144
|
-
TYPE_IS_VALID_INPUT_TYPE = ->(type) {
|
145
|
-
outer_type = type.type
|
146
|
-
inner_type = outer_type.respond_to?(:unwrap) ? outer_type.unwrap : nil
|
147
|
-
|
148
|
-
case inner_type
|
149
|
-
when GraphQL::ScalarType, GraphQL::InputObjectType, GraphQL::EnumType
|
150
|
-
# OK
|
151
|
-
else
|
152
|
-
"type must be a valid input type (Scalar or InputObject), not #{outer_type.class} (#{outer_type})"
|
153
|
-
end
|
154
|
-
}
|
155
|
-
|
156
|
-
SCHEMA_CAN_RESOLVE_TYPES = ->(schema) {
|
157
|
-
if schema.types.values.any? { |type| type.kind.abstract? } && schema.resolve_type_proc.nil?
|
158
|
-
"schema contains Interfaces or Unions, so you must define a `resolve_type -> (obj, ctx) { ... }` function"
|
159
|
-
else
|
160
|
-
# :+1:
|
161
|
-
end
|
162
|
-
}
|
163
|
-
|
164
|
-
SCHEMA_CAN_FETCH_IDS = ->(schema) {
|
165
|
-
has_node_field = schema.query && schema.query.fields.each_value.any?(&:relay_node_field)
|
166
|
-
if has_node_field && schema.object_from_id_proc.nil?
|
167
|
-
"schema contains `node(id:...)` field, so you must define a `object_from_id -> (id, ctx) { ... }` function"
|
168
|
-
else
|
169
|
-
# :rocket:
|
170
|
-
end
|
171
|
-
}
|
172
|
-
|
173
|
-
SCHEMA_CAN_GENERATE_IDS = ->(schema) {
|
174
|
-
has_id_field = schema.types.values.any? { |t| t.kind.fields? && t.all_fields.any? { |f| f.resolve_proc.is_a?(GraphQL::Relay::GlobalIdResolve) } }
|
175
|
-
if has_id_field && schema.id_from_object_proc.nil?
|
176
|
-
"schema contains `global_id_field`, so you must define a `id_from_object -> (obj, type, ctx) { ... }` function"
|
177
|
-
else
|
178
|
-
# :ok_hand:
|
179
|
-
end
|
180
|
-
}
|
181
|
-
|
182
|
-
SCHEMA_INSTRUMENTERS_ARE_VALID = ->(schema) {
|
183
|
-
errs = []
|
184
|
-
schema.instrumenters[:query].each do |inst|
|
185
|
-
if !inst.respond_to?(:before_query) || !inst.respond_to?(:after_query)
|
186
|
-
errs << "`instrument(:query, #{inst})` is invalid: must respond to `before_query(query)` and `after_query(query)` "
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
schema.instrumenters[:field].each do |inst|
|
191
|
-
if !inst.respond_to?(:instrument)
|
192
|
-
errs << "`instrument(:field, #{inst})` is invalid: must respond to `instrument(type, field)`"
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
if errs.any?
|
197
|
-
errs.join("Invalid instrumenters:\n" + errs.join("\n"))
|
198
|
-
else
|
199
|
-
nil
|
200
|
-
end
|
201
|
-
}
|
202
|
-
|
203
|
-
RESERVED_TYPE_NAME = ->(type) {
|
204
|
-
if type.name.start_with?('__') && !type.introspection?
|
205
|
-
# TODO: make this a hard failure in a later version
|
206
|
-
GraphQL::Deprecation.warn("Name #{type.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
|
207
|
-
nil
|
208
|
-
else
|
209
|
-
# ok name
|
210
|
-
end
|
211
|
-
}
|
212
|
-
|
213
|
-
RESERVED_NAME = ->(named_thing) {
|
214
|
-
if named_thing.name.start_with?('__')
|
215
|
-
# TODO: make this a hard failure in a later version
|
216
|
-
GraphQL::Deprecation.warn("Name #{named_thing.name.inspect} must not begin with \"__\", which is reserved by GraphQL introspection.")
|
217
|
-
nil
|
218
|
-
else
|
219
|
-
# no worries
|
220
|
-
end
|
221
|
-
}
|
222
|
-
|
223
|
-
INTERFACES_ARE_IMPLEMENTED = ->(obj_type) {
|
224
|
-
field_errors = []
|
225
|
-
obj_type.interfaces.each do |interface_type|
|
226
|
-
interface_type.fields.each do |field_name, field_defn|
|
227
|
-
object_field = obj_type.get_field(field_name)
|
228
|
-
if object_field.nil?
|
229
|
-
field_errors << %|"#{field_name}" is required by #{interface_type.name} but not implemented by #{obj_type.name}|
|
230
|
-
elsif !GraphQL::Execution::Typecast.subtype?(field_defn.type, object_field.type)
|
231
|
-
field_errors << %|"#{field_name}" is required by #{interface_type.name} to return #{field_defn.type} but #{obj_type.name}.#{field_name} returns #{object_field.type}|
|
232
|
-
else
|
233
|
-
field_defn.arguments.each do |arg_name, arg_defn|
|
234
|
-
object_field_arg = object_field.arguments[arg_name]
|
235
|
-
if object_field_arg.nil?
|
236
|
-
field_errors << %|"#{arg_name}" argument is required by #{interface_type.name}.#{field_name} but not accepted by #{obj_type.name}.#{field_name}|
|
237
|
-
elsif arg_defn.type != object_field_arg.type
|
238
|
-
field_errors << %|"#{arg_name}" is required by #{interface_type.name}.#{field_defn.name} to accept #{arg_defn.type} but #{obj_type.name}.#{field_name} accepts #{object_field_arg.type} for "#{arg_name}"|
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
object_field.arguments.each do |arg_name, arg_defn|
|
243
|
-
if field_defn.arguments[arg_name].nil? && arg_defn.type.is_a?(GraphQL::NonNullType)
|
244
|
-
field_errors << %|"#{arg_name}" is not accepted by #{interface_type.name}.#{field_name} but required by #{obj_type.name}.#{field_name}|
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
if field_errors.any?
|
251
|
-
"#{obj_type.name} failed to implement some interfaces: #{field_errors.join(", ")}"
|
252
|
-
else
|
253
|
-
nil
|
254
|
-
end
|
255
|
-
}
|
256
|
-
end
|
257
|
-
|
258
|
-
# A mapping of `{Class => [Proc, Proc...]}` pairs.
|
259
|
-
# To validate an instance, find entries where `object.is_a?(key)` is true.
|
260
|
-
# Then apply each rule from the matching values.
|
261
|
-
RULES = {
|
262
|
-
GraphQL::Field => [
|
263
|
-
Rules::NAME_IS_STRING,
|
264
|
-
Rules::RESERVED_NAME,
|
265
|
-
Rules::DESCRIPTION_IS_STRING_OR_NIL,
|
266
|
-
Rules.assert_property(:deprecation_reason, String, NilClass),
|
267
|
-
Rules.assert_property(:type, GraphQL::BaseType, GraphQL::Schema::LateBoundType),
|
268
|
-
Rules.assert_property(:property, Symbol, NilClass),
|
269
|
-
Rules::ARGUMENTS_ARE_STRING_TO_ARGUMENT,
|
270
|
-
Rules::ARGUMENTS_ARE_VALID,
|
271
|
-
],
|
272
|
-
GraphQL::Argument => [
|
273
|
-
Rules::NAME_IS_STRING,
|
274
|
-
Rules::RESERVED_NAME,
|
275
|
-
Rules::DESCRIPTION_IS_STRING_OR_NIL,
|
276
|
-
Rules.assert_property(:deprecation_reason, String, NilClass),
|
277
|
-
Rules::TYPE_IS_VALID_INPUT_TYPE,
|
278
|
-
Rules::DEFAULT_VALUE_IS_VALID_FOR_TYPE,
|
279
|
-
Rules::DEPRECATED_ARGUMENTS_ARE_OPTIONAL,
|
280
|
-
],
|
281
|
-
GraphQL::BaseType => [
|
282
|
-
Rules::NAME_IS_STRING,
|
283
|
-
Rules::RESERVED_TYPE_NAME,
|
284
|
-
Rules::DESCRIPTION_IS_STRING_OR_NIL,
|
285
|
-
],
|
286
|
-
GraphQL::ObjectType => [
|
287
|
-
Rules::HAS_AT_LEAST_ONE_FIELD,
|
288
|
-
Rules.assert_property_list_of(:interfaces, GraphQL::InterfaceType),
|
289
|
-
Rules::FIELDS_ARE_VALID,
|
290
|
-
Rules::INTERFACES_ARE_IMPLEMENTED,
|
291
|
-
],
|
292
|
-
GraphQL::InputObjectType => [
|
293
|
-
Rules::HAS_AT_LEAST_ONE_ARGUMENT,
|
294
|
-
Rules::ARGUMENTS_ARE_STRING_TO_ARGUMENT,
|
295
|
-
Rules::ARGUMENTS_ARE_VALID,
|
296
|
-
],
|
297
|
-
GraphQL::UnionType => [
|
298
|
-
Rules.assert_property_list_of(:possible_types, GraphQL::ObjectType),
|
299
|
-
Rules::HAS_ONE_OR_MORE_POSSIBLE_TYPES,
|
300
|
-
],
|
301
|
-
GraphQL::InterfaceType => [
|
302
|
-
Rules::FIELDS_ARE_VALID,
|
303
|
-
],
|
304
|
-
GraphQL::Schema => [
|
305
|
-
Rules::SCHEMA_INSTRUMENTERS_ARE_VALID,
|
306
|
-
Rules::SCHEMA_CAN_RESOLVE_TYPES,
|
307
|
-
Rules::SCHEMA_CAN_FETCH_IDS,
|
308
|
-
Rules::SCHEMA_CAN_GENERATE_IDS,
|
309
|
-
],
|
310
|
-
}
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module StaticValidation
|
4
|
-
class DefaultVisitor < BaseVisitor
|
5
|
-
include(GraphQL::StaticValidation::DefinitionDependencies)
|
6
|
-
|
7
|
-
StaticValidation::ALL_RULES.reverse_each do |r|
|
8
|
-
include(r)
|
9
|
-
end
|
10
|
-
|
11
|
-
include(GraphQL::InternalRepresentation::Rewrite)
|
12
|
-
include(ContextMethods)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module StaticValidation
|
4
|
-
class NoValidateVisitor < StaticValidation::BaseVisitor
|
5
|
-
include(GraphQL::InternalRepresentation::Rewrite)
|
6
|
-
include(GraphQL::StaticValidation::DefinitionDependencies)
|
7
|
-
include(ContextMethods)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
data/lib/graphql/string_type.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQL
|
4
|
-
class Subscriptions
|
5
|
-
# @api private
|
6
|
-
# @deprecated This module is no longer needed.
|
7
|
-
module SubscriptionRoot
|
8
|
-
def self.extended(child_cls)
|
9
|
-
GraphQL::Deprecation.warn "`extend GraphQL::Subscriptions::SubscriptionRoot` is no longer required; you can remove it from your Subscription type (#{child_cls})"
|
10
|
-
child_cls.include(InstanceMethods)
|
11
|
-
end
|
12
|
-
|
13
|
-
# This is for maintaining backwards compatibility:
|
14
|
-
# if a subscription field is created without a `subscription:` resolver class,
|
15
|
-
# then implement the method with the previous default behavior.
|
16
|
-
module InstanceMethods
|
17
|
-
def skip_subscription_root(*)
|
18
|
-
if context.query.subscription_update?
|
19
|
-
object
|
20
|
-
else
|
21
|
-
context.skip
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def field(*args, extensions: [], **rest, &block)
|
27
|
-
extensions += [Extension]
|
28
|
-
# Backwards-compat for schemas
|
29
|
-
if !rest[:subscription]
|
30
|
-
name = args.first
|
31
|
-
alias_method(name, :skip_subscription_root)
|
32
|
-
end
|
33
|
-
super(*args, extensions: extensions, **rest, &block)
|
34
|
-
end
|
35
|
-
|
36
|
-
class Extension < GraphQL::Schema::FieldExtension
|
37
|
-
def after_resolve(value:, context:, object:, arguments:, **rest)
|
38
|
-
if value.is_a?(GraphQL::ExecutionError)
|
39
|
-
value
|
40
|
-
elsif (events = context.namespace(:subscriptions)[:events])
|
41
|
-
# This is the first execution, so gather an Event
|
42
|
-
# for the backend to register:
|
43
|
-
event = Subscriptions::Event.new(
|
44
|
-
name: field.name,
|
45
|
-
arguments: arguments_without_field_extras(arguments: arguments),
|
46
|
-
context: context,
|
47
|
-
field: field,
|
48
|
-
)
|
49
|
-
events << event
|
50
|
-
value
|
51
|
-
elsif context.query.subscription_topic == Subscriptions::Event.serialize(
|
52
|
-
field.name,
|
53
|
-
arguments_without_field_extras(arguments: arguments),
|
54
|
-
field,
|
55
|
-
scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
|
56
|
-
)
|
57
|
-
# This is a subscription update. The resolver returned `skip` if it should be skipped,
|
58
|
-
# or else it returned an object to resolve the update.
|
59
|
-
value
|
60
|
-
else
|
61
|
-
# This is a subscription update, but this event wasn't triggered.
|
62
|
-
context.skip
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def arguments_without_field_extras(arguments:)
|
69
|
-
arguments.dup.tap do |event_args|
|
70
|
-
field.extras.each { |k| event_args.delete(k) }
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|