graphql 2.4.15 → 2.4.16

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  3. data/lib/graphql/dashboard/installable.rb +22 -0
  4. data/lib/graphql/dashboard/limiters.rb +93 -0
  5. data/lib/graphql/dashboard/operation_store.rb +199 -0
  6. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  7. data/lib/graphql/dashboard/statics/dashboard.css +27 -0
  8. data/lib/graphql/dashboard/statics/dashboard.js +74 -9
  9. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  10. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  11. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  12. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  13. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  14. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  16. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  17. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  18. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  19. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  20. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  24. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
  25. data/lib/graphql/dashboard.rb +45 -29
  26. data/lib/graphql/execution/interpreter.rb +3 -2
  27. data/lib/graphql/execution/multiplex.rb +1 -1
  28. data/lib/graphql/language/parser.rb +13 -6
  29. data/lib/graphql/query.rb +2 -1
  30. data/lib/graphql/tracing/perfetto_trace.rb +4 -4
  31. data/lib/graphql/version.rb +1 -1
  32. metadata +22 -3
  33. data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +0 -63
@@ -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.traces_path, class: "nav-link #{params[:controller] == "graphql/dashboard/traces" ? "active" : ""}" %>
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 %>
@@ -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
- delete "/traces/delete_all", to: "traces#delete_all", as: :traces_delete_all
42
- resources :traces, only: [:index, :show, :destroy]
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,13 +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
+ Fiber[:__graphql_current_multiplex] = multiplex
41
42
  trace.begin_execute_multiplex(multiplex)
42
43
  trace.execute_multiplex(multiplex: multiplex) do
43
44
  schema = multiplex.schema
@@ -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
- type = case token_name
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
- type = Nodes::NonNullType.new(pos: pos, of_type: type, source: self)
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
- type
504
+ parsed_type
503
505
  end
504
506
 
505
507
  def list_type
506
508
  loc = pos
507
509
  expect_token(:LBRACKET)
508
- type = Nodes::ListType.new(pos: loc, of_type: self.type, source: self)
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
- type
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
@@ -63,6 +63,7 @@ module GraphQL
63
63
  # @param active_support_notifications_pattern [String, RegExp, false] A filter for `ActiveSupport::Notifications`, if it's present. Or `false` to skip subscribing.
64
64
  def initialize(active_support_notifications_pattern: nil, save_profile: false, **_rest)
65
65
  super
66
+ @active_support_notifications_pattern = active_support_notifications_pattern
66
67
  @save_profile = save_profile
67
68
  Fiber[:graphql_flow_stack] = nil
68
69
  @sequence_id = object_id
@@ -168,13 +169,12 @@ module GraphQL
168
169
  track_uuid: @fields_counter_id,
169
170
  counter_value: count_fields,
170
171
  )
171
-
172
- if defined?(ActiveSupport::Notifications) && active_support_notifications_pattern != false
173
- subscribe_to_active_support_notifications(active_support_notifications_pattern)
174
- end
175
172
  end
176
173
 
177
174
  def begin_execute_multiplex(m)
175
+ if defined?(ActiveSupport::Notifications) && @active_support_notifications_pattern != false
176
+ subscribe_to_active_support_notifications(@active_support_notifications_pattern)
177
+ end
178
178
  @operation_name = m.queries.map { |q| q.selected_operation_name || "anonymous" }.join(",")
179
179
  @begin_time = Time.now
180
180
  @packets << trace_packet(
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.4.15"
3
+ VERSION = "2.4.16"
4
4
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.15
4
+ version: 2.4.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-19 00:00:00.000000000 Z
10
+ date: 2025-04-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -432,14 +432,33 @@ files:
432
432
  - lib/graphql/coercion_error.rb
433
433
  - lib/graphql/current.rb
434
434
  - lib/graphql/dashboard.rb
435
+ - lib/graphql/dashboard/detailed_traces.rb
436
+ - lib/graphql/dashboard/installable.rb
437
+ - lib/graphql/dashboard/limiters.rb
438
+ - lib/graphql/dashboard/operation_store.rb
435
439
  - lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css
436
440
  - lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js
441
+ - lib/graphql/dashboard/statics/charts.min.css
437
442
  - lib/graphql/dashboard/statics/dashboard.css
438
443
  - lib/graphql/dashboard/statics/dashboard.js
439
444
  - lib/graphql/dashboard/statics/header-icon.png
440
445
  - lib/graphql/dashboard/statics/icon.png
446
+ - lib/graphql/dashboard/subscriptions.rb
447
+ - lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb
441
448
  - lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb
442
- - lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb
449
+ - lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb
450
+ - lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb
451
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb
452
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb
453
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb
454
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb
455
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb
456
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb
457
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb
458
+ - lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb
459
+ - lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb
460
+ - lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb
461
+ - lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb
443
462
  - lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb
444
463
  - lib/graphql/dataloader.rb
445
464
  - lib/graphql/dataloader/active_record_association_source.rb
@@ -1,63 +0,0 @@
1
- <% content_for(:title, "Profiles") %>
2
- <div class="row">
3
- <div class="col">
4
- <h1>Detailed Profiles</h1>
5
- </div>
6
- </div>
7
- <% if !@detailed_trace_installed %>
8
- <div class="row">
9
- <div class="col-md col-lg-6 mx-auto">
10
- <div class="card mt-4">
11
- <div class="card-body">
12
- <div class="card-title">
13
- <h3>
14
- Traces aren't installed yet
15
- </h3>
16
- </div>
17
- <p class="card-text">
18
- GraphQL-Ruby can instrument production traffic and save tracing artifacts here for later review.
19
- </p>
20
- <p class="card-text">
21
- Read more in <%= link_to "the tracing docs", "https://graphql-ruby.org/queries/tracing#detailed-traces" %>.
22
- </p>
23
- </div>
24
- </div>
25
- </div>
26
- </div>
27
- <% else %>
28
- <div class="row">
29
- <div class="col">
30
- <table class="table table-striped">
31
- <thead>
32
- <tr>
33
- <th>Operation</th>
34
- <th>Duration (ms) </th>
35
- <th>Timestamp</th>
36
- <th>Open in Perfetto UI</th>
37
- </tr>
38
- </thead>
39
- <tbody>
40
- <% if @traces.empty? %>
41
- <tr>
42
- <td colspan="4" class="text-center">
43
- <em>No traces saved yet. Read about saving traces <%= link_to "in the docs", "https://graphql-ruby.org/queries/tracing#detailed-profiles" %>.</em>
44
- </td>
45
- </tr>
46
- <% end %>
47
- <% @traces.each do |trace| %>
48
- <tr>
49
- <td><%= trace.operation_name %></td>
50
- <td><%= trace.duration_ms.round(2) %></td>
51
- <td><%= Time.at(trace.begin_ms / 1000.0).strftime("%Y-%m-%d %H:%M:%S.%L") %></td>
52
- <td><%= link_to "View ↗", "#", data: { perfetto_open: trace.operation_name, perfetto_path: "#{graphql_dashboard.traces_path}/#{trace.id}" } %></td>
53
- <td><%= link_to "Delete", "#", data: { perfetto_delete: "#{graphql_dashboard.traces_path}/#{trace.id}" }, class: "text-danger" %></td>
54
- </tr>
55
- <% end %>
56
- </tbody>
57
- </table>
58
- <% if @last && @traces.size >= @last %>
59
- <%= link_to("Previous >", graphql_dashboard.traces_path(last: @last, before: @traces.last.begin_ms), class: "btn btn-outline-primary") %>
60
- <% end %>
61
- </div>
62
- </div>
63
- <% end %>