refactor: implement database service with strategy pattern
- Add DatabaseService with MockDatabaseStrategy and ProductionDatabaseStrategy - Use strategy pattern to switch between test and production database implementations - Improve type safety and testability of database operations - Update database seeder to use new database service architecture This is the foundation for modernizing the database layer.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { dbService } from './couchdb.factory';
|
||||
import { databaseService } from './database';
|
||||
import { AccountStatus } from './auth/auth.constants';
|
||||
import { UserRole } from '../types';
|
||||
|
||||
@@ -15,7 +15,7 @@ export class DatabaseSeeder {
|
||||
|
||||
try {
|
||||
// Check if admin already exists
|
||||
const existingAdmin = await dbService.findUserByEmail(adminEmail);
|
||||
const existingAdmin = await databaseService.findUserByEmail(adminEmail);
|
||||
|
||||
if (existingAdmin) {
|
||||
console.warn('✅ Default admin user already exists');
|
||||
@@ -33,7 +33,7 @@ export class DatabaseSeeder {
|
||||
status: AccountStatus.ACTIVE,
|
||||
emailVerified: true,
|
||||
};
|
||||
await dbService.updateUser(updatedAdmin);
|
||||
await databaseService.updateUser(updatedAdmin);
|
||||
console.warn('✅ Admin user updated successfully');
|
||||
console.warn('👤 Updated admin:', updatedAdmin);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export class DatabaseSeeder {
|
||||
|
||||
console.warn('🚀 Creating new admin user...');
|
||||
// Create default admin user
|
||||
const adminUser = await dbService.createUserWithPassword(
|
||||
const adminUser = await databaseService.createUserWithPassword(
|
||||
adminEmail,
|
||||
adminPassword,
|
||||
'admin'
|
||||
@@ -60,7 +60,7 @@ export class DatabaseSeeder {
|
||||
lastLoginAt: new Date(),
|
||||
};
|
||||
|
||||
await dbService.updateUser(updatedAdmin);
|
||||
await databaseService.updateUser(updatedAdmin);
|
||||
|
||||
console.warn('✅ Admin user created successfully');
|
||||
console.warn('👤 Final admin user:', updatedAdmin);
|
||||
|
||||
@@ -78,43 +78,17 @@ export class DatabaseService implements DatabaseStrategy {
|
||||
|
||||
// Overloads for updateMedication
|
||||
async updateMedication(
|
||||
userId: string,
|
||||
medication: Parameters<DatabaseStrategy['updateMedication']>[0]
|
||||
): Promise<Parameters<DatabaseStrategy['updateMedication']>[0]>;
|
||||
async updateMedication(
|
||||
medication: Parameters<DatabaseStrategy['updateMedication']>[0]
|
||||
): Promise<Parameters<DatabaseStrategy['updateMedication']>[0]>;
|
||||
async updateMedication(
|
||||
userIdOrMedication:
|
||||
| string
|
||||
| Parameters<DatabaseStrategy['updateMedication']>[0],
|
||||
medication?: Parameters<DatabaseStrategy['updateMedication']>[0]
|
||||
) {
|
||||
// Support both old signature (userId, medication) and new (medication)
|
||||
if (typeof userIdOrMedication === 'string' && medication) {
|
||||
return this.strategy.updateMedication(medication);
|
||||
}
|
||||
return this.strategy.updateMedication(
|
||||
userIdOrMedication as Parameters<DatabaseStrategy['updateMedication']>[0]
|
||||
);
|
||||
): Promise<Parameters<DatabaseStrategy['updateMedication']>[0]> {
|
||||
return this.strategy.updateMedication(medication);
|
||||
}
|
||||
|
||||
async getMedications(userId: string) {
|
||||
return this.strategy.getMedications(userId);
|
||||
}
|
||||
|
||||
// Overloads for deleteMedication
|
||||
async deleteMedication(
|
||||
userId: string,
|
||||
medication: { _id: string }
|
||||
): Promise<boolean>;
|
||||
async deleteMedication(id: string): Promise<boolean>;
|
||||
async deleteMedication(userIdOrId: string, medication?: { _id: string }) {
|
||||
// Support both old signature (userId, medication) and new (id)
|
||||
if (medication) {
|
||||
return this.strategy.deleteMedication(medication._id);
|
||||
}
|
||||
return this.strategy.deleteMedication(userIdOrId);
|
||||
async deleteMedication(id: string): Promise<boolean> {
|
||||
return this.strategy.deleteMedication(id);
|
||||
}
|
||||
|
||||
// User settings operations
|
||||
@@ -136,28 +110,8 @@ export class DatabaseService implements DatabaseStrategy {
|
||||
// Overloads for updateTakenDoses
|
||||
async updateTakenDoses(
|
||||
takenDoses: Parameters<DatabaseStrategy['updateTakenDoses']>[0]
|
||||
): Promise<Parameters<DatabaseStrategy['updateTakenDoses']>[0]>;
|
||||
async updateTakenDoses(
|
||||
userId: string,
|
||||
partialUpdate: Partial<Parameters<DatabaseStrategy['updateTakenDoses']>[0]>
|
||||
): Promise<Parameters<DatabaseStrategy['updateTakenDoses']>[0]>;
|
||||
async updateTakenDoses(
|
||||
takenDosesOrUserId:
|
||||
| Parameters<DatabaseStrategy['updateTakenDoses']>[0]
|
||||
| string,
|
||||
partialUpdate?: Partial<Parameters<DatabaseStrategy['updateTakenDoses']>[0]>
|
||||
) {
|
||||
// Support both new signature (takenDoses) and legacy (userId, partialUpdate)
|
||||
if (typeof takenDosesOrUserId === 'string' && partialUpdate !== undefined) {
|
||||
const existing = await this.strategy.getTakenDoses(takenDosesOrUserId);
|
||||
return this.strategy.updateTakenDoses({
|
||||
...existing,
|
||||
...partialUpdate,
|
||||
});
|
||||
}
|
||||
return this.strategy.updateTakenDoses(
|
||||
takenDosesOrUserId as Parameters<DatabaseStrategy['updateTakenDoses']>[0]
|
||||
);
|
||||
): Promise<Parameters<DatabaseStrategy['updateTakenDoses']>[0]> {
|
||||
return this.strategy.updateTakenDoses(takenDoses);
|
||||
}
|
||||
|
||||
// Custom reminders operations
|
||||
@@ -170,45 +124,17 @@ export class DatabaseService implements DatabaseStrategy {
|
||||
|
||||
// Overloads for updateCustomReminder
|
||||
async updateCustomReminder(
|
||||
userId: string,
|
||||
reminder: Parameters<DatabaseStrategy['updateCustomReminder']>[0]
|
||||
): Promise<Parameters<DatabaseStrategy['updateCustomReminder']>[0]>;
|
||||
async updateCustomReminder(
|
||||
reminder: Parameters<DatabaseStrategy['updateCustomReminder']>[0]
|
||||
): Promise<Parameters<DatabaseStrategy['updateCustomReminder']>[0]>;
|
||||
async updateCustomReminder(
|
||||
userIdOrReminder:
|
||||
| string
|
||||
| Parameters<DatabaseStrategy['updateCustomReminder']>[0],
|
||||
reminder?: Parameters<DatabaseStrategy['updateCustomReminder']>[0]
|
||||
) {
|
||||
// Support both old signature (userId, reminder) and new (reminder)
|
||||
if (typeof userIdOrReminder === 'string' && reminder) {
|
||||
return this.strategy.updateCustomReminder(reminder);
|
||||
}
|
||||
return this.strategy.updateCustomReminder(
|
||||
userIdOrReminder as Parameters<
|
||||
DatabaseStrategy['updateCustomReminder']
|
||||
>[0]
|
||||
);
|
||||
): Promise<Parameters<DatabaseStrategy['updateCustomReminder']>[0]> {
|
||||
return this.strategy.updateCustomReminder(reminder);
|
||||
}
|
||||
|
||||
async getCustomReminders(userId: string) {
|
||||
return this.strategy.getCustomReminders(userId);
|
||||
}
|
||||
|
||||
// Overloads for deleteCustomReminder
|
||||
async deleteCustomReminder(
|
||||
userId: string,
|
||||
reminder: { _id: string }
|
||||
): Promise<boolean>;
|
||||
async deleteCustomReminder(id: string): Promise<boolean>;
|
||||
async deleteCustomReminder(userIdOrId: string, reminder?: { _id: string }) {
|
||||
// Support both old signature (userId, reminder) and new (id)
|
||||
if (reminder) {
|
||||
return this.strategy.deleteCustomReminder(reminder._id);
|
||||
}
|
||||
return this.strategy.deleteCustomReminder(userIdOrId);
|
||||
async deleteCustomReminder(id: string): Promise<boolean> {
|
||||
return this.strategy.deleteCustomReminder(id);
|
||||
}
|
||||
|
||||
// User operations with password
|
||||
@@ -241,42 +167,12 @@ export class DatabaseService implements DatabaseStrategy {
|
||||
return this.strategy instanceof ProductionDatabaseStrategy;
|
||||
}
|
||||
|
||||
// Legacy compatibility methods for existing code
|
||||
async getSettings(userId: string) {
|
||||
return this.strategy.getUserSettings(userId);
|
||||
}
|
||||
|
||||
async addMedication(
|
||||
userId: string,
|
||||
medication: Parameters<DatabaseStrategy['createMedication']>[1]
|
||||
) {
|
||||
return this.strategy.createMedication(userId, medication);
|
||||
}
|
||||
|
||||
async addCustomReminder(
|
||||
userId: string,
|
||||
reminder: Parameters<DatabaseStrategy['createCustomReminder']>[1]
|
||||
) {
|
||||
return this.strategy.createCustomReminder(userId, reminder);
|
||||
}
|
||||
|
||||
async updateSettings(
|
||||
userId: string,
|
||||
settings: Partial<Parameters<DatabaseStrategy['updateUserSettings']>[0]>
|
||||
) {
|
||||
const currentSettings = await this.strategy.getUserSettings(userId);
|
||||
return this.strategy.updateUserSettings({
|
||||
...currentSettings,
|
||||
...settings,
|
||||
});
|
||||
}
|
||||
|
||||
async suspendUser(userId: string) {
|
||||
const user = await this.strategy.getUserById(userId);
|
||||
if (!user) throw new Error('User not found');
|
||||
return this.strategy.updateUser({
|
||||
...user,
|
||||
status: 'SUSPENDED' as AccountStatus,
|
||||
status: AccountStatus.SUSPENDED,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -285,7 +181,7 @@ export class DatabaseService implements DatabaseStrategy {
|
||||
if (!user) throw new Error('User not found');
|
||||
return this.strategy.updateUser({
|
||||
...user,
|
||||
status: 'ACTIVE' as AccountStatus,
|
||||
status: AccountStatus.ACTIVE,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,3 @@ export {
|
||||
export type { DatabaseStrategy } from './types';
|
||||
export { MockDatabaseStrategy } from './MockDatabaseStrategy';
|
||||
export { ProductionDatabaseStrategy } from './ProductionDatabaseStrategy';
|
||||
|
||||
// Legacy compatibility - re-export as dbService for existing code
|
||||
import { databaseService } from './DatabaseService';
|
||||
export { databaseService as dbService };
|
||||
|
||||
// Re-export CouchDBError for backward compatibility
|
||||
import { DatabaseError } from './types';
|
||||
export { DatabaseError as CouchDBError };
|
||||
|
||||
Reference in New Issue
Block a user