rails_spotlight 0.4.2 → 0.5.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -4
- data/Dockerfile +1 -2
- data/Dockerfile-rails-6.0 +2 -2
- data/Dockerfile-rails-6.1 +2 -2
- data/Dockerfile-rails-7.1 +44 -0
- data/{Dockerfile-rails-7.0 → Dockerfile-rails-8.0} +3 -3
- data/{Dockerfile-rails-5.2 → Dockerfile-rails-8.0.2} +10 -5
- data/README.md +47 -15
- data/docker-compose.yml +14 -9
- data/docs/assets/images/sql_execution_toggle.gif +0 -0
- data/fake_spec_res/config/rails_spotlight.yml +47 -0
- data/fake_spec_res/rails_spotlight_spec.rb +23 -5
- data/lib/rails_spotlight/app_notifications.rb +2 -3
- data/lib/rails_spotlight/channels/handlers/{live_console_handler.rb → console_handler.rb} +3 -3
- data/lib/rails_spotlight/channels/handlers/logs_handler.rb +1 -1
- data/lib/rails_spotlight/channels/handlers.rb +3 -3
- data/lib/rails_spotlight/channels/spotlight_channel.rb +20 -1
- data/lib/rails_spotlight/configuration.rb +40 -58
- data/lib/rails_spotlight/event.rb +21 -5
- data/lib/rails_spotlight/log_interceptor.rb +34 -57
- data/lib/rails_spotlight/middlewares/handlers/base_action_handler.rb +45 -55
- data/lib/rails_spotlight/middlewares/handlers/code_analysis_action_handler.rb +8 -13
- data/lib/rails_spotlight/middlewares/handlers/console_action_handler.rb +8 -12
- data/lib/rails_spotlight/middlewares/handlers/directory_index_action_handler.rb +16 -27
- data/lib/rails_spotlight/middlewares/handlers/file_action_handler.rb +21 -55
- data/lib/rails_spotlight/middlewares/handlers/meta_action_handler.rb +3 -8
- data/lib/rails_spotlight/middlewares/handlers/not_found_action_handler.rb +1 -1
- data/lib/rails_spotlight/middlewares/handlers/sql_action_handler.rb +18 -35
- data/lib/rails_spotlight/middlewares/handlers/verify_action_handler.rb +1 -3
- data/lib/rails_spotlight/middlewares/header_marker.rb +1 -3
- data/lib/rails_spotlight/middlewares/request_completed.rb +2 -2
- data/lib/rails_spotlight/rails_command_executor.rb +3 -5
- data/lib/rails_spotlight/railtie.rb +2 -4
- data/lib/rails_spotlight/render_view_reporter.rb +3 -3
- data/lib/rails_spotlight/storage.rb +4 -14
- data/lib/rails_spotlight/utils.rb +1 -1
- data/lib/rails_spotlight/version.rb +1 -1
- data/lib/tasks/init.rake +26 -6
- metadata +16 -16
@@ -4,12 +4,14 @@ require 'yaml'
|
|
4
4
|
require 'erb'
|
5
5
|
|
6
6
|
module RailsSpotlight
|
7
|
-
class Configuration
|
7
|
+
class Configuration
|
8
8
|
DEFAULT_NOT_ENCODABLE_EVENT_VALUES = {
|
9
9
|
'ActiveRecord' => [
|
10
10
|
'ActiveRecord::ConnectionAdapters::AbstractAdapter',
|
11
11
|
'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter',
|
12
|
-
'ActiveRecord::ConnectionAdapters::RealTransaction'
|
12
|
+
'ActiveRecord::ConnectionAdapters::RealTransaction',
|
13
|
+
'ActiveRecord::Transaction',
|
14
|
+
'ActiveRecord::SchemaMigration'
|
13
15
|
],
|
14
16
|
'ActionDispatch' => ['ActionDispatch::Request', 'ActionDispatch::Response']
|
15
17
|
}.freeze
|
@@ -39,10 +41,10 @@ module RailsSpotlight
|
|
39
41
|
].freeze
|
40
42
|
|
41
43
|
attr_reader :project_name, :source_path, :logger, :storage_path, :storage_pool_size, :middleware_skipped_paths,
|
42
|
-
:not_encodable_event_values, :
|
43
|
-
:block_editing_files, :block_editing_files_outside_of_the_project, :skip_rendered_ivars,
|
44
|
-
:directory_index_ignore, :rubocop_config_path, :
|
45
|
-
:form_js_execution_token
|
44
|
+
:not_encodable_event_values, :cable_mount_path,
|
45
|
+
:file_manager_enabled, :block_editing_files, :block_editing_files_outside_of_the_project, :skip_rendered_ivars,
|
46
|
+
:directory_index_ignore, :rubocop_enabled, :rubocop_config_path, :use_cable, :default_rs_src,
|
47
|
+
:form_js_execution_token, :sql_console_enabled, :irb_console_enabled, :data_access_token
|
46
48
|
|
47
49
|
def initialize(opts = {}) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
|
48
50
|
@project_name = opts[:project_name] || detect_project_name
|
@@ -50,50 +52,43 @@ module RailsSpotlight
|
|
50
52
|
@logger = opts[:logger] || Logger.new(File.join(self.class.rails_root, 'log', 'rails_spotlight.log'))
|
51
53
|
@storage_path = opts[:storage_path] || File.join(self.class.rails_root, 'tmp', 'data', 'rails_spotlight')
|
52
54
|
@storage_pool_size = opts[:storage_pool_size] || 20
|
53
|
-
@
|
54
|
-
@request_completed_broadcast_enabled =
|
55
|
+
@cable_console_enabled = bool_val(:cable_console_enabled, opts)
|
56
|
+
@request_completed_broadcast_enabled = bool_val(:request_completed_broadcast_enabled, opts)
|
55
57
|
@middleware_skipped_paths = opts[:middleware_skipped_paths] || []
|
56
58
|
@not_encodable_event_values = DEFAULT_NOT_ENCODABLE_EVENT_VALUES.merge(opts[:not_encodable_event_values] || {})
|
57
|
-
@
|
58
|
-
@
|
59
|
-
@
|
60
|
-
@block_editing_files =
|
61
|
-
@block_editing_files_outside_of_the_project =
|
59
|
+
@use_cable = bool_val(:use_cable, opts)
|
60
|
+
@auto_mount_cable = bool_val(:auto_mount_cable, opts)
|
61
|
+
@cable_mount_path = opts[:cable_mount_path] || '/cable'
|
62
|
+
@block_editing_files = bool_val(:block_editing_files, opts)
|
63
|
+
@block_editing_files_outside_of_the_project = bool_val(:block_editing_files_outside_of_the_project, opts, default: true)
|
64
|
+
@file_manager_enabled = bool_val(:file_manager_enabled, opts, default: true)
|
62
65
|
@skip_rendered_ivars = SKIP_RENDERED_IVARS + (opts[:skip_rendered_ivars] || []).map(&:to_sym)
|
63
66
|
@directory_index_ignore = opts[:directory_index_ignore] || DEFAULT_DIRECTORY_INDEX_IGNORE
|
67
|
+
@rubocop_enabled = bool_val(:rubocop_enabled, opts, default: true)
|
64
68
|
@rubocop_config_path = opts[:rubocop_config_path] ? File.join(self.class.rails_root, opts[:rubocop_config_path]) : nil
|
65
|
-
@
|
69
|
+
@cable_logs_enabled = bool_val(:cable_logs_enabled, opts)
|
66
70
|
@default_rs_src = opts[:default_rs_src] || 'default'
|
67
71
|
@form_js_execution_token = opts[:form_js_execution_token] || Digest::MD5.hexdigest(detect_project_name)
|
72
|
+
@sql_console_enabled = bool_val(:sql_console_enabled, opts, default: true)
|
73
|
+
@irb_console_enabled = bool_val(:irb_console_enabled, opts, default: true)
|
74
|
+
@data_access_token = opts[:data_access_token].present? ? opts[:data_access_token] : nil
|
68
75
|
end
|
69
76
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
def
|
75
|
-
@live_logs_enabled && use_action_cable && action_cable_present?
|
76
|
-
end
|
77
|
-
|
78
|
-
alias live_console_enabled? live_console_enabled
|
79
|
-
alias live_logs_enabled? live_logs_enabled
|
80
|
-
alias use_action_cable? use_action_cable
|
81
|
-
|
82
|
-
def request_completed_broadcast_enabled
|
83
|
-
@request_completed_broadcast_enabled && use_action_cable && action_cable_present?
|
84
|
-
end
|
77
|
+
def cable_console_enabled = @cable_console_enabled && use_cable && action_cable_present?
|
78
|
+
def cable_logs_enabled = @cable_logs_enabled && use_cable && action_cable_present?
|
79
|
+
def request_completed_broadcast_enabled = @request_completed_broadcast_enabled && use_cable && action_cable_present?
|
80
|
+
def auto_mount_cable = @auto_mount_cable && use_cable && action_cable_present?
|
81
|
+
def action_cable_present? = defined?(ActionCable) && true
|
85
82
|
|
83
|
+
alias cable_console_enabled? cable_console_enabled
|
84
|
+
alias cable_logs_enabled? cable_logs_enabled
|
85
|
+
alias use_cable? use_cable
|
86
86
|
alias request_completed_broadcast_enabled? request_completed_broadcast_enabled
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
alias auto_mount_action_cable? auto_mount_action_cable
|
93
|
-
|
94
|
-
def action_cable_present?
|
95
|
-
defined?(ActionCable) && true
|
96
|
-
end
|
87
|
+
alias auto_mount_cable? auto_mount_cable
|
88
|
+
alias file_manager_enabled? file_manager_enabled
|
89
|
+
alias rubocop_enabled? rubocop_enabled
|
90
|
+
alias sql_console_enabled? sql_console_enabled
|
91
|
+
alias irb_console_enabled? irb_console_enabled
|
97
92
|
|
98
93
|
def self.load_config
|
99
94
|
config_file = File.join(rails_root, 'config', 'rails_spotlight.yml')
|
@@ -111,32 +106,19 @@ module RailsSpotlight
|
|
111
106
|
new(opts)
|
112
107
|
end
|
113
108
|
|
114
|
-
def self.rails_root
|
115
|
-
|
116
|
-
end
|
117
|
-
|
118
|
-
def rails_root
|
119
|
-
self.class.rails_root
|
120
|
-
end
|
109
|
+
def self.rails_root = @rails_root ||= (Rails.root.to_s.presence || Dir.pwd).freeze
|
110
|
+
def rails_root = self.class.rails_root
|
121
111
|
|
122
112
|
private
|
123
113
|
|
124
|
-
def true?(value)
|
125
|
-
[true, 'true', 1, '1'].include?(value)
|
126
|
-
end
|
127
|
-
|
128
114
|
def detect_project_name
|
129
115
|
return ENV['RAILS_SPOTLIGHT_PROJECT'] if ENV['RAILS_SPOTLIGHT_PROJECT'].present?
|
130
116
|
|
131
|
-
|
132
|
-
app_class.module_parent_name
|
133
|
-
else
|
134
|
-
app_class.parent_name
|
135
|
-
end
|
117
|
+
app_class.respond_to?(:module_parent_name) ? app_class.module_parent_name : app_class.parent_name
|
136
118
|
end
|
137
119
|
|
138
|
-
def app_class
|
139
|
-
|
140
|
-
|
120
|
+
def app_class = @app_class ||= Rails.application.class
|
121
|
+
def true?(value) = [true, 'true', 1, '1'].include?(value)
|
122
|
+
def bool_val(key, opts, default: false) = opts[key].nil? ? default : true?(opts[key])
|
141
123
|
end
|
142
124
|
end
|
@@ -15,6 +15,8 @@ module RailsSpotlight
|
|
15
15
|
def initialize(name, start, ending, transaction_id, payload)
|
16
16
|
super(name, start, ending, transaction_id, json_encodable(payload))
|
17
17
|
@duration = 1000.0 * (ending - start)
|
18
|
+
rescue # rubocop:disable Lint/RedundantCopDisableDirective, Style/RescueStandardError
|
19
|
+
@duration = 0
|
18
20
|
end
|
19
21
|
|
20
22
|
def self.events_for_exception(exception_wrapper)
|
@@ -28,7 +30,7 @@ module RailsSpotlight
|
|
28
30
|
end
|
29
31
|
trace.unshift "#{exception.class} (#{exception.message})"
|
30
32
|
trace.map do |call|
|
31
|
-
Event.new('process_action.action_controller.exception', 0, 0, nil, call:
|
33
|
+
Event.new('process_action.action_controller.exception', 0, 0, nil, call:)
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
@@ -42,7 +44,7 @@ module RailsSpotlight
|
|
42
44
|
value = value.to_h.select { |k, _| k.upcase == k }
|
43
45
|
elsif value.is_a?(Array) && defined?(ActiveRecord::Relation::QueryAttribute) && value.first.is_a?(ActiveRecord::Relation::QueryAttribute)
|
44
46
|
value = value.map(&method(:map_relation_query_attribute))
|
45
|
-
elsif not_encodable?(value)
|
47
|
+
elsif !value.respond_to?(:to_json) || not_encodable?(value)
|
46
48
|
value = NOT_JSON_ENCODABLE
|
47
49
|
end
|
48
50
|
|
@@ -54,6 +56,8 @@ module RailsSpotlight
|
|
54
56
|
end
|
55
57
|
hash[key] = new_value # encode_value(value)
|
56
58
|
end.with_indifferent_access
|
59
|
+
rescue # rubocop:disable Lint/RedundantCopDisableDirective, Style/RescueStandardError
|
60
|
+
{}
|
57
61
|
end
|
58
62
|
|
59
63
|
# ActiveRecord::Relation::QueryAttribute implementation changed in Rails 7.1 it getting binds need to be manually added
|
@@ -71,13 +75,25 @@ module RailsSpotlight
|
|
71
75
|
|
72
76
|
def not_encodable?(value)
|
73
77
|
::RailsSpotlight.config.not_encodable_event_values.any? do |module_name, class_names|
|
74
|
-
next unless
|
78
|
+
next unless safe_constantize(module_name)
|
75
79
|
|
76
|
-
class_names.any?
|
77
|
-
|
80
|
+
class_names.any? do |class_name|
|
81
|
+
klass = safe_constantize(class_name)
|
82
|
+
next false unless klass
|
83
|
+
|
84
|
+
value.is_a?(klass)
|
85
|
+
end
|
86
|
+
rescue # rubocop:disable Style/RescueStandardError
|
87
|
+
true
|
78
88
|
end
|
79
89
|
end
|
80
90
|
|
91
|
+
def safe_constantize(name)
|
92
|
+
name.constantize
|
93
|
+
rescue NameError
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
81
97
|
def transform_hash(original, options = {}, &block)
|
82
98
|
options[:safe_descent] ||= {}.compare_by_identity
|
83
99
|
|
@@ -4,95 +4,72 @@ require_relative 'channels'
|
|
4
4
|
|
5
5
|
module RailsSpotlight
|
6
6
|
module LogInterceptor
|
7
|
-
SEVERITY = %w[debug info warn error fatal unknown].freeze
|
8
7
|
SEVERITY_MAP = { 0 => 'debug', 1 => 'info', 2 => 'warn', 3 => 'error', 4 => 'fatal', 5 => 'unknown' }.freeze
|
9
8
|
|
10
|
-
def
|
11
|
-
|
12
|
-
return true if @logdev.nil? || severity < level
|
13
|
-
|
14
|
-
progname = @progname if progname.nil?
|
15
|
-
|
16
|
-
if message.nil?
|
17
|
-
if block_given?
|
18
|
-
message = yield
|
19
|
-
else
|
20
|
-
message = progname
|
21
|
-
progname = @progname
|
22
|
-
end
|
23
|
-
end
|
24
|
-
return true if _skip_logging?(message)
|
25
|
-
|
26
|
-
_rails_spotlight_log(SEVERITY_MAP[severity], message, progname, :broadcast)
|
27
|
-
super(severity, message, progname) if defined?(super)
|
28
|
-
true
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
def debug(message = nil, *args, &block)
|
33
|
-
_rails_spotlight_log(:debug, message, nil, :event, &block)
|
9
|
+
def debug(message = nil, *args, &)
|
10
|
+
_rails_spotlight_log(:debug, message, nil, &)
|
34
11
|
super
|
35
12
|
end
|
36
13
|
|
37
|
-
def info(message = nil, *args, &
|
38
|
-
_rails_spotlight_log(:info, message, nil,
|
14
|
+
def info(message = nil, *args, &)
|
15
|
+
_rails_spotlight_log(:info, message, nil, &)
|
39
16
|
super
|
40
17
|
end
|
41
18
|
|
42
|
-
def warn(message = nil, *args, &
|
43
|
-
_rails_spotlight_log(:warn, message, nil,
|
19
|
+
def warn(message = nil, *args, &)
|
20
|
+
_rails_spotlight_log(:warn, message, nil, &)
|
44
21
|
super
|
45
22
|
end
|
46
23
|
|
47
|
-
def error(message = nil, *args, &
|
48
|
-
_rails_spotlight_log(:error, message, nil,
|
24
|
+
def error(message = nil, *args, &)
|
25
|
+
_rails_spotlight_log(:error, message, nil, &)
|
49
26
|
super
|
50
27
|
end
|
51
28
|
|
52
|
-
def fatal(message = nil, *args, &
|
53
|
-
_rails_spotlight_log(:fatal, message, nil,
|
29
|
+
def fatal(message = nil, *args, &)
|
30
|
+
_rails_spotlight_log(:fatal, message, nil, &)
|
54
31
|
super
|
55
32
|
end
|
56
33
|
|
57
|
-
def unknown(message = nil, *args, &
|
58
|
-
_rails_spotlight_log(:unknown, message, nil,
|
34
|
+
def unknown(message = nil, *args, &)
|
35
|
+
_rails_spotlight_log(:unknown, message, nil, &)
|
59
36
|
super
|
60
37
|
end
|
61
38
|
|
62
39
|
private
|
63
40
|
|
64
41
|
def _skip_logging?(message)
|
65
|
-
return false unless ::RailsSpotlight.config.
|
42
|
+
return false unless ::RailsSpotlight.config.use_cable?
|
66
43
|
return false unless message.is_a?(String)
|
67
44
|
|
68
45
|
message.include?(::RailsSpotlight::Channels::SPOTLIGHT_CHANNEL)
|
69
46
|
end
|
70
47
|
|
71
|
-
def _rails_spotlight_log(
|
72
|
-
|
73
|
-
name = progname.is_a?(String) || progname.is_a?(Symbol) ? progname : nil
|
74
|
-
message = yield if output == :event && message.nil? && block_given?
|
75
|
-
output == :event ? _push_event(level, message, name, callsite) : _broadcast_log(message, level, callsite, name)
|
76
|
-
rescue StandardError => e
|
77
|
-
RailsSpotlight.config.logger.fatal("#{e.message}\n #{e.backtrace.join("\n ")}")
|
78
|
-
end
|
48
|
+
def _rails_spotlight_log(severity, message, progname = nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
49
|
+
return if message.nil? && !block_given?
|
79
50
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
51
|
+
severity ||= :unknown
|
52
|
+
level = SEVERITY_MAP[severity.to_s]
|
53
|
+
|
54
|
+
if message.nil?
|
55
|
+
if block_given?
|
56
|
+
message = yield
|
57
|
+
else
|
58
|
+
message = progname
|
59
|
+
progname = @progname
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return if _skip_logging?(message)
|
86
64
|
|
87
|
-
|
88
|
-
|
89
|
-
|
65
|
+
callsite = Utils.dev_callsite(caller.drop(1))
|
66
|
+
name = progname.is_a?(String) || progname.is_a?(Symbol) ? progname : nil
|
67
|
+
message = yield if message.nil? && block_given?
|
90
68
|
|
91
|
-
|
92
|
-
|
93
|
-
::RailsSpotlight::Channels::SpotlightChannel.broadcast(type: 'logs', payload: payload)
|
69
|
+
AppRequest.current.events << Event.new('rsl.notification.log', 0, 0, 0, (callsite || {}).merge(message:, level: severity, progname: name)) if AppRequest.current
|
70
|
+
::RailsSpotlight::Channels::SpotlightChannel.broadcast_log(message, level, callsite, name)
|
94
71
|
rescue StandardError => e
|
95
|
-
RailsSpotlight.config.logger.fatal("#{e.message}\n #{e.backtrace
|
72
|
+
RailsSpotlight.config.logger.fatal("#{e.message}\n #{e.backtrace&.join("\n ")}")
|
96
73
|
end
|
97
74
|
end
|
98
75
|
end
|
@@ -4,10 +4,25 @@ module RailsSpotlight
|
|
4
4
|
module Middlewares
|
5
5
|
module Handlers
|
6
6
|
class BaseActionHandler
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
BaseError = Class.new(StandardError) do
|
8
|
+
attr_reader :code, :status
|
9
|
+
|
10
|
+
def initialize(message = nil, code: nil)
|
11
|
+
super(message)
|
12
|
+
@code = code
|
13
|
+
@status = case self.class.name.demodulize.underscore
|
14
|
+
when 'unsupported_media_type' then 415
|
15
|
+
when 'not_found' then 404
|
16
|
+
when 'unprocessable_entity' then 422
|
17
|
+
when 'forbidden' then 403
|
18
|
+
else 500
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
UnsupportedMediaType = Class.new(BaseError)
|
23
|
+
NotFound = Class.new(BaseError)
|
24
|
+
UnprocessableEntity = Class.new(BaseError)
|
25
|
+
Forbidden = Class.new(BaseError)
|
11
26
|
|
12
27
|
def initialize(request_id, request, content_type)
|
13
28
|
@request_id = request_id
|
@@ -18,45 +33,34 @@ module RailsSpotlight
|
|
18
33
|
attr_reader :request_id, :request, :content_type
|
19
34
|
|
20
35
|
def call
|
36
|
+
validate_data_access! if data_access_token
|
21
37
|
validate_project! unless skip_project_validation?
|
22
38
|
execute
|
23
39
|
response
|
24
|
-
rescue
|
25
|
-
|
26
|
-
rescue UnprocessableEntity => e
|
27
|
-
unprocessed_response(e.message)
|
28
|
-
rescue Forbidden => e
|
29
|
-
forbidden_response(e.message)
|
40
|
+
rescue BaseError => e
|
41
|
+
error_response(e)
|
30
42
|
rescue => e # rubocop:disable Style/RescueStandardError
|
31
|
-
|
43
|
+
error_response(BaseError.new(e.message))
|
32
44
|
end
|
33
45
|
|
34
46
|
protected
|
35
47
|
|
36
48
|
attr_writer :status, :headers
|
37
49
|
|
38
|
-
def skip_project_validation?
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
def headers
|
43
|
-
@headers ||= {}
|
44
|
-
end
|
45
|
-
|
46
|
-
def status
|
47
|
-
@status ||= 200
|
48
|
-
end
|
50
|
+
def skip_project_validation? = false
|
51
|
+
def headers = @headers ||= {}
|
52
|
+
def status = @status ||= 200
|
49
53
|
|
50
54
|
def execute
|
51
55
|
raise 'Not implemented yet'
|
52
56
|
end
|
53
57
|
|
54
58
|
def json_response_body
|
55
|
-
raise
|
59
|
+
raise UnsupportedMediaType.new(content_type, code: :unsupported_media_type_json)
|
56
60
|
end
|
57
61
|
|
58
62
|
def text_response_body
|
59
|
-
raise
|
63
|
+
raise UnsupportedMediaType.new(content_type, code: :unsupported_media_type_text)
|
60
64
|
end
|
61
65
|
|
62
66
|
def json_request_body
|
@@ -65,29 +69,9 @@ module RailsSpotlight
|
|
65
69
|
raise 'Invalid JSON'
|
66
70
|
end
|
67
71
|
|
68
|
-
def body_fetch(*args)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
def internal_server_error_response(message)
|
73
|
-
response(500, message_to_body(message))
|
74
|
-
end
|
75
|
-
|
76
|
-
def unprocessed_response(message)
|
77
|
-
response(422, message_to_body(message))
|
78
|
-
end
|
79
|
-
|
80
|
-
def forbidden_response(message)
|
81
|
-
response(403, message_to_body(message))
|
82
|
-
end
|
83
|
-
|
84
|
-
def not_found_response(message)
|
85
|
-
response(404, message_to_body(message))
|
86
|
-
end
|
87
|
-
|
88
|
-
def message_to_body(message)
|
89
|
-
content_type == :json ? { message: message } : message
|
90
|
-
end
|
72
|
+
def body_fetch(*args) = json_request_body.fetch(*args)
|
73
|
+
def error_response(error) = response(error.status || 500, message_to_body(error.message, code: error.code, status: error.status || 500))
|
74
|
+
def message_to_body(message, code: :none, status: 500) = content_type == :json ? { message:, code:, status: } : message
|
91
75
|
|
92
76
|
def response_headers(headers = {})
|
93
77
|
{
|
@@ -107,19 +91,25 @@ module RailsSpotlight
|
|
107
91
|
[overridden_status.present? ? overridden_status : status, response_headers(headers), [body]]
|
108
92
|
end
|
109
93
|
|
110
|
-
def request_spotlight_version
|
111
|
-
|
112
|
-
end
|
113
|
-
|
114
|
-
def request_for_projects
|
115
|
-
@request_for_projects ||= (request.get_header('HTTP_X_FOR_PROJECTS') || '').split(',').map(&:strip)
|
116
|
-
end
|
94
|
+
def request_spotlight_version = @request_spotlight_version ||= request.get_header('HTTP_X_RAILS_SPOTLIGHT')
|
95
|
+
def request_for_projects = @request_for_projects ||= (request.get_header('HTTP_X_FOR_PROJECTS') || '').split(',').map(&:strip)
|
117
96
|
|
118
97
|
def validate_project!
|
119
98
|
return if request_for_projects.blank?
|
120
99
|
return if request_for_projects.include?(::RailsSpotlight.config.project_name)
|
121
100
|
|
122
|
-
raise Forbidden
|
101
|
+
raise Forbidden.new(
|
102
|
+
"Check your settings the current request is not allowed to be executed on the #{::RailsSpotlight.config.project_name} project",
|
103
|
+
code: :project_mismatch
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
def data_access_token = ::RailsSpotlight.config.data_access_token
|
108
|
+
|
109
|
+
def validate_data_access!
|
110
|
+
return if request.get_header('HTTP_X_DATA_ACCESS_TOKEN') == data_access_token
|
111
|
+
|
112
|
+
raise Forbidden.new('Invalid data access token', code: :invalid_data_access_token)
|
123
113
|
end
|
124
114
|
end
|
125
115
|
end
|
@@ -7,12 +7,11 @@ module RailsSpotlight
|
|
7
7
|
module Middlewares
|
8
8
|
module Handlers
|
9
9
|
class CodeAnalysisActionHandler < BaseActionHandler
|
10
|
-
def skip_project_validation?
|
11
|
-
true
|
12
|
-
end
|
10
|
+
def skip_project_validation? = true
|
13
11
|
|
14
12
|
def execute
|
15
|
-
raise
|
13
|
+
raise Forbidden.new('Code analysis is disabled', code: :disabled_rubocop_settings) unless enabled?
|
14
|
+
raise UnprocessableEntity.new('Please add rubocop to your project', code: :rubocop_not_installed) unless rubocop_installed?
|
16
15
|
end
|
17
16
|
|
18
17
|
private
|
@@ -64,8 +63,8 @@ module RailsSpotlight
|
|
64
63
|
|
65
64
|
{
|
66
65
|
source: variant == :autofix ? corrected_source : source,
|
67
|
-
analysis_result
|
68
|
-
variant:
|
66
|
+
analysis_result:,
|
67
|
+
variant:
|
69
68
|
}
|
70
69
|
ensure
|
71
70
|
# Close and unlink the tempfile
|
@@ -76,13 +75,9 @@ module RailsSpotlight
|
|
76
75
|
end
|
77
76
|
end
|
78
77
|
|
79
|
-
def source
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
def variant
|
84
|
-
@variant ||= body_fetch('variant', 'check').to_sym
|
85
|
-
end
|
78
|
+
def source = @source ||= body_fetch('source')
|
79
|
+
def variant = @variant ||= body_fetch('variant', 'check').to_sym
|
80
|
+
def enabled? = ::RailsSpotlight.config.rubocop_enabled?
|
86
81
|
end
|
87
82
|
end
|
88
83
|
end
|
@@ -6,29 +6,25 @@ module RailsSpotlight
|
|
6
6
|
module Handlers
|
7
7
|
class ConsoleActionHandler < BaseActionHandler
|
8
8
|
def execute
|
9
|
+
raise Forbidden.new('Console is disabled', code: :disabled_irb_console_settings) unless enabled?
|
10
|
+
|
9
11
|
RailsSpotlight.config.logger && RailsSpotlight.config.logger.info("Executing command: #{command}") # rubocop:disable Style/SafeNavigation
|
10
12
|
executor.execute(command)
|
11
13
|
end
|
12
14
|
|
13
15
|
private
|
14
16
|
|
15
|
-
def executor
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def inspect_types
|
20
|
-
@inspect_types ||= body_fetch('inspect_types')
|
21
|
-
end
|
22
|
-
|
23
|
-
def command
|
24
|
-
@command ||= body_fetch('command')
|
25
|
-
end
|
17
|
+
def executor = @executor ||= ::RailsSpotlight::RailsCommandExecutor.new
|
18
|
+
def inspect_types = @inspect_types ||= body_fetch('inspect_types')
|
19
|
+
def command = @command ||= body_fetch('command')
|
26
20
|
|
27
21
|
def json_response_body
|
28
22
|
return executor.result_as_json unless executor.execution_successful?
|
29
23
|
|
30
|
-
{ result: executor.result_as_json(inspect_types:
|
24
|
+
{ result: executor.result_as_json(inspect_types:) }
|
31
25
|
end
|
26
|
+
|
27
|
+
def enabled? = ::RailsSpotlight.config.irb_console_enabled?
|
32
28
|
end
|
33
29
|
end
|
34
30
|
end
|
@@ -9,9 +9,13 @@ module RailsSpotlight
|
|
9
9
|
module Handlers
|
10
10
|
class DirectoryIndexActionHandler < BaseActionHandler
|
11
11
|
def execute
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
raise Forbidden.new('File manager is disabled', code: :disabled_file_manager_settings) unless enabled?
|
13
|
+
|
14
|
+
@result = begin
|
15
|
+
directory_to_json(::RailsSpotlight.config.rails_root)
|
16
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
17
|
+
raise UnprocessableEntity.new(e.message, code: :directory_index_error)
|
18
|
+
end
|
15
19
|
end
|
16
20
|
|
17
21
|
private
|
@@ -55,29 +59,12 @@ module RailsSpotlight
|
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
58
|
-
def ignore
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
def
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
def omnit_gitignore
|
67
|
-
@omnit_gitignore ||= body_fetch('omnit_gitignore', false)
|
68
|
-
end
|
69
|
-
|
70
|
-
def ignore_patterns
|
71
|
-
@ignore_patterns ||= ignore + ::RailsSpotlight.config.directory_index_ignore + (omnit_gitignore ? [] : gitignore_patterns)
|
72
|
-
end
|
73
|
-
|
74
|
-
def gitignore_file
|
75
|
-
@gitignore_file ||= File.join(::RailsSpotlight.config.rails_root, '.gitignore')
|
76
|
-
end
|
77
|
-
|
78
|
-
def show_empty_directories
|
79
|
-
@show_empty_directories ||= body_fetch('show_empty_directories', false)
|
80
|
-
end
|
62
|
+
def ignore = @ignore ||= body_fetch('ignore', [])
|
63
|
+
def sort_folders_first = @sort_folders_first ||= body_fetch('sort_folders_first', true)
|
64
|
+
def omnit_gitignore = @omnit_gitignore ||= body_fetch('omnit_gitignore', false)
|
65
|
+
def ignore_patterns = @ignore_patterns ||= ignore + ::RailsSpotlight.config.directory_index_ignore + (omnit_gitignore ? [] : gitignore_patterns)
|
66
|
+
def gitignore_file = @gitignore_file ||= File.join(::RailsSpotlight.config.rails_root, '.gitignore')
|
67
|
+
def show_empty_directories = @show_empty_directories ||= body_fetch('show_empty_directories', false)
|
81
68
|
|
82
69
|
def gitignore_patterns
|
83
70
|
@gitignore_patterns ||= if File.exist?(gitignore_file)
|
@@ -103,9 +90,11 @@ module RailsSpotlight
|
|
103
90
|
def json_response_body
|
104
91
|
{
|
105
92
|
root_path: ::RailsSpotlight.config.rails_root,
|
106
|
-
result:
|
93
|
+
result:
|
107
94
|
}
|
108
95
|
end
|
96
|
+
|
97
|
+
def enabled? = ::RailsSpotlight.config.file_manager_enabled
|
109
98
|
end
|
110
99
|
end
|
111
100
|
end
|