✨ 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
12 KiB
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:
- Test Environment: Always uses
MockDatabaseStrategy - Production Environment: Uses
ProductionDatabaseStrategyif CouchDB URL is configured - Fallback: Uses
MockDatabaseStrategyif production strategy fails to initialize
Environment Variables
# CouchDB Configuration (for production)
VITE_COUCHDB_URL=http://localhost:5984
COUCHDB_URL=http://localhost:5984
# Test Environment Detection
NODE_ENV=test
Usage
Basic Usage
import { databaseService } from './services/database';
// The service automatically selects the appropriate strategy
const users = await databaseService.getAllUsers();
Strategy Information
// 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.
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.
const updatedUser = await databaseService.updateUser({
...existingUser,
username: 'new_username',
});
getUserById(id)
Retrieves a user by their ID.
const user = await databaseService.getUserById('user1');
findUserByEmail(email)
Finds a user by their email address.
const user = await databaseService.findUserByEmail('john@example.com');
deleteUser(id)
Deletes a user from the database.
const success = await databaseService.deleteUser('user1');
getAllUsers()
Retrieves all users from the database.
const users = await databaseService.getAllUsers();
Authentication Operations
createUserWithPassword(email, hashedPassword, username?)
Creates a user with password-based authentication.
const user = await databaseService.createUserWithPassword('user@example.com', 'hashed_password', 'username');
createUserFromOAuth(email, username, provider)
Creates a user from OAuth authentication.
const user = await databaseService.createUserFromOAuth('user@example.com', 'username', 'google');
Medication Operations
createMedication(userId, medication)
Creates a new medication for a user.
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.
const updatedMedication = await databaseService.updateMedication({
...existingMedication,
dosage: '200mg',
});
getMedications(userId)
Retrieves all medications for a user.
const medications = await databaseService.getMedications('user1');
deleteMedication(id)
Deletes a medication.
const success = await databaseService.deleteMedication('medication1');
User Settings Operations
getUserSettings(userId)
Retrieves user settings.
const settings = await databaseService.getUserSettings('user1');
updateUserSettings(settings)
Updates user settings.
const updatedSettings = await databaseService.updateUserSettings({
...existingSettings,
notificationsEnabled: false,
});
Taken Doses Operations
getTakenDoses(userId)
Retrieves taken doses record for a user.
const takenDoses = await databaseService.getTakenDoses('user1');
updateTakenDoses(takenDoses)
Updates the taken doses record.
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.
const reminder = await databaseService.createCustomReminder('user1', {
title: 'Doctor Appointment',
icon: '🏥',
frequencyMinutes: 60,
startTime: '09:00',
endTime: '17:00',
});
updateCustomReminder(reminder)
Updates a custom reminder.
const updatedReminder = await databaseService.updateCustomReminder({
...existingReminder,
title: 'Updated Appointment',
});
getCustomReminders(userId)
Retrieves all custom reminders for a user.
const reminders = await databaseService.getCustomReminders('user1');
deleteCustomReminder(id)
Deletes a custom reminder.
const success = await databaseService.deleteCustomReminder('reminder1');
Administrative Operations
suspendUser(userId)
Suspends a user account.
const suspendedUser = await databaseService.suspendUser('user1');
activateUser(userId)
Activates a suspended user account.
const activatedUser = await databaseService.activateUser('user1');
changeUserPassword(userId, newPassword)
Changes a user's password.
const updatedUser = await databaseService.changeUserPassword('user1', 'new_hashed_password');
deleteAllUserData(userId)
Deletes all data associated with a user.
const success = await databaseService.deleteAllUserData('user1');
Testing
Mock Strategy for Testing
The MockDatabaseStrategy provides an in-memory implementation that's perfect for testing:
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:
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:
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:
// 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 serviceservices/couchdb.factory.ts- CouchDB factoryservices/couchdb.production.ts- Production CouchDB configurationscripts/migrate-to-unified-config.ts- Migration script
Migration Guide
- Update imports: Change from legacy CouchDB imports to unified database service:
// Old
import { couchdbService } from './services/couchdb';
// New
import { databaseService } from './services/database';
- Update method calls: The API remains largely the same, but some methods have been standardized:
// Old
await couchdbService.updateMedication(userId, medication);
// New
await databaseService.updateMedication(medication);
- Environment configuration: Update environment variables to use the new configuration format.
Best Practices
1. Error Handling
Always handle potential database errors:
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:
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:
// 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
-
CouchDB Connection Failed
- Check
VITE_COUCHDB_URLorCOUCHDB_URLenvironment variables - Verify CouchDB is running and accessible
- Check network connectivity
- Check
-
Strategy Selection Issues
- Verify environment variable configuration
- Check console logs for strategy selection messages
- Use
getStrategyType()to confirm active strategy
-
Test Environment Issues
- Ensure
NODE_ENV=testis set for test runs - Verify test setup files are properly configured
- Check that mock data is being used
- Ensure
Debug Information
Enable debug logging to troubleshoot issues:
console.log('Active strategy:', databaseService.getStrategyType());
console.log('Using mock strategy:', databaseService.isUsingMockStrategy());
console.log('Using production strategy:', databaseService.isUsingProductionStrategy());
Related Documentation
- API Documentation - REST API endpoints
- Application Security - Security practices
- Code Quality - Development standards
Last Updated: January 2024
Version: 2.0.0