graphql 2.4.15 → 2.5.0
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/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +27 -0
- data/lib/graphql/dashboard/statics/dashboard.js +74 -9
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
- data/lib/graphql/dashboard.rb +45 -29
- data/lib/graphql/execution/interpreter.rb +3 -5
- data/lib/graphql/execution/multiplex.rb +1 -1
- data/lib/graphql/language/parser.rb +13 -6
- data/lib/graphql/query.rb +2 -4
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +5 -0
- data/lib/graphql/tracing/appsignal_trace.rb +26 -61
- data/lib/graphql/tracing/data_dog_trace.rb +41 -164
- data/lib/graphql/tracing/monitor_trace.rb +285 -0
- data/lib/graphql/tracing/new_relic_trace.rb +34 -166
- data/lib/graphql/tracing/notifications_trace.rb +181 -37
- data/lib/graphql/tracing/perfetto_trace.rb +15 -18
- data/lib/graphql/tracing/prometheus_trace.rb +47 -74
- data/lib/graphql/tracing/scout_trace.rb +25 -59
- data/lib/graphql/tracing/sentry_trace.rb +57 -99
- data/lib/graphql/tracing/statsd_trace.rb +24 -47
- data/lib/graphql/tracing/trace.rb +0 -17
- data/lib/graphql/tracing.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- metadata +25 -4
- data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +0 -63
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -0,0 +1,71 @@
|
|
1
|
+
<% content_for(:title, "View #{params[:digest]}") %>
|
2
|
+
<% if @operation.nil? %>
|
3
|
+
<div class="row">
|
4
|
+
<div class="col">
|
5
|
+
<p>No stored operation found for <code><%= params[:digest] %></code>
|
6
|
+
</div>
|
7
|
+
</div>
|
8
|
+
<% else %>
|
9
|
+
<div class="row">
|
10
|
+
<div class="col">
|
11
|
+
<h2>
|
12
|
+
<%= @operation.name %>
|
13
|
+
<% if @operation.is_archived %><small> (archived)</small><% end %>
|
14
|
+
</h2>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
<div class="row mt-3">
|
18
|
+
<div class="col">
|
19
|
+
<p>Aliases</p>
|
20
|
+
<% if @client_operations.empty? %>
|
21
|
+
<p><em>None</em></p>
|
22
|
+
<% else %>
|
23
|
+
<ul>
|
24
|
+
<% @client_operations.each do |cl_op| %>
|
25
|
+
<li>
|
26
|
+
<code><%= cl_op.operation_alias %></code>
|
27
|
+
<%= link_to(cl_op.client_name, graphql_dashboard.operation_store_client_operations_path(client_name: cl_op.client_name)) %>
|
28
|
+
<%= cl_op.is_archived ? " (archived)" : "" %>
|
29
|
+
</li>
|
30
|
+
<% end %>
|
31
|
+
</ul>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
<div class="row">
|
36
|
+
<div class="col">
|
37
|
+
<p>Last Used At</p>
|
38
|
+
<p><code><%= @operation.last_used_at %></code></p>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
<div class="row">
|
42
|
+
<div class="col">
|
43
|
+
<p>Source</p>
|
44
|
+
<%= textarea_tag "_source", @graphql_source, class: "graphql-highlight form-control", disabled: true, rows: @graphql_source.count("\n") + 1 %>
|
45
|
+
</div>
|
46
|
+
</div>
|
47
|
+
<div class="row mt-3">
|
48
|
+
<div class="col">
|
49
|
+
<p>References</p>
|
50
|
+
<ul>
|
51
|
+
<% @entries.each do |entry| %>
|
52
|
+
<li>
|
53
|
+
<%= link_to(entry.name, graphql_dashboard.operation_store_index_entry_path(name: entry.name)) %>
|
54
|
+
</li>
|
55
|
+
<% end %>
|
56
|
+
</ul>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
<div class="row mt-3">
|
60
|
+
<div class="col">
|
61
|
+
<p>Digest</p>
|
62
|
+
<p><code><%= @operation.digest %></code></p>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
<div class="row mt-3">
|
66
|
+
<div class="col">
|
67
|
+
<p>Minified Source</p>
|
68
|
+
<%= textarea_tag "_source", @operation.body, class: "graphql-highlight form-control", disabled: true, rows: @operation.body.count("\n") + 1 %>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
<% end %>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<% content_for(:title, "Subscription #{params[:id]}") %>
|
2
|
+
<div class="row mt-3">
|
3
|
+
<div class="col">
|
4
|
+
<h3>Subscription: <code><%= params[:id] %></code></h3>
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<% if @query_data.nil? %>
|
9
|
+
<div class="row">
|
10
|
+
<div class="col">
|
11
|
+
<p class="muted"><i>This subscription was not found or is no longer active.</i></p>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
<% else %>
|
15
|
+
<div class="row">
|
16
|
+
<div class="col">
|
17
|
+
<p>Created at <%= @query_data[:created_at] %>, last triggered at <%= @query_data[:last_triggered_at] || "--" %></p>
|
18
|
+
|
19
|
+
<p>Subscribed? <code><%= @still_subscribed ? "YES" : "NO" %></code></p>
|
20
|
+
<p>Broadcast? <code><%= @is_broadcast ? "YES" : "NO" %></code> <% if @is_broadcast %>
|
21
|
+
<small class="muted"><% if @subscribers_count.nil? %>
|
22
|
+
This subscription may have multiple subscribers.
|
23
|
+
<% else %>
|
24
|
+
(<%= pluralize(@subscribers_count, "subscriber") %>)
|
25
|
+
<% end %></small>
|
26
|
+
<% end %></p>
|
27
|
+
|
28
|
+
<p>Context:</p>
|
29
|
+
<pre><%= @query_data[:context].inspect %></pre>
|
30
|
+
|
31
|
+
<p>Variables:</p>
|
32
|
+
<pre><%= @query_data[:variables].inspect %></pre>
|
33
|
+
|
34
|
+
<p>Operation Name:</p>
|
35
|
+
<pre><%= @query_data[:operation_name].inspect %></pre>
|
36
|
+
|
37
|
+
<p>Query String:</p>
|
38
|
+
<%= textarea_tag "_source", @query_data[:query_string], class: "graphql-highlight form-control", disabled: true, rows: @query_data[:query_string].count("\n") + 1 %>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<% content_for(:title, "Subscriptions - Topics") %>
|
2
|
+
<div class="row mt-3 justify-content-between">
|
3
|
+
<div class="col-auto">
|
4
|
+
<h3>
|
5
|
+
<%= pluralize(@all_topics_count, "Subscription Topic") %>
|
6
|
+
</h3>
|
7
|
+
</div>
|
8
|
+
<div class="col-auto">
|
9
|
+
<%= button_tag "Clear All", class: "btn btn-outline-danger", data: { subscriptions_delete_all: graphql_dashboard.subscriptions_clear_all_path } %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<table class="table table-striped">
|
14
|
+
<thead>
|
15
|
+
<tr>
|
16
|
+
<th>Name</th>
|
17
|
+
<th># Subscriptions</th>
|
18
|
+
<th>Last Triggered At</th>
|
19
|
+
</tr>
|
20
|
+
</thead>
|
21
|
+
<tbody>
|
22
|
+
<% if @all_topics_count == 0 %>
|
23
|
+
<tr>
|
24
|
+
<td colspan="3" class="text-center">
|
25
|
+
<em>There aren't any subscriptions right now.</em>
|
26
|
+
</td>
|
27
|
+
</tr>
|
28
|
+
<% else %>
|
29
|
+
<% @topics.each do |topic| %>
|
30
|
+
<tr>
|
31
|
+
<td><%= link_to(topic.name, graphql_dashboard.subscriptions_topic_path(name: topic.name)) %></td>
|
32
|
+
<td><%= topic.subscriptions_count %></td>
|
33
|
+
<td><%= topic.last_triggered_at || "--" %></td>
|
34
|
+
</tr>
|
35
|
+
<% end %>
|
36
|
+
<% end %>
|
37
|
+
</tbody>
|
38
|
+
</table>
|
39
|
+
|
40
|
+
<div class="row">
|
41
|
+
<div class="col-auto">
|
42
|
+
<% if @page > 1 %>
|
43
|
+
<%= link_to("« prev", graphql_dashboard.subscriptions_topics_path(per_page: params[:per_page], page: @page - 1), class: "btn btn-outline-secondary") %>
|
44
|
+
<% else %>
|
45
|
+
<button class="btn btn-outline-secondary" disabled>« prev</button>
|
46
|
+
<% end %>
|
47
|
+
</div>
|
48
|
+
<div class="col-auto">
|
49
|
+
<% if @has_next_page %>
|
50
|
+
<%= link_to("next »", graphql_dashboard.subscriptions_topics_path(per_page: params[:per_page], page: @page + 1), class: "btn btn-outline-secondary") %>
|
51
|
+
<% else %>
|
52
|
+
<button class="btn btn-outline-secondary" disabled>next »</button>
|
53
|
+
<% end %>
|
54
|
+
</div>
|
55
|
+
</div>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<%= content_for(:title, "Subscriptions - #{params[:name]}") %>
|
2
|
+
<div class="row mt-3">
|
3
|
+
<div class="col">
|
4
|
+
<h3>Topic: <code><%= params[:name] %></code></h3>
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="row">
|
9
|
+
<div class="col">
|
10
|
+
<p>Last triggered: <%= @topic_last_triggered_at || "none" %></p>
|
11
|
+
<p><%= pluralize(@subscriptions_count, "Subscription") %></p>
|
12
|
+
<div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="row">
|
16
|
+
<div class="col">
|
17
|
+
<table class="table table-striped">
|
18
|
+
<thead>
|
19
|
+
<tr>
|
20
|
+
<th>Subscription ID</th>
|
21
|
+
<th>Created At</th>
|
22
|
+
<th>Subscribed?</th>
|
23
|
+
<th>Broadcast?</th>
|
24
|
+
<% if @show_broadcast_subscribers_count %><th>Subscribers</th><% end %>
|
25
|
+
</tr>
|
26
|
+
</thead>
|
27
|
+
<tbody>
|
28
|
+
<% @subscriptions.each do |subscription| %>
|
29
|
+
<tr>
|
30
|
+
<td><%= link_to(subscription[:id], graphql_dashboard.subscriptions_subscription_path(subscription[:id])) %></td>
|
31
|
+
<td><%= subscription[:created_at] %></td>
|
32
|
+
<td><code><%= subscription[:still_subscribed] ? "YES" : "NO" %></code></td>
|
33
|
+
<td><code><%= subscription[:is_broadcast] ? "YES" : "NO" %></code></td>
|
34
|
+
<% if @show_broadcast_subscribers_count %><td><%= subscription[:subscribers_count] %></td><% end %>
|
35
|
+
</tr>
|
36
|
+
<% end %>
|
37
|
+
</tbody>
|
38
|
+
</table>
|
39
|
+
</div>
|
40
|
+
</div>
|
@@ -6,6 +6,7 @@
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
7
|
<title>GraphQL Dashboard <%= content_for?(:title) ? " · #{content_for(:title)}" : "" %> </title>
|
8
8
|
<%= stylesheet_link_tag graphql_dashboard.static_path("bootstrap-5.3.3.min.css") %>
|
9
|
+
<%= stylesheet_link_tag graphql_dashboard.static_path("charts.min.css") %>
|
9
10
|
<%= stylesheet_link_tag graphql_dashboard.static_path("dashboard.css") %>
|
10
11
|
<%= javascript_include_tag graphql_dashboard.static_path("bootstrap-5.3.3.min.js") %>
|
11
12
|
<%= javascript_include_tag graphql_dashboard.static_path("dashboard.js") %>
|
@@ -27,7 +28,44 @@
|
|
27
28
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
28
29
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
29
30
|
<li class="nav-item">
|
30
|
-
<%= link_to "Traces", graphql_dashboard.
|
31
|
+
<%= link_to "Traces", graphql_dashboard.detailed_traces_traces_path, class: "nav-link #{params[:controller] == "graphql/dashboard/detailed_traces" ? "active" : ""}" %>
|
32
|
+
</li>
|
33
|
+
<li class="nav-item dropdown">
|
34
|
+
<a class="nav-link dropdown-toggle <%= params[:controller].include?("operation_store") ? "active" : "" %>" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
35
|
+
Persisted Operations
|
36
|
+
</a>
|
37
|
+
<ul class="dropdown-menu">
|
38
|
+
<li>
|
39
|
+
<%= link_to "Clients", graphql_dashboard.operation_store_clients_path, class: "dropdown-item" %>
|
40
|
+
</li>
|
41
|
+
<li>
|
42
|
+
<%= link_to "Operations", graphql_dashboard.operation_store_operations_path, class: "dropdown-item" %>
|
43
|
+
</li>
|
44
|
+
<li>
|
45
|
+
<%= link_to "Index", graphql_dashboard.operation_store_index_entries_path, class: "dropdown-item" %>
|
46
|
+
</li>
|
47
|
+
</ul>
|
48
|
+
</li>
|
49
|
+
<li class="nav-item">
|
50
|
+
<%= link_to "Subscriptions", graphql_dashboard.subscriptions_topics_path, class: "nav-link #{params[:controller] == "graphql/dashboard/subscriptions" ? "active" : ""}" %>
|
51
|
+
</li>
|
52
|
+
<li class="nav-item dropdown">
|
53
|
+
<a class="nav-link dropdown-toggle <%= params[:controller].include?("limiters") ? "active" : "" %>" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
54
|
+
Rate Limiters
|
55
|
+
</a>
|
56
|
+
<ul class="dropdown-menu">
|
57
|
+
<li>
|
58
|
+
<%= link_to "Runtime", graphql_dashboard.limiters_limiter_path("runtime", chart: params[:chart]), class: "dropdown-item" %>
|
59
|
+
</li>
|
60
|
+
<li>
|
61
|
+
<%= link_to "Active Operations", graphql_dashboard.limiters_limiter_path("active_operations", chart: params[:chart]), class: "dropdown-item" %>
|
62
|
+
</li>
|
63
|
+
<% if schema_class.respond_to?(:enterprise_mutation_limiter) && schema_class.enterprise_mutation_limiter %>
|
64
|
+
<li>
|
65
|
+
<%= link_to "Mutations", graphql_dashboard.limiters_limiter_path("mutations", chart: params[:chart]), class: "dropdown-item" %>
|
66
|
+
</li>
|
67
|
+
<% end %>
|
68
|
+
</ul>
|
31
69
|
</li>
|
32
70
|
</ul>
|
33
71
|
<span class="navbar-text pe-2">
|
@@ -38,6 +76,16 @@
|
|
38
76
|
</nav>
|
39
77
|
</div>
|
40
78
|
</div>
|
79
|
+
<% flash.each do |flash_type, flash_message| %>
|
80
|
+
<div class="row mt-2">
|
81
|
+
<div class="col">
|
82
|
+
<div class="alert alert-<%= flash_type %> alert-dismissible fade show" role="alert">
|
83
|
+
<%= flash_message %>
|
84
|
+
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
</div>
|
88
|
+
<% end %>
|
41
89
|
</div>
|
42
90
|
<div class="container-fluid">
|
43
91
|
<%= yield %>
|
data/lib/graphql/dashboard.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'rails/engine'
|
3
|
-
|
4
3
|
module Graphql
|
5
4
|
# `GraphQL::Dashboard` is a `Rails::Engine`-based dashboard for viewing metadata about your GraphQL schema.
|
6
5
|
#
|
@@ -38,8 +37,45 @@ module Graphql
|
|
38
37
|
routes.draw do
|
39
38
|
root "landings#show"
|
40
39
|
resources :statics, only: :show, constraints: { id: /[0-9A-Za-z\-.]+/ }
|
41
|
-
|
42
|
-
|
40
|
+
|
41
|
+
namespace :detailed_traces do
|
42
|
+
resources :traces, only: [:index, :show, :destroy] do
|
43
|
+
collection do
|
44
|
+
delete :delete_all, to: "traces#delete_all", as: :delete_all
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
namespace :limiters do
|
50
|
+
resources :limiters, only: [:show, :update], param: :name
|
51
|
+
end
|
52
|
+
|
53
|
+
namespace :operation_store do
|
54
|
+
resources :clients, param: :name do
|
55
|
+
resources :operations, param: :digest, only: [:index] do
|
56
|
+
collection do
|
57
|
+
get :archived, to: "operations#index", archived_status: :archived, as: :archived
|
58
|
+
post :archive, to: "operations#update", modification: :archive, as: :archive
|
59
|
+
post :unarchive, to: "operations#update", modification: :unarchive, as: :unarchive
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
resources :operations, param: :digest, only: [:index, :show] do
|
65
|
+
collection do
|
66
|
+
get :archived, to: "operations#index", archived_status: :archived, as: :archived
|
67
|
+
post :archive, to: "operations#update", modification: :archive, as: :archive
|
68
|
+
post :unarchive, to: "operations#update", modification: :unarchive, as: :unarchive
|
69
|
+
end
|
70
|
+
end
|
71
|
+
resources :index_entries, only: [:index, :show], param: :name, constraints: { name: /[A-Za-z0-9_.]+/}
|
72
|
+
end
|
73
|
+
|
74
|
+
namespace :subscriptions do
|
75
|
+
resources :topics, only: [:index, :show], param: :name, constraints: { name: /.*/ }
|
76
|
+
resources :subscriptions, only: [:show], constraints: { id: /[a-zA-Z0-9\-]+/ }
|
77
|
+
post "/subscriptions/clear_all", to: "subscriptions#clear_all", as: :clear_all
|
78
|
+
end
|
43
79
|
end
|
44
80
|
|
45
81
|
class ApplicationController < ActionController::Base
|
@@ -80,32 +116,6 @@ module Graphql
|
|
80
116
|
end
|
81
117
|
end
|
82
118
|
|
83
|
-
class TracesController < ApplicationController
|
84
|
-
def index
|
85
|
-
@detailed_trace_installed = !!schema_class.detailed_trace
|
86
|
-
if @detailed_trace_installed
|
87
|
-
@last = params[:last]&.to_i || 50
|
88
|
-
@before = params[:before]&.to_i
|
89
|
-
@traces = schema_class.detailed_trace.traces(last: @last, before: @before)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def show
|
94
|
-
trace = schema_class.detailed_trace.find_trace(params[:id].to_i)
|
95
|
-
send_data(trace.trace_data)
|
96
|
-
end
|
97
|
-
|
98
|
-
def destroy
|
99
|
-
schema_class.detailed_trace.delete_trace(params[:id])
|
100
|
-
head :no_content
|
101
|
-
end
|
102
|
-
|
103
|
-
def delete_all
|
104
|
-
schema_class.detailed_trace.delete_all_traces
|
105
|
-
head :no_content
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
119
|
class StaticsController < ApplicationController
|
110
120
|
skip_after_action :verify_same_origin_request
|
111
121
|
# Use an explicit list of files to avoid any chance of reading other files from disk
|
@@ -114,6 +124,7 @@ module Graphql
|
|
114
124
|
[
|
115
125
|
"icon.png",
|
116
126
|
"header-icon.png",
|
127
|
+
"charts.min.css",
|
117
128
|
"dashboard.css",
|
118
129
|
"dashboard.js",
|
119
130
|
"bootstrap-5.3.3.min.css",
|
@@ -134,6 +145,11 @@ module Graphql
|
|
134
145
|
end
|
135
146
|
end
|
136
147
|
|
148
|
+
require 'graphql/dashboard/detailed_traces'
|
149
|
+
require 'graphql/dashboard/limiters'
|
150
|
+
require 'graphql/dashboard/operation_store'
|
151
|
+
require 'graphql/dashboard/subscriptions'
|
152
|
+
|
137
153
|
# Rails expects the engine to be called `Graphql::Dashboard`,
|
138
154
|
# but `GraphQL::Dashboard` is consistent with this gem's naming.
|
139
155
|
# So define both constants to refer to the same class.
|
@@ -23,7 +23,7 @@ module GraphQL
|
|
23
23
|
# @return [Array<GraphQL::Query::Result>] One result per query
|
24
24
|
def run_all(schema, query_options, context: {}, max_complexity: schema.max_complexity)
|
25
25
|
queries = query_options.map do |opts|
|
26
|
-
case opts
|
26
|
+
query = case opts
|
27
27
|
when Hash
|
28
28
|
schema.query_class.new(schema, nil, **opts)
|
29
29
|
when GraphQL::Query
|
@@ -31,14 +31,14 @@ module GraphQL
|
|
31
31
|
else
|
32
32
|
raise "Expected Hash or GraphQL::Query, not #{opts.class} (#{opts.inspect})"
|
33
33
|
end
|
34
|
+
query
|
34
35
|
end
|
35
36
|
|
36
37
|
return GraphQL::EmptyObjects::EMPTY_ARRAY if queries.empty?
|
37
38
|
|
38
39
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
39
|
-
Fiber[:__graphql_current_multiplex] = multiplex
|
40
40
|
trace = multiplex.current_trace
|
41
|
-
|
41
|
+
Fiber[:__graphql_current_multiplex] = multiplex
|
42
42
|
trace.execute_multiplex(multiplex: multiplex) do
|
43
43
|
schema = multiplex.schema
|
44
44
|
queries = multiplex.queries
|
@@ -155,8 +155,6 @@ module GraphQL
|
|
155
155
|
}
|
156
156
|
end
|
157
157
|
end
|
158
|
-
ensure
|
159
|
-
trace&.end_execute_multiplex(multiplex)
|
160
158
|
end
|
161
159
|
end
|
162
160
|
|
@@ -32,10 +32,10 @@ module GraphQL
|
|
32
32
|
@queries = queries
|
33
33
|
@queries.each { |q| q.multiplex = self }
|
34
34
|
@context = context
|
35
|
-
@current_trace = @context[:trace] || schema.new_trace(multiplex: self)
|
36
35
|
@dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
|
37
36
|
@tracers = schema.tracers + (context[:tracers] || [])
|
38
37
|
@max_complexity = max_complexity
|
38
|
+
@current_trace = context[:trace] ||= schema.new_trace(multiplex: self)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -488,26 +488,33 @@ module GraphQL
|
|
488
488
|
end
|
489
489
|
|
490
490
|
def type
|
491
|
-
|
491
|
+
parsed_type = case token_name
|
492
492
|
when :IDENTIFIER
|
493
493
|
parse_type_name
|
494
494
|
when :LBRACKET
|
495
495
|
list_type
|
496
|
+
else
|
497
|
+
nil
|
496
498
|
end
|
497
499
|
|
498
|
-
if at?(:BANG)
|
499
|
-
|
500
|
+
if at?(:BANG) && parsed_type
|
501
|
+
parsed_type = Nodes::NonNullType.new(pos: pos, of_type: parsed_type, source: self)
|
500
502
|
expect_token(:BANG)
|
501
503
|
end
|
502
|
-
|
504
|
+
parsed_type
|
503
505
|
end
|
504
506
|
|
505
507
|
def list_type
|
506
508
|
loc = pos
|
507
509
|
expect_token(:LBRACKET)
|
508
|
-
|
510
|
+
inner_type = self.type
|
511
|
+
parsed_list_type = if inner_type
|
512
|
+
Nodes::ListType.new(pos: loc, of_type: inner_type, source: self)
|
513
|
+
else
|
514
|
+
nil
|
515
|
+
end
|
509
516
|
expect_token(:RBRACKET)
|
510
|
-
|
517
|
+
parsed_list_type
|
511
518
|
end
|
512
519
|
|
513
520
|
def parse_operation_type
|
data/lib/graphql/query.rb
CHANGED
@@ -98,9 +98,10 @@ module GraphQL
|
|
98
98
|
# @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
|
99
99
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
100
100
|
# @param visibility_profile [Symbol] Another way to assign `context[:visibility_profile]`
|
101
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
|
101
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, multiplex: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
|
102
102
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
103
103
|
variables ||= {}
|
104
|
+
@multiplex = multiplex
|
104
105
|
@schema = schema
|
105
106
|
@context = schema.context_class.new(query: self, values: context)
|
106
107
|
if visibility_profile
|
@@ -441,7 +442,6 @@ module GraphQL
|
|
441
442
|
@warden ||= @schema.warden_class.new(schema: @schema, context: @context)
|
442
443
|
parse_error = nil
|
443
444
|
@document ||= begin
|
444
|
-
current_trace.begin_parse(query_string)
|
445
445
|
if query_string
|
446
446
|
GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
|
447
447
|
end
|
@@ -449,8 +449,6 @@ module GraphQL
|
|
449
449
|
parse_error = err
|
450
450
|
@schema.parse_error(err, @context)
|
451
451
|
nil
|
452
|
-
ensure
|
453
|
-
current_trace.end_parse(query_string)
|
454
452
|
end
|
455
453
|
|
456
454
|
@fragments = {}
|
@@ -34,7 +34,7 @@ module GraphQL
|
|
34
34
|
GraphQL::StaticValidation::VariableUsagesAreAllowed,
|
35
35
|
GraphQL::StaticValidation::MutationRootExists,
|
36
36
|
GraphQL::StaticValidation::QueryRootExists,
|
37
|
-
GraphQL::StaticValidation::
|
37
|
+
GraphQL::StaticValidation::SubscriptionRootExistsAndSingleSubscriptionSelection,
|
38
38
|
GraphQL::StaticValidation::InputObjectNamesAreUnique,
|
39
39
|
GraphQL::StaticValidation::OneOfInputObjectsAreValid,
|
40
40
|
]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class NotSingleSubscriptionError < 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
|
+
"notSingleSubscription"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
module SubscriptionRootExistsAndSingleSubscriptionSelection
|
5
|
+
def on_operation_definition(node, parent)
|
6
|
+
if node.operation_type == "subscription"
|
7
|
+
if context.types.subscription_root.nil?
|
8
|
+
add_error(GraphQL::StaticValidation::SubscriptionRootExistsError.new(
|
9
|
+
'Schema is not configured for subscriptions',
|
10
|
+
nodes: node
|
11
|
+
))
|
12
|
+
elsif node.selections.size != 1
|
13
|
+
add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
|
14
|
+
'A subscription operation may only have one selection',
|
15
|
+
nodes: node,
|
16
|
+
))
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -10,6 +10,13 @@ module GraphQL
|
|
10
10
|
# class MySchema < GraphQL::Schema
|
11
11
|
# trace_with(GraphQL::Tracing::ActiveSupportNotificationsTrace)
|
12
12
|
# end
|
13
|
+
#
|
14
|
+
# @example Subscribing to GraphQL events with ActiveSupport::Notifications
|
15
|
+
# ActiveSupport::Notifications.subscribe(/graphql/) do |event|
|
16
|
+
# pp event.name
|
17
|
+
# pp event.payload
|
18
|
+
# end
|
19
|
+
#
|
13
20
|
module ActiveSupportNotificationsTrace
|
14
21
|
include NotificationsTrace
|
15
22
|
def initialize(engine: ActiveSupport::Notifications, **rest)
|
@@ -22,6 +22,11 @@ module GraphQL
|
|
22
22
|
# These GraphQL events will show up as 'graphql.execute' spans
|
23
23
|
EXEC_KEYS = ['execute_multiplex', 'execute_query', 'execute_query_lazy'].freeze
|
24
24
|
|
25
|
+
def initialize(...)
|
26
|
+
warn "GraphQL::Tracing::AppOptics tracing is deprecated; update to SolarWindsAPM instead, which uses OpenTelemetry."
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
25
30
|
# During auto-instrumentation this version of AppOpticsTracing is compared
|
26
31
|
# with the version provided in the appoptics_apm gem, so that the newer
|
27
32
|
# version of the class can be used
|