From 6db90ac66d329c718b18ca45f994cf47edfff7a0 Mon Sep 17 00:00:00 2001 From: rdev-worker Date: Sat, 7 Feb 2026 23:15:27 +0000 Subject: [PATCH] build: /spec-feature user-preferences --requirements 'CRUD API for user pref... --- .sdlc/features/user-preferences/manifest.yaml | 2 +- .sdlc/features/user-preferences/spec.md | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 .sdlc/features/user-preferences/spec.md diff --git a/.sdlc/features/user-preferences/manifest.yaml b/.sdlc/features/user-preferences/manifest.yaml index 6c4bc0b..bf4a3ee 100644 --- a/.sdlc/features/user-preferences/manifest.yaml +++ b/.sdlc/features/user-preferences/manifest.yaml @@ -22,7 +22,7 @@ artifacts: status: pending path: review.md spec: - status: pending + status: draft path: spec.md tasks: status: pending diff --git a/.sdlc/features/user-preferences/spec.md b/.sdlc/features/user-preferences/spec.md new file mode 100644 index 0000000..4d92e28 --- /dev/null +++ b/.sdlc/features/user-preferences/spec.md @@ -0,0 +1,70 @@ +# Feature: User Preferences API + +## Problem Statement + +Users need a way to persist and retrieve their application preferences (theme, language, notification settings) across sessions and devices. Currently, no preference storage exists — any customization is lost when a user logs out or switches devices. The preferences-api service exists as a scaffold with example CRUD patterns but has no preference-specific domain logic. + +## User Stories + +- As an authenticated user, I want to save my theme preference so that the UI matches my preference across sessions. +- As an authenticated user, I want to set my preferred language so that the application displays in my chosen locale. +- As an authenticated user, I want to configure my notification settings so that I only receive alerts I care about. +- As a frontend application, I want to retrieve all preferences for a user in a single request so that the UI can be initialized efficiently. +- As an authenticated user, I want to update individual preferences without overwriting all others so that partial updates are safe. + +## Acceptance Criteria + +- [ ] `GET /api/preferences-api/preferences/{user_id}` returns all preferences for a user as key-value pairs in the `{data, meta}` envelope +- [ ] `PUT /api/preferences-api/preferences/{user_id}` creates or updates preferences for a user (upsert semantics) +- [ ] Supported preference keys: `theme` (values: `light`, `dark`, `system`), `language` (ISO 639-1 codes, e.g. `en`, `fr`, `es`), `notifications_enabled` (boolean as string: `true`, `false`) +- [ ] Unknown preference keys are rejected with a `400 Bad Request` and descriptive error +- [ ] Invalid preference values are rejected with a `400 Bad Request` and descriptive error (e.g., `theme: "blue"` is invalid) +- [ ] PUT is idempotent — sending the same request twice produces the same result +- [ ] PUT supports partial updates — sending only `{"theme": "dark"}` does not clear `language` or `notifications_enabled` +- [ ] GET for a user with no preferences returns `200` with an empty object `{}` (not 404) +- [ ] Both endpoints require authentication via JWT (auth middleware) +- [ ] Authenticated users can only access their own preferences (user_id in path must match JWT subject), returning `403 Forbidden` otherwise +- [ ] Preferences are persisted in PostgreSQL with a migration +- [ ] OpenAPI spec documents both endpoints with request/response schemas +- [ ] Handler tests cover success, validation errors, not-found, and authorization cases +- [ ] Service-layer tests cover business logic with mock repository +- [ ] Response times < 50ms at p99 for single-user preference reads + +## Technical Constraints + +- Must follow existing hexagonal architecture: domain → service → port (interface) → adapter (implementation) +- Handlers must return `error`, wrapped with `app.Wrap()` +- Request binding must use `app.Bind()` or `app.BindAndValidate()` +- Errors must use `httperror.BadRequest`, `httperror.NotFound`, `httperror.Forbidden` — never bare `http.Error()` +- Responses must use `httpresponse.OK`, `httpresponse.Created`, `httpresponse.NoContent` with `{data, meta}` envelope +- Routes mount under `/api/preferences-api` to match ingress routing +- Database connection uses `pkg/database` with embedded SQL migrations +- Auth middleware uses `pkg/auth` with JWT validation +- The existing example CRUD code should be replaced, not left alongside preference code +- `user_id` is a UUID, validated at the handler layer + +## Dependencies + +- PostgreSQL database available (connection via `DATABASE_URL` environment variable) +- `pkg/database` package for connection pooling and migration support +- `pkg/auth` package for JWT middleware and user extraction +- JWT secret configured via `JWT_SECRET` environment variable +- `AUTH_ENABLED=true` in production environments + +## Out of Scope + +- User registration or authentication (handled by a separate auth service) +- Preference inheritance or defaults cascade (e.g., org-level defaults) +- Real-time preference sync via WebSockets +- Preference history or audit trail +- Bulk preference operations across multiple users +- Frontend UI for preference management +- Rate limiting on preference endpoints +- Preference schema versioning or migration + +## Open Questions + +1. **Default values**: Should `GET` return system defaults for keys that haven't been explicitly set (e.g., `{"theme": "system", "language": "en", "notifications_enabled": "true"}`) or only return keys the user has explicitly set? +2. **DELETE support**: Should there be a `DELETE /preferences/{user_id}` to reset all preferences, or a `DELETE /preferences/{user_id}/{key}` to reset individual ones? +3. **Extensibility**: When new preference keys are added in the future, should they be added to a configuration file/constant list, or is a code change acceptable? +4. **Admin access**: Should admin users be allowed to read/write preferences for other users, or is self-access the only mode?