graphql 2.3.7 → 2.4.5
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/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/field_usage.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +3 -3
- data/lib/graphql/analysis/visitor.rb +8 -7
- data/lib/graphql/analysis.rb +4 -4
- data/lib/graphql/autoload.rb +37 -0
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +7 -6
- data/lib/graphql/dataloader/source.rb +7 -4
- data/lib/graphql/dataloader.rb +40 -19
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +13 -9
- data/lib/graphql/execution/interpreter/runtime.rb +35 -31
- data/lib/graphql/execution/interpreter.rb +6 -4
- data/lib/graphql/execution/lookahead.rb +18 -11
- 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/invalid_null_error.rb +1 -1
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +62 -34
- data/lib/graphql/language/lexer.rb +18 -15
- data/lib/graphql/language/nodes.rb +24 -16
- data/lib/graphql/language/parser.rb +14 -1
- data/lib/graphql/language/printer.rb +31 -15
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +6 -6
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +13 -6
- data/lib/graphql/query/null_context.rb +3 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +72 -18
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -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 +2 -1
- data/lib/graphql/schema/always_visible.rb +6 -2
- data/lib/graphql/schema/argument.rb +14 -1
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +71 -23
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +102 -47
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +5 -2
- data/lib/graphql/schema/input_object.rb +90 -39
- data/lib/graphql/schema/interface.rb +22 -5
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- data/lib/graphql/schema/member/has_arguments.rb +25 -20
- data/lib/graphql/schema/member/has_directives.rb +3 -3
- data/lib/graphql/schema/member/has_fields.rb +26 -6
- data/lib/graphql/schema/member/has_interfaces.rb +4 -4
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -14
- data/lib/graphql/schema/subscription.rb +2 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +28 -4
- data/lib/graphql/schema/validator.rb +3 -1
- data/lib/graphql/schema/visibility/migration.rb +187 -0
- data/lib/graphql/schema/visibility/profile.rb +353 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +294 -0
- data/lib/graphql/schema/warden.rb +166 -16
- data/lib/graphql/schema.rb +348 -94
- 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/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
- 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/no_definitions_are_present.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 +4 -4
- 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/unique_directives_per_location.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_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
- data/lib/graphql/static_validation/validation_context.rb +18 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +6 -4
- data/lib/graphql/testing/helpers.rb +10 -6
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +81 -45
- metadata +31 -8
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
# - Arguments, via `.argument(...)` helper, which will be applied to the field.
|
9
9
|
# - Return type, via `.type(..., null: ...)`, which will be applied to the field.
|
10
10
|
# - Description, via `.description(...)`, which will be applied to the field
|
11
|
+
# - Comment, via `.comment(...)`, which will be applied to the field
|
11
12
|
# - Resolution, via `#resolve(**args)` method, which will be called to resolve the field.
|
12
13
|
# - `#object` and `#context` accessors for use during `#resolve`.
|
13
14
|
#
|
@@ -19,7 +20,7 @@ module GraphQL
|
|
19
20
|
# @see {GraphQL::Function} `Resolver` is a replacement for `GraphQL::Function`
|
20
21
|
class Resolver
|
21
22
|
include Schema::Member::GraphQLTypeNames
|
22
|
-
# Really we only need description from here, but:
|
23
|
+
# Really we only need description & comment from here, but:
|
23
24
|
extend Schema::Member::BaseDSLMethods
|
24
25
|
extend GraphQL::Schema::Member::HasArguments
|
25
26
|
extend GraphQL::Schema::Member::HasValidators
|
@@ -36,7 +37,7 @@ module GraphQL
|
|
36
37
|
@field = field
|
37
38
|
# Since this hash is constantly rebuilt, cache it for this call
|
38
39
|
@arguments_by_keyword = {}
|
39
|
-
|
40
|
+
context.types.arguments(self.class).each do |arg|
|
40
41
|
@arguments_by_keyword[arg.keyword] = arg
|
41
42
|
end
|
42
43
|
@prepared_arguments = nil
|
@@ -66,7 +67,7 @@ module GraphQL
|
|
66
67
|
# @api private
|
67
68
|
def resolve_with_support(**args)
|
68
69
|
# First call the ready? hook which may raise
|
69
|
-
raw_ready_val = if args.
|
70
|
+
raw_ready_val = if !args.empty?
|
70
71
|
ready?(**args)
|
71
72
|
else
|
72
73
|
ready?
|
@@ -87,7 +88,7 @@ module GraphQL
|
|
87
88
|
@prepared_arguments = loaded_args
|
88
89
|
Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
|
89
90
|
# Then call `authorized?`, which may raise or may return a lazy object
|
90
|
-
raw_authorized_val = if loaded_args.
|
91
|
+
raw_authorized_val = if !loaded_args.empty?
|
91
92
|
authorized?(**loaded_args)
|
92
93
|
else
|
93
94
|
authorized?
|
@@ -116,7 +117,7 @@ module GraphQL
|
|
116
117
|
|
117
118
|
# @api private {GraphQL::Schema::Mutation} uses this to clear the dataloader cache
|
118
119
|
def call_resolve(args_hash)
|
119
|
-
if args_hash.
|
120
|
+
if !args_hash.empty?
|
120
121
|
public_send(self.class.resolve_method, **args_hash)
|
121
122
|
else
|
122
123
|
public_send(self.class.resolve_method)
|
@@ -152,7 +153,7 @@ module GraphQL
|
|
152
153
|
# @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
|
153
154
|
def authorized?(**inputs)
|
154
155
|
arg_owner = @field # || self.class
|
155
|
-
args =
|
156
|
+
args = context.types.arguments(arg_owner)
|
156
157
|
authorize_arguments(args, inputs)
|
157
158
|
end
|
158
159
|
|
@@ -169,7 +170,7 @@ module GraphQL
|
|
169
170
|
private
|
170
171
|
|
171
172
|
def authorize_arguments(args, inputs)
|
172
|
-
args.
|
173
|
+
args.each do |argument|
|
173
174
|
arg_keyword = argument.keyword
|
174
175
|
if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
|
175
176
|
auth_result = argument.authorized?(self, arg_value, context)
|
@@ -182,10 +183,9 @@ module GraphQL
|
|
182
183
|
elsif auth_result == false
|
183
184
|
return auth_result
|
184
185
|
end
|
185
|
-
else
|
186
|
-
true
|
187
186
|
end
|
188
187
|
end
|
188
|
+
true
|
189
189
|
end
|
190
190
|
|
191
191
|
def load_arguments(args)
|
@@ -208,7 +208,7 @@ module GraphQL
|
|
208
208
|
end
|
209
209
|
|
210
210
|
# Avoid returning a lazy if none are needed
|
211
|
-
if prepare_lazies.
|
211
|
+
if !prepare_lazies.empty?
|
212
212
|
GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args }
|
213
213
|
else
|
214
214
|
prepared_args
|
@@ -394,7 +394,7 @@ module GraphQL
|
|
394
394
|
if superclass.respond_to?(:extensions)
|
395
395
|
s_exts = superclass.extensions
|
396
396
|
if own_exts
|
397
|
-
if s_exts.
|
397
|
+
if !s_exts.empty?
|
398
398
|
own_exts + s_exts
|
399
399
|
else
|
400
400
|
own_exts
|
@@ -409,9 +409,7 @@ module GraphQL
|
|
409
409
|
|
410
410
|
private
|
411
411
|
|
412
|
-
|
413
|
-
@own_extensions
|
414
|
-
end
|
412
|
+
attr_reader :own_extensions
|
415
413
|
end
|
416
414
|
end
|
417
415
|
end
|
@@ -55,7 +55,7 @@ module GraphQL
|
|
55
55
|
|
56
56
|
# Wrap the user-defined `#subscribe` hook
|
57
57
|
def resolve_subscribe(**args)
|
58
|
-
ret_val = args.
|
58
|
+
ret_val = !args.empty? ? subscribe(**args) : subscribe
|
59
59
|
if ret_val == :no_response
|
60
60
|
context.skip
|
61
61
|
else
|
@@ -72,7 +72,7 @@ module GraphQL
|
|
72
72
|
|
73
73
|
# Wrap the user-provided `#update` hook
|
74
74
|
def resolve_update(**args)
|
75
|
-
ret_val = args.
|
75
|
+
ret_val = !args.empty? ? update(**args) : update
|
76
76
|
if ret_val == NO_UPDATE
|
77
77
|
context.namespace(:subscriptions)[:no_update] = true
|
78
78
|
context.skip
|
@@ -5,13 +5,13 @@ module GraphQL
|
|
5
5
|
module TypeExpression
|
6
6
|
# Fetch a type from a type map by its AST specification.
|
7
7
|
# Return `nil` if not found.
|
8
|
-
# @param type_owner [#
|
8
|
+
# @param type_owner [#type] A thing for looking up types by name
|
9
9
|
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
10
10
|
# @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List]
|
11
11
|
def self.build_type(type_owner, ast_node)
|
12
12
|
case ast_node
|
13
13
|
when GraphQL::Language::Nodes::TypeName
|
14
|
-
type_owner.
|
14
|
+
type_owner.type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
|
15
15
|
when GraphQL::Language::Nodes::NonNullType
|
16
16
|
ast_inner_type = ast_node.of_type
|
17
17
|
inner_type = build_type(type_owner, ast_inner_type)
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -11,7 +11,7 @@ module GraphQL
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options)
|
14
|
-
if types.
|
14
|
+
if !types.empty?
|
15
15
|
types.each do |t|
|
16
16
|
assert_valid_union_member(t)
|
17
17
|
type_memberships << type_membership_class.new(self, t, **options)
|
@@ -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
|
@@ -35,9 +35,10 @@ module GraphQL
|
|
35
35
|
# end
|
36
36
|
#
|
37
37
|
class RequiredValidator < Validator
|
38
|
-
# @param one_of [
|
38
|
+
# @param one_of [Array<Symbol>] A list of arguments, exactly one of which is required for this field
|
39
|
+
# @param argument [Symbol] An argument that is required for this field
|
39
40
|
# @param message [String]
|
40
|
-
def initialize(one_of: nil, argument: nil, message:
|
41
|
+
def initialize(one_of: nil, argument: nil, message: nil, **default_options)
|
41
42
|
@one_of = if one_of
|
42
43
|
one_of
|
43
44
|
elsif argument
|
@@ -49,7 +50,7 @@ module GraphQL
|
|
49
50
|
super(**default_options)
|
50
51
|
end
|
51
52
|
|
52
|
-
def validate(_object,
|
53
|
+
def validate(_object, context, value)
|
53
54
|
matched_conditions = 0
|
54
55
|
|
55
56
|
if !value.nil?
|
@@ -73,9 +74,32 @@ module GraphQL
|
|
73
74
|
if matched_conditions == 1
|
74
75
|
nil # OK
|
75
76
|
else
|
76
|
-
@message
|
77
|
+
@message || build_message(context)
|
77
78
|
end
|
78
79
|
end
|
80
|
+
|
81
|
+
def build_message(context)
|
82
|
+
argument_definitions = @validated.arguments(context).values
|
83
|
+
required_names = @one_of.map do |arg_keyword|
|
84
|
+
if arg_keyword.is_a?(Array)
|
85
|
+
names = arg_keyword.map { |arg| arg_keyword_to_grapqhl_name(argument_definitions, arg) }
|
86
|
+
"(" + names.join(" and ") + ")"
|
87
|
+
else
|
88
|
+
arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if required_names.size == 1
|
93
|
+
"%{validated} must include the following argument: #{required_names.first}."
|
94
|
+
else
|
95
|
+
"%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}."
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword)
|
100
|
+
argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
|
101
|
+
argument_definition.graphql_name
|
102
|
+
end
|
79
103
|
end
|
80
104
|
end
|
81
105
|
end
|
@@ -143,7 +143,7 @@ module GraphQL
|
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
if all_errors.
|
146
|
+
if !all_errors.empty?
|
147
147
|
raise ValidationFailedError.new(errors: all_errors)
|
148
148
|
end
|
149
149
|
nil
|
@@ -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)
|
@@ -0,0 +1,187 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Visibility
|
5
|
+
# You can use this to see how {GraphQL::Schema::Warden} and {GraphQL::Schema::Visibility::Profile}
|
6
|
+
# handle `.visible?` differently in your schema.
|
7
|
+
#
|
8
|
+
# It runs the same method on both implementations and raises an error when the results diverge.
|
9
|
+
#
|
10
|
+
# To fix the error, modify your schema so that both implementations return the same thing.
|
11
|
+
# Or, open an issue on GitHub to discuss the difference.
|
12
|
+
#
|
13
|
+
# This plugin adds overhead to runtime and may cause unexpected crashes -- **don't** use it in production!
|
14
|
+
#
|
15
|
+
# This plugin adds two keys to `context` when running:
|
16
|
+
#
|
17
|
+
# - `visibility_migration_running: true`
|
18
|
+
# - For the {Schema::Warden} which it instantiates, it adds `visibility_migration_warden_running: true`.
|
19
|
+
#
|
20
|
+
# Use those keys to modify your `visible?` behavior as needed.
|
21
|
+
#
|
22
|
+
# Also, in a pinch, you can set `skip_visibility_migration_error: true` in context to turn off this behavior per-query.
|
23
|
+
# (In that case, it uses {Profile} directly.)
|
24
|
+
#
|
25
|
+
# @example Adding this plugin
|
26
|
+
#
|
27
|
+
# use GraphQL::Schema::Visibility, migration_errors: true
|
28
|
+
#
|
29
|
+
class Migration < GraphQL::Schema::Visibility::Profile
|
30
|
+
class RuntimeTypesMismatchError < GraphQL::Error
|
31
|
+
def initialize(method_called, warden_result, profile_result, method_args)
|
32
|
+
super(<<~ERR)
|
33
|
+
Mismatch in types for `##{method_called}(#{method_args.map(&:inspect).join(", ")})`:
|
34
|
+
|
35
|
+
#{compare_results(warden_result, profile_result)}
|
36
|
+
|
37
|
+
Update your `.visible?` implementation to make these implementations return the same value.
|
38
|
+
|
39
|
+
See: https://graphql-ruby.org/authorization/visibility_migration.html
|
40
|
+
ERR
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def compare_results(warden_result, profile_result)
|
45
|
+
if warden_result.is_a?(Array) && profile_result.is_a?(Array)
|
46
|
+
all_results = warden_result | profile_result
|
47
|
+
all_results.sort_by!(&:graphql_name)
|
48
|
+
|
49
|
+
entries_text = all_results.map { |entry| "#{entry.graphql_name} (#{entry})"}
|
50
|
+
width = entries_text.map(&:size).max
|
51
|
+
yes = " ✔ "
|
52
|
+
no = " "
|
53
|
+
res = "".dup
|
54
|
+
res << "#{"Result".center(width)} Warden Profile \n"
|
55
|
+
all_results.each_with_index do |entry, idx|
|
56
|
+
res << "#{entries_text[idx].ljust(width)}#{warden_result.include?(entry) ? yes : no}#{profile_result.include?(entry) ? yes : no}\n"
|
57
|
+
end
|
58
|
+
res << "\n"
|
59
|
+
else
|
60
|
+
"- Warden returned: #{humanize(warden_result)}\n\n- Visibility::Profile returned: #{humanize(profile_result)}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
def humanize(val)
|
64
|
+
case val
|
65
|
+
when Array
|
66
|
+
"#{val.size}: #{val.map { |v| humanize(v) }.sort.inspect}"
|
67
|
+
when Module
|
68
|
+
if val.respond_to?(:graphql_name)
|
69
|
+
"#{val.graphql_name} (#{val.inspect})"
|
70
|
+
else
|
71
|
+
val.inspect
|
72
|
+
end
|
73
|
+
else
|
74
|
+
val.inspect
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def initialize(context:, schema:, name: nil)
|
80
|
+
@name = name
|
81
|
+
@skip_error = context[:skip_visibility_migration_error] || context.is_a?(Query::NullContext) || context.is_a?(Hash)
|
82
|
+
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema)
|
83
|
+
if !@skip_error
|
84
|
+
context[:visibility_migration_running] = true
|
85
|
+
warden_ctx_vals = context.to_h.dup
|
86
|
+
warden_ctx_vals[:visibility_migration_warden_running] = true
|
87
|
+
if schema.const_defined?(:WardenCompatSchema, false) # don't use a defn from a superclass
|
88
|
+
warden_schema = schema.const_get(:WardenCompatSchema, false)
|
89
|
+
else
|
90
|
+
warden_schema = Class.new(schema)
|
91
|
+
warden_schema.use_visibility_profile = false
|
92
|
+
# TODO public API
|
93
|
+
warden_schema.send(:add_type_and_traverse, [warden_schema.query, warden_schema.mutation, warden_schema.subscription].compact, root: true)
|
94
|
+
warden_schema.send(:add_type_and_traverse, warden_schema.directives.values + warden_schema.orphan_types, root: false)
|
95
|
+
schema.const_set(:WardenCompatSchema, warden_schema)
|
96
|
+
end
|
97
|
+
warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals)
|
98
|
+
warden_ctx.warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
|
99
|
+
warden_ctx.warden.skip_warning = true
|
100
|
+
warden_ctx.types = @warden_types = warden_ctx.warden.visibility_profile
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def loaded_types
|
105
|
+
@profile_types.loaded_types
|
106
|
+
end
|
107
|
+
|
108
|
+
PUBLIC_PROFILE_METHODS = [
|
109
|
+
:enum_values,
|
110
|
+
:interfaces,
|
111
|
+
:all_types,
|
112
|
+
:all_types_h,
|
113
|
+
:fields,
|
114
|
+
:loadable?,
|
115
|
+
:type,
|
116
|
+
:arguments,
|
117
|
+
:argument,
|
118
|
+
:directive_exists?,
|
119
|
+
:directives,
|
120
|
+
:field,
|
121
|
+
:query_root,
|
122
|
+
:mutation_root,
|
123
|
+
:possible_types,
|
124
|
+
:subscription_root,
|
125
|
+
:reachable_type?,
|
126
|
+
:visible_enum_value?,
|
127
|
+
]
|
128
|
+
|
129
|
+
PUBLIC_PROFILE_METHODS.each do |profile_method|
|
130
|
+
define_method(profile_method) do |*args|
|
131
|
+
call_method_and_compare(profile_method, args)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def call_method_and_compare(method, args)
|
136
|
+
res_1 = @profile_types.public_send(method, *args)
|
137
|
+
if @skip_error
|
138
|
+
return res_1
|
139
|
+
end
|
140
|
+
|
141
|
+
res_2 = @warden_types.public_send(method, *args)
|
142
|
+
normalized_res_1 = res_1.is_a?(Array) ? Set.new(res_1) : res_1
|
143
|
+
normalized_res_2 = res_2.is_a?(Array) ? Set.new(res_2) : res_2
|
144
|
+
if !equivalent_schema_members?(normalized_res_1, normalized_res_2)
|
145
|
+
# Raise the errors with the orignally returned values:
|
146
|
+
err = RuntimeTypesMismatchError.new(method, res_2, res_1, args)
|
147
|
+
raise err
|
148
|
+
else
|
149
|
+
res_1
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def equivalent_schema_members?(member1, member2)
|
154
|
+
if member1.class != member2.class
|
155
|
+
return false
|
156
|
+
end
|
157
|
+
|
158
|
+
case member1
|
159
|
+
when Set
|
160
|
+
member1_array = member1.to_a.sort_by(&:graphql_name)
|
161
|
+
member2_array = member2.to_a.sort_by(&:graphql_name)
|
162
|
+
member1_array.each_with_index do |inner_member1, idx|
|
163
|
+
inner_member2 = member2_array[idx]
|
164
|
+
equivalent_schema_members?(inner_member1, inner_member2)
|
165
|
+
end
|
166
|
+
when GraphQL::Schema::Field
|
167
|
+
member1.ensure_loaded
|
168
|
+
member2.ensure_loaded
|
169
|
+
if member1.introspection? && member2.introspection?
|
170
|
+
member1.inspect == member2.inspect
|
171
|
+
else
|
172
|
+
member1 == member2
|
173
|
+
end
|
174
|
+
when Module
|
175
|
+
if member1.introspection? && member2.introspection?
|
176
|
+
member1.graphql_name == member2.graphql_name
|
177
|
+
else
|
178
|
+
member1 == member2
|
179
|
+
end
|
180
|
+
else
|
181
|
+
member1 == member2
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|