graphql 2.5.9 → 2.5.26
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/analysis.rb +20 -13
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +2 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +1 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +2 -2
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +1 -1
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +7 -7
- data/lib/graphql/dashboard.rb +11 -73
- data/lib/graphql/dataloader/active_record_association_source.rb +14 -2
- data/lib/graphql/dataloader/async_dataloader.rb +22 -11
- data/lib/graphql/dataloader/null_dataloader.rb +54 -9
- data/lib/graphql/dataloader.rb +75 -23
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/field_resolve_step.rb +631 -0
- data/lib/graphql/execution/finalize.rb +217 -0
- data/lib/graphql/execution/input_values.rb +261 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
- data/lib/graphql/execution/interpreter/resolve.rb +10 -16
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +13 -0
- data/lib/graphql/execution/interpreter/runtime.rb +28 -33
- data/lib/graphql/execution/interpreter.rb +8 -22
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/load_argument_step.rb +64 -0
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next.rb +90 -0
- data/lib/graphql/execution/prepare_object_step.rb +128 -0
- data/lib/graphql/execution/runner.rb +410 -0
- data/lib/graphql/execution/selections_step.rb +91 -0
- data/lib/graphql/execution.rb +8 -4
- data/lib/graphql/execution_error.rb +17 -10
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_null_error.rb +11 -5
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +20 -9
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +1 -0
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +11 -4
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +18 -3
- data/lib/graphql/query.rb +10 -1
- data/lib/graphql/runtime_error.rb +6 -0
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/argument.rb +17 -0
- data/lib/graphql/schema/build_from_definition.rb +15 -2
- data/lib/graphql/schema/directive.rb +45 -13
- data/lib/graphql/schema/field/connection_extension.rb +4 -37
- data/lib/graphql/schema/field/scope_extension.rb +18 -13
- data/lib/graphql/schema/field.rb +87 -48
- data/lib/graphql/schema/field_extension.rb +11 -8
- data/lib/graphql/schema/interface.rb +26 -0
- data/lib/graphql/schema/list.rb +5 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -11
- data/lib/graphql/schema/member/has_arguments.rb +43 -14
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +37 -0
- data/lib/graphql/schema/member/has_fields.rb +86 -5
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/non_null.rb +1 -1
- data/lib/graphql/schema/object.rb +1 -0
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +60 -1
- data/lib/graphql/schema/subscription.rb +0 -2
- data/lib/graphql/schema/validator/required_validator.rb +45 -5
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +140 -56
- data/lib/graphql/schema/visibility.rb +31 -18
- data/lib/graphql/schema/wrapper.rb +7 -1
- data/lib/graphql/schema.rb +108 -32
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +90 -66
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -3
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +14 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +322 -256
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +10 -7
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +27 -7
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +12 -9
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +34 -10
- data/lib/graphql/subscriptions/event.rb +1 -0
- data/lib/graphql/subscriptions.rb +36 -1
- data/lib/graphql/testing/helpers.rb +12 -9
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace.rb +70 -7
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +209 -79
- data/lib/graphql/tracing/sentry_trace.rb +3 -1
- data/lib/graphql/tracing/trace.rb +6 -0
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +8 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +4 -3
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/unauthorized_error.rb +9 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +7 -3
- metadata +21 -3
|
@@ -8,6 +8,13 @@ module GraphQL
|
|
|
8
8
|
#
|
|
9
9
|
# (This is for specifying mutually exclusive sets of arguments.)
|
|
10
10
|
#
|
|
11
|
+
# If you use {GraphQL::Schema::Visibility} to hide all the arguments in a `one_of: [..]` set,
|
|
12
|
+
# then a developer-facing {GraphQL::Error} will be raised during execution. Pass `allow_all_hidden: true` to
|
|
13
|
+
# skip validation in this case instead.
|
|
14
|
+
#
|
|
15
|
+
# This validator also implements `argument ... required: :nullable`. If an argument has `required: :nullable`
|
|
16
|
+
# but it's hidden with {GraphQL::Schema::Visibility}, then this validator doesn't run.
|
|
17
|
+
#
|
|
11
18
|
# @example Require exactly one of these arguments
|
|
12
19
|
#
|
|
13
20
|
# field :update_amount, IngredientAmount, null: false do
|
|
@@ -37,15 +44,17 @@ module GraphQL
|
|
|
37
44
|
class RequiredValidator < Validator
|
|
38
45
|
# @param one_of [Array<Symbol>] A list of arguments, exactly one of which is required for this field
|
|
39
46
|
# @param argument [Symbol] An argument that is required for this field
|
|
47
|
+
# @param allow_all_hidden [Boolean] If `true`, then this validator won't run if all the `one_of: ...` arguments have been hidden
|
|
40
48
|
# @param message [String]
|
|
41
|
-
def initialize(one_of: nil, argument: nil, message: nil, **default_options)
|
|
49
|
+
def initialize(one_of: nil, argument: nil, allow_all_hidden: nil, message: nil, **default_options)
|
|
42
50
|
@one_of = if one_of
|
|
43
51
|
one_of
|
|
44
52
|
elsif argument
|
|
45
|
-
[argument]
|
|
53
|
+
[ argument ]
|
|
46
54
|
else
|
|
47
55
|
raise ArgumentError, "`one_of:` or `argument:` must be given in `validates required: {...}`"
|
|
48
56
|
end
|
|
57
|
+
@allow_all_hidden = allow_all_hidden.nil? ? !!argument : allow_all_hidden
|
|
49
58
|
@message = message
|
|
50
59
|
super(**default_options)
|
|
51
60
|
end
|
|
@@ -54,10 +63,17 @@ module GraphQL
|
|
|
54
63
|
fully_matched_conditions = 0
|
|
55
64
|
partially_matched_conditions = 0
|
|
56
65
|
|
|
66
|
+
visible_keywords = context.types.arguments(@validated).map(&:keyword)
|
|
67
|
+
no_visible_conditions = true
|
|
68
|
+
|
|
57
69
|
if !value.nil?
|
|
58
70
|
@one_of.each do |one_of_condition|
|
|
59
71
|
case one_of_condition
|
|
60
72
|
when Symbol
|
|
73
|
+
if no_visible_conditions && visible_keywords.include?(one_of_condition)
|
|
74
|
+
no_visible_conditions = false
|
|
75
|
+
end
|
|
76
|
+
|
|
61
77
|
if value.key?(one_of_condition)
|
|
62
78
|
fully_matched_conditions += 1
|
|
63
79
|
end
|
|
@@ -66,6 +82,9 @@ module GraphQL
|
|
|
66
82
|
full_match = true
|
|
67
83
|
|
|
68
84
|
one_of_condition.each do |k|
|
|
85
|
+
if no_visible_conditions && visible_keywords.include?(k)
|
|
86
|
+
no_visible_conditions = false
|
|
87
|
+
end
|
|
69
88
|
if value.key?(k)
|
|
70
89
|
any_match = true
|
|
71
90
|
else
|
|
@@ -88,6 +107,18 @@ module GraphQL
|
|
|
88
107
|
end
|
|
89
108
|
end
|
|
90
109
|
|
|
110
|
+
if no_visible_conditions
|
|
111
|
+
if @allow_all_hidden
|
|
112
|
+
return nil
|
|
113
|
+
else
|
|
114
|
+
raise GraphQL::Error, <<~ERR
|
|
115
|
+
#{@validated.path} validates `required: ...` but all required arguments were hidden.
|
|
116
|
+
|
|
117
|
+
Update your schema definition to allow the client to see some fields or skip validation by adding `required: { ..., allow_all_hidden: true }`
|
|
118
|
+
ERR
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
91
122
|
if fully_matched_conditions == 1 && partially_matched_conditions == 0
|
|
92
123
|
nil # OK
|
|
93
124
|
else
|
|
@@ -96,17 +127,26 @@ module GraphQL
|
|
|
96
127
|
end
|
|
97
128
|
|
|
98
129
|
def build_message(context)
|
|
99
|
-
argument_definitions =
|
|
130
|
+
argument_definitions = context.types.arguments(@validated)
|
|
131
|
+
|
|
100
132
|
required_names = @one_of.map do |arg_keyword|
|
|
101
133
|
if arg_keyword.is_a?(Array)
|
|
102
134
|
names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
|
|
135
|
+
names.compact! # hidden arguments are `nil`
|
|
103
136
|
"(" + names.join(" and ") + ")"
|
|
104
137
|
else
|
|
105
138
|
arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
|
106
139
|
end
|
|
107
140
|
end
|
|
141
|
+
required_names.compact! # remove entries for hidden arguments
|
|
142
|
+
|
|
108
143
|
|
|
109
|
-
|
|
144
|
+
case required_names.size
|
|
145
|
+
when 0
|
|
146
|
+
# The required definitions were hidden from the client.
|
|
147
|
+
# Another option here would be to raise an error in the application....
|
|
148
|
+
"%{validated} is missing a required argument."
|
|
149
|
+
when 1
|
|
110
150
|
"%{validated} must include the following argument: #{required_names.first}."
|
|
111
151
|
else
|
|
112
152
|
"%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}."
|
|
@@ -115,7 +155,7 @@ module GraphQL
|
|
|
115
155
|
|
|
116
156
|
def arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
|
117
157
|
argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
|
|
118
|
-
argument_definition
|
|
158
|
+
argument_definition&.graphql_name
|
|
119
159
|
end
|
|
120
160
|
end
|
|
121
161
|
end
|
|
@@ -76,10 +76,10 @@ module GraphQL
|
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
def initialize(context:, schema:, name: nil)
|
|
79
|
+
def initialize(context:, schema:, name: nil, visibility:)
|
|
80
80
|
@name = name
|
|
81
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)
|
|
82
|
+
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema, visibility: visibility)
|
|
83
83
|
if !@skip_error
|
|
84
84
|
context[:visibility_migration_running] = true
|
|
85
85
|
warden_ctx_vals = context.to_h.dup
|
|
@@ -31,10 +31,40 @@ module GraphQL
|
|
|
31
31
|
# @return [Symbol, nil]
|
|
32
32
|
attr_reader :name
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def freeze
|
|
35
|
+
@cached_visible.default_proc = nil
|
|
36
|
+
@cached_visible_fields.default_proc = nil
|
|
37
|
+
@cached_visible_fields.each do |type, fields|
|
|
38
|
+
fields.default_proc = nil
|
|
39
|
+
end
|
|
40
|
+
@cached_visible_arguments.default_proc = nil
|
|
41
|
+
@cached_visible_arguments.each do |type, fields|
|
|
42
|
+
fields.default_proc = nil
|
|
43
|
+
end
|
|
44
|
+
@cached_parent_fields.default_proc = nil
|
|
45
|
+
@cached_parent_fields.each do |type, fields|
|
|
46
|
+
fields.default_proc = nil
|
|
47
|
+
end
|
|
48
|
+
@cached_parent_arguments.default_proc = nil
|
|
49
|
+
@cached_parent_arguments.each do |type, args|
|
|
50
|
+
args.default_proc = nil
|
|
51
|
+
end
|
|
52
|
+
@cached_possible_types.default_proc = nil
|
|
53
|
+
@cached_enum_values.default_proc = nil
|
|
54
|
+
@cached_fields.default_proc = nil
|
|
55
|
+
@cached_arguments.default_proc = nil
|
|
56
|
+
@loadable_possible_types.default_proc = nil
|
|
57
|
+
@cached_field_result.default_proc = nil
|
|
58
|
+
@cached_field_result.each { |_, h| h.default_proc = nil }
|
|
59
|
+
@cached_type_result.default_proc = nil
|
|
60
|
+
super
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def initialize(name: nil, context:, schema:, visibility:)
|
|
35
64
|
@name = name
|
|
36
65
|
@context = context
|
|
37
66
|
@schema = schema
|
|
67
|
+
@visibility = visibility
|
|
38
68
|
@all_types = {}
|
|
39
69
|
@all_types_loaded = false
|
|
40
70
|
@unvisited_types = []
|
|
@@ -95,6 +125,14 @@ module GraphQL
|
|
|
95
125
|
end.compare_by_identity
|
|
96
126
|
|
|
97
127
|
@loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
|
|
128
|
+
|
|
129
|
+
# Combined cache for field(owner, field_name) — avoids repeated kind check + parent lookup + visibility check
|
|
130
|
+
@cached_field_result = Hash.new { |h, owner|
|
|
131
|
+
h[owner] = Hash.new { |h2, field_name| h2[field_name] = compute_field(owner, field_name) }
|
|
132
|
+
}.compare_by_identity
|
|
133
|
+
|
|
134
|
+
# Cache for type(type_name) — avoids repeated get_type + visibility + referenced? checks
|
|
135
|
+
@cached_type_result = Hash.new { |h, type_name| h[type_name] = compute_type(type_name) }
|
|
98
136
|
end
|
|
99
137
|
|
|
100
138
|
def field_on_visible_interface?(field, owner)
|
|
@@ -122,58 +160,11 @@ module GraphQL
|
|
|
122
160
|
end
|
|
123
161
|
|
|
124
162
|
def type(type_name)
|
|
125
|
-
|
|
126
|
-
if t
|
|
127
|
-
if t.is_a?(Array)
|
|
128
|
-
vis_t = nil
|
|
129
|
-
t.each do |t_defn|
|
|
130
|
-
if @cached_visible[t_defn] && referenced?(t_defn)
|
|
131
|
-
if vis_t.nil?
|
|
132
|
-
vis_t = t_defn
|
|
133
|
-
else
|
|
134
|
-
raise_duplicate_definition(vis_t, t_defn)
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
vis_t
|
|
139
|
-
else
|
|
140
|
-
if t && @cached_visible[t] && referenced?(t)
|
|
141
|
-
t
|
|
142
|
-
else
|
|
143
|
-
nil
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
end
|
|
163
|
+
@cached_type_result[type_name]
|
|
147
164
|
end
|
|
148
165
|
|
|
149
166
|
def field(owner, field_name)
|
|
150
|
-
|
|
151
|
-
field
|
|
152
|
-
elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
|
|
153
|
-
entry_point_field
|
|
154
|
-
elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
|
|
155
|
-
dynamic_field
|
|
156
|
-
else
|
|
157
|
-
nil
|
|
158
|
-
end
|
|
159
|
-
if f.is_a?(Array)
|
|
160
|
-
visible_f = nil
|
|
161
|
-
f.each do |f_defn|
|
|
162
|
-
if @cached_visible_fields[owner][f_defn]
|
|
163
|
-
|
|
164
|
-
if visible_f.nil?
|
|
165
|
-
visible_f = f_defn
|
|
166
|
-
else
|
|
167
|
-
raise_duplicate_definition(visible_f, f_defn)
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
visible_f&.ensure_loaded
|
|
172
|
-
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
|
|
173
|
-
f
|
|
174
|
-
else
|
|
175
|
-
nil
|
|
176
|
-
end
|
|
167
|
+
@cached_field_result[owner][field_name]
|
|
177
168
|
end
|
|
178
169
|
|
|
179
170
|
def fields(owner)
|
|
@@ -250,8 +241,8 @@ module GraphQL
|
|
|
250
241
|
end
|
|
251
242
|
|
|
252
243
|
def directives
|
|
253
|
-
@all_directives ||= @
|
|
254
|
-
@cached_visible[dir] && @
|
|
244
|
+
@all_directives ||= @visibility.all_directives.select { |dir|
|
|
245
|
+
@cached_visible[dir] && @visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) }
|
|
255
246
|
}
|
|
256
247
|
end
|
|
257
248
|
|
|
@@ -276,8 +267,99 @@ module GraphQL
|
|
|
276
267
|
@cached_visible[enum_value]
|
|
277
268
|
end
|
|
278
269
|
|
|
270
|
+
def preload
|
|
271
|
+
load_all_types
|
|
272
|
+
@all_types.each do |type_name, type_defn|
|
|
273
|
+
type(type_name)
|
|
274
|
+
if type_defn.kind.fields?
|
|
275
|
+
fields(type_defn).each do |f|
|
|
276
|
+
field(type_defn, f.graphql_name)
|
|
277
|
+
arguments(f).each do |arg|
|
|
278
|
+
argument(f, arg.graphql_name)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
@schema.introspection_system.dynamic_fields.each do |f|
|
|
282
|
+
field(type_defn, f.graphql_name)
|
|
283
|
+
end
|
|
284
|
+
elsif type_defn.kind.input_object?
|
|
285
|
+
arguments(type_defn).each do |arg|
|
|
286
|
+
argument(type_defn, arg.graphql_name)
|
|
287
|
+
end
|
|
288
|
+
elsif type_defn.kind.enum?
|
|
289
|
+
enum_values(type_defn)
|
|
290
|
+
end
|
|
291
|
+
# Lots more to do here
|
|
292
|
+
end
|
|
293
|
+
@schema.introspection_system.entry_points.each do |f|
|
|
294
|
+
arguments(f).each do |arg|
|
|
295
|
+
argument(f, arg.graphql_name)
|
|
296
|
+
end
|
|
297
|
+
field(@schema.query, f.graphql_name)
|
|
298
|
+
end
|
|
299
|
+
@schema.introspection_system.dynamic_fields.each do |f|
|
|
300
|
+
arguments(f).each do |arg|
|
|
301
|
+
argument(f, arg.graphql_name)
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
end
|
|
306
|
+
|
|
279
307
|
private
|
|
280
308
|
|
|
309
|
+
def compute_type(type_name)
|
|
310
|
+
t = @visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
|
311
|
+
if t
|
|
312
|
+
if t.is_a?(Array)
|
|
313
|
+
vis_t = nil
|
|
314
|
+
t.each do |t_defn|
|
|
315
|
+
if @cached_visible[t_defn] && referenced?(t_defn)
|
|
316
|
+
if vis_t.nil?
|
|
317
|
+
vis_t = t_defn
|
|
318
|
+
else
|
|
319
|
+
raise_duplicate_definition(vis_t, t_defn)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
vis_t
|
|
324
|
+
else
|
|
325
|
+
if t && @cached_visible[t] && referenced?(t)
|
|
326
|
+
t
|
|
327
|
+
else
|
|
328
|
+
nil
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def compute_field(owner, field_name)
|
|
335
|
+
f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
|
|
336
|
+
field
|
|
337
|
+
elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
|
|
338
|
+
entry_point_field
|
|
339
|
+
elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
|
|
340
|
+
dynamic_field
|
|
341
|
+
else
|
|
342
|
+
nil
|
|
343
|
+
end
|
|
344
|
+
if f.is_a?(Array)
|
|
345
|
+
visible_f = nil
|
|
346
|
+
f.each do |f_defn|
|
|
347
|
+
if @cached_visible_fields[owner][f_defn]
|
|
348
|
+
if visible_f.nil?
|
|
349
|
+
visible_f = f_defn
|
|
350
|
+
else
|
|
351
|
+
raise_duplicate_definition(visible_f, f_defn)
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
visible_f&.ensure_loaded
|
|
356
|
+
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
|
|
357
|
+
f
|
|
358
|
+
else
|
|
359
|
+
nil
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
281
363
|
def non_duplicate_items(definitions, visibility_cache)
|
|
282
364
|
non_dups = []
|
|
283
365
|
names = Set.new
|
|
@@ -322,7 +404,7 @@ module GraphQL
|
|
|
322
404
|
end
|
|
323
405
|
|
|
324
406
|
def referenced?(type_defn)
|
|
325
|
-
@
|
|
407
|
+
@visibility.all_references[type_defn].any? do |ref|
|
|
326
408
|
case ref
|
|
327
409
|
when GraphQL::Schema::Argument
|
|
328
410
|
@cached_visible_arguments[ref.owner][ref]
|
|
@@ -340,9 +422,11 @@ module GraphQL
|
|
|
340
422
|
case type.kind.name
|
|
341
423
|
when "INTERFACE"
|
|
342
424
|
pts = []
|
|
343
|
-
@
|
|
344
|
-
if
|
|
345
|
-
|
|
425
|
+
@visibility.all_interface_type_memberships[type].each do |impl_type, type_memberships|
|
|
426
|
+
if impl_type.kind.object? && referenced?(impl_type) && @cached_visible[impl_type]
|
|
427
|
+
if type_memberships.any? { |itm| @cached_visible[itm] }
|
|
428
|
+
pts << impl_type
|
|
429
|
+
end
|
|
346
430
|
end
|
|
347
431
|
end
|
|
348
432
|
pts
|
|
@@ -10,17 +10,14 @@ module GraphQL
|
|
|
10
10
|
class Visibility
|
|
11
11
|
# @param schema [Class<GraphQL::Schema>]
|
|
12
12
|
# @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
|
|
13
|
-
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
|
|
13
|
+
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?` and `Rails.env.staging?`)
|
|
14
14
|
# @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
|
|
15
|
-
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? Rails.env.production? : nil), migration_errors: false)
|
|
15
|
+
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? (Rails.env.production? || Rails.env.staging?) : nil), migration_errors: false)
|
|
16
16
|
profiles&.each { |name, ctx|
|
|
17
17
|
ctx[:visibility_profile] = name
|
|
18
18
|
ctx.freeze
|
|
19
19
|
}
|
|
20
20
|
schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
|
|
21
|
-
if preload
|
|
22
|
-
schema.visibility.preload
|
|
23
|
-
end
|
|
24
21
|
end
|
|
25
22
|
|
|
26
23
|
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
|
|
@@ -43,6 +40,17 @@ module GraphQL
|
|
|
43
40
|
@types = nil
|
|
44
41
|
@all_references = nil
|
|
45
42
|
@loaded_all = false
|
|
43
|
+
if preload
|
|
44
|
+
self.preload
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def freeze
|
|
49
|
+
load_all
|
|
50
|
+
@visit = true
|
|
51
|
+
@interface_type_memberships.default_proc = nil
|
|
52
|
+
@all_references.default_proc = nil
|
|
53
|
+
super
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
def all_directives
|
|
@@ -65,6 +73,8 @@ module GraphQL
|
|
|
65
73
|
@types[type_name]
|
|
66
74
|
end
|
|
67
75
|
|
|
76
|
+
attr_accessor :types
|
|
77
|
+
|
|
68
78
|
def preload?
|
|
69
79
|
@preload
|
|
70
80
|
end
|
|
@@ -86,7 +96,7 @@ module GraphQL
|
|
|
86
96
|
ensure_all_loaded(types_to_visit)
|
|
87
97
|
@profiles.each do |profile_name, example_ctx|
|
|
88
98
|
prof = profile_for(example_ctx)
|
|
89
|
-
prof.
|
|
99
|
+
prof.preload
|
|
90
100
|
end
|
|
91
101
|
end
|
|
92
102
|
|
|
@@ -153,12 +163,12 @@ module GraphQL
|
|
|
153
163
|
visibility_profile = context[:visibility_profile]
|
|
154
164
|
if @profiles.include?(visibility_profile)
|
|
155
165
|
profile_ctx = @profiles[visibility_profile]
|
|
156
|
-
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema)
|
|
166
|
+
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema, visibility: self)
|
|
157
167
|
elsif @dynamic
|
|
158
168
|
if context.is_a?(Query::NullContext)
|
|
159
169
|
top_level_profile
|
|
160
170
|
else
|
|
161
|
-
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
|
171
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema, visibility: self)
|
|
162
172
|
end
|
|
163
173
|
elsif !context.key?(:visibility_profile)
|
|
164
174
|
raise ArgumentError, "#{@schema} expects a visibility profile, but `visibility_profile:` wasn't passed. Provide a `visibility_profile:` value or add `dynamic: true` to your visibility configuration."
|
|
@@ -168,7 +178,7 @@ module GraphQL
|
|
|
168
178
|
elsif context.is_a?(Query::NullContext)
|
|
169
179
|
top_level_profile
|
|
170
180
|
else
|
|
171
|
-
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
|
181
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema, visibility: self)
|
|
172
182
|
end
|
|
173
183
|
end
|
|
174
184
|
|
|
@@ -181,7 +191,7 @@ module GraphQL
|
|
|
181
191
|
if refresh
|
|
182
192
|
@top_level_profile = nil
|
|
183
193
|
end
|
|
184
|
-
@top_level_profile ||= @schema.visibility_profile_class.new(context:
|
|
194
|
+
@top_level_profile ||= @schema.visibility_profile_class.new(context: @schema.null_context, schema: @schema, visibility: self)
|
|
185
195
|
end
|
|
186
196
|
|
|
187
197
|
private
|
|
@@ -202,7 +212,11 @@ module GraphQL
|
|
|
202
212
|
def load_all(types: nil)
|
|
203
213
|
if @visit.nil?
|
|
204
214
|
# Set up the visit system
|
|
205
|
-
@interface_type_memberships = Hash.new { |h, interface_type|
|
|
215
|
+
@interface_type_memberships = Hash.new { |h, interface_type|
|
|
216
|
+
h[interface_type] = Hash.new { |h2, obj_type|
|
|
217
|
+
h2[obj_type] = []
|
|
218
|
+
}.compare_by_identity
|
|
219
|
+
}.compare_by_identity
|
|
206
220
|
@directives = []
|
|
207
221
|
@types = {} # String => Module
|
|
208
222
|
@all_references = Hash.new { |h, member| h[member] = Set.new.compare_by_identity }.compare_by_identity
|
|
@@ -227,7 +241,7 @@ module GraphQL
|
|
|
227
241
|
@all_references[itm.abstract_type] << member
|
|
228
242
|
# `itm.object_type` may not actually be `member` if this implementation
|
|
229
243
|
# is inherited from a superclass
|
|
230
|
-
@interface_type_memberships[itm.abstract_type] <<
|
|
244
|
+
@interface_type_memberships[itm.abstract_type][member] << itm
|
|
231
245
|
end
|
|
232
246
|
elsif member < GraphQL::Schema::Union
|
|
233
247
|
@unions_for_references << member
|
|
@@ -276,12 +290,11 @@ module GraphQL
|
|
|
276
290
|
|
|
277
291
|
# TODO: somehow don't iterate over all these,
|
|
278
292
|
# only the ones that may have been modified
|
|
279
|
-
@interface_type_memberships.each do |int_type,
|
|
280
|
-
|
|
281
|
-
if !
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
@all_references[impl_type] |= referers
|
|
293
|
+
@interface_type_memberships.each do |int_type, obj_type_memberships|
|
|
294
|
+
referrers = @all_references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) }
|
|
295
|
+
if !referrers.empty?
|
|
296
|
+
obj_type_memberships.each_key do |impl_type|
|
|
297
|
+
@all_references[impl_type] |= referrers
|
|
285
298
|
end
|
|
286
299
|
end
|
|
287
300
|
end
|