re-uploading work
This commit is contained in:
365
web/API-CLIENT-QUICK-REFERENCE.md
Normal file
365
web/API-CLIENT-QUICK-REFERENCE.md
Normal file
@@ -0,0 +1,365 @@
|
||||
# API Client Quick Reference
|
||||
|
||||
> **TL;DR:** Use the auto-generated TypeScript client instead of manual API calls for type safety and schema validation.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. Generate/Update Client
|
||||
|
||||
```bash
|
||||
# Ensure API server is running first!
|
||||
cd web
|
||||
npm run generate:api
|
||||
```
|
||||
|
||||
### 2. Import and Use
|
||||
|
||||
```typescript
|
||||
import { PacksService, AuthService, ActionsService } from '@/api';
|
||||
import type { CreatePackRequest, PackResponse } from '@/api';
|
||||
|
||||
// All API calls are now type-safe! ✅
|
||||
const packs = await PacksService.listPacks({ page: 1, pageSize: 50 });
|
||||
```
|
||||
|
||||
## 📚 Common Operations
|
||||
|
||||
### Authentication
|
||||
|
||||
```typescript
|
||||
import { AuthService } from '@/api';
|
||||
|
||||
// Login
|
||||
const response = await AuthService.login({
|
||||
requestBody: { login: 'admin', password: 'secret123' }
|
||||
});
|
||||
const { access_token, refresh_token, user } = response.data;
|
||||
|
||||
// Get current user
|
||||
const currentUser = await AuthService.getCurrentUser();
|
||||
|
||||
// Refresh token
|
||||
const refreshed = await AuthService.refreshToken({
|
||||
requestBody: { refresh_token }
|
||||
});
|
||||
|
||||
// Register new user
|
||||
await AuthService.register({
|
||||
requestBody: {
|
||||
login: 'newuser',
|
||||
password: 'strongpass',
|
||||
display_name: 'New User'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Packs
|
||||
|
||||
```typescript
|
||||
import { PacksService } from '@/api';
|
||||
import type { CreatePackRequest, UpdatePackRequest } from '@/api';
|
||||
|
||||
// List packs
|
||||
const packs = await PacksService.listPacks({ page: 1, pageSize: 50 });
|
||||
|
||||
// Get single pack
|
||||
const pack = await PacksService.getPack({ ref: 'core' });
|
||||
|
||||
// Create pack
|
||||
const newPack = await PacksService.createPack({
|
||||
requestBody: {
|
||||
ref: 'my-pack',
|
||||
label: 'My Custom Pack',
|
||||
description: 'A pack for custom automations',
|
||||
version: '1.0.0'
|
||||
}
|
||||
});
|
||||
|
||||
// Update pack
|
||||
const updated = await PacksService.updatePack({
|
||||
ref: 'my-pack',
|
||||
requestBody: {
|
||||
label: 'Updated Pack Name',
|
||||
description: 'New description'
|
||||
}
|
||||
});
|
||||
|
||||
// Delete pack
|
||||
await PacksService.deletePack({ ref: 'my-pack' });
|
||||
```
|
||||
|
||||
### Actions
|
||||
|
||||
```typescript
|
||||
import { ActionsService } from '@/api';
|
||||
|
||||
// List actions
|
||||
const actions = await ActionsService.listActions({ page: 1, pageSize: 50 });
|
||||
|
||||
// Get action
|
||||
const action = await ActionsService.getAction({ ref: 'slack.post_message' });
|
||||
|
||||
// Create action
|
||||
const newAction = await ActionsService.createAction({
|
||||
requestBody: {
|
||||
ref: 'slack.post_message',
|
||||
pack: 1,
|
||||
label: 'Post Message to Slack',
|
||||
description: 'Posts a message to a Slack channel',
|
||||
entrypoint: '/actions/slack/post_message.py',
|
||||
param_schema: { /* JSON Schema */ }
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
||||
```typescript
|
||||
import { RulesService } from '@/api';
|
||||
|
||||
// List rules
|
||||
const rules = await RulesService.listRules({ page: 1, pageSize: 50 });
|
||||
|
||||
// Create rule
|
||||
const rule = await RulesService.createRule({
|
||||
requestBody: {
|
||||
ref: 'webhook-to-slack',
|
||||
label: 'Webhook to Slack',
|
||||
pack: 1,
|
||||
trigger: 'webhook.received',
|
||||
action: 'slack.post_message',
|
||||
criteria: { /* Rule conditions */ },
|
||||
parameters: { /* Action parameters */ }
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Executions
|
||||
|
||||
```typescript
|
||||
import { ExecutionsService } from '@/api';
|
||||
|
||||
// List executions
|
||||
const executions = await ExecutionsService.listExecutions({
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
status: 'Running' // Optional filter
|
||||
});
|
||||
|
||||
// Get execution details
|
||||
const execution = await ExecutionsService.getExecution({ id: 123 });
|
||||
|
||||
// Cancel execution
|
||||
await ExecutionsService.cancelExecution({ id: 123 });
|
||||
|
||||
// Re-run execution
|
||||
const rerun = await ExecutionsService.rerunExecution({ id: 123 });
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
```typescript
|
||||
import { EventsService } from '@/api';
|
||||
|
||||
// List events
|
||||
const events = await EventsService.listEvents({
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
triggerRef: 'webhook.received' // Optional filter
|
||||
});
|
||||
|
||||
// Get event details
|
||||
const event = await EventsService.getEvent({ id: 456 });
|
||||
```
|
||||
|
||||
## 🔄 React Query Integration
|
||||
|
||||
### Query Hooks
|
||||
|
||||
```typescript
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { PacksService } from '@/api';
|
||||
|
||||
function PacksList() {
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['packs'],
|
||||
queryFn: () => PacksService.listPacks({ page: 1, pageSize: 50 })
|
||||
});
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
if (error) return <div>Error: {error.message}</div>;
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{data?.data.items.map(pack => (
|
||||
<li key={pack.id}>{pack.label}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Mutation Hooks
|
||||
|
||||
```typescript
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { PacksService } from '@/api';
|
||||
import type { CreatePackRequest } from '@/api';
|
||||
|
||||
function CreatePackForm() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: (data: CreatePackRequest) =>
|
||||
PacksService.createPack({ requestBody: data }),
|
||||
onSuccess: () => {
|
||||
// Invalidate and refetch
|
||||
queryClient.invalidateQueries({ queryKey: ['packs'] });
|
||||
}
|
||||
});
|
||||
|
||||
const handleSubmit = (formData: CreatePackRequest) => {
|
||||
mutation.mutate(formData);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={e => {
|
||||
e.preventDefault();
|
||||
handleSubmit({
|
||||
ref: 'my-pack',
|
||||
label: 'My Pack',
|
||||
description: 'Description'
|
||||
});
|
||||
}}>
|
||||
{/* form fields */}
|
||||
<button type="submit" disabled={mutation.isPending}>
|
||||
{mutation.isPending ? 'Creating...' : 'Create Pack'}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 Error Handling
|
||||
|
||||
```typescript
|
||||
import { ApiError } from '@/api';
|
||||
|
||||
try {
|
||||
await PacksService.getPack({ ref: 'nonexistent' });
|
||||
} catch (error) {
|
||||
if (error instanceof ApiError) {
|
||||
console.error(`Status: ${error.status}`);
|
||||
console.error(`Message: ${error.message}`);
|
||||
console.error(`Body:`, error.body);
|
||||
|
||||
switch (error.status) {
|
||||
case 401:
|
||||
// Unauthorized - redirect to login
|
||||
break;
|
||||
case 404:
|
||||
// Not found
|
||||
break;
|
||||
case 422:
|
||||
// Validation error
|
||||
console.error('Validation errors:', error.body);
|
||||
break;
|
||||
default:
|
||||
// Other errors
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📦 Available Services
|
||||
|
||||
| Service | Description | Common Methods |
|
||||
|---------|-------------|----------------|
|
||||
| `AuthService` | Authentication | `login`, `register`, `getCurrentUser`, `refreshToken` |
|
||||
| `PacksService` | Pack management | `listPacks`, `getPack`, `createPack`, `updatePack` |
|
||||
| `ActionsService` | Action CRUD | `listActions`, `getAction`, `createAction`, `updateAction` |
|
||||
| `RulesService` | Rule management | `listRules`, `getRule`, `createRule`, `updateRule` |
|
||||
| `TriggersService` | Trigger config | `listTriggers`, `getTrigger`, `createTrigger` |
|
||||
| `SensorsService` | Sensor monitoring | `listSensors`, `getSensor`, `createSensor` |
|
||||
| `ExecutionsService` | Execution tracking | `listExecutions`, `getExecution`, `cancelExecution` |
|
||||
| `EventsService` | Event history | `listEvents`, `getEvent` |
|
||||
| `InquiriesService` | Human-in-the-loop | `listInquiries`, `getInquiry`, `respondToInquiry` |
|
||||
| `WorkflowsService` | Workflow orchestration | `listWorkflows`, `getWorkflow`, `createWorkflow` |
|
||||
| `HealthService` | Health checks | `health`, `healthDetailed`, `healthReady` |
|
||||
| `SecretsService` | Secret management | `listKeys`, `getKey`, `createKey` |
|
||||
| `EnforcementsService` | Rule enforcements | `listEnforcements`, `getEnforcement` |
|
||||
|
||||
## 🚨 Common Mistakes
|
||||
|
||||
### ❌ Don't Do This
|
||||
|
||||
```typescript
|
||||
// Manual axios calls - NO TYPE SAFETY!
|
||||
import { apiClient } from '@/lib/api-client';
|
||||
|
||||
const response = await apiClient.post('/api/v1/packs', {
|
||||
name: 'my-pack', // ❌ Wrong field name
|
||||
system: false // ❌ Wrong field name
|
||||
});
|
||||
```
|
||||
|
||||
### ✅ Do This Instead
|
||||
|
||||
```typescript
|
||||
// Generated client - FULL TYPE SAFETY!
|
||||
import { PacksService } from '@/api';
|
||||
import type { CreatePackRequest } from '@/api';
|
||||
|
||||
const response = await PacksService.createPack({
|
||||
requestBody: {
|
||||
ref: 'my-pack', // ✅ Correct field (enforced by TypeScript)
|
||||
label: 'My Pack', // ✅ Correct field
|
||||
is_standard: false // ✅ Correct field
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Client out of sync with backend?
|
||||
|
||||
```bash
|
||||
npm run generate:api
|
||||
```
|
||||
|
||||
### Token not being sent?
|
||||
|
||||
Make sure `src/lib/api-config.ts` is imported in `src/main.tsx`:
|
||||
|
||||
```typescript
|
||||
import './lib/api-config'; // ← This line must be present
|
||||
```
|
||||
|
||||
### TypeScript errors after generation?
|
||||
|
||||
The backend schema changed. Update your code to match the new schema:
|
||||
|
||||
1. Read the error messages
|
||||
2. Check what changed in the OpenAPI spec at http://localhost:8080/docs
|
||||
3. Update your code accordingly
|
||||
|
||||
## 📖 Full Documentation
|
||||
|
||||
- **Detailed Guide:** `src/api/README.md`
|
||||
- **Migration Guide:** `MIGRATION-TO-GENERATED-CLIENT.md`
|
||||
- **Architecture:** `../docs/openapi-client-generation.md`
|
||||
- **Interactive Docs:** http://localhost:8080/docs
|
||||
|
||||
## 💡 Pro Tips
|
||||
|
||||
1. **Always regenerate after backend changes** to stay in sync
|
||||
2. **Use TypeScript** - let the compiler catch errors
|
||||
3. **Create custom hooks** - wrap services in React Query hooks
|
||||
4. **Don't edit generated files** - they'll be overwritten
|
||||
5. **Use generated types** - import from `@/api`, not manual definitions
|
||||
6. **Leverage auto-completion** - your IDE knows the full API schema
|
||||
|
||||
---
|
||||
|
||||
**Remember:** The generated client is your single source of truth for API interactions. Use it exclusively for type safety and automatic schema validation! 🎉
|
||||
Reference in New Issue
Block a user