Skip to content

Commit 6b48c7b

Browse files
Claudehotlong
andauthored
Add documentation for User Preferences Service
- Add comprehensive README with usage examples and API reference - Add CHANGELOG with v1.0.0 initial release notes - Document all features, REST API routes, and schemas - Include integration examples and best practices Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/6c82c1b8-239c-4a91-8794-7e22d1fb5fdc Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent c48068d commit 6b48c7b

2 files changed

Lines changed: 237 additions & 0 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# @objectstack/service-user-preferences
2+
3+
## 1.0.0 (2026-04-09)
4+
5+
### Features
6+
7+
- **Initial Release** - User Preferences Service implementation
8+
- **IUserPreferencesService** - Full service contract with get/set/setMany/delete/getAll/has/clear/listEntries methods
9+
- **IUserFavoritesService** - Specialized favorites service with add/remove/has/toggle/list methods
10+
- **ObjectQL Persistence** - Database-agnostic storage via IDataEngine
11+
- **REST API** - Complete HTTP routes for preferences and favorites management
12+
- `/api/v1/user/preferences` - CRUD operations for preferences
13+
- `/api/v1/user/favorites` - Favorites management endpoints
14+
- **Type Safety** - Full TypeScript support with Zod schemas
15+
- **Prefix Filtering** - Query preferences by key prefix (e.g., `plugin.ai.*`)
16+
- **Well-Known Keys** - Predefined system preference keys (theme, locale, timezone, etc.)
17+
- **Auto-Registration** - Automatic plugin registration in CLI and Studio
18+
19+
### Schema
20+
21+
- **UserPreferenceEntry** - Core preference data model
22+
- **FavoriteEntry** - Favorite item structure with type/target/metadata
23+
- **WellKnownPreferenceKeys** - Enum of reserved system preference keys
24+
25+
### Tests
26+
27+
- Comprehensive unit tests for all service methods
28+
- In-memory IDataEngine stub for fast testing
29+
- Test coverage for scalar values, structured data, and edge cases
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# @objectstack/service-user-preferences
2+
3+
User Preferences Service for ObjectStack — implements IUserPreferencesService and IUserFavoritesService with ObjectQL persistence and REST routes.
4+
5+
## Features
6+
7+
- **Scalar Preferences**: Simple key-value storage for user settings (theme, locale, etc.)
8+
- **Structured Data**: Store complex data structures (favorites, recent items)
9+
- **ObjectQL Persistence**: Leverages IDataEngine for database-agnostic storage
10+
- **REST API**: Full HTTP routes for preferences and favorites management
11+
- **Type Safety**: Complete TypeScript support with Zod schemas
12+
- **Multi-tenant**: User-scoped preferences with isolation
13+
- **Prefix Filtering**: Query preferences by key prefix (e.g., all `plugin.ai.*` settings)
14+
15+
## Installation
16+
17+
```bash
18+
pnpm add @objectstack/service-user-preferences
19+
```
20+
21+
## Usage
22+
23+
### Basic Setup
24+
25+
```typescript
26+
import { ObjectKernel } from '@objectstack/core';
27+
import { ObjectQLPlugin } from '@objectstack/objectql';
28+
import { DriverPlugin } from '@objectstack/runtime';
29+
import { InMemoryDriver } from '@objectstack/driver-memory';
30+
import { UserPreferencesServicePlugin } from '@objectstack/service-user-preferences';
31+
32+
const kernel = new ObjectKernel();
33+
34+
await kernel.use(new ObjectQLPlugin());
35+
await kernel.use(new DriverPlugin(new InMemoryDriver()));
36+
await kernel.use(new UserPreferencesServicePlugin());
37+
38+
await kernel.bootstrap();
39+
40+
const prefs = kernel.getService<IUserPreferencesService>('user-preferences');
41+
```
42+
43+
### Scalar Preferences
44+
45+
```typescript
46+
// Set a preference
47+
await prefs.set('user123', 'theme', 'dark');
48+
49+
// Get a preference
50+
const theme = await prefs.get('user123', 'theme'); // => 'dark'
51+
52+
// Set multiple preferences at once
53+
await prefs.setMany('user123', {
54+
theme: 'dark',
55+
locale: 'en-US',
56+
sidebar_collapsed: true,
57+
});
58+
59+
// Get all preferences
60+
const all = await prefs.getAll('user123');
61+
// => { theme: 'dark', locale: 'en-US', sidebar_collapsed: true }
62+
63+
// Delete a preference
64+
await prefs.delete('user123', 'theme');
65+
66+
// Check if a preference exists
67+
const hasTheme = await prefs.has('user123', 'theme');
68+
```
69+
70+
### Structured Data (Favorites)
71+
72+
```typescript
73+
const favorites = kernel.getService<IUserFavoritesService>('user-favorites');
74+
75+
// Add a favorite
76+
const entry = await favorites.add('user123', {
77+
type: 'view',
78+
target: 'kanban_tasks',
79+
label: 'My Tasks',
80+
icon: 'kanban',
81+
});
82+
83+
// List all favorites
84+
const allFavorites = await favorites.list('user123');
85+
86+
// Remove a favorite
87+
await favorites.remove('user123', entry.id);
88+
89+
// Check if an item is favorited
90+
const isFav = await favorites.has('user123', 'view', 'kanban_tasks');
91+
92+
// Toggle a favorite (add if not exists, remove if exists)
93+
const added = await favorites.toggle('user123', {
94+
type: 'view',
95+
target: 'kanban_tasks',
96+
});
97+
```
98+
99+
### Prefix Filtering
100+
101+
```typescript
102+
// Set plugin-specific preferences
103+
await prefs.setMany('user123', {
104+
'plugin.ai.auto_save': true,
105+
'plugin.ai.model': 'gpt-4',
106+
'plugin.security.mfa_enabled': false,
107+
});
108+
109+
// Get all AI plugin preferences
110+
const aiPrefs = await prefs.getAll('user123', { prefix: 'plugin.ai.' });
111+
// => { 'plugin.ai.auto_save': true, 'plugin.ai.model': 'gpt-4' }
112+
113+
// Clear all AI plugin preferences
114+
await prefs.clear('user123', { prefix: 'plugin.ai.' });
115+
```
116+
117+
## REST API
118+
119+
The plugin automatically registers HTTP routes when started:
120+
121+
### Preferences Routes
122+
123+
- **GET `/api/v1/user/preferences`** - Get all preferences (with optional `?prefix=` query param)
124+
- **GET `/api/v1/user/preferences/:key`** - Get a single preference
125+
- **POST `/api/v1/user/preferences`** - Batch set preferences (body: `{ preferences: { ... } }`)
126+
- **PUT `/api/v1/user/preferences/:key`** - Set a single preference (body: `{ value: ... }`)
127+
- **DELETE `/api/v1/user/preferences/:key`** - Delete a preference
128+
129+
### Favorites Routes
130+
131+
- **GET `/api/v1/user/favorites`** - List all favorites
132+
- **POST `/api/v1/user/favorites`** - Add a favorite (body: `{ type, target, label?, icon?, metadata? }`)
133+
- **DELETE `/api/v1/user/favorites/:id`** - Remove a favorite
134+
- **POST `/api/v1/user/favorites/toggle`** - Toggle a favorite (body: `{ type, target, label?, icon?, metadata? }`)
135+
136+
All routes require authentication and the user's ID is automatically extracted from the request context.
137+
138+
## Well-Known Preference Keys
139+
140+
The following preference keys are reserved for system-level settings:
141+
142+
- `theme` - UI theme (`'light'` | `'dark'` | `'system'`)
143+
- `locale` - User's preferred locale (`'en-US'`, `'zh-CN'`, etc.)
144+
- `timezone` - User's timezone (`'America/New_York'`, etc.)
145+
- `favorites` - User's favorite items (structured array)
146+
- `recent_items` - Recently accessed items (structured array)
147+
- `sidebar_collapsed` - UI: sidebar state (boolean)
148+
- `page_size` - Default pagination size (number)
149+
150+
Plugins can define custom keys using their own namespace, e.g., `plugin.ai.auto_save`.
151+
152+
## Schema
153+
154+
### UserPreferenceEntry
155+
156+
```typescript
157+
{
158+
id: string; // Auto-generated (e.g., 'pref_abc123')
159+
userId: string; // User ID
160+
key: string; // Preference key
161+
value: unknown; // JSON-serializable value
162+
valueType?: string; // Type hint: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null'
163+
createdAt: string; // ISO timestamp
164+
updatedAt: string; // ISO timestamp
165+
}
166+
```
167+
168+
### FavoriteEntry
169+
170+
```typescript
171+
{
172+
id: string; // Auto-generated (e.g., 'fav_xyz789')
173+
type: 'object' | 'view' | 'app' | 'dashboard' | 'report' | 'record';
174+
target: string; // Target reference (object name, view name, etc.)
175+
label?: string; // Display label override
176+
icon?: string; // Icon override
177+
metadata?: Record<string, unknown>; // Custom metadata
178+
createdAt: string; // ISO timestamp
179+
}
180+
```
181+
182+
## Database
183+
184+
The plugin creates a `user_preferences` object in ObjectQL with the following schema:
185+
186+
- `id` (text, primary) - Unique identifier
187+
- `user_id` (text, indexed) - User who owns the preference
188+
- `key` (text, indexed) - Preference key
189+
- `value` (textarea) - JSON-serialized value
190+
- `value_type` (select) - Type hint for client-side type safety
191+
- `created_at` (datetime) - Creation timestamp
192+
- `updated_at` (datetime) - Last update timestamp
193+
194+
Unique composite index: `(user_id, key)`
195+
196+
## Architecture
197+
198+
The service follows ObjectStack's standard patterns:
199+
200+
1. **Spec Layer** (`@objectstack/spec/identity`) - Zod schemas for preferences and favorites
201+
2. **Contract Layer** (`@objectstack/spec/contracts`) - Service interfaces (IUserPreferencesService, IUserFavoritesService)
202+
3. **Implementation Layer** - ObjectQL-based adapter for persistence
203+
4. **Plugin Layer** - Kernel plugin with service registration and HTTP routes
204+
5. **Client Layer** - Type-safe client SDK (future enhancement)
205+
206+
## License
207+
208+
Apache-2.0 © ObjectStack

0 commit comments

Comments
 (0)