Complete database service consolidation and documentation
✨ Features: - Add comprehensive database service documentation - Create detailed module README with usage examples - Expand main documentation index with database links - Add component test support to Jest configuration 🔧 Improvements: - Fix AvatarDropdown test failures (dark mode classes and rapid clicking) - Update documentation version to 2.1 - Include migration guide and troubleshooting sections - Add performance considerations and security notes 📚 Documentation: - Complete API reference with code examples - Architecture overview with Strategy pattern explanation - Environment configuration and strategy selection guide - Best practices and development guidelines - Comprehensive refactoring summary 🧪 Testing: - All 292 tests passing across all modules - Component tests now properly integrated with Jest - Fixed TypeScript compatibility issues in tests - Verified database service functionality in all environments 📋 Summary: - Removed deprecated CouchDB service files - Consolidated database operations under unified service - Enhanced documentation structure and content - Improved test coverage and reliability - Maintained backward compatibility where possible
This commit is contained in:
527
docs/development/DATABASE.md
Normal file
527
docs/development/DATABASE.md
Normal file
@@ -0,0 +1,527 @@
|
||||
# Database Service Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The RxMinder application uses a unified database service that implements the Strategy pattern to provide a flexible data access layer. This service can seamlessly switch between different database implementations based on the environment configuration.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Strategy Pattern Implementation
|
||||
|
||||
The database service uses the Strategy pattern to abstract database operations:
|
||||
|
||||
```
|
||||
DatabaseService (Context)
|
||||
├── DatabaseStrategy (Interface)
|
||||
├── MockDatabaseStrategy (Concrete Strategy)
|
||||
└── ProductionDatabaseStrategy (Concrete Strategy)
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
- **DatabaseService**: Main service class that delegates operations to the selected strategy
|
||||
- **DatabaseStrategy**: Interface defining all database operations
|
||||
- **MockDatabaseStrategy**: In-memory implementation for development and testing
|
||||
- **ProductionDatabaseStrategy**: CouchDB implementation for production environments
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment-Based Strategy Selection
|
||||
|
||||
The service automatically selects the appropriate strategy based on environment variables:
|
||||
|
||||
1. **Test Environment**: Always uses `MockDatabaseStrategy`
|
||||
2. **Production Environment**: Uses `ProductionDatabaseStrategy` if CouchDB URL is configured
|
||||
3. **Fallback**: Uses `MockDatabaseStrategy` if production strategy fails to initialize
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# CouchDB Configuration (for production)
|
||||
VITE_COUCHDB_URL=http://localhost:5984
|
||||
COUCHDB_URL=http://localhost:5984
|
||||
|
||||
# Test Environment Detection
|
||||
NODE_ENV=test
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
import { databaseService } from './services/database';
|
||||
|
||||
// The service automatically selects the appropriate strategy
|
||||
const users = await databaseService.getAllUsers();
|
||||
```
|
||||
|
||||
### Strategy Information
|
||||
|
||||
```typescript
|
||||
// Check which strategy is being used
|
||||
console.log(databaseService.getStrategyType()); // "MockDatabaseStrategy" or "ProductionDatabaseStrategy"
|
||||
|
||||
// Check if using mock strategy
|
||||
if (databaseService.isUsingMockStrategy()) {
|
||||
console.log('Using in-memory database');
|
||||
}
|
||||
|
||||
// Check if using production strategy
|
||||
if (databaseService.isUsingProductionStrategy()) {
|
||||
console.log('Using CouchDB database');
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### User Operations
|
||||
|
||||
#### createUser(user)
|
||||
|
||||
Creates a new user in the database.
|
||||
|
||||
```typescript
|
||||
const user = await databaseService.createUser({
|
||||
_id: 'user1',
|
||||
_rev: '1-abc123',
|
||||
username: 'john_doe',
|
||||
email: 'john@example.com',
|
||||
role: UserRole.USER,
|
||||
status: AccountStatus.ACTIVE,
|
||||
});
|
||||
```
|
||||
|
||||
#### updateUser(user)
|
||||
|
||||
Updates an existing user.
|
||||
|
||||
```typescript
|
||||
const updatedUser = await databaseService.updateUser({
|
||||
...existingUser,
|
||||
username: 'new_username',
|
||||
});
|
||||
```
|
||||
|
||||
#### getUserById(id)
|
||||
|
||||
Retrieves a user by their ID.
|
||||
|
||||
```typescript
|
||||
const user = await databaseService.getUserById('user1');
|
||||
```
|
||||
|
||||
#### findUserByEmail(email)
|
||||
|
||||
Finds a user by their email address.
|
||||
|
||||
```typescript
|
||||
const user = await databaseService.findUserByEmail('john@example.com');
|
||||
```
|
||||
|
||||
#### deleteUser(id)
|
||||
|
||||
Deletes a user from the database.
|
||||
|
||||
```typescript
|
||||
const success = await databaseService.deleteUser('user1');
|
||||
```
|
||||
|
||||
#### getAllUsers()
|
||||
|
||||
Retrieves all users from the database.
|
||||
|
||||
```typescript
|
||||
const users = await databaseService.getAllUsers();
|
||||
```
|
||||
|
||||
### Authentication Operations
|
||||
|
||||
#### createUserWithPassword(email, hashedPassword, username?)
|
||||
|
||||
Creates a user with password-based authentication.
|
||||
|
||||
```typescript
|
||||
const user = await databaseService.createUserWithPassword('user@example.com', 'hashed_password', 'username');
|
||||
```
|
||||
|
||||
#### createUserFromOAuth(email, username, provider)
|
||||
|
||||
Creates a user from OAuth authentication.
|
||||
|
||||
```typescript
|
||||
const user = await databaseService.createUserFromOAuth('user@example.com', 'username', 'google');
|
||||
```
|
||||
|
||||
### Medication Operations
|
||||
|
||||
#### createMedication(userId, medication)
|
||||
|
||||
Creates a new medication for a user.
|
||||
|
||||
```typescript
|
||||
const medication = await databaseService.createMedication('user1', {
|
||||
name: 'Aspirin',
|
||||
dosage: '100mg',
|
||||
frequency: Frequency.Daily,
|
||||
startTime: '08:00',
|
||||
notes: 'Take with food',
|
||||
});
|
||||
```
|
||||
|
||||
#### updateMedication(medication)
|
||||
|
||||
Updates an existing medication.
|
||||
|
||||
```typescript
|
||||
const updatedMedication = await databaseService.updateMedication({
|
||||
...existingMedication,
|
||||
dosage: '200mg',
|
||||
});
|
||||
```
|
||||
|
||||
#### getMedications(userId)
|
||||
|
||||
Retrieves all medications for a user.
|
||||
|
||||
```typescript
|
||||
const medications = await databaseService.getMedications('user1');
|
||||
```
|
||||
|
||||
#### deleteMedication(id)
|
||||
|
||||
Deletes a medication.
|
||||
|
||||
```typescript
|
||||
const success = await databaseService.deleteMedication('medication1');
|
||||
```
|
||||
|
||||
### User Settings Operations
|
||||
|
||||
#### getUserSettings(userId)
|
||||
|
||||
Retrieves user settings.
|
||||
|
||||
```typescript
|
||||
const settings = await databaseService.getUserSettings('user1');
|
||||
```
|
||||
|
||||
#### updateUserSettings(settings)
|
||||
|
||||
Updates user settings.
|
||||
|
||||
```typescript
|
||||
const updatedSettings = await databaseService.updateUserSettings({
|
||||
...existingSettings,
|
||||
notificationsEnabled: false,
|
||||
});
|
||||
```
|
||||
|
||||
### Taken Doses Operations
|
||||
|
||||
#### getTakenDoses(userId)
|
||||
|
||||
Retrieves taken doses record for a user.
|
||||
|
||||
```typescript
|
||||
const takenDoses = await databaseService.getTakenDoses('user1');
|
||||
```
|
||||
|
||||
#### updateTakenDoses(takenDoses)
|
||||
|
||||
Updates the taken doses record.
|
||||
|
||||
```typescript
|
||||
const updatedTakenDoses = await databaseService.updateTakenDoses({
|
||||
...existingTakenDoses,
|
||||
doses: { ...existingTakenDoses.doses, dose1: new Date().toISOString() },
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Reminders Operations
|
||||
|
||||
#### createCustomReminder(userId, reminder)
|
||||
|
||||
Creates a custom reminder for a user.
|
||||
|
||||
```typescript
|
||||
const reminder = await databaseService.createCustomReminder('user1', {
|
||||
title: 'Doctor Appointment',
|
||||
icon: '🏥',
|
||||
frequencyMinutes: 60,
|
||||
startTime: '09:00',
|
||||
endTime: '17:00',
|
||||
});
|
||||
```
|
||||
|
||||
#### updateCustomReminder(reminder)
|
||||
|
||||
Updates a custom reminder.
|
||||
|
||||
```typescript
|
||||
const updatedReminder = await databaseService.updateCustomReminder({
|
||||
...existingReminder,
|
||||
title: 'Updated Appointment',
|
||||
});
|
||||
```
|
||||
|
||||
#### getCustomReminders(userId)
|
||||
|
||||
Retrieves all custom reminders for a user.
|
||||
|
||||
```typescript
|
||||
const reminders = await databaseService.getCustomReminders('user1');
|
||||
```
|
||||
|
||||
#### deleteCustomReminder(id)
|
||||
|
||||
Deletes a custom reminder.
|
||||
|
||||
```typescript
|
||||
const success = await databaseService.deleteCustomReminder('reminder1');
|
||||
```
|
||||
|
||||
### Administrative Operations
|
||||
|
||||
#### suspendUser(userId)
|
||||
|
||||
Suspends a user account.
|
||||
|
||||
```typescript
|
||||
const suspendedUser = await databaseService.suspendUser('user1');
|
||||
```
|
||||
|
||||
#### activateUser(userId)
|
||||
|
||||
Activates a suspended user account.
|
||||
|
||||
```typescript
|
||||
const activatedUser = await databaseService.activateUser('user1');
|
||||
```
|
||||
|
||||
#### changeUserPassword(userId, newPassword)
|
||||
|
||||
Changes a user's password.
|
||||
|
||||
```typescript
|
||||
const updatedUser = await databaseService.changeUserPassword('user1', 'new_hashed_password');
|
||||
```
|
||||
|
||||
#### deleteAllUserData(userId)
|
||||
|
||||
Deletes all data associated with a user.
|
||||
|
||||
```typescript
|
||||
const success = await databaseService.deleteAllUserData('user1');
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Mock Strategy for Testing
|
||||
|
||||
The `MockDatabaseStrategy` provides an in-memory implementation that's perfect for testing:
|
||||
|
||||
```typescript
|
||||
import { DatabaseService } from './services/database';
|
||||
|
||||
// In test environment, automatically uses MockDatabaseStrategy
|
||||
const service = new DatabaseService();
|
||||
|
||||
// All operations work the same way but use in-memory storage
|
||||
const user = await service.createUser(mockUser);
|
||||
```
|
||||
|
||||
### Test Utilities
|
||||
|
||||
The test setup provides utilities for creating mock data:
|
||||
|
||||
```typescript
|
||||
import { testUtils } from './tests/setup';
|
||||
|
||||
const mockUser = testUtils.createMockUser();
|
||||
const mockMedication = testUtils.createMockMedication();
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### DatabaseError
|
||||
|
||||
All database operations can throw `DatabaseError` for various failure scenarios:
|
||||
|
||||
```typescript
|
||||
import { DatabaseError } from './services/database';
|
||||
|
||||
try {
|
||||
const user = await databaseService.getUserById('nonexistent');
|
||||
} catch (error) {
|
||||
if (error instanceof DatabaseError) {
|
||||
console.error('Database operation failed:', error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fallback Behavior
|
||||
|
||||
The service automatically falls back to the mock strategy if the production strategy fails to initialize:
|
||||
|
||||
```typescript
|
||||
// If CouchDB is not available, this will log a warning and use MockDatabaseStrategy
|
||||
const service = new DatabaseService();
|
||||
```
|
||||
|
||||
## Migration from Legacy Implementation
|
||||
|
||||
### Removed Files
|
||||
|
||||
The following deprecated files have been removed:
|
||||
|
||||
- `services/couchdb.ts` - Legacy CouchDB service
|
||||
- `services/couchdb.factory.ts` - CouchDB factory
|
||||
- `services/couchdb.production.ts` - Production CouchDB configuration
|
||||
- `scripts/migrate-to-unified-config.ts` - Migration script
|
||||
|
||||
### Migration Guide
|
||||
|
||||
1. **Update imports**: Change from legacy CouchDB imports to unified database service:
|
||||
|
||||
```typescript
|
||||
// Old
|
||||
import { couchdbService } from './services/couchdb';
|
||||
|
||||
// New
|
||||
import { databaseService } from './services/database';
|
||||
```
|
||||
|
||||
2. **Update method calls**: The API remains largely the same, but some methods have been standardized:
|
||||
|
||||
```typescript
|
||||
// Old
|
||||
await couchdbService.updateMedication(userId, medication);
|
||||
|
||||
// New
|
||||
await databaseService.updateMedication(medication);
|
||||
```
|
||||
|
||||
3. **Environment configuration**: Update environment variables to use the new configuration format.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Error Handling
|
||||
|
||||
Always handle potential database errors:
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const user = await databaseService.getUserById(userId);
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
// Process user
|
||||
} catch (error) {
|
||||
console.error('Failed to retrieve user:', error);
|
||||
// Handle error appropriately
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Transaction-like Operations
|
||||
|
||||
For operations that affect multiple entities, use proper error handling:
|
||||
|
||||
```typescript
|
||||
try {
|
||||
const medications = await databaseService.getMedications(userId);
|
||||
const reminders = await databaseService.getCustomReminders(userId);
|
||||
|
||||
// Process both together
|
||||
return { medications, reminders };
|
||||
} catch (error) {
|
||||
console.error('Failed to load user data:', error);
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Strategy-Agnostic Code
|
||||
|
||||
Write code that works with any strategy:
|
||||
|
||||
```typescript
|
||||
// Good - works with any strategy
|
||||
const user = await databaseService.getUserById(userId);
|
||||
|
||||
// Avoid - strategy-specific checks unless necessary
|
||||
if (databaseService.isUsingMockStrategy()) {
|
||||
// Only do this if absolutely necessary
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Mock Strategy
|
||||
|
||||
- In-memory storage for fast operations
|
||||
- Perfect for development and testing
|
||||
- Data is lost when the application restarts
|
||||
|
||||
### Production Strategy
|
||||
|
||||
- Persistent storage with CouchDB
|
||||
- Network latency considerations
|
||||
- Proper error handling for network failures
|
||||
|
||||
## Security
|
||||
|
||||
### Data Validation
|
||||
|
||||
All data is validated before database operations:
|
||||
|
||||
- User input sanitization
|
||||
- Type checking with TypeScript interfaces
|
||||
- Required field validation
|
||||
|
||||
### Access Control
|
||||
|
||||
- User authentication required for all operations
|
||||
- Role-based access control for administrative operations
|
||||
- User isolation (users can only access their own data)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **CouchDB Connection Failed**
|
||||
- Check `VITE_COUCHDB_URL` or `COUCHDB_URL` environment variables
|
||||
- Verify CouchDB is running and accessible
|
||||
- Check network connectivity
|
||||
|
||||
2. **Strategy Selection Issues**
|
||||
- Verify environment variable configuration
|
||||
- Check console logs for strategy selection messages
|
||||
- Use `getStrategyType()` to confirm active strategy
|
||||
|
||||
3. **Test Environment Issues**
|
||||
- Ensure `NODE_ENV=test` is set for test runs
|
||||
- Verify test setup files are properly configured
|
||||
- Check that mock data is being used
|
||||
|
||||
### Debug Information
|
||||
|
||||
Enable debug logging to troubleshoot issues:
|
||||
|
||||
```typescript
|
||||
console.log('Active strategy:', databaseService.getStrategyType());
|
||||
console.log('Using mock strategy:', databaseService.isUsingMockStrategy());
|
||||
console.log('Using production strategy:', databaseService.isUsingProductionStrategy());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [API Documentation](API.md) - REST API endpoints
|
||||
- [Application Security](APPLICATION_SECURITY.md) - Security practices
|
||||
- [Code Quality](CODE_QUALITY.md) - Development standards
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** January 2024
|
||||
**Version:** 2.0.0
|
||||
Reference in New Issue
Block a user