solid_observer 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/README.md +198 -36
  4. data/app/assets/javascripts/solid_observer/live_poll.js +376 -0
  5. data/app/controllers/concerns/solid_observer/paginatable.rb +17 -0
  6. data/app/controllers/concerns/solid_observer/require_persistence_mode.rb +19 -0
  7. data/app/controllers/concerns/solid_observer/require_solid_queue.rb +19 -0
  8. data/app/controllers/solid_observer/application_controller.rb +69 -0
  9. data/app/controllers/solid_observer/dashboard_controller.rb +79 -0
  10. data/app/controllers/solid_observer/events_controller.rb +50 -0
  11. data/app/controllers/solid_observer/jobs_controller.rb +85 -0
  12. data/app/controllers/solid_observer/storages_controller.rb +12 -0
  13. data/app/helpers/solid_observer/application_helper.rb +95 -0
  14. data/app/helpers/solid_observer/dashboard_helper.rb +39 -0
  15. data/app/models/solid_observer/queue_event.rb +134 -0
  16. data/app/models/solid_observer/queue_metric.rb +1 -1
  17. data/app/presenters/solid_observer/execution_presenter.rb +50 -0
  18. data/app/views/layouts/solid_observer/application.html.erb +470 -0
  19. data/app/views/solid_observer/dashboard/_chart.html.erb +28 -0
  20. data/app/views/solid_observer/dashboard/_live_state.html.erb +20 -0
  21. data/app/views/solid_observer/dashboard/_queue_table.html.erb +34 -0
  22. data/app/views/solid_observer/dashboard/_right_now.html.erb +3 -0
  23. data/app/views/solid_observer/dashboard/_throughput.html.erb +32 -0
  24. data/app/views/solid_observer/dashboard/index.html.erb +113 -0
  25. data/app/views/solid_observer/errors/storage_unavailable.html.erb +27 -0
  26. data/app/views/solid_observer/events/index.html.erb +53 -0
  27. data/app/views/solid_observer/events/show.html.erb +47 -0
  28. data/app/views/solid_observer/jobs/index.html.erb +61 -0
  29. data/app/views/solid_observer/jobs/show.html.erb +71 -0
  30. data/app/views/solid_observer/shared/_empty_state.html.erb +5 -0
  31. data/app/views/solid_observer/shared/_pagination.html.erb +17 -0
  32. data/app/views/solid_observer/shared/_stat_card.html.erb +9 -0
  33. data/app/views/solid_observer/storages/show.html.erb +39 -0
  34. data/bin/quality_gate +95 -0
  35. data/config/routes.rb +17 -0
  36. data/db/migrate/20260424000001_add_composite_indexes_to_queue_events.rb +30 -0
  37. data/lib/generators/solid_observer/install_generator.rb +12 -25
  38. data/lib/generators/solid_observer/templates/initializer.rb.tt +5 -6
  39. data/lib/solid_observer/base_metric.rb +1 -1
  40. data/lib/solid_observer/chart_buffer.rb +83 -0
  41. data/lib/solid_observer/cli/base.rb +2 -2
  42. data/lib/solid_observer/cli/jobs.rb +2 -2
  43. data/lib/solid_observer/cli/status.rb +20 -2
  44. data/lib/solid_observer/cli/storage.rb +41 -32
  45. data/lib/solid_observer/configuration.rb +67 -34
  46. data/lib/solid_observer/correlation_id_resolver.rb +8 -6
  47. data/lib/solid_observer/engine.rb +75 -15
  48. data/lib/solid_observer/params/events_filter.rb +37 -0
  49. data/lib/solid_observer/params/jobs_filter.rb +35 -0
  50. data/lib/solid_observer/queries/events_query.rb +27 -0
  51. data/lib/solid_observer/queries/execution_finder.rb +42 -0
  52. data/lib/solid_observer/queries/job_executions_query.rb +73 -0
  53. data/lib/solid_observer/queue_event_buffer.rb +163 -22
  54. data/lib/solid_observer/queue_stats.rb +165 -19
  55. data/lib/solid_observer/services/cleanup_storage.rb +60 -42
  56. data/lib/solid_observer/services/database_size.rb +86 -0
  57. data/lib/solid_observer/services/flush_event_buffer.rb +31 -15
  58. data/lib/solid_observer/services/install_migrations.rb +49 -0
  59. data/lib/solid_observer/services/record_event.rb +53 -14
  60. data/lib/solid_observer/services/ui_auth_check.rb +65 -0
  61. data/lib/solid_observer/subscriber.rb +15 -8
  62. data/lib/solid_observer/version.rb +1 -1
  63. data/lib/solid_observer.rb +7 -0
  64. data/lib/tasks/solid_observer.rake +10 -2
  65. metadata +55 -1
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "database_size"
4
+
3
5
  module SolidObserver
4
6
  module Services
5
7
  class CleanupStorage
@@ -8,86 +10,102 @@ module SolidObserver
8
10
  end
9
11
 
10
12
  def call
11
- deleted_count = 0
13
+ return 0 if SolidObserver.config.realtime_mode?
14
+
15
+ deleted_count = perform_cleanup_transaction
16
+ post_cleanup(deleted_count)
17
+ rescue => e
18
+ handle_cleanup_failure(e)
19
+ end
20
+
21
+ private
22
+
23
+ def handle_cleanup_failure(error)
24
+ Rails.logger.error "[SolidObserver] Cleanup failed: #{error.message}"
25
+ raise
26
+ end
12
27
 
28
+ def perform_cleanup_transaction
13
29
  QueueEvent.transaction do
14
30
  deleted_count = delete_old_events
15
31
  record_snapshot_after_cleanup
32
+ deleted_count
16
33
  end
34
+ end
17
35
 
36
+ def post_cleanup(deleted_count)
18
37
  vacuum_database
19
-
20
38
  check_storage_warnings
21
39
  log_results(deleted_count)
22
-
23
40
  deleted_count
24
- rescue => e
25
- Rails.logger.error "[SolidObserver] Cleanup failed: #{e.message}"
26
- raise
27
41
  end
28
42
 
29
- private
30
-
31
43
  def delete_old_events
32
44
  cutoff = SolidObserver.config.event_retention.ago
33
45
  QueueEvent.where("recorded_at < ?", cutoff).delete_all
34
46
  end
35
47
 
36
48
  def record_snapshot_after_cleanup
37
- db_size = calculate_database_size
38
- event_count = QueueEvent.count
39
-
49
+ # StorageInfo.db_size_bytes is NOT NULL; record_snapshot coerces nil to 0.
40
50
  StorageInfo.record_snapshot(
41
- db_size: db_size,
42
- event_count: event_count
51
+ db_size: current_database_size,
52
+ event_count: QueueEvent.count
43
53
  )
44
54
  end
45
55
 
46
56
  def vacuum_database
47
- adapter = QueueEvent.connection.adapter_name.downcase
48
- case adapter
49
- when "sqlite"
50
- QueueEvent.connection.execute("VACUUM")
51
- when "postgresql"
52
- QueueEvent.connection.execute("VACUUM ANALYZE solid_observer_queue_events")
53
- when "mysql2", "trilogy"
54
- QueueEvent.connection.execute("OPTIMIZE TABLE solid_observer_queue_events")
55
- end
57
+ statement = maintenance_statement
58
+ return unless statement
59
+
60
+ QueueEvent.connection.execute(statement)
56
61
  rescue => e
57
62
  Rails.logger.warn "[SolidObserver] Database maintenance failed: #{e.message}"
58
63
  end
59
64
 
60
- def calculate_database_size
61
- db_path = QueueEvent.connection_db_config.database
62
- File.size(db_path) if File.exist?(db_path)
63
- rescue => e
64
- Rails.logger.warn "[SolidObserver] Could not calculate DB size: #{e.message}"
65
- 0
65
+ def check_storage_warnings
66
+ current_size = current_database_size
67
+ return unless warning_needed?(current_size)
68
+
69
+ Rails.logger.warn(storage_warning_message(current_size))
66
70
  end
67
71
 
68
- def check_storage_warnings
69
- max_size = SolidObserver.config.max_db_size
70
- threshold = SolidObserver.config.warning_threshold
71
- current_size = calculate_database_size
72
+ def warning_needed?(current_size)
73
+ return false unless current_size
72
74
 
73
- return unless current_size > (max_size * threshold)
75
+ config = SolidObserver.config
76
+ max_size = config.max_db_size
77
+ threshold = config.warning_threshold
78
+ current_size > (max_size * threshold)
79
+ end
74
80
 
81
+ def storage_warning_message(current_size)
82
+ max_size = SolidObserver.config.max_db_size
75
83
  percentage = ((current_size.to_f / max_size) * 100).round(1)
76
- Rails.logger.warn "[SolidObserver] Queue DB approaching limit: #{format_bytes(current_size)} / #{format_bytes(max_size)} (#{percentage}%)"
84
+ current_size_human = human_size(current_size)
85
+ max_size_human = human_size(max_size)
86
+ "[SolidObserver] Queue DB approaching limit: #{current_size_human} / #{max_size_human} (#{percentage}%)"
77
87
  end
78
88
 
79
- def log_results(deleted_count)
80
- Rails.logger.info "[SolidObserver] Cleaned #{deleted_count} queue events"
89
+ def human_size(bytes)
90
+ ActiveSupport::NumberHelper.number_to_human_size(bytes, precision: 1, significant: false, strip_insignificant_zeros: false)
81
91
  end
82
92
 
83
- def format_bytes(bytes)
84
- return "0 B" if bytes.zero?
93
+ def current_database_size
94
+ return @current_database_size if defined?(@current_database_size)
95
+
96
+ @current_database_size = DatabaseSize.call(connection: QueueEvent.connection)
97
+ end
85
98
 
86
- units = ["B", "KB", "MB", "GB"]
87
- exp = (Math.log(bytes) / Math.log(1024)).to_i
88
- exp = [exp, units.length - 1].min
99
+ def log_results(deleted_count)
100
+ Rails.logger.info "[SolidObserver] Cleaned #{deleted_count} queue events"
101
+ end
89
102
 
90
- "%.1f %s" % [bytes.to_f / (1024**exp), units[exp]]
103
+ def maintenance_statement
104
+ case QueueEvent.connection.adapter_name.downcase
105
+ when "sqlite" then "VACUUM"
106
+ when "postgresql" then "VACUUM ANALYZE solid_observer_queue_events"
107
+ when "mysql2", "trilogy" then "OPTIMIZE TABLE solid_observer_queue_events"
108
+ end
91
109
  end
92
110
  end
93
111
  end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidObserver
4
+ module Services
5
+ # Returns bytes used by solid_observer_queue_events across supported adapters.
6
+ #
7
+ # SQLite uses whole-database page accounting; PostgreSQL and MySQL/Trilogy
8
+ # use table + index size from adapter-native system functions.
9
+ class DatabaseSize
10
+ TABLE_NAME = "solid_observer_queue_events"
11
+
12
+ def self.call(connection:)
13
+ new(connection).call
14
+ end
15
+
16
+ def initialize(connection)
17
+ @connection = connection
18
+ end
19
+
20
+ def call
21
+ fetch_size
22
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::ConnectionNotEstablished => e
23
+ log_query_failure(e.message)
24
+ nil
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :connection
30
+
31
+ def adapter_key
32
+ case connection.adapter_name.to_s.downcase
33
+ when /sqlite/ then :sqlite
34
+ when /postgres|postgis/ then :postgresql
35
+ when "mysql2", "trilogy" then :mysql
36
+ end
37
+ end
38
+
39
+ def fetch_size
40
+ case adapter_key
41
+ when :sqlite then sqlite_size
42
+ when :postgresql then postgresql_size
43
+ when :mysql then mysql_size
44
+ else
45
+ unknown_adapter_size
46
+ end
47
+ end
48
+
49
+ def unknown_adapter_size
50
+ log_unknown_adapter
51
+ nil
52
+ end
53
+
54
+ def sqlite_size
55
+ connection.query_value("SELECT pragma_page_count() * pragma_page_size()")&.to_i
56
+ end
57
+
58
+ def postgresql_size
59
+ quoted_table = connection.quote(TABLE_NAME)
60
+ connection.query_value("SELECT pg_total_relation_size(#{quoted_table})")&.to_i
61
+ end
62
+
63
+ def mysql_size
64
+ quoted_table = connection.quote(TABLE_NAME)
65
+
66
+ connection.query_value(<<~SQL)&.to_i
67
+ SELECT COALESCE(data_length + index_length, 0)
68
+ FROM information_schema.tables
69
+ WHERE table_schema = DATABASE()
70
+ AND table_name = #{quoted_table}
71
+ SQL
72
+ end
73
+
74
+ def log_unknown_adapter
75
+ Rails.logger&.warn(
76
+ "[SolidObserver] Unknown adapter for DatabaseSize: " \
77
+ "#{connection.adapter_name.inspect} — storage monitoring disabled"
78
+ )
79
+ end
80
+
81
+ def log_query_failure(message)
82
+ Rails.logger&.warn("[SolidObserver] DatabaseSize query failed: #{message}")
83
+ end
84
+ end
85
+ end
86
+ end
@@ -26,31 +26,47 @@ module SolidObserver
26
26
  def call
27
27
  return 0 if @events.empty?
28
28
 
29
+ bulk_insert
30
+ @events.size
31
+ rescue ActiveRecord::RecordInvalid, ActiveRecord::StatementInvalid => e
32
+ handle_bulk_insert_failure(e)
33
+ end
34
+
35
+ private
36
+
37
+ def bulk_insert
29
38
  QueueEvent.transaction do
30
39
  QueueEvent.insert_all!(@events)
31
40
  end
41
+ end
32
42
 
33
- @events.size
34
- rescue ActiveRecord::RecordInvalid, ActiveRecord::StatementInvalid => e
35
- log_error("Bulk insert failed, retrying in batches: #{e.message}")
43
+ def handle_bulk_insert_failure(error)
44
+ log_error("Bulk insert failed, retrying in batches: #{error.message}")
36
45
  retry_with_smaller_batches
37
46
  end
38
47
 
39
- private
40
-
41
48
  def retry_with_smaller_batches
42
- inserted = 0
49
+ inserted = @events.each_slice(BATCH_SIZE).sum { |batch| insert_batch(batch) }
50
+ log_failed_count if @failed_count.positive?
51
+ inserted
52
+ end
43
53
 
44
- @events.each_slice(BATCH_SIZE) do |batch|
45
- QueueEvent.insert_all(batch, returning: false)
46
- inserted += batch.size
47
- rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordInvalid => e
48
- @failed_count += batch.size
49
- log_warning("Failed to insert batch of #{batch.size} events: #{e.message}")
50
- end
54
+ def insert_batch(batch)
55
+ QueueEvent.insert_all(batch, returning: false)
56
+ batch.size
57
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordInvalid => e
58
+ register_failed_batch(batch, e)
59
+ 0
60
+ end
51
61
 
52
- log_warning("#{@failed_count} events could not be saved") if @failed_count.positive?
53
- inserted
62
+ def register_failed_batch(batch, error)
63
+ batch_size = batch.size
64
+ @failed_count += batch_size
65
+ log_warning("Failed to insert batch of #{batch_size} events: #{error.message}")
66
+ end
67
+
68
+ def log_failed_count
69
+ log_warning("#{@failed_count} events could not be saved")
54
70
  end
55
71
 
56
72
  def log_error(message)
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidObserver
4
+ module Services
5
+ class InstallMigrations
6
+ def self.call(rails_env: Rails.env)
7
+ new(rails_env).call
8
+ end
9
+
10
+ def initialize(rails_env)
11
+ @rails_env = rails_env
12
+ end
13
+
14
+ def call
15
+ destination = resolve_destination
16
+ copied = ActiveRecord::Migration.copy(
17
+ destination,
18
+ "solid_observer" => SolidObserver::Engine.paths["db/migrate"].existent.first
19
+ )
20
+
21
+ {
22
+ destination: destination,
23
+ copied: copied
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ def resolve_destination
30
+ path = configured_path
31
+ return path if path
32
+
33
+ ActiveRecord::Tasks::DatabaseTasks.migrations_paths.first || "db/migrate"
34
+ end
35
+
36
+ def configured_path
37
+ config = ActiveRecord::Base.configurations.configs_for(
38
+ env_name: @rails_env,
39
+ name: "solid_observer_queue"
40
+ )
41
+ path = Array(config&.migrations_paths).first
42
+ return if path.blank?
43
+
44
+ FileUtils.mkdir_p(path) unless Dir.exist?(path)
45
+ path
46
+ end
47
+ end
48
+ end
49
+ end
@@ -54,33 +54,72 @@ module SolidObserver
54
54
  job_class: metadata[:job_class],
55
55
  queue_name: metadata[:queue_name],
56
56
  correlation_id: CorrelationIdResolver.resolve(@event),
57
- duration: @event.duration,
57
+ duration: duration_in_seconds,
58
58
  metadata: metadata.except(:job_class, :queue_name).to_json,
59
59
  recorded_at: Time.current
60
60
  }
61
61
  end
62
62
 
63
+ def duration_in_seconds
64
+ @event.duration&./(1000.0)
65
+ end
66
+
63
67
  def extract_metadata
64
68
  payload = @event.payload || {}
65
- exception_obj = payload[:exception_object]
66
-
67
- {
68
- job_id: payload.dig(:job, :job_id),
69
- job_class: payload.dig(:job, :class_name) || payload.dig(:job, :job_class),
70
- queue_name: payload.dig(:job, :queue_name),
71
- arguments: payload.dig(:job, :arguments),
72
- executions: payload.dig(:job, :executions),
73
- exception_class: exception_obj&.class&.name || payload[:exception]&.first,
74
- exception_message: exception_obj&.message || payload[:exception]&.last,
75
- enqueued_at: payload.dig(:job, :enqueued_at),
76
- priority: payload.dig(:job, :priority)
77
- }.compact
69
+ metadata_from(payload, payload[:job]).compact
78
70
  rescue => e
79
71
  Rails.logger.warn "[SolidObserver] Failed to extract metadata: #{e.message}" if defined?(Rails)
80
72
  {}
81
73
  end
82
74
 
75
+ def metadata_from(payload, job)
76
+ {
77
+ job_id: read_job_attr(job, :job_id),
78
+ job_class: read_job_class(job),
79
+ queue_name: read_job_attr(job, :queue_name),
80
+ executions: read_job_attr(job, :executions),
81
+ exception_class: exception_class(payload),
82
+ exception_message: exception_message(payload),
83
+ enqueued_at: read_job_attr(job, :enqueued_at),
84
+ priority: read_job_attr(job, :priority)
85
+ }
86
+ end
87
+
88
+ def read_job_attr(job, attr)
89
+ return nil unless job
90
+
91
+ if job.is_a?(Hash)
92
+ job[attr]
93
+ else
94
+ job.public_send(attr)
95
+ end
96
+ rescue NoMethodError
97
+ nil
98
+ end
99
+
100
+ def read_job_class(job)
101
+ return nil unless job
102
+
103
+ if job.is_a?(Hash)
104
+ job[:class_name] || job[:job_class]
105
+ else
106
+ job.class.name
107
+ end
108
+ end
109
+
110
+ def exception_class(payload)
111
+ exception_obj = payload[:exception_object]
112
+ exception_obj&.class&.name || payload[:exception]&.first
113
+ end
114
+
115
+ def exception_message(payload)
116
+ exception_obj = payload[:exception_object]
117
+ exception_obj&.message || payload[:exception]&.last
118
+ end
119
+
83
120
  def increment_metric
121
+ return unless SolidObserver.config.persistence_mode?
122
+
84
123
  period = Time.current.beginning_of_hour
85
124
  QueueMetric.increment(metric: @metric_name, period: period)
86
125
  rescue => e
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidObserver
4
+ module Services
5
+ # Logs a boot-time WARNING when the Web UI's HTTP Basic Auth is misconfigured.
6
+ #
7
+ # No-ops when the UI is disabled or both credentials are set. Otherwise logs
8
+ # one of two warnings: "no auth configured" (neither credential set) or
9
+ # "auth misconfigured" (exactly one set, naming the missing one).
10
+ #
11
+ # The UI ships fail-open on partial credentials — see
12
+ # SolidObserver::ApplicationController#authenticate.
13
+ class UiAuthCheck
14
+ def self.call(config:, logger: Rails.logger)
15
+ new(config, logger).call
16
+ end
17
+
18
+ def initialize(config, logger)
19
+ @config = config
20
+ @logger = logger
21
+ end
22
+
23
+ def call
24
+ return unless config.ui_enabled
25
+
26
+ warning = warning_message
27
+ logger.warn(warning) if warning
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :config, :logger
33
+
34
+ def warning_message
35
+ return nil if both_credentials_present?
36
+ return no_auth_warning if neither_credential_present?
37
+
38
+ partial_auth_warning
39
+ end
40
+
41
+ def both_credentials_present?
42
+ config.ui_username.present? && config.ui_password.present?
43
+ end
44
+
45
+ def neither_credential_present?
46
+ config.ui_username.blank? && config.ui_password.blank?
47
+ end
48
+
49
+ def no_auth_warning
50
+ "[SolidObserver] WARNING: UI is enabled with no authentication configured. " \
51
+ "Set config.ui_username and config.ui_password."
52
+ end
53
+
54
+ def partial_auth_warning
55
+ set, missing = partial_credential_names
56
+ "[SolidObserver] WARNING: UI authentication is misconfigured — #{set} is set but #{missing} is missing/nil. " \
57
+ "The UI will ship UNAUTHENTICATED until both are configured. Set both credentials, or unset both to silence this warning."
58
+ end
59
+
60
+ def partial_credential_names
61
+ config.ui_username.present? ? %w[ui_username ui_password] : %w[ui_password ui_username]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -18,15 +18,9 @@ module SolidObserver
18
18
 
19
19
  class << self
20
20
  def subscribe!
21
- return unless SolidObserver.config.observe_queue
22
- return if subscribed?
21
+ return unless subscription_allowed?
23
22
 
24
- @subscriptions = []
25
- @subscriptions << subscribe_to_enqueue
26
- @subscriptions << subscribe_to_perform
27
- @subscriptions << subscribe_to_retry_stopped
28
- @subscriptions << subscribe_to_discard
29
- @subscriptions.compact!
23
+ @subscriptions = subscriptions_for_events.compact
30
24
  end
31
25
 
32
26
  def unsubscribe!
@@ -44,6 +38,19 @@ module SolidObserver
44
38
 
45
39
  private
46
40
 
41
+ def subscription_allowed?
42
+ SolidObserver.config.observe_queue && !subscribed?
43
+ end
44
+
45
+ def subscriptions_for_events
46
+ [
47
+ subscribe_to_enqueue,
48
+ subscribe_to_perform,
49
+ subscribe_to_retry_stopped,
50
+ subscribe_to_discard
51
+ ]
52
+ end
53
+
47
54
  def subscribe_to_enqueue
48
55
  ActiveSupport::Notifications.subscribe("enqueue.active_job") do |*args|
49
56
  event = ActiveSupport::Notifications::Event.new(*args)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidObserver
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  RUBY_MINIMUM_VERSION = "3.2.0"
6
6
  RAILS_MINIMUM_VERSION = "8.0"
7
7
  end
@@ -5,9 +5,15 @@ require_relative "solid_observer/configuration"
5
5
  require_relative "solid_observer/correlation_id_resolver"
6
6
  require_relative "solid_observer/base_event" if defined?(ActiveRecord)
7
7
  require_relative "solid_observer/base_metric" if defined?(ActiveRecord)
8
+ require_relative "solid_observer/params/jobs_filter"
9
+ require_relative "solid_observer/params/events_filter"
10
+ require_relative "solid_observer/queries/job_executions_query" if defined?(ActiveRecord)
11
+ require_relative "solid_observer/queries/events_query" if defined?(ActiveRecord)
12
+ require_relative "solid_observer/queries/execution_finder" if defined?(ActiveRecord)
8
13
  require_relative "solid_observer/services/record_event" if defined?(ActiveRecord)
9
14
  require_relative "solid_observer/services/flush_event_buffer" if defined?(ActiveRecord)
10
15
  require_relative "solid_observer/services/cleanup_storage" if defined?(ActiveRecord)
16
+ require_relative "solid_observer/services/ui_auth_check"
11
17
  require_relative "solid_observer/queue_event_buffer" if defined?(ActiveRecord)
12
18
  require_relative "solid_observer/subscriber" if defined?(ActiveSupport)
13
19
  require_relative "solid_observer/cli/base"
@@ -15,6 +21,7 @@ require_relative "solid_observer/cli/status"
15
21
  require_relative "solid_observer/cli/storage"
16
22
  require_relative "solid_observer/cli/jobs"
17
23
  require_relative "solid_observer/queue_stats"
24
+ require_relative "../app/presenters/solid_observer/execution_presenter"
18
25
  require_relative "solid_observer/engine" if defined?(Rails::Engine)
19
26
 
20
27
  module SolidObserver
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../solid_observer/services/install_migrations"
4
+
3
5
  namespace :solid_observer do
4
6
  desc "Display SolidObserver version"
5
7
  task :version do
@@ -11,8 +13,14 @@ namespace :solid_observer do
11
13
  namespace :install do
12
14
  desc "Copy SolidObserver migrations to your application"
13
15
  task migrations: :environment do
14
- Rake::Task["railties:install:migrations"].reenable
15
- Rake::Task["railties:install:migrations"].invoke
16
+ result = SolidObserver::Services::InstallMigrations.call
17
+
18
+ if result[:copied].any?
19
+ suffix = (result[:copied].size == 1) ? "" : "s"
20
+ puts "Copied #{result[:copied].size} SolidObserver migration#{suffix} to #{result[:destination]}/"
21
+ else
22
+ puts "No new SolidObserver migrations to copy (all already present in #{result[:destination]}/)"
23
+ end
16
24
  end
17
25
  end
18
26