graphql 1.9.17 → 1.11.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +18 -2
- data/lib/generators/graphql/install_generator.rb +27 -0
- data/lib/generators/graphql/object_generator.rb +52 -8
- data/lib/generators/graphql/templates/base_argument.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_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -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/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +14 -10
- data/lib/generators/graphql/templates/interface.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/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/object.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +10 -0
- data/lib/generators/graphql/templates/union.erb +3 -1
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +178 -67
- data/lib/graphql/analysis/ast/visitor.rb +3 -3
- data/lib/graphql/analysis/ast.rb +12 -11
- data/lib/graphql/argument.rb +10 -38
- data/lib/graphql/backtrace/table.rb +10 -2
- data/lib/graphql/backtrace/tracer.rb +2 -1
- data/lib/graphql/base_type.rb +4 -0
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
- data/lib/graphql/define/assign_enum_value.rb +1 -1
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/define/assign_object_field.rb +3 -3
- data/lib/graphql/define/defined_object_proxy.rb +3 -0
- data/lib/graphql/define/instance_definable.rb +18 -108
- data/lib/graphql/directive/deprecated_directive.rb +1 -12
- data/lib/graphql/directive.rb +8 -1
- data/lib/graphql/enum_type.rb +5 -71
- data/lib/graphql/execution/directive_checks.rb +2 -2
- data/lib/graphql/execution/errors.rb +2 -3
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/instrumentation.rb +1 -1
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +51 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +79 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
- data/lib/graphql/execution/interpreter/runtime.rb +227 -254
- data/lib/graphql/execution/interpreter.rb +34 -11
- data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
- data/lib/graphql/execution/lookahead.rb +39 -114
- data/lib/graphql/execution/multiplex.rb +14 -5
- data/lib/graphql/field.rb +14 -118
- data/lib/graphql/filter.rb +1 -1
- data/lib/graphql/function.rb +1 -30
- data/lib/graphql/input_object_type.rb +6 -24
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/interface_type.rb +7 -23
- data/lib/graphql/internal_representation/scope.rb +2 -2
- data/lib/graphql/internal_representation/visit.rb +2 -2
- data/lib/graphql/introspection/base_object.rb +2 -5
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +7 -7
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +33 -9
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/schema_type.rb +4 -9
- data/lib/graphql/introspection/type_type.rb +11 -7
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/invalid_null_error.rb +18 -0
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/definition_slice.rb +21 -10
- data/lib/graphql/language/document_from_schema_definition.rb +89 -64
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +52 -91
- data/lib/graphql/language/parser.rb +719 -717
- data/lib/graphql/language/parser.y +104 -98
- data/lib/graphql/language/printer.rb +1 -1
- data/lib/graphql/language/sanitized_printer.rb +222 -0
- data/lib/graphql/language/visitor.rb +2 -2
- data/lib/graphql/language.rb +2 -1
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/non_null_type.rb +0 -10
- data/lib/graphql/object_type.rb +45 -56
- data/lib/graphql/pagination/active_record_relation_connection.rb +41 -0
- data/lib/graphql/pagination/array_connection.rb +77 -0
- data/lib/graphql/pagination/connection.rb +208 -0
- data/lib/graphql/pagination/connections.rb +145 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
- data/lib/graphql/pagination/relation_connection.rb +185 -0
- data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
- data/lib/graphql/pagination.rb +6 -0
- data/lib/graphql/query/arguments.rb +4 -2
- data/lib/graphql/query/context.rb +36 -9
- data/lib/graphql/query/fingerprint.rb +26 -0
- data/lib/graphql/query/input_validation_result.rb +23 -6
- data/lib/graphql/query/literal_input.rb +30 -10
- data/lib/graphql/query/null_context.rb +5 -1
- data/lib/graphql/query/validation_pipeline.rb +4 -1
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +16 -7
- data/lib/graphql/query.rb +64 -15
- data/lib/graphql/rake_task/validate.rb +3 -0
- data/lib/graphql/rake_task.rb +9 -9
- data/lib/graphql/relay/array_connection.rb +10 -12
- data/lib/graphql/relay/base_connection.rb +23 -13
- data/lib/graphql/relay/connection_type.rb +2 -1
- data/lib/graphql/relay/edge_type.rb +1 -0
- data/lib/graphql/relay/edges_instrumentation.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -86
- data/lib/graphql/relay/node.rb +2 -2
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/relay/relation_connection.rb +8 -10
- data/lib/graphql/scalar_type.rb +15 -59
- data/lib/graphql/schema/argument.rb +113 -11
- data/lib/graphql/schema/base_64_encoder.rb +2 -0
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
- data/lib/graphql/schema/build_from_definition.rb +212 -190
- data/lib/graphql/schema/built_in_types.rb +5 -5
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive.rb +34 -3
- data/lib/graphql/schema/enum.rb +52 -4
- data/lib/graphql/schema/enum_value.rb +6 -1
- data/lib/graphql/schema/field/connection_extension.rb +44 -20
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +200 -129
- data/lib/graphql/schema/find_inherited_value.rb +13 -0
- data/lib/graphql/schema/finder.rb +13 -11
- data/lib/graphql/schema/input_object.rb +131 -22
- data/lib/graphql/schema/interface.rb +26 -8
- data/lib/graphql/schema/introspection_system.rb +108 -37
- data/lib/graphql/schema/late_bound_type.rb +3 -2
- data/lib/graphql/schema/list.rb +47 -0
- data/lib/graphql/schema/loader.rb +134 -96
- data/lib/graphql/schema/member/base_dsl_methods.rb +29 -12
- data/lib/graphql/schema/member/build_type.rb +19 -5
- data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
- data/lib/graphql/schema/member/has_arguments.rb +105 -5
- data/lib/graphql/schema/member/has_ast_node.rb +20 -0
- data/lib/graphql/schema/member/has_fields.rb +20 -10
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/mutation.rb +5 -1
- data/lib/graphql/schema/non_null.rb +30 -0
- data/lib/graphql/schema/object.rb +65 -12
- data/lib/graphql/schema/possible_types.rb +9 -4
- data/lib/graphql/schema/printer.rb +0 -15
- data/lib/graphql/schema/relay_classic_mutation.rb +5 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +5 -2
- data/lib/graphql/schema/resolver.rb +26 -18
- data/lib/graphql/schema/scalar.rb +27 -3
- data/lib/graphql/schema/subscription.rb +8 -18
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +21 -13
- data/lib/graphql/schema/type_membership.rb +2 -2
- data/lib/graphql/schema/union.rb +37 -3
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +10 -2
- data/lib/graphql/schema/warden.rb +115 -29
- data/lib/graphql/schema.rb +903 -195
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +10 -6
- data/lib/graphql/static_validation/literal_validator.rb +52 -27
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +43 -83
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +33 -25
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -5
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
- data/lib/graphql/static_validation/type_stack.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +30 -8
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +89 -19
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
- data/lib/graphql/subscriptions/event.rb +23 -5
- data/lib/graphql/subscriptions/instrumentation.rb +10 -5
- data/lib/graphql/subscriptions/serialize.rb +22 -4
- data/lib/graphql/subscriptions/subscription_root.rb +15 -5
- data/lib/graphql/subscriptions.rb +108 -35
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
- data/lib/graphql/tracing/appoptics_tracing.rb +171 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
- data/lib/graphql/tracing/platform_tracing.rb +53 -9
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
- data/lib/graphql/tracing/scout_tracing.rb +19 -0
- data/lib/graphql/tracing/skylight_tracing.rb +8 -0
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/tracing.rb +14 -34
- data/lib/graphql/types/big_int.rb +1 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/iso_8601_date.rb +3 -3
- data/lib/graphql/types/iso_8601_date_time.rb +25 -10
- data/lib/graphql/types/relay/base_connection.rb +11 -7
- data/lib/graphql/types/relay/base_edge.rb +2 -1
- data/lib/graphql/types/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/union_type.rb +13 -28
- data/lib/graphql/unresolved_type_error.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +31 -6
- data/readme.md +1 -1
- metadata +34 -9
- data/lib/graphql/literal_validation_error.rb +0 -6
@@ -0,0 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Language
|
4
|
+
# A custom printer used to print sanitized queries. It inlines provided variables
|
5
|
+
# within the query for facilitate logging and analysis of queries.
|
6
|
+
#
|
7
|
+
# The printer returns `nil` if the query is invalid.
|
8
|
+
#
|
9
|
+
# Since the GraphQL Ruby AST for a GraphQL query doesnt contain any reference
|
10
|
+
# on the type of fields or arguments, we have to track the current object, field
|
11
|
+
# and input type while printing the query.
|
12
|
+
#
|
13
|
+
# @example Printing a scrubbed string
|
14
|
+
# printer = QueryPrinter.new(query)
|
15
|
+
# puts printer.sanitized_query_string
|
16
|
+
#
|
17
|
+
# @see {Query#sanitized_query_string}
|
18
|
+
class SanitizedPrinter < GraphQL::Language::Printer
|
19
|
+
|
20
|
+
REDACTED = "\"<REDACTED>\""
|
21
|
+
|
22
|
+
def initialize(query, inline_variables: true)
|
23
|
+
@query = query
|
24
|
+
@current_type = nil
|
25
|
+
@current_field = nil
|
26
|
+
@current_input_type = nil
|
27
|
+
@inline_variables = inline_variables
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String, nil] A scrubbed query string, if the query was valid.
|
31
|
+
def sanitized_query_string
|
32
|
+
if query.valid?
|
33
|
+
print(query.document)
|
34
|
+
else
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def print_node(node, indent: "")
|
40
|
+
case node
|
41
|
+
when FalseClass, Float, Integer, String, TrueClass
|
42
|
+
if @current_argument && redact_argument_value?(@current_argument, node)
|
43
|
+
redacted_argument_value(@current_argument)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
when Array
|
48
|
+
old_input_type = @current_input_type
|
49
|
+
if @current_input_type && @current_input_type.list?
|
50
|
+
@current_input_type = @current_input_type.of_type
|
51
|
+
@current_input_type = @current_input_type.of_type if @current_input_type.non_null?
|
52
|
+
end
|
53
|
+
|
54
|
+
res = super
|
55
|
+
@current_input_type = old_input_type
|
56
|
+
res
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Indicates whether or not to redact non-null values for the given argument. Defaults to redacting all strings
|
63
|
+
# arguments but this can be customized by subclasses.
|
64
|
+
def redact_argument_value?(argument, value)
|
65
|
+
# Default to redacting any strings or custom scalars encoded as strings
|
66
|
+
type = argument.type.unwrap
|
67
|
+
value.is_a?(String) && type.kind.scalar? && (type.graphql_name == "String" || !type.default_scalar?)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the value to use for redacted versions of the given argument. Defaults to the
|
71
|
+
# string "<REDACTED>".
|
72
|
+
def redacted_argument_value(argument)
|
73
|
+
REDACTED
|
74
|
+
end
|
75
|
+
|
76
|
+
def print_argument(argument)
|
77
|
+
# We won't have type information if we're recursing into a custom scalar
|
78
|
+
return super if @current_input_type && @current_input_type.kind.scalar?
|
79
|
+
|
80
|
+
arg_owner = @current_input_type || @current_directive || @current_field
|
81
|
+
old_current_argument = @current_argument
|
82
|
+
@current_argument = arg_owner.arguments[argument.name]
|
83
|
+
|
84
|
+
old_input_type = @current_input_type
|
85
|
+
@current_input_type = @current_argument.type.non_null? ? @current_argument.type.of_type : @current_argument.type
|
86
|
+
|
87
|
+
argument_value = if coerce_argument_value_to_list?(@current_input_type, argument.value)
|
88
|
+
[argument.value]
|
89
|
+
else
|
90
|
+
argument.value
|
91
|
+
end
|
92
|
+
res = "#{argument.name}: #{print_node(argument_value)}".dup
|
93
|
+
|
94
|
+
@current_input_type = old_input_type
|
95
|
+
@current_argument = old_current_argument
|
96
|
+
res
|
97
|
+
end
|
98
|
+
|
99
|
+
def coerce_argument_value_to_list?(type, value)
|
100
|
+
type.list? &&
|
101
|
+
!value.is_a?(Array) &&
|
102
|
+
!value.nil? &&
|
103
|
+
!value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
104
|
+
end
|
105
|
+
|
106
|
+
def print_variable_identifier(variable_id)
|
107
|
+
if @inline_variables
|
108
|
+
variable_value = query.variables[variable_id.name]
|
109
|
+
print_node(value_to_ast(variable_value, @current_input_type))
|
110
|
+
else
|
111
|
+
super
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def print_field(field, indent: "")
|
116
|
+
@current_field = query.schema.get_field(@current_type, field.name)
|
117
|
+
old_type = @current_type
|
118
|
+
@current_type = @current_field.type.unwrap
|
119
|
+
res = super
|
120
|
+
@current_type = old_type
|
121
|
+
res
|
122
|
+
end
|
123
|
+
|
124
|
+
def print_inline_fragment(inline_fragment, indent: "")
|
125
|
+
old_type = @current_type
|
126
|
+
|
127
|
+
if inline_fragment.type
|
128
|
+
@current_type = query.schema.types[inline_fragment.type.name]
|
129
|
+
end
|
130
|
+
|
131
|
+
res = super
|
132
|
+
|
133
|
+
@current_type = old_type
|
134
|
+
|
135
|
+
res
|
136
|
+
end
|
137
|
+
|
138
|
+
def print_fragment_definition(fragment_def, indent: "")
|
139
|
+
old_type = @current_type
|
140
|
+
@current_type = query.schema.types[fragment_def.type.name]
|
141
|
+
|
142
|
+
res = super
|
143
|
+
|
144
|
+
@current_type = old_type
|
145
|
+
|
146
|
+
res
|
147
|
+
end
|
148
|
+
|
149
|
+
def print_directive(directive)
|
150
|
+
@current_directive = query.schema.directives[directive.name]
|
151
|
+
|
152
|
+
res = super
|
153
|
+
|
154
|
+
@current_directive = nil
|
155
|
+
res
|
156
|
+
end
|
157
|
+
|
158
|
+
# Print the operation definition but do not include the variable
|
159
|
+
# definitions since we will inline them within the query
|
160
|
+
def print_operation_definition(operation_definition, indent: "")
|
161
|
+
old_type = @current_type
|
162
|
+
@current_type = query.schema.public_send(operation_definition.operation_type)
|
163
|
+
|
164
|
+
if @inline_variables
|
165
|
+
out = "#{indent}#{operation_definition.operation_type}".dup
|
166
|
+
out << " #{operation_definition.name}" if operation_definition.name
|
167
|
+
out << print_directives(operation_definition.directives)
|
168
|
+
out << print_selections(operation_definition.selections, indent: indent)
|
169
|
+
else
|
170
|
+
out = super
|
171
|
+
end
|
172
|
+
|
173
|
+
@current_type = old_type
|
174
|
+
out
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def value_to_ast(value, type)
|
180
|
+
type = type.of_type if type.non_null?
|
181
|
+
|
182
|
+
if value.nil?
|
183
|
+
return GraphQL::Language::Nodes::NullValue.new(name: "null")
|
184
|
+
end
|
185
|
+
|
186
|
+
case type.kind.name
|
187
|
+
when "INPUT_OBJECT"
|
188
|
+
value = if value.respond_to?(:to_unsafe_h)
|
189
|
+
# for ActionController::Parameters
|
190
|
+
value.to_unsafe_h
|
191
|
+
else
|
192
|
+
value.to_h
|
193
|
+
end
|
194
|
+
|
195
|
+
arguments = value.map do |key, val|
|
196
|
+
sub_type = type.arguments[key.to_s].type
|
197
|
+
|
198
|
+
GraphQL::Language::Nodes::Argument.new(
|
199
|
+
name: key.to_s,
|
200
|
+
value: value_to_ast(val, sub_type)
|
201
|
+
)
|
202
|
+
end
|
203
|
+
GraphQL::Language::Nodes::InputObject.new(
|
204
|
+
arguments: arguments
|
205
|
+
)
|
206
|
+
when "LIST"
|
207
|
+
if value.is_a?(Array)
|
208
|
+
value.map { |v| value_to_ast(v, type.of_type) }
|
209
|
+
else
|
210
|
+
[value].map { |v| value_to_ast(v, type.of_type) }
|
211
|
+
end
|
212
|
+
when "ENUM"
|
213
|
+
GraphQL::Language::Nodes::Enum.new(name: value)
|
214
|
+
else
|
215
|
+
value
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
attr_reader :query
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -89,7 +89,7 @@ module GraphQL
|
|
89
89
|
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
90
90
|
# @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
|
91
91
|
def on_abstract_node(node, parent)
|
92
|
-
if node
|
92
|
+
if node.equal?(DELETE_NODE)
|
93
93
|
# This might be passed to `super(DELETE_NODE, ...)`
|
94
94
|
# by a user hook, don't want to keep visiting in that case.
|
95
95
|
nil
|
@@ -179,7 +179,7 @@ module GraphQL
|
|
179
179
|
# The user-provided hook returned a new node.
|
180
180
|
new_parent = new_parent && new_parent.replace_child(node, new_node)
|
181
181
|
return new_node, new_parent
|
182
|
-
elsif new_node
|
182
|
+
elsif new_node.equal?(DELETE_NODE)
|
183
183
|
# The user-provided hook requested to remove this node
|
184
184
|
new_parent = new_parent && new_parent.delete_child(node)
|
185
185
|
return nil, new_parent
|
data/lib/graphql/language.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "graphql/language/block_string"
|
3
3
|
require "graphql/language/printer"
|
4
|
-
require "graphql/language/
|
4
|
+
require "graphql/language/sanitized_printer"
|
5
5
|
require "graphql/language/document_from_schema_definition"
|
6
6
|
require "graphql/language/generation"
|
7
7
|
require "graphql/language/lexer"
|
@@ -9,6 +9,7 @@ require "graphql/language/nodes"
|
|
9
9
|
require "graphql/language/parser"
|
10
10
|
require "graphql/language/token"
|
11
11
|
require "graphql/language/visitor"
|
12
|
+
require "graphql/language/definition_slice"
|
12
13
|
|
13
14
|
module GraphQL
|
14
15
|
module Language
|
@@ -1,16 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
class NameValidator
|
4
|
-
|
5
|
-
|
6
|
-
def self.validate!(name)
|
7
|
-
raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless valid?(name)
|
4
|
+
if !String.method_defined?(:match?)
|
5
|
+
using GraphQL::StringMatchBackport
|
8
6
|
end
|
9
7
|
|
10
|
-
|
8
|
+
VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/
|
11
9
|
|
12
|
-
def self.
|
13
|
-
name
|
10
|
+
def self.validate!(name)
|
11
|
+
name = name.is_a?(String) ? name : name.to_s
|
12
|
+
raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless name.match?(VALID_NAME_REGEX)
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
|
-
class DoubleNonNullTypeError < GraphQL::Error
|
4
|
-
end
|
5
|
-
|
6
3
|
# A non-null type modifies another type.
|
7
4
|
#
|
8
5
|
# Non-null types can be created with `!` (`InnerType!`)
|
@@ -37,13 +34,6 @@ module GraphQL
|
|
37
34
|
|
38
35
|
attr_reader :of_type
|
39
36
|
def initialize(of_type:)
|
40
|
-
if of_type.is_a?(GraphQL::NonNullType)
|
41
|
-
raise(
|
42
|
-
DoubleNonNullTypeError,
|
43
|
-
"You tried to add a non-null constraint twice (!! instead of !)"
|
44
|
-
)
|
45
|
-
end
|
46
|
-
|
47
37
|
super()
|
48
38
|
@of_type = of_type
|
49
39
|
end
|
data/lib/graphql/object_type.rb
CHANGED
@@ -1,26 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# @example defining a type for your IMDB clone
|
6
|
-
# MovieType = GraphQL::ObjectType.define do
|
7
|
-
# name "Movie"
|
8
|
-
# description "A full-length film or a short film"
|
9
|
-
# interfaces [ProductionInterface, DurationInterface]
|
10
|
-
#
|
11
|
-
# field :runtimeMinutes, !types.Int, property: :runtime_minutes
|
12
|
-
# field :director, PersonType
|
13
|
-
# field :cast, CastType
|
14
|
-
# field :starring, types[PersonType] do
|
15
|
-
# argument :limit, types.Int
|
16
|
-
# resolve ->(object, args, ctx) {
|
17
|
-
# stars = object.cast.stars
|
18
|
-
# args[:limit] && stars = stars.limit(args[:limit])
|
19
|
-
# stars
|
20
|
-
# }
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
3
|
+
# @api deprecated
|
24
4
|
class ObjectType < GraphQL::BaseType
|
25
5
|
accepts_definitions :interfaces, :fields, :mutation, :relay_node_type, field: GraphQL::Define::AssignObjectField
|
26
6
|
accepts_definitions implements: ->(type, *interfaces, inherit: false) { type.implements(interfaces, inherit: inherit) }
|
@@ -37,17 +17,15 @@ module GraphQL
|
|
37
17
|
def initialize
|
38
18
|
super
|
39
19
|
@fields = {}
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
20
|
+
@clean_inherited_fields = nil
|
21
|
+
@structural_interface_type_memberships = []
|
22
|
+
@inherited_interface_type_memberships = []
|
43
23
|
end
|
44
24
|
|
45
25
|
def initialize_copy(other)
|
46
26
|
super
|
47
|
-
@
|
48
|
-
@
|
49
|
-
@dirty_interfaces = other.dirty_interfaces.dup
|
50
|
-
@dirty_inherited_interfaces = other.dirty_inherited_interfaces.dup
|
27
|
+
@structural_interface_type_memberships = other.structural_interface_type_memberships.dup
|
28
|
+
@inherited_interface_type_memberships = other.inherited_interface_type_memberships.dup
|
51
29
|
@fields = other.fields.dup
|
52
30
|
end
|
53
31
|
|
@@ -55,18 +33,27 @@ module GraphQL
|
|
55
33
|
# @param new_interfaces [Array<GraphQL::Interface>] interfaces that this type implements
|
56
34
|
# @deprecated Use `implements` instead of `interfaces`.
|
57
35
|
def interfaces=(new_interfaces)
|
58
|
-
@
|
59
|
-
@
|
36
|
+
@structural_interface_type_memberships = []
|
37
|
+
@inherited_interface_type_memberships = []
|
60
38
|
@clean_inherited_fields = nil
|
61
|
-
|
62
|
-
@dirty_inherited_interfaces = []
|
63
|
-
@dirty_inherited_fields = {}
|
64
39
|
implements(new_interfaces, inherit: true)
|
65
40
|
end
|
66
41
|
|
67
|
-
def interfaces
|
68
|
-
|
69
|
-
|
42
|
+
def interfaces(ctx = GraphQL::Query::NullContext)
|
43
|
+
ensure_defined
|
44
|
+
visible_ifaces = []
|
45
|
+
unfiltered = ctx == GraphQL::Query::NullContext
|
46
|
+
[@structural_interface_type_memberships, @inherited_interface_type_memberships].each do |tms|
|
47
|
+
tms.each do |type_membership|
|
48
|
+
if unfiltered || type_membership.visible?(ctx)
|
49
|
+
# if this is derived from a class-based object, we have to
|
50
|
+
# get the `.graphql_definition` of the attached interface.
|
51
|
+
visible_ifaces << GraphQL::BaseType.resolve_related_type(type_membership.abstract_type)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
visible_ifaces
|
70
57
|
end
|
71
58
|
|
72
59
|
def kind
|
@@ -91,24 +78,30 @@ module GraphQL
|
|
91
78
|
# This declaration will be validated when the schema is defined.
|
92
79
|
# @param interfaces [Array<GraphQL::Interface>] add a new interface that this type implements
|
93
80
|
# @param inherits [Boolean] If true, copy the interfaces' field definitions to this type
|
94
|
-
def implements(interfaces, inherit: false)
|
81
|
+
def implements(interfaces, inherit: false, **options)
|
95
82
|
if !interfaces.is_a?(Array)
|
96
83
|
raise ArgumentError, "`implements(interfaces)` must be an array, not #{interfaces.class} (#{interfaces})"
|
97
84
|
end
|
98
|
-
|
99
|
-
@clean_interfaces = nil
|
100
85
|
@clean_inherited_fields = nil
|
101
|
-
|
102
|
-
|
86
|
+
|
87
|
+
type_memberships = inherit ? @inherited_interface_type_memberships : @structural_interface_type_memberships
|
88
|
+
interfaces.each do |iface|
|
89
|
+
iface = BaseType.resolve_related_type(iface)
|
90
|
+
if iface.is_a?(GraphQL::InterfaceType)
|
91
|
+
type_memberships << iface.type_membership_class.new(iface, self, **options)
|
92
|
+
end
|
93
|
+
end
|
103
94
|
end
|
104
95
|
|
105
96
|
def resolve_type_proc
|
106
97
|
nil
|
107
98
|
end
|
108
99
|
|
100
|
+
attr_writer :structural_interface_type_memberships
|
101
|
+
|
109
102
|
protected
|
110
103
|
|
111
|
-
attr_reader :
|
104
|
+
attr_reader :structural_interface_type_memberships, :inherited_interface_type_memberships
|
112
105
|
|
113
106
|
private
|
114
107
|
|
@@ -117,24 +110,20 @@ module GraphQL
|
|
117
110
|
end
|
118
111
|
|
119
112
|
def interface_fields
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
def load_interfaces
|
125
|
-
@clean_interfaces ||= begin
|
113
|
+
if @clean_inherited_fields
|
114
|
+
@clean_inherited_fields
|
115
|
+
else
|
126
116
|
ensure_defined
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
clean_inherited_ifaces.each do |iface|
|
131
|
-
# This will be found later in schema validation:
|
117
|
+
@clean_inherited_fields = {}
|
118
|
+
@inherited_interface_type_memberships.each do |type_membership|
|
119
|
+
iface = GraphQL::BaseType.resolve_related_type(type_membership.abstract_type)
|
132
120
|
if iface.is_a?(GraphQL::InterfaceType)
|
133
|
-
|
121
|
+
@clean_inherited_fields.merge!(iface.fields)
|
122
|
+
else
|
123
|
+
pp iface
|
134
124
|
end
|
135
125
|
end
|
136
|
-
@clean_inherited_fields
|
137
|
-
clean_inherited_ifaces + clean_ifaces
|
126
|
+
@clean_inherited_fields
|
138
127
|
end
|
139
128
|
end
|
140
129
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "graphql/pagination/relation_connection"
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
module Pagination
|
6
|
+
# Customizes `RelationConnection` to work with `ActiveRecord::Relation`s.
|
7
|
+
class ActiveRecordRelationConnection < Pagination::RelationConnection
|
8
|
+
def relation_count(relation)
|
9
|
+
int_or_hash = if relation.respond_to?(:unscope)
|
10
|
+
relation.unscope(:order).count(:all)
|
11
|
+
else
|
12
|
+
# Rails 3
|
13
|
+
relation.count
|
14
|
+
end
|
15
|
+
if int_or_hash.is_a?(Integer)
|
16
|
+
int_or_hash
|
17
|
+
else
|
18
|
+
# Grouped relations return count-by-group hashes
|
19
|
+
int_or_hash.length
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def relation_limit(relation)
|
24
|
+
relation.limit_value
|
25
|
+
end
|
26
|
+
|
27
|
+
def relation_offset(relation)
|
28
|
+
relation.offset_value
|
29
|
+
end
|
30
|
+
|
31
|
+
def null_relation(relation)
|
32
|
+
if relation.respond_to?(:none)
|
33
|
+
relation.none
|
34
|
+
else
|
35
|
+
# Rails 3
|
36
|
+
relation.where("1=2")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "graphql/pagination/connection"
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
module Pagination
|
6
|
+
class ArrayConnection < Pagination::Connection
|
7
|
+
def nodes
|
8
|
+
load_nodes
|
9
|
+
@nodes
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_previous_page
|
13
|
+
load_nodes
|
14
|
+
@has_previous_page
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_next_page
|
18
|
+
load_nodes
|
19
|
+
@has_next_page
|
20
|
+
end
|
21
|
+
|
22
|
+
def cursor_for(item)
|
23
|
+
idx = items.find_index(item) + 1
|
24
|
+
encode(idx.to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def index_from_cursor(cursor)
|
30
|
+
decode(cursor).to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
# Populate all the pagination info _once_,
|
34
|
+
# It doesn't do anything on subsequent calls.
|
35
|
+
def load_nodes
|
36
|
+
@nodes ||= begin
|
37
|
+
sliced_nodes = if before && after
|
38
|
+
items[index_from_cursor(after)..index_from_cursor(before)-1] || []
|
39
|
+
elsif before
|
40
|
+
items[0..index_from_cursor(before)-2] || []
|
41
|
+
elsif after
|
42
|
+
items[index_from_cursor(after)..-1] || []
|
43
|
+
else
|
44
|
+
items
|
45
|
+
end
|
46
|
+
|
47
|
+
@has_previous_page = if last
|
48
|
+
# There are items preceding the ones in this result
|
49
|
+
sliced_nodes.count > last
|
50
|
+
elsif after
|
51
|
+
# We've paginated into the Array a bit, there are some behind us
|
52
|
+
index_from_cursor(after) > 0
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
@has_next_page = if first
|
58
|
+
# There are more items after these items
|
59
|
+
sliced_nodes.count > first
|
60
|
+
elsif before
|
61
|
+
# The original array is longer than the `before` index
|
62
|
+
index_from_cursor(before) < items.length + 1
|
63
|
+
else
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
limited_nodes = sliced_nodes
|
68
|
+
|
69
|
+
limited_nodes = limited_nodes.first(first) if first
|
70
|
+
limited_nodes = limited_nodes.last(last) if last
|
71
|
+
|
72
|
+
limited_nodes
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|