karafka-web 0.8.2 → 0.9.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|