graphql 2.0.13 → 2.3.10

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  7. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  8. data/lib/generators/graphql/relay.rb +18 -1
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  12. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  13. data/lib/generators/graphql/templates/base_field.erb +2 -0
  14. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  16. data/lib/generators/graphql/templates/base_object.erb +2 -0
  17. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  18. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  19. data/lib/generators/graphql/templates/base_union.erb +2 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +8 -0
  26. data/lib/graphql/analysis/analyzer.rb +89 -0
  27. data/lib/graphql/analysis/field_usage.rb +82 -0
  28. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  29. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  30. data/lib/graphql/analysis/query_complexity.rb +183 -0
  31. data/lib/graphql/analysis/query_depth.rb +58 -0
  32. data/lib/graphql/analysis/visitor.rb +283 -0
  33. data/lib/graphql/analysis.rb +92 -1
  34. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  35. data/lib/graphql/backtrace/table.rb +2 -2
  36. data/lib/graphql/backtrace/trace.rb +93 -0
  37. data/lib/graphql/backtrace/tracer.rb +1 -1
  38. data/lib/graphql/backtrace.rb +2 -1
  39. data/lib/graphql/coercion_error.rb +1 -9
  40. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/request.rb +5 -0
  43. data/lib/graphql/dataloader/source.rb +89 -45
  44. data/lib/graphql/dataloader.rb +115 -142
  45. data/lib/graphql/duration_encoding_error.rb +16 -0
  46. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  47. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  48. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  49. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  50. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +331 -455
  52. data/lib/graphql/execution/interpreter.rb +125 -61
  53. data/lib/graphql/execution/lazy.rb +6 -12
  54. data/lib/graphql/execution/lookahead.rb +124 -46
  55. data/lib/graphql/execution/multiplex.rb +3 -117
  56. data/lib/graphql/execution.rb +0 -1
  57. data/lib/graphql/introspection/directive_type.rb +3 -3
  58. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  59. data/lib/graphql/introspection/entry_points.rb +11 -5
  60. data/lib/graphql/introspection/field_type.rb +2 -2
  61. data/lib/graphql/introspection/schema_type.rb +10 -13
  62. data/lib/graphql/introspection/type_type.rb +17 -10
  63. data/lib/graphql/introspection.rb +3 -2
  64. data/lib/graphql/language/block_string.rb +34 -18
  65. data/lib/graphql/language/definition_slice.rb +1 -1
  66. data/lib/graphql/language/document_from_schema_definition.rb +75 -59
  67. data/lib/graphql/language/lexer.rb +358 -1506
  68. data/lib/graphql/language/nodes.rb +166 -93
  69. data/lib/graphql/language/parser.rb +795 -1953
  70. data/lib/graphql/language/printer.rb +340 -160
  71. data/lib/graphql/language/sanitized_printer.rb +21 -23
  72. data/lib/graphql/language/static_visitor.rb +167 -0
  73. data/lib/graphql/language/visitor.rb +188 -141
  74. data/lib/graphql/language.rb +61 -1
  75. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  76. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  77. data/lib/graphql/pagination/array_connection.rb +6 -6
  78. data/lib/graphql/pagination/connection.rb +33 -6
  79. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  80. data/lib/graphql/query/context/scoped_context.rb +101 -0
  81. data/lib/graphql/query/context.rb +117 -112
  82. data/lib/graphql/query/null_context.rb +12 -25
  83. data/lib/graphql/query/validation_pipeline.rb +6 -5
  84. data/lib/graphql/query/variables.rb +3 -3
  85. data/lib/graphql/query.rb +86 -30
  86. data/lib/graphql/railtie.rb +9 -6
  87. data/lib/graphql/rake_task.rb +29 -11
  88. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +59 -23
  90. data/lib/graphql/schema/always_visible.rb +11 -0
  91. data/lib/graphql/schema/argument.rb +55 -26
  92. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  93. data/lib/graphql/schema/build_from_definition.rb +56 -32
  94. data/lib/graphql/schema/directive/one_of.rb +24 -0
  95. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  96. data/lib/graphql/schema/directive/transform.rb +1 -1
  97. data/lib/graphql/schema/directive.rb +15 -3
  98. data/lib/graphql/schema/enum.rb +35 -24
  99. data/lib/graphql/schema/enum_value.rb +2 -3
  100. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  101. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  102. data/lib/graphql/schema/field.rb +147 -107
  103. data/lib/graphql/schema/field_extension.rb +1 -4
  104. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  105. data/lib/graphql/schema/has_single_input_argument.rb +158 -0
  106. data/lib/graphql/schema/input_object.rb +47 -11
  107. data/lib/graphql/schema/interface.rb +15 -21
  108. data/lib/graphql/schema/introspection_system.rb +7 -17
  109. data/lib/graphql/schema/late_bound_type.rb +10 -0
  110. data/lib/graphql/schema/list.rb +2 -2
  111. data/lib/graphql/schema/loader.rb +2 -3
  112. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
  113. data/lib/graphql/schema/member/build_type.rb +11 -3
  114. data/lib/graphql/schema/member/has_arguments.rb +170 -130
  115. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  116. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  117. data/lib/graphql/schema/member/has_directives.rb +81 -61
  118. data/lib/graphql/schema/member/has_fields.rb +100 -38
  119. data/lib/graphql/schema/member/has_interfaces.rb +65 -10
  120. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  121. data/lib/graphql/schema/member/has_validators.rb +32 -6
  122. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  123. data/lib/graphql/schema/member/scoped.rb +19 -0
  124. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  125. data/lib/graphql/schema/member/validates_input.rb +3 -3
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +16 -5
  128. data/lib/graphql/schema/printer.rb +11 -8
  129. data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
  130. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  131. data/lib/graphql/schema/resolver.rb +47 -32
  132. data/lib/graphql/schema/scalar.rb +3 -3
  133. data/lib/graphql/schema/subscription.rb +11 -4
  134. data/lib/graphql/schema/subset.rb +397 -0
  135. data/lib/graphql/schema/timeout.rb +25 -29
  136. data/lib/graphql/schema/type_expression.rb +2 -2
  137. data/lib/graphql/schema/type_membership.rb +3 -0
  138. data/lib/graphql/schema/union.rb +11 -2
  139. data/lib/graphql/schema/unique_within_type.rb +1 -1
  140. data/lib/graphql/schema/validator/all_validator.rb +60 -0
  141. data/lib/graphql/schema/validator.rb +4 -2
  142. data/lib/graphql/schema/warden.rb +238 -93
  143. data/lib/graphql/schema.rb +498 -103
  144. data/lib/graphql/static_validation/all_rules.rb +2 -1
  145. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  146. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  147. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  148. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  149. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  150. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  151. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  152. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  153. data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
  154. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  155. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  156. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  157. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  158. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  159. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  160. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  161. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  163. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  164. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  165. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  166. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  167. data/lib/graphql/static_validation/validation_context.rb +5 -5
  168. data/lib/graphql/static_validation/validator.rb +4 -1
  169. data/lib/graphql/static_validation.rb +0 -1
  170. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
  171. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  172. data/lib/graphql/subscriptions/event.rb +11 -10
  173. data/lib/graphql/subscriptions/serialize.rb +2 -0
  174. data/lib/graphql/subscriptions.rb +20 -13
  175. data/lib/graphql/testing/helpers.rb +151 -0
  176. data/lib/graphql/testing.rb +2 -0
  177. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  178. data/lib/graphql/tracing/appoptics_trace.rb +251 -0
  179. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  180. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  181. data/lib/graphql/tracing/data_dog_trace.rb +183 -0
  182. data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
  183. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
  184. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  185. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  186. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  187. data/lib/graphql/tracing/platform_trace.rb +118 -0
  188. data/lib/graphql/tracing/platform_tracing.rb +17 -3
  189. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
  190. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  191. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  192. data/lib/graphql/tracing/scout_trace.rb +72 -0
  193. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  194. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  195. data/lib/graphql/tracing/trace.rb +76 -0
  196. data/lib/graphql/tracing.rb +20 -40
  197. data/lib/graphql/type_kinds.rb +7 -4
  198. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  199. data/lib/graphql/types/relay/base_connection.rb +1 -1
  200. data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
  201. data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
  202. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  203. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  204. data/lib/graphql/types/relay.rb +0 -1
  205. data/lib/graphql/types/string.rb +1 -1
  206. data/lib/graphql/types.rb +1 -0
  207. data/lib/graphql/version.rb +1 -1
  208. data/lib/graphql.rb +27 -20
  209. data/readme.md +13 -3
  210. metadata +96 -47
  211. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  212. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  213. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  214. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  215. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  216. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  217. data/lib/graphql/analysis/ast/visitor.rb +0 -269
  218. data/lib/graphql/analysis/ast.rb +0 -81
  219. data/lib/graphql/deprecation.rb +0 -9
  220. data/lib/graphql/filter.rb +0 -53
  221. data/lib/graphql/language/lexer.rl +0 -280
  222. data/lib/graphql/language/parser.y +0 -554
  223. data/lib/graphql/language/token.rb +0 -34
  224. data/lib/graphql/schema/base_64_bp.rb +0 -26
  225. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  226. data/lib/graphql/static_validation/type_stack.rb +0 -216
  227. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
  228. data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -1,51 +1,38 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/query/context"
2
3
  module GraphQL
3
4
  class Query
4
5
  # This object can be `ctx` in places where there is no query
5
- class NullContext
6
- class NullWarden < GraphQL::Schema::Warden
7
- def visible_field?(field, ctx); true; end
8
- def visible_argument?(arg, ctx); true; end
9
- def visible_type?(type, ctx); true; end
10
- def visible_enum_value?(ev, ctx); true; end
11
- def visible_type_membership?(tm, ctx); true; end
12
- end
6
+ class NullContext < Context
7
+ include Singleton
13
8
 
14
9
  class NullQuery
10
+ def after_lazy(value)
11
+ yield(value)
12
+ end
15
13
  end
16
14
 
17
15
  class NullSchema < GraphQL::Schema
18
16
  end
19
17
 
18
+ extend Forwardable
19
+
20
20
  attr_reader :schema, :query, :warden, :dataloader
21
+ def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key?
21
22
 
22
23
  def initialize
23
24
  @query = NullQuery.new
24
25
  @dataloader = GraphQL::Dataloader::NullDataloader.new
25
26
  @schema = NullSchema
26
- @warden = NullWarden.new(
27
- GraphQL::Filter.new,
28
- context: self,
29
- schema: @schema,
30
- )
27
+ @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
31
28
  end
32
29
 
33
- def [](key); end
34
-
35
30
  def interpreter?
36
31
  true
37
32
  end
38
33
 
39
- class << self
40
- extend Forwardable
41
-
42
- def [](key); end
43
-
44
- def instance
45
- @instance ||= self.new
46
- end
47
-
48
- def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader
34
+ def types
35
+ @types ||= GraphQL::Schema::Warden::SchemaSubset.new(@warden)
49
36
  end
50
37
  end
51
38
  end
@@ -14,7 +14,7 @@ module GraphQL
14
14
  #
15
15
  # @api private
16
16
  class ValidationPipeline
17
- attr_reader :max_depth, :max_complexity
17
+ attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
18
18
 
19
19
  def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
20
20
  @validation_errors = []
@@ -68,9 +68,10 @@ module GraphQL
68
68
  elsif @operation_name_error
69
69
  @validation_errors << @operation_name_error
70
70
  else
71
- validation_result = @schema.static_validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
71
+ validator = @query.static_validator || @schema.static_validator
72
+ validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
72
73
  @validation_errors.concat(validation_result[:errors])
73
-
74
+ @validate_timeout_remaining = validation_result[:remaining_timeout]
74
75
  if @validation_errors.empty?
75
76
  @validation_errors.concat(@query.variables.errors)
76
77
  end
@@ -99,10 +100,10 @@ module GraphQL
99
100
  # Depending on the analysis engine, we must use different analyzers
100
101
  # remove this once everything has switched over to AST analyzers
101
102
  if max_depth
102
- qa << GraphQL::Analysis::AST::MaxQueryDepth
103
+ qa << GraphQL::Analysis::MaxQueryDepth
103
104
  end
104
105
  if max_complexity
105
- qa << GraphQL::Analysis::AST::MaxQueryComplexity
106
+ qa << GraphQL::Analysis::MaxQueryComplexity
106
107
  end
107
108
  qa
108
109
  else
@@ -26,7 +26,7 @@ module GraphQL
26
26
  # - Then, fall back to the default value from the query string
27
27
  # If it's still nil, raise an error if it's required.
28
28
  variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
29
- if variable_type.nil?
29
+ if variable_type.nil? || !variable_type.unwrap.kind.input?
30
30
  # Pass -- it will get handled by a validator
31
31
  else
32
32
  variable_name = ast_variable.name
@@ -80,12 +80,12 @@ module GraphQL
80
80
  else
81
81
  val
82
82
  end
83
- end
83
+ end
84
84
 
85
85
  def add_max_errors_reached_message
86
86
  message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
87
87
  validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
88
- errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
88
+ errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
89
89
  end
90
90
  end
91
91
  end
data/lib/graphql/query.rb CHANGED
@@ -45,6 +45,20 @@ module GraphQL
45
45
  end
46
46
  end
47
47
 
48
+ # @return [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules.
49
+ attr_reader :static_validator
50
+
51
+ # @param new_validate [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules. This can't be reasssigned after validation.
52
+ def static_validator=(new_validator)
53
+ if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated?
54
+ raise ArgumentError, "Can't reassign Query#static_validator= after validation has run, remove this assignment."
55
+ elsif !new_validator.is_a?(GraphQL::StaticValidation::Validator)
56
+ raise ArgumentError, "Expected a `GraphQL::StaticValidation::Validator` instance."
57
+ else
58
+ @static_validator = new_validator
59
+ end
60
+ end
61
+
48
62
  attr_writer :query_string
49
63
 
50
64
  # @return [GraphQL::Language::Nodes::Document]
@@ -81,24 +95,43 @@ module GraphQL
81
95
  # @param root_value [Object] the object used to resolve fields on the root type
82
96
  # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
83
97
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
84
- # @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
85
- # @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
86
- def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
98
+ def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_schema_subset: nil)
87
99
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
88
100
  variables ||= {}
89
101
  @schema = schema
90
- @filter = schema.default_filter.merge(except: except, only: only)
91
- @context = schema.context_class.new(query: self, object: root_value, values: context)
92
- @warden = warden
102
+ @context = schema.context_class.new(query: self, values: context)
103
+
104
+ if use_schema_subset.nil?
105
+ use_schema_subset = warden ? false : schema.use_schema_subset?
106
+ end
107
+
108
+ if use_schema_subset
109
+ @schema_subset = @schema.subset_class.new(self)
110
+ @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
111
+ else
112
+ @schema_subset = nil
113
+ @warden = warden
114
+ end
115
+
93
116
  @subscription_topic = subscription_topic
94
117
  @root_value = root_value
95
118
  @fragments = nil
96
119
  @operations = nil
97
120
  @validate = validate
98
- @tracers = schema.tracers + (context ? context.fetch(:tracers, []) : [])
121
+ self.static_validator = static_validator if static_validator
122
+ context_tracers = (context ? context.fetch(:tracers, []) : [])
123
+ @tracers = schema.tracers + context_tracers
124
+
99
125
  # Support `ctx[:backtrace] = true` for wrapping backtraces
100
126
  if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
101
- @tracers << GraphQL::Backtrace::Tracer
127
+ if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
128
+ context_tracers += [GraphQL::Backtrace::Tracer]
129
+ @tracers << GraphQL::Backtrace::Tracer
130
+ end
131
+ end
132
+
133
+ if context_tracers.any? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
134
+ raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
102
135
  end
103
136
 
104
137
  @analysis_errors = []
@@ -140,9 +173,12 @@ module GraphQL
140
173
  @result_values = nil
141
174
  @executed = false
142
175
 
143
- # TODO add a general way to define schema-level filters
144
- if @schema.respond_to?(:visible?)
145
- merge_filters(only: @schema.method(:visible?))
176
+ @logger = if context && context[:logger] == false
177
+ Logger.new(IO::NULL)
178
+ elsif context && (l = context[:logger])
179
+ l
180
+ else
181
+ schema.default_logger
146
182
  end
147
183
  end
148
184
 
@@ -157,6 +193,11 @@ module GraphQL
157
193
 
158
194
  attr_accessor :multiplex
159
195
 
196
+ # @return [GraphQL::Tracing::Trace]
197
+ def current_trace
198
+ @current_trace ||= context[:trace] || (multiplex ? multiplex.current_trace : schema.new_trace(multiplex: multiplex, query: self))
199
+ end
200
+
160
201
  def subscription_update?
161
202
  @subscription_topic && subscription?
162
203
  end
@@ -166,7 +207,14 @@ module GraphQL
166
207
  def lookahead
167
208
  @lookahead ||= begin
168
209
  ast_node = selected_operation
169
- root_type = warden.root_type_for_operation(ast_node.operation_type || "query")
210
+ root_type = case ast_node.operation_type
211
+ when nil, "query"
212
+ types.query_root # rubocop:disable Development/ContextIsPassedCop
213
+ when "mutation"
214
+ types.mutation_root # rubocop:disable Development/ContextIsPassedCop
215
+ when "subscription"
216
+ types.subscription_root # rubocop:disable Development/ContextIsPassedCop
217
+ end
170
218
  GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
171
219
  end
172
220
  end
@@ -193,10 +241,10 @@ module GraphQL
193
241
  end
194
242
 
195
243
  # Get the result for this query, executing it once
196
- # @return [Hash] A GraphQL response, with `"data"` and/or `"errors"` keys
244
+ # @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
197
245
  def result
198
246
  if !@executed
199
- Execution::Multiplex.run_all(@schema, [self], context: @context)
247
+ Execution::Interpreter.run_all(@schema, [self], context: @context)
200
248
  end
201
249
  @result ||= Query::Result.new(query: self, values: @result_values)
202
250
  end
@@ -275,7 +323,7 @@ module GraphQL
275
323
 
276
324
  # @return [String] An opaque hash for identifying this query's given query string and selected operation
277
325
  def operation_fingerprint
278
- @operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string)}"
326
+ @operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string || "")}"
279
327
  end
280
328
 
281
329
  # @return [String] An opaque hash for identifying this query's given a variable values (not including defaults)
@@ -288,7 +336,7 @@ module GraphQL
288
336
  end
289
337
 
290
338
  def_delegators :validation_pipeline, :validation_errors,
291
- :analyzers, :ast_analyzers, :max_depth, :max_complexity
339
+ :analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
292
340
 
293
341
  attr_accessor :analysis_errors
294
342
  def valid?
@@ -301,12 +349,16 @@ module GraphQL
301
349
 
302
350
  def_delegators :warden, :get_type, :get_field, :possible_types, :root_type_for_operation
303
351
 
352
+ def types
353
+ @schema_subset || warden.schema_subset
354
+ end
355
+
304
356
  # @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType]
305
357
  # @param value [Object] Any runtime value
306
358
  # @return [GraphQL::ObjectType, nil] The runtime type of `value` from {Schema#resolve_type}
307
359
  # @see {#possible_types} to apply filtering from `only` / `except`
308
- def resolve_type(abstract_type, value = :__undefined__)
309
- if value.is_a?(Symbol) && value == :__undefined__
360
+ def resolve_type(abstract_type, value = NOT_CONFIGURED)
361
+ if value.is_a?(Symbol) && value == NOT_CONFIGURED
310
362
  # Old method signature
311
363
  value = abstract_type
312
364
  abstract_type = nil
@@ -325,16 +377,6 @@ module GraphQL
325
377
  with_prepared_ast { @query }
326
378
  end
327
379
 
328
- # @return [void]
329
- def merge_filters(only: nil, except: nil)
330
- if @prepared_ast
331
- raise "Can't add filters after preparing the query"
332
- else
333
- @filter = @filter.merge(only: only, except: except)
334
- end
335
- nil
336
- end
337
-
338
380
  def subscription?
339
381
  with_prepared_ast { @subscription }
340
382
  end
@@ -344,6 +386,20 @@ module GraphQL
344
386
  schema.handle_or_reraise(context, err)
345
387
  end
346
388
 
389
+ def after_lazy(value, &block)
390
+ if !defined?(@runtime_instance)
391
+ @runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
392
+ end
393
+
394
+ if @runtime_instance
395
+ @runtime_instance.minimal_after_lazy(value, &block)
396
+ else
397
+ @schema.after_lazy(value, &block)
398
+ end
399
+ end
400
+
401
+ attr_reader :logger
402
+
347
403
  private
348
404
 
349
405
  def find_operation(operations, operation_name)
@@ -358,11 +414,11 @@ module GraphQL
358
414
 
359
415
  def prepare_ast
360
416
  @prepared_ast = true
361
- @warden ||= GraphQL::Schema::Warden.new(@filter, schema: @schema, context: @context)
417
+ @warden ||= @schema.warden_class.new(schema: @schema, context: @context)
362
418
  parse_error = nil
363
419
  @document ||= begin
364
420
  if query_string
365
- GraphQL.parse(query_string, tracer: self)
421
+ GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
366
422
  end
367
423
  rescue GraphQL::ParseError => err
368
424
  parse_error = err
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module GraphQL
3
4
  class Railtie < Rails::Railtie
4
- config.before_configuration do
5
- # Bootsnap compile cache has similar expiration properties,
6
- # so we assume that if the user has bootsnap setup it's ok
7
- # to piggy back on it.
8
- if ::Object.const_defined?("Bootsnap::CompileCache::ISeq") && Bootsnap::CompileCache::ISeq.cache_dir
9
- Language::Parser.cache ||= Language::Cache.new(Pathname.new(Bootsnap::CompileCache::ISeq.cache_dir).join('graphql'))
5
+ config.graphql = ActiveSupport::OrderedOptions.new
6
+ config.graphql.parser_cache = false
7
+
8
+ initializer("graphql.cache") do |app|
9
+ if config.graphql.parser_cache
10
+ Language::Parser.cache ||= Language::Cache.new(
11
+ app.root.join("tmp/cache/graphql")
12
+ )
10
13
  end
11
14
  end
12
15
  end
@@ -9,8 +9,7 @@ module GraphQL
9
9
  # By default, schemas are looked up by name as constants using `schema_name:`.
10
10
  # You can provide a `load_schema` function to return your schema another way.
11
11
  #
12
- # `load_context:`, `only:` and `except:` are supported so that
13
- # you can keep an eye on how filters affect your schema.
12
+ # Use `load_context:` and `visible?` to dump schemas under certain visibility constraints.
14
13
  #
15
14
  # @example Dump a Schema to .graphql + .json files
16
15
  # require "graphql/rake_task"
@@ -23,6 +22,10 @@ module GraphQL
23
22
  # @example Invoking the task from Ruby
24
23
  # require "rake"
25
24
  # Rake::Task["graphql:schema:dump"].invoke
25
+ #
26
+ # @example Providing arguments to build the introspection query
27
+ # require "graphql/rake_task"
28
+ # GraphQL::RakeTask.new(schema_name: "MySchema", include_is_one_of: true)
26
29
  class RakeTask
27
30
  include Rake::DSL
28
31
 
@@ -32,11 +35,14 @@ module GraphQL
32
35
  schema_name: nil,
33
36
  load_schema: ->(task) { Object.const_get(task.schema_name) },
34
37
  load_context: ->(task) { {} },
35
- only: nil,
36
- except: nil,
37
38
  directory: ".",
38
39
  idl_outfile: "schema.graphql",
39
40
  json_outfile: "schema.json",
41
+ include_deprecated_args: true,
42
+ include_schema_description: false,
43
+ include_is_repeatable: false,
44
+ include_specified_by_url: false,
45
+ include_is_one_of: false
40
46
  }
41
47
 
42
48
  # @return [String] Namespace for generated tasks
@@ -59,12 +65,6 @@ module GraphQL
59
65
  # @return [<#call(task)>] A callable for loading the query context
60
66
  attr_accessor :load_context
61
67
 
62
- # @return [<#call(member, ctx)>, nil] A filter for this task
63
- attr_accessor :only
64
-
65
- # @return [<#call(member, ctx)>, nil] A filter for this task
66
- attr_accessor :except
67
-
68
68
  # @return [String] target for IDL task
69
69
  attr_accessor :idl_outfile
70
70
 
@@ -74,6 +74,10 @@ module GraphQL
74
74
  # @return [String] directory for IDL & JSON files
75
75
  attr_accessor :directory
76
76
 
77
+ # @return [Boolean] Options for additional fields in the introspection query JSON response
78
+ # @see GraphQL::Schema.as_json
79
+ attr_accessor :include_deprecated_args, :include_schema_description, :include_is_repeatable, :include_specified_by_url, :include_is_one_of
80
+
77
81
  # Set the parameters of this task by passing keyword arguments
78
82
  # or assigning attributes inside the block
79
83
  def initialize(options = {})
@@ -96,7 +100,21 @@ module GraphQL
96
100
  def write_outfile(method_name, file)
97
101
  schema = @load_schema.call(self)
98
102
  context = @load_context.call(self)
99
- result = schema.public_send(method_name, only: @only, except: @except, context: context)
103
+ result = case method_name
104
+ when :to_json
105
+ schema.to_json(
106
+ include_is_one_of: include_is_one_of,
107
+ include_deprecated_args: include_deprecated_args,
108
+ include_is_repeatable: include_is_repeatable,
109
+ include_specified_by_url: include_specified_by_url,
110
+ include_schema_description: include_schema_description,
111
+ context: context
112
+ )
113
+ when :to_definition
114
+ schema.to_definition(context: context)
115
+ else
116
+ raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
117
+ end
100
118
  dir = File.dirname(file)
101
119
  FileUtils.mkdir_p(dir)
102
120
  if !result.end_with?("\n")
@@ -9,7 +9,7 @@ module GraphQL
9
9
 
10
10
  # Return the source of `send_node`, but without the keyword argument represented by `pair_node`
11
11
  def source_without_keyword_argument(send_node, pair_node)
12
- # work back to the preceeding comma
12
+ # work back to the preceding comma
13
13
  first_pos = pair_node.location.expression.begin_pos
14
14
  end_pos = pair_node.location.expression.end_pos
15
15
  node_source = send_node.source_range.source
@@ -12,7 +12,7 @@ module GraphQL
12
12
  @possible_types = {}
13
13
  @types = {}
14
14
  @union_memberships = {}
15
- @references = Hash.new { |h, k| h[k] = [] }
15
+ @references = Hash.new { |h, k| h[k] = Set.new }
16
16
  @arguments_with_default_values = []
17
17
  add_type_and_traverse(new_types)
18
18
  end
@@ -20,7 +20,7 @@ module GraphQL
20
20
  private
21
21
 
22
22
  def references_to(thing, from:)
23
- @references[thing] << from
23
+ @references[thing].add(from)
24
24
  end
25
25
 
26
26
  def get_type(name)
@@ -40,14 +40,21 @@ module GraphQL
40
40
  end
41
41
 
42
42
  def add_directives_from(owner)
43
- dirs = owner.directives.map(&:class)
44
- @directives.merge(dirs)
45
- add_type_and_traverse(dirs)
43
+ if (dir_instances = owner.directives).any?
44
+ dirs = dir_instances.map(&:class)
45
+ @directives.merge(dirs)
46
+ add_type_and_traverse(dirs)
47
+ end
46
48
  end
47
49
 
48
50
  def add_type_and_traverse(new_types)
49
51
  late_types = []
50
- new_types.each { |t| add_type(t, owner: nil, late_types: late_types, path: [t.graphql_name]) }
52
+ path = []
53
+ new_types.each do |t|
54
+ path.push(t.graphql_name)
55
+ add_type(t, owner: nil, late_types: late_types, path: path)
56
+ path.pop
57
+ end
51
58
  missed_late_types = 0
52
59
  while (late_type_vals = late_types.shift)
53
60
  type_owner, lt = late_type_vals
@@ -88,7 +95,7 @@ module GraphQL
88
95
  # It's a union with possible_types
89
96
  # Replace the item by class name
90
97
  owner.assign_type_membership_object_type(type)
91
- @possible_types[owner.graphql_name] = owner.possible_types
98
+ @possible_types[owner] = owner.possible_types
92
99
  elsif type.kind.interface? && (owner.kind.object? || owner.kind.interface?)
93
100
  new_interfaces = []
94
101
  owner.interfaces.each do |int_t|
@@ -103,7 +110,7 @@ module GraphQL
103
110
  end
104
111
  owner.implements(*new_interfaces)
105
112
  new_interfaces.each do |int|
106
- pt = @possible_types[int.graphql_name] ||= []
113
+ pt = @possible_types[int] ||= []
107
114
  if !pt.include?(owner) && owner.is_a?(Class)
108
115
  pt << owner
109
116
  end
@@ -119,6 +126,7 @@ module GraphQL
119
126
  @types[type.graphql_name] = type
120
127
  when GraphQL::Schema::Field, GraphQL::Schema::Argument
121
128
  orig_type = owner.type
129
+ unwrapped_t = type
122
130
  # Apply list/non-null wrapper as needed
123
131
  if orig_type.respond_to?(:of_type)
124
132
  transforms = []
@@ -135,6 +143,7 @@ module GraphQL
135
143
  transforms.reverse_each { |t| type = type.public_send(t) }
136
144
  end
137
145
  owner.type = type
146
+ references_to(unwrapped_t, from: owner)
138
147
  else
139
148
  raise "Unexpected update: #{owner.inspect} #{type.inspect}"
140
149
  end
@@ -157,8 +166,12 @@ module GraphQL
157
166
  @directives << type
158
167
  type.all_argument_definitions.each do |arg|
159
168
  arg_type = arg.type.unwrap
160
- references_to(arg_type, from: arg)
161
- add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg.graphql_name])
169
+ if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
170
+ references_to(arg_type, from: arg)
171
+ end
172
+ path.push(arg.graphql_name)
173
+ add_type(arg_type, owner: arg, late_types: late_types, path: path)
174
+ path.pop
162
175
  if arg.default_value?
163
176
  @arguments_with_default_values << arg
164
177
  end
@@ -178,56 +191,72 @@ module GraphQL
178
191
  type.all_field_definitions.each do |field|
179
192
  name = field.graphql_name
180
193
  field_type = field.type.unwrap
181
- references_to(field_type, from: field)
182
- field_path = path + [name]
183
- add_type(field_type, owner: field, late_types: late_types, path: field_path)
194
+ if !field_type.is_a?(GraphQL::Schema::LateBoundType)
195
+ references_to(field_type, from: field)
196
+ end
197
+ path.push(name)
198
+ add_type(field_type, owner: field, late_types: late_types, path: path)
184
199
  add_directives_from(field)
185
200
  field.all_argument_definitions.each do |arg|
186
201
  add_directives_from(arg)
187
202
  arg_type = arg.type.unwrap
188
- references_to(arg_type, from: arg)
189
- add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg.graphql_name])
203
+ if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
204
+ references_to(arg_type, from: arg)
205
+ end
206
+ path.push(arg.graphql_name)
207
+ add_type(arg_type, owner: arg, late_types: late_types, path: path)
208
+ path.pop
190
209
  if arg.default_value?
191
210
  @arguments_with_default_values << arg
192
211
  end
193
212
  end
213
+ path.pop
194
214
  end
195
215
  end
196
216
  if type.kind.input_object?
197
217
  type.all_argument_definitions.each do |arg|
198
218
  add_directives_from(arg)
199
219
  arg_type = arg.type.unwrap
200
- references_to(arg_type, from: arg)
201
- add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg.graphql_name])
220
+ if !arg_type.is_a?(GraphQL::Schema::LateBoundType)
221
+ references_to(arg_type, from: arg)
222
+ end
223
+ path.push(arg.graphql_name)
224
+ add_type(arg_type, owner: arg, late_types: late_types, path: path)
225
+ path.pop
202
226
  if arg.default_value?
203
227
  @arguments_with_default_values << arg
204
228
  end
205
229
  end
206
230
  end
207
231
  if type.kind.union?
208
- @possible_types[type.graphql_name] = type.all_possible_types
232
+ @possible_types[type] = type.all_possible_types
233
+ path.push("possible_types")
209
234
  type.all_possible_types.each do |t|
210
- add_type(t, owner: type, late_types: late_types, path: path + ["possible_types"])
235
+ add_type(t, owner: type, late_types: late_types, path: path)
211
236
  end
237
+ path.pop
212
238
  end
213
239
  if type.kind.interface?
240
+ path.push("orphan_types")
214
241
  type.orphan_types.each do |t|
215
- add_type(t, owner: type, late_types: late_types, path: path + ["orphan_types"])
242
+ add_type(t, owner: type, late_types: late_types, path: path)
216
243
  end
244
+ path.pop
217
245
  end
218
246
  if type.kind.object?
219
- possible_types_for_this_name = @possible_types[type.graphql_name] ||= []
247
+ possible_types_for_this_name = @possible_types[type] ||= []
220
248
  possible_types_for_this_name << type
221
249
  end
222
250
 
223
251
  if type.kind.object? || type.kind.interface?
252
+ path.push("implements")
224
253
  type.interface_type_memberships.each do |interface_type_membership|
225
254
  case interface_type_membership
226
255
  when Schema::TypeMembership
227
256
  interface_type = interface_type_membership.abstract_type
228
257
  # We can get these now; we'll have to get late-bound types later
229
258
  if interface_type.is_a?(Module) && type.is_a?(Class)
230
- implementers = @possible_types[interface_type.graphql_name] ||= []
259
+ implementers = @possible_types[interface_type] ||= []
231
260
  implementers << type
232
261
  end
233
262
  when String, Schema::LateBoundType
@@ -235,7 +264,14 @@ module GraphQL
235
264
  else
236
265
  raise ArgumentError, "Invariant: unexpected type membership for #{type.graphql_name}: #{interface_type_membership.class} (#{interface_type_membership.inspect})"
237
266
  end
238
- add_type(interface_type, owner: type, late_types: late_types, path: path + ["implements"])
267
+ add_type(interface_type, owner: type, late_types: late_types, path: path)
268
+ end
269
+ path.pop
270
+ end
271
+
272
+ if type.kind.enum?
273
+ type.all_enum_value_definitions.each do |value_definition|
274
+ add_directives_from(value_definition)
239
275
  end
240
276
  end
241
277
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class AlwaysVisible
5
+ def self.use(schema, **opts)
6
+ schema.warden_class = GraphQL::Schema::Warden::NullWarden
7
+ schema.subset_class = GraphQL::Schema::Warden::NullWarden::NullSubset
8
+ end
9
+ end
10
+ end
11
+ end