CommunityPro / CommunityPro/community-pro-api

Contributors: leaderboard endpoint with period filters (Phase 5.3)

Open
#8 0 comments 0 reactions 0 assignees View on GitHub
contributors
Dominant language
C#
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Scope
`GET /contributors?period=all|90d|30d` (anonymous, paginated) returning the ranked leaderboard the Top Contributors page renders (ISR, revalidate 300s).

## Shape
```json
{ "rank": 1, "login": "octocat", "avatarUrl": "…", "memberId": "…|null", "displayName": "…|null", "mergedPrs": 42 }
```

## Rules
- Aggregate with a plain grouped query over `ContributionEvent` first (materialize into a `contributor_stats` view/table only if it proves slow — don't pre-optimize).
- Join to member profiles via `GithubUserId` **through a public module contract** on the Members/Identity side (e.g. a bulk lookup `IReadOnlyDictionary` by GitHub id) — no cross-module DbContext access.
- Non-member org contributors still appear (login + GitHub avatar, `memberId: null`). Deactivated members appear as non-members (no profile link).
- Period boundaries computed in UTC off `MergedAt`.

## Error codes
`contributors.invalid_period`

## Dependencies
Blocked by #6. Backfill #7 makes the data real but isn't a code dependency.

## Acceptance criteria
- [ ] Ranking is stable (ties broken deterministically, e.g. earliest merge first)
- [ ] Period filters tested at the boundary (e.g. a PR merged exactly 90d ago)
- [ ] Member join returns profile info only for active members
- [ ] Paginated; default page size sensible (~20)

### Conventions (project-wide, non-negotiable)
- .NET 9, records for immutable shapes, file-scoped namespaces, primary constructors where they read well. Minimal-API endpoints grouped per module via `IEndpointModule.MapEndpoints`.
- `Result` (SharedKernel) instead of exception-driven control flow. Endpoint results map failures to ProblemDetails with the stable error codes listed above — the frontend keys off them.
- Module owns its EF Core `DbContext` mapped to its own Postgres schema. Modules never reference each other's internals — cross-module needs go through a public contract interface or an in-process domain event (`IEventPublisher`).
- All external calls (GitHub, Stripe, Brevo, Cloudinary, Meilisearch) behind interfaces owned by the consuming module.
- Every list endpoint paginated (offset is fine). xUnit tests in `tests/CommunityPro.Tests//` following the existing harness patterns (see `Members/MembersTestHarness.cs`).

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by checking dependency #6, then trace the endpoint module pattern through IEndpointModule.MapEndpoints and the existing Members/Identity public contract. Read ContributionEvent and the MembersTestHarness.cs patterns before adding xUnit coverage under tests/CommunityPro.Tests//. Done means a paginated anonymous response, deterministic ranking, UTC period-boundary coverage, stable error mapping, and active-member-only profile data.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, postgresql
Domain
backend-api-design, databases
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.