feat(audit_trail): add audit trail plugin gem#320
Open
bexchauveto wants to merge 6 commits into
Open
Conversation
Introduce the forest_admin_audit_trail package: a datasource-agnostic plugin that captures who changed what (before/after diff) for every change Forest performs through its data layer, with pluggable storage (in-memory, log, SQL). Supporting agent/rails wiring: - correlation id generated per request, propagated to the caller as request_id and echoed back via response header (CorrelationIdMiddleware) - record-history route (/_audit-trail/:collection/:id) reading from a configurable store - register the gem in the semantic-release pipeline (.releaserc.js) and exclude its version.rb from rubocop, matching the other packages Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Qlty doesn't post analysis or coverage comments for contributors without a seat. An authorized user can grant @bexchauveto a seat from this pull request's page in Qlty. |
Matches the other packages, which disable MFA and are excluded from the cop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add GET /_audit-trail/correlation/:correlation_key (one key) and the batch GET/POST /_audit-trail/correlations (correlationKeys list, POST body to dodge URL limits), both scoped to a record via collection/recordId params, sharing the per-record auth and store gate. Back them with list_by_correlation / list_by_correlations on the stores (SQL + in-memory + log no-op). Register the correlation source before audit_trail so /_audit-trail/correlation matches it instead of the per-record /_audit-trail/:collection_name/:id (Rails matches in definition order). Mirror the Node README: Rails configuration process plus docs for the record-history, correlation and batch correlation routes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Multiple SqlStore instances mutated the shared Sql::AuditLog.table_name, so stores against different Postgres schemas clobbered each other. Make AuditLog an abstract template and build a per-instance concrete subclass bound to the store's own qualified table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Without the Postgres advisory lock, two instances booting at once can both see a migration as pending and both run its create_table/add_index, crashing the loser with "already exists". Add if_not_exists: true to the table and index DDL. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An empty-string schema made schema? true, producing invalid identifiers like ".audit_migrations". Use present? so nil, "" and whitespace all mean no schema qualification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What
Adds
forest_admin_audit_trail, a new datasource-agnostic plugin gem that captures who changed what (before/after diff) for every change Forest performs through its data layer, persisting it to a pluggable store. This is the Ruby counterpart of the audit trail plugin being released as an npm package on the Node agent v2.Why
The audit trail is shipped as a separate package (not folded into
forest_admin_datasource_customizer) because it carries its own heavy dependencies (activerecord,activesupport,zeitwerk) that shouldn't be forced on users running non-Rails datasources. This keeps parity with the Node decision to ship it as its own npm package.Contents
New gem —
packages/forest_admin_audit_trailPlugin— registers hooks on collections to capture changesDiff— computes before/after field-level diffsInMemoryStore,LogStore,SqlStore(+ SQL migrator / connection base)Agent / Rails wiring
CorrelationId+CorrelationIdMiddleware— one id per request, propagated to the caller asrequest_idand echoed back to the client via a response header (mirrors the NodecorrelationIdMiddleware)/_audit-trail/:collection/:id, reading from a configurable store (ForestAdminRails.audit_trailsetting)Callergainsrequest_idto correlate operations triggered by a single requestRelease plumbing
.releaserc.js(version stamping,gem build && gem push, git asset)version.rbexcluded from rubocopStyle/MutableConstantandStyle/StringLiterals, matching every other package (semantic-release rewrites it with double quotes / no.freezeat release time)false, consistent with sibling gemsNotes
version.rbcarries1.33.1(current monorepo version); semantic-release re-stamps it to the next release version on merge.🤖 Generated with Claude Code
Note
Add audit trail plugin gem for capturing and querying create/update/delete history
forest_admin_audit_trailgem with aPluginclass that hooks into Forest Admin datasource customizer lifecycle events (create/update/delete) to emit structuredAuditRecordentries with field diffs, user identity, and correlation keys.SqlStore(persistent, with auto-migration),InMemoryStore(ephemeral, for testing), andLogStore(write-only, JSON to logger/stderr).GET /_audit-trail/:collection/:idandGET/POST /_audit-trail/correlationsagent routes, gated on a configured store, with pagination, timezone-aware date filters, user id filters, and read permission checks.CorrelationIdMiddlewareandCorrelationId, exposingx-forest-correlation-idin response headers and wiring the id intoCaller#request_idfor audit attribution.ForestAdminRails::Engineto install the correlation id middleware and expose the header via CORS during agent setup.Macroscope summarized 5af078c.