graphql 2.5.14 → 2.5.22
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/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/null_dataloader.rb +7 -3
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/execution/interpreter.rb +0 -1
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/execution/next/field_resolve_step.rb +711 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +70 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -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.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 +3 -2
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/schema/argument.rb +12 -0
- data/lib/graphql/schema/build_from_definition.rb +7 -0
- data/lib/graphql/schema/directive.rb +8 -1
- data/lib/graphql/schema/field/connection_extension.rb +15 -35
- data/lib/graphql/schema/field/scope_extension.rb +22 -13
- data/lib/graphql/schema/field.rb +80 -48
- data/lib/graphql/schema/field_extension.rb +33 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +37 -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 +81 -4
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/object.rb +1 -0
- data/lib/graphql/schema/resolver.rb +45 -1
- data/lib/graphql/schema/validator/required_validator.rb +33 -2
- data/lib/graphql/schema/visibility.rb +3 -3
- data/lib/graphql/schema.rb +53 -10
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions.rb +1 -1
- 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/perfetto_trace.rb +208 -78
- 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 +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +8 -2
- metadata +17 -3
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Tracing
|
|
5
|
+
class DetailedTrace
|
|
6
|
+
class ActiveRecordBackend
|
|
7
|
+
class GraphqlDetailedTrace < ActiveRecord::Base
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(limit: nil, model_class: nil)
|
|
11
|
+
@limit = limit
|
|
12
|
+
@model_class = model_class || GraphqlDetailedTrace
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def traces(last:, before:)
|
|
16
|
+
gdts = @model_class.all.order("begin_ms DESC")
|
|
17
|
+
if before
|
|
18
|
+
gdts = gdts.where("begin_ms < ?", before)
|
|
19
|
+
end
|
|
20
|
+
if last
|
|
21
|
+
gdts = gdts.limit(last)
|
|
22
|
+
end
|
|
23
|
+
gdts.map { |gdt| record_to_stored_trace(gdt) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete_trace(id)
|
|
27
|
+
@model_class.where(id: id).destroy_all
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def delete_all_traces
|
|
32
|
+
@model_class.all.destroy_all
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def find_trace(id)
|
|
36
|
+
gdt = @model_class.find_by(id: id)
|
|
37
|
+
if gdt
|
|
38
|
+
record_to_stored_trace(gdt)
|
|
39
|
+
else
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def save_trace(operation_name, duration_ms, begin_ms, trace_data)
|
|
45
|
+
gdt = @model_class.create!(
|
|
46
|
+
begin_ms: begin_ms,
|
|
47
|
+
operation_name: operation_name,
|
|
48
|
+
duration_ms: duration_ms,
|
|
49
|
+
trace_data: trace_data,
|
|
50
|
+
)
|
|
51
|
+
if @limit
|
|
52
|
+
@model_class
|
|
53
|
+
.where("id NOT IN(SELECT id FROM graphql_detailed_traces ORDER BY begin_ms DESC LIMIT ?)", @limit)
|
|
54
|
+
.delete_all
|
|
55
|
+
end
|
|
56
|
+
gdt.id
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def record_to_stored_trace(gdt)
|
|
62
|
+
StoredTrace.new(
|
|
63
|
+
id: gdt.id,
|
|
64
|
+
begin_ms: gdt.begin_ms,
|
|
65
|
+
operation_name: gdt.operation_name,
|
|
66
|
+
duration_ms: gdt.duration_ms,
|
|
67
|
+
trace_data: gdt.trace_data
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
if defined?(ActiveRecord)
|
|
3
|
+
require "graphql/tracing/detailed_trace/active_record_backend"
|
|
4
|
+
end
|
|
2
5
|
require "graphql/tracing/detailed_trace/memory_backend"
|
|
3
6
|
require "graphql/tracing/detailed_trace/redis_backend"
|
|
4
7
|
|
|
5
8
|
module GraphQL
|
|
6
9
|
module Tracing
|
|
7
|
-
# `DetailedTrace` can make detailed profiles for a subset of production traffic.
|
|
10
|
+
# `DetailedTrace` can make detailed profiles for a subset of production traffic. Install it in Rails with `rails generate graphql:detailed_trace`.
|
|
8
11
|
#
|
|
9
12
|
# When `MySchema.detailed_trace?(query)` returns `true`, a profiler-specific `trace_mode: ...` will be used for the query,
|
|
10
13
|
# overriding the one in `context[:trace_mode]`.
|
|
11
14
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
15
|
+
# By default, the detailed tracer calls `.inspect` on application objects returned from fields. You can customize
|
|
16
|
+
# this behavior by extending {DetailedTrace} and overriding {#inspect_object}. You can opt out of debug annotations
|
|
17
|
+
# entirely with `use ..., debug: false` or for a single query with `context: { detailed_trace_debug: false }`.
|
|
18
|
+
#
|
|
19
|
+
# You can store saved traces in two ways:
|
|
20
|
+
#
|
|
21
|
+
# - __ActiveRecord__: With `rails generate graphql:detailed_trace`, a new migration will be added to your app.
|
|
22
|
+
# That table will be used to store trace data.
|
|
23
|
+
#
|
|
24
|
+
# - __Redis__: Pass `redis: ...` to save trace data to a Redis database. Depending on your needs,
|
|
25
|
+
# you can configure this database to retain all data (persistent) or to expire data according to your rules.
|
|
26
|
+
#
|
|
14
27
|
# If you need to save traces indefinitely, you can download them from Perfetto after opening them there.
|
|
15
28
|
#
|
|
29
|
+
# @example Installing with Rails
|
|
30
|
+
# rails generate graphql:detailed_trace # optional: --redis
|
|
31
|
+
#
|
|
16
32
|
# @example Adding the sampler to your schema
|
|
17
33
|
# class MySchema < GraphQL::Schema
|
|
18
34
|
# # Add the sampler:
|
|
@@ -27,24 +43,48 @@ module GraphQL
|
|
|
27
43
|
# end
|
|
28
44
|
#
|
|
29
45
|
# @see Graphql::Dashboard GraphQL::Dashboard for viewing stored results
|
|
46
|
+
#
|
|
47
|
+
# @example Customizing debug output in traces
|
|
48
|
+
# class CustomDetailedTrace < GraphQL::Tracing::DetailedTrace
|
|
49
|
+
# def inspect_object(object)
|
|
50
|
+
# if object.is_a?(SomeThing)
|
|
51
|
+
# # handle it specially ...
|
|
52
|
+
# else
|
|
53
|
+
# super
|
|
54
|
+
# end
|
|
55
|
+
# end
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# @example disabling debug annotations completely
|
|
59
|
+
# use DetailedTrace, debug: false, ...
|
|
60
|
+
#
|
|
61
|
+
# @example disabling debug annotations for one query
|
|
62
|
+
# MySchema.execute(query_str, context: { detailed_trace_debug: false })
|
|
63
|
+
#
|
|
30
64
|
class DetailedTrace
|
|
31
65
|
# @param redis [Redis] If provided, profiles will be stored in Redis for later review
|
|
32
66
|
# @param limit [Integer] A maximum number of profiles to store
|
|
33
|
-
|
|
67
|
+
# @param debug [Boolean] if `false`, it won't create `debug` annotations in Perfetto traces (reduces overhead)
|
|
68
|
+
# @param model_class [Class<ActiveRecord::Base>] Overrides {ActiveRecordBackend::GraphqlDetailedTrace} if present
|
|
69
|
+
def self.use(schema, trace_mode: :profile_sample, memory: false, debug: debug?, redis: nil, limit: nil, model_class: nil)
|
|
34
70
|
storage = if redis
|
|
35
71
|
RedisBackend.new(redis: redis, limit: limit)
|
|
36
72
|
elsif memory
|
|
37
73
|
MemoryBackend.new(limit: limit)
|
|
74
|
+
elsif defined?(ActiveRecord)
|
|
75
|
+
ActiveRecordBackend.new(limit: limit, model_class: model_class)
|
|
38
76
|
else
|
|
39
|
-
raise ArgumentError, "
|
|
77
|
+
raise ArgumentError, "To store traces, install ActiveRecord or provide `redis: ...`"
|
|
40
78
|
end
|
|
41
|
-
|
|
79
|
+
detailed_trace = self.new(storage: storage, trace_mode: trace_mode, debug: debug)
|
|
80
|
+
schema.detailed_trace = detailed_trace
|
|
42
81
|
schema.trace_with(PerfettoTrace, mode: trace_mode, save_profile: true)
|
|
43
82
|
end
|
|
44
83
|
|
|
45
|
-
def initialize(storage:, trace_mode:)
|
|
84
|
+
def initialize(storage:, trace_mode:, debug:)
|
|
46
85
|
@storage = storage
|
|
47
86
|
@trace_mode = trace_mode
|
|
87
|
+
@debug = debug
|
|
48
88
|
end
|
|
49
89
|
|
|
50
90
|
# @return [Symbol] The trace mode to use when {Schema.detailed_trace?} returns `true`
|
|
@@ -55,6 +95,11 @@ module GraphQL
|
|
|
55
95
|
@storage.save_trace(operation_name, duration_ms, begin_ms, trace_data)
|
|
56
96
|
end
|
|
57
97
|
|
|
98
|
+
# @return [Boolean]
|
|
99
|
+
def debug?
|
|
100
|
+
@debug
|
|
101
|
+
end
|
|
102
|
+
|
|
58
103
|
# @param last [Integer]
|
|
59
104
|
# @param before [Integer] Timestamp in milliseconds since epoch
|
|
60
105
|
# @return [Enumerable<StoredTrace>]
|
|
@@ -77,6 +122,24 @@ module GraphQL
|
|
|
77
122
|
@storage.delete_all_traces
|
|
78
123
|
end
|
|
79
124
|
|
|
125
|
+
def inspect_object(object)
|
|
126
|
+
self.class.inspect_object(object)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.inspect_object(object)
|
|
130
|
+
if defined?(ActiveRecord::Relation) && object.is_a?(ActiveRecord::Relation)
|
|
131
|
+
"#{object.class}, .to_sql=#{object.to_sql.inspect}"
|
|
132
|
+
else
|
|
133
|
+
object.inspect
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Default debug setting
|
|
138
|
+
# @return [true]
|
|
139
|
+
def self.debug?
|
|
140
|
+
true
|
|
141
|
+
end
|
|
142
|
+
|
|
80
143
|
class StoredTrace
|
|
81
144
|
def initialize(id:, operation_name:, duration_ms:, begin_ms:, trace_data:)
|
|
82
145
|
@id = id
|