build: /spec-feature user-preferences --requirements 'CRUD API for user pref...
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
rdev-worker 2026-02-08 09:55:18 +00:00
parent c5a7a54a9d
commit 3624cb8e6d
2 changed files with 142 additions and 1 deletions

View File

@ -22,7 +22,7 @@ artifacts:
status: pending
path: review.md
spec:
status: pending
status: draft
path: spec.md
tasks:
status: pending

View File

@ -0,0 +1,141 @@
# Feature: User Preferences API
## Problem Statement
Users need a way to persist and retrieve their application preferences (theme, language, notification settings) so that their experience is consistent across sessions and devices. Currently, the `preferences-api` service contains only scaffold code with no preference management functionality.
## User Stories
- As a user, I want to save my theme preference (light/dark) so that the UI renders consistently across sessions.
- As a user, I want to save my language preference so that the application displays content in my chosen language.
- As a user, I want to configure my notification settings so that I only receive the notifications I care about.
- As a user, I want to retrieve all my preferences in a single request so that the frontend can initialize quickly.
- As a user, I want to update individual preferences without overwriting others so that partial updates are safe.
## Acceptance Criteria
- [ ] `GET /api/preferences-api/preferences/{user_id}` returns all preferences for the given user as key-value pairs
- [ ] `GET /api/preferences-api/preferences/{user_id}` returns an empty preferences object (not 404) for users with no saved preferences
- [ ] `PUT /api/preferences-api/preferences/{user_id}` creates or updates preferences for the given user (upsert semantics)
- [ ] `PUT /api/preferences-api/preferences/{user_id}` supports partial updates — only provided keys are changed, omitted keys are preserved
- [ ] Supported preference keys: `theme` (values: `light`, `dark`), `language` (ISO 639-1 codes, e.g. `en`, `es`, `fr`), `notifications_enabled` (boolean)
- [ ] Invalid preference values are rejected with a 400 Bad Request and descriptive error message
- [ ] Unknown preference keys are rejected with a 400 Bad Request
- [ ] Both endpoints require authentication — unauthenticated requests receive 401
- [ ] Users can only access their own preferences — mismatched user ID returns 403
- [ ] Preferences are persisted to PostgreSQL (not in-memory)
- [ ] All endpoints return responses in the standard `{data, meta}` envelope
- [ ] Endpoints are documented in OpenAPI spec via `spec.go`
- [ ] Domain layer validates preference keys and values independently of HTTP layer
- [ ] Service, handler, and repository layers follow existing hexagonal architecture patterns
- [ ] Unit tests cover service layer business logic (valid/invalid keys, partial updates, authorization)
- [ ] Integration tests cover handler layer HTTP behavior (status codes, response format, error cases)
## API Design
### GET /api/preferences-api/preferences/{user_id}
**Response 200:**
```json
{
"data": {
"user_id": "uuid",
"preferences": {
"theme": "dark",
"language": "en",
"notifications_enabled": true
},
"updated_at": "2026-02-08T12:00:00Z"
},
"meta": {
"request_id": "...",
"timestamp": "..."
}
}
```
**Response 200 (no preferences saved yet):**
```json
{
"data": {
"user_id": "uuid",
"preferences": {},
"updated_at": null
},
"meta": { ... }
}
```
### PUT /api/preferences-api/preferences/{user_id}
**Request:**
```json
{
"preferences": {
"theme": "dark",
"notifications_enabled": false
}
}
```
**Response 200:**
```json
{
"data": {
"user_id": "uuid",
"preferences": {
"theme": "dark",
"language": "en",
"notifications_enabled": false
},
"updated_at": "2026-02-08T12:00:05Z"
},
"meta": { ... }
}
```
## Technical Constraints
- Must use PostgreSQL for persistence (migration in `services/preferences-api/migrations/`)
- Must follow the existing hexagonal architecture: domain → service → port → adapter
- Must use `app.Wrap()` handler pattern, `app.BindAndValidate()` for request binding
- Must use `httperror.*` for error responses, `httpresponse.*` for success responses
- Must use chi router with `{param}` brace syntax for URL parameters
- Must use `auth.Middleware()` for authentication and validate user ownership
- Preference keys are a closed set — only `theme`, `language`, `notifications_enabled` are accepted
- Preference values must be validated per-key (enum for theme, ISO code pattern for language, boolean for notifications)
## Database Schema
```sql
CREATE TABLE user_preferences (
user_id UUID PRIMARY KEY,
preferences JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
Storing preferences as JSONB allows flexible key-value storage while the application layer enforces the allowed key set and value validation.
## Dependencies
- PostgreSQL database accessible from the service (connection config via `DatabaseConfig`)
- Authentication system functional (`auth.Middleware()` with JWT validation)
- Existing `pkg/app`, `pkg/httperror`, `pkg/httpresponse`, `pkg/auth`, `pkg/openapi` packages
## Out of Scope
- Preference defaults / fallback values at the API level (frontend handles defaults)
- Preference history or versioning
- Bulk preference operations across multiple users
- Admin endpoints to manage other users' preferences
- Real-time preference change notifications (WebSocket/SSE)
- Custom/user-defined preference keys beyond the initial set
## Open Questions
1. **Language validation strictness:** Should language values be validated against a specific list of supported languages, or accept any valid ISO 639-1 code?
2. **Default preferences:** Should the API return default values for unset preferences (e.g., `theme: "light"` if never set), or let the frontend handle defaults?
3. **Rate limiting:** Should preference updates be rate-limited to prevent abuse?
4. **Removing the example scaffold:** Should the existing Example entity/handlers/routes be removed as part of this feature, or left for reference?