Conversation
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
There was a problem hiding this comment.
Pull request overview
Adds backend and shared-type support for “collection likes” (like/unlike, listing liked collections, and exposing like counts on collections), plus extends project insights typing/API responses.
Changes:
- Add
/api/collection/likeGET/POST/DELETE endpoints and aCollectionLikeRepositoryfor persistence. - Include
likeCountin collection types and populate it in public collection queries/details. - Add project insights multi-fetch endpoint (
/api/project/insights) and normalize insights responses (isLF,achievements).
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/types/project.ts | Extends ProjectInsights types and introduces achievement type. |
| frontend/types/collection.ts | Adds optional likeCount to collections. |
| frontend/setup/caching.ts | Disables caching for like endpoints and mutating API methods. |
| frontend/server/utils/common.ts | Adds getAuthUsername helper for deriving usernames from OIDC sub. |
| frontend/server/repo/communityCollection.repo.ts | Joins owner info + adds likeCount aggregation for public collection queries/details. |
| frontend/server/repo/collectionLike.repo.ts | New repository implementing like/unlike and listing liked collections. |
| frontend/server/middleware/jwt-auth.ts | Protects /api/collection/like via JWT middleware. |
| frontend/server/api/project/insights.get.ts | New endpoint to fetch insights for multiple projects and normalize response. |
| frontend/server/api/project/[slug]/insights.get.ts | Normalizes single-project insights response (isLF, achievements). |
| frontend/server/api/collection/like/index.post.ts | New endpoint to like a collection. |
| frontend/server/api/collection/like/index.get.ts | New endpoint to list liked collections (paginated). |
| frontend/server/api/collection/like/index.delete.ts | New endpoint to unlike a collection. |
| frontend/server/api/collection/community/my.get.ts | Switches username derivation to getAuthUsername. |
| frontend/server/api/collection/community/index.post.ts | Switches username derivation to getAuthUsername. |
| frontend/server/api/collection/community/[id].put.ts | Switches username derivation to getAuthUsername. |
| frontend/server/api/collection/community/[id].delete.ts | Switches username derivation to getAuthUsername. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export interface ProjectInsightsAchievement { | ||
| leaderboardType: string; | ||
| rank: number; | ||
| totalCount: number; | ||
| } | ||
|
|
||
| export interface ProjectInsights { | ||
| id: string; | ||
| name: string; | ||
| slug: string; | ||
| logoUrl: string; | ||
| isLF: number; | ||
| contributorCount: number; | ||
| organizationCount: number; | ||
| softwareValue: number; | ||
| contributorDependencyCount: number; | ||
| contributorDependencyPercentage: number; | ||
| organizationDependencyCount: number; | ||
| organizationDependencyPercentage: number; | ||
| achievements: [string, number, number][]; |
There was a problem hiding this comment.
ProjectInsightsAchievement is introduced but ProjectInsights.achievements is still typed as a tuple array, and ProjectInsights.isLF is still number even though the API endpoints now return isLF as a boolean and map achievements into {leaderboardType, rank, totalCount} objects. This makes the shared type misleading for API consumers and will cause type drift.
Consider splitting the types into a Tinybird/raw shape (e.g. ProjectInsightsTinybird with isLF: number and tuple achievements) and an API/client shape (e.g. ProjectInsights with isLF: boolean and achievements: ProjectInsightsAchievement[]), and update the endpoints to use the raw type for fetchFromTinybird and the mapped type for responses.
| const response = await fetchFromTinybird<ProjectInsights[]>('/v0/pipes/project_insights.json', { | ||
| slugs: slugs.length > 0 ? slugs : undefined, | ||
| ids: ids.length > 0 ? ids : undefined, | ||
| }); | ||
|
|
||
| return response.data.map((project) => ({ | ||
| ...project, | ||
| isLF: !!project.isLF, | ||
| achievements: | ||
| project.achievements?.map(([leaderboardType, rank, totalCount]) => ({ | ||
| leaderboardType, | ||
| rank, | ||
| totalCount, | ||
| })) ?? [], |
There was a problem hiding this comment.
This handler maps isLF from a number to a boolean and converts achievements tuples into objects, but it still uses the shared ProjectInsights type for the Tinybird response. To keep type safety and avoid confusing consumers, use a raw/Tinybird type for fetchFromTinybird and explicitly type the returned payload as the mapped API shape (with isLF: boolean and structured achievements).
| totalCount, | ||
| }), | ||
| ) ?? [], | ||
| }; |
There was a problem hiding this comment.
This endpoint returns a transformed shape (isLF boolean + structured achievements) but it fetches Tinybird data typed as ProjectInsights[] (which currently models isLF as number and achievements as tuples). Please align the types by introducing a Tinybird/raw type for the fetch and a separate API response type for the mapped return value, then type the handler accordingly.
joanagmaia
left a comment
There was a problem hiding this comment.
LGTM, @gaspergrom can you just address the typing issues raised by cursor bot review?
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
No description provided.