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.

Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/ast/query_complexity.rb +1 -1
  3. data/lib/graphql/analysis/ast/query_depth.rb +0 -1
  4. data/lib/graphql/analysis/ast/visitor.rb +1 -1
  5. data/lib/graphql/analysis/ast.rb +0 -10
  6. data/lib/graphql/analysis.rb +0 -7
  7. data/lib/graphql/backtrace/table.rb +0 -18
  8. data/lib/graphql/backtrace/tracer.rb +1 -2
  9. data/lib/graphql/backtrace.rb +2 -8
  10. data/lib/graphql/dig.rb +1 -1
  11. data/lib/graphql/execution/errors.rb +1 -9
  12. data/lib/graphql/execution/interpreter/runtime.rb +6 -13
  13. data/lib/graphql/execution/interpreter.rb +0 -22
  14. data/lib/graphql/execution/lazy.rb +1 -1
  15. data/lib/graphql/execution/lookahead.rb +6 -13
  16. data/lib/graphql/execution/multiplex.rb +50 -107
  17. data/lib/graphql/execution.rb +11 -3
  18. data/lib/graphql/introspection/directive_type.rb +2 -2
  19. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  20. data/lib/graphql/introspection/entry_points.rb +2 -15
  21. data/lib/graphql/introspection/field_type.rb +1 -1
  22. data/lib/graphql/introspection/schema_type.rb +2 -2
  23. data/lib/graphql/introspection/type_type.rb +5 -5
  24. data/lib/graphql/language/document_from_schema_definition.rb +0 -17
  25. data/lib/graphql/language/nodes.rb +0 -3
  26. data/lib/graphql/pagination/connections.rb +2 -28
  27. data/lib/graphql/pagination/relation_connection.rb +0 -2
  28. data/lib/graphql/query/context.rb +1 -185
  29. data/lib/graphql/query/input_validation_result.rb +0 -9
  30. data/lib/graphql/query/literal_input.rb +8 -13
  31. data/lib/graphql/query/validation_pipeline.rb +6 -34
  32. data/lib/graphql/query/variable_validation_error.rb +2 -2
  33. data/lib/graphql/query/variables.rb +8 -31
  34. data/lib/graphql/query.rb +5 -34
  35. data/lib/graphql/railtie.rb +0 -104
  36. data/lib/graphql/relay/range_add.rb +0 -4
  37. data/lib/graphql/relay.rb +0 -15
  38. data/lib/graphql/schema/addition.rb +1 -8
  39. data/lib/graphql/schema/argument.rb +6 -28
  40. data/lib/graphql/schema/build_from_definition.rb +7 -9
  41. data/lib/graphql/schema/directive.rb +1 -22
  42. data/lib/graphql/schema/enum.rb +3 -19
  43. data/lib/graphql/schema/enum_value.rb +1 -23
  44. data/lib/graphql/schema/field.rb +22 -221
  45. data/lib/graphql/schema/input_object.rb +17 -65
  46. data/lib/graphql/schema/interface.rb +1 -30
  47. data/lib/graphql/schema/introspection_system.rb +3 -8
  48. data/lib/graphql/schema/late_bound_type.rb +2 -2
  49. data/lib/graphql/schema/list.rb +3 -24
  50. data/lib/graphql/schema/loader.rb +0 -1
  51. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -6
  52. data/lib/graphql/schema/member/build_type.rb +4 -6
  53. data/lib/graphql/schema/member/has_arguments.rb +16 -20
  54. data/lib/graphql/schema/member/has_fields.rb +3 -3
  55. data/lib/graphql/schema/member/has_interfaces.rb +1 -13
  56. data/lib/graphql/schema/member/validates_input.rb +2 -2
  57. data/lib/graphql/schema/member.rb +0 -6
  58. data/lib/graphql/schema/mutation.rb +0 -9
  59. data/lib/graphql/schema/non_null.rb +3 -9
  60. data/lib/graphql/schema/object.rb +0 -40
  61. data/lib/graphql/schema/relay_classic_mutation.rb +17 -28
  62. data/lib/graphql/schema/scalar.rb +1 -16
  63. data/lib/graphql/schema/union.rb +0 -16
  64. data/lib/graphql/schema/warden.rb +3 -12
  65. data/lib/graphql/schema/wrapper.rb +0 -5
  66. data/lib/graphql/schema.rb +106 -945
  67. data/lib/graphql/static_validation/base_visitor.rb +4 -21
  68. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +12 -12
  69. data/lib/graphql/static_validation/validator.rb +2 -24
  70. data/lib/graphql/static_validation.rb +0 -2
  71. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +38 -1
  72. data/lib/graphql/subscriptions/event.rb +1 -1
  73. data/lib/graphql/subscriptions/instrumentation.rb +0 -51
  74. data/lib/graphql/subscriptions.rb +4 -13
  75. data/lib/graphql/tracing/data_dog_tracing.rb +16 -20
  76. data/lib/graphql/tracing/platform_tracing.rb +4 -32
  77. data/lib/graphql/tracing.rb +0 -1
  78. data/lib/graphql/types/relay/connection_behaviors.rb +2 -6
  79. data/lib/graphql/types/relay/default_relay.rb +0 -10
  80. data/lib/graphql/types/relay/node_behaviors.rb +5 -1
  81. data/lib/graphql/types/relay.rb +0 -2
  82. data/lib/graphql/types/string.rb +1 -1
  83. data/lib/graphql/version.rb +1 -1
  84. data/lib/graphql.rb +1 -66
  85. metadata +28 -164
  86. data/lib/graphql/analysis/analyze_query.rb +0 -98
  87. data/lib/graphql/analysis/field_usage.rb +0 -45
  88. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  89. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  90. data/lib/graphql/analysis/query_complexity.rb +0 -88
  91. data/lib/graphql/analysis/query_depth.rb +0 -43
  92. data/lib/graphql/analysis/reducer_state.rb +0 -48
  93. data/lib/graphql/argument.rb +0 -131
  94. data/lib/graphql/authorization.rb +0 -82
  95. data/lib/graphql/backtrace/legacy_tracer.rb +0 -56
  96. data/lib/graphql/backwards_compatibility.rb +0 -61
  97. data/lib/graphql/base_type.rb +0 -232
  98. data/lib/graphql/boolean_type.rb +0 -2
  99. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  100. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  101. data/lib/graphql/compatibility/execution_specification.rb +0 -436
  102. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  103. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -215
  104. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
  105. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  106. data/lib/graphql/compatibility/query_parser_specification.rb +0 -266
  107. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -682
  108. data/lib/graphql/compatibility.rb +0 -5
  109. data/lib/graphql/define/assign_argument.rb +0 -12
  110. data/lib/graphql/define/assign_connection.rb +0 -13
  111. data/lib/graphql/define/assign_enum_value.rb +0 -18
  112. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  113. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  114. data/lib/graphql/define/assign_object_field.rb +0 -42
  115. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  116. data/lib/graphql/define/instance_definable.rb +0 -255
  117. data/lib/graphql/define/no_definition_error.rb +0 -7
  118. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  119. data/lib/graphql/define/type_definer.rb +0 -31
  120. data/lib/graphql/define.rb +0 -31
  121. data/lib/graphql/deprecated_dsl.rb +0 -55
  122. data/lib/graphql/directive/deprecated_directive.rb +0 -2
  123. data/lib/graphql/directive/include_directive.rb +0 -2
  124. data/lib/graphql/directive/skip_directive.rb +0 -2
  125. data/lib/graphql/directive.rb +0 -107
  126. data/lib/graphql/enum_type.rb +0 -133
  127. data/lib/graphql/execution/execute.rb +0 -333
  128. data/lib/graphql/execution/flatten.rb +0 -40
  129. data/lib/graphql/execution/typecast.rb +0 -50
  130. data/lib/graphql/field/resolve.rb +0 -59
  131. data/lib/graphql/field.rb +0 -226
  132. data/lib/graphql/float_type.rb +0 -2
  133. data/lib/graphql/function.rb +0 -128
  134. data/lib/graphql/id_type.rb +0 -2
  135. data/lib/graphql/input_object_type.rb +0 -138
  136. data/lib/graphql/int_type.rb +0 -2
  137. data/lib/graphql/interface_type.rb +0 -72
  138. data/lib/graphql/internal_representation/document.rb +0 -27
  139. data/lib/graphql/internal_representation/node.rb +0 -206
  140. data/lib/graphql/internal_representation/print.rb +0 -51
  141. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  142. data/lib/graphql/internal_representation/scope.rb +0 -88
  143. data/lib/graphql/internal_representation/visit.rb +0 -36
  144. data/lib/graphql/internal_representation.rb +0 -7
  145. data/lib/graphql/list_type.rb +0 -80
  146. data/lib/graphql/non_null_type.rb +0 -71
  147. data/lib/graphql/object_type.rb +0 -130
  148. data/lib/graphql/query/arguments.rb +0 -189
  149. data/lib/graphql/query/arguments_cache.rb +0 -24
  150. data/lib/graphql/query/executor.rb +0 -52
  151. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  152. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  153. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  154. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  155. data/lib/graphql/query/serial_execution.rb +0 -40
  156. data/lib/graphql/relay/array_connection.rb +0 -83
  157. data/lib/graphql/relay/base_connection.rb +0 -189
  158. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  159. data/lib/graphql/relay/connection_resolve.rb +0 -43
  160. data/lib/graphql/relay/connection_type.rb +0 -54
  161. data/lib/graphql/relay/edge.rb +0 -27
  162. data/lib/graphql/relay/edge_type.rb +0 -19
  163. data/lib/graphql/relay/edges_instrumentation.rb +0 -39
  164. data/lib/graphql/relay/global_id_resolve.rb +0 -17
  165. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  166. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  167. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  168. data/lib/graphql/relay/mutation/result.rb +0 -38
  169. data/lib/graphql/relay/mutation.rb +0 -106
  170. data/lib/graphql/relay/node.rb +0 -39
  171. data/lib/graphql/relay/page_info.rb +0 -7
  172. data/lib/graphql/relay/relation_connection.rb +0 -188
  173. data/lib/graphql/relay/type_extensions.rb +0 -32
  174. data/lib/graphql/scalar_type.rb +0 -91
  175. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  176. data/lib/graphql/schema/default_parse_error.rb +0 -10
  177. data/lib/graphql/schema/default_type_error.rb +0 -17
  178. data/lib/graphql/schema/member/accepts_definition.rb +0 -164
  179. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -58
  180. data/lib/graphql/schema/member/instrumentation.rb +0 -131
  181. data/lib/graphql/schema/middleware_chain.rb +0 -82
  182. data/lib/graphql/schema/possible_types.rb +0 -44
  183. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  184. data/lib/graphql/schema/timeout_middleware.rb +0 -88
  185. data/lib/graphql/schema/traversal.rb +0 -228
  186. data/lib/graphql/schema/validation.rb +0 -313
  187. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  188. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  189. data/lib/graphql/string_type.rb +0 -2
  190. data/lib/graphql/subscriptions/subscription_root.rb +0 -76
  191. data/lib/graphql/tracing/skylight_tracing.rb +0 -70
  192. data/lib/graphql/types/relay/node_field.rb +0 -24
  193. data/lib/graphql/types/relay/nodes_field.rb +0 -43
  194. data/lib/graphql/union_type.rb +0 -115
  195. data/lib/graphql/upgrader/member.rb +0 -937
  196. 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
@@ -1,2 +0,0 @@
1
- # frozen_string_literal: true
2
- GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition(silence_deprecation_warning: true)
@@ -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