Slice 14: Categories — List + Create - #37
Merged
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
GET /api/v1/categories(system defaults + the caller's own) andPOST /api/v1/categories(create a custom category). New controller/repository, isolated fromSubscriptionsController— 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,ICategoryRepositorysrc/SubVora.Infrastructure/Repositories/CategoryRepository.cs:GetForUserAsyncfiltersuser_id IS NULL OR user_id = @userIddirectly in the query;AddAsyncjust inserts and lets the DB'sUNIQUE (user_id, name)constraint (Slice 2) be the source of truth for duplicates — no pre-check race conditionsrc/SubVora.Api/Controllers/CategoriesController.cs: catchesDbUpdateException, narrowed toPostgresException { SqlState: PostgresErrorCodes.UniqueViolation }specifically — an unrelated DB failure still surfaces as 500 rather than being silently reported as a false 409Verification
dotnet build SubVora.slnx— 0 warnings, 0 errorsdotnet test SubVora.slnx— 44/44 pass (2 smoke + 17 Infrastructure.Tests + 26 in Api.Tests — this branch is off currentmain, 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 casesGetCategories_WithoutAuth_Returns401,CreateCategory_SameNameForDifferentUsers_BothSucceed(proves the constraint is per-user, not global),CreateCategory_WithEmptyName_Returns400Note: 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 ofmainby 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 somainhas the full subscriptions CRUD surfacetechnical_requirements.md§3 documents.Acceptance criteria (from #15)
GET /api/v1/categoriesreturns both system defaults (user_id IS NULL) and the caller's own categories.POST /api/v1/categoriescreates a category owned by the caller.Closes #15