feat(categories): add categories create to complete the v1 surface#21
Conversation
|
Warning Review limit reached
Your plan currently allows 1 review/hour. Refill in 50 minutes and 7 seconds. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more review capacity refills, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Closes the last `/api/v1` write endpoint that was unexposed in the CLI:
`POST /api/v1/categories` (Api::V1::CategoriesController#create).
Body matches the upstream `params.require(:category).permit(:name,
:color, :icon, :parent_id)` shape (icon is mapped to lucide_icon
server-side, so we keep the original key).
Client-side validation matches the Category model:
- `--name` required (server-side: presence + uniqueness per family).
- `--color` required and validated against `^#[0-9A-Fa-f]{6}$` — same
regex the model uses — so users get fast feedback before a 422.
- `--icon` optional; upstream auto-suggests via
`Category.suggested_icon(name)` when blank.
- `--parent-id` optional; upstream verifies it belongs to the family
and 422s otherwise.
8 unit tests cover: missing name, missing color, invalid hex formats
(short, non-hex, trailing space), accepted hex formats, payload wraps
in `{category: ...}`, optional fields propagate, whitespace-only name
rejected, and command registration with the full flag set.
After this lands the CLI mirrors every `/api/v1` endpoint that is
meaningfully usable from a shell. The remaining auth endpoints
(`signup`, `sso_*`) are intentionally not wrapped — they are
mobile-device-bound flows that don't fit a CLI.
Refs #11.
buildCategoryCreatePayload trimmed whitespace before the presence check but then sent the original `o.Name` in the payload. With `--name ' Food '` that meant the request body included the spaces even though validation looked at the trimmed value — which would clash with the upstream uniqueness check against an existing 'Food'. Send the trimmed value to match validation. Adds regression test TestBuildCategoryCreatePayload_TrimsNameInPayload.
af0f664 to
424e29c
Compare
Last-mile patch for #11: wraps
POST /api/v1/categories, the only remaining/api/v1write endpoint that wasn't exposed in the CLI.Command
Validation (mirrors upstream
Categorymodel)--namevalidates :name, presence: true, uniqueness: { scope: :family_id }--color^#[0-9A-Fa-f]{6}$(same regex as the model)format: { with: /\A#[0-9A-Fa-f]{6}\z/ }--iconcategory_paramsmaps:icon→:lucide_icon;Category.suggested_icon(name)runs if blank--parent-idBody is wrapped as
{"category": {...}}to matchparams.require(:category).Tests (TDD)
8 unit tests:
RequiresName,RequiresColor,WhitespaceOnlyNameRejectedColorMustBeHex— rejects3b82f6,blue,#abc,#GGGGGG,#3b82f6(trailing space)AcceptsValidHex— accepts#3b82f6,#000000,#FFFFFF,#abcdefWrapsInCategoryKey— top-levelcategorykey + optional-field omissionOptionalFields— icon + parent_id propagateTestCategoriesCreateRegistered— full flag set + resolved-name checkgo test ./...green across the suite.Coverage note
After this lands, the CLI mirrors every
/api/v1endpoint that is meaningfully usable from a shell. The remainingauthendpoints (signup,sso_*) are intentionally not wrapped — they are mobile-device-bound flows (requiredevice_id,device_name,device_type,os_version,app_version) that don't fit a CLI use case.Refs #11.