feat(api): add post count and sortBy to GET /content/profiles#183
feat(api): add post count and sortBy to GET /content/profiles#183bbornino wants to merge 2 commits into
Conversation
|
Warning Review limit reached
Next review available in: 45 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe /content/profiles endpoint is reworked to compute a post count per profile via a joined select query and to support a new ChangesProfiles Endpoint Post Count and Sorting
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant ProfilesRoute
participant DrizzleDB
Client->>ProfilesRoute: GET /content/profiles?sortBy=posts&page=1&limit=10
ProfilesRoute->>DrizzleDB: select profiles left-joined with postAuthors, postData
DrizzleDB-->>ProfilesRoute: rows filtered by publishedAt not-null and noindex=false, grouped and counted
ProfilesRoute->>ProfilesRoute: apply orderBy (slug asc or postsCount desc), limit/offset
ProfilesRoute-->>Client: JSON profiles with posts count and profileImageUrl
Related issues: Suggested reviewers: playfulprogramming maintainers familiar with the /content/profiles endpoint and Drizzle query patterns. Poem: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/api/src/routes/content/profiles.test.ts (1)
3-28: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winMissing coverage: first join predicate (
postAuthors↔profiles) is never asserted.
mockSelectChainexposesleftJoinPostAuthors, but no test asserts what it was called with. Only the second join (leftJoinPostData, publishedAt/noindex filter) is verified. A regression that swaps or breakseq(postAuthors.authorSlug, profiles.slug)inprofiles.tswould go undetected.♻️ Suggested addition
test("only counts posts with a publishedAt date and noindex false", async () => { const chain = mockSelectChain([]); const response = await app.inject({ method: "GET", url: "/content/profiles", query: { page: "0", limit: "10" }, }); expect(response.statusCode).toBe(200); + expect(chain.leftJoinPostAuthors).toBeCalledWith( + postAuthors, + eq(postAuthors.authorSlug, profiles.slug), + ); expect(chain.leftJoinPostData).toBeCalledWith( postData, and( eq(postData.slug, postAuthors.postSlug), isNotNull(postData.publishedAt), eq(postData.noindex, false), ), ); });Also applies to: 157-175
🤖 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 `@apps/api/src/routes/content/profiles.test.ts` around lines 3 - 28, Add test coverage for the first join in mockSelectChain by asserting leftJoinPostAuthors is called with the expected join predicate between postAuthors and profiles. Update the profiles test cases that exercise the query builder in profiles.ts so they verify eq(postAuthors.authorSlug, profiles.slug) is used, alongside the existing assertions for leftJoinPostData and the publishedAt/noindex filter.
🤖 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 `@apps/api/src/routes/content/profiles.ts`:
- Around line 97-101: The sorting in the profiles query is unstable when sortBy
is "posts" because the orderBy call only uses countDistinct(postData.slug),
which can reshuffle equal-count profiles across pages. Update the ordering in
the profiles route query so the sortBy === "posts" branch uses
desc(countDistinct(postData.slug)) with asc(profiles.slug) as a tiebreaker,
keeping pagination deterministic while preserving the existing sort logic.
---
Nitpick comments:
In `@apps/api/src/routes/content/profiles.test.ts`:
- Around line 3-28: Add test coverage for the first join in mockSelectChain by
asserting leftJoinPostAuthors is called with the expected join predicate between
postAuthors and profiles. Update the profiles test cases that exercise the query
builder in profiles.ts so they verify eq(postAuthors.authorSlug, profiles.slug)
is used, alongside the existing assertions for leftJoinPostData and the
publishedAt/noindex filter.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f3248282-07fd-4130-9e76-60b76c63b7f2
📒 Files selected for processing (3)
apps/api/src/routes/content/profiles.test.tsapps/api/src/routes/content/profiles.tsapps/api/test-utils/setup.ts
Summary
Closes #176.
Extends
GET /content/profileswith apostscount per author and asortByquery param (id|posts), so the Search page can render a "top N authors by post count" list.db.query.profiles.findMany(relational query API) todb.select(core query builder), since ordering by an aggregated to-many count while paginating correctly isn't supported by the relational API.profiles→ left joinpostAuthors→ left joinpostData, filtered topublishedAt is not nullandnoindex = falsein the join condition (same draft-post visibility convention as Endpoint for get post by ID #80's/content/post/:slugendpoint), grouped by profile, withcountDistinct(postData.slug)as the post count.postDatais keyed per locale/version, socountDistinctavoids double-counting a post published in multiple locales.sortBydefaults toid(existing behavior:profiles.slugascending, unchanged) orposts(post count descending, most active authors first).postsas a required field on the response schema, with updated example payloads.Note: the issue text described joining
postAuthorstopostsand filteringpostsbypublishedAt/noindex— but those columns actually live onpostData, notposts, so the query joinspostDatadirectly (skipping an unnecessary extra hop throughposts, sincepostAuthors.postSlugalready FK-referencesposts.slug).Test plan
pnpm test:unit(lint, knip, publint, sherif, vitest across all NX projects)pnpm prettier(check mode)profiles.test.ts: defaultsortBy=idunchanged,sortBy=postsordering, and posts without apublishedAtor withnoindex: trueexcluded from the count (verified via the join's filter predicate, since the DB layer is mocked in this test suite)apps/api/test-utils/setup.ts@playfulprogramming/dbmock to exposeprofiles/postAuthors/postDatatable placeholders anddb.select, needed for the new query builder usage (existing tests for other routes unaffected — all 29 API tests pass)Summary by CodeRabbit
New Features
Bug Fixes
Tests