karafka-web 1.0.0 → 1.0.1

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile.lock +13 -13
  4. data/lib/karafka/web/management/actions/enable.rb +74 -10
  5. data/lib/karafka/web/pro/ui/app.rb +13 -0
  6. data/lib/karafka/web/pro/ui/controllers/base_controller.rb +4 -0
  7. data/lib/karafka/web/pro/ui/controllers/cluster_controller.rb +66 -4
  8. data/lib/karafka/web/pro/ui/controllers/consumers/consumers_controller.rb +9 -2
  9. data/lib/karafka/web/pro/ui/controllers/consumers/controls_controller.rb +6 -0
  10. data/lib/karafka/web/pro/ui/controllers/consumers/jobs_controller.rb +8 -2
  11. data/lib/karafka/web/pro/ui/controllers/dlq_controller.rb +6 -0
  12. data/lib/karafka/web/pro/ui/controllers/explorer/explorer_controller.rb +6 -0
  13. data/lib/karafka/web/pro/ui/controllers/health_controller.rb +19 -3
  14. data/lib/karafka/web/pro/ui/controllers/jobs_controller.rb +25 -0
  15. data/lib/karafka/web/pro/ui/controllers/recurring_tasks_controller.rb +10 -1
  16. data/lib/karafka/web/pro/ui/controllers/requests/params.rb +67 -0
  17. data/lib/karafka/web/pro/ui/controllers/routing_controller.rb +23 -1
  18. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/schedules_controller.rb +6 -0
  19. data/lib/karafka/web/pro/ui/controllers/topics/configs_controller.rb +6 -1
  20. data/lib/karafka/web/pro/ui/controllers/topics/distributions_controller.rb +1 -1
  21. data/lib/karafka/web/pro/ui/controllers/topics/offsets_controller.rb +1 -1
  22. data/lib/karafka/web/pro/ui/controllers/topics/replications_controller.rb +1 -1
  23. data/lib/karafka/web/pro/ui/controllers/topics/topics_controller.rb +6 -0
  24. data/lib/karafka/web/pro/ui/helpers/filtering_helper.rb +173 -0
  25. data/lib/karafka/web/pro/ui/helpers/topics_filtering_helper.rb +80 -0
  26. data/lib/karafka/web/pro/ui/lib/filtering.rb +128 -0
  27. data/lib/karafka/web/pro/ui/lib/search/matchers/base.rb +2 -2
  28. data/lib/karafka/web/pro/ui/routes/cluster.rb +0 -2
  29. data/lib/karafka/web/pro/ui/views/cluster/_brokers_table.erb +41 -0
  30. data/lib/karafka/web/pro/ui/views/cluster/_configs_table.erb +41 -0
  31. data/lib/karafka/web/pro/ui/views/cluster/_partition.erb +2 -2
  32. data/lib/karafka/web/pro/ui/views/cluster/index.erb +12 -20
  33. data/lib/karafka/web/pro/ui/views/cluster/replication.erb +39 -0
  34. data/lib/karafka/web/pro/ui/views/cluster/show.erb +13 -21
  35. data/lib/karafka/web/pro/ui/views/consumers/consumers/_consumer_performances_table.erb +56 -0
  36. data/lib/karafka/web/pro/ui/views/consumers/consumers/_consumers_table.erb +43 -0
  37. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_subscription_group.erb +10 -2
  38. data/lib/karafka/web/pro/ui/views/consumers/consumers/index.erb +8 -24
  39. data/lib/karafka/web/pro/ui/views/consumers/consumers/performance.erb +8 -37
  40. data/lib/karafka/web/pro/ui/views/consumers/consumers/subscriptions.erb +20 -6
  41. data/lib/karafka/web/pro/ui/views/consumers/controls/_processes_table.erb +45 -0
  42. data/lib/karafka/web/pro/ui/views/consumers/controls/index.erb +14 -27
  43. data/lib/karafka/web/pro/ui/views/consumers/jobs/_jobs_table.erb +48 -0
  44. data/lib/karafka/web/pro/ui/views/consumers/jobs/pending.erb +8 -29
  45. data/lib/karafka/web/pro/ui/views/consumers/jobs/running.erb +8 -29
  46. data/lib/karafka/web/pro/ui/views/dlq/index.erb +9 -1
  47. data/lib/karafka/web/pro/ui/views/explorer/explorer/index.erb +9 -1
  48. data/lib/karafka/web/pro/ui/views/health/_changes_table.erb +59 -0
  49. data/lib/karafka/web/pro/ui/views/health/_cluster_lags_table.erb +43 -0
  50. data/lib/karafka/web/pro/ui/views/health/_lags_table.erb +60 -0
  51. data/lib/karafka/web/pro/ui/views/health/_no_data.erb +5 -1
  52. data/lib/karafka/web/pro/ui/views/health/_offsets_table.erb +63 -0
  53. data/lib/karafka/web/pro/ui/views/health/_overview_table.erb +83 -0
  54. data/lib/karafka/web/pro/ui/views/health/_tabs.erb +6 -0
  55. data/lib/karafka/web/pro/ui/views/health/changes.erb +13 -43
  56. data/lib/karafka/web/pro/ui/views/health/cluster_lags.erb +11 -25
  57. data/lib/karafka/web/pro/ui/views/health/lags.erb +13 -44
  58. data/lib/karafka/web/pro/ui/views/health/offsets.erb +13 -47
  59. data/lib/karafka/web/pro/ui/views/health/overview.erb +13 -67
  60. data/lib/karafka/web/pro/ui/views/jobs/_jobs_table.erb +48 -0
  61. data/lib/karafka/web/pro/ui/views/jobs/pending.erb +8 -30
  62. data/lib/karafka/web/pro/ui/views/jobs/running.erb +8 -30
  63. data/lib/karafka/web/pro/ui/views/recurring_tasks/_tasks_table.erb +43 -0
  64. data/lib/karafka/web/pro/ui/views/recurring_tasks/schedule.erb +8 -24
  65. data/lib/karafka/web/pro/ui/views/routing/_consumer_group.erb +36 -32
  66. data/lib/karafka/web/pro/ui/views/routing/_details_table.erb +43 -0
  67. data/lib/karafka/web/pro/ui/views/routing/index.erb +18 -6
  68. data/lib/karafka/web/pro/ui/views/routing/show.erb +12 -40
  69. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/index.erb +7 -1
  70. data/lib/karafka/web/pro/ui/views/shared/_filter_box.erb +61 -0
  71. data/lib/karafka/web/pro/ui/views/topics/configs/_configs_table.erb +42 -0
  72. data/lib/karafka/web/pro/ui/views/topics/configs/index.erb +13 -22
  73. data/lib/karafka/web/pro/ui/views/topics/replications/_partition.erb +2 -2
  74. data/lib/karafka/web/pro/ui/views/topics/replications/show.erb +12 -2
  75. data/lib/karafka/web/pro/ui/views/topics/topics/index.erb +9 -1
  76. data/lib/karafka/web/tracking/consumers/listeners/errors.rb +80 -14
  77. data/lib/karafka/web/ui/base.rb +4 -0
  78. data/lib/karafka/web/ui/controllers/base_controller.rb +1 -1
  79. data/lib/karafka/web/ui/controllers/cluster_controller.rb +21 -22
  80. data/lib/karafka/web/ui/controllers/consumers_controller.rb +1 -1
  81. data/lib/karafka/web/ui/controllers/jobs_controller.rb +23 -27
  82. data/lib/karafka/web/ui/controllers/requests/hookable.rb +5 -0
  83. data/lib/karafka/web/ui/controllers/routing_controller.rb +15 -1
  84. data/lib/karafka/web/ui/helpers/application_helper.rb +3 -278
  85. data/lib/karafka/web/ui/helpers/badges_helper.rb +66 -0
  86. data/lib/karafka/web/ui/helpers/formatting_helper.rb +77 -0
  87. data/lib/karafka/web/ui/helpers/partitions_helper.rb +167 -0
  88. data/lib/karafka/web/ui/helpers/sorting_helper.rb +78 -0
  89. data/lib/karafka/web/ui/lib/filter.rb +377 -0
  90. data/lib/karafka/web/ui/lib/hash_flattener.rb +37 -0
  91. data/lib/karafka/web/ui/lib/sorter.rb +3 -1
  92. data/lib/karafka/web/ui/models/jobs.rb +3 -2
  93. data/lib/karafka/web/ui/models/process.rb +12 -0
  94. data/lib/karafka/web/ui/public/javascripts/application.min.js.gz +0 -0
  95. data/lib/karafka/web/ui/public/stylesheets/application.css +9 -0
  96. data/lib/karafka/web/ui/public/stylesheets/application.min.css +4230 -1258
  97. data/lib/karafka/web/ui/public/stylesheets/application.min.css.br +0 -0
  98. data/lib/karafka/web/ui/public/stylesheets/application.min.css.gz +0 -0
  99. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_dark.min.css.gz +0 -0
  100. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_light.min.css.gz +0 -0
  101. data/lib/karafka/web/ui/views/cluster/_brokers_table.erb +21 -0
  102. data/lib/karafka/web/ui/views/cluster/_partition.erb +2 -2
  103. data/lib/karafka/web/ui/views/cluster/_partitions_table.erb +31 -0
  104. data/lib/karafka/web/ui/views/cluster/brokers.erb +1 -21
  105. data/lib/karafka/web/ui/views/cluster/replication.erb +1 -23
  106. data/lib/karafka/web/ui/views/consumers/_consumers_table.erb +22 -0
  107. data/lib/karafka/web/ui/views/consumers/index.erb +1 -22
  108. data/lib/karafka/web/ui/views/errors/_table.erb +28 -0
  109. data/lib/karafka/web/ui/views/errors/index.erb +1 -28
  110. data/lib/karafka/web/ui/views/jobs/_jobs_table.erb +22 -0
  111. data/lib/karafka/web/ui/views/jobs/pending.erb +1 -22
  112. data/lib/karafka/web/ui/views/jobs/running.erb +1 -22
  113. data/lib/karafka/web/ui/views/routing/_consumer_group.erb +18 -12
  114. data/lib/karafka/web/ui/views/routing/_details_table.erb +23 -0
  115. data/lib/karafka/web/ui/views/routing/show.erb +1 -29
  116. data/lib/karafka/web/ui/views/shared/_no_filter_results.erb +6 -0
  117. data/lib/karafka/web/version.rb +1 -1
  118. data/package-lock.json +98 -61
  119. metadata +35 -1
@@ -22,27 +22,18 @@
22
22
 
23
23
  <%== partial 'topics/topics/tabs' %>
24
24
 
25
- <div class="data-table-wrapper">
26
- <table class="data-table">
27
- <thead>
28
- <tr>
29
- <th><%== sort_link(:name) %></th>
30
- <th><%== sort_link(:value) %></th>
31
- <th><%== sort_link(:default?) %></th>
32
- <th><%== sort_link(:sensitive?) %></th>
33
- <th><%== sort_link(:read_only?) %></th>
34
- <th class="options">Options</th>
35
- </tr>
36
- </thead>
37
- <tbody>
38
- <%==
39
- each_partial(
40
- @configs,
41
- 'topics/configs/config'
42
- )
43
- %>
44
- </tbody>
45
- </table>
46
- </div>
25
+ <% if filtering? || !@configs.empty? %>
26
+ <div class="col-span-12 mb-3">
27
+ <%== filter_box %>
28
+ </div>
29
+ <% end %>
30
+
31
+ <% if filtering? && @configs.empty? %>
32
+ <div class="col-span-12">
33
+ <%== partial 'shared/no_filter_results' %>
34
+ </div>
35
+ <% else %>
36
+ <%== partial 'topics/configs/configs_table' %>
37
+ <% end %>
47
38
 
48
39
  <%== partial 'topics/configs/delete_button' %>
@@ -28,9 +28,9 @@
28
28
  <%= partition[:leader] %>
29
29
  </td>
30
30
  <td>
31
- <%= partition[:replica_count] %>
31
+ <%== partition_replica_brokers(partition, link: true) %>
32
32
  </td>
33
33
  <td>
34
- <%= partition[:in_sync_replica_brokers] %>
34
+ <%== partition_in_sync_brokers(partition, link: true) %>
35
35
  </td>
36
36
  </tr>
@@ -24,14 +24,24 @@
24
24
 
25
25
  <%== partial 'topics/replications/replication_info' %>
26
26
 
27
+ <div class="mb-3 flex justify-end">
28
+ <small class="text-muted">
29
+ Broker roles:
30
+ <span class="badge badge-primary">leader</span>
31
+ <span class="badge badge-info">replica</span>
32
+ <span class="badge badge-success">in-sync</span>
33
+ <span class="badge badge-warning">out-of-sync</span>
34
+ </small>
35
+ </div>
36
+
27
37
  <div class="data-table-wrapper">
28
38
  <table class="data-table">
29
39
  <thead>
30
40
  <tr>
31
41
  <th><%== sort_link(:partition_id) %></th>
32
42
  <th><%== sort_link(:leader) %></th>
33
- <th><%== sort_link(:replica_count) %></th>
34
- <th><%== sort_link('In Sync Brokers', :in_sync_replica_brokers) %></th>
43
+ <th><%== sort_link('Replicas', :replica_count) %></th>
44
+ <th><%== sort_link('In-Sync (ISR)', :in_sync_replica_brokers) %></th>
35
45
  </tr>
36
46
  </thead>
37
47
  <tbody>
@@ -22,8 +22,16 @@
22
22
 
23
23
  <%== partial 'topics/topics/create_button' %>
24
24
 
25
- <% if @topics.empty? %>
25
+ <% if filtering? || !@topics.empty? %>
26
+ <div class="col-span-12 mb-3">
27
+ <%== filter_box %>
28
+ </div>
29
+ <% end %>
30
+
31
+ <% if @topics.empty? && !filtering? %>
26
32
  <%== partial 'explorer/explorer/no_topics' %>
33
+ <% elsif @topics.empty? %>
34
+ <%== partial 'shared/no_filter_results' %>
27
35
  <% else %>
28
36
  <div class="topic-tiles">
29
37
  <%==
@@ -18,20 +18,11 @@ module Karafka
18
18
  #
19
19
  # @param event [Karafka::Core::Monitoring::Event]
20
20
  def on_error_occurred(event)
21
- caller_ref = event[:caller]
22
-
23
- # Collect extra info if it was a consumer related error.
24
- # Those come from user code
25
- details = case caller_ref
26
- when Karafka::BaseConsumer
27
- extract_consumer_info(caller_ref)
28
- when Karafka::Connection::Client
29
- extract_client_info(caller_ref)
30
- when Karafka::Connection::Listener
31
- extract_listener_info(caller_ref)
32
- else
33
- {}
34
- end
21
+ # Error details come from two independent sources that we merge together:
22
+ # - the caller: the user code context in which the error happened
23
+ # - the error type: extra diagnostics some error types carry in the event payload
24
+ details = extract_caller_details(event[:caller])
25
+ .merge(extract_type_details(event))
35
26
 
36
27
  error_class, error_message, backtrace = extract_error_info(event[:error])
37
28
 
@@ -72,6 +63,38 @@ module Karafka
72
63
 
73
64
  private
74
65
 
66
+ # Collects error details based on who reported the error, that is the user code
67
+ # context in which it happened (consumer, client or listener).
68
+ #
69
+ # @param caller_ref [Object] object that reported the error
70
+ # @return [Hash] hash with caller specific info for details of error
71
+ def extract_caller_details(caller_ref)
72
+ case caller_ref
73
+ when Karafka::BaseConsumer
74
+ extract_consumer_info(caller_ref)
75
+ when Karafka::Connection::Client
76
+ extract_client_info(caller_ref)
77
+ when Karafka::Connection::Listener
78
+ extract_listener_info(caller_ref)
79
+ else
80
+ {}
81
+ end
82
+ end
83
+
84
+ # Collects error details based on the error type. Some error types carry extra
85
+ # diagnostics in the event payload that do not come from the caller.
86
+ #
87
+ # @param event [Karafka::Core::Monitoring::Event]
88
+ # @return [Hash] hash with type specific info for details of error
89
+ def extract_type_details(event)
90
+ case event[:type]
91
+ when "app.stopping.error"
92
+ extract_forceful_shutdown_info(event)
93
+ else
94
+ {}
95
+ end
96
+ end
97
+
75
98
  # @param consumer [::Karafka::BaseConsumer]
76
99
  # @return [Hash] hash with consumer specific info for details of error
77
100
  def extract_consumer_info(consumer)
@@ -111,6 +134,49 @@ module Karafka
111
134
  id: listener.id
112
135
  }
113
136
  end
137
+
138
+ # Extracts details about what was still blocking when Karafka forcefully terminated.
139
+ # Karafka publishes the listeners that were still active and the jobs that were still
140
+ # in the processing pipeline alongside the forceful shutdown error.
141
+ #
142
+ # The lists are formatted into readable strings so they render as regular rows in the
143
+ # generic error details table (same presentation as every other error).
144
+ #
145
+ # @param event [Karafka::Core::Monitoring::Event]
146
+ # @return [Hash] hash with forceful shutdown diagnostics for details of error
147
+ def extract_forceful_shutdown_info(event)
148
+ active_listeners = event[:active_listeners] || []
149
+ alive_workers = event[:alive_workers] || []
150
+ in_processing = event[:in_processing] || {}
151
+
152
+ listeners = active_listeners.map do |listener|
153
+ "#{listener.id} (#{listener.subscription_group.id})"
154
+ end
155
+
156
+ jobs = in_processing.flat_map do |group_id, group_jobs|
157
+ group_jobs.map { |job| format_in_processing_job(group_id, job) }
158
+ end
159
+
160
+ # Only include the listener/job lists when there is something to show, so we do not
161
+ # render empty rows in the error details table
162
+ details = { alive_workers: alive_workers.size }
163
+ details[:active_listeners] = listeners.join(", ") unless listeners.empty?
164
+ details[:in_processing] = jobs.join("; ") unless jobs.empty?
165
+ details
166
+ end
167
+
168
+ # Formats a single job that was still in processing into a readable description.
169
+ #
170
+ # @param group_id [String] subscription group id the job belongs to
171
+ # @param job [Object] job that was still in the processing pipeline
172
+ # @return [String] readable job description
173
+ def format_in_processing_job(group_id, job)
174
+ status = job.non_blocking? ? "non-blocking" : "blocking"
175
+ job_type = job.class.name.split("::").last
176
+
177
+ "#{job_type} #{job.executor.topic.name}/#{job.executor.partition} " \
178
+ "(#{group_id}, #{status})"
179
+ end
114
180
  end
115
181
  end
116
182
  end
@@ -7,6 +7,10 @@ module Karafka
7
7
  class Base < Roda
8
8
  include Helpers::PathsHelper
9
9
  include Helpers::ApplicationHelper
10
+ include Helpers::BadgesHelper
11
+ include Helpers::FormattingHelper
12
+ include Helpers::PartitionsHelper
13
+ include Helpers::SortingHelper
10
14
  include Helpers::TimeHelper
11
15
  include Helpers::TopicsHelper
12
16
  include Helpers::TailwindHelper
@@ -132,7 +132,7 @@ module Karafka
132
132
 
133
133
  # @param resources [Hash, Array, Lib::HashProxy] object for sorting
134
134
  # @return [Hash, Array, Lib::HashProxy] sorted results
135
- def refine(resources)
135
+ def sort(resources)
136
136
  Lib::Sorter.new(
137
137
  @params.current_sort,
138
138
  allowed_attributes: self.class.sortable_attributes
@@ -22,27 +22,15 @@ module Karafka
22
22
 
23
23
  # Lists available brokers in the cluster
24
24
  def brokers
25
- @brokers = refine(cluster_info.brokers)
25
+ @brokers = sort(Models::ClusterInfo.fetch.brokers)
26
26
 
27
27
  render
28
28
  end
29
29
 
30
30
  # List partitions replication details
31
31
  def replication
32
- partitions_total = []
33
-
34
- displayable_topics(cluster_info).each do |topic|
35
- topic[:partitions].each do |partition|
36
- partitions_total << partition.merge(
37
- topic: topic,
38
- # Will allow sorting by name
39
- topic_name: topic.fetch(:topic_name)
40
- )
41
- end
42
- end
43
-
44
32
  @partitions, last_page = Paginators::Arrays.call(
45
- refine(partitions_total),
33
+ sort(replication_partitions),
46
34
  @params.current_page
47
35
  )
48
36
 
@@ -53,16 +41,27 @@ module Karafka
53
41
 
54
42
  private
55
43
 
56
- # @return [Array] whole cluster info
57
- def cluster_info
58
- @cluster_info ||= Models::ClusterInfo.fetch
44
+ # Expands the displayable cluster topics into one flat row per partition, which the
45
+ # replication table renders, sorts and paginates. Each row is a partition carrying its
46
+ # topic (and topic name, so it can be sorted/filtered by name).
47
+ #
48
+ # @return [Array<Hash>] partition rows
49
+ def replication_partitions
50
+ displayable_topics.flat_map do |topic|
51
+ topic[:partitions].map do |partition|
52
+ partition.merge(
53
+ topic: topic,
54
+ topic_name: topic.fetch(:topic_name)
55
+ )
56
+ end
57
+ end
59
58
  end
60
59
 
61
- # @param cluster_info [Rdkafka::Metadata] cluster metadata
62
- # @return [Array<Hash>] array with topics to be displayed sorted in an alphabetical
63
- # order
64
- def displayable_topics(cluster_info)
65
- all = cluster_info
60
+ # @return [Array<Hash>] cluster topics to display in an alphabetical order, with internal
61
+ # topics excluded unless the visibility config allows them
62
+ def displayable_topics
63
+ all = Models::ClusterInfo
64
+ .fetch
66
65
  .topics
67
66
  .sort_by { |topic| topic[:topic_name] }
68
67
 
@@ -18,7 +18,7 @@ module Karafka
18
18
  @current_state = Models::ConsumersState.current!
19
19
  @counters = Models::Counters.new(@current_state)
20
20
  @processes, last_page = Paginators::Arrays.call(
21
- refine(Models::Processes.active(@current_state)),
21
+ sort(Models::Processes.active(@current_state)),
22
22
  @params.current_page
23
23
  )
24
24
 
@@ -16,56 +16,52 @@ module Karafka
16
16
 
17
17
  # Lists running jobs
18
18
  def running
19
- current_state = Models::ConsumersState.current!
20
- processes = Models::Processes.active(current_state)
21
-
22
- @jobs_counters = count_jobs_types(processes)
23
-
24
- # Aggregate jobs and inject the process info into them for better reporting
25
- jobs_total = processes.flat_map do |process|
26
- process.jobs.running.map do |job|
27
- job.to_h[:process] = process
28
- job
29
- end
30
- end
31
-
32
- @jobs, last_page = Paginators::Arrays.call(
33
- refine(jobs_total),
34
- @params.current_page
35
- )
36
-
37
- paginate(@params.current_page, !last_page)
19
+ paginate_jobs(build_jobs(:running))
38
20
 
39
21
  render
40
22
  end
41
23
 
42
24
  # Lists pending jobs
43
25
  def pending
26
+ paginate_jobs(build_jobs(:pending))
27
+
28
+ render
29
+ end
30
+
31
+ private
32
+
33
+ # Aggregates jobs of a given type across all active processes, injecting the process info
34
+ # into each job for better reporting.
35
+ #
36
+ # @param type [Symbol] `:running` or `:pending`
37
+ # @return [Array] aggregated jobs
38
+ def build_jobs(type)
44
39
  current_state = Models::ConsumersState.current!
45
40
  processes = Models::Processes.active(current_state)
46
41
 
47
42
  @jobs_counters = count_jobs_types(processes)
48
43
 
49
- # Aggregate jobs and inject the process info into them for better reporting
50
- jobs_total = processes.flat_map do |process|
51
- process.jobs.pending.map do |job|
44
+ processes.flat_map do |process|
45
+ process.jobs.public_send(type).map do |job|
52
46
  job.to_h[:process] = process
53
47
  job
54
48
  end
55
49
  end
50
+ end
56
51
 
52
+ # Sorts and paginates the aggregated jobs into `@jobs`. This is the seam a Pro subclass
53
+ # overrides to also apply filtering (a Pro-only feature); OSS only sorts.
54
+ #
55
+ # @param jobs_total [Array] aggregated jobs
56
+ def paginate_jobs(jobs_total)
57
57
  @jobs, last_page = Paginators::Arrays.call(
58
- refine(jobs_total),
58
+ sort(jobs_total),
59
59
  @params.current_page
60
60
  )
61
61
 
62
62
  paginate(@params.current_page, !last_page)
63
-
64
- render
65
63
  end
66
64
 
67
- private
68
-
69
65
  # @param processes [Array<Process>]
70
66
  # @return [Lib::HashProxy] particular type jobs count
71
67
  def count_jobs_types(processes)
@@ -78,6 +78,11 @@ module Karafka
78
78
  #
79
79
  # @param action_name [Symbol] the method name being invoked
80
80
  def run_before_hooks(action_name)
81
+ # Track the dispatched action so controllers can resolve per-action configuration
82
+ # (e.g. `filterable_attributes`) from within the hooks and their action body, before
83
+ # `render` is ever reached
84
+ @current_action_name = action_name
85
+
81
86
  self.class.before_hooks.each do |actions, block|
82
87
  instance_exec(&block) if actions.empty? || actions.include?(action_name)
83
88
  end
@@ -9,6 +9,7 @@ module Karafka
9
9
  self.sortable_attributes = %w[
10
10
  name
11
11
  active?
12
+ value
12
13
  ].freeze
13
14
 
14
15
  # Routing list
@@ -16,7 +17,7 @@ module Karafka
16
17
  @routes = Karafka::App.routes
17
18
 
18
19
  @routes.each do |consumer_group|
19
- refine(consumer_group.topics)
20
+ sort(consumer_group.topics)
20
21
  end
21
22
 
22
23
  render
@@ -30,8 +31,21 @@ module Karafka
30
31
 
31
32
  @topic || not_found!(topic_id)
32
33
 
34
+ # Present the routing settings as a flat, sortable list of name/value rows
35
+ @details = sort(topic_detail_rows(@topic))
36
+
33
37
  render
34
38
  end
39
+
40
+ private
41
+
42
+ # @param topic [Karafka::Routing::Topic] topic we present the routing details for
43
+ # @return [Array<Hash>] `{ name:, value: }` rows for every routing setting
44
+ def topic_detail_rows(topic)
45
+ rows = Lib::HashFlattener.call(topic.subscription_group.kafka, "kafka")
46
+ rows.merge!(Lib::HashFlattener.call(topic.to_h.except(:kafka)))
47
+ rows.map { |name, value| { name: name.to_s, value: value } }
48
+ end
35
49
  end
36
50
  end
37
51
  end