Remove MSW handlers and update demo credentials for improved standardization
- Deleted `admin.ts`, `auth.ts`, and `users.ts` MSW handler files to streamline demo mode setup. - Updated demo credentials logic in `DemoCredentialsModal` and `DemoModeBanner` for stronger password requirements (≥12 characters). - Refined documentation in `CLAUDE.md` to align with new credential standards and auto-generated MSW workflows.
This commit is contained in:
402
frontend/docs/MSW_AUTO_GENERATION.md
Normal file
402
frontend/docs/MSW_AUTO_GENERATION.md
Normal file
@@ -0,0 +1,402 @@
|
||||
# MSW Auto-Generation from OpenAPI
|
||||
|
||||
## Overview
|
||||
|
||||
MSW (Mock Service Worker) handlers are **automatically generated** from your OpenAPI specification, ensuring perfect synchronization between your backend API and demo mode.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Backend API Changes
|
||||
↓
|
||||
npm run generate:api
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ 1. Fetches OpenAPI spec │
|
||||
│ 2. Generates TypeScript API client │
|
||||
│ 3. Generates MSW handlers │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
src/mocks/handlers/
|
||||
├── generated.ts (AUTO-GENERATED - DO NOT EDIT)
|
||||
├── overrides.ts (CUSTOM LOGIC - EDIT AS NEEDED)
|
||||
└── index.ts (MERGES BOTH)
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. Automatic Generation
|
||||
|
||||
When you run:
|
||||
```bash
|
||||
npm run generate:api
|
||||
```
|
||||
|
||||
The system:
|
||||
1. Fetches `/api/v1/openapi.json` from backend
|
||||
2. Generates TypeScript API client (`src/lib/api/generated/`)
|
||||
3. **NEW:** Generates MSW handlers (`src/mocks/handlers/generated.ts`)
|
||||
|
||||
### 2. Generated Handlers
|
||||
|
||||
The generator (`scripts/generate-msw-handlers.ts`) creates handlers with:
|
||||
|
||||
**Smart Response Logic:**
|
||||
- **Auth endpoints** → Use `validateCredentials()` and `setCurrentUser()`
|
||||
- **User endpoints** → Use `currentUser` and mock data
|
||||
- **Admin endpoints** → Check `is_superuser` + return paginated data
|
||||
- **Generic endpoints** → Return success response
|
||||
|
||||
**Example Generated Handler:**
|
||||
```typescript
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
http.post(`${API_BASE_URL}/api/v1/auth/login`, async ({ request, params }) => {
|
||||
await delay(NETWORK_DELAY);
|
||||
|
||||
const body = (await request.json()) as any;
|
||||
const user = validateCredentials(body.email, body.password);
|
||||
|
||||
if (!user) {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Incorrect email or password' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const accessToken = `demo-access-${user.id}-${Date.now()}`;
|
||||
const refreshToken = `demo-refresh-${user.id}-${Date.now()}`;
|
||||
|
||||
setCurrentUser(user);
|
||||
|
||||
return HttpResponse.json({
|
||||
access_token: accessToken,
|
||||
refresh_token: refreshToken,
|
||||
token_type: 'bearer',
|
||||
expires_in: 900,
|
||||
});
|
||||
}),
|
||||
```
|
||||
|
||||
### 3. Custom Overrides
|
||||
|
||||
For complex logic that can't be auto-generated, use `overrides.ts`:
|
||||
|
||||
```typescript
|
||||
// src/mocks/handlers/overrides.ts
|
||||
|
||||
export const overrideHandlers = [
|
||||
// Example: Simulate rate limiting
|
||||
http.post(`${API_BASE_URL}/api/v1/auth/login`, async ({ request }) => {
|
||||
// 10% chance of rate limit
|
||||
if (Math.random() < 0.1) {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Too many login attempts' },
|
||||
{ status: 429 }
|
||||
);
|
||||
}
|
||||
// Fall through to generated handler
|
||||
}),
|
||||
|
||||
// Example: Complex validation
|
||||
http.post(`${API_BASE_URL}/api/v1/users`, async ({ request }) => {
|
||||
const body = await request.json();
|
||||
|
||||
// Custom validation logic
|
||||
if (body.email.endsWith('@blocked.com')) {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Email domain not allowed' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Fall through to generated handler
|
||||
}),
|
||||
];
|
||||
```
|
||||
|
||||
**Override Precedence:**
|
||||
Overrides are applied FIRST, so they take precedence over generated handlers.
|
||||
|
||||
## Benefits
|
||||
|
||||
### ✅ Zero Manual Work
|
||||
|
||||
**Before:**
|
||||
```bash
|
||||
# Backend adds new endpoint
|
||||
# 1. Run npm run generate:api
|
||||
# 2. Manually add MSW handler
|
||||
# 3. Test demo mode
|
||||
# 4. Fix bugs
|
||||
# 5. Repeat for every endpoint change
|
||||
```
|
||||
|
||||
**After:**
|
||||
```bash
|
||||
# Backend adds new endpoint
|
||||
npm run generate:api # Done! MSW auto-synced
|
||||
```
|
||||
|
||||
### ✅ Always In Sync
|
||||
|
||||
- OpenAPI spec is single source of truth
|
||||
- Generator reads same spec as API client
|
||||
- Impossible to have mismatched endpoints
|
||||
- New endpoints automatically available in demo mode
|
||||
|
||||
### ✅ Type-Safe
|
||||
|
||||
```typescript
|
||||
// Generated handlers use your mock data
|
||||
import { validateCredentials, currentUser } from '../data/users';
|
||||
import { sampleOrganizations } from '../data/organizations';
|
||||
import { adminStats } from '../data/stats';
|
||||
|
||||
// Everything is typed!
|
||||
```
|
||||
|
||||
### ✅ Batteries Included
|
||||
|
||||
Generated handlers include:
|
||||
- ✅ Network delays (300ms - realistic UX)
|
||||
- ✅ Auth checks (401/403 responses)
|
||||
- ✅ Pagination support
|
||||
- ✅ Path parameters
|
||||
- ✅ Request body parsing
|
||||
- ✅ Proper HTTP methods
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
frontend/
|
||||
├── scripts/
|
||||
│ ├── generate-api-client.sh # Main generation script
|
||||
│ └── generate-msw-handlers.ts # MSW handler generator
|
||||
│
|
||||
├── src/
|
||||
│ ├── lib/api/generated/ # Auto-generated API client
|
||||
│ │ ├── client.gen.ts
|
||||
│ │ ├── sdk.gen.ts
|
||||
│ │ └── types.gen.ts
|
||||
│ │
|
||||
│ └── mocks/
|
||||
│ ├── browser.ts # MSW setup
|
||||
│ ├── data/ # Mock data (EDIT THESE)
|
||||
│ │ ├── users.ts
|
||||
│ │ ├── organizations.ts
|
||||
│ │ └── stats.ts
|
||||
│ └── handlers/
|
||||
│ ├── generated.ts # ⚠️ AUTO-GENERATED
|
||||
│ ├── overrides.ts # ✅ EDIT FOR CUSTOM LOGIC
|
||||
│ └── index.ts # Merges both
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Adding New Backend Endpoint
|
||||
|
||||
1. **Add endpoint to backend** (FastAPI route)
|
||||
2. **Regenerate clients:**
|
||||
```bash
|
||||
cd frontend
|
||||
npm run generate:api
|
||||
```
|
||||
3. **Test demo mode:**
|
||||
```bash
|
||||
NEXT_PUBLIC_DEMO_MODE=true npm run dev
|
||||
```
|
||||
4. **Done!** New endpoint automatically works in demo mode
|
||||
|
||||
### Customizing Handler Behavior
|
||||
|
||||
If generated handler doesn't fit your needs:
|
||||
|
||||
1. **Add override** in `src/mocks/handlers/overrides.ts`
|
||||
2. **Keep generated handler** (don't edit `generated.ts`)
|
||||
3. **Override takes precedence** automatically
|
||||
|
||||
Example:
|
||||
```typescript
|
||||
// overrides.ts
|
||||
export const overrideHandlers = [
|
||||
// Override auto-generated login to add 2FA simulation
|
||||
http.post(`${API_BASE_URL}/api/v1/auth/login`, async ({ request }) => {
|
||||
const body = await request.json();
|
||||
|
||||
// Simulate 2FA requirement for admin users
|
||||
if (body.email.includes('admin') && !body.two_factor_code) {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Two-factor authentication required' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
// Fall through to generated handler
|
||||
}),
|
||||
];
|
||||
```
|
||||
|
||||
### Updating Mock Data
|
||||
|
||||
Mock data is separate from handlers:
|
||||
|
||||
```typescript
|
||||
// src/mocks/data/users.ts
|
||||
export const demoUser: UserResponse = {
|
||||
id: 'demo-user-id-1',
|
||||
email: 'demo@example.com',
|
||||
first_name: 'Demo',
|
||||
last_name: 'User',
|
||||
// ... add more fields as backend evolves
|
||||
};
|
||||
```
|
||||
|
||||
**To update:**
|
||||
1. Edit `data/*.ts` files
|
||||
2. Handlers automatically use updated data
|
||||
3. No regeneration needed!
|
||||
|
||||
## Generator Internals
|
||||
|
||||
The generator (`scripts/generate-msw-handlers.ts`) does:
|
||||
|
||||
1. **Parse OpenAPI spec**
|
||||
```typescript
|
||||
const spec = JSON.parse(fs.readFileSync(specPath, 'utf-8'));
|
||||
```
|
||||
|
||||
2. **For each endpoint:**
|
||||
- Convert path params: `{id}` → `:id`
|
||||
- Determine handler category (auth/users/admin)
|
||||
- Generate appropriate mock response
|
||||
- Add network delay
|
||||
- Include error handling
|
||||
|
||||
3. **Write generated file:**
|
||||
```typescript
|
||||
fs.writeFileSync('src/mocks/handlers/generated.ts', handlerCode);
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Generated handler doesn't work
|
||||
|
||||
**Check:**
|
||||
1. Is backend running? (`npm run generate:api` requires backend)
|
||||
2. Check console for `[MSW]` warnings
|
||||
3. Verify `generated.ts` exists and has your endpoint
|
||||
4. Check path parameters match exactly
|
||||
|
||||
**Debug:**
|
||||
```bash
|
||||
# See what endpoints were generated
|
||||
cat src/mocks/handlers/generated.ts | grep "http\."
|
||||
```
|
||||
|
||||
### Need custom behavior
|
||||
|
||||
**Don't edit `generated.ts`!** Use overrides instead:
|
||||
|
||||
```typescript
|
||||
// overrides.ts
|
||||
export const overrideHandlers = [
|
||||
http.post(`${API_BASE_URL}/your/endpoint`, async ({ request }) => {
|
||||
// Your custom logic
|
||||
}),
|
||||
];
|
||||
```
|
||||
|
||||
### Regeneration fails
|
||||
|
||||
```bash
|
||||
# Manual regeneration
|
||||
cd frontend
|
||||
curl -s http://localhost:8000/api/v1/openapi.json > /tmp/openapi.json
|
||||
npx tsx scripts/generate-msw-handlers.ts /tmp/openapi.json
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ Do
|
||||
|
||||
- Run `npm run generate:api` after backend changes
|
||||
- Use `overrides.ts` for complex logic
|
||||
- Keep mock data in `data/` files
|
||||
- Test demo mode regularly
|
||||
- Commit `overrides.ts` to git
|
||||
|
||||
### ❌ Don't
|
||||
|
||||
- Don't edit `generated.ts` manually (changes will be overwritten)
|
||||
- Don't commit `generated.ts` to git (it's auto-generated)
|
||||
- Don't duplicate logic between overrides and generated
|
||||
- Don't skip regeneration after API changes
|
||||
|
||||
## Advanced: Generator Customization
|
||||
|
||||
Want to customize the generator itself?
|
||||
|
||||
Edit `scripts/generate-msw-handlers.ts`:
|
||||
|
||||
```typescript
|
||||
function generateMockResponse(path: string, method: string, operation: any): string {
|
||||
// Your custom generation logic
|
||||
|
||||
if (path.includes('/your-special-endpoint')) {
|
||||
return `
|
||||
// Your custom handler code
|
||||
`;
|
||||
}
|
||||
|
||||
// ... rest of generation logic
|
||||
}
|
||||
```
|
||||
|
||||
## Comparison
|
||||
|
||||
### Before (Manual)
|
||||
|
||||
```typescript
|
||||
// Had to manually write this for EVERY endpoint:
|
||||
http.post(`${API_BASE_URL}/api/v1/auth/login`, async ({ request }) => {
|
||||
// 50 lines of code...
|
||||
}),
|
||||
|
||||
http.get(`${API_BASE_URL}/api/v1/users/me`, async ({ request }) => {
|
||||
// 30 lines of code...
|
||||
}),
|
||||
|
||||
// ... repeat for 31+ endpoints
|
||||
// ... manually update when backend changes
|
||||
// ... easy to forget endpoints
|
||||
// ... prone to bugs
|
||||
```
|
||||
|
||||
### After (Automated)
|
||||
|
||||
```bash
|
||||
npm run generate:api # Done! All 31+ endpoints handled automatically
|
||||
```
|
||||
|
||||
**Manual Code: 1500+ lines**
|
||||
**Automated: 1 command**
|
||||
**Time Saved: Hours per API change**
|
||||
**Bugs: Near zero (generated from spec)**
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [DEMO_MODE.md](./DEMO_MODE.md) - Complete demo mode guide
|
||||
- [API_INTEGRATION.md](./API_INTEGRATION.md) - API client docs
|
||||
- [ARCHITECTURE_FIX_REPORT.md](./ARCHITECTURE_FIX_REPORT.md) - DI patterns
|
||||
|
||||
## Summary
|
||||
|
||||
**This template is batteries-included.**
|
||||
Your API client and MSW handlers stay perfectly synchronized with zero manual work.
|
||||
Just run `npm run generate:api` and everything updates automatically.
|
||||
|
||||
That's the power of OpenAPI + automation! 🚀
|
||||
Reference in New Issue
Block a user