backend-service/docs/API_FLOW.md

652 lines
16 KiB
Markdown
Raw Permalink Normal View History

# API Usage Flow Guide
## Overview
This document provides detailed workflows for different user types and common scenarios in the Attune Heart Therapy API system.
## User Types and Flows
### 1. Client User Flow (Regular Users)
#### A. New User Registration & First Booking
```mermaid
sequenceDiagram
participant C as Client
participant API as API Server
participant DB as Database
participant Stripe as Stripe
participant Jitsi as Jitsi
C->>API: POST /api/auth/register
API->>DB: Create user record
API->>C: User created successfully
C->>API: POST /api/auth/login
API->>DB: Validate credentials
API->>C: JWT token + user info
C->>API: GET /api/schedules?date=2024-12-15
API->>DB: Query available slots
API->>C: Available time slots
C->>API: POST /api/bookings (with JWT)
API->>DB: Create booking record
API->>Jitsi: Generate room details
API->>C: Booking created + room info
C->>API: POST /api/payments/intent (with JWT)
API->>Stripe: Create payment intent
API->>C: Payment intent + client secret
C->>Stripe: Process payment (frontend)
Stripe->>API: POST /api/payments/webhook
API->>DB: Update booking payment status
C->>API: POST /api/payments/confirm (with JWT)
API->>Stripe: Confirm payment
API->>DB: Update booking status
API->>C: Payment confirmed
```
**Step-by-step breakdown:**
1. **Registration** (`POST /api/auth/register`)
```json
{
"first_name": "Sarah",
"last_name": "Johnson",
"email": "sarah.johnson@email.com",
"phone": "+1234567890",
"location": "New York, NY",
"password": "securePassword123"
}
```
2. **Login** (`POST /api/auth/login`)
```json
{
"email": "sarah.johnson@email.com",
"password": "securePassword123"
}
```
*Store the returned JWT token for subsequent requests*
3. **Browse Available Slots** (`GET /api/schedules?date=2024-12-15`)
*No authentication required - public endpoint*
4. **Create Booking** (`POST /api/bookings`)
```json
{
"schedule_id": 5,
"duration": 60,
"notes": "First therapy session - anxiety management"
}
```
*Requires JWT token in Authorization header*
5. **Process Payment** (`POST /api/payments/intent`)
```json
{
"amount": 15000,
"currency": "usd"
}
```
*Amount in cents ($150.00)*
6. **Confirm Payment** (`POST /api/payments/confirm`)
```json
{
"payment_intent_id": "pi_1234567890abcdef"
}
```
#### B. Returning User Flow
```mermaid
sequenceDiagram
participant C as Client
participant API as API Server
participant DB as Database
C->>API: POST /api/auth/login
API->>C: JWT token
C->>API: GET /api/bookings (with JWT)
API->>DB: Get user's bookings
API->>C: List of bookings
C->>API: GET /api/auth/profile (with JWT)
API->>C: User profile data
alt Update Profile
C->>API: PUT /api/auth/profile (with JWT)
API->>DB: Update user data
API->>C: Updated profile
end
alt Cancel Booking
C->>API: PUT /api/bookings/123/cancel (with JWT)
API->>DB: Update booking status
API->>C: Booking cancelled
end
alt Reschedule Booking
C->>API: PUT /api/bookings/123/reschedule (with JWT)
API->>DB: Update booking schedule
API->>C: Booking rescheduled
end
```
### 2. Admin User Flow
#### A. Admin Dashboard & Management
```mermaid
sequenceDiagram
participant A as Admin
participant API as API Server
participant DB as Database
A->>API: POST /api/auth/login (admin credentials)
API->>A: JWT token (with admin privileges)
A->>API: GET /api/admin/dashboard (with admin JWT)
API->>DB: Aggregate statistics
API->>A: Dashboard stats
A->>API: POST /api/admin/schedules (with admin JWT)
API->>DB: Create schedule slots
API->>A: Schedule created
A->>API: GET /api/admin/users?limit=50&offset=0
API->>DB: Query users with pagination
API->>A: User list
A->>API: GET /api/admin/bookings?limit=50&offset=0
API->>DB: Query all bookings
API->>A: Booking list
A->>API: GET /api/admin/reports/financial?start_date=2024-01-01&end_date=2024-12-31
API->>DB: Generate financial report
API->>A: Financial data
```
#### B. Schedule Management Flow
```mermaid
sequenceDiagram
participant A as Admin
participant API as API Server
participant DB as Database
Note over A,DB: Creating Weekly Schedule
loop For each day of the week
A->>API: POST /api/admin/schedules
Note right of A: Create morning slot (9:00-10:00)
API->>DB: Insert schedule record
A->>API: POST /api/admin/schedules
Note right of A: Create afternoon slot (14:00-15:00)
API->>DB: Insert schedule record
A->>API: POST /api/admin/schedules
Note right of A: Create evening slot (18:00-19:00)
API->>DB: Insert schedule record
end
A->>API: GET /api/schedules?date=2024-12-15
API->>A: Verify created slots are available
```
## Common Integration Patterns
### 1. Frontend Application Integration
#### React/Vue.js Example Flow
```javascript
// 1. Authentication Service
class AuthService {
async login(email, password) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (data.token) {
localStorage.setItem('authToken', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
}
return data;
}
getAuthHeader() {
const token = localStorage.getItem('authToken');
return token ? { 'Authorization': `Bearer ${token}` } : {};
}
}
// 2. Booking Service
class BookingService {
constructor(authService) {
this.authService = authService;
}
async getAvailableSlots(date) {
const response = await fetch(`/api/schedules?date=${date}`);
return response.json();
}
async createBooking(scheduleId, duration, notes) {
const response = await fetch('/api/bookings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...this.authService.getAuthHeader()
},
body: JSON.stringify({
schedule_id: scheduleId,
duration,
notes
})
});
return response.json();
}
async getUserBookings() {
const response = await fetch('/api/bookings', {
headers: this.authService.getAuthHeader()
});
return response.json();
}
}
// 3. Payment Service
class PaymentService {
constructor(authService) {
this.authService = authService;
}
async createPaymentIntent(amount, currency = 'usd') {
const response = await fetch('/api/payments/intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...this.authService.getAuthHeader()
},
body: JSON.stringify({ amount, currency })
});
return response.json();
}
async confirmPayment(paymentIntentId) {
const response = await fetch('/api/payments/confirm', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...this.authService.getAuthHeader()
},
body: JSON.stringify({ payment_intent_id: paymentIntentId })
});
return response.json();
}
}
```
### 2. Mobile Application Integration
#### React Native Example
```javascript
// API Client with automatic token management
class APIClient {
constructor() {
this.baseURL = 'http://localhost:8080';
this.token = null;
}
async setToken(token) {
this.token = token;
await AsyncStorage.setItem('authToken', token);
}
async getToken() {
if (!this.token) {
this.token = await AsyncStorage.getItem('authToken');
}
return this.token;
}
async request(endpoint, options = {}) {
const token = await this.getToken();
const headers = {
'Content-Type': 'application/json',
...(token && { 'Authorization': `Bearer ${token}` }),
...options.headers
};
const response = await fetch(`${this.baseURL}${endpoint}`, {
...options,
headers
});
if (response.status === 401) {
// Token expired, redirect to login
await AsyncStorage.removeItem('authToken');
this.token = null;
// Navigate to login screen
}
return response.json();
}
}
```
## Error Handling Patterns
### 1. Client-Side Error Handling
```javascript
class APIErrorHandler {
static handle(error, response) {
switch (response.status) {
case 400:
return { type: 'VALIDATION_ERROR', message: error.details || error.error };
case 401:
return { type: 'AUTH_ERROR', message: 'Please login again' };
case 403:
return { type: 'PERMISSION_ERROR', message: 'Access denied' };
case 409:
return { type: 'CONFLICT_ERROR', message: error.error };
case 422:
return { type: 'PAYMENT_ERROR', message: error.error };
case 500:
return { type: 'SERVER_ERROR', message: 'Server error, please try again' };
default:
return { type: 'UNKNOWN_ERROR', message: 'An unexpected error occurred' };
}
}
}
// Usage in service methods
async createBooking(data) {
try {
const response = await fetch('/api/bookings', {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(data)
});
const result = await response.json();
if (!response.ok) {
const error = APIErrorHandler.handle(result, response);
throw error;
}
return result;
} catch (error) {
if (error.type) {
// Handled API error
throw error;
} else {
// Network or other error
throw { type: 'NETWORK_ERROR', message: 'Network error, please check your connection' };
}
}
}
```
### 2. Retry Logic for Failed Requests
```javascript
class RetryableAPIClient {
async requestWithRetry(endpoint, options = {}, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(`${this.baseURL}${endpoint}`, options);
if (response.ok) {
return response.json();
}
// Don't retry client errors (4xx)
if (response.status >= 400 && response.status < 500) {
throw await response.json();
}
lastError = await response.json();
if (attempt < maxRetries) {
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
} catch (error) {
lastError = error;
if (attempt < maxRetries && this.isRetryableError(error)) {
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
} else {
break;
}
}
}
throw lastError;
}
isRetryableError(error) {
return error.code === 'NETWORK_ERROR' ||
error.code === 'TIMEOUT' ||
(error.status >= 500);
}
}
```
## Testing Workflows
### 1. Postman Collection Testing Flow
```javascript
// Collection-level pre-request script
pm.collectionVariables.set("baseUrl", "http://localhost:8080");
// Test script for login request
if (pm.response.code === 200) {
const response = pm.response.json();
pm.collectionVariables.set("authToken", response.token);
pm.collectionVariables.set("userId", response.user.id);
pm.test("Login successful", function () {
pm.expect(response.token).to.not.be.empty;
pm.expect(response.user.email).to.not.be.empty;
});
}
// Test script for booking creation
if (pm.response.code === 201) {
const response = pm.response.json();
pm.collectionVariables.set("bookingId", response.booking.id);
pm.test("Booking created successfully", function () {
pm.expect(response.booking.id).to.be.a('number');
pm.expect(response.booking.jitsi_room_url).to.not.be.empty;
});
}
```
### 2. Automated Testing Sequence
```bash
# Run the complete user journey
newman run "Attune Heart Therapy API.postman_collection.json" \
-e "Local Environment.postman_environment.json" \
--folder "Authentication" \
--folder "Schedules & Bookings" \
--folder "Payments" \
--reporters cli,json \
--reporter-json-export results.json
```
## Performance Considerations
### 1. Caching Strategy
```javascript
// Client-side caching for available slots
class CachedBookingService {
constructor() {
this.cache = new Map();
this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
}
async getAvailableSlots(date) {
const cacheKey = `slots_${date}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
const data = await this.fetchAvailableSlots(date);
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
return data;
}
}
```
### 2. Pagination Handling
```javascript
// Efficient pagination for admin endpoints
class PaginatedDataService {
async getAllUsers(pageSize = 50) {
let allUsers = [];
let offset = 0;
let hasMore = true;
while (hasMore) {
const response = await fetch(
`/api/admin/users?limit=${pageSize}&offset=${offset}`,
{ headers: this.getAuthHeaders() }
);
const data = await response.json();
allUsers = [...allUsers, ...data.users];
hasMore = data.users.length === pageSize;
offset += pageSize;
}
return allUsers;
}
}
```
## Security Best Practices
### 1. Token Management
```javascript
// Secure token storage and refresh
class SecureAuthService {
constructor() {
this.tokenRefreshThreshold = 5 * 60 * 1000; // 5 minutes before expiry
}
async getValidToken() {
const token = localStorage.getItem('authToken');
const tokenExpiry = localStorage.getItem('tokenExpiry');
if (!token || !tokenExpiry) {
throw new Error('No valid token found');
}
const expiryTime = new Date(tokenExpiry).getTime();
const now = Date.now();
if (now >= expiryTime - this.tokenRefreshThreshold) {
// Token is about to expire, refresh it
await this.refreshToken();
return localStorage.getItem('authToken');
}
return token;
}
async refreshToken() {
// Implement token refresh logic
// This would require a refresh token endpoint
}
}
```
### 2. Input Validation
```javascript
// Client-side validation before API calls
class ValidationService {
static validateBookingData(data) {
const errors = [];
if (!data.schedule_id || !Number.isInteger(data.schedule_id)) {
errors.push('Valid schedule ID is required');
}
if (!data.duration || data.duration < 15 || data.duration > 480) {
errors.push('Duration must be between 15 and 480 minutes');
}
if (data.notes && data.notes.length > 1000) {
errors.push('Notes cannot exceed 1000 characters');
}
return {
isValid: errors.length === 0,
errors
};
}
static validateUserRegistration(data) {
const errors = [];
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!data.first_name || data.first_name.length < 2) {
errors.push('First name must be at least 2 characters');
}
if (!data.last_name || data.last_name.length < 2) {
errors.push('Last name must be at least 2 characters');
}
if (!data.email || !emailRegex.test(data.email)) {
errors.push('Valid email address is required');
}
if (!data.password || data.password.length < 8) {
errors.push('Password must be at least 8 characters');
}
return {
isValid: errors.length === 0,
errors
};
}
}
```
This comprehensive flow guide should help developers understand how to integrate with your API effectively and handle various scenarios that may arise during implementation.