Add expires_at timestamp to Firestore salt documents#602
Add expires_at timestamp to Firestore salt documents#602
expires_at timestamp to Firestore salt documents#602Conversation
WalkthroughThis pull request adds expiration handling to FirestoreSaltStore. It introduces EXPIRE_AT_BUFFER_DAYS = 2 and a private getExpireAt(key, fallbackDate) that parses keys of the form salt:{YYYY-MM-DD}:{siteUuid} to compute an expire_at timestamp (UTC midnight plus two days), falling back to fallbackDate + 2 days when parsing fails. The set() method now persists expire_at alongside created_at and salt. Integration tests were added to cover set(), getOrCreate(), malformed-key fallback behavior, and to verify expire_at is not exposed by get()/getAll(). Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 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 |
|
❌ Staging deployment failed (tree: f8571daa77594d03840e589c05dd0e74dde14ee7) |
🤖 Velo CI Failure AnalysisClassification: 🔴 HARD FAIL
|
|
✅ Deployed to staging (tree: f8571daa77594d03840e589c05dd0e74dde14ee7) |
expires_at timestamp to Firestore salt documents
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/services/salt-store/FirestoreSaltStore.ts (1)
196-200: Treat the TTL switchover as a small schema migration.This only populates
expires_aton newly created documents. Before retiringcleanup(), make sure the Firestore TTL policy targets the renamedexpires_atfield and backfill older salt docs, otherwise pre-existing records will never age out automatically.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/services/salt-store/FirestoreSaltStore.ts` around lines 196 - 200, The current write in FirestoreSaltStore (docRef.create with expires_at from getExpireAt) only sets expires_at for new documents; treat this as a small schema migration: update the Firestore TTL policy to target the renamed expires_at field and add a backfill step to populate expires_at for existing salt documents (e.g., run a one-time migration job inside the FirestoreSaltStore migration path that reads existing records, computes getExpireAt(key, now) or appropriate original-creation-based TTL, and writes expires_at back to those docs), and keep cleanup() until backfill is complete and verified so older records will age out automatically.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/services/salt-store/FirestoreSaltStore.ts`:
- Line 28: The getExpireAt method in FirestoreSaltStore currently accepts
non-canonical date strings via new Date(dateStr); tighten validation by first
matching the key-derived date segment against a strict /^\d{4}-\d{2}-\d{2}$/
regex, then parse year/month/day integers and construct a canonical Date using
new Date(Date.UTC(year, month-1, day)) (or new Date(year, month-1, day)) and
verify the constructed date's year/month/day equal the parsed values to reject
invalid dates like 2026-02-30; if validation fails, fall back to the existing
behavior (use current date minus EXPIRE_AT_BUFFER_DAYS) so getExpireAt preserves
the documented YYYY-MM-DD contract.
---
Nitpick comments:
In `@src/services/salt-store/FirestoreSaltStore.ts`:
- Around line 196-200: The current write in FirestoreSaltStore (docRef.create
with expires_at from getExpireAt) only sets expires_at for new documents; treat
this as a small schema migration: update the Firestore TTL policy to target the
renamed expires_at field and add a backfill step to populate expires_at for
existing salt documents (e.g., run a one-time migration job inside the
FirestoreSaltStore migration path that reads existing records, computes
getExpireAt(key, now) or appropriate original-creation-based TTL, and writes
expires_at back to those docs), and keep cleanup() until backfill is complete
and verified so older records will age out automatically.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0e996f34-1102-4776-b95d-3c2cac436e4b
📒 Files selected for processing (2)
src/services/salt-store/FirestoreSaltStore.tstest/integration/services/salt-store/FirestoreSaltStore.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- test/integration/services/salt-store/FirestoreSaltStore.test.ts
| * | ||
| * Falls back to fallbackDate + 2 days if the key format is unexpected. | ||
| */ | ||
| private getExpireAt(key: string, fallbackDate: Date): Date { |
There was a problem hiding this comment.
thought: my personal preference is to infer return types whenever possible, though when i just when to search for an article to explain my reasoning i was surprised to find a suggestion that they should be explicit to help ai understand better.
another thought: it'd be neat if we could type the key in the expected format instead of just string
(neither of these thoughts need any action)
There was a problem hiding this comment.
(fwiw, i personally still like inferred return types even if ai doesn't lol)
closes https://linear.app/ghost/issue/NY-1083/add-an-expire-at-timestamp-to-future-firestore-keys
Summary
expires_atfield to new Firestore salt documents, computed as the key's date + 2 days at midnight UTCexpire_atis a Firestore-only storage concern — not added toSaltRecordorISaltStoreChanges
FirestoreSaltStore.ts: Added privategetExpireAt(key, fallbackDate)helper that parses the date from the key formatsalt:{date}:{siteUuid}and returns that date + 2 days. Falls back tofallbackDate + 2 daysfor unexpected key formats. Modifiedset()to includeexpires_atwhen writing documents.FirestoreSaltStore.test.ts: Added 6 integration tests coveringset(),getOrCreate(), fallback behavior, and verifyingget()/getAll()don't leakexpires_atintoSaltRecord.Test plan
expires_atwritten correctly viaset()(key date + 2 days)expires_atwritten correctly viagetOrCreate()created_at + 2 daysfor unexpected key formatsget()does not includeexpires_atin returnedSaltRecordgetAll()does not includeexpires_atin returned records