graphql 2.4.3 → 2.5.2
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/analyzer.rb +2 -1
- data/lib/graphql/analysis/visitor.rb +38 -41
- data/lib/graphql/analysis.rb +15 -12
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -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/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- 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/landings/show.html.erb +18 -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 +108 -0
- data/lib/graphql/dashboard.rb +158 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
- data/lib/graphql/dataloader/active_record_source.rb +26 -0
- data/lib/graphql/dataloader/async_dataloader.rb +21 -9
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +3 -3
- data/lib/graphql/dataloader.rb +43 -14
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -4
- data/lib/graphql/execution/interpreter/runtime.rb +94 -51
- data/lib/graphql/execution/interpreter.rb +16 -7
- data/lib/graphql/execution/multiplex.rb +1 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +5 -15
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/document_from_schema_definition.rb +8 -7
- data/lib/graphql/language/lexer.rb +11 -4
- data/lib/graphql/language/nodes.rb +3 -0
- data/lib/graphql/language/parser.rb +15 -8
- data/lib/graphql/language/printer.rb +8 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +6 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +19 -23
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/schema/addition.rb +1 -1
- data/lib/graphql/schema/argument.rb +7 -8
- data/lib/graphql/schema/build_from_definition.rb +99 -53
- data/lib/graphql/schema/directive/flagged.rb +3 -1
- data/lib/graphql/schema/directive.rb +2 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +27 -13
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +3 -1
- data/lib/graphql/schema/input_object.rb +77 -40
- data/lib/graphql/schema/interface.rb +3 -2
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +25 -17
- data/lib/graphql/schema/member/has_dataloader.rb +60 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +19 -1
- data/lib/graphql/schema/member/has_interfaces.rb +5 -5
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +25 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -10
- data/lib/graphql/schema/subscription.rb +52 -6
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/validator.rb +1 -1
- data/lib/graphql/schema/visibility/migration.rb +1 -0
- data/lib/graphql/schema/visibility/profile.rb +95 -243
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +169 -28
- data/lib/graphql/schema/warden.rb +18 -5
- data/lib/graphql/schema.rb +93 -44
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- 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 +1 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +1 -0
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -1
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +7 -4
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +93 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +182 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +734 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +62 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/types/relay/connection_behaviors.rb +3 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +2 -2
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +55 -47
- metadata +146 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Graphql
|
3
|
+
class Dashboard < Rails::Engine
|
4
|
+
module Subscriptions
|
5
|
+
class BaseController < Graphql::Dashboard::ApplicationController
|
6
|
+
include Installable
|
7
|
+
|
8
|
+
def feature_installed?
|
9
|
+
schema_class.subscriptions.is_a?(GraphQL::Pro::Subscriptions)
|
10
|
+
end
|
11
|
+
|
12
|
+
INSTALLABLE_COMPONENT_HEADER_HTML = "GraphQL-Pro Subscriptions aren't installed on this schema yet.".html_safe
|
13
|
+
INSTALLABLE_COMPONENT_MESSAGE_HTML = <<-HTML.html_safe
|
14
|
+
Deliver live updates over
|
15
|
+
<a href="https://graphql-ruby.org/subscriptions/pusher_implementation.html">Pusher</a> or
|
16
|
+
<a href="https://graphql-ruby.org/subscriptions/ably_implementation.html"> Ably</a>
|
17
|
+
with GraphQL-Pro's subscription integrations.
|
18
|
+
HTML
|
19
|
+
end
|
20
|
+
|
21
|
+
class TopicsController < BaseController
|
22
|
+
def show
|
23
|
+
topic_name = params[:name]
|
24
|
+
all_subscription_ids = []
|
25
|
+
schema_class.subscriptions.each_subscription_id(topic_name) do |sid|
|
26
|
+
all_subscription_ids << sid
|
27
|
+
end
|
28
|
+
|
29
|
+
page = params[:page]&.to_i || 1
|
30
|
+
limit = params[:per_page]&.to_i || 20
|
31
|
+
offset = limit * (page - 1)
|
32
|
+
subscription_ids = all_subscription_ids[offset, limit]
|
33
|
+
subs = schema_class.subscriptions.read_subscriptions(subscription_ids)
|
34
|
+
show_broadcast_subscribers_count = schema_class.subscriptions.show_broadcast_subscribers_count?
|
35
|
+
subs.each do |sub|
|
36
|
+
sub[:is_broadcast] = is_broadcast = schema_class.subscriptions.broadcast_subscription_id?(sub[:id])
|
37
|
+
if is_broadcast && show_broadcast_subscribers_count
|
38
|
+
sub[:subscribers_count] = sub_count =schema_class.subscriptions.count_broadcast_subscribed(sub[:id])
|
39
|
+
sub[:still_subscribed] = sub_count > 0
|
40
|
+
else
|
41
|
+
sub[:still_subscribed] = schema_class.subscriptions.still_subscribed?(sub[:id])
|
42
|
+
sub[:subscribers_count] = nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
@topic_last_triggered_at = schema_class.subscriptions.topic_last_triggered_at(topic_name)
|
47
|
+
@subscriptions = subs
|
48
|
+
@subscriptions_count = all_subscription_ids.size
|
49
|
+
@show_broadcast_subscribers_count = show_broadcast_subscribers_count
|
50
|
+
@has_next_page = all_subscription_ids.size > offset + limit ? page + 1 : false
|
51
|
+
end
|
52
|
+
|
53
|
+
def index
|
54
|
+
page = params[:page]&.to_i || 1
|
55
|
+
per_page = params[:per_page]&.to_i || 20
|
56
|
+
offset = per_page * (page - 1)
|
57
|
+
limit = per_page
|
58
|
+
topics, all_topics_count, has_next_page = schema_class.subscriptions.topics(offset: offset, limit: limit)
|
59
|
+
|
60
|
+
@topics = topics
|
61
|
+
@all_topics_count = all_topics_count
|
62
|
+
@has_next_page = has_next_page
|
63
|
+
@page = page
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class SubscriptionsController < BaseController
|
68
|
+
def show
|
69
|
+
subscription_id = params[:id]
|
70
|
+
subscriptions = schema_class.subscriptions
|
71
|
+
query_data = subscriptions.read_subscription(subscription_id)
|
72
|
+
is_broadcast = subscriptions.broadcast_subscription_id?(subscription_id)
|
73
|
+
|
74
|
+
if is_broadcast && subscriptions.show_broadcast_subscribers_count?
|
75
|
+
subscribers_count = subscriptions.count_broadcast_subscribed(subscription_id)
|
76
|
+
is_still_subscribed = subscribers_count > 0
|
77
|
+
else
|
78
|
+
subscribers_count = nil
|
79
|
+
is_still_subscribed = subscriptions.still_subscribed?(subscription_id)
|
80
|
+
end
|
81
|
+
|
82
|
+
@query_data = query_data
|
83
|
+
@still_subscribed = is_still_subscribed
|
84
|
+
@is_broadcast = is_broadcast
|
85
|
+
@subscribers_count = subscribers_count
|
86
|
+
end
|
87
|
+
|
88
|
+
def clear_all
|
89
|
+
schema_class.subscriptions.clear
|
90
|
+
flash[:success] = "All subscription data cleared."
|
91
|
+
head :no_content
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<% content_for(:title, "Profiles") %>
|
2
|
+
<div class="row justify-content-between mt-3">
|
3
|
+
<div class="col-auto">
|
4
|
+
<h3>Detailed Profiles</h3>
|
5
|
+
</div>
|
6
|
+
<div class="col-auto">
|
7
|
+
<%= button_tag "Delete All Traces", class: "btn btn-sm btn-outline-danger", data: { perfetto_delete_all: graphql_dashboard.delete_all_detailed_traces_traces_path } %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="row">
|
12
|
+
<div class="col">
|
13
|
+
<table class="table table-striped">
|
14
|
+
<thead>
|
15
|
+
<tr>
|
16
|
+
<th>Operation</th>
|
17
|
+
<th>Duration (ms) </th>
|
18
|
+
<th>Timestamp</th>
|
19
|
+
<th>Open in Perfetto UI</th>
|
20
|
+
</tr>
|
21
|
+
</thead>
|
22
|
+
<tbody>
|
23
|
+
<% if @traces.empty? %>
|
24
|
+
<tr>
|
25
|
+
<td colspan="4" class="text-center">
|
26
|
+
<em>No traces saved yet. Read about saving traces <%= link_to "in the docs", "https://graphql-ruby.org/queries/tracing#detailed-profiles" %>.</em>
|
27
|
+
</td>
|
28
|
+
</tr>
|
29
|
+
<% end %>
|
30
|
+
<% @traces.each do |trace| %>
|
31
|
+
<tr>
|
32
|
+
<td><%= trace.operation_name %></td>
|
33
|
+
<td><%= trace.duration_ms.round(2) %></td>
|
34
|
+
<td><%= Time.at(trace.begin_ms / 1000.0).strftime("%Y-%m-%d %H:%M:%S.%L") %></td>
|
35
|
+
<td><%= link_to "View ↗", "#", data: { perfetto_open: trace.operation_name, perfetto_path: graphql_dashboard.detailed_traces_trace_path(trace.id) } %></td>
|
36
|
+
<td><%= link_to "Delete", "#", data: { perfetto_delete: graphql_dashboard.detailed_traces_trace_path(trace.id) }, class: "text-danger" %></td>
|
37
|
+
</tr>
|
38
|
+
<% end %>
|
39
|
+
</tbody>
|
40
|
+
</table>
|
41
|
+
<% if @last && @traces.size >= @last %>
|
42
|
+
<%= link_to("Previous >", graphql_dashboard.detailed_traces_traces_path(last: @last, before: @traces.last.begin_ms), class: "btn btn-outline-primary") %>
|
43
|
+
<% end %>
|
44
|
+
</div>
|
45
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<% content_for(:title, "Landing") %>
|
2
|
+
|
3
|
+
<div class="row">
|
4
|
+
<div class="col-md col-lg-8 mx-auto pt-4">
|
5
|
+
<div class="card mt-4">
|
6
|
+
<div class="card-body">
|
7
|
+
<div class="card-title">
|
8
|
+
<h2>
|
9
|
+
Welcome to the GraphQL-Ruby Dashboard
|
10
|
+
</h2>
|
11
|
+
</div>
|
12
|
+
<p class="card-text">
|
13
|
+
Click the links above to see data about your schema (<code><%= schema_class %></code>).
|
14
|
+
</p>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
@@ -0,0 +1,62 @@
|
|
1
|
+
<% content_for(:title, @title) %>
|
2
|
+
<% if @install_path %>
|
3
|
+
<div class="row mt-3">
|
4
|
+
<div class="col">
|
5
|
+
<h3><%= @title %></h3>
|
6
|
+
<p>It looks like this limiter isn't installed yet. <a href="<%= @install_path %>">Install it now</a>.</p>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<% else %>
|
10
|
+
<div class="row mt-3 justify-content-between">
|
11
|
+
<div class="col-auto">
|
12
|
+
<h3><%= @title %></h3>
|
13
|
+
</div>
|
14
|
+
<div class="col-auto">
|
15
|
+
<div class="btn-group">
|
16
|
+
<%= link_to("This Hour", graphql_dashboard.limiters_limiter_path(params[:name], chart: "hour"), class: "btn btn-sm btn-outline-primary #{@chart_mode == "hour" ? "active" : "inactive"}", params: { chart: "hour" }) %>
|
17
|
+
<%= link_to("Today", graphql_dashboard.limiters_limiter_path(params[:name], chart: "day"), class: "btn btn-sm btn-outline-primary #{@chart_mode == "day" ? "active" : "inactive"}", params: { chart: "day" }) %>
|
18
|
+
<%= link_to("This Month", graphql_dashboard.limiters_limiter_path(params[:name], chart: "month"), class: "btn btn-sm btn-outline-primary #{@chart_mode == "month" ? "active" : "inactive"}", params: { chart: "month" }) %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
<div class="col-auto">
|
22
|
+
<%= form_tag graphql_dashboard.limiters_limiter_path(params[:name], chart: @chart_mode), method: "patch" do %>
|
23
|
+
<%= submit_tag "#{@current_soft ? "Disable" : "Enable"} Soft Limiting", class: "btn btn-sm btn-outline-warning" %>
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<div class="row">
|
29
|
+
<div class="col ms-4">
|
30
|
+
<div id="limiter-histogram">
|
31
|
+
<table class="charts-css column multiple stacked show-labels hide-data show-primary-axis">
|
32
|
+
<thead>
|
33
|
+
<th>Date</th>
|
34
|
+
<th scope="col">Limited Requests</th>
|
35
|
+
<th scope="col">Unlimited Requests</th>
|
36
|
+
</thead>
|
37
|
+
<tbody>
|
38
|
+
<% @histogram.columns.each_with_index do |col, col_idx| %>
|
39
|
+
<tr>
|
40
|
+
<th scope="row" class="text-end"><%= col.label %></th>
|
41
|
+
<% col.values.each_with_index do |value, val_idx| %>
|
42
|
+
<td id="data-<%= col_idx %>-<%= val_idx %>">
|
43
|
+
<span class="data"><%= value.formatted_value %></span>
|
44
|
+
<span class="tooltip"><%= value.label %>: <%= value.formatted_value %><br><%= col.label %></span>
|
45
|
+
</td>
|
46
|
+
<% end %>
|
47
|
+
</tr>
|
48
|
+
<% end %>
|
49
|
+
</tbody>
|
50
|
+
</table>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
<%= content_tag "style", nonce: @csp_nonce do %>
|
55
|
+
<% @histogram.columns.each_with_index do |col, col_idx| %>
|
56
|
+
<% col_max = @histogram.max_column_value.to_f %>
|
57
|
+
<% col.values.each_with_index do |val, val_idx| %>
|
58
|
+
#data-<%= col_idx %>-<%= val_idx %> { --size: <%= val.value / col_max %>}
|
59
|
+
<% end %>
|
60
|
+
<% end %>
|
61
|
+
<% end %>
|
62
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<% content_for(:title, "Operation Store") %>
|
2
|
+
|
3
|
+
<div class="row">
|
4
|
+
<div class="col-md col-lg-8 mx-auto pt-4">
|
5
|
+
<div class="card mt-4">
|
6
|
+
<div class="card-body">
|
7
|
+
<div class="card-title">
|
8
|
+
<h2>
|
9
|
+
<%= @component_header_html %>
|
10
|
+
</h2>
|
11
|
+
</div>
|
12
|
+
<p class="card-text">
|
13
|
+
<%= @component_message_html %>
|
14
|
+
</p>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<%= form_tag((@client.persisted? ? graphql_dashboard.operation_store_client_path(name: @client.name) : graphql_dashboard.operation_store_clients_path), method: (@client.persisted? ? "patch" : "post")) do %>
|
2
|
+
<div class="row">
|
3
|
+
<label class="col-2 col-form-label">Name</label>
|
4
|
+
<div class="col">
|
5
|
+
<%= text_field_tag "client[name]", @client.name, class: "form-control", disabled: @client.persisted? %>
|
6
|
+
<div class="form-text">a unique identifier for this owner of persisted operations</div>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<div class="row">
|
10
|
+
<label class="col-2 col-form-label">Secret</label>
|
11
|
+
<div class="col">
|
12
|
+
<%= textarea_tag "client[secret]", @client.secret, class: "form-control" %>
|
13
|
+
<div class="form-text">authentication credential for <code>sync</code> transactions</div>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<div class="row">
|
17
|
+
<div class="col-auto">
|
18
|
+
<%= submit_tag "Save", class: "btn btn-outline-primary" %>
|
19
|
+
</div>
|
20
|
+
<div class="col-auto">
|
21
|
+
<%= link_to "Back", graphql_dashboard.operation_store_clients_path, class: "btn btn-outline-secondary" %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% content_for(:title, "Edit #{@client.name}") %>
|
2
|
+
<div class="row">
|
3
|
+
<div class="col">
|
4
|
+
<h1>Edit <%= @client.name %></h1>
|
5
|
+
</div>
|
6
|
+
<div>
|
7
|
+
<%= render partial: "graphql/dashboard/operation_store/clients/form" %>
|
8
|
+
|
9
|
+
<hr class="mt-5"/>
|
10
|
+
<div class="row mt-5">
|
11
|
+
<div class="col">
|
12
|
+
<div class="alert alert-danger">
|
13
|
+
<h4>Delete <%= @client.name %></h4>
|
14
|
+
<p>If you delete this client, it will no longer be able to use stored operations.</p>
|
15
|
+
<p>There is no way to undo this action.</p>
|
16
|
+
<%= form_tag(graphql_dashboard.operation_store_client_path(name: @client.name), method: "delete") do %>
|
17
|
+
<%= submit_tag "Permanently Delete #{@client.name.inspect}", class: "btn btn-outline-danger" %>
|
18
|
+
<% end %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
@@ -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 %>
|