CommunityPro / CommunityPro/community-pro-api
Contributors: org webhook receiver + ContributionEvent (Phase 5.1)
- Dominant language
- C#
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
## Scope
New `CommunityPro.Contributors` module (schema `contributors`): receive org-level GitHub webhooks and record merged PRs. **Merged PRs are the main community metric.**
## Endpoint
`POST /webhooks/github/org` — validate `X-Hub-Signature-256` HMAC with a **separate** org webhook secret (new env var; add to `env.example`). Handle `pull_request` events where `action == "closed" && pull_request.merged == true`.
## Data model
```csharp
public sealed class ContributionEvent
{
public Guid Id { get; set; }
public long GithubUserId { get; set; } // numeric id — the identity key (logins get renamed)
public string GithubLogin { get; set; } // display only, refresh freely
public string Repo { get; set; }
public long PrNumber { get; set; }
public DateTimeOffset MergedAt { get; set; }
}
```
## Idempotency (non-negotiable)
GitHub redelivers. Record `X-GitHub-Delivery` GUIDs in a per-source delivery table with a unique-constraint insert as the guard — copy the Phase 1 practice-repo webhook pattern (see the Identity module's webhook handling). Also add a unique constraint on `(Repo, PrNumber)` as a second layer.
## Error codes
`webhooks.invalid_signature` (401), replays → 200 no-op
## Acceptance criteria
- [ ] Invalid/missing HMAC rejected; valid signature verified against raw body bytes
- [ ] Replayed delivery id is a no-op; duplicate repo+PR is a no-op
- [ ] Non-merge closes ignored
- [ ] xUnit coverage incl. signature validation and idempotency races
### 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
Assessment
This issue has not been assessed yet.