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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +5 -16
- data/CHANGELOG.md +40 -0
- data/Gemfile.lock +21 -23
- data/LICENSE +3 -3
- data/bin/rspecs +1 -1
- data/config/locales/pro_errors.yml +11 -0
- data/config/locales/slogans.yml +62 -0
- data/docker-compose.yml +1 -1
- data/karafka-web.gemspec +4 -2
- data/lib/karafka/web/app.rb +1 -1
- data/lib/karafka/web/config.rb +23 -3
- data/lib/karafka/web/contracts/config.rb +7 -1
- data/lib/karafka/web/management/actions/create_topics.rb +21 -0
- data/lib/karafka/web/management/actions/delete_topics.rb +1 -0
- data/lib/karafka/web/management/actions/enable.rb +12 -6
- data/lib/karafka/web/management/migrations/0_base.rb +1 -1
- data/lib/karafka/web/pro/commanding/commands/base.rb +33 -0
- data/lib/karafka/web/pro/commanding/commands/probe.rb +41 -0
- data/lib/karafka/web/pro/commanding/commands/quiet.rb +31 -0
- data/lib/karafka/web/pro/commanding/commands/stop.rb +31 -0
- data/lib/karafka/web/pro/commanding/config.rb +51 -0
- data/lib/karafka/web/pro/commanding/contracts/config.rb +56 -0
- data/lib/karafka/web/pro/commanding/dispatcher.rb +93 -0
- data/lib/karafka/web/pro/commanding/listener.rb +97 -0
- data/lib/karafka/web/pro/commanding/manager.rb +98 -0
- data/lib/karafka/web/pro/commanding/matcher.rb +50 -0
- data/lib/karafka/web/pro/commanding.rb +40 -0
- data/lib/karafka/web/pro/loader.rb +40 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/app.rb +103 -22
- data/lib/karafka/web/{ui/pro/controllers/cluster.rb → pro/ui/controllers/base_controller.rb} +4 -5
- data/lib/karafka/web/pro/ui/controllers/cluster_controller.rb +54 -0
- data/lib/karafka/web/pro/ui/controllers/commanding_controller.rb +118 -0
- data/lib/karafka/web/pro/ui/controllers/commands_controller.rb +96 -0
- data/lib/karafka/web/{ui/pro/controllers/consumers.rb → pro/ui/controllers/consumers_controller.rb} +31 -4
- data/lib/karafka/web/{ui/pro/controllers/dashboard.rb → pro/ui/controllers/dashboard_controller.rb} +5 -3
- data/lib/karafka/web/pro/ui/controllers/dlq_controller.rb +60 -0
- data/lib/karafka/web/{ui/pro/controllers/errors.rb → pro/ui/controllers/errors_controller.rb} +5 -7
- data/lib/karafka/web/{ui/pro/controllers/explorer.rb → pro/ui/controllers/explorer_controller.rb} +24 -19
- data/lib/karafka/web/{ui/pro/controllers/health.rb → pro/ui/controllers/health_controller.rb} +16 -3
- data/lib/karafka/web/{ui/pro/controllers/jobs.rb → pro/ui/controllers/jobs_controller.rb} +4 -4
- data/lib/karafka/web/{ui/pro/controllers/messages.rb → pro/ui/controllers/messages_controller.rb} +8 -6
- data/lib/karafka/web/{ui/pro/controllers/routing.rb → pro/ui/controllers/routing_controller.rb} +6 -22
- data/lib/karafka/web/{ui/pro/controllers/status.rb → pro/ui/controllers/status_controller.rb} +3 -3
- data/lib/karafka/web/pro/ui/controllers/topics_controller.rb +99 -0
- data/lib/karafka/web/pro/ui/lib/patterns_detector.rb +50 -0
- data/lib/karafka/web/pro/ui/views/cluster/_breadcrumbs.erb +29 -0
- data/lib/karafka/web/pro/ui/views/cluster/_broker.erb +13 -0
- data/lib/karafka/web/pro/ui/views/cluster/_config.erb +13 -0
- data/lib/karafka/web/pro/ui/views/cluster/_tabs.erb +27 -0
- data/lib/karafka/web/pro/ui/views/cluster/brokers.erb +27 -0
- data/lib/karafka/web/pro/ui/views/cluster/index.erb +27 -0
- data/lib/karafka/web/pro/ui/views/cluster/show.erb +27 -0
- data/lib/karafka/web/pro/ui/views/commands/_backtrace.erb +20 -0
- data/lib/karafka/web/pro/ui/views/commands/_breadcrumbs.erb +21 -0
- data/lib/karafka/web/pro/ui/views/commands/_command.erb +60 -0
- data/lib/karafka/web/pro/ui/views/commands/_command_details.erb +11 -0
- data/lib/karafka/web/pro/ui/views/commands/_details.erb +26 -0
- data/lib/karafka/web/pro/ui/views/commands/_empty.erb +3 -0
- data/lib/karafka/web/pro/ui/views/commands/_incompatible_schema.erb +14 -0
- data/lib/karafka/web/pro/ui/views/commands/_metadata.erb +50 -0
- data/lib/karafka/web/pro/ui/views/commands/_table.erb +23 -0
- data/lib/karafka/web/pro/ui/views/commands/index.erb +17 -0
- data/lib/karafka/web/pro/ui/views/commands/show.erb +38 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_breadcrumbs.erb +20 -4
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_consumer.erb +2 -21
- data/lib/karafka/web/pro/ui/views/consumers/_consumer_controls.erb +78 -0
- data/lib/karafka/web/pro/ui/views/consumers/_consumer_performance.erb +59 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/_counters.erb +7 -5
- data/lib/karafka/web/pro/ui/views/consumers/_tabs.erb +35 -0
- data/lib/karafka/web/pro/ui/views/consumers/consumer/_commands.erb +32 -0
- data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_jobs.erb +7 -0
- data/lib/karafka/web/pro/ui/views/consumers/consumer/_no_subscriptions.erb +7 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_subscription_group.erb +13 -8
- data/lib/karafka/web/pro/ui/views/consumers/consumer/_title.erb +5 -0
- data/lib/karafka/web/pro/ui/views/consumers/controls.erb +67 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/details.erb +6 -1
- data/lib/karafka/web/pro/ui/views/consumers/index.erb +39 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/pending_jobs.erb +15 -7
- data/lib/karafka/web/pro/ui/views/consumers/performance.erb +52 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/running_jobs.erb +15 -7
- data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/subscriptions.erb +6 -1
- data/lib/karafka/web/{ui/pro → pro/ui}/views/dashboard/index.erb +10 -10
- data/lib/karafka/web/pro/ui/views/dlq/_no_topics.erb +7 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/index.erb +1 -1
- data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_error.erb +2 -6
- data/lib/karafka/web/pro/ui/views/errors/_table.erb +23 -0
- data/lib/karafka/web/pro/ui/views/explorer/_failed_deserialization.erb +4 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_message.erb +7 -1
- data/lib/karafka/web/pro/ui/views/explorer/_no_topics.erb +5 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/index.erb +1 -6
- data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_metadata.erb +33 -9
- data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_payload.erb +4 -4
- data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_payload_actions.erb +1 -1
- data/lib/karafka/web/pro/ui/views/explorer/messages/_headers.erb +33 -0
- data/lib/karafka/web/pro/ui/views/explorer/messages/_key.erb +20 -0
- data/lib/karafka/web/pro/ui/views/explorer/partition/_cleaned.erb +5 -0
- data/lib/karafka/web/pro/ui/views/explorer/partition/_empty.erb +5 -0
- data/lib/karafka/web/pro/ui/views/explorer/partition/_messages.erb +21 -0
- data/lib/karafka/web/pro/ui/views/explorer/topic/_empty.erb +5 -0
- data/lib/karafka/web/pro/ui/views/explorer/topic/_limited.erb +10 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_breadcrumbs.erb +8 -0
- data/lib/karafka/web/pro/ui/views/health/_no_data.erb +7 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition.erb +1 -1
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_lags.erb +6 -3
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_tabs.erb +11 -1
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/changes.erb +13 -8
- data/lib/karafka/web/pro/ui/views/health/cluster_lags.erb +54 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/lags.erb +14 -8
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/offsets.erb +15 -12
- data/lib/karafka/web/{ui/pro → pro/ui}/views/health/overview.erb +21 -7
- data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/_job.erb +1 -1
- data/lib/karafka/web/pro/ui/views/jobs/_no_jobs.erb +7 -0
- data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/pending.erb +12 -8
- data/lib/karafka/web/{ui/pro → pro/ui}/views/jobs/running.erb +12 -8
- data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_consumer_group.erb +2 -2
- data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/index.erb +1 -1
- data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/show.erb +1 -1
- data/lib/karafka/web/{ui/pro → pro/ui}/views/shared/_navigation.erb +14 -0
- data/lib/karafka/web/pro/ui/views/topics/_breadcrumbs.erb +37 -0
- data/lib/karafka/web/pro/ui/views/topics/_partition.erb +16 -0
- data/lib/karafka/web/pro/ui/views/topics/_tabs.erb +37 -0
- data/lib/karafka/web/pro/ui/views/topics/_topic.erb +12 -0
- data/lib/karafka/web/pro/ui/views/topics/config.erb +29 -0
- data/lib/karafka/web/pro/ui/views/topics/distribution/_badges.erb +7 -0
- data/lib/karafka/web/pro/ui/views/topics/distribution/_chart.erb +2 -0
- data/lib/karafka/web/pro/ui/views/topics/distribution/_empty_partitions.erb +1 -0
- data/lib/karafka/web/pro/ui/views/topics/distribution/_limited.erb +10 -0
- data/lib/karafka/web/pro/ui/views/topics/distribution/_partition.erb +10 -0
- data/lib/karafka/web/pro/ui/views/topics/distribution.erb +47 -0
- data/lib/karafka/web/pro/ui/views/topics/index.erb +16 -0
- data/lib/karafka/web/pro/ui/views/topics/replication.erb +28 -0
- data/lib/karafka/web/processing/consumers/aggregators/base.rb +1 -1
- data/lib/karafka/web/processing/consumers/aggregators/metrics.rb +1 -1
- data/lib/karafka/web/processing/consumers/aggregators/state.rb +4 -4
- data/lib/karafka/web/tracking/consumers/contracts/report.rb +1 -1
- data/lib/karafka/web/tracking/consumers/listeners/booting.rb +3 -1
- data/lib/karafka/web/tracking/consumers/listeners/errors.rb +2 -2
- data/lib/karafka/web/tracking/consumers/reporter.rb +3 -3
- data/lib/karafka/web/tracking/consumers/sampler.rb +2 -2
- data/lib/karafka/web/tracking/contracts/error.rb +1 -1
- data/lib/karafka/web/tracking/producers/listeners/booting.rb +3 -1
- data/lib/karafka/web/tracking/producers/listeners/errors.rb +2 -2
- data/lib/karafka/web/tracking/producers/reporter.rb +1 -1
- data/lib/karafka/web/tracking/producers/sampler.rb +1 -1
- data/lib/karafka/web/tracking/sampler.rb +3 -3
- data/lib/karafka/web/ui/app.rb +13 -9
- data/lib/karafka/web/ui/base.rb +1 -0
- data/lib/karafka/web/ui/controllers/{base.rb → base_controller.rb} +15 -2
- data/lib/karafka/web/ui/controllers/{become_pro.rb → become_pro_controller.rb} +1 -1
- data/lib/karafka/web/ui/controllers/{cluster.rb → cluster_controller.rb} +4 -4
- data/lib/karafka/web/ui/controllers/{consumers.rb → consumers_controller.rb} +3 -3
- data/lib/karafka/web/ui/controllers/{dashboard.rb → dashboard_controller.rb} +1 -1
- data/lib/karafka/web/ui/controllers/{errors.rb → errors_controller.rb} +2 -2
- data/lib/karafka/web/ui/controllers/{jobs.rb → jobs_controller.rb} +5 -5
- data/lib/karafka/web/ui/controllers/{routing.rb → routing_controller.rb} +2 -2
- data/lib/karafka/web/ui/controllers/{status.rb → status_controller.rb} +1 -1
- data/lib/karafka/web/ui/helpers/alerts_helper.rb +23 -0
- data/lib/karafka/web/ui/helpers/application_helper.rb +53 -1
- data/lib/karafka/web/ui/lib/paginations/offset_based.rb +3 -4
- data/lib/karafka/web/ui/lib/safe_runner.rb +59 -0
- data/lib/karafka/web/ui/models/broker.rb +66 -0
- data/lib/karafka/web/ui/models/health.rb +28 -2
- data/lib/karafka/web/ui/models/message.rb +9 -3
- data/lib/karafka/web/ui/models/process.rb +10 -5
- data/lib/karafka/web/ui/models/processes.rb +2 -2
- data/lib/karafka/web/ui/models/status.rb +1 -1
- data/lib/karafka/web/ui/models/topic.rb +78 -0
- data/lib/karafka/web/ui/public/javascripts/application.js +18 -1
- data/lib/karafka/web/ui/public/javascripts/charts/data_formatting_utility.js +71 -0
- data/lib/karafka/web/ui/public/javascripts/charts/dataset_state_manager.js +49 -0
- data/lib/karafka/web/ui/public/javascripts/charts/types/bar.js +123 -0
- data/lib/karafka/web/ui/public/javascripts/charts/types/line.js +143 -0
- data/lib/karafka/web/ui/public/javascripts/charts.js +10 -325
- data/lib/karafka/web/ui/public/javascripts/live_poll.js +5 -5
- data/lib/karafka/web/ui/public/javascripts/tabs_manager.js +57 -0
- data/lib/karafka/web/ui/public/stylesheets/application.css +7 -6
- data/lib/karafka/web/ui/views/cluster/_breadcrumbs.erb +3 -3
- data/lib/karafka/web/ui/views/cluster/_no_partitions.erb +1 -3
- data/lib/karafka/web/ui/views/cluster/_tabs.erb +3 -3
- data/lib/karafka/web/ui/views/cluster/brokers.erb +19 -19
- data/lib/karafka/web/ui/views/cluster/replication.erb +37 -0
- data/lib/karafka/web/ui/views/consumers/_assignments_badges.erb +24 -0
- data/lib/karafka/web/ui/views/consumers/_consumer.erb +2 -15
- data/lib/karafka/web/ui/views/consumers/_counters.erb +1 -1
- data/lib/karafka/web/ui/views/consumers/_summary.erb +5 -5
- data/lib/karafka/web/ui/views/consumers/index.erb +22 -20
- data/lib/karafka/web/ui/views/dashboard/index.erb +9 -9
- data/lib/karafka/web/ui/views/errors/_cleaned.erb +1 -3
- data/lib/karafka/web/ui/views/errors/_error.erb +2 -6
- data/lib/karafka/web/ui/views/errors/_no_errors.erb +1 -3
- data/lib/karafka/web/ui/views/errors/index.erb +22 -20
- data/lib/karafka/web/ui/views/jobs/_job.erb +4 -1
- data/lib/karafka/web/ui/views/jobs/_no_jobs.erb +1 -3
- data/lib/karafka/web/ui/views/jobs/pending.erb +4 -3
- data/lib/karafka/web/ui/views/jobs/running.erb +4 -3
- data/lib/karafka/web/ui/views/routing/_consumer_group.erb +2 -2
- data/lib/karafka/web/ui/views/routing/index.erb +1 -1
- data/lib/karafka/web/ui/views/routing/show.erb +1 -1
- data/lib/karafka/web/ui/views/shared/_become_pro.erb +3 -3
- data/lib/karafka/web/ui/views/shared/_header.erb +16 -10
- data/lib/karafka/web/ui/views/shared/_navigation.erb +17 -3
- data/lib/karafka/web/ui/views/shared/_not_a_message.erb +5 -0
- data/lib/karafka/web/ui/views/shared/alerts/_info.erb +3 -0
- data/lib/karafka/web/ui/views/shared/charts/_bar.erb +7 -0
- data/lib/karafka/web/ui/views/shared/{_chart.erb → charts/_line.erb} +1 -1
- data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +1 -1
- data/lib/karafka/web/ui/views/status/show.erb +1 -1
- data/lib/karafka/web/version.rb +1 -1
- data/lib/karafka/web.rb +17 -1
- data.tar.gz.sig +0 -0
- metadata +189 -120
- metadata.gz.sig +0 -0
- data/lib/karafka/web/ui/pro/controllers/dlq.rb +0 -43
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_jobs.erb +0 -9
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_subscriptions.erb +0 -9
- data/lib/karafka/web/ui/pro/views/consumers/index.erb +0 -36
- data/lib/karafka/web/ui/pro/views/dlq/_no_topics.erb +0 -9
- data/lib/karafka/web/ui/pro/views/errors/_table.erb +0 -21
- data/lib/karafka/web/ui/pro/views/explorer/_failed_deserialization.erb +0 -4
- data/lib/karafka/web/ui/pro/views/explorer/_no_topics.erb +0 -7
- data/lib/karafka/web/ui/pro/views/explorer/messages/_headers.erb +0 -15
- data/lib/karafka/web/ui/pro/views/explorer/messages/_key.erb +0 -12
- data/lib/karafka/web/ui/pro/views/explorer/partition/_cleaned.erb +0 -3
- data/lib/karafka/web/ui/pro/views/explorer/partition/_empty.erb +0 -3
- data/lib/karafka/web/ui/pro/views/explorer/partition/_messages.erb +0 -19
- data/lib/karafka/web/ui/pro/views/explorer/topic/_empty.erb +0 -3
- data/lib/karafka/web/ui/pro/views/explorer/topic/_limited.erb +0 -4
- data/lib/karafka/web/ui/pro/views/health/_no_data.erb +0 -9
- data/lib/karafka/web/ui/pro/views/jobs/_no_jobs.erb +0 -9
- data/lib/karafka/web/ui/public/javascripts/tabs.js +0 -59
- data/lib/karafka/web/ui/views/cluster/topics.erb +0 -35
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_consumer_group.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_job.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_metrics.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_partition.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_stopped.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/consumers/consumer/_tabs.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/_breadcrumbs.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/dlq/_topic.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_breadcrumbs.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_partition_option.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/_title_with_select.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/index.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/partition.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/errors/show.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_breadcrumbs.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_filtered.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_partition_option.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/_topic.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/message/_message_actions.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/messages/_detail.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/partition/_details.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/partition.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/show.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/topic/_details.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/explorer/topic.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_consumer_group_header.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_offset.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/health/_partition_times.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_detail.erb +0 -0
- /data/lib/karafka/web/{ui/pro → pro/ui}/views/routing/_topic.erb +0 -0
- /data/lib/karafka/web/ui/public/javascripts/{bootstrap.min.js → libs/bootstrap.min.js} +0 -0
- /data/lib/karafka/web/ui/public/javascripts/{chart.min.js → libs/chart.min.js} +0 -0
- /data/lib/karafka/web/ui/public/javascripts/{datepicker.js → libs/datepicker.js} +0 -0
- /data/lib/karafka/web/ui/public/javascripts/{highlight.min.js → libs/highlight.min.js} +0 -0
- /data/lib/karafka/web/ui/public/javascripts/{timeago.min.js → libs/timeago.min.js} +0 -0
- /data/lib/karafka/web/ui/public/stylesheets/{bootstrap.min.css → libs/bootstrap.min.css} +0 -0
- /data/lib/karafka/web/ui/public/stylesheets/{datepicker.min.css → libs/datepicker.min.css} +0 -0
- /data/lib/karafka/web/ui/public/stylesheets/{highlight.min.css → libs/highlight.min.css} +0 -0
|
@@ -8,6 +8,29 @@ module Karafka
|
|
|
8
8
|
module Helpers
|
|
9
9
|
# Main application helper
|
|
10
10
|
module ApplicationHelper
|
|
11
|
+
# Default attribute names mapped from the attributes themselves
|
|
12
|
+
# It makes it easier as we do not have to declare those all the time
|
|
13
|
+
SORT_NAMES = {
|
|
14
|
+
id: 'ID',
|
|
15
|
+
partition_id: 'Partition',
|
|
16
|
+
memory_usage: 'RSS',
|
|
17
|
+
started_at: 'Started',
|
|
18
|
+
committed_offset: 'Committed',
|
|
19
|
+
last_offset: 'Last',
|
|
20
|
+
first_offset: 'First',
|
|
21
|
+
lo_offset: 'Low',
|
|
22
|
+
hi_offset: 'High',
|
|
23
|
+
ls_offset: 'LSO',
|
|
24
|
+
lag_hybrid: 'Lag',
|
|
25
|
+
lag_stored: 'Stored',
|
|
26
|
+
stored_offset: 'Stored',
|
|
27
|
+
fetch_state: 'Fetch',
|
|
28
|
+
poll_state: 'Poll',
|
|
29
|
+
lso_risk_state: 'LSO'
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
private_constant :SORT_NAMES
|
|
33
|
+
|
|
11
34
|
# Adds active class to the current location in the nav if needed
|
|
12
35
|
# @param location [Hash]
|
|
13
36
|
def nav_class(location)
|
|
@@ -260,7 +283,7 @@ module Karafka
|
|
|
260
283
|
def sort_link(name, attribute = nil, rev: false)
|
|
261
284
|
unless attribute
|
|
262
285
|
attribute = name
|
|
263
|
-
name = attribute.to_s.tr('_', ' ').capitalize
|
|
286
|
+
name = SORT_NAMES[attribute] || attribute.to_s.tr('_', ' ').tr('?', '').capitalize
|
|
264
287
|
end
|
|
265
288
|
|
|
266
289
|
arrow_both = '⇕'
|
|
@@ -284,6 +307,35 @@ module Karafka
|
|
|
284
307
|
|
|
285
308
|
"<a class=\"sort\" href=\"#{path}\">#{full_name}</a>"
|
|
286
309
|
end
|
|
310
|
+
|
|
311
|
+
# Truncates given text if it is too long and wraps it with a title with full text.
|
|
312
|
+
# Can use a middle-based strategy that keeps beginning and ending of a string instead of
|
|
313
|
+
# keeping just the beginning.
|
|
314
|
+
#
|
|
315
|
+
# The `:middle` strategy is useful when we have strings such as really long process names
|
|
316
|
+
# that have important beginning and end but middle can be removed without risk of not
|
|
317
|
+
# allowing user to recognize the content.
|
|
318
|
+
#
|
|
319
|
+
# @param string [String] string we want to truncate
|
|
320
|
+
# @param length [Integer] max length of the final string that we accept before truncating
|
|
321
|
+
# @param omission [String] truncation omission
|
|
322
|
+
# @param strategy [Symbol] `:default` or `:middle` how should we truncate
|
|
323
|
+
# @return [String] HTML span tag with truncated content and full content title
|
|
324
|
+
def truncate(string, length: 50, omission: '...', strategy: :default)
|
|
325
|
+
return string if string.length <= length
|
|
326
|
+
|
|
327
|
+
case strategy
|
|
328
|
+
when :default
|
|
329
|
+
truncated = string[0...(length - omission.length)] + omission
|
|
330
|
+
when :middle
|
|
331
|
+
part_length = (length - omission.length) / 2
|
|
332
|
+
truncated = string[0...part_length] + omission + string[-part_length..]
|
|
333
|
+
else
|
|
334
|
+
raise Karafka::Errors::UnsupportedCaseError, "Unknown strategy: #{strategy}"
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
%(<span title="#{string}">#{truncated}</span>)
|
|
338
|
+
end
|
|
287
339
|
end
|
|
288
340
|
end
|
|
289
341
|
end
|
|
@@ -42,13 +42,12 @@ module Karafka
|
|
|
42
42
|
# not consider it as a first page and we allow to "reset" to -1 via the first page
|
|
43
43
|
# button
|
|
44
44
|
def first_offset?
|
|
45
|
-
@current_offset != -1
|
|
45
|
+
@current_offset != -1 && @previous_offset != false
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# @return [
|
|
49
|
-
# for the offset.
|
|
48
|
+
# @return [Integer] -1 because it will then select the highest offsets
|
|
50
49
|
def first_offset
|
|
51
|
-
|
|
50
|
+
-1
|
|
52
51
|
end
|
|
53
52
|
|
|
54
53
|
# @return [Boolean] Active previous page link when it is not the first page
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Web
|
|
5
|
+
module Ui
|
|
6
|
+
module Lib
|
|
7
|
+
# Class used to execute code that can fail but we do not want to fail the whole operation.
|
|
8
|
+
# The primary use-case is for displaying deserialized data. We always need to assume, that
|
|
9
|
+
# part of the data can be corrupted and it should not crash the whole UI.
|
|
10
|
+
#
|
|
11
|
+
# It caches the result and does not run the code twice (only once)
|
|
12
|
+
class SafeRunner
|
|
13
|
+
attr_reader :error, :result
|
|
14
|
+
|
|
15
|
+
# @param block [Proc] code we want to safe-guard
|
|
16
|
+
def initialize(&block)
|
|
17
|
+
@code = block
|
|
18
|
+
@executed = false
|
|
19
|
+
@success = false
|
|
20
|
+
@error = nil
|
|
21
|
+
@result = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Boolean] was the code execution successful or not
|
|
25
|
+
def success?
|
|
26
|
+
return @success if executed?
|
|
27
|
+
|
|
28
|
+
call
|
|
29
|
+
|
|
30
|
+
@success
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @return [Boolean] was the code execution failed or not
|
|
34
|
+
def failure?
|
|
35
|
+
!success?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Runs the execution and returns block result
|
|
39
|
+
def call
|
|
40
|
+
return @result if executed?
|
|
41
|
+
|
|
42
|
+
@executed = true
|
|
43
|
+
@result = @code.call
|
|
44
|
+
@success = true
|
|
45
|
+
@result
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
@error = e
|
|
48
|
+
@success = false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @return [Boolean] was the code executed already or not yet
|
|
52
|
+
def executed?
|
|
53
|
+
@executed
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Web
|
|
5
|
+
module Ui
|
|
6
|
+
module Models
|
|
7
|
+
# Represents a single broker data within the cluster
|
|
8
|
+
class Broker < Lib::HashProxy
|
|
9
|
+
class << self
|
|
10
|
+
# @return [Array<Broker>] all brokers in the cluster
|
|
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|
|
|
14
|
+
new(broker)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Finds requested broker
|
|
19
|
+
#
|
|
20
|
+
# @param broker_id [String, Integer] id of the broker
|
|
21
|
+
# @return [Broker]
|
|
22
|
+
# @raise [::Karafka::Web::Errors::Ui::NotFoundError]
|
|
23
|
+
def find(broker_id)
|
|
24
|
+
found = all.find { |broker| broker.id.to_s == broker_id }
|
|
25
|
+
|
|
26
|
+
return found if found
|
|
27
|
+
|
|
28
|
+
raise(::Karafka::Web::Errors::Ui::NotFoundError, broker_id)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [Integer]
|
|
33
|
+
def id
|
|
34
|
+
broker_id
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [String]
|
|
38
|
+
def name
|
|
39
|
+
broker_name
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Integer]
|
|
43
|
+
def port
|
|
44
|
+
broker_port
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [String] full broker name for presentation
|
|
48
|
+
def full_name
|
|
49
|
+
"#{id} - #{name}:#{port}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [Array<Karafka::Admin::Configs::Config>] all broker configs
|
|
53
|
+
def configs
|
|
54
|
+
# We copy the array because the result one is frozen and we sort
|
|
55
|
+
@configs ||= ::Karafka::Admin::Configs.describe(
|
|
56
|
+
::Karafka::Admin::Configs::Resource.new(
|
|
57
|
+
type: :broker,
|
|
58
|
+
name: id
|
|
59
|
+
)
|
|
60
|
+
).first.configs.dup
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -8,7 +8,7 @@ module Karafka
|
|
|
8
8
|
class Health
|
|
9
9
|
class << self
|
|
10
10
|
# @param state [State] current system state
|
|
11
|
-
# @return [Hash]
|
|
11
|
+
# @return [Hash] hash with aggregated statistics
|
|
12
12
|
def current(state)
|
|
13
13
|
stats = {}
|
|
14
14
|
|
|
@@ -18,6 +18,32 @@ module Karafka
|
|
|
18
18
|
sort_structure(stats)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# @return [Hash] hash with cluster lag data
|
|
22
|
+
def cluster_lags_with_offsets
|
|
23
|
+
# We need to remap raw results so they comply with our sorting flows
|
|
24
|
+
mapped_lags = {}
|
|
25
|
+
|
|
26
|
+
::Karafka::Admin.read_lags_with_offsets(
|
|
27
|
+
active_topics_only: Web.config.ui.visibility.active_topics_cluster_lags_only
|
|
28
|
+
).each do |consumer_group, topics|
|
|
29
|
+
mapped_lags[consumer_group] ||= {}
|
|
30
|
+
|
|
31
|
+
topics.each do |topic_name, partitions_details|
|
|
32
|
+
mapped_lags[consumer_group][topic_name] ||= []
|
|
33
|
+
|
|
34
|
+
partitions_details.each do |partition_id, lags_with_offsets|
|
|
35
|
+
mapped_lags[consumer_group][topic_name] << {
|
|
36
|
+
id: partition_id,
|
|
37
|
+
lag: lags_with_offsets.fetch(:lag),
|
|
38
|
+
stored_offset: lags_with_offsets.fetch(:offset)
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
mapped_lags
|
|
45
|
+
end
|
|
46
|
+
|
|
21
47
|
private
|
|
22
48
|
|
|
23
49
|
# Aggregates data on a per topic basis (in the context of a consumer group)
|
|
@@ -62,7 +88,7 @@ module Karafka
|
|
|
62
88
|
#
|
|
63
89
|
# @param state [State]
|
|
64
90
|
def iterate_partitions(state)
|
|
65
|
-
# By default processes are sort by
|
|
91
|
+
# By default processes are sort by id and this is not what we want here
|
|
66
92
|
# We want to make sure that the newest data is processed the last, so we get
|
|
67
93
|
# the most accurate state in case of deployments and shutdowns, etc without the
|
|
68
94
|
# expired processes partitions data overwriting the newly created processes
|
|
@@ -100,10 +100,16 @@ module Karafka
|
|
|
100
100
|
|
|
101
101
|
previous_offset = start_offset + count
|
|
102
102
|
|
|
103
|
+
if previous_offset >= high_offset
|
|
104
|
+
previous_offset = false
|
|
105
|
+
elsif previous_offset + (per_page - 1) > high_offset
|
|
106
|
+
previous_offset = high_offset - per_page
|
|
107
|
+
else
|
|
108
|
+
previous_offset
|
|
109
|
+
end
|
|
110
|
+
|
|
103
111
|
return [
|
|
104
|
-
|
|
105
|
-
# offset
|
|
106
|
-
previous_offset >= high_offset ? false : previous_offset,
|
|
112
|
+
previous_offset,
|
|
107
113
|
fill_compacted(messages, partition_id, context_offset, context_count, high_offset).reverse,
|
|
108
114
|
next_offset
|
|
109
115
|
]
|
|
@@ -18,11 +18,6 @@ module Karafka
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
# @return [String] process id without the name and ip
|
|
22
|
-
def id
|
|
23
|
-
@id ||= name.split(':').last
|
|
24
|
-
end
|
|
25
|
-
|
|
26
21
|
# @return [Array<ConsumerGroup>] consumer groups to which this process is subscribed in
|
|
27
22
|
# an alphabetical order
|
|
28
23
|
def consumer_groups
|
|
@@ -41,6 +36,16 @@ module Karafka
|
|
|
41
36
|
.then { |jobs| Jobs.new(jobs) }
|
|
42
37
|
end
|
|
43
38
|
|
|
39
|
+
# @return [Integer] number of running jobs on a process
|
|
40
|
+
def running_jobs_count
|
|
41
|
+
jobs.running.count
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Integer] number of pending jobs on a process
|
|
45
|
+
def pending_jobs_count
|
|
46
|
+
jobs.pending.count
|
|
47
|
+
end
|
|
48
|
+
|
|
44
49
|
# @return [Integer] collective hybrid lag on this process
|
|
45
50
|
def lag_hybrid
|
|
46
51
|
consumer_groups
|
|
@@ -79,11 +79,11 @@ module Karafka
|
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
# Ensures that we always return processes sorted by their
|
|
82
|
+
# Ensures that we always return processes sorted by their id
|
|
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][:
|
|
86
|
+
processes.sort_by { |consumer| consumer[:process][:id] }
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
end
|
|
@@ -6,6 +6,28 @@ module Karafka
|
|
|
6
6
|
module Models
|
|
7
7
|
# Single topic data representation model
|
|
8
8
|
class Topic < Lib::HashProxy
|
|
9
|
+
class << self
|
|
10
|
+
# @return [Array<Broker>] all topics in the cluster
|
|
11
|
+
def all
|
|
12
|
+
ClusterInfo.fetch.topics.map do |topic|
|
|
13
|
+
new(topic)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Finds requested topic
|
|
18
|
+
#
|
|
19
|
+
# @param topic_name [String] name of the topic
|
|
20
|
+
# @return [Topic]
|
|
21
|
+
# @raise [::Karafka::Web::Errors::Ui::NotFoundError]
|
|
22
|
+
def find(topic_name)
|
|
23
|
+
found = all.find { |topic| topic.topic_name == topic_name }
|
|
24
|
+
|
|
25
|
+
return found if found
|
|
26
|
+
|
|
27
|
+
raise(::Karafka::Web::Errors::Ui::NotFoundError, topic_name)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
9
31
|
# @return [Array<Partition>] All topic partitions data
|
|
10
32
|
def partitions
|
|
11
33
|
super.map do |partition_id, partition_hash|
|
|
@@ -14,6 +36,62 @@ module Karafka
|
|
|
14
36
|
Partition.new(partition_hash)
|
|
15
37
|
end
|
|
16
38
|
end
|
|
39
|
+
|
|
40
|
+
# @return [Array<Karafka::Admin::Configs::Config>] all topic configs
|
|
41
|
+
def configs
|
|
42
|
+
@configs ||= ::Karafka::Admin::Configs.describe(
|
|
43
|
+
::Karafka::Admin::Configs::Resource.new(
|
|
44
|
+
type: :topic,
|
|
45
|
+
name: topic_name
|
|
46
|
+
)
|
|
47
|
+
).first.configs.dup
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Generates info about estimated messages distribution in partitions, allowing for
|
|
51
|
+
# inspection and detection of imbalances
|
|
52
|
+
#
|
|
53
|
+
# @param partitions [Array<Integer>] partitions we're interested in
|
|
54
|
+
#
|
|
55
|
+
# @return [Array<HashProxy, Array<HashProxy>>] array where first value contains
|
|
56
|
+
# aggregated statistics and then the second value is an array with per partition data
|
|
57
|
+
def distribution(partitions)
|
|
58
|
+
sum = 0.0
|
|
59
|
+
avg = 0.0
|
|
60
|
+
|
|
61
|
+
counts = partitions.map do |partition_id|
|
|
62
|
+
offsets = Admin.read_watermark_offsets(topic_name, partition_id)
|
|
63
|
+
count = offsets.last - offsets.first
|
|
64
|
+
|
|
65
|
+
sum += count
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
count: count,
|
|
69
|
+
partition_id: partition_id
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
avg = sum / counts.size
|
|
74
|
+
|
|
75
|
+
counts.each do |part_stats|
|
|
76
|
+
count = part_stats[:count]
|
|
77
|
+
|
|
78
|
+
part_stats[:share] = ((count / sum) * 100).round(2)
|
|
79
|
+
part_stats[:diff] = ((count - avg) / avg) * 100
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
variance = counts
|
|
83
|
+
.map { |part_stats| part_stats[:count] }
|
|
84
|
+
.sum { |count| (count - avg)**2 } / counts.size
|
|
85
|
+
|
|
86
|
+
std_dev = Math.sqrt(variance)
|
|
87
|
+
std_dev_rel = ((std_dev / avg) * 100).round(2)
|
|
88
|
+
|
|
89
|
+
[
|
|
90
|
+
# round stdev since its message count
|
|
91
|
+
Lib::HashProxy.new(std_dev: std_dev.round, std_dev_rel: std_dev_rel, sum: sum),
|
|
92
|
+
counts.map { |part_stats| Lib::HashProxy.new(part_stats) }
|
|
93
|
+
]
|
|
94
|
+
end
|
|
17
95
|
end
|
|
18
96
|
end
|
|
19
97
|
end
|
|
@@ -18,6 +18,18 @@ function updateTimeAgo() {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// Cheap way to do breadcrumbs
|
|
22
|
+
function refreshTitle() {
|
|
23
|
+
const breadcrumbs = document.querySelectorAll('.breadcrumb 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
|
+
|
|
21
33
|
// To prevent from flickering, the UI is initially hidden and visible when all the JS components
|
|
22
34
|
// are fully initialized
|
|
23
35
|
function displayUi() {
|
|
@@ -66,11 +78,16 @@ function addListeners() {
|
|
|
66
78
|
hljs.highlightAll();
|
|
67
79
|
updateTimeAgo();
|
|
68
80
|
redirectToPartition();
|
|
69
|
-
|
|
81
|
+
|
|
82
|
+
const tabsManager = new TabsManager();
|
|
83
|
+
tabsManager.manageTabs();
|
|
84
|
+
|
|
70
85
|
manageCharts();
|
|
71
86
|
bindActionsConfirmations();
|
|
72
87
|
loadOffsetLookupDatePicker();
|
|
73
88
|
displayUi();
|
|
89
|
+
|
|
90
|
+
refreshTitle()
|
|
74
91
|
}
|
|
75
92
|
|
|
76
93
|
var ready = (callback) => {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const DataFormattingUtils = {
|
|
2
|
+
niceBytes(x, precision = 2) {
|
|
3
|
+
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
4
|
+
let l = 0, n = parseInt(x, 10) || 0;
|
|
5
|
+
|
|
6
|
+
while (n >= 1024 && ++l) {
|
|
7
|
+
n /= 1024;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return `${n.toFixed(n < 10 && l > 0 ? 1 : precision)} ${units[l]}`;
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
formatLabelX(value, type) {
|
|
14
|
+
switch (type) {
|
|
15
|
+
case 'date':
|
|
16
|
+
let date = new Date(value * 1000)
|
|
17
|
+
let date_str =
|
|
18
|
+
("00" + (date.getMonth() + 1)).slice(-2) + "/" +
|
|
19
|
+
("00" + date.getDate()).slice(-2) + "/" +
|
|
20
|
+
date.getFullYear() + " " +
|
|
21
|
+
("00" + date.getHours()).slice(-2) + ":" +
|
|
22
|
+
("00" + date.getMinutes()).slice(-2) + ":" +
|
|
23
|
+
("00" + date.getSeconds()).slice(-2);
|
|
24
|
+
|
|
25
|
+
return date_str
|
|
26
|
+
default:
|
|
27
|
+
return value
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
formatTooltip(type, tooltipItem) {
|
|
32
|
+
let value = tooltipItem.parsed.y;
|
|
33
|
+
let label = tooltipItem.dataset.label;
|
|
34
|
+
|
|
35
|
+
switch(type) {
|
|
36
|
+
case 'percentage':
|
|
37
|
+
if (Math.floor(value) === value) {
|
|
38
|
+
return label + ': ' + value + ' %';
|
|
39
|
+
} else {
|
|
40
|
+
return label + ': ' + (Math.round(value * 100) / 100) + ' %';
|
|
41
|
+
}
|
|
42
|
+
case 'memory':
|
|
43
|
+
return label + ': ' + DataFormattingUtils.niceBytes(value * 1024, 2);
|
|
44
|
+
default:
|
|
45
|
+
return tooltipItem.yLabel
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
formatLabelY(type, label) {
|
|
50
|
+
switch(type) {
|
|
51
|
+
case 'percentage':
|
|
52
|
+
if (Math.floor(label) === label) {
|
|
53
|
+
return label + '%'
|
|
54
|
+
} else {
|
|
55
|
+
return (Math.round(label * 100) / 100) + '%'
|
|
56
|
+
}
|
|
57
|
+
case 'memory':
|
|
58
|
+
return DataFormattingUtils.niceBytes(label * 1024, 1)
|
|
59
|
+
default:
|
|
60
|
+
if (Math.floor(label) === label) {
|
|
61
|
+
return label
|
|
62
|
+
} else {
|
|
63
|
+
return Math.round(label * 100) / 100
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
isFractionalPrecision(value) {
|
|
69
|
+
return value !== Math.floor(value);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class DatasetStateManager {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.storageKey = 'karafkaDisabledDatasets';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// Reads all disabled datasets from localStorage
|
|
7
|
+
readAll() {
|
|
8
|
+
const raw = localStorage.getItem(this.storageKey);
|
|
9
|
+
return raw ? JSON.parse(raw) : {};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Saves all disabled datasets to localStorage
|
|
13
|
+
saveAll(data) {
|
|
14
|
+
localStorage.setItem(this.storageKey, JSON.stringify(data));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Saves the current disabled datasets for all '.chartjs-line' charts
|
|
18
|
+
saveCurrent() {
|
|
19
|
+
const charts = document.querySelectorAll('.chartjs');
|
|
20
|
+
const url = window.location.href.split('?')[0];
|
|
21
|
+
let currentDisabled = {};
|
|
22
|
+
let allDisabled = this.readAll();
|
|
23
|
+
|
|
24
|
+
charts.forEach(chart => {
|
|
25
|
+
const chartId = chart.id;
|
|
26
|
+
const chartInstance = Chart.getChart(chartId);
|
|
27
|
+
if (!chartInstance || !chartInstance.legend || !chartInstance.legend.legendItems) return;
|
|
28
|
+
|
|
29
|
+
let disabledIndices = chartInstance.legend.legendItems
|
|
30
|
+
.map((item, index) => item.hidden ? index : null)
|
|
31
|
+
.filter(index => index !== null);
|
|
32
|
+
|
|
33
|
+
if (disabledIndices.length > 0) {
|
|
34
|
+
currentDisabled[chartId] = disabledIndices;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
allDisabled[url] = currentDisabled;
|
|
39
|
+
this.saveAll(allDisabled);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Retrieves the disabled datasets for a specific chart ID
|
|
43
|
+
getCurrentChart(chartId) {
|
|
44
|
+
const url = window.location.href.split('?')[0];
|
|
45
|
+
let allDisabled = this.readAll();
|
|
46
|
+
let currentDisabled = allDisabled[url] || {};
|
|
47
|
+
return currentDisabled[chartId] || [];
|
|
48
|
+
}
|
|
49
|
+
}
|