karafka-web 0.8.2 → 0.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (269) 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 +39 -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/karafka-web.gemspec +4 -2
  11. data/lib/karafka/web/app.rb +1 -1
  12. data/lib/karafka/web/config.rb +17 -0
  13. data/lib/karafka/web/contracts/config.rb +6 -0
  14. data/lib/karafka/web/management/actions/create_topics.rb +21 -0
  15. data/lib/karafka/web/management/actions/delete_topics.rb +1 -0
  16. data/lib/karafka/web/management/actions/enable.rb +11 -5
  17. data/lib/karafka/web/management/migrations/0_base.rb +1 -1
  18. data/lib/karafka/web/pro/commanding/commands/base.rb +33 -0
  19. data/lib/karafka/web/pro/commanding/commands/probe.rb +41 -0
  20. data/lib/karafka/web/pro/commanding/commands/quiet.rb +31 -0
  21. data/lib/karafka/web/pro/commanding/commands/stop.rb +31 -0
  22. data/lib/karafka/web/pro/commanding/config.rb +57 -0
  23. data/lib/karafka/web/pro/commanding/contracts/config.rb +60 -0
  24. data/lib/karafka/web/pro/commanding/dispatcher.rb +93 -0
  25. data/lib/karafka/web/pro/commanding/listener.rb +97 -0
  26. data/lib/karafka/web/pro/commanding/manager.rb +98 -0
  27. data/lib/karafka/web/pro/commanding/matcher.rb +50 -0
  28. data/lib/karafka/web/pro/commanding.rb +40 -0
  29. data/lib/karafka/web/pro/loader.rb +40 -0
  30. data/lib/karafka/web/{ui/pro → pro/ui}/app.rb +103 -22
  31. data/lib/karafka/web/{ui/pro/controllers/cluster.rb → pro/ui/controllers/base_controller.rb} +4 -5
  32. data/lib/karafka/web/pro/ui/controllers/cluster_controller.rb +54 -0
  33. data/lib/karafka/web/pro/ui/controllers/commanding_controller.rb +118 -0
  34. data/lib/karafka/web/pro/ui/controllers/commands_controller.rb +96 -0
  35. data/lib/karafka/web/{ui/pro/controllers/consumers.rb → pro/ui/controllers/consumers_controller.rb} +31 -4
  36. data/lib/karafka/web/{ui/pro/controllers/dashboard.rb → pro/ui/controllers/dashboard_controller.rb} +5 -3
  37. data/lib/karafka/web/pro/ui/controllers/dlq_controller.rb +60 -0
  38. data/lib/karafka/web/{ui/pro/controllers/errors.rb → pro/ui/controllers/errors_controller.rb} +5 -7
  39. data/lib/karafka/web/{ui/pro/controllers/explorer.rb → pro/ui/controllers/explorer_controller.rb} +24 -19
  40. data/lib/karafka/web/{ui/pro/controllers/health.rb → pro/ui/controllers/health_controller.rb} +16 -3
  41. data/lib/karafka/web/{ui/pro/controllers/jobs.rb → pro/ui/controllers/jobs_controller.rb} +4 -4
  42. data/lib/karafka/web/{ui/pro/controllers/messages.rb → pro/ui/controllers/messages_controller.rb} +8 -6
  43. data/lib/karafka/web/{ui/pro/controllers/routing.rb → pro/ui/controllers/routing_controller.rb} +6 -22
  44. data/lib/karafka/web/{ui/pro/controllers/status.rb → pro/ui/controllers/status_controller.rb} +3 -3
  45. data/lib/karafka/web/pro/ui/controllers/topics_controller.rb +99 -0
  46. data/lib/karafka/web/pro/ui/lib/patterns_detector.rb +50 -0
  47. data/lib/karafka/web/pro/ui/views/cluster/_breadcrumbs.erb +29 -0
  48. data/lib/karafka/web/pro/ui/views/cluster/_broker.erb +13 -0
  49. data/lib/karafka/web/pro/ui/views/cluster/_config.erb +13 -0
  50. data/lib/karafka/web/pro/ui/views/cluster/_tabs.erb +27 -0
  51. data/lib/karafka/web/pro/ui/views/cluster/brokers.erb +27 -0
  52. data/lib/karafka/web/pro/ui/views/cluster/index.erb +27 -0
  53. data/lib/karafka/web/pro/ui/views/cluster/show.erb +27 -0
  54. data/lib/karafka/web/pro/ui/views/commands/_backtrace.erb +20 -0
  55. data/lib/karafka/web/pro/ui/views/commands/_breadcrumbs.erb +21 -0
  56. data/lib/karafka/web/pro/ui/views/commands/_command.erb +60 -0
  57. data/lib/karafka/web/pro/ui/views/commands/_command_details.erb +11 -0
  58. data/lib/karafka/web/pro/ui/views/commands/_details.erb +26 -0
  59. data/lib/karafka/web/pro/ui/views/commands/_empty.erb +3 -0
  60. data/lib/karafka/web/pro/ui/views/commands/_incompatible_schema.erb +14 -0
  61. data/lib/karafka/web/pro/ui/views/commands/_metadata.erb +50 -0
  62. data/lib/karafka/web/pro/ui/views/commands/_table.erb +23 -0
  63. data/lib/karafka/web/pro/ui/views/commands/index.erb +17 -0
  64. data/lib/karafka/web/pro/ui/views/commands/show.erb +38 -0
  65. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_breadcrumbs.erb +20 -4
  66. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_consumer.erb +2 -21
  67. data/lib/karafka/web/pro/ui/views/consumers/_consumer_controls.erb +78 -0
  68. data/lib/karafka/web/pro/ui/views/consumers/_consumer_performance.erb +59 -0
  69. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_counters.erb +7 -5
  70. data/lib/karafka/web/pro/ui/views/consumers/_tabs.erb +35 -0
  71. data/lib/karafka/web/pro/ui/views/consumers/consumer/_commands.erb +32 -0
  72. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_jobs.erb +7 -0
  73. data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_subscriptions.erb +7 -0
  74. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_subscription_group.erb +13 -8
  75. data/lib/karafka/web/pro/ui/views/consumers/consumer/_title.erb +5 -0
  76. data/lib/karafka/web/pro/ui/views/consumers/controls.erb +67 -0
  77. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/details.erb +6 -1
  78. data/lib/karafka/web/pro/ui/views/consumers/index.erb +39 -0
  79. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/pending_jobs.erb +15 -7
  80. data/lib/karafka/web/pro/ui/views/consumers/performance.erb +52 -0
  81. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/running_jobs.erb +15 -7
  82. data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/subscriptions.erb +6 -1
  83. data/lib/karafka/web/{ui/pro → pro/ui}/views/dashboard/index.erb +10 -10
  84. data/lib/karafka/web/pro/ui/views/dlq/_no_topics.erb +7 -0
  85. data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/index.erb +1 -1
  86. data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_error.erb +2 -6
  87. data/lib/karafka/web/pro/ui/views/errors/_table.erb +23 -0
  88. data/lib/karafka/web/pro/ui/views/explorer/_failed_deserialization.erb +4 -0
  89. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_message.erb +7 -1
  90. data/lib/karafka/web/pro/ui/views/explorer/_no_topics.erb +5 -0
  91. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/index.erb +1 -6
  92. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_metadata.erb +33 -9
  93. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_payload.erb +4 -4
  94. data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_payload_actions.erb +1 -1
  95. data/lib/karafka/web/pro/ui/views/explorer/messages/_headers.erb +33 -0
  96. data/lib/karafka/web/pro/ui/views/explorer/messages/_key.erb +20 -0
  97. data/lib/karafka/web/pro/ui/views/explorer/partition/_cleaned.erb +5 -0
  98. data/lib/karafka/web/pro/ui/views/explorer/partition/_empty.erb +5 -0
  99. data/lib/karafka/web/pro/ui/views/explorer/partition/_messages.erb +21 -0
  100. data/lib/karafka/web/pro/ui/views/explorer/topic/_empty.erb +5 -0
  101. data/lib/karafka/web/pro/ui/views/explorer/topic/_limited.erb +10 -0
  102. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_breadcrumbs.erb +8 -0
  103. data/lib/karafka/web/pro/ui/views/health/_no_data.erb +7 -0
  104. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition.erb +1 -1
  105. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_lags.erb +6 -3
  106. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_tabs.erb +11 -1
  107. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/changes.erb +13 -8
  108. data/lib/karafka/web/pro/ui/views/health/cluster_lags.erb +54 -0
  109. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/lags.erb +14 -8
  110. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/offsets.erb +15 -12
  111. data/lib/karafka/web/{ui/pro → pro/ui}/views/health/overview.erb +21 -7
  112. data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/_job.erb +1 -1
  113. data/lib/karafka/web/pro/ui/views/jobs/_no_jobs.erb +7 -0
  114. data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/pending.erb +12 -8
  115. data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/running.erb +12 -8
  116. data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_consumer_group.erb +2 -2
  117. data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/index.erb +1 -1
  118. data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/show.erb +1 -1
  119. data/lib/karafka/web/{ui/pro → pro/ui}/views/shared/_navigation.erb +14 -0
  120. data/lib/karafka/web/pro/ui/views/topics/_breadcrumbs.erb +37 -0
  121. data/lib/karafka/web/pro/ui/views/topics/_partition.erb +16 -0
  122. data/lib/karafka/web/pro/ui/views/topics/_tabs.erb +37 -0
  123. data/lib/karafka/web/pro/ui/views/topics/_topic.erb +12 -0
  124. data/lib/karafka/web/pro/ui/views/topics/config.erb +29 -0
  125. data/lib/karafka/web/pro/ui/views/topics/distribution/_badges.erb +7 -0
  126. data/lib/karafka/web/pro/ui/views/topics/distribution/_chart.erb +2 -0
  127. data/lib/karafka/web/pro/ui/views/topics/distribution/_empty_partitions.erb +1 -0
  128. data/lib/karafka/web/pro/ui/views/topics/distribution/_limited.erb +10 -0
  129. data/lib/karafka/web/pro/ui/views/topics/distribution/_partition.erb +10 -0
  130. data/lib/karafka/web/pro/ui/views/topics/distribution.erb +47 -0
  131. data/lib/karafka/web/pro/ui/views/topics/index.erb +16 -0
  132. data/lib/karafka/web/pro/ui/views/topics/replication.erb +28 -0
  133. data/lib/karafka/web/processing/consumers/aggregators/base.rb +1 -1
  134. data/lib/karafka/web/processing/consumers/aggregators/metrics.rb +1 -1
  135. data/lib/karafka/web/processing/consumers/aggregators/state.rb +4 -4
  136. data/lib/karafka/web/tracking/consumers/contracts/report.rb +1 -1
  137. data/lib/karafka/web/tracking/consumers/listeners/booting.rb +3 -1
  138. data/lib/karafka/web/tracking/consumers/listeners/errors.rb +2 -2
  139. data/lib/karafka/web/tracking/consumers/reporter.rb +3 -3
  140. data/lib/karafka/web/tracking/consumers/sampler.rb +2 -2
  141. data/lib/karafka/web/tracking/contracts/error.rb +1 -1
  142. data/lib/karafka/web/tracking/producers/listeners/booting.rb +3 -1
  143. data/lib/karafka/web/tracking/producers/listeners/errors.rb +2 -2
  144. data/lib/karafka/web/tracking/producers/reporter.rb +1 -1
  145. data/lib/karafka/web/tracking/producers/sampler.rb +1 -1
  146. data/lib/karafka/web/tracking/sampler.rb +3 -3
  147. data/lib/karafka/web/ui/app.rb +13 -9
  148. data/lib/karafka/web/ui/base.rb +1 -0
  149. data/lib/karafka/web/ui/controllers/{base.rb → base_controller.rb} +15 -2
  150. data/lib/karafka/web/ui/controllers/{become_pro.rb → become_pro_controller.rb} +1 -1
  151. data/lib/karafka/web/ui/controllers/{cluster.rb → cluster_controller.rb} +4 -4
  152. data/lib/karafka/web/ui/controllers/{consumers.rb → consumers_controller.rb} +3 -3
  153. data/lib/karafka/web/ui/controllers/{dashboard.rb → dashboard_controller.rb} +1 -1
  154. data/lib/karafka/web/ui/controllers/{errors.rb → errors_controller.rb} +2 -2
  155. data/lib/karafka/web/ui/controllers/{jobs.rb → jobs_controller.rb} +5 -5
  156. data/lib/karafka/web/ui/controllers/{routing.rb → routing_controller.rb} +2 -2
  157. data/lib/karafka/web/ui/controllers/{status.rb → status_controller.rb} +1 -1
  158. data/lib/karafka/web/ui/helpers/alerts_helper.rb +23 -0
  159. data/lib/karafka/web/ui/helpers/application_helper.rb +53 -1
  160. data/lib/karafka/web/ui/lib/paginations/offset_based.rb +3 -4
  161. data/lib/karafka/web/ui/lib/safe_runner.rb +59 -0
  162. data/lib/karafka/web/ui/models/broker.rb +66 -0
  163. data/lib/karafka/web/ui/models/health.rb +28 -2
  164. data/lib/karafka/web/ui/models/message.rb +9 -3
  165. data/lib/karafka/web/ui/models/process.rb +10 -5
  166. data/lib/karafka/web/ui/models/processes.rb +2 -2
  167. data/lib/karafka/web/ui/models/topic.rb +78 -0
  168. data/lib/karafka/web/ui/public/javascripts/application.js +18 -1
  169. data/lib/karafka/web/ui/public/javascripts/charts/data_formatting_utility.js +71 -0
  170. data/lib/karafka/web/ui/public/javascripts/charts/dataset_state_manager.js +49 -0
  171. data/lib/karafka/web/ui/public/javascripts/charts/types/bar.js +123 -0
  172. data/lib/karafka/web/ui/public/javascripts/charts/types/line.js +143 -0
  173. data/lib/karafka/web/ui/public/javascripts/charts.js +10 -325
  174. data/lib/karafka/web/ui/public/javascripts/live_poll.js +5 -5
  175. data/lib/karafka/web/ui/public/javascripts/tabs_manager.js +57 -0
  176. data/lib/karafka/web/ui/public/stylesheets/application.css +7 -6
  177. data/lib/karafka/web/ui/views/cluster/_breadcrumbs.erb +3 -3
  178. data/lib/karafka/web/ui/views/cluster/_no_partitions.erb +1 -3
  179. data/lib/karafka/web/ui/views/cluster/_tabs.erb +3 -3
  180. data/lib/karafka/web/ui/views/cluster/brokers.erb +19 -19
  181. data/lib/karafka/web/ui/views/cluster/replication.erb +37 -0
  182. data/lib/karafka/web/ui/views/consumers/_assignments_badges.erb +24 -0
  183. data/lib/karafka/web/ui/views/consumers/_consumer.erb +2 -15
  184. data/lib/karafka/web/ui/views/consumers/_counters.erb +1 -1
  185. data/lib/karafka/web/ui/views/consumers/_summary.erb +5 -5
  186. data/lib/karafka/web/ui/views/consumers/index.erb +22 -20
  187. data/lib/karafka/web/ui/views/dashboard/index.erb +9 -9
  188. data/lib/karafka/web/ui/views/errors/_cleaned.erb +1 -3
  189. data/lib/karafka/web/ui/views/errors/_error.erb +2 -6
  190. data/lib/karafka/web/ui/views/errors/_no_errors.erb +1 -3
  191. data/lib/karafka/web/ui/views/errors/index.erb +22 -20
  192. data/lib/karafka/web/ui/views/jobs/_job.erb +4 -1
  193. data/lib/karafka/web/ui/views/jobs/_no_jobs.erb +1 -3
  194. data/lib/karafka/web/ui/views/jobs/pending.erb +4 -3
  195. data/lib/karafka/web/ui/views/jobs/running.erb +4 -3
  196. data/lib/karafka/web/ui/views/routing/_consumer_group.erb +2 -2
  197. data/lib/karafka/web/ui/views/routing/index.erb +1 -1
  198. data/lib/karafka/web/ui/views/routing/show.erb +1 -1
  199. data/lib/karafka/web/ui/views/shared/_become_pro.erb +3 -3
  200. data/lib/karafka/web/ui/views/shared/_header.erb +16 -10
  201. data/lib/karafka/web/ui/views/shared/_navigation.erb +17 -3
  202. data/lib/karafka/web/ui/views/shared/_not_a_message.erb +5 -0
  203. data/lib/karafka/web/ui/views/shared/alerts/_info.erb +3 -0
  204. data/lib/karafka/web/ui/views/shared/charts/_bar.erb +7 -0
  205. data/lib/karafka/web/ui/views/shared/{_chart.erb → charts/_line.erb} +1 -1
  206. data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +1 -1
  207. data/lib/karafka/web/ui/views/status/show.erb +1 -1
  208. data/lib/karafka/web/version.rb +1 -1
  209. data/lib/karafka/web.rb +17 -1
  210. data.tar.gz.sig +0 -0
  211. metadata +189 -120
  212. metadata.gz.sig +0 -0
  213. data/lib/karafka/web/ui/pro/controllers/dlq.rb +0 -43
  214. data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_jobs.erb +0 -9
  215. data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_subscriptions.erb +0 -9
  216. data/lib/karafka/web/ui/pro/views/consumers/index.erb +0 -36
  217. data/lib/karafka/web/ui/pro/views/dlq/_no_topics.erb +0 -9
  218. data/lib/karafka/web/ui/pro/views/errors/_table.erb +0 -21
  219. data/lib/karafka/web/ui/pro/views/explorer/_failed_deserialization.erb +0 -4
  220. data/lib/karafka/web/ui/pro/views/explorer/_no_topics.erb +0 -7
  221. data/lib/karafka/web/ui/pro/views/explorer/messages/_headers.erb +0 -15
  222. data/lib/karafka/web/ui/pro/views/explorer/messages/_key.erb +0 -12
  223. data/lib/karafka/web/ui/pro/views/explorer/partition/_cleaned.erb +0 -3
  224. data/lib/karafka/web/ui/pro/views/explorer/partition/_empty.erb +0 -3
  225. data/lib/karafka/web/ui/pro/views/explorer/partition/_messages.erb +0 -19
  226. data/lib/karafka/web/ui/pro/views/explorer/topic/_empty.erb +0 -3
  227. data/lib/karafka/web/ui/pro/views/explorer/topic/_limited.erb +0 -4
  228. data/lib/karafka/web/ui/pro/views/health/_no_data.erb +0 -9
  229. data/lib/karafka/web/ui/pro/views/jobs/_no_jobs.erb +0 -9
  230. data/lib/karafka/web/ui/public/javascripts/tabs.js +0 -59
  231. data/lib/karafka/web/ui/views/cluster/topics.erb +0 -35
  232. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_consumer_group.erb +0 -0
  233. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_job.erb +0 -0
  234. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_metrics.erb +0 -0
  235. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_partition.erb +0 -0
  236. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_stopped.erb +0 -0
  237. /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_tabs.erb +0 -0
  238. /data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/_breadcrumbs.erb +0 -0
  239. /data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/_topic.erb +0 -0
  240. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_breadcrumbs.erb +0 -0
  241. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_partition_option.erb +0 -0
  242. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_title_with_select.erb +0 -0
  243. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/index.erb +0 -0
  244. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/partition.erb +0 -0
  245. /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/show.erb +0 -0
  246. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_breadcrumbs.erb +0 -0
  247. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_filtered.erb +0 -0
  248. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_partition_option.erb +0 -0
  249. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_topic.erb +0 -0
  250. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_message_actions.erb +0 -0
  251. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/messages/_detail.erb +0 -0
  252. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/partition/_details.erb +0 -0
  253. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/partition.erb +0 -0
  254. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/show.erb +0 -0
  255. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/topic/_details.erb +0 -0
  256. /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/topic.erb +0 -0
  257. /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_consumer_group_header.erb +0 -0
  258. /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_offset.erb +0 -0
  259. /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_times.erb +0 -0
  260. /data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_detail.erb +0 -0
  261. /data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_topic.erb +0 -0
  262. /data/lib/karafka/web/ui/public/javascripts/{bootstrap.min.js → libs/bootstrap.min.js} +0 -0
  263. /data/lib/karafka/web/ui/public/javascripts/{chart.min.js → libs/chart.min.js} +0 -0
  264. /data/lib/karafka/web/ui/public/javascripts/{datepicker.js → libs/datepicker.js} +0 -0
  265. /data/lib/karafka/web/ui/public/javascripts/{highlight.min.js → libs/highlight.min.js} +0 -0
  266. /data/lib/karafka/web/ui/public/javascripts/{timeago.min.js → libs/timeago.min.js} +0 -0
  267. /data/lib/karafka/web/ui/public/stylesheets/{bootstrap.min.css → libs/bootstrap.min.css} +0 -0
  268. /data/lib/karafka/web/ui/public/stylesheets/{datepicker.min.css → libs/datepicker.min.css} +0 -0
  269. /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
+ }