karafka-web 0.10.4 → 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 (479) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +54 -176
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +87 -43
  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 +2 -2
  13. data/lib/karafka/web/config.rb +80 -9
  14. data/lib/karafka/web/contracts/config.rb +44 -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 +13 -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/pausing.rb +2 -2
  311. data/lib/karafka/web/tracking/consumers/listeners/transactions.rb +44 -0
  312. data/lib/karafka/web/tracking/consumers/reporter.rb +2 -2
  313. data/lib/karafka/web/tracking/consumers/sampler.rb +81 -14
  314. data/lib/karafka/web/tracking/helpers/sysconf.rb +33 -0
  315. data/lib/karafka/web/tracking/producers/reporter.rb +1 -1
  316. data/lib/karafka/web/ui/app.rb +19 -112
  317. data/lib/karafka/web/ui/base.rb +63 -4
  318. data/lib/karafka/web/ui/controllers/base_controller.rb +43 -1
  319. data/lib/karafka/web/ui/controllers/cluster_controller.rb +5 -2
  320. data/lib/karafka/web/ui/controllers/errors_controller.rb +1 -1
  321. data/lib/karafka/web/ui/controllers/requests/execution_wrapper.rb +52 -0
  322. data/lib/karafka/web/ui/controllers/requests/hookable.rb +99 -0
  323. data/lib/karafka/web/ui/controllers/requests/params.rb +39 -1
  324. data/lib/karafka/web/ui/controllers/responses/redirect.rb +0 -5
  325. data/lib/karafka/web/ui/controllers/status_controller.rb +3 -0
  326. data/lib/karafka/web/ui/helpers/application_helper.rb +10 -1
  327. data/lib/karafka/web/ui/helpers/paths_helper.rb +54 -10
  328. data/lib/karafka/web/ui/lib/admin.rb +1 -1
  329. data/lib/karafka/web/ui/lib/cache.rb +135 -0
  330. data/lib/karafka/web/ui/models/broker.rb +1 -2
  331. data/lib/karafka/web/ui/models/cluster_info.rb +15 -21
  332. data/lib/karafka/web/ui/models/consumers_metrics.rb +1 -1
  333. data/lib/karafka/web/ui/models/consumers_state.rb +1 -1
  334. data/lib/karafka/web/ui/models/counters.rb +1 -1
  335. data/lib/karafka/web/ui/models/health.rb +9 -7
  336. data/lib/karafka/web/ui/models/process.rb +14 -0
  337. data/lib/karafka/web/ui/models/processes.rb +2 -2
  338. data/lib/karafka/web/ui/models/recurring_tasks/schedule.rb +1 -1
  339. data/lib/karafka/web/ui/models/status.rb +27 -8
  340. data/lib/karafka/web/ui/models/topic.rb +1 -2
  341. data/lib/karafka/web/ui/public/javascripts/application.js +8 -98
  342. data/lib/karafka/web/ui/public/javascripts/application.min.js +12 -4
  343. data/lib/karafka/web/ui/public/javascripts/application.min.js.br +0 -0
  344. data/lib/karafka/web/ui/public/javascripts/application.min.js.gz +0 -0
  345. data/lib/karafka/web/ui/public/javascripts/components/action_confirmation_manager.js +30 -0
  346. data/lib/karafka/web/ui/public/javascripts/components/alerts.js +39 -0
  347. data/lib/karafka/web/ui/public/javascripts/components/button_lock_manager.js +50 -0
  348. data/lib/karafka/web/ui/public/javascripts/components/live_poll.js +71 -19
  349. data/lib/karafka/web/ui/public/javascripts/components/message_republish_manager.js +50 -0
  350. data/lib/karafka/web/ui/public/javascripts/components/page_title_tracker.js +21 -0
  351. data/lib/karafka/web/ui/public/javascripts/components/partition_redirect_manager.js +21 -0
  352. data/lib/karafka/web/ui/public/javascripts/components/time_ago_manager.js +25 -0
  353. data/lib/karafka/web/ui/public/javascripts/components/timestamp_selector.js +30 -0
  354. data/lib/karafka/web/ui/public/javascripts/libs/datepicker.js +2 -2
  355. data/lib/karafka/web/ui/public/stylesheets/application.css +30 -0
  356. data/lib/karafka/web/ui/public/stylesheets/application.min.css +5122 -13
  357. data/lib/karafka/web/ui/public/stylesheets/application.min.css.br +0 -0
  358. data/lib/karafka/web/ui/public/stylesheets/application.min.css.gz +0 -0
  359. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_dark.min.css.gz +0 -0
  360. data/lib/karafka/web/ui/public/stylesheets/libs/highlight_light.min.css.gz +0 -0
  361. data/lib/karafka/web/ui/public/stylesheets/libs/tailwind.css +512 -213
  362. data/lib/karafka/web/ui/routes/assets.rb +53 -0
  363. data/lib/karafka/web/ui/routes/base.rb +36 -0
  364. data/lib/karafka/web/ui/routes/cluster.rb +28 -0
  365. data/lib/karafka/web/ui/routes/consumers.rb +35 -0
  366. data/lib/karafka/web/ui/routes/dashboard.rb +20 -0
  367. data/lib/karafka/web/ui/routes/errors.rb +26 -0
  368. data/lib/karafka/web/ui/routes/jobs.rb +28 -0
  369. data/lib/karafka/web/ui/routes/pro_only.rb +27 -0
  370. data/lib/karafka/web/ui/routes/routing.rb +26 -0
  371. data/lib/karafka/web/ui/routes/status.rb +19 -0
  372. data/lib/karafka/web/ui/routes/support.rb +19 -0
  373. data/lib/karafka/web/ui/routes/ux.rb +19 -0
  374. data/lib/karafka/web/ui/views/cluster/_partition.erb +2 -2
  375. data/lib/karafka/web/ui/views/cluster/brokers.erb +1 -1
  376. data/lib/karafka/web/ui/views/consumers/_breadcrumbs.erb +7 -1
  377. data/lib/karafka/web/ui/views/consumers/_consumer.erb +39 -30
  378. data/lib/karafka/web/ui/views/consumers/_incompatible.erb +13 -0
  379. data/lib/karafka/web/ui/views/consumers/_no_consumers.erb +2 -2
  380. data/lib/karafka/web/ui/views/consumers/_tabs.erb +4 -4
  381. data/lib/karafka/web/ui/views/consumers/index.erb +1 -1
  382. data/lib/karafka/web/ui/views/dashboard/_feature_pro.erb +1 -1
  383. data/lib/karafka/web/ui/views/dashboard/_not_enough_data.erb +2 -2
  384. data/lib/karafka/web/ui/views/dashboard/_ranges_selector.erb +1 -1
  385. data/lib/karafka/web/ui/views/dashboard/index.erb +6 -49
  386. data/lib/karafka/web/ui/views/errors/_detail.erb +3 -3
  387. data/lib/karafka/web/ui/views/errors/index.erb +1 -1
  388. data/lib/karafka/web/ui/views/jobs/_job.erb +38 -29
  389. data/lib/karafka/web/ui/views/jobs/pending.erb +1 -1
  390. data/lib/karafka/web/ui/views/jobs/running.erb +1 -1
  391. data/lib/karafka/web/ui/views/layout.erb +7 -5
  392. data/lib/karafka/web/ui/views/shared/_become_pro.erb +1 -1
  393. data/lib/karafka/web/ui/views/shared/_brand.erb +1 -1
  394. data/lib/karafka/web/ui/views/shared/_breadcrumbs.erb +1 -1
  395. data/lib/karafka/web/ui/views/shared/_content.erb +1 -1
  396. data/lib/karafka/web/ui/views/shared/_controls.erb +10 -3
  397. data/lib/karafka/web/ui/views/shared/_custom_nav.erb +9 -0
  398. data/lib/karafka/web/ui/views/shared/_flashes.erb +3 -5
  399. data/lib/karafka/web/ui/views/shared/_header.erb +25 -2
  400. data/lib/karafka/web/ui/views/shared/_navigation.erb +17 -15
  401. data/lib/karafka/web/ui/views/shared/alerts/_error.erb +8 -0
  402. data/lib/karafka/web/ui/views/shared/alerts/_info.erb +8 -0
  403. data/lib/karafka/web/ui/views/shared/alerts/_primary.erb +8 -0
  404. data/lib/karafka/web/ui/views/shared/alerts/_secondary.erb +8 -0
  405. data/lib/karafka/web/ui/views/shared/alerts/_success.erb +8 -0
  406. data/lib/karafka/web/ui/views/shared/alerts/_warning.erb +8 -0
  407. data/lib/karafka/web/ui/views/shared/exceptions/incompatible_schema.erb +34 -0
  408. data/lib/karafka/web/ui/views/shared/exceptions/not_allowed.erb +4 -0
  409. data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +5 -1
  410. data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +4 -0
  411. data/lib/karafka/web/ui/views/shared/icons/_arrow_left.erb +3 -0
  412. data/lib/karafka/web/ui/views/shared/icons/_arrow_up_tray.erb +3 -0
  413. data/lib/karafka/web/ui/views/shared/icons/_clock.erb +3 -0
  414. data/lib/karafka/web/ui/views/shared/icons/_pencil.erb +3 -0
  415. data/lib/karafka/web/ui/views/shared/icons/_pencil_square.erb +3 -0
  416. data/lib/karafka/web/ui/views/shared/icons/_play_pause.erb +3 -0
  417. data/lib/karafka/web/ui/views/shared/icons/_plus.erb +3 -0
  418. data/lib/karafka/web/ui/views/shared/icons/_trash.erb +3 -0
  419. data/lib/karafka/web/ui/views/status/failures/_live_reporting.erb +1 -1
  420. data/lib/karafka/web/ui/views/status/failures/_partitions.erb +3 -3
  421. data/lib/karafka/web/ui/views/status/failures/_state_calculation.erb +2 -2
  422. data/lib/karafka/web/ui/views/status/info/_components.erb +6 -6
  423. data/lib/karafka/web/ui/views/status/show.erb +15 -0
  424. data/lib/karafka/web/ui/views/status/warnings/_consumers_schemas.erb +31 -0
  425. data/lib/karafka/web/ui/views/ux/_icons.erb +1 -1
  426. data/lib/karafka/web/ui/views/ux/_status_rows.erb +6 -0
  427. data/lib/karafka/web/version.rb +1 -1
  428. data/lib/karafka/web.rb +3 -0
  429. data/package-lock.json +776 -1208
  430. data/package.json +3 -4
  431. data/postcss.config.js +1 -2
  432. data/renovate.json +13 -1
  433. data/tailwind.config.js +0 -4
  434. data.tar.gz.sig +0 -0
  435. metadata +232 -108
  436. metadata.gz.sig +0 -0
  437. data/lib/karafka/web/pro/commanding/commands/quiet.rb +0 -34
  438. data/lib/karafka/web/pro/commanding/commands/stop.rb +0 -34
  439. data/lib/karafka/web/pro/commanding/commands/trace.rb +0 -41
  440. data/lib/karafka/web/pro/ui/controllers/commanding_controller.rb +0 -118
  441. data/lib/karafka/web/pro/ui/controllers/commands_controller.rb +0 -96
  442. data/lib/karafka/web/pro/ui/controllers/consumers_controller.rb +0 -138
  443. data/lib/karafka/web/pro/ui/controllers/explorer_controller.rb +0 -220
  444. data/lib/karafka/web/pro/ui/controllers/messages_controller.rb +0 -107
  445. data/lib/karafka/web/pro/ui/controllers/search_controller.rb +0 -73
  446. data/lib/karafka/web/pro/ui/controllers/topics_controller.rb +0 -130
  447. data/lib/karafka/web/pro/ui/views/commands/_breadcrumbs.erb +0 -21
  448. data/lib/karafka/web/pro/ui/views/commands/_command_details.erb +0 -1
  449. data/lib/karafka/web/pro/ui/views/commands/_empty.erb +0 -3
  450. data/lib/karafka/web/pro/ui/views/commands/show.erb +0 -33
  451. data/lib/karafka/web/pro/ui/views/consumers/_breadcrumbs.erb +0 -55
  452. data/lib/karafka/web/pro/ui/views/consumers/_consumer.erb +0 -47
  453. data/lib/karafka/web/pro/ui/views/consumers/_consumer_controls.erb +0 -95
  454. data/lib/karafka/web/pro/ui/views/consumers/_consumer_performance.erb +0 -59
  455. data/lib/karafka/web/pro/ui/views/consumers/_tabs.erb +0 -33
  456. data/lib/karafka/web/pro/ui/views/consumers/consumer/_commands.erb +0 -72
  457. data/lib/karafka/web/pro/ui/views/consumers/consumer/_consumer_group.erb +0 -8
  458. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_subscriptions.erb +0 -7
  459. data/lib/karafka/web/pro/ui/views/consumers/details.erb +0 -13
  460. data/lib/karafka/web/pro/ui/views/consumers/subscriptions.erb +0 -25
  461. data/lib/karafka/web/pro/ui/views/explorer/_no_topics.erb +0 -1
  462. data/lib/karafka/web/pro/ui/views/explorer/_topic.erb +0 -10
  463. data/lib/karafka/web/pro/ui/views/explorer/index.erb +0 -14
  464. data/lib/karafka/web/pro/ui/views/explorer/messages/_headers.erb +0 -33
  465. data/lib/karafka/web/pro/ui/views/explorer/partition/_cleaned.erb +0 -3
  466. data/lib/karafka/web/pro/ui/views/explorer/partition/_empty.erb +0 -3
  467. data/lib/karafka/web/pro/ui/views/explorer/topic/_empty.erb +0 -3
  468. data/lib/karafka/web/pro/ui/views/search/_breadcrumbs.erb +0 -1
  469. data/lib/karafka/web/pro/ui/views/search/_fix_errors.erb +0 -3
  470. data/lib/karafka/web/pro/ui/views/search/_no_results.erb +0 -3
  471. data/lib/karafka/web/pro/ui/views/search/_timeout.erb +0 -3
  472. data/lib/karafka/web/pro/ui/views/search/index.erb +0 -29
  473. data/lib/karafka/web/pro/ui/views/topics/_breadcrumbs.erb +0 -45
  474. data/lib/karafka/web/pro/ui/views/topics/_partition_offsets.erb +0 -10
  475. data/lib/karafka/web/pro/ui/views/topics/_topic.erb +0 -9
  476. data/lib/karafka/web/pro/ui/views/topics/distribution/_empty_partitions.erb +0 -1
  477. data/lib/karafka/web/pro/ui/views/topics/distribution/_partition.erb +0 -10
  478. data/lib/karafka/web/pro/ui/views/topics/index.erb +0 -14
  479. data/lib/karafka/web/ui/lib/ttl_cache.rb +0 -82
@@ -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
@@ -16,7 +16,7 @@ module Karafka
16
16
  Step = Struct.new(:status, :details) do
17
17
  # @return [Boolean] is the given step successfully configured and working
18
18
  def success?
19
- status == :success || status == :warning
19
+ %i[success warning].include?(status)
20
20
  end
21
21
 
22
22
  # @return [String] local namespace for partial of a given type
@@ -209,6 +209,27 @@ module Karafka
209
209
  )
210
210
  end
211
211
 
212
+ # @return [Status::Step] are there any consumer processes with incompatible schema that
213
+ # does not match exactly the one used by the Web UI.
214
+ #
215
+ # @note It issues a warning, not an error.
216
+ def consumers_schemas
217
+ details = { incompatible: [] }
218
+
219
+ status = if consumers_reports.success?
220
+ incompatible = @processes.reject(&:schema_compatible?)
221
+ details[:incompatible] = incompatible
222
+ incompatible.empty? ? :success : :warning
223
+ else
224
+ :halted
225
+ end
226
+
227
+ Step.new(
228
+ status,
229
+ details
230
+ )
231
+ end
232
+
212
233
  # @return [Status::Step] Is there a significant lag in the reporting of aggregated data
213
234
  # back to the Kafka. If yes, it means that the results in the Web UI will be delayed
214
235
  # against the reality. Often it means, that there is over-saturation on the consumer
@@ -307,22 +328,22 @@ module Karafka
307
328
 
308
329
  # @return [String] consumers states topic name
309
330
  def topics_consumers_states
310
- ::Karafka::Web.config.topics.consumers.states.to_s
331
+ ::Karafka::Web.config.topics.consumers.states.name.to_s
311
332
  end
312
333
 
313
334
  # @return [String] consumers reports topic name
314
335
  def topics_consumers_reports
315
- ::Karafka::Web.config.topics.consumers.reports.to_s
336
+ ::Karafka::Web.config.topics.consumers.reports.name.to_s
316
337
  end
317
338
 
318
339
  # @return [String] consumers metrics topic name
319
340
  def topics_consumers_metrics
320
- ::Karafka::Web.config.topics.consumers.metrics.to_s
341
+ ::Karafka::Web.config.topics.consumers.metrics.name.to_s
321
342
  end
322
343
 
323
344
  # @return [String] errors topic name
324
345
  def topics_errors
325
- ::Karafka::Web.config.topics.errors
346
+ ::Karafka::Web.config.topics.errors.name
326
347
  end
327
348
 
328
349
  # @return [Hash] hash with topics with which we work details (even if don't exist)
@@ -355,9 +376,7 @@ module Karafka
355
376
  # @note If fails, `connection_time` will be 1_000_000
356
377
  def connect
357
378
  started = Time.now.to_f
358
- # For status we always need uncached data, otherwise status could cache outdated
359
- # info
360
- @cluster_info = Models::ClusterInfo.fetch(cached: false)
379
+ @cluster_info = Models::ClusterInfo.fetch
361
380
  @connection_time = (Time.now.to_f - started) * 1_000
362
381
  rescue ::Rdkafka::RdkafkaError
363
382
  @connection_time = 1_000_000
@@ -20,7 +20,7 @@ module Karafka
20
20
  # @return [Topic]
21
21
  # @raise [::Karafka::Web::Errors::Ui::NotFoundError]
22
22
  def find(topic_name)
23
- found = all.find { |topic| topic.topic_name == topic_name }
23
+ found = ClusterInfo.topic(topic_name)
24
24
 
25
25
  return found if found
26
26
 
@@ -56,7 +56,6 @@ module Karafka
56
56
  # aggregated statistics and then the second value is an array with per partition data
57
57
  def distribution(partitions)
58
58
  sum = 0.0
59
- avg = 0.0
60
59
 
61
60
  counts = partitions.map do |partition_id|
62
61
  offsets = Admin.read_watermark_offsets(topic_name, partition_id)
@@ -1,121 +1,31 @@
1
- // Nicer display of time distance from a given event
2
- function updateTimeAgo() {
3
- var selection = document.querySelectorAll('time');
4
-
5
- if (selection.length != 0) {
6
- timeago.render(selection);
7
- timeago.cancel()
8
- }
9
-
10
- var selection = document.getElementsByClassName('time-title')
11
- var title = null
12
-
13
- for (var i = 0; i < selection.length; i++) {
14
- let element = selection[i]
15
-
16
- title = element.getAttribute('title')
17
- element.setAttribute('title', timeago.format(title))
18
- }
19
- }
20
-
21
- // Cheap way to do breadcrumbs
22
- function refreshTitle() {
23
- const breadcrumbs = document.querySelectorAll('.breadcrumbs a');
24
- let breadcrumbTexts = Array.from(breadcrumbs).slice(1).map(crumb => crumb.textContent.trim());
25
-
26
- if (breadcrumbTexts.length > 0) {
27
- document.title = breadcrumbTexts.join(' > ') + ' - Karafka Web UI';
28
- } else {
29
- document.title = 'Karafka Web UI';
30
- }
31
- }
32
-
33
- // When using explorer, we can select the desired partition. This code redirects without having
34
- // to press a button after a select
35
- function redirectToPartition() {
36
- var selector = document.getElementById('current-partition');
37
-
38
- if (selector == null) { return }
39
-
40
- selector.addEventListener('change', function(){
41
- Turbo.visit(this.value);
42
- });
43
- }
44
-
45
- // Binds to links and forms to make sure action is what user wants
46
- function bindActionsConfirmations() {
47
- var elements = document.getElementsByClassName('confirm-action')
48
- var confirmation = 'Are you sure?'
49
-
50
- for (var i = 0; i < elements.length; i++) {
51
- let element = elements[i]
52
- let action = 'click'
53
-
54
- if (element.nodeName === 'FORM') {
55
- action = 'submit'
56
- }
57
-
58
- element.addEventListener(action, function(event) {
59
- if (!window.confirm(confirmation)) {
60
- event.preventDefault();
61
- }
62
- })
63
- }
64
- }
65
-
66
- function bindLockableButtons() {
67
- document.querySelectorAll('.btn-lockable').forEach(function(button) {
68
- button.addEventListener('click', function(event) {
69
- // Find the form element the button is within
70
- const form = button.closest('form');
71
-
72
- // If the button is part of a form, add a submit event listener to the form
73
- if (form) {
74
- form.addEventListener('submit', function() {
75
- button.disabled = true;
76
- button.textContent += '...';
77
- // also lock any modal that is open as the form is submitted
78
- var modals = document.querySelectorAll('.modal');
79
-
80
- // Add 'modal-locked' class to each modal
81
- modals.forEach(function (modal) {
82
- modal.classList.add('modal-locked');
83
- });
84
- }, { once: true });
85
- } else {
86
- // If the button is not part of a form, disable it immediately
87
- button.disabled = true;
88
- button.textContent += '...';
89
- }
90
- });
91
- });
92
- }
93
-
94
1
  function addListeners() {
95
2
  initLivePolling();
96
3
  bindPollingButtonClick();
97
- bindLockableButtons();
98
4
  setLivePollButton();
99
5
  setPollingListener();
100
6
 
101
7
  hljs.highlightAll();
102
- updateTimeAgo();
103
- redirectToPartition();
8
+ new TimeAgoManager();
9
+ new PartitionRedirectManager();
104
10
 
105
11
  const tabsManager = new TabsManager();
106
12
  tabsManager.manageTabs();
107
13
 
108
14
  manageCharts();
109
- bindActionsConfirmations();
110
15
  loadOffsetLookupDatePicker();
111
16
 
112
17
  new BtnToggleManager();
113
18
  new BtnToggleManager('.btn-toggle-nav-collapsed', 'collapsed');
114
19
  new ThemeManager();
115
20
 
116
- refreshTitle()
21
+ new ButtonLockManager();
22
+ new ActionConfirmationManager();
23
+ new PageTitleTracker();
117
24
  new SearchMetadataVisibilityManager();
118
25
  new SearchModalManager();
26
+ new TimestampSelector();
27
+ new AlertsManager();
28
+ new MessageRepublishManager();
119
29
  }
120
30
 
121
31
  document.addEventListener('turbo:load', addListeners);