graphql 1.11.4 → 1.11.8
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/object_generator.rb +2 -0
- data/lib/generators/graphql/templates/union.erb +1 -1
- data/lib/graphql.rb +17 -0
- data/lib/graphql/argument.rb +3 -3
- data/lib/graphql/backtrace/tracer.rb +2 -1
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/execution/interpreter.rb +10 -0
- data/lib/graphql/execution/interpreter/arguments.rb +21 -6
- data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
- data/lib/graphql/execution/interpreter/runtime.rb +70 -42
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +6 -0
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/type_type.rb +7 -3
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +107 -103
- data/lib/graphql/language/parser.y +4 -0
- data/lib/graphql/language/sanitized_printer.rb +59 -26
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/pagination/connections.rb +11 -3
- data/lib/graphql/query.rb +6 -3
- data/lib/graphql/query/context.rb +14 -3
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/relay/array_connection.rb +2 -2
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/schema.rb +47 -19
- data/lib/graphql/schema/argument.rb +56 -5
- data/lib/graphql/schema/build_from_definition.rb +67 -38
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/field.rb +32 -16
- data/lib/graphql/schema/field/connection_extension.rb +8 -7
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/input_object.rb +5 -3
- data/lib/graphql/schema/interface.rb +1 -1
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/loader.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +14 -4
- data/lib/graphql/schema/member/has_arguments.rb +54 -53
- data/lib/graphql/schema/member/has_fields.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
- data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +8 -0
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
- 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/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +29 -7
- data/lib/graphql/subscriptions.rb +1 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
- data/lib/graphql/tracing/platform_tracing.rb +1 -1
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/relay/base_connection.rb +2 -1
- 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/version.rb +1 -1
- data/readme.md +1 -1
- metadata +7 -3
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
module InputObjectNamesAreUnique
|
5
|
+
def on_input_object(node, parent)
|
6
|
+
validate_input_fields(node)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def validate_input_fields(node)
|
13
|
+
input_field_defns = node.arguments
|
14
|
+
input_fields_by_name = Hash.new { |h, k| h[k] = [] }
|
15
|
+
input_field_defns.each { |a| input_fields_by_name[a.name] << a }
|
16
|
+
|
17
|
+
input_fields_by_name.each do |name, defns|
|
18
|
+
if defns.size > 1
|
19
|
+
error = GraphQL::StaticValidation::InputObjectNamesAreUniqueError.new(
|
20
|
+
"There can be only one input field named \"#{name}\"",
|
21
|
+
nodes: defns,
|
22
|
+
name: name
|
23
|
+
)
|
24
|
+
add_error(error)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class InputObjectNamesAreUniqueError < StaticValidation::Error
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(message, path: nil, nodes: [], name:)
|
8
|
+
super(message, path: path, nodes: nodes)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
# A hash representation of this Message
|
13
|
+
def to_h
|
14
|
+
extensions = {
|
15
|
+
"code" => code,
|
16
|
+
"name" => name
|
17
|
+
}
|
18
|
+
|
19
|
+
super.merge({
|
20
|
+
"extensions" => extensions
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def code
|
25
|
+
"inputFieldNotUnique"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class ValidationTimeoutError < StaticValidation::Error
|
5
|
+
def initialize(message, path: nil, nodes: [])
|
6
|
+
super(message, path: path, nodes: nodes)
|
7
|
+
end
|
8
|
+
|
9
|
+
# A hash representation of this Message
|
10
|
+
def to_h
|
11
|
+
extensions = {
|
12
|
+
"code" => code
|
13
|
+
}
|
14
|
+
|
15
|
+
super.merge({
|
16
|
+
"extensions" => extensions
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
def code
|
21
|
+
"validationTimeout"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -20,8 +20,10 @@ module GraphQL
|
|
20
20
|
|
21
21
|
# Validate `query` against the schema. Returns an array of message hashes.
|
22
22
|
# @param query [GraphQL::Query]
|
23
|
+
# @param validate [Boolean]
|
24
|
+
# @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
|
23
25
|
# @return [Array<Hash>]
|
24
|
-
def validate(query, validate: true)
|
26
|
+
def validate(query, validate: true, timeout: nil)
|
25
27
|
query.trace("validate", { validate: validate, query: query }) do
|
26
28
|
can_skip_rewrite = query.context.interpreter? && query.schema.using_ast_analysis? && query.schema.is_a?(Class)
|
27
29
|
errors = if validate == false && can_skip_rewrite
|
@@ -32,18 +34,29 @@ module GraphQL
|
|
32
34
|
|
33
35
|
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
begin
|
38
|
+
# CAUTION: Usage of the timeout module makes the assumption that validation rules are stateless Ruby code that requires no cleanup if process was interrupted. This means no blocking IO calls, native gems, locks, or `rescue` clauses that must be reached.
|
39
|
+
# A timeout value of 0 or nil will execute the block without any timeout.
|
40
|
+
Timeout::timeout(timeout) do
|
41
|
+
# Attach legacy-style rules.
|
42
|
+
# Only loop through rules if it has legacy-style rules
|
43
|
+
unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
|
44
|
+
legacy_rules.each do |rule_class_or_module|
|
45
|
+
if rule_class_or_module.method_defined?(:validate)
|
46
|
+
rule_class_or_module.new.validate(context)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context.visitor.visit
|
39
52
|
end
|
53
|
+
rescue Timeout::Error
|
54
|
+
handle_timeout(query, context)
|
40
55
|
end
|
41
56
|
|
42
|
-
context.visitor.visit
|
43
57
|
context.errors
|
44
58
|
end
|
45
59
|
|
46
|
-
|
47
60
|
irep = if errors.empty? && context
|
48
61
|
# Only return this if there are no errors and validation was actually run
|
49
62
|
context.visitor.rewrite_document
|
@@ -57,6 +70,15 @@ module GraphQL
|
|
57
70
|
}
|
58
71
|
end
|
59
72
|
end
|
73
|
+
|
74
|
+
# Invoked when static validation times out.
|
75
|
+
# @param query [GraphQL::Query]
|
76
|
+
# @param context [GraphQL::StaticValidation::ValidationContext]
|
77
|
+
def handle_timeout(query, context)
|
78
|
+
context.errors << GraphQL::StaticValidation::ValidationTimeoutError.new(
|
79
|
+
"Timeout on validation of query"
|
80
|
+
)
|
81
|
+
end
|
60
82
|
end
|
61
83
|
end
|
62
84
|
end
|
@@ -4,9 +4,7 @@ require "graphql/subscriptions/broadcast_analyzer"
|
|
4
4
|
require "graphql/subscriptions/event"
|
5
5
|
require "graphql/subscriptions/instrumentation"
|
6
6
|
require "graphql/subscriptions/serialize"
|
7
|
-
|
8
|
-
require "graphql/subscriptions/action_cable_subscriptions"
|
9
|
-
end
|
7
|
+
require "graphql/subscriptions/action_cable_subscriptions"
|
10
8
|
require "graphql/subscriptions/subscription_root"
|
11
9
|
require "graphql/subscriptions/default_subscription_resolve_extension"
|
12
10
|
|
@@ -4,7 +4,7 @@ module GraphQL
|
|
4
4
|
# A subscriptions implementation that sends data
|
5
5
|
# as ActionCable broadcastings.
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# Some things to keep in mind:
|
8
8
|
#
|
9
9
|
# - No queueing system; ActiveJob should be added
|
10
10
|
# - Take care to reload context when re-delivering the subscription. (see {Query#subscription_update?})
|
@@ -86,28 +86,32 @@ module GraphQL
|
|
86
86
|
EVENT_PREFIX = "graphql-event:"
|
87
87
|
|
88
88
|
# @param serializer [<#dump(obj), #load(string)] Used for serializing messages before handing them to `.broadcast(msg)`
|
89
|
-
|
89
|
+
# @param namespace [string] Used to namespace events and subscriptions (default: '')
|
90
|
+
def initialize(serializer: Serialize, namespace: '', action_cable: ActionCable, action_cable_coder: ActiveSupport::JSON, **rest)
|
90
91
|
# A per-process map of subscriptions to deliver.
|
91
92
|
# This is provided by Rails, so let's use it
|
92
93
|
@subscriptions = Concurrent::Map.new
|
93
94
|
@events = Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new { |h2, k2| h2[k2] = Concurrent::Array.new } }
|
95
|
+
@action_cable = action_cable
|
96
|
+
@action_cable_coder = action_cable_coder
|
94
97
|
@serializer = serializer
|
98
|
+
@transmit_ns = namespace
|
95
99
|
super
|
96
100
|
end
|
97
101
|
|
98
102
|
# An event was triggered; Push the data over ActionCable.
|
99
103
|
# Subscribers will re-evaluate locally.
|
100
104
|
def execute_all(event, object)
|
101
|
-
stream =
|
105
|
+
stream = stream_event_name(event)
|
102
106
|
message = @serializer.dump(object)
|
103
|
-
|
107
|
+
@action_cable.server.broadcast(stream, message)
|
104
108
|
end
|
105
109
|
|
106
110
|
# This subscription was re-evaluated.
|
107
111
|
# Send it to the specific stream where this client was waiting.
|
108
112
|
def deliver(subscription_id, result)
|
109
113
|
payload = { result: result.to_h, more: true }
|
110
|
-
|
114
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
111
115
|
end
|
112
116
|
|
113
117
|
# A query was run where these events were subscribed to.
|
@@ -117,7 +121,7 @@ module GraphQL
|
|
117
121
|
def write_subscription(query, events)
|
118
122
|
channel = query.context.fetch(:channel)
|
119
123
|
subscription_id = query.context[:subscription_id] ||= build_id
|
120
|
-
stream =
|
124
|
+
stream = stream_subscription_name(subscription_id)
|
121
125
|
channel.stream_from(stream)
|
122
126
|
@subscriptions[subscription_id] = query
|
123
127
|
events.each do |event|
|
@@ -141,7 +145,7 @@ module GraphQL
|
|
141
145
|
#
|
142
146
|
def setup_stream(channel, initial_event)
|
143
147
|
topic = initial_event.topic
|
144
|
-
channel.stream_from(
|
148
|
+
channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
|
145
149
|
object = @serializer.load(message)
|
146
150
|
events_by_fingerprint = @events[topic]
|
147
151
|
events_by_fingerprint.each do |_fingerprint, events|
|
@@ -197,6 +201,16 @@ module GraphQL
|
|
197
201
|
end
|
198
202
|
end
|
199
203
|
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
def stream_subscription_name(subscription_id)
|
208
|
+
[SUBSCRIPTION_PREFIX, @transmit_ns, subscription_id].join
|
209
|
+
end
|
210
|
+
|
211
|
+
def stream_event_name(event)
|
212
|
+
[EVENT_PREFIX, @transmit_ns, event.topic].join
|
213
|
+
end
|
200
214
|
end
|
201
215
|
end
|
202
216
|
end
|
@@ -16,7 +16,10 @@ module GraphQL
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def collect(object)
|
19
|
-
|
19
|
+
default_labels = { key: object['key'], platform_key: object['platform_key'] }
|
20
|
+
custom = object['custom_labels']
|
21
|
+
labels = custom.nil? ? default_labels : default_labels.merge(custom)
|
22
|
+
|
20
23
|
@graphql_gauge.observe object['duration'], labels
|
21
24
|
end
|
22
25
|
|
data/lib/graphql/types/int.rb
CHANGED
@@ -9,8 +9,15 @@ module GraphQL
|
|
9
9
|
MIN = -(2**31)
|
10
10
|
MAX = (2**31) - 1
|
11
11
|
|
12
|
-
def self.coerce_input(value,
|
13
|
-
value.is_a?(Integer)
|
12
|
+
def self.coerce_input(value, ctx)
|
13
|
+
return if !value.is_a?(Integer)
|
14
|
+
|
15
|
+
if value >= MIN && value <= MAX
|
16
|
+
value
|
17
|
+
else
|
18
|
+
err = GraphQL::IntegerDecodingError.new(value)
|
19
|
+
ctx.schema.type_error(err, ctx)
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
16
23
|
def self.coerce_result(value, ctx)
|
@@ -33,7 +33,8 @@ module GraphQL
|
|
33
33
|
if node_type
|
34
34
|
@node_type = node_type
|
35
35
|
# Add a default `node` field
|
36
|
-
field :node, node_type, null: null, description: "The item at the end of the edge."
|
36
|
+
field :node, node_type, null: null, description: "The item at the end of the edge.",
|
37
|
+
connection: false
|
37
38
|
end
|
38
39
|
@node_type
|
39
40
|
end
|
data/lib/graphql/types/string.rb
CHANGED
@@ -7,7 +7,13 @@ module GraphQL
|
|
7
7
|
|
8
8
|
def self.coerce_result(value, ctx)
|
9
9
|
str = value.to_s
|
10
|
-
str.encoding == Encoding::UTF_8
|
10
|
+
if str.encoding == Encoding::UTF_8
|
11
|
+
str
|
12
|
+
elsif str.frozen?
|
13
|
+
str.encode(Encoding::UTF_8)
|
14
|
+
else
|
15
|
+
str.encode!(Encoding::UTF_8)
|
16
|
+
end
|
11
17
|
rescue EncodingError
|
12
18
|
err = GraphQL::StringEncodingError.new(str)
|
13
19
|
ctx.schema.type_error(err, ctx)
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
22
22
|
@object = object
|
23
23
|
@type = type
|
24
24
|
@context = context
|
25
|
-
message ||= "An instance of #{object.class} failed #{type.
|
25
|
+
message ||= "An instance of #{object.class} failed #{type.graphql_name}'s authorization check"
|
26
26
|
super(message)
|
27
27
|
end
|
28
28
|
end
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
A Ruby implementation of [GraphQL](https://graphql.org/).
|
10
10
|
|
11
11
|
- [Website](https://graphql-ruby.org/)
|
12
|
-
- [API Documentation](https://www.rubydoc.info/
|
12
|
+
- [API Documentation](https://www.rubydoc.info/github/rmosolgo/graphql-ruby)
|
13
13
|
- [Newsletter](https://tinyletter.com/graphql-ruby)
|
14
14
|
|
15
15
|
## Installation
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.11.
|
4
|
+
version: 1.11.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -445,6 +445,7 @@ files:
|
|
445
445
|
- lib/graphql/id_type.rb
|
446
446
|
- lib/graphql/input_object_type.rb
|
447
447
|
- lib/graphql/int_type.rb
|
448
|
+
- lib/graphql/integer_decoding_error.rb
|
448
449
|
- lib/graphql/integer_encoding_error.rb
|
449
450
|
- lib/graphql/interface_type.rb
|
450
451
|
- lib/graphql/internal_representation.rb
|
@@ -650,6 +651,8 @@ files:
|
|
650
651
|
- lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb
|
651
652
|
- lib/graphql/static_validation/rules/fragments_are_used.rb
|
652
653
|
- lib/graphql/static_validation/rules/fragments_are_used_error.rb
|
654
|
+
- lib/graphql/static_validation/rules/input_object_names_are_unique.rb
|
655
|
+
- lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb
|
653
656
|
- lib/graphql/static_validation/rules/mutation_root_exists.rb
|
654
657
|
- lib/graphql/static_validation/rules/mutation_root_exists_error.rb
|
655
658
|
- lib/graphql/static_validation/rules/no_definitions_are_present.rb
|
@@ -676,6 +679,7 @@ files:
|
|
676
679
|
- lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb
|
677
680
|
- lib/graphql/static_validation/type_stack.rb
|
678
681
|
- lib/graphql/static_validation/validation_context.rb
|
682
|
+
- lib/graphql/static_validation/validation_timeout_error.rb
|
679
683
|
- lib/graphql/static_validation/validator.rb
|
680
684
|
- lib/graphql/string_encoding_error.rb
|
681
685
|
- lib/graphql/string_type.rb
|
@@ -752,7 +756,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
752
756
|
- !ruby/object:Gem::Version
|
753
757
|
version: '0'
|
754
758
|
requirements: []
|
755
|
-
rubygems_version: 3.1.
|
759
|
+
rubygems_version: 3.1.4
|
756
760
|
signing_key:
|
757
761
|
specification_version: 4
|
758
762
|
summary: A GraphQL language and runtime for Ruby
|