Changed gift-links service to split CQRS queries and commands#28977
Changed gift-links service to split CQRS queries and commands#28977rob-ghost wants to merge 1 commit into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ 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 33s | View ↗ |
nx run ghost:test:integration |
✅ Succeeded | 2m 23s | View ↗ |
nx run ghost:test:legacy |
✅ Succeeded | 2m 55s | View ↗ |
nx run ghost:test:e2e |
✅ Succeeded | 2m 15s | View ↗ |
nx run-many --target=build --projects=tag:publi... |
✅ Succeeded | 1s | View ↗ |
nx run-many -t lint -p ghost |
✅ Succeeded | 37s | View ↗ |
nx run-many -t test:unit -p ghost |
✅ Succeeded | 29s | View ↗ |
nx run @tryghost/admin:build |
✅ Succeeded | 6s | 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-06-29 23:03:52 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
Separated the gift-links read path (GiftLinkQueries) from the write path (GiftLinkCommands) so reads and mutations no longer share one class, and extracted action recording and the row codec into their own modules. The entry controller now resolves tokens through queries directly, keeping the read seam free of command/action dependencies.
f4a20bc to
cf704c4
Compare

The gift-links service had grown into a single
GiftLinksServiceclass that mixed read and write responsibilities, so anything that only needed to resolve a token (such as the frontend entry controller) still pulled in the full surface of mutation and action-logging logic. That coupling made the read path harder to reason about and harder to reuse in isolation.This change reshapes the service into a CQRS layout. Reads now live in
GiftLinkQueriesand writes inGiftLinkCommands, so the two paths no longer share a class or each other's dependencies. Action recording and the database row codec were pulled out into their ownactions.tsandcodec.tsmodules, and the database concerns file was renamed fromdatabase.tstoschema.tsto better reflect its contents. The entry routing controller now resolves gift-link tokens through the query path directly, keeping that read seam free of command and action dependencies.The old
service.tsand its integration test were removed in favour of the split modules, with new focused unit tests covering the extracted actions. This is an internal refactor with no behavioural or user-facing change.