graphql 2.4.14 → 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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/backtrace/table.rb +37 -14
  3. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  4. data/lib/graphql/dashboard/installable.rb +22 -0
  5. data/lib/graphql/dashboard/limiters.rb +93 -0
  6. data/lib/graphql/dashboard/operation_store.rb +199 -0
  7. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  8. data/lib/graphql/dashboard/statics/dashboard.css +27 -0
  9. data/lib/graphql/dashboard/statics/dashboard.js +74 -9
  10. data/lib/graphql/dashboard/subscriptions.rb +96 -0
  11. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  12. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  13. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  14. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
  15. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  16. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  17. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  18. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  19. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  20. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  21. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  22. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  23. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  24. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  25. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
  26. data/lib/graphql/dashboard.rb +45 -29
  27. data/lib/graphql/execution/interpreter/runtime.rb +11 -1
  28. data/lib/graphql/execution/interpreter.rb +4 -2
  29. data/lib/graphql/execution/multiplex.rb +1 -1
  30. data/lib/graphql/language/parser.rb +13 -6
  31. data/lib/graphql/query.rb +2 -1
  32. data/lib/graphql/schema/argument.rb +4 -3
  33. data/lib/graphql/schema/build_from_definition.rb +91 -46
  34. data/lib/graphql/schema/input_object.rb +9 -8
  35. data/lib/graphql/schema.rb +3 -1
  36. data/lib/graphql/testing/helpers.rb +5 -2
  37. data/lib/graphql/tracing/perfetto_trace.rb +4 -4
  38. data/lib/graphql/version.rb +1 -1
  39. metadata +23 -35
  40. 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.
@@ -587,7 +587,17 @@ module GraphQL
587
587
  set_result(selection_result, result_name, r, false, is_non_null)
588
588
  r
589
589
  when "UNION", "INTERFACE"
590
- resolved_type_or_lazy = resolve_type(current_type, value)
590
+ resolved_type_or_lazy = begin
591
+ resolve_type(current_type, value)
592
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
593
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
594
+ rescue StandardError => err
595
+ begin
596
+ query.handle_or_reraise(err)
597
+ rescue GraphQL::ExecutionError => ex_err
598
+ return continue_value(ex_err, field, is_non_null, ast_node, result_name, selection_result)
599
+ end
600
+ end
591
601
  after_lazy(resolved_type_or_lazy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |resolved_type_result, runtime_state|
592
602
  if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
593
603
  resolved_type, resolved_value = resolved_type_result
@@ -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,12 +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
 
37
+ return GraphQL::EmptyObjects::EMPTY_ARRAY if queries.empty?
36
38
 
37
39
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
38
- Fiber[:__graphql_current_multiplex] = multiplex
39
40
  trace = multiplex.current_trace
41
+ Fiber[:__graphql_current_multiplex] = multiplex
40
42
  trace.begin_execute_multiplex(multiplex)
41
43
  trace.execute_multiplex(multiplex: multiplex) do
42
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
@@ -217,7 +217,7 @@ module GraphQL
217
217
  # @api private
218
218
  def prepare_value(obj, value, context: nil)
219
219
  if type.unwrap.kind.input_object?
220
- value = recursively_prepare_input_object(value, type)
220
+ value = recursively_prepare_input_object(value, type, context)
221
221
  end
222
222
 
223
223
  Schema::Validator.validate!(validators, obj, context, value)
@@ -398,15 +398,16 @@ module GraphQL
398
398
 
399
399
  private
400
400
 
401
- def recursively_prepare_input_object(value, type)
401
+ def recursively_prepare_input_object(value, type, context)
402
402
  if type.non_null?
403
403
  type = type.of_type
404
404
  end
405
405
 
406
406
  if type.list? && !value.nil?
407
407
  inner_type = type.of_type
408
- value.map { |v| recursively_prepare_input_object(v, inner_type) }
408
+ value.map { |v| recursively_prepare_input_object(v, inner_type, context) }
409
409
  elsif value.is_a?(GraphQL::Schema::InputObject)
410
+ value.validate_for(context)
410
411
  value.prepare
411
412
  else
412
413
  value