From dd4adb850c8985bb4aab2e6adc462e19bfac4b6c Mon Sep 17 00:00:00 2001 From: Nathan Griffith Date: Thu, 13 Oct 2022 13:43:51 -0400 Subject: [PATCH] Add `snapshot_on_deletion` feature to AuditLog (#28) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Summary Because the `changes` hash is always empty on deletion, we would like the option to create a snapshot by default on deletes, to ensure that we know what it was exactly that was deleted. (This is particularly useful for models that existed prior to the introduction of this framework.) ### Other Information I'm defaulting it to `false` because ... I guess it felt extra confusing to have a feature that defaults to only partially being enabled. But I could be convinced that `true` is actually a better default 🤷 --- .gitignore | 1 + README.md | 9 +++++++++ app/models/journaled/audit_log/event.rb | 3 ++- lib/journaled/audit_log.rb | 1 + lib/journaled/version.rb | 2 +- spec/lib/journaled/audit_log_spec.rb | 17 +++++++++++++++++ spec/spec_helper.rb | 1 + 7 files changed, 32 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f0089ff..08cbf90 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ Gemfile.lock log/*.log pkg/ +spec/examples.txt spec/dummy/log/*.log spec/dummy/tmp/ /gemfiles/*.gemfile.lock diff --git a/README.md b/README.md index 5b2683b..0e537c4 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,15 @@ Journaled::AuditLog.with_snapshots do end ``` +Snapshots can also be enabled globally for all _deletion_ operations. Since +`changes` will be empty on deletion, you should consider using this if you care +about the contents of any records being deleted (and/or don't have a full audit +trail from their time of creation): + +```ruby +Journaled::AuditLog.snapshot_on_deletion = true +``` + Events with snapshots will continue to populate the `changes` field, but will additionally contain a snapshot with the full state of the user: diff --git a/app/models/journaled/audit_log/event.rb b/app/models/journaled/audit_log/event.rb index 165ab6a..856578d 100644 --- a/app/models/journaled/audit_log/event.rb +++ b/app/models/journaled/audit_log/event.rb @@ -58,7 +58,8 @@ def changes end def snapshot - filtered_attributes if record._log_snapshot || AuditLog.snapshots_enabled + filtered_attributes if record._log_snapshot || AuditLog.snapshots_enabled || + (database_operation == 'delete' && AuditLog.snapshot_on_deletion) end def actor diff --git a/lib/journaled/audit_log.rb b/lib/journaled/audit_log.rb index 78ca40a..e59d77a 100644 --- a/lib/journaled/audit_log.rb +++ b/lib/journaled/audit_log.rb @@ -18,6 +18,7 @@ module AuditLog mattr_accessor(:default_enqueue_opts) { {} } mattr_accessor(:excluded_classes) { DEFAULT_EXCLUDED_CLASSES.dup } thread_mattr_accessor(:snapshots_enabled) { false } + thread_mattr_accessor(:snapshot_on_deletion) { false } thread_mattr_accessor(:_disabled) { false } thread_mattr_accessor(:_force) { false } diff --git a/lib/journaled/version.rb b/lib/journaled/version.rb index 9f26ec8..7b5b5a8 100644 --- a/lib/journaled/version.rb +++ b/lib/journaled/version.rb @@ -1,3 +1,3 @@ module Journaled - VERSION = "5.1.1".freeze + VERSION = "5.2.0".freeze end diff --git a/spec/lib/journaled/audit_log_spec.rb b/spec/lib/journaled/audit_log_spec.rb index d49cd8c..d3e57b4 100644 --- a/spec/lib/journaled/audit_log_spec.rb +++ b/spec/lib/journaled/audit_log_spec.rb @@ -389,6 +389,23 @@ def assign_attrs(**attrs) end end end + + context 'and snapshotting is enabled only on deletion' do + subject { MyModel.new(name: 'bob') } + + before do + described_class.snapshot_on_deletion = true + end + + it 'emits snapshots through the lifecycle of the object, and filters the expected fields' do + expect { subject.save } + .to not_journal_event_including(snapshot: { all: 'attributes', password: '[FILTERED]' }) + expect { subject.update(name: 'robert') } + .to not_journal_event_including(snapshot: { all: 'attributes', password: '[FILTERED]' }) + expect { subject.destroy } + .to journal_event_including(snapshot: { all: 'attributes', password: '[FILTERED]' }) + end + end end context 'and a field is ignored' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7a091e5..c2d3eeb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,6 +24,7 @@ mocks.verify_partial_doubles = true end + config.example_status_persistence_file_path = 'spec/examples.txt' config.order = :random end