karafka-web 0.8.2 → 0.9.0.rc2

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 (271) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +5 -16
  4. data/CHANGELOG.md +40 -0
  5. data/Gemfile.lock +21 -23
  6. data/LICENSE +3 -3
  7. data/bin/rspecs +1 -1
  8. data/config/locales/pro_errors.yml +11 -0
  9. data/config/locales/slogans.yml +62 -0
  10. data/docker-compose.yml +1 -1
  11. data/karafka-web.gemspec +4 -2
  12. data/lib/karafka/web/app.rb +1 -1
  13. data/lib/karafka/web/config.rb +23 -3
  14. data/lib/karafka/web/contracts/config.rb +7 -1
  15. data/lib/karafka/web/management/actions/create_topics.rb +21 -0
  16. data/lib/karafka/web/management/actions/delete_topics.rb +1 -0
  17. data/lib/karafka/web/management/actions/enable.rb +12 -6
  18. data/lib/karafka/web/management/migrations/0_base.rb +1 -1
  19. data/lib/karafka/web/pro/commanding/commands/base.rb +33 -0
  20. data/lib/karafka/web/pro/commanding/commands/probe.rb +41 -0
  21. data/lib/karafka/web/pro/commanding/commands/quiet.rb +31 -0
  22. data/lib/karafka/web/pro/commanding/commands/stop.rb +31 -0
  23. data/lib/karafka/web/pro/commanding/config.rb +51 -0
  24. data/lib/karafka/web/pro/commanding/contracts/config.rb +56 -0
  25. data/lib/karafka/web/pro/commanding/dispatcher.rb +93 -0
  26. data/lib/karafka/web/pro/commanding/listener.rb +97 -0
  27. data/lib/karafka/web/pro/commanding/manager.rb +98 -0
  28. data/lib/karafka/web/pro/commanding/matcher.rb +50 -0
  29. data/lib/karafka/web/pro/commanding.rb +40 -0
  30. data/lib/karafka/web/pro/loader.rb +40 -0
  31. data/lib/karafka/web/{ui/pro → pro/ui}/app.rb +103 -22
  32. data/lib/karafka/web/{ui/pro/controllers/cluster.rb → pro/ui/controllers/base_controller.rb} +4 -5
  33. data/lib/karafka/web/pro/ui/controllers/cluster_controller.rb +54 -0
  34. data/lib/karafka/web/pro/ui/controllers/commanding_controller.rb +118 -0
  35. data/lib/karafka/web/pro/ui/controllers/commands_controller.rb +96 -0
  36. data/lib/karafka/web/{ui/pro/controllers/consumers.rb → pro/ui/controllers/consumers_controller.rb} +31 -4
  37. data/lib/karafka/web/{ui/pro/controllers/dashboard.rb → pro/ui/controllers/dashboard_controller.rb} +5 -3
  38. data/lib/karafka/web/pro/ui/controllers/dlq_controller.rb +60 -0
  39. data/lib/karafka/web/{ui/pro/controllers/errors.rb → pro/ui/controllers/errors_controller.rb} +5 -7
  40. data/lib/karafka/web/{ui/pro/controllers/explorer.rb → pro/ui/controllers/explorer_controller.rb} +24 -19
  41. data/lib/karafka/web/{ui/pro/controllers/health.rb → pro/ui/controllers/health_controller.rb} +16 -3
  42. data/lib/karafka/web/{ui/pro/controllers/jobs.rb → pro/ui/controllers/jobs_controller.rb} +4 -4
  43. data/lib/karafka/web/{ui/pro/controllers/messages.rb → pro/ui/controllers/messages_controller.rb} +8 -6
  44. data/lib/karafka/web/{ui/pro/controllers/routing.rb → pro/ui/controllers/routing_controller.rb} +6 -22
  45. data/lib/karafka/web/{ui/pro/controllers/status.rb → pro/ui/controllers/status_controller.rb} +3 -3
  46. data/lib/karafka/web/pro/ui/controllers/topics_controller.rb +99 -0
  47. data/lib/karafka/web/pro/ui/lib/patterns_detector.rb +50 -0
  48. data/lib/karafka/web/pro/ui/views/cluster/_breadcrumbs.erb +29 -0
  49. data/lib/karafka/web/pro/ui/views/cluster/_broker.erb +13 -0
  50. data/lib/karafka/web/pro/ui/views/cluster/_config.erb +13 -0
  51. data/lib/karafka/web/pro/ui/views/cluster/_tabs.erb +27 -0
  52. data/lib/karafka/web/pro/ui/views/cluster/brokers.erb +27 -0
  53. data/lib/karafka/web/pro/ui/views/cluster/index.erb +27 -0
  54. data/lib/karafka/web/pro/ui/views/cluster/show.erb +27 -0
  55. data/lib/karafka/web/pro/ui/views/commands/_backtrace.erb +20 -0
  56. data/lib/karafka/web/pro/ui/views/commands/_breadcrumbs.erb +21 -0
  57. data/lib/karafka/web/pro/ui/views/commands/_command.erb +60 -0
  58. data/lib/karafka/web/pro/ui/views/commands/_command_details.erb +11 -0
  59. data/lib/karafka/web/pro/ui/views/commands/_details.erb +26 -0
  60. data/lib/karafka/web/pro/ui/views/commands/_empty.erb +3 -0
  61. data/lib/karafka/web/pro/ui/views/commands/_incompatible_schema.erb +14 -0
  62. data/lib/karafka/web/pro/ui/views/commands/_metadata.erb +50 -0
  63. data/lib/karafka/web/pro/ui/views/commands/_table.erb +23 -0
  64. data/lib/karafka/web/pro/ui/views/commands/index.erb +17 -0
  65. data/lib/karafka/web/pro/ui/views/commands/show.erb +38 -0
  66. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_breadcrumbs.erb +20 -4
  67. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_consumer.erb +2 -21
  68. data/lib/karafka/web/pro/ui/views/consumers/_consumer_controls.erb +78 -0
  69. data/lib/karafka/web/pro/ui/views/consumers/_consumer_performance.erb +59 -0
  70. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_counters.erb +7 -5
  71. data/lib/karafka/web/pro/ui/views/consumers/_tabs.erb +35 -0
  72. data/lib/karafka/web/pro/ui/views/consumers/consumer/_commands.erb +32 -0
  73. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_jobs.erb +7 -0
  74. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_subscriptions.erb +7 -0
  75. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_subscription_group.erb +13 -8
  76. data/lib/karafka/web/pro/ui/views/consumers/consumer/_title.erb +5 -0
  77. data/lib/karafka/web/pro/ui/views/consumers/controls.erb +67 -0
  78. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/details.erb +6 -1
  79. data/lib/karafka/web/pro/ui/views/consumers/index.erb +39 -0
  80. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/pending_jobs.erb +15 -7
  81. data/lib/karafka/web/pro/ui/views/consumers/performance.erb +52 -0
  82. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/running_jobs.erb +15 -7
  83. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/subscriptions.erb +6 -1
  84. data/lib/karafka/web/{ui/pro → pro/ui}/views/dashboard/index.erb +10 -10
  85. data/lib/karafka/web/pro/ui/views/dlq/_no_topics.erb +7 -0
  86. data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/index.erb +1 -1
  87. data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_error.erb +2 -6
  88. data/lib/karafka/web/pro/ui/views/errors/_table.erb +23 -0
  89. data/lib/karafka/web/pro/ui/views/explorer/_failed_deserialization.erb +4 -0
  90. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_message.erb +7 -1
  91. data/lib/karafka/web/pro/ui/views/explorer/_no_topics.erb +5 -0
  92. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/index.erb +1 -6
  93. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_metadata.erb +33 -9
  94. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_payload.erb +4 -4
  95. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_payload_actions.erb +1 -1
  96. data/lib/karafka/web/pro/ui/views/explorer/messages/_headers.erb +33 -0
  97. data/lib/karafka/web/pro/ui/views/explorer/messages/_key.erb +20 -0
  98. data/lib/karafka/web/pro/ui/views/explorer/partition/_cleaned.erb +5 -0
  99. data/lib/karafka/web/pro/ui/views/explorer/partition/_empty.erb +5 -0
  100. data/lib/karafka/web/pro/ui/views/explorer/partition/_messages.erb +21 -0
  101. data/lib/karafka/web/pro/ui/views/explorer/topic/_empty.erb +5 -0
  102. data/lib/karafka/web/pro/ui/views/explorer/topic/_limited.erb +10 -0
  103. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_breadcrumbs.erb +8 -0
  104. data/lib/karafka/web/pro/ui/views/health/_no_data.erb +7 -0
  105. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition.erb +1 -1
  106. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_lags.erb +6 -3
  107. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_tabs.erb +11 -1
  108. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/changes.erb +13 -8
  109. data/lib/karafka/web/pro/ui/views/health/cluster_lags.erb +54 -0
  110. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/lags.erb +14 -8
  111. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/offsets.erb +15 -12
  112. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/overview.erb +21 -7
  113. data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/_job.erb +1 -1
  114. data/lib/karafka/web/pro/ui/views/jobs/_no_jobs.erb +7 -0
  115. data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/pending.erb +12 -8
  116. data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/running.erb +12 -8
  117. data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_consumer_group.erb +2 -2
  118. data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/index.erb +1 -1
  119. data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/show.erb +1 -1
  120. data/lib/karafka/web/{ui/pro → pro/ui}/views/shared/_navigation.erb +14 -0
  121. data/lib/karafka/web/pro/ui/views/topics/_breadcrumbs.erb +37 -0
  122. data/lib/karafka/web/pro/ui/views/topics/_partition.erb +16 -0
  123. data/lib/karafka/web/pro/ui/views/topics/_tabs.erb +37 -0
  124. data/lib/karafka/web/pro/ui/views/topics/_topic.erb +12 -0
  125. data/lib/karafka/web/pro/ui/views/topics/config.erb +29 -0
  126. data/lib/karafka/web/pro/ui/views/topics/distribution/_badges.erb +7 -0
  127. data/lib/karafka/web/pro/ui/views/topics/distribution/_chart.erb +2 -0
  128. data/lib/karafka/web/pro/ui/views/topics/distribution/_empty_partitions.erb +1 -0
  129. data/lib/karafka/web/pro/ui/views/topics/distribution/_limited.erb +10 -0
  130. data/lib/karafka/web/pro/ui/views/topics/distribution/_partition.erb +10 -0
  131. data/lib/karafka/web/pro/ui/views/topics/distribution.erb +47 -0
  132. data/lib/karafka/web/pro/ui/views/topics/index.erb +16 -0
  133. data/lib/karafka/web/pro/ui/views/topics/replication.erb +28 -0
  134. data/lib/karafka/web/processing/consumers/aggregators/base.rb +1 -1
  135. data/lib/karafka/web/processing/consumers/aggregators/metrics.rb +1 -1
  136. data/lib/karafka/web/processing/consumers/aggregators/state.rb +4 -4
  137. data/lib/karafka/web/tracking/consumers/contracts/report.rb +1 -1
  138. data/lib/karafka/web/tracking/consumers/listeners/booting.rb +3 -1
  139. data/lib/karafka/web/tracking/consumers/listeners/errors.rb +2 -2
  140. data/lib/karafka/web/tracking/consumers/reporter.rb +3 -3
  141. data/lib/karafka/web/tracking/consumers/sampler.rb +2 -2
  142. data/lib/karafka/web/tracking/contracts/error.rb +1 -1
  143. data/lib/karafka/web/tracking/producers/listeners/booting.rb +3 -1
  144. data/lib/karafka/web/tracking/producers/listeners/errors.rb +2 -2
  145. data/lib/karafka/web/tracking/producers/reporter.rb +1 -1
  146. data/lib/karafka/web/tracking/producers/sampler.rb +1 -1
  147. data/lib/karafka/web/tracking/sampler.rb +3 -3
  148. data/lib/karafka/web/ui/app.rb +13 -9
  149. data/lib/karafka/web/ui/base.rb +1 -0
  150. data/lib/karafka/web/ui/controllers/{base.rb → base_controller.rb} +15 -2
  151. data/lib/karafka/web/ui/controllers/{become_pro.rb → become_pro_controller.rb} +1 -1
  152. data/lib/karafka/web/ui/controllers/{cluster.rb → cluster_controller.rb} +4 -4
  153. data/lib/karafka/web/ui/controllers/{consumers.rb → consumers_controller.rb} +3 -3
  154. data/lib/karafka/web/ui/controllers/{dashboard.rb → dashboard_controller.rb} +1 -1
  155. data/lib/karafka/web/ui/controllers/{errors.rb → errors_controller.rb} +2 -2
  156. data/lib/karafka/web/ui/controllers/{jobs.rb → jobs_controller.rb} +5 -5
  157. data/lib/karafka/web/ui/controllers/{routing.rb → routing_controller.rb} +2 -2
  158. data/lib/karafka/web/ui/controllers/{status.rb → status_controller.rb} +1 -1
  159. data/lib/karafka/web/ui/helpers/alerts_helper.rb +23 -0
  160. data/lib/karafka/web/ui/helpers/application_helper.rb +53 -1
  161. data/lib/karafka/web/ui/lib/paginations/offset_based.rb +3 -4
  162. data/lib/karafka/web/ui/lib/safe_runner.rb +59 -0
  163. data/lib/karafka/web/ui/models/broker.rb +66 -0
  164. data/lib/karafka/web/ui/models/health.rb +28 -2
  165. data/lib/karafka/web/ui/models/message.rb +9 -3
  166. data/lib/karafka/web/ui/models/process.rb +10 -5
  167. data/lib/karafka/web/ui/models/processes.rb +2 -2
  168. data/lib/karafka/web/ui/models/status.rb +1 -1
  169. data/lib/karafka/web/ui/models/topic.rb +78 -0
  170. data/lib/karafka/web/ui/public/javascripts/application.js +18 -1
  171. data/lib/karafka/web/ui/public/javascripts/charts/data_formatting_utility.js +71 -0
  172. data/lib/karafka/web/ui/public/javascripts/charts/dataset_state_manager.js +49 -0
  173. data/lib/karafka/web/ui/public/javascripts/charts/types/bar.js +123 -0
  174. data/lib/karafka/web/ui/public/javascripts/charts/types/line.js +143 -0
  175. data/lib/karafka/web/ui/public/javascripts/charts.js +10 -325
  176. data/lib/karafka/web/ui/public/javascripts/live_poll.js +5 -5
  177. data/lib/karafka/web/ui/public/javascripts/tabs_manager.js +57 -0
  178. data/lib/karafka/web/ui/public/stylesheets/application.css +7 -6
  179. data/lib/karafka/web/ui/views/cluster/_breadcrumbs.erb +3 -3
  180. data/lib/karafka/web/ui/views/cluster/_no_partitions.erb +1 -3
  181. data/lib/karafka/web/ui/views/cluster/_tabs.erb +3 -3
  182. data/lib/karafka/web/ui/views/cluster/brokers.erb +19 -19
  183. data/lib/karafka/web/ui/views/cluster/replication.erb +37 -0
  184. data/lib/karafka/web/ui/views/consumers/_assignments_badges.erb +24 -0
  185. data/lib/karafka/web/ui/views/consumers/_consumer.erb +2 -15
  186. data/lib/karafka/web/ui/views/consumers/_counters.erb +1 -1
  187. data/lib/karafka/web/ui/views/consumers/_summary.erb +5 -5
  188. data/lib/karafka/web/ui/views/consumers/index.erb +22 -20
  189. data/lib/karafka/web/ui/views/dashboard/index.erb +9 -9
  190. data/lib/karafka/web/ui/views/errors/_cleaned.erb +1 -3
  191. data/lib/karafka/web/ui/views/errors/_error.erb +2 -6
  192. data/lib/karafka/web/ui/views/errors/_no_errors.erb +1 -3
  193. data/lib/karafka/web/ui/views/errors/index.erb +22 -20
  194. data/lib/karafka/web/ui/views/jobs/_job.erb +4 -1
  195. data/lib/karafka/web/ui/views/jobs/_no_jobs.erb +1 -3
  196. data/lib/karafka/web/ui/views/jobs/pending.erb +4 -3
  197. data/lib/karafka/web/ui/views/jobs/running.erb +4 -3
  198. data/lib/karafka/web/ui/views/routing/_consumer_group.erb +2 -2
  199. data/lib/karafka/web/ui/views/routing/index.erb +1 -1
  200. data/lib/karafka/web/ui/views/routing/show.erb +1 -1
  201. data/lib/karafka/web/ui/views/shared/_become_pro.erb +3 -3
  202. data/lib/karafka/web/ui/views/shared/_header.erb +16 -10
  203. data/lib/karafka/web/ui/views/shared/_navigation.erb +17 -3
  204. data/lib/karafka/web/ui/views/shared/_not_a_message.erb +5 -0
  205. data/lib/karafka/web/ui/views/shared/alerts/_info.erb +3 -0
  206. data/lib/karafka/web/ui/views/shared/charts/_bar.erb +7 -0
  207. data/lib/karafka/web/ui/views/shared/{_chart.erb → charts/_line.erb} +1 -1
  208. data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +1 -1
  209. data/lib/karafka/web/ui/views/status/show.erb +1 -1
  210. data/lib/karafka/web/version.rb +1 -1
  211. data/lib/karafka/web.rb +17 -1
  212. data.tar.gz.sig +0 -0
  213. metadata +189 -120
  214. metadata.gz.sig +0 -0
  215. data/lib/karafka/web/ui/pro/controllers/dlq.rb +0 -43
  216. data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_jobs.erb +0 -9
  217. data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_subscriptions.erb +0 -9
  218. data/lib/karafka/web/ui/pro/views/consumers/index.erb +0 -36
  219. data/lib/karafka/web/ui/pro/views/dlq/_no_topics.erb +0 -9
  220. data/lib/karafka/web/ui/pro/views/errors/_table.erb +0 -21
  221. data/lib/karafka/web/ui/pro/views/explorer/_failed_deserialization.erb +0 -4
  222. data/lib/karafka/web/ui/pro/views/explorer/_no_topics.erb +0 -7
  223. data/lib/karafka/web/ui/pro/views/explorer/messages/_headers.erb +0 -15
  224. data/lib/karafka/web/ui/pro/views/explorer/messages/_key.erb +0 -12
  225. data/lib/karafka/web/ui/pro/views/explorer/partition/_cleaned.erb +0 -3
  226. data/lib/karafka/web/ui/pro/views/explorer/partition/_empty.erb +0 -3
  227. data/lib/karafka/web/ui/pro/views/explorer/partition/_messages.erb +0 -19
  228. data/lib/karafka/web/ui/pro/views/explorer/topic/_empty.erb +0 -3
  229. data/lib/karafka/web/ui/pro/views/explorer/topic/_limited.erb +0 -4
  230. data/lib/karafka/web/ui/pro/views/health/_no_data.erb +0 -9
  231. data/lib/karafka/web/ui/pro/views/jobs/_no_jobs.erb +0 -9
  232. data/lib/karafka/web/ui/public/javascripts/tabs.js +0 -59
  233. data/lib/karafka/web/ui/views/cluster/topics.erb +0 -35
  234. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_consumer_group.erb +0 -0
  235. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_job.erb +0 -0
  236. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_metrics.erb +0 -0
  237. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_partition.erb +0 -0
  238. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_stopped.erb +0 -0
  239. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_tabs.erb +0 -0
  240. /data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/_breadcrumbs.erb +0 -0
  241. /data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/_topic.erb +0 -0
  242. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_breadcrumbs.erb +0 -0
  243. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_partition_option.erb +0 -0
  244. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_title_with_select.erb +0 -0
  245. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/index.erb +0 -0
  246. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/partition.erb +0 -0
  247. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/show.erb +0 -0
  248. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_breadcrumbs.erb +0 -0
  249. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_filtered.erb +0 -0
  250. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_partition_option.erb +0 -0
  251. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_topic.erb +0 -0
  252. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_message_actions.erb +0 -0
  253. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/messages/_detail.erb +0 -0
  254. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/partition/_details.erb +0 -0
  255. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/partition.erb +0 -0
  256. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/show.erb +0 -0
  257. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/topic/_details.erb +0 -0
  258. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/topic.erb +0 -0
  259. /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_consumer_group_header.erb +0 -0
  260. /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_offset.erb +0 -0
  261. /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_times.erb +0 -0
  262. /data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_detail.erb +0 -0
  263. /data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_topic.erb +0 -0
  264. /data/lib/karafka/web/ui/public/javascripts/{bootstrap.min.js → libs/bootstrap.min.js} +0 -0
  265. /data/lib/karafka/web/ui/public/javascripts/{chart.min.js → libs/chart.min.js} +0 -0
  266. /data/lib/karafka/web/ui/public/javascripts/{datepicker.js → libs/datepicker.js} +0 -0
  267. /data/lib/karafka/web/ui/public/javascripts/{highlight.min.js → libs/highlight.min.js} +0 -0
  268. /data/lib/karafka/web/ui/public/javascripts/{timeago.min.js → libs/timeago.min.js} +0 -0
  269. /data/lib/karafka/web/ui/public/stylesheets/{bootstrap.min.css → libs/bootstrap.min.css} +0 -0
  270. /data/lib/karafka/web/ui/public/stylesheets/{datepicker.min.css → libs/datepicker.min.css} +0 -0
  271. /data/lib/karafka/web/ui/public/stylesheets/{highlight.min.css → libs/highlight.min.css} +0 -0
@@ -0,0 +1,123 @@
1
+ class BarChartManager {
2
+ constructor() {
3
+ this.datasetStateManager = new DatasetStateManager();
4
+ }
5
+
6
+ refreshAndRenderBarCharts(doc, isRefresh = false) {
7
+ const charts = (isRefresh ? doc : document).querySelectorAll('.chartjs-bar');
8
+
9
+ charts.forEach((chartElement) => {
10
+ const chartId = chartElement.id;
11
+ const chartn = (isRefresh ? doc : document).getElementById(chartId);
12
+ const chartData = JSON.parse(chartn.dataset.datasets);
13
+ let labels = [],
14
+ data = [],
15
+ allDataPoints = [];
16
+ const labelTypeY = chartn.dataset.label_type_y;
17
+ const labelTypeX = chartn.dataset.label_type_x;
18
+ const disabledSets = this.datasetStateManager.getCurrentChart(chartId);
19
+ Object.entries(chartData).forEach(([key, value], index) => {
20
+ value.forEach(([label, dataPoint]) => {
21
+ allDataPoints.push(dataPoint);
22
+ if (index === 0) {
23
+ labels.push(DataFormattingUtils.formatLabelX(label, labelTypeX));
24
+ }
25
+ });
26
+ data.push({
27
+ data: value.map(([, dataPoint]) => dataPoint),
28
+ label: key,
29
+ hidden: disabledSets.includes(index),
30
+ borderWidth: 2.5
31
+ });
32
+ });
33
+ const minYValue = Math.min(...allDataPoints);
34
+ const maxYValue = Math.max(...allDataPoints);
35
+ const adjustedMinYValue = Math.round(minYValue - (0.1 * minYValue));
36
+ const adjustedMaxYValue = Math.round(maxYValue + (0.005 * maxYValue));
37
+ const average = Math.round(allDataPoints.reduce((sum, current) => sum + current, 0) / allDataPoints.length);
38
+
39
+ data.push({
40
+ type: 'line',
41
+ label: 'Average',
42
+ data: new Array(labels.length).fill(average),
43
+ borderWidth: 2,
44
+ fill: false,
45
+ pointRadius: 0,
46
+ hoverBorderWidth: 3, // Makes the line slightly thicker on hover, making it easier to hover over
47
+ pointHitRadius: 20 // Increases the radius around the invisible points that will detect a hover
48
+ });
49
+
50
+ if (isRefresh) {
51
+ const chart = Chart.getChart(chartId);
52
+ chart.data.datasets = data;
53
+ chart.data.labels = labels;
54
+ chart.options.scales.y.min = adjustedMinYValue;
55
+ chart.options.scales.y.max = adjustedMaxYValue;
56
+ chart.update('none');
57
+ } else {
58
+ this.renderBarChart(chartElement, labels, data, adjustedMinYValue, adjustedMaxYValue, labelTypeY);
59
+ }
60
+ });
61
+ }
62
+
63
+ renderBarChart(handler, labels, data, minYValue, maxYValue, labelTypeY) {
64
+ new Chart(handler, {
65
+ type: 'bar',
66
+ data: {
67
+ labels: labels,
68
+ datasets: data
69
+ },
70
+ options: {
71
+ responsive: true,
72
+ maintainAspectRatio: false,
73
+ aspectRatio: 5,
74
+ scales: {
75
+ x: {
76
+ display: true,
77
+ },
78
+ y: {
79
+ beginAtZero: false,
80
+ min: minYValue,
81
+ max: maxYValue,
82
+ ticks: {
83
+ maxTicksLimit: 8,
84
+ callback: function(label, index, labels) {
85
+ return DataFormattingUtils.formatLabelY(labelTypeY, label, index, labels);
86
+ }
87
+ }
88
+ }
89
+ },
90
+ animation: false,
91
+ animations: {
92
+ colors: false,
93
+ x: false
94
+ },
95
+ transitions: {
96
+ active: {
97
+ animation: {
98
+ duration: false
99
+ }
100
+ }
101
+ },
102
+ plugins: {
103
+ legend: {
104
+ position: 'hidden'
105
+ },
106
+ tooltip: {
107
+ callbacks: {
108
+ label: function(context) {
109
+ let label = context.dataset.label || '';
110
+ if (label === 'Average') {
111
+ label += ': ' + context.formattedValue;
112
+ } else {
113
+ return DataFormattingUtils.formatTooltip(labelTypeY, context);
114
+ }
115
+ return label;
116
+ }
117
+ }
118
+ }
119
+ },
120
+ }
121
+ });
122
+ }
123
+ }
@@ -0,0 +1,143 @@
1
+ class LineChartsManager {
2
+ constructor() {
3
+ this.datasetStateManager = new DatasetStateManager();
4
+ }
5
+
6
+ refreshAndRender(doc, isRefresh = false) {
7
+ const charts = (isRefresh ? doc : document).querySelectorAll('.chartjs-line');
8
+
9
+ charts.forEach((chartElement) => {
10
+ const chartId = chartElement.id;
11
+ const chartn = (isRefresh ? doc : document).getElementById(chartId);
12
+ const chartData = JSON.parse(chartn.dataset.datasets);
13
+ let labels = [],
14
+ data = [],
15
+ yPrecision = 0;
16
+ const labelTypeY = chartn.dataset.label_type_y;
17
+ const labelTypeX = chartn.dataset.label_type_x;
18
+ const disabledSets = this.datasetStateManager.getCurrentChart(chartId);
19
+
20
+ Object.entries(chartData).forEach(([key, value], index) => {
21
+ value.forEach(([label, dataPoint]) => {
22
+ if (index === 0) {
23
+ labels.push(DataFormattingUtils.formatLabelX(label, labelTypeX));
24
+ }
25
+ if (DataFormattingUtils.isFractionalPrecision(dataPoint)) {
26
+ yPrecision = 2;
27
+ }
28
+ });
29
+
30
+ data.push({
31
+ data: value,
32
+ label: key,
33
+ hidden: disabledSets.includes(index),
34
+ borderWidth: 2.5
35
+ });
36
+ });
37
+
38
+ if (isRefresh) {
39
+ const chart = Chart.getChart(chartId);
40
+ chart.data.datasets = data;
41
+ chart.data.labels = labels;
42
+ chart.update('none');
43
+ } else {
44
+ this.render(chartElement, labels, data, yPrecision, labelTypeY);
45
+ }
46
+ });
47
+ }
48
+
49
+ render(handler, labels, data, yPrecision, labelTypeY) {
50
+ new Chart(handler, {
51
+ type: 'line',
52
+ data: {
53
+ labels: labels,
54
+ datasets: data
55
+ },
56
+ options: {
57
+ responsive: true,
58
+ maintainAspectRatio: false,
59
+ aspectRatio: 5,
60
+ title: {
61
+ display: false
62
+ },
63
+ interaction: {
64
+ mode: 'nearest',
65
+ axis: 'x',
66
+ intersect: false
67
+ },
68
+ hover: {
69
+ intersect: false
70
+ },
71
+ animation: false,
72
+ animations: {
73
+ colors: false,
74
+ x: false
75
+ },
76
+ transitions: {
77
+ active: {
78
+ animation: {
79
+ duration: false
80
+ }
81
+ }
82
+ },
83
+ plugins: {
84
+ legend: {
85
+ position: 'bottom',
86
+ labels: {
87
+ padding: 20
88
+ },
89
+ onClick: (e, legendItem, legend) => {
90
+ const index = legendItem.datasetIndex;
91
+ const ci = legend.chart;
92
+ if (ci.isDatasetVisible(index)) {
93
+ ci.hide(index);
94
+ legendItem.hidden = true;
95
+ } else {
96
+ ci.show(index);
97
+ legendItem.hidden = false;
98
+ }
99
+
100
+ this.datasetStateManager.saveCurrent();
101
+ }
102
+ },
103
+ tooltip: {
104
+ callbacks: {
105
+ label: function(tooltipItem) {
106
+ return DataFormattingUtils.formatTooltip(labelTypeY, tooltipItem);
107
+ }
108
+ }
109
+ }
110
+ },
111
+ scales: {
112
+ x: {
113
+ display: false,
114
+ },
115
+ y: {
116
+ ticks: {
117
+ precision: yPrecision,
118
+ count: 5,
119
+ callback: function(label, index, labels) {
120
+ return DataFormattingUtils.formatLabelY(labelTypeY, label, index, labels);
121
+ }
122
+ }
123
+ }
124
+ },
125
+ elements: {
126
+ point: {
127
+ radius: 0,
128
+ style: false
129
+ },
130
+ line: {
131
+ style: 'star',
132
+ radius: 0,
133
+ spanGaps: false
134
+ }
135
+ },
136
+ hover: {
137
+ mode: 'index',
138
+ intersect: false,
139
+ }
140
+ }
141
+ });
142
+ }
143
+ }
@@ -1,330 +1,15 @@
1
- function readAllDisabledDatasets() {
2
- let raw_disabled_datasets = localStorage.karafkaDisabledDatasets
1
+ function refreshCharts(newDoc) {
2
+ const lineChartsManager = new LineChartsManager();
3
+ lineChartsManager.refreshAndRender(newDoc, true);
3
4
 
4
- if (raw_disabled_datasets == undefined) {
5
- return {}
6
- } else {
7
- return JSON.parse(raw_disabled_datasets)
8
- }
9
- }
10
-
11
- function saveAllDisabledDatasets(data) {
12
- localStorage.karafkaDisabledDatasets = JSON.stringify(data)
13
- }
14
-
15
- function saveCurrentDisabledDatasets() {
16
- let charts = document.querySelectorAll('.chartjs')
17
- let url = window.location.href.split('?')[0]
18
- let current = {}
19
- let tabs = readAllDisabledDatasets()
20
-
21
- for (var i = 0; i < charts.length; i++) {
22
- let chart_id = charts[i].id
23
- let chart = Chart.getChart(chart_id)
24
- let items = chart.legend.legendItems
25
- current[chart_id] = []
26
-
27
- for (var y = 0; y < items.length; y++) {
28
- if (items[y].hidden) {
29
- current[chart_id].push(y)
30
- }
31
- }
32
- }
33
-
34
- tabs[url] = current
35
- saveAllDisabledDatasets(tabs)
36
- }
37
-
38
- function getCurrentChartDisabledDatasets(chart_id) {
39
- let all_disabled = readAllDisabledDatasets()
40
- let url = window.location.href.split('?')[0]
41
- let current = all_disabled[url]
42
-
43
- if (current == undefined) { return [] }
44
-
45
- var disabled_indexes = current[chart_id]
46
-
47
- if (disabled_indexes != undefined) {
48
- return disabled_indexes
49
- } else {
50
- return []
51
- }
52
- }
53
-
54
- function niceBytes(x, precision){
55
- let units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
56
-
57
- let l = 0, n = parseInt(x, 10) || 0;
58
-
59
- while(n >= 1024 && ++l){
60
- n = n/1024;
61
- }
62
-
63
- return(n.toFixed(n < 10 && l > 0 ? 1 : precision) + ' ' + units[l]);
64
- }
65
-
66
- function formatLabelX(value, type) {
67
- switch (type) {
68
- case 'date':
69
- let date = new Date(value * 1000)
70
- let date_str =
71
- ("00" + (date.getMonth() + 1)).slice(-2) + "/" +
72
- ("00" + date.getDate()).slice(-2) + "/" +
73
- date.getFullYear() + " " +
74
- ("00" + date.getHours()).slice(-2) + ":" +
75
- ("00" + date.getMinutes()).slice(-2) + ":" +
76
- ("00" + date.getSeconds()).slice(-2);
77
-
78
- return date_str
79
- default:
80
- return value
81
- }
82
- }
83
-
84
- function formatTooltip(type, tooltipItem){
85
- let value = tooltipItem.parsed.y;
86
- let label = tooltipItem.dataset.label;
87
-
88
- switch(type) {
89
- case 'percentage':
90
-
91
- console.log(tooltipItem)
92
-
93
- if (Math.floor(value) === value) {
94
- return label + ': ' + value + ' %';
95
- } else {
96
- return label + ': ' + (Math.round(value * 100) / 100) + ' %';
97
- }
98
- case 'memory':
99
- return label + ': ' + niceBytes(value * 1024, 2);
100
- default:
101
- return tooltipItem.yLabel
102
- }
103
- }
104
-
105
- function formatLabelY(type, label, index, labels) {
106
- switch(type) {
107
- case 'percentage':
108
- if (Math.floor(label) === label) {
109
- return label + '%'
110
- } else {
111
- return (Math.round(label * 100) / 100) + '%'
112
- }
113
- case 'memory':
114
- return niceBytes(label * 1024, 1)
115
- default:
116
- if (Math.floor(label) === label) {
117
- return label
118
- } else {
119
- return Math.round(label * 100) / 100
120
- }
121
- }
122
- }
123
-
124
- function isFractialPrecision(value) {
125
- return value === +value && value !== (value|0)
126
- }
127
-
128
- function refreshCharts(new_doc) {
129
- var charts = new_doc.querySelectorAll('.chartjs')
130
-
131
- for (var i = 0; i < charts.length; i++) {
132
- var chart = Chart.getChart(charts[i].id)
133
- var chart_id = charts[i].id
134
- var chartn = new_doc.getElementById(chart_id)
135
- var chart_data = chartn.dataset.datasets
136
-
137
- let datasets = JSON.parse(chart_data)
138
- let data = []
139
- let labels = []
140
- let label_type_y = chartn.dataset.label_type_y
141
- let label_type_x = chartn.dataset.label_type_x
142
- // by default assume integer values
143
- let y_precision = 0
144
- let disabled_sets = getCurrentChartDisabledDatasets(chart_id)
145
-
146
- Object.keys(datasets).forEach(function(key, i) {
147
- var current_set = datasets[key]
148
-
149
- let y = 0;
150
-
151
- while (y < current_set.length) {
152
- let current_value = current_set[y][1]
153
- let current_label = current_set[y][0]
154
-
155
- if (i == 0) {
156
- labels.push(formatLabelX(current_label, label_type_x))
157
- }
158
-
159
- if (isFractialPrecision(current_value)) {
160
- y_precision = 2
161
- }
162
-
163
- y++
164
- }
165
-
166
- data.push(
167
- {
168
- data: datasets[key],
169
- label: key,
170
- hidden: disabled_sets.includes(i),
171
- borderWidth: 2.5
172
- }
173
- )
174
- })
175
-
176
- chart.data.datasets = data
177
- chart.data.labels = labels
178
-
179
- chart.update()
180
- }
181
- }
182
-
183
- function renderChart(handler, labels, data, y_precision, label_type_y) {
184
- new Chart(handler, {
185
- type : 'line',
186
- data : {
187
- labels : labels,
188
- datasets : data
189
- },
190
- options : {
191
- responsive: true,
192
- aspectRatio: 5,
193
- title : {
194
- display : false
195
- },
196
- interaction: {
197
- mode: 'nearest',
198
- axis: 'x',
199
- intersect: false
200
- },
201
- hover: {
202
- intersect: false
203
- },
204
- animation: false,
205
- animations: {
206
- colors: false,
207
- x: false
208
- },
209
- transitions: {
210
- active: {
211
- animation: {
212
- duration: false
213
- }
214
- }
215
- },
216
- plugins: {
217
- legend: {
218
- position: 'bottom',
219
- labels: {
220
- padding: 20
221
- },
222
- onClick: function(e, legendItem, legend) {
223
- const index = legendItem.datasetIndex;
224
- const ci = legend.chart;
225
- if (ci.isDatasetVisible(index)) {
226
- ci.hide(index);
227
- legendItem.hidden = true;
228
- } else {
229
- ci.show(index);
230
- legendItem.hidden = false;
231
- }
232
-
233
- saveCurrentDisabledDatasets()
234
- }
235
- },
236
- tooltip: {
237
- callbacks: {
238
- label: function (tooltipItem) {
239
- return formatTooltip(label_type_y, tooltipItem)
240
- }
241
- }
242
- }
243
- },
244
- scales:{
245
- x: {
246
- display: false,
247
- },
248
- y: {
249
- ticks: {
250
- precision: y_precision,
251
- // forces step size to be 50 units
252
- count: 5,
253
- callback: function(label, index, labels) {
254
- return formatLabelY(label_type_y, label, index, labels)
255
- }
256
- }
257
- }
258
- },
259
- elements: {
260
- point: {
261
- radius: 0,
262
- style: false
263
- },
264
- line: {
265
- style: 'star',
266
- radius: 0,
267
- spanGaps: false
268
- }
269
- },
270
- hover: {
271
- mode: 'index',
272
- intersect: false,
273
- }
274
- }
275
- })
276
- }
277
-
278
- function renderCharts() {
279
- var charts = document.getElementsByClassName('chartjs');
280
-
281
- for (let i = 0; i < charts.length; i++) {
282
- let chart = charts.item(i)
283
- let chart_id = chart.id
284
- let raw_data = chart.dataset.datasets
285
- let datasets = JSON.parse(raw_data)
286
- let data = []
287
- let labels = []
288
- let label_type_y = chart.dataset.label_type_y
289
- let label_type_x = chart.dataset.label_type_x
290
- // by default assume integer values
291
- let y_precision = 0
292
- let disabled_sets = getCurrentChartDisabledDatasets(chart_id)
293
-
294
- Object.keys(datasets).forEach(function(key, i) {
295
- var current_set = datasets[key]
296
-
297
- let y = 0;
298
-
299
- while (y < current_set.length) {
300
- let current_value = current_set[y][1]
301
- let current_label = current_set[y][0]
302
-
303
- if (i == 0) {
304
- labels.push(formatLabelX(current_label, label_type_x))
305
- }
306
-
307
- if (isFractialPrecision(current_value)) {
308
- y_precision = 2
309
- }
310
-
311
- y++
312
- }
313
-
314
- data.push(
315
- {
316
- data: datasets[key],
317
- label: key,
318
- hidden: disabled_sets.includes(i),
319
- borderWidth: 2.5
320
- }
321
- )
322
- })
323
-
324
- renderChart(chart, labels, data, y_precision, label_type_y)
325
- }
5
+ const barChartManager = new BarChartManager();
6
+ barChartManager.refreshAndRenderBarCharts(newDoc, true);
326
7
  }
327
8
 
328
9
  function manageCharts() {
329
- renderCharts()
10
+ const lineChartsManager = new LineChartsManager();
11
+ lineChartsManager.refreshAndRender(document, false);
12
+
13
+ const barChartManager = new BarChartManager();
14
+ barChartManager.refreshAndRenderBarCharts(document, false);
330
15
  }
@@ -89,17 +89,17 @@ function refreshPage(text) {
89
89
  var old_charts_count = document.querySelectorAll('.chartjs').length
90
90
  var new_charts_count = new_content.querySelectorAll('.chartjs').length
91
91
 
92
- // if there are any charts, we will not replace the whole page but only counters
92
+ // if there are any charts, we will not replace the whole page but only refreshable part
93
93
  // and we will use graphs data and update this as well
94
94
  // this will prevent us from leaking the charts references and overloading the memory
95
95
  // ChartsJS does not free all the resources when dom is replaced which causes problems after
96
96
  // the charts run for a long period of time without page reload
97
97
  if (new_charts_count == 0 || old_charts_count == 0) {
98
- document.getElementById("content").replaceWith(new_content)
98
+ document.getElementById('content').replaceWith(new_content)
99
99
  addListeners()
100
100
  } else {
101
- let new_counters = new_doc.getElementById('counters');
102
- document.getElementById('counters').replaceWith(new_counters)
101
+ let new_refreshable = new_doc.getElementById('refreshable');
102
+ document.getElementById('refreshable').replaceWith(new_refreshable)
103
103
  refreshCharts(new_doc)
104
104
  }
105
105
 
@@ -112,7 +112,7 @@ function showError(error) {
112
112
 
113
113
  function scheduleLivePoll() {
114
114
  if (oldDOM == null) {
115
- oldDOM = document.getElementById("content").innerHTML;
115
+ oldDOM = document.getElementById('content').innerHTML;
116
116
  }
117
117
 
118
118
  let ti = parseInt(localStorage.karafkaTimeInterval) || 5000;
@@ -0,0 +1,57 @@
1
+ class TabsManager {
2
+ constructor() {
3
+ this.storageKey = 'karafkaActiveTabs';
4
+ }
5
+
6
+ // Reads the active tabs from local storage
7
+ readAllActiveTabs() {
8
+ const rawActiveTabs = localStorage.getItem(this.storageKey);
9
+ return rawActiveTabs ? JSON.parse(rawActiveTabs) : {};
10
+ }
11
+
12
+ // Saves the active tabs to local storage
13
+ saveAllActiveTabs(data) {
14
+ localStorage.setItem(this.storageKey, JSON.stringify(data));
15
+ }
16
+
17
+ // Saves the current state of active tabs
18
+ saveCurrentActiveTabs() {
19
+ const activeTabs = document.querySelectorAll('.tab-content > .active');
20
+ const url = window.location.href.split('?')[0];
21
+ let currentActiveTabs = [];
22
+ let allTabs = this.readAllActiveTabs();
23
+
24
+ activeTabs.forEach(activeTab => {
25
+ currentActiveTabs.push(activeTab.id);
26
+ });
27
+
28
+ allTabs[url] = currentActiveTabs;
29
+ this.saveAllActiveTabs(allTabs);
30
+ }
31
+
32
+ // Sets the active tabs based on stored data
33
+ setActiveTabs() {
34
+ const url = window.location.href.split('?')[0];
35
+ const allTabs = this.readAllActiveTabs();
36
+ const activeTabs = allTabs[url];
37
+
38
+ if (!activeTabs) return;
39
+
40
+ activeTabs.forEach(activeTabId => {
41
+ const tabElement = document.getElementById(activeTabId + '-tab');
42
+ if (tabElement) {
43
+ const bsTab = new bootstrap.Tab(tabElement);
44
+ bsTab.show();
45
+ }
46
+ });
47
+ }
48
+
49
+ // Initializes tab management and event listeners
50
+ manageTabs() {
51
+ this.setActiveTabs();
52
+
53
+ document.addEventListener('shown.bs.tab', (event) => {
54
+ this.saveCurrentActiveTabs();
55
+ });
56
+ }
57
+ }