fix: reduces update logs that contain no actual changes#547
fix: reduces update logs that contain no actual changes#547andrestejerina97 wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughFormatter equality checks suppress semantically identical field changes (including same-instant DateTimes) and expose hasMeaningfulChanges(); the OTLP strategy checks this and skips emitting audit jobs for no-op updates. Unit tests verify date-noise suppression and job dispatch behavior. ChangesSuppress Meaningless Audit Log Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
📘 OpenAPI / Swagger preview ➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-547/ This page is automatically updated on each push to this PR. |
|
@caseylocker it's ready to review |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/Audit/AuditLogOtlpStrategy.php`:
- Around line 81-85: The current direct call
EmitAuditLogJob::dispatch($this->getLogMessage($event_type),
$auditData)->onQueue('audit-logs') removed the DB fallback and can drop audit
logs on queue failures; restore the previous fallback-backed emission by
replacing this line with the project's fallback helper (e.g.,
EmitAuditLogJob::dispatchWithFallback(...) or the existing
emitAuditJobWithDbFallback helper) passing $this->getLogMessage($event_type) and
$auditData and targeting 'audit-logs'; if no helper exists, wrap
EmitAuditLogJob::dispatch(...)->onQueue('audit-logs') in a try/catch and persist
$auditData to the AuditLog/backup store inside the catch so queue failures fall
back to the DB.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2216cbcc-c568-4c92-9d61-54cd0664dc07
📒 Files selected for processing (5)
app/Audit/AbstractAuditLogFormatter.phpapp/Audit/AuditLogOtlpStrategy.phptests/Unit/Audit/AbstractAuditLogFormatterDateNoiseTest.phptests/Unit/Audit/AbstractAuditLogFormatterHasMeaningfulChangesTest.phptests/Unit/Audit/AuditLogOtlpStrategyNoOpSuppressionTest.php
620d972 to
6a6d3eb
Compare
|
📘 OpenAPI / Swagger preview ➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-547/ This page is automatically updated on each push to this PR. |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
tests/Unit/Audit/AuditLogOtlpStrategyNoOpSuppressionTest.php (1)
90-90: ⚡ Quick winAssert dispatch targets the expected queue as well.
Given this stack’s contract, the positive-path test should also verify
EmitAuditLogJobis queued onaudit-logs, not just dispatched.Proposed patch
- Bus::assertDispatched(EmitAuditLogJob::class); + Bus::assertDispatched(EmitAuditLogJob::class, function (EmitAuditLogJob $job): bool { + return $job->queue === 'audit-logs'; + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Unit/Audit/AuditLogOtlpStrategyNoOpSuppressionTest.php` at line 90, Update the positive-path assertion to verify the job was queued on the expected queue: replace or augment Bus::assertDispatched(EmitAuditLogJob::class) with Bus::assertDispatched(EmitAuditLogJob::class, function ($job) { return /* check queue name */ $job->queue === 'audit-logs' || method_exists($job, 'onQueue') && $job->onQueue === 'audit-logs'; }); ensuring the closure inspects the dispatched EmitAuditLogJob instance to confirm it's targeted at 'audit-logs'.tests/Unit/Audit/AbstractAuditLogFormatterDateNoiseTest.php (1)
37-37: ⚡ Quick winUse the shared no-changes constant instead of a string literal.
These assertions should reference
AbstractAuditLogFormatter::NO_CHANGES_REGISTERED_MESSAGEto keep tests aligned with the formatter contract and avoid brittle message duplication.Proposed patch
- $this->assertSame('properties without changes registered', $details); + $this->assertSame(AbstractAuditLogFormatter::NO_CHANGES_REGISTERED_MESSAGE, $details); @@ - $this->assertSame('properties without changes registered', $details); + $this->assertSame(AbstractAuditLogFormatter::NO_CHANGES_REGISTERED_MESSAGE, $details);Also applies to: 57-57
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/Unit/Audit/AbstractAuditLogFormatterDateNoiseTest.php` at line 37, Replace the hard-coded string 'properties without changes registered' in the test assertions with the shared constant AbstractAuditLogFormatter::NO_CHANGES_REGISTERED_MESSAGE; update the assertions in AbstractAuditLogFormatterDateNoiseTest (both occurrences around the current checks that compare $details) to use that constant so the test references the formatter's contract instead of duplicating the literal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/Unit/Audit/AbstractAuditLogFormatterDateNoiseTest.php`:
- Line 37: Replace the hard-coded string 'properties without changes registered'
in the test assertions with the shared constant
AbstractAuditLogFormatter::NO_CHANGES_REGISTERED_MESSAGE; update the assertions
in AbstractAuditLogFormatterDateNoiseTest (both occurrences around the current
checks that compare $details) to use that constant so the test references the
formatter's contract instead of duplicating the literal.
In `@tests/Unit/Audit/AuditLogOtlpStrategyNoOpSuppressionTest.php`:
- Line 90: Update the positive-path assertion to verify the job was queued on
the expected queue: replace or augment
Bus::assertDispatched(EmitAuditLogJob::class) with
Bus::assertDispatched(EmitAuditLogJob::class, function ($job) { return /* check
queue name */ $job->queue === 'audit-logs' || method_exists($job, 'onQueue') &&
$job->onQueue === 'audit-logs'; }); ensuring the closure inspects the dispatched
EmitAuditLogJob instance to confirm it's targeted at 'audit-logs'.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1056b6fc-96e5-4488-a922-290a6046d636
📒 Files selected for processing (5)
app/Audit/AbstractAuditLogFormatter.phpapp/Audit/AuditLogOtlpStrategy.phptests/Unit/Audit/AbstractAuditLogFormatterDateNoiseTest.phptests/Unit/Audit/AbstractAuditLogFormatterHasMeaningfulChangesTest.phptests/Unit/Audit/AuditLogOtlpStrategyNoOpSuppressionTest.php
🚧 Files skipped from review as they are similar to previous changes (2)
- app/Audit/AbstractAuditLogFormatter.php
- app/Audit/AuditLogOtlpStrategy.php
caseylocker
left a comment
There was a problem hiding this comment.
LGTM to land this, closes the SelectionPlan screenshot case Mark reported.
@smarcet , the following came out of a second-pass review and extends the fix to more fully cover Mark's "widespread, not just Selection Plans" hypothesis and the preferred shape ("return a null description").
It's your call since the current pr already addresses the specific clickup ticket. Just wanted to be complete.
Follow-up plan (separate PR)
Correctness gaps in the current implementation
AbstractAuditLogFormatter.php:228-238—getTimestamp()returns integer seconds, so theformat('U.u')fallback is unreachable and same-second microsecond changes are wrongly suppressed. Collapse to a singleformat('U.u') === format('U.u')compare.AbstractAuditLogFormatter.php:240-242— scalar branch reusesformatChangeValue()for equality, sonullvs"null",truevs"true", and1vs"1"all compare equal. Replace with: null pair → strict===; otherwise requiregettypematch plus strict===. No string-coercion fallback.AuditLogOtlpStrategy.php:65-71—hasMeaningfulChanges()isn't onIAuditLogFormatter, andAuditLogFormatterFactory.php:163can instantiate config-driven formatters that don't extend the abstract. Drop the pre-gate; switch the existingis_null($description)check at line 73 toempty($description)so null-bubble fromformat()does the work.
Coverage gaps (Mark's "widespread" concern)
EntityUpdateAuditLogFormatter: applyvaluesAreEffectivelyEqualbefore every branch (child-entity at lines 103-108, BaseEntity at 111-142, plain at 145-157), so same-instance objects and same-instant datetimes are skipped while different entity instances still log. Returnnullon empty result. This closes the OTLP fallback case AND the DB-strategy noise forSummitEvent/SummitAttendeeBadge(the only entitiesAuditLoggerFactoryresolves).- Make
buildChangeDetails(): ?stringreturnnullinstead of the"properties without changes registered"sentinel, then update each of the ~85 callers (andBasePresentationAuditLogFormatter::formatUpdate(): ?string) to propagate the null so each concreteformat()returnsnullon no-op. That lets both strategies' existingis_null/emptyguards
do the suppression — matching Sebastian's directive structurally and removing the need for a strategy-level gate.
Polish
- Trailing whitespace at
AuditLogOtlpStrategy.php:86, indent oftryat line 53, blank EOF in the new OTLP test (AuditLogOtlpStrategyNoOpSuppressionTest.php:93). DefaultEntityManyToManyCollectionDeleteAuditLogFormatter.php:73emits"Removed IDs: []"when$deletedCount === 0. Same family of audit noise; returnnullinstead. Outside the original ticket but cheap to fix together — call out as adjacent cleanup in the PR description.
Tests to add
- Microsecond DateTime change → logged.
nullvs"null",truevs"true",falsevs"false",1vs"1"→ logged.- Ignored-fields-only change_set → null description, no dispatch on both strategies.
EntityUpdateAuditLogFormatter: same-instance BaseEntity → skipped; different-instance BaseEntity (same id) → logged; tz-noise DateTime → skipped; real DateTime change → logged.- OTLP suppresses empty-string description (not just null).
- DB strategy does not call
createAuditLogEntrywhenformat()returns null. - PrePaid + Sponsor discount-code formatters return null on no-op (they currently add "current state" context).
ref: https://app.clickup.com/t/86b9xe3fk
Summary by CodeRabbit
New Features
Tests