karafka-web 0.10.3 → 0.11.0.beta1

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 (481) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +64 -176
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +88 -44
  6. data/LICENSE +6 -2
  7. data/bin/verify_kafka_warnings +35 -0
  8. data/bin/verify_topics_naming +35 -0
  9. data/config/locales/pro_errors.yml +1 -0
  10. data/docker-compose.yml +1 -1
  11. data/gulpfile.js +0 -2
  12. data/karafka-web.gemspec +3 -5
  13. data/lib/karafka/web/config.rb +86 -10
  14. data/lib/karafka/web/contracts/config.rb +45 -5
  15. data/lib/karafka/web/errors.rb +10 -0
  16. data/lib/karafka/web/management/actions/create_initial_states.rb +6 -6
  17. data/lib/karafka/web/management/actions/create_topics.rb +30 -64
  18. data/lib/karafka/web/management/actions/delete_topics.rb +5 -5
  19. data/lib/karafka/web/management/actions/enable.rb +5 -5
  20. data/lib/karafka/web/pro/commanding/commands/base.rb +37 -13
  21. data/lib/karafka/web/pro/commanding/commands/consumers/quiet.rb +33 -0
  22. data/lib/karafka/web/pro/commanding/commands/consumers/stop.rb +32 -0
  23. data/lib/karafka/web/pro/commanding/commands/consumers/trace.rb +37 -0
  24. data/lib/karafka/web/pro/commanding/commands/partitions/pause.rb +30 -0
  25. data/lib/karafka/web/pro/commanding/commands/partitions/resume.rb +30 -0
  26. data/lib/karafka/web/pro/commanding/commands/partitions/seek.rb +30 -0
  27. data/lib/karafka/web/pro/commanding/config.rb +6 -10
  28. data/lib/karafka/web/pro/commanding/contracts/config.rb +2 -10
  29. data/lib/karafka/web/pro/commanding/dispatcher.rb +45 -24
  30. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/base.rb +67 -0
  31. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/pause.rb +44 -0
  32. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/resume.rb +29 -0
  33. data/lib/karafka/web/pro/commanding/handlers/partitions/commands/seek.rb +86 -0
  34. data/lib/karafka/web/pro/commanding/handlers/partitions/executor.rb +56 -0
  35. data/lib/karafka/web/pro/commanding/handlers/partitions/listener.rb +55 -0
  36. data/lib/karafka/web/pro/commanding/handlers/partitions/tracker.rb +62 -0
  37. data/lib/karafka/web/pro/commanding/listener.rb +4 -12
  38. data/lib/karafka/web/pro/commanding/manager.rb +36 -24
  39. data/lib/karafka/web/pro/commanding/matcher.rb +7 -17
  40. data/lib/karafka/web/pro/commanding/request.rb +39 -0
  41. data/lib/karafka/web/pro/commanding.rb +2 -10
  42. data/lib/karafka/web/pro/loader.rb +32 -10
  43. data/lib/karafka/web/pro/ui/app.rb +31 -390
  44. data/lib/karafka/web/pro/ui/controllers/base_controller.rb +8 -10
  45. data/lib/karafka/web/pro/ui/controllers/cluster_controller.rb +2 -10
  46. data/lib/karafka/web/pro/ui/controllers/consumers/base_controller.rb +21 -0
  47. data/lib/karafka/web/pro/ui/controllers/consumers/commanding_controller.rb +148 -0
  48. data/lib/karafka/web/pro/ui/controllers/consumers/commands_controller.rb +96 -0
  49. data/lib/karafka/web/pro/ui/controllers/consumers/consumers_controller.rb +101 -0
  50. data/lib/karafka/web/pro/ui/controllers/consumers/controls_controller.rb +36 -0
  51. data/lib/karafka/web/pro/ui/controllers/consumers/jobs_controller.rb +57 -0
  52. data/lib/karafka/web/pro/ui/controllers/consumers/partitions/base_controller.rb +86 -0
  53. data/lib/karafka/web/pro/ui/controllers/consumers/partitions/offsets_controller.rb +75 -0
  54. data/lib/karafka/web/pro/ui/controllers/consumers/partitions/pauses_controller.rb +110 -0
  55. data/lib/karafka/web/pro/ui/controllers/dashboard_controller.rb +2 -10
  56. data/lib/karafka/web/pro/ui/controllers/dlq_controller.rb +2 -10
  57. data/lib/karafka/web/pro/ui/controllers/errors_controller.rb +3 -11
  58. data/lib/karafka/web/pro/ui/controllers/explorer/base_controller.rb +21 -0
  59. data/lib/karafka/web/pro/ui/controllers/explorer/explorer_controller.rb +215 -0
  60. data/lib/karafka/web/pro/ui/controllers/explorer/messages_controller.rb +145 -0
  61. data/lib/karafka/web/pro/ui/controllers/explorer/search_controller.rb +68 -0
  62. data/lib/karafka/web/pro/ui/controllers/health_controller.rb +2 -10
  63. data/lib/karafka/web/pro/ui/controllers/jobs_controller.rb +2 -10
  64. data/lib/karafka/web/pro/ui/controllers/recurring_tasks_controller.rb +12 -13
  65. data/lib/karafka/web/pro/ui/controllers/routing_controller.rb +2 -10
  66. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/base_controller.rb +2 -10
  67. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/explorer_controller.rb +8 -16
  68. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/messages_controller.rb +9 -15
  69. data/lib/karafka/web/pro/ui/controllers/scheduled_messages/schedules_controller.rb +2 -10
  70. data/lib/karafka/web/pro/ui/controllers/status_controller.rb +2 -10
  71. data/lib/karafka/web/pro/ui/controllers/support_controller.rb +2 -10
  72. data/lib/karafka/web/pro/ui/controllers/topics/base_controller.rb +21 -0
  73. data/lib/karafka/web/pro/ui/controllers/topics/configs_controller.rb +86 -0
  74. data/lib/karafka/web/pro/ui/controllers/topics/distributions_controller.rb +91 -0
  75. data/lib/karafka/web/pro/ui/controllers/topics/offsets_controller.rb +55 -0
  76. data/lib/karafka/web/pro/ui/controllers/topics/replications_controller.rb +37 -0
  77. data/lib/karafka/web/pro/ui/controllers/topics/topics_controller.rb +101 -0
  78. data/lib/karafka/web/pro/ui/controllers/ux_controller.rb +2 -10
  79. data/lib/karafka/web/pro/ui/lib/branding/config.rb +2 -10
  80. data/lib/karafka/web/pro/ui/lib/branding/contracts/config.rb +2 -10
  81. data/lib/karafka/web/pro/ui/lib/branding.rb +2 -10
  82. data/lib/karafka/web/pro/ui/lib/features.rb +53 -0
  83. data/lib/karafka/web/pro/ui/lib/patterns_detector.rb +2 -10
  84. data/lib/karafka/web/pro/ui/lib/policies/config.rb +2 -10
  85. data/lib/karafka/web/pro/ui/lib/policies/contracts/config.rb +2 -10
  86. data/lib/karafka/web/pro/ui/lib/policies/messages.rb +2 -10
  87. data/lib/karafka/web/pro/ui/lib/policies/requests.rb +2 -10
  88. data/lib/karafka/web/pro/ui/lib/policies.rb +2 -10
  89. data/lib/karafka/web/pro/ui/lib/safe_runner.rb +5 -0
  90. data/lib/karafka/web/pro/ui/lib/search/config.rb +2 -10
  91. data/lib/karafka/web/pro/ui/lib/search/contracts/config.rb +2 -10
  92. data/lib/karafka/web/pro/ui/lib/search/contracts/form.rb +2 -10
  93. data/lib/karafka/web/pro/ui/lib/search/matchers/base.rb +2 -10
  94. data/lib/karafka/web/pro/ui/lib/search/matchers/raw_header_includes.rb +10 -11
  95. data/lib/karafka/web/pro/ui/lib/search/matchers/raw_key_includes.rb +2 -10
  96. data/lib/karafka/web/pro/ui/lib/search/matchers/raw_payload_includes.rb +23 -11
  97. data/lib/karafka/web/pro/ui/lib/search/normalizer.rb +2 -10
  98. data/lib/karafka/web/pro/ui/lib/search/runner.rb +3 -11
  99. data/lib/karafka/web/pro/ui/lib/search.rb +2 -10
  100. data/lib/karafka/web/pro/ui/routes/base.rb +19 -0
  101. data/lib/karafka/web/pro/ui/routes/cluster.rb +37 -0
  102. data/lib/karafka/web/pro/ui/routes/consumers.rb +145 -0
  103. data/lib/karafka/web/pro/ui/routes/dashboard.rb +25 -0
  104. data/lib/karafka/web/pro/ui/routes/dlq.rb +24 -0
  105. data/lib/karafka/web/pro/ui/routes/errors.rb +39 -0
  106. data/lib/karafka/web/pro/ui/routes/explorer.rb +118 -0
  107. data/lib/karafka/web/pro/ui/routes/health.rb +47 -0
  108. data/lib/karafka/web/pro/ui/routes/jobs.rb +33 -0
  109. data/lib/karafka/web/pro/ui/routes/recurring_tasks.rb +59 -0
  110. data/lib/karafka/web/pro/ui/routes/routing.rb +31 -0
  111. data/lib/karafka/web/pro/ui/routes/scheduled_messages.rb +75 -0
  112. data/lib/karafka/web/pro/ui/routes/status.rb +24 -0
  113. data/lib/karafka/web/pro/ui/routes/support.rb +24 -0
  114. data/lib/karafka/web/pro/ui/routes/topics.rb +90 -0
  115. data/lib/karafka/web/pro/ui/routes/ux.rb +24 -0
  116. data/lib/karafka/web/pro/ui/views/cluster/_breadcrumbs.erb +3 -0
  117. data/lib/karafka/web/pro/ui/views/cluster/_broker.erb +3 -0
  118. data/lib/karafka/web/pro/ui/views/cluster/_config.erb +3 -0
  119. data/lib/karafka/web/pro/ui/views/cluster/_tabs.erb +3 -0
  120. data/lib/karafka/web/pro/ui/views/cluster/index.erb +4 -1
  121. data/lib/karafka/web/pro/ui/views/cluster/show.erb +3 -0
  122. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_backtrace.erb +3 -0
  123. data/lib/karafka/web/pro/ui/views/consumers/commands/_breadcrumbs.erb +24 -0
  124. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_command.erb +22 -6
  125. data/lib/karafka/web/pro/ui/views/consumers/commands/_command_details.erb +4 -0
  126. data/lib/karafka/web/pro/ui/views/consumers/commands/_empty.erb +6 -0
  127. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_incompatible_schema.erb +3 -0
  128. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_metadata.erb +4 -1
  129. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/_table.erb +5 -2
  130. data/lib/karafka/web/pro/ui/views/{commands → consumers/commands}/index.erb +7 -4
  131. data/lib/karafka/web/pro/ui/views/consumers/commands/show.erb +32 -0
  132. data/lib/karafka/web/pro/ui/views/consumers/consumers/_breadcrumbs.erb +46 -0
  133. data/lib/karafka/web/pro/ui/views/consumers/consumers/_consumer.erb +59 -0
  134. data/lib/karafka/web/pro/ui/views/consumers/consumers/_consumer_performance.erb +71 -0
  135. data/lib/karafka/web/pro/ui/views/consumers/consumers/_tabs.erb +38 -0
  136. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_commands.erb +80 -0
  137. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_consumer_group.erb +11 -0
  138. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_metrics.erb +3 -0
  139. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_no_subscriptions.erb +10 -0
  140. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_partition.erb +16 -0
  141. data/lib/karafka/web/pro/ui/views/consumers/consumers/consumer/_partition_edit_options.erb +33 -0
  142. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_stopped.erb +3 -0
  143. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_subscription_group.erb +7 -3
  144. data/lib/karafka/web/pro/ui/views/consumers/{consumer → consumers/consumer}/_tabs.erb +7 -4
  145. data/lib/karafka/web/pro/ui/views/consumers/consumers/details.erb +15 -0
  146. data/lib/karafka/web/pro/ui/views/consumers/{index.erb → consumers/index.erb} +6 -3
  147. data/lib/karafka/web/pro/ui/views/consumers/{performance.erb → consumers/performance.erb} +6 -3
  148. data/lib/karafka/web/pro/ui/views/consumers/consumers/subscriptions.erb +24 -0
  149. data/lib/karafka/web/pro/ui/views/consumers/controls/_breadcrumbs.erb +16 -0
  150. data/lib/karafka/web/pro/ui/views/consumers/controls/_controls.erb +107 -0
  151. data/lib/karafka/web/pro/ui/views/consumers/{controls.erb → controls/index.erb} +8 -5
  152. data/lib/karafka/web/pro/ui/views/consumers/jobs/_breadcrumbs.erb +36 -0
  153. data/lib/karafka/web/pro/ui/views/consumers/{consumer → jobs}/_job.erb +3 -0
  154. data/lib/karafka/web/pro/ui/views/consumers/{consumer → jobs}/_no_jobs.erb +3 -0
  155. data/lib/karafka/web/pro/ui/views/consumers/{pending_jobs.erb → jobs/pending.erb} +7 -8
  156. data/lib/karafka/web/pro/ui/views/consumers/{running_jobs.erb → jobs/running.erb} +7 -8
  157. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_basics.erb +77 -0
  158. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_breadcrumbs.erb +58 -0
  159. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_form.erb +109 -0
  160. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_not_running_error.erb +16 -0
  161. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/_running_warning.erb +15 -0
  162. data/lib/karafka/web/pro/ui/views/consumers/partitions/offsets/edit.erb +12 -0
  163. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_active_not_editable.erb +22 -0
  164. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_adjusting_warning.erb +27 -0
  165. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_breadcrumbs.erb +60 -0
  166. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_edit_form.erb +59 -0
  167. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_lrj_not_manageable.erb +24 -0
  168. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_new_form.erb +78 -0
  169. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/_not_running.erb +16 -0
  170. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/edit.erb +20 -0
  171. data/lib/karafka/web/pro/ui/views/consumers/partitions/pauses/new.erb +16 -0
  172. data/lib/karafka/web/pro/ui/views/dashboard/index.erb +4 -1
  173. data/lib/karafka/web/pro/ui/views/dlq/_breadcrumbs.erb +3 -0
  174. data/lib/karafka/web/pro/ui/views/dlq/_no_topics.erb +3 -0
  175. data/lib/karafka/web/pro/ui/views/dlq/_topic.erb +4 -1
  176. data/lib/karafka/web/pro/ui/views/dlq/index.erb +3 -0
  177. data/lib/karafka/web/pro/ui/views/errors/_breadcrumbs.erb +3 -0
  178. data/lib/karafka/web/pro/ui/views/errors/_error.erb +3 -0
  179. data/lib/karafka/web/pro/ui/views/errors/_partition_option.erb +3 -0
  180. data/lib/karafka/web/pro/ui/views/errors/_selector.erb +3 -0
  181. data/lib/karafka/web/pro/ui/views/errors/_table.erb +4 -1
  182. data/lib/karafka/web/pro/ui/views/errors/index.erb +6 -3
  183. data/lib/karafka/web/pro/ui/views/errors/partition.erb +5 -2
  184. data/lib/karafka/web/pro/ui/views/errors/show.erb +3 -0
  185. data/lib/karafka/web/pro/ui/views/explorer/{_breadcrumbs.erb → explorer/_breadcrumbs.erb} +7 -4
  186. data/lib/karafka/web/pro/ui/views/explorer/{_failed_deserialization.erb → explorer/_failed_deserialization.erb} +3 -0
  187. data/lib/karafka/web/pro/ui/views/explorer/{_filtered.erb → explorer/_filtered.erb} +3 -0
  188. data/lib/karafka/web/pro/ui/views/explorer/{_message.erb → explorer/_message.erb} +4 -1
  189. data/lib/karafka/web/pro/ui/views/explorer/explorer/_no_topics.erb +4 -0
  190. data/lib/karafka/web/pro/ui/views/explorer/{_partition_option.erb → explorer/_partition_option.erb} +4 -1
  191. data/lib/karafka/web/pro/ui/views/explorer/{_selector.erb → explorer/_selector.erb} +4 -1
  192. data/lib/karafka/web/pro/ui/views/explorer/explorer/_topic.erb +13 -0
  193. data/lib/karafka/web/pro/ui/views/explorer/explorer/index.erb +17 -0
  194. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_metadata.erb +10 -7
  195. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_payload.erb +6 -3
  196. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_resources_utilization.erb +7 -4
  197. data/lib/karafka/web/pro/ui/views/explorer/{message → explorer/message}/_too_big_to_be_displayed.erb +3 -0
  198. data/lib/karafka/web/pro/ui/views/explorer/{messages → explorer/messages}/_detail.erb +3 -0
  199. data/lib/karafka/web/pro/ui/views/explorer/explorer/messages/_headers.erb +51 -0
  200. data/lib/karafka/web/pro/ui/views/explorer/{messages → explorer/messages}/_key.erb +3 -0
  201. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_cleaned.erb +6 -0
  202. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_empty.erb +6 -0
  203. data/lib/karafka/web/pro/ui/views/explorer/{partition → explorer/partition}/_messages.erb +4 -1
  204. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_time_selector.erb +16 -0
  205. data/lib/karafka/web/pro/ui/views/explorer/explorer/partition/_timestamp_selector.erb +33 -0
  206. data/lib/karafka/web/pro/ui/views/explorer/{partition.erb → explorer/partition.erb} +24 -17
  207. data/lib/karafka/web/pro/ui/views/explorer/{show.erb → explorer/show.erb} +17 -19
  208. data/lib/karafka/web/pro/ui/views/explorer/{topic → explorer/topic}/_actions.erb +5 -2
  209. data/lib/karafka/web/pro/ui/views/explorer/explorer/topic/_empty.erb +6 -0
  210. data/lib/karafka/web/pro/ui/views/explorer/{topic → explorer/topic}/_limited.erb +3 -0
  211. data/lib/karafka/web/pro/ui/views/explorer/{topic.erb → explorer/topic.erb} +7 -4
  212. data/lib/karafka/web/pro/ui/views/explorer/messages/_breadcrumbs.erb +32 -0
  213. data/lib/karafka/web/pro/ui/views/explorer/messages/forward.erb +143 -0
  214. data/lib/karafka/web/pro/ui/views/explorer/search/_breadcrumbs.erb +4 -0
  215. data/lib/karafka/web/pro/ui/views/explorer/search/_fix_errors.erb +6 -0
  216. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_metadata.erb +3 -0
  217. data/lib/karafka/web/pro/ui/views/explorer/search/_no_results.erb +6 -0
  218. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_no_search_criteria.erb +3 -0
  219. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_search_criteria.erb +3 -0
  220. data/lib/karafka/web/pro/ui/views/{search → explorer/search}/_search_modal.erb +5 -2
  221. data/lib/karafka/web/pro/ui/views/explorer/search/_timeout.erb +6 -0
  222. data/lib/karafka/web/pro/ui/views/explorer/search/index.erb +32 -0
  223. data/lib/karafka/web/pro/ui/views/health/_breadcrumbs.erb +3 -0
  224. data/lib/karafka/web/pro/ui/views/health/_no_data.erb +3 -0
  225. data/lib/karafka/web/pro/ui/views/health/_partition.erb +16 -1
  226. data/lib/karafka/web/pro/ui/views/health/_partition_lags.erb +3 -0
  227. data/lib/karafka/web/pro/ui/views/health/_partition_offset.erb +3 -0
  228. data/lib/karafka/web/pro/ui/views/health/_partition_times.erb +3 -0
  229. data/lib/karafka/web/pro/ui/views/health/_table_metadata.erb +4 -1
  230. data/lib/karafka/web/pro/ui/views/health/_tabs.erb +3 -0
  231. data/lib/karafka/web/pro/ui/views/health/changes.erb +4 -1
  232. data/lib/karafka/web/pro/ui/views/health/cluster_lags.erb +3 -0
  233. data/lib/karafka/web/pro/ui/views/health/lags.erb +5 -2
  234. data/lib/karafka/web/pro/ui/views/health/offsets.erb +4 -1
  235. data/lib/karafka/web/pro/ui/views/health/overview.erb +8 -3
  236. data/lib/karafka/web/pro/ui/views/jobs/_job.erb +50 -38
  237. data/lib/karafka/web/pro/ui/views/jobs/_no_jobs.erb +3 -0
  238. data/lib/karafka/web/pro/ui/views/jobs/pending.erb +4 -1
  239. data/lib/karafka/web/pro/ui/views/jobs/running.erb +4 -1
  240. data/lib/karafka/web/pro/ui/views/recurring_tasks/_actions.erb +3 -0
  241. data/lib/karafka/web/pro/ui/views/recurring_tasks/_batch_actions.erb +3 -0
  242. data/lib/karafka/web/pro/ui/views/recurring_tasks/_breadcrumbs.erb +3 -0
  243. data/lib/karafka/web/pro/ui/views/recurring_tasks/_log.erb +3 -0
  244. data/lib/karafka/web/pro/ui/views/recurring_tasks/_not_active.erb +3 -0
  245. data/lib/karafka/web/pro/ui/views/recurring_tasks/_tabs.erb +3 -0
  246. data/lib/karafka/web/pro/ui/views/recurring_tasks/_task.erb +3 -0
  247. data/lib/karafka/web/pro/ui/views/recurring_tasks/logs.erb +3 -0
  248. data/lib/karafka/web/pro/ui/views/recurring_tasks/schedule.erb +3 -0
  249. data/lib/karafka/web/pro/ui/views/routing/_consumer_group.erb +3 -0
  250. data/lib/karafka/web/pro/ui/views/routing/_detail.erb +3 -0
  251. data/lib/karafka/web/pro/ui/views/routing/_topic.erb +3 -0
  252. data/lib/karafka/web/pro/ui/views/routing/index.erb +3 -0
  253. data/lib/karafka/web/pro/ui/views/routing/show.erb +3 -0
  254. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_breadcrumbs.erb +6 -3
  255. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_key.erb +3 -0
  256. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_message.erb +4 -1
  257. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/_messages.erb +3 -0
  258. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/partition.erb +23 -16
  259. data/lib/karafka/web/pro/ui/views/scheduled_messages/explorer/topic.erb +6 -3
  260. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/_breadcrumbs.erb +3 -0
  261. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/_no_groups.erb +3 -0
  262. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/index.erb +4 -1
  263. data/lib/karafka/web/pro/ui/views/scheduled_messages/schedules/show.erb +3 -0
  264. data/lib/karafka/web/pro/ui/views/shared/_navigation.erb +25 -17
  265. data/lib/karafka/web/pro/ui/views/shared/_rdkafka_form_error_alert_box.erb +16 -0
  266. data/lib/karafka/web/pro/ui/views/shared/branding/_label.erb +3 -0
  267. data/lib/karafka/web/pro/ui/views/shared/branding/_notice.erb +3 -0
  268. data/lib/karafka/web/pro/ui/views/topics/configs/_breadcrumbs.erb +34 -0
  269. data/lib/karafka/web/pro/ui/views/topics/configs/_config.erb +26 -0
  270. data/lib/karafka/web/pro/ui/views/topics/configs/_delete_button.erb +13 -0
  271. data/lib/karafka/web/pro/ui/views/topics/configs/_edit_form.erb +50 -0
  272. data/lib/karafka/web/pro/ui/views/topics/configs/_edit_plan.erb +16 -0
  273. data/lib/karafka/web/pro/ui/views/topics/configs/_edit_warning.erb +12 -0
  274. data/lib/karafka/web/pro/ui/views/topics/configs/edit.erb +16 -0
  275. data/lib/karafka/web/pro/ui/views/topics/{config.erb → configs/index.erb} +9 -3
  276. data/lib/karafka/web/pro/ui/views/topics/distributions/_add_partitions_button.erb +13 -0
  277. data/lib/karafka/web/pro/ui/views/topics/{distribution → distributions}/_badges.erb +3 -0
  278. data/lib/karafka/web/pro/ui/views/topics/distributions/_breadcrumbs.erb +28 -0
  279. data/lib/karafka/web/pro/ui/views/topics/{distribution → distributions}/_chart.erb +3 -0
  280. data/lib/karafka/web/pro/ui/views/topics/distributions/_edit_form.erb +47 -0
  281. data/lib/karafka/web/pro/ui/views/topics/distributions/_edit_hints.erb +15 -0
  282. data/lib/karafka/web/pro/ui/views/topics/distributions/_edit_warnings.erb +14 -0
  283. data/lib/karafka/web/pro/ui/views/topics/distributions/_empty_partitions.erb +4 -0
  284. data/lib/karafka/web/pro/ui/views/topics/{distribution → distributions}/_limited.erb +3 -0
  285. data/lib/karafka/web/pro/ui/views/topics/distributions/_partition.erb +13 -0
  286. data/lib/karafka/web/pro/ui/views/topics/distributions/edit.erb +16 -0
  287. data/lib/karafka/web/pro/ui/views/topics/{distribution.erb → distributions/show.erb} +11 -7
  288. data/lib/karafka/web/pro/ui/views/topics/offsets/_breadcrumbs.erb +20 -0
  289. data/lib/karafka/web/pro/ui/views/topics/offsets/_partition.erb +13 -0
  290. data/lib/karafka/web/pro/ui/views/topics/{offsets.erb → offsets/show.erb} +6 -3
  291. data/lib/karafka/web/pro/ui/views/topics/replications/_breadcrumbs.erb +20 -0
  292. data/lib/karafka/web/pro/ui/views/topics/{_partition.erb → replications/_partition.erb} +4 -1
  293. data/lib/karafka/web/pro/ui/views/topics/{replication.erb → replications/show.erb} +6 -3
  294. data/lib/karafka/web/pro/ui/views/topics/topics/_breadcrumbs.erb +32 -0
  295. data/lib/karafka/web/pro/ui/views/topics/topics/_create_button.erb +13 -0
  296. data/lib/karafka/web/pro/ui/views/topics/topics/_create_hints.erb +15 -0
  297. data/lib/karafka/web/pro/ui/views/topics/topics/_delete_form.erb +36 -0
  298. data/lib/karafka/web/pro/ui/views/topics/topics/_delete_hints.erb +15 -0
  299. data/lib/karafka/web/pro/ui/views/topics/topics/_delete_warning.erb +13 -0
  300. data/lib/karafka/web/pro/ui/views/topics/topics/_new_form.erb +80 -0
  301. data/lib/karafka/web/pro/ui/views/topics/{_tabs.erb → topics/_tabs.erb} +7 -4
  302. data/lib/karafka/web/pro/ui/views/topics/topics/_topic.erb +12 -0
  303. data/lib/karafka/web/pro/ui/views/topics/topics/edit.erb +10 -0
  304. data/lib/karafka/web/pro/ui/views/topics/topics/index.erb +19 -0
  305. data/lib/karafka/web/pro/ui/views/topics/topics/new.erb +12 -0
  306. data/lib/karafka/web/processing/consumers/metrics.rb +1 -1
  307. data/lib/karafka/web/processing/consumers/state.rb +1 -1
  308. data/lib/karafka/web/processing/publisher.rb +4 -4
  309. data/lib/karafka/web/tracking/consumers/contracts/partition.rb +1 -0
  310. data/lib/karafka/web/tracking/consumers/listeners/errors.rb +38 -9
  311. data/lib/karafka/web/tracking/consumers/listeners/pausing.rb +2 -2
  312. data/lib/karafka/web/tracking/consumers/listeners/transactions.rb +44 -0
  313. data/lib/karafka/web/tracking/consumers/reporter.rb +2 -2
  314. data/lib/karafka/web/tracking/consumers/sampler.rb +81 -14
  315. data/lib/karafka/web/tracking/helpers/sysconf.rb +33 -0
  316. data/lib/karafka/web/tracking/producers/reporter.rb +2 -9
  317. data/lib/karafka/web/ui/app.rb +19 -112
  318. data/lib/karafka/web/ui/base.rb +63 -4
  319. data/lib/karafka/web/ui/controllers/base_controller.rb +43 -1
  320. data/lib/karafka/web/ui/controllers/cluster_controller.rb +5 -2
  321. data/lib/karafka/web/ui/controllers/errors_controller.rb +1 -1
  322. data/lib/karafka/web/ui/controllers/requests/execution_wrapper.rb +52 -0
  323. data/lib/karafka/web/ui/controllers/requests/hookable.rb +99 -0
  324. data/lib/karafka/web/ui/controllers/requests/params.rb +39 -1
  325. data/lib/karafka/web/ui/controllers/responses/redirect.rb +0 -5
  326. data/lib/karafka/web/ui/controllers/status_controller.rb +3 -0
  327. data/lib/karafka/web/ui/helpers/application_helper.rb +10 -1
  328. data/lib/karafka/web/ui/helpers/paths_helper.rb +54 -10
  329. data/lib/karafka/web/ui/lib/admin.rb +1 -1
  330. data/lib/karafka/web/ui/lib/cache.rb +135 -0
  331. data/lib/karafka/web/ui/models/broker.rb +1 -2
  332. data/lib/karafka/web/ui/models/cluster_info.rb +15 -21
  333. data/lib/karafka/web/ui/models/consumers_metrics.rb +1 -1
  334. data/lib/karafka/web/ui/models/consumers_state.rb +1 -1
  335. data/lib/karafka/web/ui/models/counters.rb +1 -1
  336. data/lib/karafka/web/ui/models/health.rb +9 -7
  337. data/lib/karafka/web/ui/models/process.rb +14 -0
  338. data/lib/karafka/web/ui/models/processes.rb +2 -2
  339. data/lib/karafka/web/ui/models/recurring_tasks/schedule.rb +1 -1
  340. data/lib/karafka/web/ui/models/status.rb +27 -8
  341. data/lib/karafka/web/ui/models/topic.rb +1 -2
  342. data/lib/karafka/web/ui/public/javascripts/application.js +8 -98
  343. data/lib/karafka/web/ui/public/javascripts/application.min.js +15 -6
  344. data/lib/karafka/web/ui/public/javascripts/application.min.js.br +0 -0
  345. data/lib/karafka/web/ui/public/javascripts/application.min.js.gz +0 -0
  346. data/lib/karafka/web/ui/public/javascripts/components/action_confirmation_manager.js +30 -0
  347. data/lib/karafka/web/ui/public/javascripts/components/alerts.js +39 -0
  348. data/lib/karafka/web/ui/public/javascripts/components/btn_toggle_manager.js +17 -7
  349. data/lib/karafka/web/ui/public/javascripts/components/button_lock_manager.js +50 -0
  350. data/lib/karafka/web/ui/public/javascripts/components/live_poll.js +71 -19
  351. data/lib/karafka/web/ui/public/javascripts/components/message_republish_manager.js +50 -0
  352. data/lib/karafka/web/ui/public/javascripts/components/page_title_tracker.js +21 -0
  353. data/lib/karafka/web/ui/public/javascripts/components/partition_redirect_manager.js +21 -0
  354. data/lib/karafka/web/ui/public/javascripts/components/time_ago_manager.js +25 -0
  355. data/lib/karafka/web/ui/public/javascripts/components/timestamp_selector.js +30 -0
  356. data/lib/karafka/web/ui/public/javascripts/libs/datepicker.js +2 -2
  357. data/lib/karafka/web/ui/public/stylesheets/application.css +30 -0
  358. data/lib/karafka/web/ui/public/stylesheets/application.min.css +5123 -13
  359. data/lib/karafka/web/ui/public/stylesheets/application.min.css.br +0 -0
  360. data/lib/karafka/web/ui/public/stylesheets/application.min.css.gz +0 -0
  361. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_dark.min.css.gz +0 -0
  362. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_light.min.css.gz +0 -0
  363. data/lib/karafka/web/ui/public/stylesheets/libs/tailwind.css +512 -213
  364. data/lib/karafka/web/ui/routes/assets.rb +53 -0
  365. data/lib/karafka/web/ui/routes/base.rb +36 -0
  366. data/lib/karafka/web/ui/routes/cluster.rb +28 -0
  367. data/lib/karafka/web/ui/routes/consumers.rb +35 -0
  368. data/lib/karafka/web/ui/routes/dashboard.rb +20 -0
  369. data/lib/karafka/web/ui/routes/errors.rb +26 -0
  370. data/lib/karafka/web/ui/routes/jobs.rb +28 -0
  371. data/lib/karafka/web/ui/routes/pro_only.rb +27 -0
  372. data/lib/karafka/web/ui/routes/routing.rb +26 -0
  373. data/lib/karafka/web/ui/routes/status.rb +19 -0
  374. data/lib/karafka/web/ui/routes/support.rb +19 -0
  375. data/lib/karafka/web/ui/routes/ux.rb +19 -0
  376. data/lib/karafka/web/ui/views/cluster/_partition.erb +2 -2
  377. data/lib/karafka/web/ui/views/cluster/brokers.erb +1 -1
  378. data/lib/karafka/web/ui/views/consumers/_breadcrumbs.erb +7 -1
  379. data/lib/karafka/web/ui/views/consumers/_consumer.erb +39 -30
  380. data/lib/karafka/web/ui/views/consumers/_incompatible.erb +13 -0
  381. data/lib/karafka/web/ui/views/consumers/_no_consumers.erb +2 -2
  382. data/lib/karafka/web/ui/views/consumers/_tabs.erb +4 -4
  383. data/lib/karafka/web/ui/views/consumers/index.erb +1 -1
  384. data/lib/karafka/web/ui/views/dashboard/_feature_pro.erb +1 -1
  385. data/lib/karafka/web/ui/views/dashboard/_not_enough_data.erb +2 -2
  386. data/lib/karafka/web/ui/views/dashboard/_ranges_selector.erb +1 -1
  387. data/lib/karafka/web/ui/views/dashboard/index.erb +6 -49
  388. data/lib/karafka/web/ui/views/errors/_detail.erb +3 -3
  389. data/lib/karafka/web/ui/views/errors/index.erb +1 -1
  390. data/lib/karafka/web/ui/views/jobs/_job.erb +38 -29
  391. data/lib/karafka/web/ui/views/jobs/pending.erb +1 -1
  392. data/lib/karafka/web/ui/views/jobs/running.erb +1 -1
  393. data/lib/karafka/web/ui/views/layout.erb +7 -5
  394. data/lib/karafka/web/ui/views/shared/_become_pro.erb +1 -1
  395. data/lib/karafka/web/ui/views/shared/_brand.erb +1 -1
  396. data/lib/karafka/web/ui/views/shared/_breadcrumbs.erb +1 -1
  397. data/lib/karafka/web/ui/views/shared/_content.erb +1 -1
  398. data/lib/karafka/web/ui/views/shared/_controls.erb +10 -3
  399. data/lib/karafka/web/ui/views/shared/_custom_nav.erb +9 -0
  400. data/lib/karafka/web/ui/views/shared/_flashes.erb +3 -5
  401. data/lib/karafka/web/ui/views/shared/_header.erb +25 -2
  402. data/lib/karafka/web/ui/views/shared/_navigation.erb +17 -15
  403. data/lib/karafka/web/ui/views/shared/alerts/_error.erb +8 -0
  404. data/lib/karafka/web/ui/views/shared/alerts/_info.erb +8 -0
  405. data/lib/karafka/web/ui/views/shared/alerts/_primary.erb +8 -0
  406. data/lib/karafka/web/ui/views/shared/alerts/_secondary.erb +8 -0
  407. data/lib/karafka/web/ui/views/shared/alerts/_success.erb +8 -0
  408. data/lib/karafka/web/ui/views/shared/alerts/_warning.erb +8 -0
  409. data/lib/karafka/web/ui/views/shared/exceptions/incompatible_schema.erb +34 -0
  410. data/lib/karafka/web/ui/views/shared/exceptions/not_allowed.erb +4 -0
  411. data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +5 -1
  412. data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +4 -0
  413. data/lib/karafka/web/ui/views/shared/icons/_arrow_left.erb +3 -0
  414. data/lib/karafka/web/ui/views/shared/icons/_arrow_up_tray.erb +3 -0
  415. data/lib/karafka/web/ui/views/shared/icons/_clock.erb +3 -0
  416. data/lib/karafka/web/ui/views/shared/icons/_pencil.erb +3 -0
  417. data/lib/karafka/web/ui/views/shared/icons/_pencil_square.erb +3 -0
  418. data/lib/karafka/web/ui/views/shared/icons/_play_pause.erb +3 -0
  419. data/lib/karafka/web/ui/views/shared/icons/_plus.erb +3 -0
  420. data/lib/karafka/web/ui/views/shared/icons/_trash.erb +3 -0
  421. data/lib/karafka/web/ui/views/status/failures/_live_reporting.erb +1 -1
  422. data/lib/karafka/web/ui/views/status/failures/_partitions.erb +3 -3
  423. data/lib/karafka/web/ui/views/status/failures/_state_calculation.erb +2 -2
  424. data/lib/karafka/web/ui/views/status/info/_components.erb +6 -6
  425. data/lib/karafka/web/ui/views/status/show.erb +15 -0
  426. data/lib/karafka/web/ui/views/status/warnings/_consumers_schemas.erb +31 -0
  427. data/lib/karafka/web/ui/views/ux/_icons.erb +1 -1
  428. data/lib/karafka/web/ui/views/ux/_status_rows.erb +6 -0
  429. data/lib/karafka/web/version.rb +1 -1
  430. data/lib/karafka/web.rb +9 -1
  431. data/package-lock.json +799 -1226
  432. data/package.json +3 -4
  433. data/postcss.config.js +1 -2
  434. data/renovate.json +13 -1
  435. data/tailwind.config.js +0 -4
  436. data.tar.gz.sig +0 -0
  437. metadata +233 -109
  438. metadata.gz.sig +0 -0
  439. data/lib/karafka/web/pro/commanding/commands/quiet.rb +0 -34
  440. data/lib/karafka/web/pro/commanding/commands/stop.rb +0 -34
  441. data/lib/karafka/web/pro/commanding/commands/trace.rb +0 -41
  442. data/lib/karafka/web/pro/ui/controllers/commanding_controller.rb +0 -118
  443. data/lib/karafka/web/pro/ui/controllers/commands_controller.rb +0 -96
  444. data/lib/karafka/web/pro/ui/controllers/consumers_controller.rb +0 -138
  445. data/lib/karafka/web/pro/ui/controllers/explorer_controller.rb +0 -220
  446. data/lib/karafka/web/pro/ui/controllers/messages_controller.rb +0 -107
  447. data/lib/karafka/web/pro/ui/controllers/search_controller.rb +0 -73
  448. data/lib/karafka/web/pro/ui/controllers/topics_controller.rb +0 -130
  449. data/lib/karafka/web/pro/ui/views/commands/_breadcrumbs.erb +0 -21
  450. data/lib/karafka/web/pro/ui/views/commands/_command_details.erb +0 -1
  451. data/lib/karafka/web/pro/ui/views/commands/_empty.erb +0 -3
  452. data/lib/karafka/web/pro/ui/views/commands/show.erb +0 -33
  453. data/lib/karafka/web/pro/ui/views/consumers/_breadcrumbs.erb +0 -55
  454. data/lib/karafka/web/pro/ui/views/consumers/_consumer.erb +0 -47
  455. data/lib/karafka/web/pro/ui/views/consumers/_consumer_controls.erb +0 -95
  456. data/lib/karafka/web/pro/ui/views/consumers/_consumer_performance.erb +0 -59
  457. data/lib/karafka/web/pro/ui/views/consumers/_tabs.erb +0 -33
  458. data/lib/karafka/web/pro/ui/views/consumers/consumer/_commands.erb +0 -72
  459. data/lib/karafka/web/pro/ui/views/consumers/consumer/_consumer_group.erb +0 -8
  460. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_subscriptions.erb +0 -7
  461. data/lib/karafka/web/pro/ui/views/consumers/details.erb +0 -13
  462. data/lib/karafka/web/pro/ui/views/consumers/subscriptions.erb +0 -25
  463. data/lib/karafka/web/pro/ui/views/explorer/_no_topics.erb +0 -1
  464. data/lib/karafka/web/pro/ui/views/explorer/_topic.erb +0 -10
  465. data/lib/karafka/web/pro/ui/views/explorer/index.erb +0 -14
  466. data/lib/karafka/web/pro/ui/views/explorer/messages/_headers.erb +0 -33
  467. data/lib/karafka/web/pro/ui/views/explorer/partition/_cleaned.erb +0 -3
  468. data/lib/karafka/web/pro/ui/views/explorer/partition/_empty.erb +0 -3
  469. data/lib/karafka/web/pro/ui/views/explorer/topic/_empty.erb +0 -3
  470. data/lib/karafka/web/pro/ui/views/search/_breadcrumbs.erb +0 -1
  471. data/lib/karafka/web/pro/ui/views/search/_fix_errors.erb +0 -3
  472. data/lib/karafka/web/pro/ui/views/search/_no_results.erb +0 -3
  473. data/lib/karafka/web/pro/ui/views/search/_timeout.erb +0 -3
  474. data/lib/karafka/web/pro/ui/views/search/index.erb +0 -29
  475. data/lib/karafka/web/pro/ui/views/topics/_breadcrumbs.erb +0 -45
  476. data/lib/karafka/web/pro/ui/views/topics/_partition_offsets.erb +0 -10
  477. data/lib/karafka/web/pro/ui/views/topics/_topic.erb +0 -9
  478. data/lib/karafka/web/pro/ui/views/topics/distribution/_empty_partitions.erb +0 -1
  479. data/lib/karafka/web/pro/ui/views/topics/distribution/_partition.erb +0 -10
  480. data/lib/karafka/web/pro/ui/views/topics/index.erb +0 -14
  481. data/lib/karafka/web/ui/lib/ttl_cache.rb +0 -82
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Ui
6
+ module Controllers
7
+ module Requests
8
+ # Adds before/after hook support for controller methods.
9
+ #
10
+ # This module allows registering callbacks that run before and after
11
+ # any named method (e.g., `call`, `show`) or for all methods.
12
+ #
13
+ # Hooks are inherited from parent controllers, making it easy to
14
+ # define shared behavior across a hierarchy.
15
+ #
16
+ # @example Adding hooks to a controller
17
+ # class MyController
18
+ # include Hookable
19
+ #
20
+ # before(:call) { puts "before call" }
21
+ # after(:call) { puts "after call" }
22
+ #
23
+ # def call
24
+ # run_before_hooks(:call)
25
+ # # actual logic
26
+ # run_after_hooks(:call)
27
+ # end
28
+ # end
29
+ module Hookable
30
+ # Hook into class inclusion to extend DSL
31
+ #
32
+ # @param base [Class] the class including this module
33
+ def self.included(base)
34
+ base.extend(ClassMethods)
35
+ end
36
+
37
+ # DSL methods for defining and inheriting hooks
38
+ module ClassMethods
39
+ # Inherit parent hooks into subclass
40
+ #
41
+ # @param subclass [Class] the subclass inheriting from base controller
42
+ def inherited(subclass)
43
+ super
44
+ subclass.before_hooks.concat(before_hooks)
45
+ subclass.after_hooks.concat(after_hooks)
46
+ end
47
+
48
+ # @return [Array] accumulated before hooks
49
+ def before_hooks
50
+ @before_hooks ||= []
51
+ end
52
+
53
+ # @return [Array] accumulated after hooks
54
+ def after_hooks
55
+ @after_hooks ||= []
56
+ end
57
+
58
+ # Register a before hook
59
+ #
60
+ # @param actions [Array<Symbol>] optional action names to match (e.g., :call)
61
+ # @param block [Proc]
62
+ # @yield a block to execute before matched actions
63
+ def before(*actions, &block)
64
+ before_hooks << [actions, block]
65
+ end
66
+
67
+ # Register an after hook
68
+ #
69
+ # @param actions [Array<Symbol>] optional action names to match (e.g., :call)
70
+ # @param block [Proc]
71
+ # @yield a block to execute after matched actions
72
+ def after(*actions, &block)
73
+ after_hooks << [actions, block]
74
+ end
75
+ end
76
+
77
+ # Run all before hooks matching the action
78
+ #
79
+ # @param action_name [Symbol] the method name being invoked
80
+ def run_before_hooks(action_name)
81
+ self.class.before_hooks.each do |actions, block|
82
+ instance_exec(&block) if actions.empty? || actions.include?(action_name)
83
+ end
84
+ end
85
+
86
+ # Run all after hooks matching the action
87
+ #
88
+ # @param action_name [Symbol] the method name being invoked
89
+ def run_after_hooks(action_name)
90
+ self.class.after_hooks.each do |actions, block|
91
+ instance_exec(&block) if actions.empty? || actions.include?(action_name)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -17,13 +17,51 @@ module Karafka
17
17
  days
18
18
  ].freeze
19
19
 
20
- private_constant :ALLOWED_RANGES
20
+ # What string values true can be in params
21
+ ALLOWED_BOOLEAN_TRUE = %w[
22
+ on
23
+ yes
24
+ true
25
+ ].freeze
26
+
27
+ private_constant :ALLOWED_RANGES, :ALLOWED_BOOLEAN_TRUE
21
28
 
22
29
  # @param request_params [Hash] raw hash with params
23
30
  def initialize(request_params)
24
31
  @request_params = request_params
25
32
  end
26
33
 
34
+ # @param key [String, Symbol] params key
35
+ # @return [Object] value of the requested param
36
+ def [](key)
37
+ fetch(key.to_s)
38
+ end
39
+
40
+ # @param args [Symbol, Object] key we want to fetch and other args for `Hash#fetch`
41
+ # @return [Object] fetched object
42
+ def fetch(*args)
43
+ args[0] = args[0].to_s
44
+ @request_params.fetch(*args)
45
+ end
46
+
47
+ # @param key [String, Symbol] params key
48
+ # @return [Integer] integer value of the key
49
+ def int(key)
50
+ self[key].to_i
51
+ end
52
+
53
+ # @param key [String, Symbol] params key
54
+ # @return [Boolean] boolean key value
55
+ def bool(key)
56
+ ALLOWED_BOOLEAN_TRUE.include?(self[key])
57
+ end
58
+
59
+ # @param key [String, Symbol] params key
60
+ # @return [String] stringified key value
61
+ def str(key)
62
+ self[key].to_s
63
+ end
64
+
27
65
  # @return [Hash] current search or empty if no search query present
28
66
  def current_search
29
67
  return @current_search if @current_search
@@ -16,11 +16,6 @@ module Karafka
16
16
  @path = path
17
17
  @flashes = flashes
18
18
  end
19
-
20
- # @return [Boolean] are we going back via referer and not explicit path
21
- def back?
22
- @path == :back
23
- end
24
19
  end
25
20
  end
26
21
  end
@@ -10,6 +10,9 @@ module Karafka
10
10
  # page where we check that everything is as expected and if not, we can provide some
11
11
  # helpful instructions on how to fix the issues.
12
12
  class StatusController < BaseController
13
+ # Fresh state is needed to generate always correct status
14
+ before { cache.clear }
15
+
13
16
  # Displays the Web UI setup status
14
17
  def show
15
18
  @status = Models::Status.new
@@ -223,13 +223,22 @@ module Karafka
223
223
  title = 'Not available until first offset commit'
224
224
  %(<span class="badge badge-secondary" title="#{title}">N/A</span>)
225
225
  elsif explore
226
- path = explorer_path(topic_name, partition_id, offset)
226
+ path = explorer_topics_path(topic_name, partition_id, offset)
227
227
  %(<a href="#{path}">#{offset}</a>)
228
228
  else
229
229
  offset.to_s
230
230
  end
231
231
  end
232
232
 
233
+ # Normalizes the metric value for display. Negative values coming from statistics usually
234
+ # mean, that the value is not (yet) available.
235
+ #
236
+ # @param value [Integer]
237
+ # @return [String] input value if not negative or N/A
238
+ def normalized_metric(value)
239
+ value.negative? ? 'N/A' : value.to_s
240
+ end
241
+
233
242
  # @param details [::Karafka::Web::Ui::Models::Partition] partition information with
234
243
  # lso risk state info
235
244
  # @return [String] background classes for row marking
@@ -6,6 +6,15 @@ module Karafka
6
6
  module Helpers
7
7
  # Helper for web ui paths builders
8
8
  module PathsHelper
9
+ # Method that can be used to have conditions in breadcrumbs, etc based on the action
10
+ # we're in
11
+ #
12
+ # @param args [Array<Symbol>] action names we want to check
13
+ # @return [Boolean] true if any matches the current action name
14
+ def action?(*args)
15
+ args.include?(@current_action_name)
16
+ end
17
+
9
18
  # Helper method to flatten nested hashes and arrays
10
19
  # @param prefix [String] The prefix for nested keys, initially an empty string.
11
20
  # @param hash [Hash, Array] The nested hash or array to be flattened.
@@ -54,16 +63,51 @@ module Karafka
54
63
 
55
64
  # Helps build explorer paths. We often link offsets to proper messages, etc so this
56
65
  # allows us to short-track this
57
- # @param topic_name [String, nil] name of the topic where we want to go within the
58
- # explorer or nil if we want to just go to the explorer root
59
- # @param partition_id [Integer, nil] partition we want to display in the explorer or nil
60
- # if we want to go to the topic root
61
- # @param offset [Integer, nil] offset of particular message or nil of we want to just go
62
- # to the partition root
63
- # @param action [String, nil] specific routed action or nil
66
+ # @param args [Array<String>] sub-paths
64
67
  # @return [String] path to the expected location
65
- def explorer_path(topic_name = nil, partition_id = nil, offset = nil, action = nil)
66
- root_path(*['explorer', topic_name, partition_id, offset, action].compact)
68
+ def explorer_path(*args)
69
+ root_path(*['explorer', args.compact].flatten)
70
+ end
71
+
72
+ # Generates routes for explorer topics paths
73
+ #
74
+ # @param args [Array<String>] sub-paths
75
+ # @return explorer topics path
76
+ def explorer_topics_path(*args)
77
+ explorer_path(*['topics', args.compact].flatten)
78
+ end
79
+
80
+ # Generates routes for explorer messages paths
81
+ #
82
+ # @param args [Array<String>] sub-paths
83
+ # @return [String] explorer messages path
84
+ def explorer_messages_path(*args)
85
+ explorer_path(*['messages', args.compact].flatten)
86
+ end
87
+
88
+ # Helps build topics paths
89
+ #
90
+ # @param args [Array<String>] path params for the topics scope
91
+ # @return [String] topics scope path
92
+ def topics_path(*args)
93
+ root_path('topics', *args)
94
+ end
95
+
96
+ # Helps build consumers paths
97
+ #
98
+ # @param args [Array<String>] path params for consumers scope
99
+ # @return [String] consumers scope path
100
+ def consumers_path(*args)
101
+ root_path('consumers', *args)
102
+ end
103
+
104
+ # Helps build per-consumer scope paths
105
+ #
106
+ # @param consumer_id [String] consumer process id
107
+ # @param args [Array<String>] other path components
108
+ # @return [String] per consumer specific path
109
+ def consumer_path(consumer_id, *args)
110
+ consumers_path(consumer_id, *args)
67
111
  end
68
112
 
69
113
  # Helps build scheduled messages paths.
@@ -80,7 +124,7 @@ module Karafka
80
124
  action = nil
81
125
  )
82
126
  root_path(
83
- *['scheduled_messages', 'explorer', topic_name, partition_id, offset, action].compact
127
+ *['scheduled_messages', 'explorer', 'topics', topic_name, partition_id, offset, action].compact
84
128
  )
85
129
  end
86
130
  end
@@ -17,7 +17,7 @@ module Karafka
17
17
  class << self
18
18
  extend Forwardable
19
19
 
20
- def_delegators ::Karafka::Admin, :read_watermark_offsets, :cluster_info
20
+ def_delegators ::Karafka::Admin, :read_watermark_offsets, :cluster_info, :topic_info
21
21
 
22
22
  # Allows us to read messages from the topic
23
23
  #
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Web
5
+ module Ui
6
+ module Lib
7
+ # Thread-safe in-memory cache with metadata tracking.
8
+ #
9
+ # This cache supports storing computed values, tracking the last update time,
10
+ # and computing a hash of the contents for change detection.
11
+ # It's designed for ephemeral, per-instance caching in Karafka Web controllers or libs.
12
+ #
13
+ # The cache ensures safe concurrent access via a mutex and provides utilities
14
+ # for cache invalidation based on external session state (timestamp + hash).
15
+ #
16
+ # @note All cache operations are mutex-synchronized for thread safety.
17
+ #
18
+ # @note We do not have granular level caching because our Web UI cache is fairly simple
19
+ # and we do not want to overcomplicate things.
20
+ class Cache
21
+ # Initializes an empty cache instance
22
+ # @param ttl_ms [Integer] time to live of the whole cache. After this time cache will be
23
+ # cleaned whether or not it is expired.
24
+ def initialize(ttl_ms)
25
+ @ttl_ms = ttl_ms
26
+ @values = {}
27
+ @timestamp = nil
28
+ @hash = nil
29
+ @mutex = Mutex.new
30
+ end
31
+
32
+ # Fetches or computes and stores a value under the given key.
33
+ #
34
+ # If the key already exists, returns the cached value.
35
+ # Otherwise, computes it via the provided block, stores it,
36
+ # and updates metadata (timestamp + hash).
37
+ #
38
+ # @param key [Object] key to retrieve
39
+ # @yield block to compute the value if key is not present
40
+ # @return [Object] cached or computed value
41
+ def fetch(key)
42
+ @mutex.synchronize do
43
+ return @values[key] if @values.key?(key)
44
+
45
+ @values[key] = yield
46
+ @hash = Digest::SHA256.hexdigest(
47
+ @values.sort.to_h.to_json
48
+ )
49
+ @timestamp = Time.now.to_f
50
+ @values[key]
51
+ end
52
+ end
53
+
54
+ # Clears the cache and resets metadata (timestamp and hash).
55
+ #
56
+ # If the mutex is already owned by the current thread, clears immediately.
57
+ # Otherwise, synchronizes first.
58
+ #
59
+ # @return [void]
60
+ def clear
61
+ cleaning = lambda do
62
+ @values.clear
63
+ @timestamp = nil
64
+ @hash = nil
65
+ end
66
+
67
+ return cleaning.call if @mutex.owned?
68
+
69
+ @mutex.synchronize do
70
+ cleaning.call
71
+ end
72
+ end
73
+
74
+ # Checks whether any values have been cached yet
75
+ #
76
+ # @return [Boolean] true if the cache has been written to
77
+ def exist?
78
+ !timestamp.nil?
79
+ end
80
+
81
+ # Returns the last update timestamp of the cache
82
+ #
83
+ # @return [Integer, nil] Unix timestamp or nil if never set
84
+ def timestamp
85
+ @mutex.synchronize { @timestamp }
86
+ end
87
+
88
+ # Returns the hash representing the current cached data state
89
+ #
90
+ # @return [String, nil] SHA256 hex digest or nil if never set
91
+ def hash
92
+ @mutex.synchronize { @hash }
93
+ end
94
+
95
+ # Clears the cache if the provided session hash and timestamp differ
96
+ #
97
+ # This is used to invalidate the cache if the external session data indicates
98
+ # a newer or inconsistent state.
99
+ #
100
+ # @param session_hash [String, nil] hash from the session or remote side
101
+ # @param session_timestamp [Integer, nil] timestamp from the session
102
+ # @return [Boolean] true if the cache was cleared, false otherwise
103
+ def clear_if_needed(session_hash, session_timestamp)
104
+ @mutex.synchronize do
105
+ return unless should_refresh?(session_hash, session_timestamp)
106
+
107
+ clear
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ # Determines whether the cache should be refreshed based on session data
114
+ #
115
+ # @param session_hash [String, nil]
116
+ # @param session_timestamp [Integer, nil]
117
+ # @return [Boolean] true if cache should be refreshed
118
+ def should_refresh?(session_hash, session_timestamp)
119
+ return true if @hash.nil? || @timestamp.nil?
120
+ return true if session_hash.nil? || session_timestamp.nil?
121
+
122
+ now = (Time.now.to_f * 1_000).to_i
123
+
124
+ return true if now - (@timestamp * 1_000) > @ttl_ms
125
+
126
+ return false if @hash == session_hash
127
+ return false if @timestamp > session_timestamp
128
+
129
+ true
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -9,8 +9,7 @@ module Karafka
9
9
  class << self
10
10
  # @return [Array<Broker>] all brokers in the cluster
11
11
  def all
12
- # We do not cache here because we want the most recent state of brokers possible
13
- ClusterInfo.fetch(cached: false).brokers.map do |broker|
12
+ ClusterInfo.fetch.brokers.map do |broker|
14
13
  new(broker)
15
14
  end
16
15
  end
@@ -10,26 +10,18 @@ module Karafka
10
10
  class << self
11
11
  # Gets us all the cluster metadata info
12
12
  #
13
- # @param cached [Boolean] should we use cached data (true by default)
14
13
  # @return [Rdkafka::Metadata] cluster metadata info
15
- def fetch(cached: true)
16
- cache = ::Karafka::Web.config.ui.cache
17
-
18
- cluster_info = cache.read(:cluster_info)
19
-
20
- if cluster_info.nil? || !cached
21
- cluster_info = cache.write(:cluster_info, Lib::Admin.cluster_info)
14
+ def fetch
15
+ Karafka::Web.config.ui.cache.fetch(:cluster_info) do
16
+ Lib::Admin.cluster_info
22
17
  end
23
-
24
- cluster_info
25
18
  end
26
19
 
27
20
  # Returns us all the info about available topics from the cluster
28
21
  #
29
- # @param cached [Boolean] should we use cached data (true by default)
30
22
  # @return [Array<Ui::Models::Topic>] topics details
31
- def topics(cached: true)
32
- fetch(cached: cached)
23
+ def topics
24
+ fetch
33
25
  .topics
34
26
  .map { |topic| Topic.new(topic) }
35
27
  end
@@ -37,19 +29,21 @@ module Karafka
37
29
  # Fetches us details about particular topic
38
30
  #
39
31
  # @param topic_name [String] name of the topic we are looking for
40
- # @param cached [Boolean] should we use cached data (true by default)
41
32
  # @return [Ui::Models::Topic] topic details
42
- def topic(topic_name, cached: true)
43
- topics(cached: cached)
44
- .find { |topic_data| topic_data.topic_name == topic_name }
45
- .tap { |topic| topic || raise(Web::Errors::Ui::NotFoundError, topic_name) }
33
+ def topic(topic_name)
34
+ Lib::Admin
35
+ .topic_info(topic_name)
36
+ .then { |topic| Topic.new(topic) }
37
+ rescue Rdkafka::RdkafkaError => e
38
+ raise e unless e.code == :unknown_topic_or_part
39
+
40
+ raise(Web::Errors::Ui::NotFoundError, topic_name)
46
41
  end
47
42
 
48
43
  # @param topic_name [String] name of the topic we are looking for
49
- # @param cached [Boolean] should we use cached data (true by default)
50
44
  # @return [Integer] number of partitions in a given topic
51
- def partitions_count(topic_name, cached: true)
52
- topic(topic_name, cached: cached).partition_count
45
+ def partitions_count(topic_name)
46
+ topic(topic_name).partition_count
53
47
  end
54
48
  end
55
49
  end
@@ -33,7 +33,7 @@ module Karafka
33
33
  # @return [::Karafka::Messages::Message, nil] most recent state or nil if none
34
34
  def fetch
35
35
  Lib::Admin.read_topic(
36
- Karafka::Web.config.topics.consumers.metrics,
36
+ Karafka::Web.config.topics.consumers.metrics.name,
37
37
  0,
38
38
  # We need to take last two and not the last because in case of a transactional
39
39
  # producer, the last one will match the transaction commit message
@@ -42,7 +42,7 @@ module Karafka
42
42
  # @return [::Karafka::Messages::Message, nil] most recent state or nil if none
43
43
  def fetch
44
44
  Lib::Admin.read_topic(
45
- Karafka::Web.config.topics.consumers.states,
45
+ Karafka::Web.config.topics.consumers.states.name,
46
46
  0,
47
47
  # We need to take last two and not the last because in case of a transactional
48
48
  # producer, the last one will match the transaction commit message
@@ -32,7 +32,7 @@ module Karafka
32
32
  MAX_ERROR_PARTITIONS.times do |partition|
33
33
  begin
34
34
  offsets = Lib::Admin.read_watermark_offsets(
35
- ::Karafka::Web.config.topics.errors,
35
+ ::Karafka::Web.config.topics.errors.name,
36
36
  partition
37
37
  )
38
38
  # We estimate that way instead of using `#cluster_info` to get the partitions count
@@ -50,15 +50,17 @@ module Karafka
50
50
  # @param state [Hash]
51
51
  # @param stats [Hash] hash where we will store all the aggregated data
52
52
  def fetch_topics_data(state, stats)
53
- iterate_partitions(state) do |process, consumer_group, topic, partition|
54
- cg_name = consumer_group.id
53
+ iterate_partitions(state) do |process, consumer_group, subscription_group, topic, partition|
54
+ cg_id = consumer_group.id
55
+ sg_id = subscription_group.id
55
56
  t_name = topic.name
56
57
  pt_id = partition.id
57
58
 
58
- stats[cg_name] ||= { topics: {} }
59
- stats[cg_name][:topics][t_name] ||= {}
60
- stats[cg_name][:topics][t_name][pt_id] = partition
61
- stats[cg_name][:topics][t_name][pt_id][:process] = process
59
+ stats[cg_id] ||= { topics: {} }
60
+ stats[cg_id][:topics][t_name] ||= {}
61
+ stats[cg_id][:topics][t_name][pt_id] = partition
62
+ stats[cg_id][:topics][t_name][pt_id][:process] = process
63
+ stats[cg_id][:topics][t_name][pt_id][:subscription_group_id] = sg_id
62
64
  end
63
65
  end
64
66
 
@@ -99,7 +101,7 @@ module Karafka
99
101
  consumer_group.subscription_groups.each do |subscription_group|
100
102
  subscription_group.topics.each do |topic|
101
103
  topic.partitions.each do |partition|
102
- yield(process, consumer_group, topic, partition)
104
+ yield(process, consumer_group, subscription_group, topic, partition)
103
105
  end
104
106
  end
105
107
  end
@@ -74,6 +74,20 @@ module Karafka
74
74
  .flat_map(&:partitions)
75
75
  .count
76
76
  end
77
+
78
+ # @return [Boolean] consumer process data is only compatible if the version of its schema
79
+ # matches the Web UI version of the schema. Some users have gradual deployment where
80
+ # they slowly rollout new versions of consumers and for this time, we want to indicate
81
+ # which instances have incompatible schema. This will allow us to show those processes
82
+ # to indicate they exist, so users are not confused with them missing, but will also
83
+ # block all capabilities until the full update of all components.
84
+ #
85
+ # @note We do not differentiate between reporting older or newer against Web UI Puma
86
+ # instance. Any incompatibility will cause reporting of incompatible. That's for the
87
+ # sake of simplicity as the long term goal for user is anyhow to align those.
88
+ def schema_compatible?
89
+ schema_version == ::Karafka::Web::Tracking::Consumers::Sampler::SCHEMA_VERSION
90
+ end
77
91
  end
78
92
  end
79
93
  end
@@ -45,7 +45,7 @@ module Karafka
45
45
  .sort
46
46
 
47
47
  Lib::Admin.read_topic(
48
- ::Karafka::Web.config.topics.consumers.reports,
48
+ ::Karafka::Web.config.topics.consumers.reports.name,
49
49
  0,
50
50
  # We set 10k here because we start from the latest offset of the reports, hence
51
51
  # we will never get this much. Do do not know however exactly how many reports
@@ -83,7 +83,7 @@ module Karafka
83
83
  # @param processes [Array<Hash>]
84
84
  # @return [Array<Hash>] sorted processes data
85
85
  def sort_processes(processes)
86
- processes.sort_by { |consumer| consumer[:process][:id] }
86
+ processes.sort_by { |consumer| consumer[:process].fetch(:id) }
87
87
  end
88
88
  end
89
89
  end
@@ -22,7 +22,7 @@ module Karafka
22
22
  # get it because requested topic/partition does not exist or nothing was present
23
23
  def current
24
24
  messages = Karafka::Admin.read_topic(
25
- config.topics.schedules,
25
+ config.topics.schedules.name,
26
26
  0,
27
27
  # We work here with the assumption that users won't click so fast to load
28
28
  # more than 20 commands prior to a state flush. If that happens, this will