Extracted gift-links side effects into ports#28977
Conversation
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ 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 |
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run ghost:test:ci:integration |
✅ Succeeded | 2m 47s | View ↗ |
nx run ghost:test:integration |
✅ Succeeded | 2m 49s | View ↗ |
nx run ghost:test:legacy |
✅ Succeeded | 2m 50s | View ↗ |
nx run ghost:test:e2e |
✅ Succeeded | 2m 27s | View ↗ |
nx run-many --target=build --projects=tag:publi... |
✅ Succeeded | <1s | View ↗ |
nx run-many -t test:unit -p ghost |
✅ Succeeded | 29s | View ↗ |
nx run @tryghost/admin:build |
✅ Succeeded | 9s | View ↗ |
nx run-many -t lint -p ghost |
✅ Succeeded | 33s | View ↗ |
Additional runs (2) |
✅ Succeeded | ... | View ↗ |
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗
☁️ Nx Cloud last updated this comment at 2026-07-07 08:00:05 UTC
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 `@ghost/core/core/server/services/gift-links/commands.ts`:
- Around line 19-26: The `GiftLinksCommands.ensure()` flow is not atomic: it
checks `post.giftLinks` before the write path, while `mint()` merges on
`post_id`, so concurrent calls can mint different tokens and return one that is
later overwritten. Move the “no gift link exists” check into the transaction
used by `mint()`/the ensure path, and use an insert-or-lock strategy so only one
call can create the association. Make `ensure()` return the live association
created or observed in that transaction, and call `recordAction({context, verb:
'add', subject: postId})` only when this invocation actually created the link.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c8a01828-04e2-4700-9f03-55892801828d
📒 Files selected for processing (19)
ghost/core/core/frontend/services/proxy.jsghost/core/core/frontend/services/routing/controllers/entry/gift-links.tsghost/core/core/server/api/endpoints/gift-links.tsghost/core/core/server/api/endpoints/utils/serializers/output/gift-links.tsghost/core/core/server/services/gift-links/actions.tsghost/core/core/server/services/gift-links/codec.tsghost/core/core/server/services/gift-links/commands.tsghost/core/core/server/services/gift-links/index.tsghost/core/core/server/services/gift-links/models.tsghost/core/core/server/services/gift-links/queries.tsghost/core/core/server/services/gift-links/schema.tsghost/core/core/server/services/gift-links/serializers.tsghost/core/core/server/services/gift-links/service.tsghost/core/test/e2e-api/admin/gift-links.test.tsghost/core/test/e2e-frontend/gift-links-toast-override.test.tsghost/core/test/e2e-frontend/gift-links.test.tsghost/core/test/integration/services/gift-links.test.tsghost/core/test/unit/server/services/gift-links/actions.test.tsghost/core/test/unit/server/services/gift-links/gift-link-token.test.ts
💤 Files with no reviewable changes (4)
- ghost/core/test/integration/services/gift-links.test.ts
- ghost/core/core/server/services/gift-links/serializers.ts
- ghost/core/core/server/services/gift-links/schema.ts
- ghost/core/core/server/services/gift-links/service.ts
f4a20bc to
cf704c4
Compare
cf704c4 to
69523d0
Compare
Kept a single GiftLinksService but pulled the action-log write out into actions.ts (injected as a port rather than reaching for the model), moved the DB row<->domain codec into codec.ts, renamed database.ts to schema.ts, and moved token generation onto the model. Replaced the DB-backed service-level integration suite with unit tests plus HTTP-level e2e coverage for the not-found and concurrency cases.
69523d0 to
ae7e092
Compare
|
Bugbot is not enabled for this team, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
ref #28977 - the ports refactor swapped the integration suite for e2e + unit tests, leaving several service-level behaviours unpinned: revocation of a replaced token, idempotent ensure, removeAll's cross-post scope, the DB-enforced one-live-link invariant, and best-effort action recording as init() composes it - these need a real database but no HTTP, so they are pinned at the integration layer instead of growing the e2e suites - also strengthened the concurrent-ensures e2e assertion so last-writer-wins is actually asserted: the surviving live token must be one a caller received
|
Bugbot is not enabled for this team, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |

Problem
The gift-links service bundled several unrelated concerns into one file — logging actions, mapping database rows, generating tokens, and the core read/write logic — which blurred the boundaries and made the service harder to follow.
Solution
Kept a single service but gave each concern its own home: action logging is now an injected dependency, row mapping and token generation moved out, and the schema definition was renamed for clarity. The slower database-backed service-level tests were replaced with faster unit tests plus end-to-end coverage of the not-found and concurrency behaviours.