models_auditor 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -0
- data/app/controllers/models_auditor/audit_controller.rb +3 -63
- data/app/models/models_auditor/audit_request.rb +1 -0
- data/app/workers/models_auditor/models_auditor_worker.rb +23 -0
- data/config/config_option_descriptions.yml +1 -3
- data/lib/models_auditor/audit.rb +22 -38
- data/lib/models_auditor/config.rb +5 -5
- data/lib/models_auditor/controller.rb +20 -7
- data/lib/models_auditor/default_formatter.rb +72 -0
- data/lib/models_auditor/version.rb +1 -1
- data/lib/models_auditor.rb +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99a387e69efd7a90352cdf2ef193a8ac885d2f58
|
4
|
+
data.tar.gz: d45f0de4510f98035c96635760bb5a654621b874
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d501e1c682daae07a70054ab5b23638a00ea71f6dd7c1b05ce7a9af4eaa9f17386b140bc88f5689bfb5b1df214f211c0b84ed60665790ee059a59c97835bf2b
|
7
|
+
data.tar.gz: 48c6ab3335b1b4325a10ffca829b6338cbed273e33410d2da85d22d133a1532e223c5879555ced53e6f5d2d62c973ffd698038e38110ca9ec65c19da41d11354
|
data/README.md
CHANGED
@@ -19,9 +19,11 @@ module ModelsAuditor
|
|
19
19
|
|
20
20
|
respond_to do |f|
|
21
21
|
if ModelsAuditor.config.respond_to_json_enabled
|
22
|
+
formatter = ModelsAuditor.config.log_output_formatter.constantize.new(@collection)
|
23
|
+
|
22
24
|
f.json {
|
23
25
|
render json: {
|
24
|
-
ModelsAuditor.config.json_response_data_key =>
|
26
|
+
ModelsAuditor.config.json_response_data_key => formatter.as_json,
|
25
27
|
ModelsAuditor.config.json_response_meta_key => {
|
26
28
|
per_page: @collection.per_page,
|
27
29
|
total: @collection.total_entries,
|
@@ -77,67 +79,5 @@ module ModelsAuditor
|
|
77
79
|
original.where(id: filtered_requests)
|
78
80
|
end
|
79
81
|
|
80
|
-
def get_relations(record, records)
|
81
|
-
rel_records = records.select do |r|
|
82
|
-
!r.bridge.nil? && r.bridge.any? do |_, v|
|
83
|
-
v_type, v_id = v.to_a[0]
|
84
|
-
v_type.to_s == record.object_type && v_id.to_i == record.object_id.to_i
|
85
|
-
end
|
86
|
-
end
|
87
|
-
rel_records.map do |i|
|
88
|
-
target_info = except_target_class(i.bridge, record.object_type, record.object_id)
|
89
|
-
next if target_info.empty?
|
90
|
-
t_key, klass_with_id = target_info.to_a[0]
|
91
|
-
target_klass, target_id = klass_with_id.to_a[0]
|
92
|
-
i.attributes.slice('id', 'object_type', 'object_id').merge(target: {
|
93
|
-
class: target_klass,
|
94
|
-
foreign_key: t_key,
|
95
|
-
foreign_id: target_id,
|
96
|
-
})
|
97
|
-
end.compact
|
98
|
-
end
|
99
|
-
|
100
|
-
# @return [Array]
|
101
|
-
def except_target_class(bridge, target_class, target_id)
|
102
|
-
target_id = target_id.to_i
|
103
|
-
bridge.select do |_, v|
|
104
|
-
klass, id = v.to_a[0]
|
105
|
-
!(klass.to_s == target_class.to_s && id.to_i == target_id.to_i)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# @param [ActiveRecord::Relation|ModelsAuditor::AuditRequest|Array] data
|
110
|
-
def structure_requests_data(data)
|
111
|
-
requests =
|
112
|
-
case data
|
113
|
-
when ActiveRecord::Relation
|
114
|
-
data.to_a
|
115
|
-
when ModelsAuditor::AuditRequest
|
116
|
-
[data]
|
117
|
-
when Array
|
118
|
-
data
|
119
|
-
else
|
120
|
-
raise ArgumentError('Incorrect type of argument `requests`')
|
121
|
-
end
|
122
|
-
|
123
|
-
requests.map do |request|
|
124
|
-
records = request.records
|
125
|
-
{}.tap do |result|
|
126
|
-
changed_models_collection =
|
127
|
-
records
|
128
|
-
.select { |record| record.bridge.nil? }
|
129
|
-
.map do |record|
|
130
|
-
{
|
131
|
-
data: record.attributes.slice('id', 'object_type', 'object_id'),
|
132
|
-
relationships: get_relations(record, records)
|
133
|
-
}
|
134
|
-
end
|
135
|
-
|
136
|
-
result[:request] = request.as_json
|
137
|
-
result[:changes_struct] = changed_models_collection.group_by { |i| i[:data]['object_type'] }
|
138
|
-
result[:all_changes] = records.map(&:as_json)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
82
|
end
|
143
83
|
end
|
@@ -8,6 +8,7 @@ module ModelsAuditor
|
|
8
8
|
self.table_name = ModelsAuditor.config.audit_requests_table_name
|
9
9
|
|
10
10
|
has_many :records, class_name: ModelsAuditor::AuditRecord.name, foreign_key: :request_id, inverse_of: :request
|
11
|
+
accepts_nested_attributes_for :records
|
11
12
|
|
12
13
|
# def as_json(options = nil)
|
13
14
|
# super({include: :records }.merge(options || {}))
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'sidekiq'
|
2
|
+
|
3
|
+
module ModelsAuditor
|
4
|
+
class ModelsAuditorWorker
|
5
|
+
include Sidekiq::Worker
|
6
|
+
|
7
|
+
sidekiq_options queue: 'models_auditor', retry: 2, backtrace: true
|
8
|
+
|
9
|
+
def perform(request_data_json)
|
10
|
+
return unless ModelsAuditor.config.audit_enabled
|
11
|
+
|
12
|
+
ModelsAuditor::AuditRecord.connection.pool.with_connection do
|
13
|
+
request_data = JSON.parse(request_data_json)
|
14
|
+
request = ModelsAuditor::AuditRequest.new(request_data)
|
15
|
+
unless request.save
|
16
|
+
ModelsAuditor.log_error("Couldn't save request record")
|
17
|
+
ModelsAuditor.log_error(request.errors.full_messages)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -3,9 +3,6 @@ connection_namespace: Namespace для конфига базы данн
|
|
3
3
|
audit_migrations_dir: Папка для хранения миграций аудитора
|
4
4
|
audit_records_table_name: Название таблицы для хрянения логов
|
5
5
|
audit_requests_table_name: Название таблицы для хранения информации о запросах, приведших к изменению данных
|
6
|
-
audit_request_changes_only: |-
|
7
|
-
true: Логировать только изменения, производимые через Request
|
8
|
-
false: Логировать любые изменения
|
9
6
|
logger: |-
|
10
7
|
Может принимать объект класса Logger или false
|
11
8
|
Пример:
|
@@ -21,3 +18,4 @@ respond_to_json_enabled: Доступ к логам через json api
|
|
21
18
|
respond_to_html_enabled: Доступ к логам через html
|
22
19
|
json_response_data_key: Ключ с залогированными данными в json ответе
|
23
20
|
json_response_meta_key: Ключ с meta информацией в json ответе
|
21
|
+
log_output_formatter: Класс форматирующий данные логов перед выдачей
|
data/lib/models_auditor/audit.rb
CHANGED
@@ -84,46 +84,30 @@ module ModelsAuditor
|
|
84
84
|
options[:bridge].each_with_object({}) { |(key, model_name), o| o[key] = {model_name => __send__(key)} }
|
85
85
|
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
when AUDIT_CHANGES_MODES.include?(mode)
|
98
|
-
current_data
|
99
|
-
else
|
100
|
-
raise ArgumentError.new('Incorrect value of argument audit_type')
|
101
|
-
end
|
102
|
-
|
103
|
-
if request.try(:new_record?) && !request.save
|
104
|
-
ModelsAuditor.log_error("Couldn't save request record")
|
105
|
-
ModelsAuditor.log_error(request.errors.full_messages)
|
106
|
-
return
|
107
|
-
end
|
108
|
-
record =
|
109
|
-
ModelsAuditor::AuditRecord.new(
|
110
|
-
request: request,
|
111
|
-
auditable: self,
|
112
|
-
content: body,
|
113
|
-
action: action,
|
114
|
-
bridge: bridge
|
115
|
-
)
|
116
|
-
unless record.save
|
117
|
-
ModelsAuditor.log_error("Couldn't logged changes of #{self.class.name} id: #{self.try(:id)}")
|
118
|
-
ModelsAuditor.log_error(record.errors.full_messages)
|
119
|
-
end
|
87
|
+
begin
|
88
|
+
if (request_data = store[:audit_request_data]).present?
|
89
|
+
body =
|
90
|
+
case
|
91
|
+
when AUDIT_SNAPSHOT_MODES.include?(mode)
|
92
|
+
ma_eliminate_not_changed_keys(initial_data, current_data)
|
93
|
+
when AUDIT_CHANGES_MODES.include?(mode)
|
94
|
+
current_data
|
95
|
+
else
|
96
|
+
raise ArgumentError.new('Incorrect value of argument audit_type')
|
120
97
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
98
|
+
|
99
|
+
request_data[:records_attributes] << {
|
100
|
+
object_id: self.id,
|
101
|
+
object_type: self.class.name,
|
102
|
+
content: body,
|
103
|
+
action: action,
|
104
|
+
bridge: bridge
|
105
|
+
}
|
126
106
|
end
|
107
|
+
rescue StandardError => e
|
108
|
+
ModelsAuditor.log_error("Couldn't generate log changes of #{self.class.name} id: #{self.try(:id)}")
|
109
|
+
ModelsAuditor.log_error(e.message)
|
110
|
+
ModelsAuditor.log_error(e.backtrace.take(100).join("\n"))
|
127
111
|
end
|
128
112
|
end
|
129
113
|
|
@@ -6,7 +6,6 @@ module ModelsAuditor
|
|
6
6
|
audit_records_table_name
|
7
7
|
audit_requests_table_name
|
8
8
|
audit_migrations_dir
|
9
|
-
audit_request_changes_only
|
10
9
|
logger
|
11
10
|
records_per_page
|
12
11
|
fake_total_count
|
@@ -15,6 +14,7 @@ module ModelsAuditor
|
|
15
14
|
respond_to_html_enabled
|
16
15
|
json_response_data_key
|
17
16
|
json_response_meta_key
|
17
|
+
log_output_formatter
|
18
18
|
)
|
19
19
|
|
20
20
|
def initialize
|
@@ -47,10 +47,6 @@ module ModelsAuditor
|
|
47
47
|
config: "Logger.new(Rails.root.join('log', 'models_auditor.log'))",
|
48
48
|
val: Logger.new(Rails.root.join('log', 'models_auditor.log'))
|
49
49
|
},
|
50
|
-
audit_request_changes_only: {
|
51
|
-
config: 'true',
|
52
|
-
val: true
|
53
|
-
},
|
54
50
|
records_per_page: {
|
55
51
|
config: '10',
|
56
52
|
val: 10
|
@@ -79,6 +75,10 @@ module ModelsAuditor
|
|
79
75
|
config: "'meta'",
|
80
76
|
val: 'meta'
|
81
77
|
},
|
78
|
+
log_output_formatter: {
|
79
|
+
config: "'ModelsAuditor::DefaultFormatter'",
|
80
|
+
val: 'ModelsAuditor::DefaultFormatter'
|
81
|
+
},
|
82
82
|
}
|
83
83
|
end
|
84
84
|
|
@@ -1,19 +1,32 @@
|
|
1
1
|
module ModelsAuditor
|
2
2
|
module Controller
|
3
3
|
def self.included(base)
|
4
|
-
base.
|
4
|
+
base.around_action :set_models_auditor_request_params
|
5
5
|
end
|
6
6
|
|
7
7
|
protected
|
8
8
|
|
9
9
|
def set_models_auditor_request_params
|
10
|
-
ModelsAuditor.
|
11
|
-
ModelsAuditor
|
10
|
+
if ModelsAuditor.config.audit_enabled
|
11
|
+
ModelsAuditor.store[:audit_request_data] = {
|
12
12
|
user_id: user_for_models_auditor,
|
13
|
-
request_info: info_for_models_auditor
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
request_info: info_for_models_auditor,
|
14
|
+
records_attributes: []
|
15
|
+
}
|
16
|
+
|
17
|
+
yield
|
18
|
+
|
19
|
+
if ModelsAuditor.store[:audit_request_data][:records_attributes].present?
|
20
|
+
begin
|
21
|
+
ModelsAuditor::ModelsAuditorWorker.perform_async(ModelsAuditor.store[:audit_request_data].to_json)
|
22
|
+
rescue StandardError => e
|
23
|
+
ModelsAuditor.log_error(e.message)
|
24
|
+
ModelsAuditor.log_error(e.backtrace.take(100).join("\n"))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
else
|
28
|
+
yield
|
29
|
+
end
|
17
30
|
end
|
18
31
|
|
19
32
|
# Returns the user who is responsible for any changes that occur.
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ModelsAuditor
|
2
|
+
class DefaultFormatter
|
3
|
+
def initialize(collection)
|
4
|
+
@collection = collection
|
5
|
+
end
|
6
|
+
|
7
|
+
# @param [ActiveRecord::Relation|ModelsAuditor::AuditRequest|Array] data
|
8
|
+
def as_json
|
9
|
+
requests =
|
10
|
+
case @collection
|
11
|
+
when ActiveRecord::Relation
|
12
|
+
@collection.to_a
|
13
|
+
when ModelsAuditor::AuditRequest
|
14
|
+
[@collection]
|
15
|
+
when Array
|
16
|
+
@collection
|
17
|
+
else
|
18
|
+
raise ArgumentError('Incorrect type of argument `requests`')
|
19
|
+
end
|
20
|
+
|
21
|
+
requests.map do |request|
|
22
|
+
records = request.records
|
23
|
+
{}.tap do |result|
|
24
|
+
changed_models_collection =
|
25
|
+
records
|
26
|
+
.select { |record| record.bridge.nil? }
|
27
|
+
.map do |record|
|
28
|
+
{
|
29
|
+
data: record.attributes.slice('id', 'object_type', 'object_id'),
|
30
|
+
relationships: get_relations(record, records)
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
result[:request] = request.as_json
|
35
|
+
result[:changes_struct] = changed_models_collection.group_by { |i| i[:data]['object_type'] }
|
36
|
+
result[:all_changes] = records.map(&:as_json)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# @return [Array]
|
44
|
+
def except_target_class(bridge, target_class, target_id)
|
45
|
+
target_id = target_id.to_i
|
46
|
+
bridge.select do |_, v|
|
47
|
+
klass, id = v.to_a[0]
|
48
|
+
!(klass.to_s == target_class.to_s && id.to_i == target_id.to_i)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_relations(record, records)
|
53
|
+
rel_records = records.select do |r|
|
54
|
+
!r.bridge.nil? && r.bridge.any? do |_, v|
|
55
|
+
v_type, v_id = v.to_a[0]
|
56
|
+
v_type.to_s == record.object_type && v_id.to_i == record.object_id.to_i
|
57
|
+
end
|
58
|
+
end
|
59
|
+
rel_records.map do |i|
|
60
|
+
target_info = except_target_class(i.bridge, record.object_type, record.object_id)
|
61
|
+
next if target_info.empty?
|
62
|
+
t_key, klass_with_id = target_info.to_a[0]
|
63
|
+
target_klass, target_id = klass_with_id.to_a[0]
|
64
|
+
i.attributes.slice('id', 'object_type', 'object_id').merge(target: {
|
65
|
+
class: target_klass,
|
66
|
+
foreign_key: t_key,
|
67
|
+
foreign_id: target_id,
|
68
|
+
})
|
69
|
+
end.compact
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/models_auditor.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: models_auditor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Gorbunov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.3.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sidekiq
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.17.7
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.17.7
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: pry
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -131,6 +145,7 @@ files:
|
|
131
145
|
- app/views/models_auditor/audit/_record.html.erb
|
132
146
|
- app/views/models_auditor/audit/_request.html.erb
|
133
147
|
- app/views/models_auditor/audit/index.html.erb
|
148
|
+
- app/workers/models_auditor/models_auditor_worker.rb
|
134
149
|
- config/config_option_descriptions.yml
|
135
150
|
- config/routes.rb
|
136
151
|
- lib/generators/models_auditor/db_config/USAGE
|
@@ -149,6 +164,7 @@ files:
|
|
149
164
|
- lib/models_auditor/capistrano/tasks/audit_migrations.rake
|
150
165
|
- lib/models_auditor/config.rb
|
151
166
|
- lib/models_auditor/controller.rb
|
167
|
+
- lib/models_auditor/default_formatter.rb
|
152
168
|
- lib/models_auditor/engine.rb
|
153
169
|
- lib/models_auditor/version.rb
|
154
170
|
- lib/tasks/models_auditor_tasks.rake
|