import { AdminUser } from '@core/admin/domain/entities/AdminUser'; import { AdminUserRepository, StoredAdminUser, UserFilter, UserListQuery, UserListResult } from '@core/admin/domain/repositories/AdminUserRepository'; import { Email } from '@core/admin/domain/value-objects/Email'; import { UserId } from '@core/admin/domain/value-objects/UserId'; /** * In-memory implementation of AdminUserRepository for testing and development * Follows TDD - created with tests first */ export class InMemoryAdminUserRepository implements AdminUserRepository { private storage: Map = new Map(); async findById(id: UserId): Promise { const stored = this.storage.get(id.value); return stored ? this.fromStored(stored) : null; } async findByEmail(email: Email): Promise { for (const stored of this.storage.values()) { if (stored.email === email.value) { return this.fromStored(stored); } } return null; } async emailExists(email: Email): Promise { for (const stored of this.storage.values()) { if (stored.email === email.value) { return true; } } return false; } async existsById(id: UserId): Promise { return this.storage.has(id.value); } async existsByEmail(email: Email): Promise { return this.emailExists(email); } async list(query?: UserListQuery): Promise { let users: AdminUser[] = []; // Get all users for (const stored of this.storage.values()) { users.push(this.fromStored(stored)); } // Apply filters if (query?.filter) { users = users.filter(user => { if (query.filter?.role && !user.roles.some(r => r.equals(query.filter!.role!))) { return false; } if (query.filter?.status && !user.status.equals(query.filter.status)) { return false; } if (query.filter?.email && !user.email.equals(query.filter.email)) { return false; } if (query.filter?.search) { const search = query.filter.search.toLowerCase(); const matchesEmail = user.email.value.toLowerCase().includes(search); const matchesDisplayName = user.displayName.toLowerCase().includes(search); if (!matchesEmail && !matchesDisplayName) { return false; } } return true; }); } const total = users.length; // Apply sorting if (query?.sort) { const { field, direction } = query.sort; users.sort((a, b) => { let aVal: string | number | Date; let bVal: string | number | Date; switch (field) { case 'email': aVal = a.email.value; bVal = b.email.value; break; case 'displayName': aVal = a.displayName; bVal = b.displayName; break; case 'createdAt': aVal = a.createdAt; bVal = b.createdAt; break; case 'lastLoginAt': aVal = a.lastLoginAt || 0; bVal = b.lastLoginAt || 0; break; case 'status': aVal = a.status.value; bVal = b.status.value; break; default: return 0; } if (aVal < bVal) return direction === 'asc' ? -1 : 1; if (aVal > bVal) return direction === 'asc' ? 1 : -1; return 0; }); } // Apply pagination const page = query?.pagination?.page || 1; const limit = query?.pagination?.limit || 10; const totalPages = Math.ceil(total / limit); const start = (page - 1) * limit; const end = start + limit; const paginatedUsers = users.slice(start, end); return { users: paginatedUsers, total, page, limit, totalPages, }; } async count(filter?: UserFilter): Promise { let count = 0; for (const stored of this.storage.values()) { const user = this.fromStored(stored); if (filter?.role && !user.roles.some(r => r.equals(filter.role!))) { continue; } if (filter?.status && !user.status.equals(filter.status)) { continue; } if (filter?.email && !user.email.equals(filter.email)) { continue; } if (filter?.search) { const search = filter.search.toLowerCase(); const matchesEmail = user.email.value.toLowerCase().includes(search); const matchesDisplayName = user.displayName.toLowerCase().includes(search); if (!matchesEmail && !matchesDisplayName) { continue; } } count++; } return count; } async create(user: AdminUser): Promise { // Check for duplicate email if (await this.emailExists(user.email)) { throw new Error('Email already exists'); } // Check for duplicate ID if (this.storage.has(user.id.value)) { throw new Error('User ID already exists'); } const stored = this.toStored(user); this.storage.set(user.id.value, stored); return user; } async update(user: AdminUser): Promise { // Check if user exists if (!this.storage.has(user.id.value)) { throw new Error('User not found'); } // Check for duplicate email (excluding current user) for (const [id, stored] of this.storage.entries()) { if (id !== user.id.value && stored.email === user.email.value) { throw new Error('Email already exists'); } } const stored = this.toStored(user); this.storage.set(user.id.value, stored); return user; } async delete(id: UserId): Promise { if (!this.storage.has(id.value)) { throw new Error('User not found'); } this.storage.delete(id.value); } toStored(user: AdminUser): StoredAdminUser { const stored: StoredAdminUser = { id: user.id.value, email: user.email.value, roles: user.roles.map(r => r.value), status: user.status.value, displayName: user.displayName, createdAt: user.createdAt, updatedAt: user.updatedAt, }; if (user.lastLoginAt !== undefined) { stored.lastLoginAt = user.lastLoginAt; } if (user.primaryDriverId !== undefined) { stored.primaryDriverId = user.primaryDriverId; } return stored; } fromStored(stored: StoredAdminUser): AdminUser { const props: { id: string; email: string; roles: string[]; status: string; displayName: string; createdAt: Date; updatedAt: Date; lastLoginAt?: Date; primaryDriverId?: string; } = { id: stored.id, email: stored.email, roles: stored.roles, status: stored.status, displayName: stored.displayName, createdAt: stored.createdAt, updatedAt: stored.updatedAt, }; if (stored.lastLoginAt !== undefined) { props.lastLoginAt = stored.lastLoginAt; } if (stored.primaryDriverId !== undefined) { props.primaryDriverId = stored.primaryDriverId; } return AdminUser.rehydrate(props); } }