graphql 2.4.13 → 2.5.11
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/analysis/query_complexity.rb +87 -7
- data/lib/graphql/backtrace/table.rb +37 -14
- data/lib/graphql/current.rb +1 -1
- 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/dataloader/active_record_association_source.rb +28 -8
- data/lib/graphql/dataloader/active_record_source.rb +26 -5
- data/lib/graphql/dataloader/null_dataloader.rb +7 -0
- data/lib/graphql/dataloader/source.rb +16 -4
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -1
- data/lib/graphql/execution/interpreter/runtime.rb +163 -59
- data/lib/graphql/execution/interpreter.rb +5 -13
- data/lib/graphql/execution/multiplex.rb +6 -1
- data/lib/graphql/invalid_null_error.rb +15 -2
- data/lib/graphql/language/lexer.rb +9 -2
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +14 -6
- data/lib/graphql/query/context.rb +3 -8
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +59 -55
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +9 -3
- data/lib/graphql/schema/build_from_definition.rb +96 -47
- data/lib/graphql/schema/directive/flagged.rb +2 -0
- data/lib/graphql/schema/directive.rb +33 -1
- data/lib/graphql/schema/field.rb +23 -1
- data/lib/graphql/schema/input_object.rb +38 -30
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_dataloader.rb +4 -2
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +1 -0
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +15 -6
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +107 -21
- data/lib/graphql/schema/visibility.rb +41 -29
- data/lib/graphql/schema/warden.rb +13 -5
- data/lib/graphql/schema.rb +228 -32
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +78 -16
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- 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/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/testing/helpers.rb +5 -2
- 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 +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +34 -164
- data/lib/graphql/tracing/notifications_trace.rb +183 -37
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +16 -19
- 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 +56 -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/type_kinds.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +35 -26
- 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,69 @@
|
|
1
|
+
<% content_for(:title, "Clients") %>
|
2
|
+
<div class="row mt-3 justify-content-between">
|
3
|
+
<div class="col-4">
|
4
|
+
<h3>
|
5
|
+
<%= pluralize(@clients_page.total_count, "Client") %>
|
6
|
+
</h3>
|
7
|
+
</div>
|
8
|
+
<div class="col-auto">
|
9
|
+
<%= link_to("New Client", graphql_dashboard.new_operation_store_client_path, class: "btn btn-outline-primary") %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<table class="table table-striped">
|
14
|
+
<thead>
|
15
|
+
<tr>
|
16
|
+
<th><%= link_to("Name", graphql_dashboard.operation_store_clients_path, params: { order_by: "name", order_dir: ((@order_by == "name" && @order_dir != :desc) ? "desc" : "asc" )}) %></th>
|
17
|
+
<th>Operations</th>
|
18
|
+
<th>Created At</th>
|
19
|
+
<th>Last Updated</th>
|
20
|
+
<th><%= link_to("Last Used At", graphql_dashboard.operation_store_clients_path, params: { order_by: "last_used_at", order_dir: ((@order_by == "last_used_at" && @order_dir != :desc) ? "desc": "asc")}) %></th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
<tbody>
|
24
|
+
<% if @clients_page.total_count == 0 %>
|
25
|
+
<tr>
|
26
|
+
<td colspan="5" class="text-center">
|
27
|
+
<em>To get started, create a <%= link_to "new client", graphql_dashboard.new_operation_store_client_path %>, then <%= link_to "sync operations", "https://graphql-ruby.org/operation_store/client_workflow.html" %> to your schema.</em>
|
28
|
+
</td>
|
29
|
+
</tr>
|
30
|
+
<% else %>
|
31
|
+
<% @clients_page.items.each do |client| %>
|
32
|
+
<tr>
|
33
|
+
<td><%= link_to(client.name, graphql_dashboard.edit_operation_store_client_path(name: client.name)) %></td>
|
34
|
+
<td>
|
35
|
+
<%= link_to(graphql_dashboard.operation_store_client_operations_path(client_name: client.name)) do %>
|
36
|
+
<%= client.operations_count %><% if client.archived_operations_count > 0 %> <span class="muted">(<%=client.archived_operations_count%> archived)</span><% end %>
|
37
|
+
<% end %>
|
38
|
+
</td>
|
39
|
+
<td><%= client.created_at %></td>
|
40
|
+
<td>
|
41
|
+
<% if client.operations_count == 0 %>
|
42
|
+
—
|
43
|
+
<% else %>
|
44
|
+
<%= client.last_synced_at %>
|
45
|
+
<% end %>
|
46
|
+
</td>
|
47
|
+
<td><%= client.last_used_at || "—" %></td>
|
48
|
+
</tr>
|
49
|
+
<% end %>
|
50
|
+
<% end %>
|
51
|
+
</tbody>
|
52
|
+
</table>
|
53
|
+
|
54
|
+
<div class="row">
|
55
|
+
<div class="col-auto">
|
56
|
+
<% if @clients_page.prev_page %>
|
57
|
+
<%= link_to("« prev", graphql_dashboard.operation_store_clients_path(per_page: params[:per_page], page: @clients_page.prev_page), class: "btn btn-outline-secondary") %>
|
58
|
+
<% else %>
|
59
|
+
<button class="btn btn-outline-secondary" disabled>« prev</button>
|
60
|
+
<% end %>
|
61
|
+
</div>
|
62
|
+
<div class="col-auto">
|
63
|
+
<% if @clients_page.next_page %>
|
64
|
+
<%= link_to("next »", graphql_dashboard.operation_store_clients_path(per_page: params[:per_page], page: @clients_page.next_page), class: "btn btn-outline-secondary") %>
|
65
|
+
<% else %>
|
66
|
+
<button class="btn btn-outline-secondary" disabled>next »</button>
|
67
|
+
<% end %>
|
68
|
+
</div>
|
69
|
+
</div>
|
data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
<% content_for(:title, "Index#{@search_term ? " - #{@search_term}" : ""}") %>
|
2
|
+
<div class="row mt-2">
|
3
|
+
<h3>Schema Index</h3>
|
4
|
+
<div class="col">
|
5
|
+
<p style="margin-left: 15px;">
|
6
|
+
<%= pluralize(@index_entries_page.total_count, @search_term ? "result" : "entry") %>
|
7
|
+
</p>
|
8
|
+
</div>
|
9
|
+
<div class="col">
|
10
|
+
<form method="GET" action="<%= graphql_dashboard.operation_store_index_entries_path %>" style="margin-left: auto; margin-top:-5px;">
|
11
|
+
<div class="input-group">
|
12
|
+
<%= text_field_tag "q", @search_term, class: "form-control", placeholder: "Find types, fields, arguments, or enum values" %>
|
13
|
+
<input type="submit" value="Search" class="btn btn-outline-primary btn-sm"/>
|
14
|
+
</div>
|
15
|
+
</form>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
<table class="table">
|
19
|
+
<thead>
|
20
|
+
<tr>
|
21
|
+
<th>Name</th>
|
22
|
+
<th># Usages</th>
|
23
|
+
<th>Last Used At</th>
|
24
|
+
</tr>
|
25
|
+
</thead>
|
26
|
+
<tbody>
|
27
|
+
<% @index_entries_page.items.each do |entry| %>
|
28
|
+
<tr>
|
29
|
+
<td><%= link_to(entry.name, graphql_dashboard.operation_store_index_entry_path(name: entry.name)) %></td>
|
30
|
+
<td><%= entry.references_count %><% if entry.archived_references_count.nil? %><span class="muted">(missing data - call `YourSchema.operation_store.reindex` to repair index)</span><% elsif entry.archived_references_count > 0 %> <span class="muted">(<%= entry.archived_references_count %> archived)</span><% end %></td>
|
31
|
+
<td><%= entry.last_used_at %></td>
|
32
|
+
</tr>
|
33
|
+
<% end %>
|
34
|
+
</tbody>
|
35
|
+
</table>
|
36
|
+
|
37
|
+
<%=
|
38
|
+
# render_partial("_pagination")
|
39
|
+
%>
|
data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
<% name = @chain.pop %>
|
2
|
+
<% content_for(:title, "Index - #{@entry.name}") %>
|
3
|
+
<div class="row mt-2">
|
4
|
+
<div class="col">
|
5
|
+
<%= link_to("Index", graphql_dashboard.operation_store_index_entries_path) %>
|
6
|
+
<% @chain.each do |c| %>
|
7
|
+
> <%= link_to(c.split(".").last, graphql_dashboard.operation_store_index_entry_path(name: c)) %>
|
8
|
+
<% end %>
|
9
|
+
> <%= name.split(".").last %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
<div class="row mt-2">
|
13
|
+
<div class="col">
|
14
|
+
<h3><%= name %></h3>
|
15
|
+
<p>
|
16
|
+
Used By:
|
17
|
+
<% if @operations.any? %>
|
18
|
+
<ul>
|
19
|
+
<% @operations.each do |operation| %>
|
20
|
+
<li>
|
21
|
+
<%= link_to(operation.name, graphql_dashboard.operation_store_operation_path(digest: operation.digest)) %><% if operation.is_archived %> <span class="muted">(archived)</span><% end %>
|
22
|
+
</li>
|
23
|
+
<% end %>
|
24
|
+
</ul>
|
25
|
+
<% else %>
|
26
|
+
<i>none</i>
|
27
|
+
<% end %>
|
28
|
+
</p>
|
29
|
+
|
30
|
+
<p>Last used at: <%= @entry.last_used_at || "—" %></p>
|
31
|
+
</div>
|
32
|
+
</div>
|
@@ -0,0 +1,81 @@
|
|
1
|
+
<div class="row mt-2">
|
2
|
+
<% if @client_operations %>
|
3
|
+
<%= content_for(:title, "#{params[:client_name]} Operations") %>
|
4
|
+
<div class="col">
|
5
|
+
<h3><%= params[:client_name] %></h3>
|
6
|
+
<ul class="nav nav-tabs">
|
7
|
+
<li class="nav-item">
|
8
|
+
<%= link_to "#{@unarchived_operations_count} Active", graphql_dashboard.operation_store_client_operations_path(client_name: params[:client_name]), class: "nav-link #{@is_archived ? "" : "active"}" %>
|
9
|
+
</li>
|
10
|
+
<li class="nav-item">
|
11
|
+
<%= link_to "#{@archived_operations_count} Archived", graphql_dashboard.archived_operation_store_client_operations_path(client_name: params[:client_name]), class: "nav-link #{@is_archived ? "active" : ""}" %>
|
12
|
+
</li>
|
13
|
+
</ul>
|
14
|
+
</div>
|
15
|
+
<% else %>
|
16
|
+
<%= content_for(:title, "Operations") %>
|
17
|
+
<div class="col">
|
18
|
+
<ul class="nav nav-tabs">
|
19
|
+
<li class="nav-item">
|
20
|
+
<%= link_to "#{@unarchived_operations_count} Active", graphql_dashboard.operation_store_operations_path, class: "nav-link #{@is_archived ? "" : "active"}" %>
|
21
|
+
</li>
|
22
|
+
<li class="nav-item">
|
23
|
+
<%= link_to "#{@archived_operations_count} Archived", graphql_dashboard.archived_operation_store_operations_path, class: "nav-link #{@is_archived ? "active" : ""}" %>
|
24
|
+
</li>
|
25
|
+
</ul>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div class="row">
|
31
|
+
<div class="col">
|
32
|
+
<table class="table table-striped">
|
33
|
+
<thead>
|
34
|
+
<tr>
|
35
|
+
<th><%= link_to "Name", graphql_dashboard.operation_store_operations_path({ order_by: "name", order_dir: params[:order_dir] == "asc" ? "desc" : "asc" }) %></th>
|
36
|
+
<% if @client_operations %>
|
37
|
+
<th>Alias</th>
|
38
|
+
<% else %>
|
39
|
+
<th># Clients</th>
|
40
|
+
<% end %>
|
41
|
+
<th>Digest</th>
|
42
|
+
<th><%= link_to "Last Used At", graphql_dashboard.operation_store_operations_path({ order_by: "last_used_at", order_dir: params[:order_dir] == "asc" ? "desc" : "asc" }) %></th>
|
43
|
+
<th>
|
44
|
+
<button class="btn btn-sm btn-outline-primary" data-archive-client="<%= params[:client_name] %>" data-archive-all="<%= params[:client_name] ? nil : "true" %>">
|
45
|
+
<%= @is_archived ? "Unarchive" : "Archive" %>
|
46
|
+
</button>
|
47
|
+
</th>
|
48
|
+
</tr>
|
49
|
+
</thead>
|
50
|
+
<tbody>
|
51
|
+
<% if @operations_page.total_count == 0 %>
|
52
|
+
<tr>
|
53
|
+
<td colspan="5" class="text-center">
|
54
|
+
<% if @is_archived %>
|
55
|
+
<em><%= link_to "Archived operations", "https://graphql-ruby.org/operation_store/server_management.html#archiving-and-deleting-data" %> will appear here.</em>
|
56
|
+
<% else %>
|
57
|
+
<em>Add your first stored operations with <%= link_to "sync", "https://graphql-ruby.org/operation_store/client_workflow.html" %>.</em>
|
58
|
+
<% end %>
|
59
|
+
</td>
|
60
|
+
</tr>
|
61
|
+
<% else %>
|
62
|
+
<% @operations_page.items.each do |operation| %>
|
63
|
+
<tr>
|
64
|
+
<td><%= link_to(operation.name, graphql_dashboard.operation_store_operation_path(digest: operation.digest)) %></td>
|
65
|
+
<% if @client_operations %>
|
66
|
+
<td><code><%= operation.operation_alias %></code></td>
|
67
|
+
<% else %>
|
68
|
+
<td><%= operation.clients_count %></td>
|
69
|
+
<% end %>
|
70
|
+
<td><code><%= operation.digest %></code></td>
|
71
|
+
<td><%= operation.last_used_at %></td>
|
72
|
+
<td>
|
73
|
+
<%= check_box_tag("value", (@client_operations ? operation.operation_alias : operation.digest), class: "archive-check form-check-input") %>
|
74
|
+
</td>
|
75
|
+
</tr>
|
76
|
+
<% end %>
|
77
|
+
<% end %>
|
78
|
+
</tbody>
|
79
|
+
</table>
|
80
|
+
</div>
|
81
|
+
</div>
|
@@ -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.
|