graphql 2.3.5 → 2.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/graphql/analysis/analyzer.rb +89 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +183 -0
- data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +2 -0
- data/lib/graphql/dataloader/source.rb +5 -2
- data/lib/graphql/dataloader.rb +4 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/runtime.rb +29 -25
- data/lib/graphql/execution/interpreter.rb +3 -1
- data/lib/graphql/execution/lookahead.rb +10 -10
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +6 -11
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language/document_from_schema_definition.rb +19 -26
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/parser.rb +9 -1
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +0 -1
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/null_context.rb +2 -2
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query.rb +26 -7
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +129 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +1 -0
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +19 -5
- data/lib/graphql/schema/build_from_definition.rb +8 -1
- data/lib/graphql/schema/directive/flagged.rb +1 -1
- data/lib/graphql/schema/directive.rb +2 -0
- data/lib/graphql/schema/enum.rb +51 -20
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +85 -39
- data/lib/graphql/schema/has_single_input_argument.rb +2 -1
- data/lib/graphql/schema/input_object.rb +8 -7
- data/lib/graphql/schema/interface.rb +20 -4
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/member/has_arguments.rb +14 -9
- data/lib/graphql/schema/member/has_fields.rb +8 -6
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/resolver.rb +5 -5
- data/lib/graphql/schema/subset.rb +509 -0
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/types_migration.rb +187 -0
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator.rb +2 -0
- data/lib/graphql/schema/warden.rb +89 -5
- data/lib/graphql/schema.rb +109 -53
- data/lib/graphql/static_validation/base_visitor.rb +6 -5
- data/lib/graphql/static_validation/literal_validator.rb +4 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +7 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +2 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +3 -3
- data/lib/graphql/testing/helpers.rb +8 -5
- data/lib/graphql/types/relay/connection_behaviors.rb +10 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +10 -0
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -0
- metadata +31 -13
- data/lib/graphql/analysis/ast/analyzer.rb +0 -91
- data/lib/graphql/analysis/ast/field_usage.rb +0 -84
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
- data/lib/graphql/analysis/ast/visitor.rb +0 -284
- data/lib/graphql/analysis/ast.rb +0 -94
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -0,0 +1,187 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
# You can add this plugin to your schema to see how {GraphQL::Schema::Warden} and {GraphQL::Schema::Subset}
|
5
|
+
# handle `.visible?` differently in your schema.
|
6
|
+
#
|
7
|
+
# This plugin runs the same method on both implementations and raises an error when the results diverge.
|
8
|
+
#
|
9
|
+
# To fix the error, modify your schema so that both implementations return the same thing.
|
10
|
+
# Or, open an issue on GitHub to discuss the difference.
|
11
|
+
#
|
12
|
+
# This plugin adds overhead to runtime and may cause unexpected crashes -- **don't** use it in production!
|
13
|
+
#
|
14
|
+
# This plugin adds two keys to `context` when running:
|
15
|
+
#
|
16
|
+
# - `types_migration_running: true`
|
17
|
+
# - For the {Warden} which it instantiates, it adds `types_migration_warden_running: true`.
|
18
|
+
#
|
19
|
+
# Use those keys to modify your `visible?` behavior as needed.
|
20
|
+
#
|
21
|
+
# Also, in a pinch, you can set `skip_types_migration_error: true` in context to turn off this plugin's behavior per-query.
|
22
|
+
# (In that case, it uses {Subset} directly.)
|
23
|
+
#
|
24
|
+
# @example Adding this plugin
|
25
|
+
#
|
26
|
+
# if !Rails.env.production?
|
27
|
+
# use GraphQL::Schema::TypesMigration
|
28
|
+
# end
|
29
|
+
class TypesMigration < GraphQL::Schema::Subset
|
30
|
+
def self.use(schema)
|
31
|
+
schema.subset_class = self
|
32
|
+
end
|
33
|
+
|
34
|
+
class RuntimeTypesMismatchError < GraphQL::Error
|
35
|
+
def initialize(method_called, warden_result, subset_result, method_args)
|
36
|
+
super(<<~ERR)
|
37
|
+
Mismatch in types for `##{method_called}(#{method_args.map(&:inspect).join(", ")})`:
|
38
|
+
|
39
|
+
#{compare_results(warden_result, subset_result)}
|
40
|
+
|
41
|
+
Update your `.visible?` implementation to make these implementations return the same value.
|
42
|
+
|
43
|
+
See: https://graphql-ruby.org/authorization/visibility_migration.html
|
44
|
+
ERR
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def compare_results(warden_result, subset_result)
|
49
|
+
if warden_result.is_a?(Array) && subset_result.is_a?(Array)
|
50
|
+
all_results = warden_result | subset_result
|
51
|
+
all_results.sort_by!(&:graphql_name)
|
52
|
+
|
53
|
+
entries_text = all_results.map { |entry| "#{entry.graphql_name} (#{entry})"}
|
54
|
+
width = entries_text.map(&:size).max
|
55
|
+
yes = " ✔ "
|
56
|
+
no = " "
|
57
|
+
res = "".dup
|
58
|
+
res << "#{"Result".center(width)} Warden Subset \n"
|
59
|
+
all_results.each_with_index do |entry, idx|
|
60
|
+
res << "#{entries_text[idx].ljust(width)}#{warden_result.include?(entry) ? yes : no}#{subset_result.include?(entry) ? yes : no}\n"
|
61
|
+
end
|
62
|
+
res << "\n"
|
63
|
+
else
|
64
|
+
"- Warden returned: #{humanize(warden_result)}\n\n- Subset returned: #{humanize(subset_result)}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def humanize(val)
|
68
|
+
case val
|
69
|
+
when Array
|
70
|
+
"#{val.size}: #{val.map { |v| humanize(v) }.sort.inspect}"
|
71
|
+
when Module
|
72
|
+
if val.respond_to?(:graphql_name)
|
73
|
+
"#{val.graphql_name} (#{val.inspect})"
|
74
|
+
else
|
75
|
+
val.inspect
|
76
|
+
end
|
77
|
+
else
|
78
|
+
val.inspect
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize(context:, schema:)
|
84
|
+
@skip_error = context[:skip_types_migration_error]
|
85
|
+
context[:types_migration_running] = true
|
86
|
+
@subset_types = GraphQL::Schema::Subset.new(context: context, schema: schema)
|
87
|
+
if !@skip_error
|
88
|
+
warden_ctx_vals = context.to_h.dup
|
89
|
+
warden_ctx_vals[:types_migration_warden_running] = true
|
90
|
+
if defined?(schema::WardenCompatSchema)
|
91
|
+
warden_schema = schema::WardenCompatSchema
|
92
|
+
else
|
93
|
+
warden_schema = Class.new(schema)
|
94
|
+
warden_schema.use_schema_subset = false
|
95
|
+
# TODO public API
|
96
|
+
warden_schema.send(:add_type_and_traverse, [warden_schema.query, warden_schema.mutation, warden_schema.subscription].compact, root: true)
|
97
|
+
warden_schema.send(:add_type_and_traverse, warden_schema.directives.values + warden_schema.orphan_types, root: false)
|
98
|
+
end
|
99
|
+
warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals)
|
100
|
+
example_warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
|
101
|
+
@warden_types = example_warden.schema_subset
|
102
|
+
warden_ctx.warden = example_warden
|
103
|
+
warden_ctx.types = @warden_types
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def loaded_types
|
108
|
+
@subset_types.loaded_types
|
109
|
+
end
|
110
|
+
|
111
|
+
PUBLIC_SUBSET_METHODS = [
|
112
|
+
:enum_values,
|
113
|
+
:interfaces,
|
114
|
+
:all_types,
|
115
|
+
:fields,
|
116
|
+
:loadable?,
|
117
|
+
:type,
|
118
|
+
:arguments,
|
119
|
+
:argument,
|
120
|
+
:directive_exists?,
|
121
|
+
:directives,
|
122
|
+
:field,
|
123
|
+
:query_root,
|
124
|
+
:mutation_root,
|
125
|
+
:possible_types,
|
126
|
+
:subscription_root,
|
127
|
+
:reachable_type?
|
128
|
+
]
|
129
|
+
|
130
|
+
PUBLIC_SUBSET_METHODS.each do |subset_method|
|
131
|
+
define_method(subset_method) do |*args|
|
132
|
+
call_method_and_compare(subset_method, args)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def call_method_and_compare(method, args)
|
137
|
+
res_1 = @subset_types.public_send(method, *args)
|
138
|
+
if @skip_error
|
139
|
+
return res_1
|
140
|
+
end
|
141
|
+
|
142
|
+
res_2 = @warden_types.public_send(method, *args)
|
143
|
+
normalized_res_1 = res_1.is_a?(Array) ? Set.new(res_1) : res_1
|
144
|
+
normalized_res_2 = res_2.is_a?(Array) ? Set.new(res_2) : res_2
|
145
|
+
if !equivalent_schema_members?(normalized_res_1, normalized_res_2)
|
146
|
+
# Raise the errors with the orignally returned values:
|
147
|
+
err = RuntimeTypesMismatchError.new(method, res_2, res_1, args)
|
148
|
+
raise err
|
149
|
+
else
|
150
|
+
res_1
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def equivalent_schema_members?(member1, member2)
|
155
|
+
if member1.class != member2.class
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
|
159
|
+
case member1
|
160
|
+
when Set
|
161
|
+
member1_array = member1.to_a.sort_by(&:graphql_name)
|
162
|
+
member2_array = member2.to_a.sort_by(&:graphql_name)
|
163
|
+
member1_array.each_with_index do |inner_member1, idx|
|
164
|
+
inner_member2 = member2_array[idx]
|
165
|
+
equivalent_schema_members?(inner_member1, inner_member2)
|
166
|
+
end
|
167
|
+
when GraphQL::Schema::Field
|
168
|
+
member1.ensure_loaded
|
169
|
+
member2.ensure_loaded
|
170
|
+
if member1.introspection? && member2.introspection?
|
171
|
+
member1.inspect == member2.inspect
|
172
|
+
else
|
173
|
+
member1 == member2
|
174
|
+
end
|
175
|
+
when Module
|
176
|
+
if member1.introspection? && member2.introspection?
|
177
|
+
member1.graphql_name == member2.graphql_name
|
178
|
+
else
|
179
|
+
member1 == member2
|
180
|
+
end
|
181
|
+
else
|
182
|
+
member1 == member2
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Validator
|
6
|
+
# Use this to validate each member of an array value.
|
7
|
+
#
|
8
|
+
# @example validate format of all strings in an array
|
9
|
+
#
|
10
|
+
# argument :handles, [String],
|
11
|
+
# validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ } } }
|
12
|
+
#
|
13
|
+
# @example multiple validators can be combined
|
14
|
+
#
|
15
|
+
# argument :handles, [String],
|
16
|
+
# validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ }, length: { maximum: 32 } } }
|
17
|
+
#
|
18
|
+
# @example any type can be used
|
19
|
+
#
|
20
|
+
# argument :choices, [Integer],
|
21
|
+
# validates: { all: { inclusion: { in: 1..12 } } }
|
22
|
+
#
|
23
|
+
class AllValidator < Validator
|
24
|
+
def initialize(validated:, allow_blank: false, allow_null: false, **validators)
|
25
|
+
super(validated: validated, allow_blank: allow_blank, allow_null: allow_null)
|
26
|
+
|
27
|
+
@validators = Validator.from_config(validated, validators)
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate(object, context, value)
|
31
|
+
return EMPTY_ARRAY if permitted_empty_value?(value)
|
32
|
+
|
33
|
+
all_errors = EMPTY_ARRAY
|
34
|
+
|
35
|
+
value.each do |subvalue|
|
36
|
+
@validators.each do |validator|
|
37
|
+
errors = validator.validate(object, context, subvalue)
|
38
|
+
if errors &&
|
39
|
+
(errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
|
40
|
+
(errors.is_a?(String))
|
41
|
+
if all_errors.frozen? # It's empty
|
42
|
+
all_errors = []
|
43
|
+
end
|
44
|
+
if errors.is_a?(String)
|
45
|
+
all_errors << errors
|
46
|
+
else
|
47
|
+
all_errors.concat(errors)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
unless all_errors.frozen?
|
54
|
+
all_errors.uniq!
|
55
|
+
end
|
56
|
+
|
57
|
+
all_errors
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -169,3 +169,5 @@ require "graphql/schema/validator/allow_null_validator"
|
|
169
169
|
GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator)
|
170
170
|
require "graphql/schema/validator/allow_blank_validator"
|
171
171
|
GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator)
|
172
|
+
require "graphql/schema/validator/all_validator"
|
173
|
+
GraphQL::Schema::Validator.install(:all, GraphQL::Schema::Validator::AllValidator)
|
@@ -61,14 +61,27 @@ module GraphQL
|
|
61
61
|
def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
|
62
62
|
def arguments(owner, ctx); owner.arguments(ctx); end
|
63
63
|
def loadable?(type, ctx); type.visible?(ctx); end
|
64
|
+
def schema_subset
|
65
|
+
@schema_subset ||= Warden::SchemaSubset.new(self)
|
66
|
+
end
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
67
70
|
class NullWarden
|
68
71
|
def initialize(_filter = nil, context:, schema:)
|
69
72
|
@schema = schema
|
73
|
+
@schema_subset = Warden::SchemaSubset.new(self)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @api private
|
77
|
+
module NullSubset
|
78
|
+
def self.new(context:, schema:)
|
79
|
+
NullWarden.new(context: context, schema: schema).schema_subset
|
80
|
+
end
|
70
81
|
end
|
71
82
|
|
83
|
+
attr_reader :schema_subset
|
84
|
+
|
72
85
|
def visible_field?(field_defn, _ctx = nil, owner = nil); true; end
|
73
86
|
def visible_argument?(arg_defn, _ctx = nil); true; end
|
74
87
|
def visible_type?(type_defn, _ctx = nil); true; end
|
@@ -91,6 +104,80 @@ module GraphQL
|
|
91
104
|
def interfaces(obj_type); obj_type.interfaces; end
|
92
105
|
end
|
93
106
|
|
107
|
+
def schema_subset
|
108
|
+
@schema_subset ||= SchemaSubset.new(self)
|
109
|
+
end
|
110
|
+
|
111
|
+
class SchemaSubset
|
112
|
+
def initialize(warden)
|
113
|
+
@warden = warden
|
114
|
+
end
|
115
|
+
|
116
|
+
def directives
|
117
|
+
@warden.directives
|
118
|
+
end
|
119
|
+
|
120
|
+
def directive_exists?(dir_name)
|
121
|
+
@warden.directives.any? { |d| d.graphql_name == dir_name }
|
122
|
+
end
|
123
|
+
|
124
|
+
def type(name)
|
125
|
+
@warden.get_type(name)
|
126
|
+
end
|
127
|
+
|
128
|
+
def field(owner, field_name)
|
129
|
+
@warden.get_field(owner, field_name)
|
130
|
+
end
|
131
|
+
|
132
|
+
def argument(owner, arg_name)
|
133
|
+
@warden.get_argument(owner, arg_name)
|
134
|
+
end
|
135
|
+
|
136
|
+
def query_root
|
137
|
+
@warden.root_type_for_operation("query")
|
138
|
+
end
|
139
|
+
|
140
|
+
def mutation_root
|
141
|
+
@warden.root_type_for_operation("mutation")
|
142
|
+
end
|
143
|
+
|
144
|
+
def subscription_root
|
145
|
+
@warden.root_type_for_operation("subscription")
|
146
|
+
end
|
147
|
+
|
148
|
+
def arguments(owner)
|
149
|
+
@warden.arguments(owner)
|
150
|
+
end
|
151
|
+
|
152
|
+
def fields(owner)
|
153
|
+
@warden.fields(owner)
|
154
|
+
end
|
155
|
+
|
156
|
+
def possible_types(type)
|
157
|
+
@warden.possible_types(type)
|
158
|
+
end
|
159
|
+
|
160
|
+
def enum_values(enum_type)
|
161
|
+
@warden.enum_values(enum_type)
|
162
|
+
end
|
163
|
+
|
164
|
+
def all_types
|
165
|
+
@warden.reachable_types
|
166
|
+
end
|
167
|
+
|
168
|
+
def interfaces(obj_type)
|
169
|
+
@warden.interfaces(obj_type)
|
170
|
+
end
|
171
|
+
|
172
|
+
def loadable?(t, ctx) # TODO remove ctx here?
|
173
|
+
@warden.loadable?(t, ctx)
|
174
|
+
end
|
175
|
+
|
176
|
+
def reachable_type?(type_name)
|
177
|
+
!!@warden.reachable_type?(type_name)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
94
181
|
# @param context [GraphQL::Query::Context]
|
95
182
|
# @param schema [GraphQL::Schema]
|
96
183
|
def initialize(context:, schema:)
|
@@ -101,13 +188,12 @@ module GraphQL
|
|
101
188
|
@subscription = @schema.subscription
|
102
189
|
@context = context
|
103
190
|
@visibility_cache = read_through { |m| schema.visible?(m, context) }
|
104
|
-
@visibility_cache.compare_by_identity
|
105
191
|
# Initialize all ivars to improve object shape consistency:
|
106
192
|
@types = @visible_types = @reachable_types = @visible_parent_fields =
|
107
193
|
@visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
|
108
194
|
@visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
|
109
195
|
@visible_and_reachable_type = @unions = @unfiltered_interfaces =
|
110
|
-
@reachable_type_set =
|
196
|
+
@reachable_type_set = @schema_subset =
|
111
197
|
nil
|
112
198
|
end
|
113
199
|
|
@@ -376,9 +462,7 @@ module GraphQL
|
|
376
462
|
end
|
377
463
|
|
378
464
|
def read_through
|
379
|
-
|
380
|
-
h.compare_by_identity
|
381
|
-
h
|
465
|
+
Hash.new { |h, k| h[k] = yield(k) }.compare_by_identity
|
382
466
|
end
|
383
467
|
|
384
468
|
def reachable_type_set
|
data/lib/graphql/schema.rb
CHANGED
@@ -5,7 +5,6 @@ require "graphql/schema/always_visible"
|
|
5
5
|
require "graphql/schema/base_64_encoder"
|
6
6
|
require "graphql/schema/find_inherited_value"
|
7
7
|
require "graphql/schema/finder"
|
8
|
-
require "graphql/schema/invalid_type_error"
|
9
8
|
require "graphql/schema/introspection_system"
|
10
9
|
require "graphql/schema/late_bound_type"
|
11
10
|
require "graphql/schema/null_mask"
|
@@ -46,6 +45,8 @@ require "graphql/schema/mutation"
|
|
46
45
|
require "graphql/schema/has_single_input_argument"
|
47
46
|
require "graphql/schema/relay_classic_mutation"
|
48
47
|
require "graphql/schema/subscription"
|
48
|
+
require "graphql/schema/subset"
|
49
|
+
require "graphql/schema/types_migration"
|
49
50
|
|
50
51
|
module GraphQL
|
51
52
|
# A GraphQL schema which may be queried with {GraphQL::Query}.
|
@@ -338,6 +339,9 @@ module GraphQL
|
|
338
339
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
339
340
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
340
341
|
def types(context = GraphQL::Query::NullContext.instance)
|
342
|
+
if use_schema_subset?
|
343
|
+
return Subset.from_context(context, self).all_types_h
|
344
|
+
end
|
341
345
|
all_types = non_introspection_types.merge(introspection_system.types)
|
342
346
|
visible_types = {}
|
343
347
|
all_types.each do |k, v|
|
@@ -365,25 +369,32 @@ module GraphQL
|
|
365
369
|
# @param type_name [String]
|
366
370
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
367
371
|
def get_type(type_name, context = GraphQL::Query::NullContext.instance)
|
372
|
+
if use_schema_subset?
|
373
|
+
return Subset.from_context(context, self).type(type_name)
|
374
|
+
end
|
368
375
|
local_entry = own_types[type_name]
|
369
376
|
type_defn = case local_entry
|
370
377
|
when nil
|
371
378
|
nil
|
372
379
|
when Array
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
380
|
+
if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Subset)
|
381
|
+
local_entry
|
382
|
+
else
|
383
|
+
visible_t = nil
|
384
|
+
warden = Warden.from_context(context)
|
385
|
+
local_entry.each do |t|
|
386
|
+
if warden.visible_type?(t, context)
|
387
|
+
if visible_t.nil?
|
388
|
+
visible_t = t
|
389
|
+
else
|
390
|
+
raise DuplicateNamesError.new(
|
391
|
+
duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
|
392
|
+
)
|
393
|
+
end
|
383
394
|
end
|
384
395
|
end
|
396
|
+
visible_t
|
385
397
|
end
|
386
|
-
visible_t
|
387
398
|
when Module
|
388
399
|
local_entry
|
389
400
|
else
|
@@ -419,48 +430,62 @@ module GraphQL
|
|
419
430
|
end
|
420
431
|
end
|
421
432
|
|
422
|
-
def
|
423
|
-
|
424
|
-
end
|
425
|
-
|
426
|
-
def query(new_query_object = nil)
|
427
|
-
if new_query_object
|
433
|
+
def query(new_query_object = nil, &lazy_load_block)
|
434
|
+
if new_query_object || block_given?
|
428
435
|
if @query_object
|
429
|
-
|
436
|
+
dup_defn = new_query_object || yield
|
437
|
+
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
438
|
+
elsif use_schema_subset?
|
439
|
+
@query_object = block_given? ? lazy_load_block : new_query_object
|
430
440
|
else
|
431
|
-
@query_object = new_query_object
|
432
|
-
add_type_and_traverse(
|
433
|
-
nil
|
441
|
+
@query_object = new_query_object || lazy_load_block.call
|
442
|
+
add_type_and_traverse(@query_object, root: true)
|
434
443
|
end
|
444
|
+
nil
|
445
|
+
elsif @query_object.is_a?(Proc)
|
446
|
+
@query_object = @query_object.call
|
435
447
|
else
|
436
448
|
@query_object || find_inherited_value(:query)
|
437
449
|
end
|
438
450
|
end
|
439
451
|
|
440
|
-
def mutation(new_mutation_object = nil)
|
441
|
-
if new_mutation_object
|
452
|
+
def mutation(new_mutation_object = nil, &lazy_load_block)
|
453
|
+
if new_mutation_object || block_given?
|
442
454
|
if @mutation_object
|
443
|
-
|
455
|
+
dup_defn = new_mutation_object || yield
|
456
|
+
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
457
|
+
elsif use_schema_subset?
|
458
|
+
@mutation_object = block_given? ? lazy_load_block : new_mutation_object
|
444
459
|
else
|
445
|
-
@mutation_object = new_mutation_object
|
446
|
-
add_type_and_traverse(
|
447
|
-
nil
|
460
|
+
@mutation_object = new_mutation_object || lazy_load_block.call
|
461
|
+
add_type_and_traverse(@mutation_object, root: true)
|
448
462
|
end
|
463
|
+
nil
|
464
|
+
elsif @mutation_object.is_a?(Proc)
|
465
|
+
@mutation_object = @mutation_object.call
|
449
466
|
else
|
450
467
|
@mutation_object || find_inherited_value(:mutation)
|
451
468
|
end
|
452
469
|
end
|
453
470
|
|
454
|
-
def subscription(new_subscription_object = nil)
|
455
|
-
if new_subscription_object
|
471
|
+
def subscription(new_subscription_object = nil, &lazy_load_block)
|
472
|
+
if new_subscription_object || block_given?
|
456
473
|
if @subscription_object
|
457
|
-
|
474
|
+
dup_defn = new_subscription_object || yield
|
475
|
+
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
476
|
+
elsif use_schema_subset?
|
477
|
+
@subscription_object = block_given? ? lazy_load_block : new_subscription_object
|
478
|
+
add_subscription_extension_if_necessary
|
458
479
|
else
|
459
|
-
@subscription_object = new_subscription_object
|
480
|
+
@subscription_object = new_subscription_object || lazy_load_block.call
|
460
481
|
add_subscription_extension_if_necessary
|
461
|
-
add_type_and_traverse(
|
462
|
-
nil
|
482
|
+
add_type_and_traverse(@subscription_object, root: true)
|
463
483
|
end
|
484
|
+
nil
|
485
|
+
elsif @subscription_object.is_a?(Proc)
|
486
|
+
@subscription_object = @subscription_object.call
|
487
|
+
add_subscription_extension_if_necessary
|
488
|
+
@subscription_object
|
464
489
|
else
|
465
490
|
@subscription_object || find_inherited_value(:subscription)
|
466
491
|
end
|
@@ -482,7 +507,11 @@ module GraphQL
|
|
482
507
|
end
|
483
508
|
|
484
509
|
def root_types
|
485
|
-
|
510
|
+
if use_schema_subset?
|
511
|
+
[query, mutation, subscription].compact
|
512
|
+
else
|
513
|
+
@root_types
|
514
|
+
end
|
486
515
|
end
|
487
516
|
|
488
517
|
def warden_class
|
@@ -497,10 +526,39 @@ module GraphQL
|
|
497
526
|
|
498
527
|
attr_writer :warden_class
|
499
528
|
|
529
|
+
def subset_class
|
530
|
+
if defined?(@subset_class)
|
531
|
+
@subset_class
|
532
|
+
elsif superclass.respond_to?(:subset_class)
|
533
|
+
superclass.subset_class
|
534
|
+
else
|
535
|
+
GraphQL::Schema::Subset
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
attr_writer :subset_class, :use_schema_subset
|
540
|
+
|
541
|
+
def use_schema_subset?
|
542
|
+
if defined?(@use_schema_subset)
|
543
|
+
@use_schema_subset
|
544
|
+
elsif superclass.respond_to?(:use_schema_subset?)
|
545
|
+
superclass.use_schema_subset?
|
546
|
+
else
|
547
|
+
false
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
500
551
|
# @param type [Module] The type definition whose possible types you want to see
|
501
552
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
502
553
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
503
554
|
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
|
555
|
+
if use_schema_subset?
|
556
|
+
if type
|
557
|
+
return Subset.from_context(context, self).possible_types(type)
|
558
|
+
else
|
559
|
+
raise "Schema.possible_types is not implemented for `use_schema_subset?`"
|
560
|
+
end
|
561
|
+
end
|
504
562
|
if type
|
505
563
|
# TODO duck-typing `.possible_types` would probably be nicer here
|
506
564
|
if type.kind.union?
|
@@ -573,9 +631,8 @@ module GraphQL
|
|
573
631
|
end
|
574
632
|
end
|
575
633
|
|
576
|
-
def type_from_ast(ast_node, context:
|
577
|
-
|
578
|
-
GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
|
634
|
+
def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context)
|
635
|
+
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
|
579
636
|
end
|
580
637
|
|
581
638
|
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
|
@@ -769,16 +826,6 @@ module GraphQL
|
|
769
826
|
@analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
|
770
827
|
end
|
771
828
|
|
772
|
-
def using_ast_analysis?
|
773
|
-
true
|
774
|
-
end
|
775
|
-
|
776
|
-
def interpreter?
|
777
|
-
true
|
778
|
-
end
|
779
|
-
|
780
|
-
attr_writer :interpreter
|
781
|
-
|
782
829
|
def error_bubbling(new_error_bubbling = nil)
|
783
830
|
if !new_error_bubbling.nil?
|
784
831
|
warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
|
@@ -887,7 +934,7 @@ module GraphQL
|
|
887
934
|
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
888
935
|
ERR
|
889
936
|
end
|
890
|
-
add_type_and_traverse(new_orphan_types, root: false)
|
937
|
+
add_type_and_traverse(new_orphan_types, root: false) unless use_schema_subset?
|
891
938
|
own_orphan_types.concat(new_orphan_types.flatten)
|
892
939
|
end
|
893
940
|
|
@@ -1053,6 +1100,10 @@ module GraphQL
|
|
1053
1100
|
Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
|
1054
1101
|
end
|
1055
1102
|
|
1103
|
+
# Called when a type is needed by name at runtime
|
1104
|
+
def load_type(type_name, ctx)
|
1105
|
+
get_type(type_name, ctx)
|
1106
|
+
end
|
1056
1107
|
# This hook is called when an object fails an `authorized?` check.
|
1057
1108
|
# You might report to your bug tracker here, so you can correct
|
1058
1109
|
# the field resolvers not to return unauthorized objects.
|
@@ -1142,7 +1193,11 @@ module GraphQL
|
|
1142
1193
|
# @param new_directive [Class]
|
1143
1194
|
# @return void
|
1144
1195
|
def directive(new_directive)
|
1145
|
-
|
1196
|
+
if use_schema_subset?
|
1197
|
+
own_directives[new_directive.graphql_name] = new_directive
|
1198
|
+
else
|
1199
|
+
add_type_and_traverse(new_directive, root: false)
|
1200
|
+
end
|
1146
1201
|
end
|
1147
1202
|
|
1148
1203
|
def default_directives
|
@@ -1336,7 +1391,8 @@ module GraphQL
|
|
1336
1391
|
|
1337
1392
|
# @api private
|
1338
1393
|
def add_subscription_extension_if_necessary
|
1339
|
-
|
1394
|
+
# TODO: when there's a proper API for extending root types, migrat this to use it.
|
1395
|
+
if !defined?(@subscription_extension_added) && @subscription_object.is_a?(Class) && self.subscriptions
|
1340
1396
|
@subscription_extension_added = true
|
1341
1397
|
subscription.all_field_definitions.each do |field|
|
1342
1398
|
if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
|
@@ -1485,7 +1541,7 @@ module GraphQL
|
|
1485
1541
|
end
|
1486
1542
|
|
1487
1543
|
def own_references_to
|
1488
|
-
@own_references_to ||= {}.
|
1544
|
+
@own_references_to ||= {}.compare_by_identity
|
1489
1545
|
end
|
1490
1546
|
|
1491
1547
|
def non_introspection_types
|
@@ -1501,7 +1557,7 @@ module GraphQL
|
|
1501
1557
|
end
|
1502
1558
|
|
1503
1559
|
def own_possible_types
|
1504
|
-
@own_possible_types ||= {}.
|
1560
|
+
@own_possible_types ||= {}.compare_by_identity
|
1505
1561
|
end
|
1506
1562
|
|
1507
1563
|
def own_union_memberships
|