graphql 2.0.13 → 2.3.10

Sign up to get free protection for your applications and to get access to all the features.
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