Skip to content

Adopt browse/ensure/create nomenclature for the gift-links API and drop revoked_at#28837

Merged
rob-ghost merged 1 commit into
mainfrom
chore/gift-links-api-nomenclature
Jun 24, 2026
Merged

Adopt browse/ensure/create nomenclature for the gift-links API and drop revoked_at#28837
rob-ghost merged 1 commit into
mainfrom
chore/gift-links-api-nomenclature

Conversation

@rob-ghost

Copy link
Copy Markdown
Contributor

Problem

The gift-links admin API used issue / reissue / revokeAll verbs and a singular /gift_link route. "Reissue" implied a dyad that does not exist - the operation just creates a new gift link - and the singular route did not match the collection the response already returns. The table also carried a revoked_at validity timestamp.

Solution

Adopt the agreed vocabulary across the controller, service, serializer and routes - browse / ensure / create / removeAll - and pluralise the per-post routes to the collection (/posts/:id/gift_links). The POST is named create rather than add because the framework reserves add for body-driven creates and this create is body-less; POST to the collection carries the "add" semantics at the REST layer.

Drop revoked_at and all its handling: a forthcoming action audit log will own the who/what/when of changes, so a redundant per-row validity timestamp is removed via a drop-column migration. The bulk-remove permission and route are renamed from revoke_all to remove_all to match.

Validated: integration + e2e suites green (the e2e boots Ghost, exercising both migrations, the routes, and the renamed permission).

Stacked on #28783. Retargets to main once that merges.

@github-actions github-actions Bot added the migration [pull request] Includes migration for review label Jun 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

It looks like this PR contains a migration 👀
Here's the checklist for reviewing migrations:

General requirements

  • ⚠️ Tested performance on staging database servers, as performance on local machines is not comparable to a production environment
  • Satisfies idempotency requirement (both up() and down())
  • Does not reference models
  • Filename is in the correct format (and correctly ordered)
  • Targets the next minor version
  • All code paths have appropriate log messages
  • Uses the correct utils
  • Contains a minimal changeset
  • Does not mix DDL/DML operations
  • Tested in MySQL and SQLite

Schema changes

  • Both schema change and related migration have been implemented
  • For index changes: has been performance tested for large tables
  • For new tables/columns: fields use the appropriate predefined field lengths
  • For new tables/columns: field names follow the appropriate conventions
  • Does not drop a non-alpha table outside of a major version

Data changes

  • Mass updates/inserts are batched appropriately
  • Does not loop over large tables/datasets
  • Defends against missing or invalid data
  • For settings updates: follows the appropriate guidelines

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The pull request renames gift-links controller and service actions to browse, ensure, create, and removeAll, and updates the admin routes and serializers to use /gift_links and /gift_links/remove_all. It removes the gift_links.revoked_at column, adds a drop-column migration, and renames the gift_link permission from revokeAll to removeAll. Fixtures, integrity hashes, package versions, and related tests were updated to match the new API and storage behavior.

Possibly related PRs

  • TryGhost/Ghost#28693: Refactors the same gift-links API and service surface that this PR renames.
  • TryGhost/Ghost#28783: Touches the same gift-links service and mint/DB mapping area that this PR changes.
  • TryGhost/Ghost#28809: Updates the same gift-links serializer layer and endpoint response handling.

Suggested reviewers

  • jonatansberg
  • kevinansfield
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main API rename and revoked_at removal.
Description check ✅ Passed The description accurately matches the gift-links API, route, and schema changes in the diff.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/gift-links-api-nomenclature

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Jun 23, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 15ce3ba

Command Status Duration Result
nx run ghost:test:ci:integration ✅ Succeeded 2m 38s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-06-24 14:07:03 UTC

Base automatically changed from chore/gift-links-colocate-db-concerns to main June 24, 2026 10:47
@rob-ghost rob-ghost force-pushed the chore/gift-links-api-nomenclature branch from 0df1b72 to 254daba Compare June 24, 2026 10:57
@rob-ghost rob-ghost marked this pull request as ready for review June 24, 2026 10:57
router.put('/pages/:id/gift_link', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.issue));
router.post('/pages/:id/gift_link', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.reissue));
router.put('/gift_links/revoke_all', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.revokeAll));
router.get('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.post('/pages/:id/gift_link', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.reissue));
router.put('/gift_links/revoke_all', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.revokeAll));
router.get('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.put('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.ensure));
router.put('/gift_links/revoke_all', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.revokeAll));
router.get('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.put('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.ensure));
router.post('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.create));
router.get('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.put('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.ensure));
router.post('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.create));
router.get('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.put('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.ensure));
router.post('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.create));
router.get('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.put('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.ensure));
router.post('/posts/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.create));
router.get('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.put('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.ensure));
router.post('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.create));
router.get('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.browse));
router.put('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.ensure));
router.post('/pages/:id/gift_links', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.create));
router.put('/gift_links/remove_all', mw.authAdminApi, labs.enabledMiddleware('giftLinks'), http(api.giftLinks.removeAll));

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
ghost/core/core/server/web/api/endpoints/admin/routes.js (1)

73-73: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

/gift_links/remove_all is a global, destructive, unauthenticated-by-rate-limit mutation.

This single PUT wipes every post_gift_links row across the whole site. It's gated by authAdminApi + the labs flag + the removeAll permission (consistent with the rest of the Admin API), but CodeQL also flags the route as not rate-limited. Given the blast radius of a global delete, consider whether the bulk endpoint warrants additional protection (e.g., brute/rate-limit middleware) beyond standard auth. The other gift_links routes (67-72) follow the same auth pattern as the rest of the file and don't need separate treatment.

🤖 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 `@ghost/core/core/server/web/api/endpoints/admin/routes.js` at line 73, The
`/gift_links/remove_all` admin mutation in the routes setup is a global
destructive endpoint and currently lacks any rate-limiting protection. Update
the `router.put('/gift_links/remove_all', ...)` entry in the admin routes to add
the same kind of brute/rate-limit middleware used elsewhere in the API, while
keeping the existing `mw.authAdminApi`, `labs.enabledMiddleware('giftLinks')`,
and `http(api.giftLinks.removeAll)` wiring intact.

Source: Linters/SAST tools

🤖 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 `@ghost/core/core/server/web/api/endpoints/admin/routes.js`:
- Line 73: The `/gift_links/remove_all` admin mutation in the routes setup is a
global destructive endpoint and currently lacks any rate-limiting protection.
Update the `router.put('/gift_links/remove_all', ...)` entry in the admin routes
to add the same kind of brute/rate-limit middleware used elsewhere in the API,
while keeping the existing `mw.authAdminApi`,
`labs.enabledMiddleware('giftLinks')`, and `http(api.giftLinks.removeAll)`
wiring intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 756b0e12-7e4c-4559-ac3e-88eb05f16707

📥 Commits

Reviewing files that changed from the base of the PR and between 80b71a3 and 254daba.

📒 Files selected for processing (12)
  • ghost/core/core/server/api/endpoints/gift-links.ts
  • ghost/core/core/server/api/endpoints/utils/serializers/output/gift-links.ts
  • ghost/core/core/server/data/migrations/versions/6.46/2026-06-23-00-00-00-drop-gift-links-revoked-at.js
  • ghost/core/core/server/data/migrations/versions/6.46/2026-06-23-00-00-01-rename-gift-links-revoke-all-permission-to-remove-all.js
  • ghost/core/core/server/data/schema/fixtures/fixtures.json
  • ghost/core/core/server/data/schema/schema.js
  • ghost/core/core/server/services/gift-links/database.ts
  • ghost/core/core/server/services/gift-links/service.ts
  • ghost/core/core/server/web/api/endpoints/admin/routes.js
  • ghost/core/test/e2e-api/admin/gift-links.test.ts
  • ghost/core/test/integration/services/gift-links.test.ts
  • ghost/core/test/utils/fixtures/fixtures.json

@rob-ghost rob-ghost force-pushed the chore/gift-links-api-nomenclature branch 4 times, most recently from a565fce to fb4593e Compare June 24, 2026 13:09
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.12%. Comparing base (a595828) to head (15ce3ba).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #28837      +/-   ##
==========================================
- Coverage   74.23%   73.12%   -1.11%     
==========================================
  Files        1563     1563              
  Lines      134939   134925      -14     
  Branches    16362    16369       +7     
==========================================
- Hits       100166    98658    -1508     
- Misses      33791    35336    +1545     
+ Partials      982      931      -51     
Flag Coverage Δ
admin-tests 43.91% <ø> (-11.25%) ⬇️
e2e-tests 76.39% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ed revoked_at

no ref

Adopts the agreed gift-links vocabulary - browse / ensure / create / removeAll - across the controller, service, serializer and routes, and pluralises the per-post routes to /gift_links. The POST is named create, not the BREAD add: the framework reserves add for body-driven creates and this create is body-less, so POST /gift_links carries the add semantics at the REST layer. Drops the revoked_at validity timestamp and its handling (a forthcoming action audit log will own the who/what/when) via a drop-column migration. The bulk-remove permission and route are renamed from revoke_all to remove_all. Test names and suites follow the new verbs.
@rob-ghost rob-ghost force-pushed the chore/gift-links-api-nomenclature branch from fb4593e to 15ce3ba Compare June 24, 2026 13:33
@rob-ghost rob-ghost enabled auto-merge (rebase) June 24, 2026 14:05
@rob-ghost rob-ghost merged commit 9bb3471 into main Jun 24, 2026
91 of 94 checks passed
@rob-ghost rob-ghost deleted the chore/gift-links-api-nomenclature branch June 24, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

migration [pull request] Includes migration for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants