Skip to content

Slice 14: Categories — List + Create - #37

Merged
rghvgrv merged 1 commit into
mainfrom
slice-14-categories-list-create
Jul 11, 2026
Merged

Slice 14: Categories — List + Create#37
rghvgrv merged 1 commit into
mainfrom
slice-14-categories-list-create

Conversation

@rghvgrv

@rghvgrv rghvgrv commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

What

Adds GET /api/v1/categories (system defaults + the caller's own) and POST /api/v1/categories (create a custom category). New controller/repository, isolated from SubscriptionsController — no shared files touched, matching the issue's parallel-safety note (safe alongside Slice 15/Payment Sources).

Implements Slice 14 of the backend foundation PRD (#1). Branched from current main — not blocked by the subscriptions read/update/delete work.

Changes

  • src/SubVora.Application/Categories/: CategoryDto, CreateCategoryRequest + validator, ICategoryRepository
  • src/SubVora.Infrastructure/Repositories/CategoryRepository.cs: GetForUserAsync filters user_id IS NULL OR user_id = @userId directly in the query; AddAsync just inserts and lets the DB's UNIQUE (user_id, name) constraint (Slice 2) be the source of truth for duplicates — no pre-check race condition
  • src/SubVora.Api/Controllers/CategoriesController.cs: catches DbUpdateException, narrowed to PostgresException { SqlState: PostgresErrorCodes.UniqueViolation } specifically — an unrelated DB failure still surfaces as 500 rather than being silently reported as a false 409

Verification

  • dotnet build SubVora.slnx — 0 warnings, 0 errors
  • dotnet test SubVora.slnx — 44/44 pass (2 smoke + 17 Infrastructure.Tests + 26 in Api.Tests — this branch is off current main, which is missing the Slice 11-13 subscription tests pending PR Land Slices 11-13 into main (stacked-PR base gap) #36, so the count here reflects 20 pre-existing + 6 new category tests, not 34+6)
  • GetCategories_ReturnsSystemDefaultsAndCallersOwn, CreateCategory_AddsUserOwnedCategory, CreateCategory_WithDuplicateNameForUser_Returns409 — the 3 required cases
  • Plus GetCategories_WithoutAuth_Returns401, CreateCategory_SameNameForDifferentUsers_BothSucceed (proves the constraint is per-user, not global), CreateCategory_WithEmptyName_Returns400

Note: separate PR needed to land the previous 3 slices in main

Heads up if you're reviewing multiple PRs at once: #36 is a corrective PR bringing Slices 11-13 (subscriptions list/get/update/delete) into main — they were merged into a stacked branch (slice-10-subscriptions-create) instead of main by mistake and never landed there. This PR (Categories) doesn't depend on that work, so it was fine to proceed independently, but #36 should get merged too so main has the full subscriptions CRUD surface technical_requirements.md §3 documents.

Acceptance criteria (from #15)

  • GET /api/v1/categories returns both system defaults (user_id IS NULL) and the caller's own categories.
  • POST /api/v1/categories creates a category owned by the caller.
  • Creating a duplicate category name for the same user returns 409, not 500.

Closes #15

Implements Slice 14: GET returns system default categories (user_id IS
NULL) plus the caller's own; POST creates a category owned by the caller.

- ICategoryRepository/CategoryRepository: GetForUserAsync filters
  user_id IS NULL OR user_id = @userid directly in the query projection
  (no separate mapping step); AddAsync just inserts and lets the DB's
  UNIQUE (user_id, name) constraint (Slice 2) do the duplicate check
- CategoriesController.Create catches DbUpdateException, narrowed to
  PostgresException with SqlState 23505 (unique_violation) specifically -
  not a bare catch-all - so unrelated DB failures still surface as 500
  instead of being silently reported as a false 409

New controller/repository, isolated from SubscriptionsController per the
issue's parallel-safety note - no shared files touched.

Tests cover the 3 required cases plus unauthenticated rejection, proving
the same name is allowed across different users (the constraint is
per-user, not global), and empty-name validation.

Closes #15
@rghvgrv
rghvgrv merged commit 136f0df into main Jul 11, 2026
@rghvgrv
rghvgrv deleted the slice-14-categories-list-create branch July 12, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slice 14: Categories — List + Create

1 participant