graphql 2.0.13 → 2.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +3 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
- data/lib/generators/graphql/mutation_update_generator.rb +1 -1
- data/lib/generators/graphql/relay.rb +18 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +8 -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/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/backtrace/table.rb +2 -2
- data/lib/graphql/backtrace/trace.rb +93 -0
- data/lib/graphql/backtrace/tracer.rb +1 -1
- data/lib/graphql/backtrace.rb +2 -1
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +88 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +89 -45
- data/lib/graphql/dataloader.rb +115 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
- data/lib/graphql/execution/interpreter/resolve.rb +19 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
- data/lib/graphql/execution/interpreter/runtime.rb +331 -455
- data/lib/graphql/execution/interpreter.rb +125 -61
- data/lib/graphql/execution/lazy.rb +6 -12
- data/lib/graphql/execution/lookahead.rb +124 -46
- data/lib/graphql/execution/multiplex.rb +3 -117
- data/lib/graphql/execution.rb +0 -1
- data/lib/graphql/introspection/directive_type.rb +3 -3
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +10 -13
- data/lib/graphql/introspection/type_type.rb +17 -10
- data/lib/graphql/introspection.rb +3 -2
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +75 -59
- data/lib/graphql/language/lexer.rb +358 -1506
- data/lib/graphql/language/nodes.rb +166 -93
- data/lib/graphql/language/parser.rb +795 -1953
- data/lib/graphql/language/printer.rb +340 -160
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +188 -141
- data/lib/graphql/language.rb +61 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +33 -6
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +117 -112
- data/lib/graphql/query/null_context.rb +12 -25
- data/lib/graphql/query/validation_pipeline.rb +6 -5
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +86 -30
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +29 -11
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +59 -23
- data/lib/graphql/schema/always_visible.rb +11 -0
- data/lib/graphql/schema/argument.rb +55 -26
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +56 -32
- data/lib/graphql/schema/directive/one_of.rb +24 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +15 -3
- data/lib/graphql/schema/enum.rb +35 -24
- data/lib/graphql/schema/enum_value.rb +2 -3
- data/lib/graphql/schema/field/connection_extension.rb +2 -16
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +147 -107
- data/lib/graphql/schema/field_extension.rb +1 -4
- data/lib/graphql/schema/find_inherited_value.rb +2 -7
- data/lib/graphql/schema/has_single_input_argument.rb +158 -0
- data/lib/graphql/schema/input_object.rb +47 -11
- data/lib/graphql/schema/interface.rb +15 -21
- data/lib/graphql/schema/introspection_system.rb +7 -17
- data/lib/graphql/schema/late_bound_type.rb +10 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/loader.rb +2 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
- data/lib/graphql/schema/member/build_type.rb +11 -3
- data/lib/graphql/schema/member/has_arguments.rb +170 -130
- data/lib/graphql/schema/member/has_ast_node.rb +12 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_directives.rb +81 -61
- data/lib/graphql/schema/member/has_fields.rb +100 -38
- data/lib/graphql/schema/member/has_interfaces.rb +65 -10
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +32 -6
- data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +16 -5
- data/lib/graphql/schema/printer.rb +11 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
- data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
- data/lib/graphql/schema/resolver.rb +47 -32
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/subset.rb +397 -0
- data/lib/graphql/schema/timeout.rb +25 -29
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/type_membership.rb +3 -0
- data/lib/graphql/schema/union.rb +11 -2
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +60 -0
- data/lib/graphql/schema/validator.rb +4 -2
- data/lib/graphql/schema/warden.rb +238 -93
- data/lib/graphql/schema.rb +498 -103
- data/lib/graphql/static_validation/all_rules.rb +2 -1
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
- data/lib/graphql/static_validation/literal_validator.rb +24 -7
- 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_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
- 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/one_of_input_objects_are_valid.rb +66 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
- 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 +5 -5
- 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 +5 -5
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +11 -10
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +20 -13
- data/lib/graphql/testing/helpers.rb +151 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/appoptics_trace.rb +251 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/appsignal_trace.rb +77 -0
- data/lib/graphql/tracing/data_dog_trace.rb +183 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
- data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
- data/lib/graphql/tracing/legacy_trace.rb +69 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/notifications_trace.rb +45 -0
- data/lib/graphql/tracing/platform_trace.rb +118 -0
- data/lib/graphql/tracing/platform_tracing.rb +17 -3
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing/trace.rb +76 -0
- data/lib/graphql/tracing.rb +20 -40
- data/lib/graphql/type_kinds.rb +7 -4
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
- data/lib/graphql/types/relay/node_behaviors.rb +8 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
- data/lib/graphql/types/relay.rb +0 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +27 -20
- data/readme.md +13 -3
- metadata +96 -47
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- 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 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -269
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -53
- data/lib/graphql/language/lexer.rl +0 -280
- data/lib/graphql/language/parser.y +0 -554
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
- data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -0,0 +1,397 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Subset
|
6
|
+
def initialize(query)
|
7
|
+
@query = query
|
8
|
+
@context = query.context
|
9
|
+
@schema = query.schema
|
10
|
+
@all_types = {}
|
11
|
+
@all_types_loaded = false
|
12
|
+
@unvisited_types = []
|
13
|
+
@referenced_types = Hash.new { |h, type_defn| h[type_defn] = [] }.compare_by_identity
|
14
|
+
@cached_possible_types = nil
|
15
|
+
@cached_visible = Hash.new { |h, member|
|
16
|
+
h[member] = @schema.visible?(member, @context)
|
17
|
+
}.compare_by_identity
|
18
|
+
|
19
|
+
@cached_visible_fields = Hash.new { |h, owner|
|
20
|
+
h[owner] = Hash.new do |h2, field|
|
21
|
+
h2[field] = if @cached_visible[field] &&
|
22
|
+
(ret_type = field.type.unwrap) &&
|
23
|
+
@cached_visible[ret_type] &&
|
24
|
+
reachable_type?(ret_type.graphql_name) &&
|
25
|
+
(owner == field.owner || (!owner.kind.object?) || field_on_visible_interface?(field, owner))
|
26
|
+
|
27
|
+
if !field.introspection?
|
28
|
+
# The problem is that some introspection fields may have references
|
29
|
+
# to non-custom introspection types.
|
30
|
+
# If those were added here, they'd cause a DuplicateNamesError.
|
31
|
+
# This is basically a bug -- those fields _should_ reference the custom types.
|
32
|
+
add_type(ret_type, field)
|
33
|
+
end
|
34
|
+
true
|
35
|
+
else
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end.compare_by_identity
|
39
|
+
}.compare_by_identity
|
40
|
+
|
41
|
+
@cached_visible_arguments = Hash.new do |h, arg|
|
42
|
+
h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
|
43
|
+
add_type(arg_type, arg)
|
44
|
+
true
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end.compare_by_identity
|
49
|
+
|
50
|
+
@unfiltered_pt = Hash.new do |hash, type|
|
51
|
+
hash[type] = @schema.possible_types(type)
|
52
|
+
end.compare_by_identity
|
53
|
+
end
|
54
|
+
|
55
|
+
def field_on_visible_interface?(field, owner)
|
56
|
+
ints = owner.interface_type_memberships.map(&:abstract_type)
|
57
|
+
field_name = field.graphql_name
|
58
|
+
filtered_ints = interfaces(owner)
|
59
|
+
any_interface_has_field = false
|
60
|
+
any_interface_has_visible_field = false
|
61
|
+
ints.each do |int_t|
|
62
|
+
if (_int_f_defn = int_t.get_field(field_name, @context))
|
63
|
+
any_interface_has_field = true
|
64
|
+
|
65
|
+
if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field]
|
66
|
+
any_interface_has_visible_field = true
|
67
|
+
break
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if any_interface_has_field
|
73
|
+
any_interface_has_visible_field
|
74
|
+
else
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def type(type_name)
|
80
|
+
t = if (loaded_t = @all_types[type_name])
|
81
|
+
loaded_t
|
82
|
+
elsif !@all_types_loaded
|
83
|
+
load_all_types
|
84
|
+
@all_types[type_name]
|
85
|
+
end
|
86
|
+
|
87
|
+
if t
|
88
|
+
if t.is_a?(Array)
|
89
|
+
vis_t = nil
|
90
|
+
t.each do |t_defn|
|
91
|
+
if @cached_visible[t_defn]
|
92
|
+
if vis_t.nil?
|
93
|
+
vis_t = t_defn
|
94
|
+
else
|
95
|
+
raise_duplicate_definition(vis_t, t_defn)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
vis_t
|
100
|
+
else
|
101
|
+
if t && @cached_visible[t]
|
102
|
+
t
|
103
|
+
else
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def field(owner, field_name)
|
111
|
+
f = if owner.kind.fields? && (field = owner.get_field(field_name, @context))
|
112
|
+
field
|
113
|
+
elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
|
114
|
+
entry_point_field
|
115
|
+
elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
|
116
|
+
dynamic_field
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
if f.is_a?(Array)
|
121
|
+
visible_f = nil
|
122
|
+
f.each do |f_defn|
|
123
|
+
if @cached_visible_fields[owner][f_defn]
|
124
|
+
|
125
|
+
if visible_f.nil?
|
126
|
+
visible_f = f_defn
|
127
|
+
else
|
128
|
+
raise_duplicate_definition(visible_f, f_defn)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
visible_f
|
133
|
+
else
|
134
|
+
if f && @cached_visible_fields[owner][f]
|
135
|
+
f
|
136
|
+
else
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def fields(owner)
|
143
|
+
non_duplicate_items(owner.all_field_definitions, @cached_visible_fields[owner])
|
144
|
+
end
|
145
|
+
|
146
|
+
def arguments(owner)
|
147
|
+
non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
|
148
|
+
end
|
149
|
+
|
150
|
+
def argument(owner, arg_name)
|
151
|
+
# TODO this makes a Warden.visible_entry call down the stack
|
152
|
+
# I need a non-Warden implementation
|
153
|
+
arg = owner.get_argument(arg_name, @context)
|
154
|
+
if arg.is_a?(Array)
|
155
|
+
visible_arg = nil
|
156
|
+
arg.each do |arg_defn|
|
157
|
+
if @cached_visible_arguments[arg_defn]
|
158
|
+
if arg_defn&.loads
|
159
|
+
add_type(arg_defn.loads, arg_defn)
|
160
|
+
end
|
161
|
+
if visible_arg.nil?
|
162
|
+
visible_arg = arg_defn
|
163
|
+
else
|
164
|
+
raise_duplicate_definition(visible_arg, arg_defn)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
visible_arg
|
169
|
+
else
|
170
|
+
if arg && @cached_visible_arguments[arg]
|
171
|
+
if arg&.loads
|
172
|
+
add_type(arg.loads, arg)
|
173
|
+
end
|
174
|
+
arg
|
175
|
+
else
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def possible_types(type)
|
182
|
+
@cached_possible_types ||= Hash.new do |h, type|
|
183
|
+
pt = case type.kind.name
|
184
|
+
when "INTERFACE"
|
185
|
+
# TODO this requires the global map
|
186
|
+
@unfiltered_pt[type]
|
187
|
+
when "UNION"
|
188
|
+
type.type_memberships.select { |tm| @cached_visible[tm] && @cached_visible[tm.object_type] }.map!(&:object_type)
|
189
|
+
else
|
190
|
+
[type]
|
191
|
+
end
|
192
|
+
|
193
|
+
# TODO use `select!` when possible, skip it for `[type]`
|
194
|
+
h[type] = pt.select { |t|
|
195
|
+
@cached_visible[t] && referenced?(t)
|
196
|
+
}
|
197
|
+
end.compare_by_identity
|
198
|
+
@cached_possible_types[type]
|
199
|
+
end
|
200
|
+
|
201
|
+
def interfaces(obj_or_int_type)
|
202
|
+
ints = obj_or_int_type.interface_type_memberships
|
203
|
+
.select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] }
|
204
|
+
.map!(&:abstract_type)
|
205
|
+
ints.uniq! # Remove any duplicate interfaces implemented via other interfaces
|
206
|
+
ints
|
207
|
+
end
|
208
|
+
|
209
|
+
def query_root
|
210
|
+
add_if_visible(@schema.query)
|
211
|
+
end
|
212
|
+
|
213
|
+
def mutation_root
|
214
|
+
add_if_visible(@schema.mutation)
|
215
|
+
end
|
216
|
+
|
217
|
+
def subscription_root
|
218
|
+
add_if_visible(@schema.subscription)
|
219
|
+
end
|
220
|
+
|
221
|
+
def all_types
|
222
|
+
@all_types_filtered ||= begin
|
223
|
+
load_all_types
|
224
|
+
at = []
|
225
|
+
@all_types.each do |_name, type_defn|
|
226
|
+
if possible_types(type_defn).any? || referenced?(type_defn)
|
227
|
+
at << type_defn
|
228
|
+
end
|
229
|
+
end
|
230
|
+
at
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def enum_values(owner)
|
235
|
+
values = non_duplicate_items(owner.all_enum_value_definitions, @cached_visible)
|
236
|
+
if values.size == 0
|
237
|
+
raise GraphQL::Schema::Enum::MissingValuesError.new(owner)
|
238
|
+
end
|
239
|
+
values
|
240
|
+
end
|
241
|
+
|
242
|
+
def directive_exists?(dir_name)
|
243
|
+
dir = @schema.directives[dir_name]
|
244
|
+
dir && @cached_visible[dir]
|
245
|
+
end
|
246
|
+
|
247
|
+
def directives
|
248
|
+
@schema.directives.each_value.select { |d| @cached_visible[d] }
|
249
|
+
end
|
250
|
+
|
251
|
+
def loadable?(t, _ctx)
|
252
|
+
!@all_types[t.graphql_name] # TODO make sure t is not reachable but t is visible
|
253
|
+
end
|
254
|
+
|
255
|
+
# TODO rename this to indicate that it is called with a typename
|
256
|
+
def reachable_type?(type_name)
|
257
|
+
load_all_types
|
258
|
+
!!((t = @all_types[type_name]) && referenced?(t))
|
259
|
+
end
|
260
|
+
|
261
|
+
def loaded_types
|
262
|
+
@all_types.values
|
263
|
+
end
|
264
|
+
|
265
|
+
private
|
266
|
+
|
267
|
+
def add_if_visible(t)
|
268
|
+
(t && @cached_visible[t]) ? (add_type(t, true); t) : nil
|
269
|
+
end
|
270
|
+
|
271
|
+
def add_type(t, by_member)
|
272
|
+
if t && @cached_visible[t]
|
273
|
+
n = t.graphql_name
|
274
|
+
if (prev_t = @all_types[n])
|
275
|
+
if !prev_t.equal?(t)
|
276
|
+
raise_duplicate_definition(prev_t, t)
|
277
|
+
end
|
278
|
+
false
|
279
|
+
else
|
280
|
+
@referenced_types[t] << by_member
|
281
|
+
@all_types[n] = t
|
282
|
+
@unvisited_types << t
|
283
|
+
true
|
284
|
+
end
|
285
|
+
else
|
286
|
+
false
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def non_duplicate_items(definitions, visibility_cache)
|
291
|
+
non_dups = []
|
292
|
+
definitions.each do |defn|
|
293
|
+
if visibility_cache[defn]
|
294
|
+
if (dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name })
|
295
|
+
raise_duplicate_definition(dup_defn, defn)
|
296
|
+
end
|
297
|
+
non_dups << defn
|
298
|
+
end
|
299
|
+
end
|
300
|
+
non_dups
|
301
|
+
end
|
302
|
+
|
303
|
+
def raise_duplicate_definition(first_defn, second_defn)
|
304
|
+
raise DuplicateNamesError.new(duplicated_name: first_defn.path, duplicated_definition_1: first_defn.inspect, duplicated_definition_2: second_defn.inspect)
|
305
|
+
end
|
306
|
+
|
307
|
+
def referenced?(t)
|
308
|
+
load_all_types
|
309
|
+
res = if @referenced_types[t].any? { |member| (member == true) || @cached_visible[member] }
|
310
|
+
if t.kind.abstract?
|
311
|
+
possible_types(t).any?
|
312
|
+
else
|
313
|
+
true
|
314
|
+
end
|
315
|
+
end
|
316
|
+
res
|
317
|
+
end
|
318
|
+
|
319
|
+
def load_all_types
|
320
|
+
return if @all_types_loaded
|
321
|
+
@all_types_loaded = true
|
322
|
+
schema_types = [
|
323
|
+
query_root,
|
324
|
+
mutation_root,
|
325
|
+
subscription_root,
|
326
|
+
*@schema.introspection_system.types.values,
|
327
|
+
]
|
328
|
+
|
329
|
+
# Don't include any orphan_types whose interfaces aren't visible.
|
330
|
+
@schema.orphan_types.each do |orphan_type|
|
331
|
+
if @cached_visible[orphan_type] &&
|
332
|
+
orphan_type.interface_type_memberships.any? { |tm| @cached_visible[tm] && @cached_visible[tm.abstract_type] }
|
333
|
+
schema_types << orphan_type
|
334
|
+
end
|
335
|
+
end
|
336
|
+
schema_types.compact! # TODO why is this necessary?!
|
337
|
+
schema_types.flatten! # handle multiple defns
|
338
|
+
schema_types.each { |t| add_type(t, true) }
|
339
|
+
|
340
|
+
while t = @unvisited_types.pop
|
341
|
+
# These have already been checked for `.visible?`
|
342
|
+
visit_type(t)
|
343
|
+
end
|
344
|
+
|
345
|
+
@all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
|
346
|
+
nil
|
347
|
+
end
|
348
|
+
|
349
|
+
def visit_type(type)
|
350
|
+
if type.kind.input_object?
|
351
|
+
# recurse into visible arguments
|
352
|
+
arguments(type).each do |argument|
|
353
|
+
add_type(argument.type.unwrap, argument)
|
354
|
+
end
|
355
|
+
elsif type.kind.union?
|
356
|
+
# recurse into visible possible types
|
357
|
+
type.type_memberships.each do |tm|
|
358
|
+
if @cached_visible[tm] && @cached_visible[tm.object_type]
|
359
|
+
add_type(tm.object_type, tm)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
elsif type.kind.fields?
|
363
|
+
if type.kind.object?
|
364
|
+
# recurse into visible implemented interfaces
|
365
|
+
interfaces(type).each do |interface|
|
366
|
+
add_type(interface, type)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
# recurse into visible fields
|
371
|
+
t_f = type.all_field_definitions
|
372
|
+
t_f.each do |field|
|
373
|
+
if @cached_visible[field]
|
374
|
+
field_type = field.type.unwrap
|
375
|
+
if field_type.kind.interface?
|
376
|
+
pt = @unfiltered_pt[field_type]
|
377
|
+
pt.each do |obj_type|
|
378
|
+
if @cached_visible[obj_type] &&
|
379
|
+
(tm = obj_type.interface_type_memberships.find { |tm| tm.abstract_type == field_type }) &&
|
380
|
+
@cached_visible[tm]
|
381
|
+
add_type(obj_type, tm)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
add_type(field_type, field)
|
386
|
+
|
387
|
+
# recurse into visible arguments
|
388
|
+
arguments(field).each do |argument|
|
389
|
+
add_type(argument.type.unwrap, argument)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
@@ -33,60 +33,56 @@ module GraphQL
|
|
33
33
|
# end
|
34
34
|
#
|
35
35
|
class Timeout
|
36
|
-
def self.use(schema,
|
37
|
-
|
38
|
-
schema.
|
36
|
+
def self.use(schema, max_seconds: nil)
|
37
|
+
timeout = self.new(max_seconds: max_seconds)
|
38
|
+
schema.trace_with(self::Trace, timeout: timeout)
|
39
39
|
end
|
40
40
|
|
41
|
-
# @param max_seconds [Numeric] how many seconds the query should be allowed to resolve new fields
|
42
41
|
def initialize(max_seconds:)
|
43
42
|
@max_seconds = max_seconds
|
44
43
|
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
module Trace
|
46
|
+
# @param max_seconds [Numeric] how many seconds the query should be allowed to resolve new fields
|
47
|
+
def initialize(timeout:, **rest)
|
48
|
+
@timeout = timeout
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def execute_multiplex(multiplex:)
|
53
|
+
multiplex.queries.each do |query|
|
54
|
+
timeout_duration_s = @timeout.max_seconds(query)
|
51
55
|
timeout_state = if timeout_duration_s == false
|
52
56
|
# if the method returns `false`, don't apply a timeout
|
53
57
|
false
|
54
58
|
else
|
55
59
|
now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
56
|
-
timeout_at = now + (
|
60
|
+
timeout_at = now + (timeout_duration_s * 1000)
|
57
61
|
{
|
58
62
|
timeout_at: timeout_at,
|
59
63
|
timed_out: false
|
60
64
|
}
|
61
65
|
end
|
62
|
-
query.context.namespace(
|
66
|
+
query.context.namespace(@timeout)[:state] = timeout_state
|
63
67
|
end
|
68
|
+
super
|
69
|
+
end
|
64
70
|
|
65
|
-
|
66
|
-
|
67
|
-
query_context = data[:context] || data[:query].context
|
68
|
-
timeout_state = query_context.namespace(self.class).fetch(:state)
|
71
|
+
def execute_field(query:, field:, **_rest)
|
72
|
+
timeout_state = query.context.namespace(@timeout).fetch(:state)
|
69
73
|
# If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
|
70
74
|
if timeout_state != false && Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
|
71
|
-
error =
|
72
|
-
GraphQL::Schema::Timeout::TimeoutError.new(query_context.parent_type, query_context.field)
|
73
|
-
else
|
74
|
-
field = data.fetch(:field)
|
75
|
-
GraphQL::Schema::Timeout::TimeoutError.new(field.owner, field)
|
76
|
-
end
|
77
|
-
|
75
|
+
error = GraphQL::Schema::Timeout::TimeoutError.new(field)
|
78
76
|
# Only invoke the timeout callback for the first timeout
|
79
77
|
if !timeout_state[:timed_out]
|
80
78
|
timeout_state[:timed_out] = true
|
81
|
-
handle_timeout(error,
|
79
|
+
@timeout.handle_timeout(error, query)
|
82
80
|
end
|
83
81
|
|
84
82
|
error
|
85
83
|
else
|
86
|
-
|
84
|
+
super
|
87
85
|
end
|
88
|
-
else
|
89
|
-
yield
|
90
86
|
end
|
91
87
|
end
|
92
88
|
|
@@ -94,7 +90,7 @@ module GraphQL
|
|
94
90
|
# The default implementation returns the `max_seconds:` value from installing this plugin.
|
95
91
|
#
|
96
92
|
# @param query [GraphQL::Query] The query that's about to run
|
97
|
-
# @return [
|
93
|
+
# @return [Numeric, false] The number of seconds after which to interrupt query execution and call {#handle_error}, or `false` to bypass the timeout.
|
98
94
|
def max_seconds(query)
|
99
95
|
@max_seconds
|
100
96
|
end
|
@@ -114,8 +110,8 @@ module GraphQL
|
|
114
110
|
# to take this error and raise a new one which _doesn't_ descend from {GraphQL::ExecutionError},
|
115
111
|
# such as `RuntimeError`.
|
116
112
|
class TimeoutError < GraphQL::ExecutionError
|
117
|
-
def initialize(
|
118
|
-
super("Timeout on #{
|
113
|
+
def initialize(field)
|
114
|
+
super("Timeout on #{field.path}")
|
119
115
|
end
|
120
116
|
end
|
121
117
|
end
|
@@ -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)
|
@@ -11,6 +11,9 @@ module GraphQL
|
|
11
11
|
# @return [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
|
12
12
|
attr_reader :abstract_type
|
13
13
|
|
14
|
+
# @return [Hash]
|
15
|
+
attr_reader :options
|
16
|
+
|
14
17
|
# Called when an object is hooked up to an abstract type, such as {Schema::Union.possible_types}
|
15
18
|
# or {Schema::Object.implements} (for interfaces).
|
16
19
|
#
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -10,7 +10,7 @@ module GraphQL
|
|
10
10
|
super
|
11
11
|
end
|
12
12
|
|
13
|
-
def possible_types(*types, context: GraphQL::Query::NullContext, **options)
|
13
|
+
def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options)
|
14
14
|
if types.any?
|
15
15
|
types.each do |t|
|
16
16
|
assert_valid_union_member(t)
|
@@ -70,9 +70,18 @@ module GraphQL
|
|
70
70
|
private
|
71
71
|
|
72
72
|
def assert_valid_union_member(type_defn)
|
73
|
-
|
73
|
+
case type_defn
|
74
|
+
when Class
|
75
|
+
if !type_defn.kind.object?
|
76
|
+
raise ArgumentError, "Union possible_types can only be object types (not #{type_defn.kind.name}, #{type_defn.inspect})"
|
77
|
+
end
|
78
|
+
when Module
|
74
79
|
# it's an interface type, defined as a module
|
75
80
|
raise ArgumentError, "Union possible_types can only be object types (not interface types), remove #{type_defn.graphql_name} (#{type_defn.inspect})"
|
81
|
+
when String, GraphQL::Schema::LateBoundType
|
82
|
+
# Ok - assume it will get checked later
|
83
|
+
else
|
84
|
+
raise ArgumentError, "Union possible_types can only be class-based GraphQL types (not #{type_defn.inspect} (#{type_defn.class.name}))."
|
76
85
|
end
|
77
86
|
end
|
78
87
|
end
|
@@ -0,0 +1,60 @@
|
|
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
|
+
all_errors = EMPTY_ARRAY
|
32
|
+
|
33
|
+
value.each do |subvalue|
|
34
|
+
@validators.each do |validator|
|
35
|
+
errors = validator.validate(object, context, subvalue)
|
36
|
+
if errors &&
|
37
|
+
(errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
|
38
|
+
(errors.is_a?(String))
|
39
|
+
if all_errors.frozen? # It's empty
|
40
|
+
all_errors = []
|
41
|
+
end
|
42
|
+
if errors.is_a?(String)
|
43
|
+
all_errors << errors
|
44
|
+
else
|
45
|
+
all_errors.concat(errors)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
unless all_errors.frozen?
|
52
|
+
all_errors.uniq!
|
53
|
+
end
|
54
|
+
|
55
|
+
all_errors
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -102,7 +102,7 @@ module GraphQL
|
|
102
102
|
|
103
103
|
self.all_validators = {}
|
104
104
|
|
105
|
-
include
|
105
|
+
include GraphQL::EmptyObjects
|
106
106
|
|
107
107
|
class ValidationFailedError < GraphQL::ExecutionError
|
108
108
|
attr_reader :errors
|
@@ -133,7 +133,7 @@ module GraphQL
|
|
133
133
|
if all_errors.frozen? # It's empty
|
134
134
|
all_errors = []
|
135
135
|
end
|
136
|
-
interpolation_vars = { validated: validated.graphql_name }
|
136
|
+
interpolation_vars = { validated: validated.graphql_name, value: value.inspect }
|
137
137
|
if errors.is_a?(String)
|
138
138
|
all_errors << (errors % interpolation_vars)
|
139
139
|
else
|
@@ -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)
|