186 lines
6.0 KiB
TypeScript
186 lines
6.0 KiB
TypeScript
/**
|
|
* Infrastructure Adapter: InMemoryTrackRepository
|
|
*
|
|
* In-memory implementation of ITrackRepository.
|
|
* Stores data in Map structure with UUID generation.
|
|
*/
|
|
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { Track, TrackCategory } from '@gridpilot/racing/domain/entities/Track';
|
|
import type { ITrackRepository } from '@gridpilot/racing/domain/repositories/ITrackRepository';
|
|
import type { Logger } from '@gridpilot/shared/logging/Logger';
|
|
|
|
export class InMemoryTrackRepository implements ITrackRepository {
|
|
private tracks: Map<string, Track>;
|
|
private readonly logger: Logger;
|
|
|
|
constructor(logger: Logger, seedData?: Track[]) {
|
|
this.logger = logger;
|
|
this.logger.info('InMemoryTrackRepository initialized.');
|
|
this.tracks = new Map();
|
|
|
|
if (seedData) {
|
|
seedData.forEach(track => {
|
|
this.tracks.set(track.id, track);
|
|
this.logger.debug(`Seeded track: ${track.id}.`);
|
|
});
|
|
}
|
|
}
|
|
|
|
async findById(id: string): Promise<Track | null> {
|
|
this.logger.debug(`Finding track by id: ${id}`);
|
|
try {
|
|
const track = this.tracks.get(id) ?? null;
|
|
if (track) {
|
|
this.logger.info(`Found track: ${id}.`);
|
|
} else {
|
|
this.logger.warn(`Track with id ${id} not found.`);
|
|
}
|
|
return track;
|
|
} catch (error) {
|
|
this.logger.error(`Error finding track by id ${id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async findAll(): Promise<Track[]> {
|
|
this.logger.debug('Finding all tracks.');
|
|
try {
|
|
const tracks = Array.from(this.tracks.values());
|
|
this.logger.info(`Found ${tracks.length} tracks.`);
|
|
return tracks;
|
|
} catch (error) {
|
|
this.logger.error('Error finding all tracks:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async findByGameId(gameId: string): Promise<Track[]> {
|
|
this.logger.debug(`Finding tracks by game id: ${gameId}`);
|
|
try {
|
|
const tracks = Array.from(this.tracks.values())
|
|
.filter(track => track.gameId === gameId)
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
this.logger.info(`Found ${tracks.length} tracks for game id: ${gameId}.`);
|
|
return tracks;
|
|
} catch (error) {
|
|
this.logger.error(`Error finding tracks by game id ${gameId}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async findByCategory(category: TrackCategory): Promise<Track[]> {
|
|
this.logger.debug(`Finding tracks by category: ${category}`);
|
|
try {
|
|
const tracks = Array.from(this.tracks.values())
|
|
.filter(track => track.category === category)
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
this.logger.info(`Found ${tracks.length} tracks for category: ${category}.`);
|
|
return tracks;
|
|
} catch (error) {
|
|
this.logger.error(`Error finding tracks by category ${category}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async findByCountry(country: string): Promise<Track[]> {
|
|
this.logger.debug(`Finding tracks by country: ${country}`);
|
|
try {
|
|
const tracks = Array.from(this.tracks.values())
|
|
.filter(track => track.country.toLowerCase() === country.toLowerCase())
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
this.logger.info(`Found ${tracks.length} tracks for country: ${country}.`);
|
|
return tracks;
|
|
} catch (error) {
|
|
this.logger.error(`Error finding tracks by country ${country}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async searchByName(query: string): Promise<Track[]> {
|
|
this.logger.debug(`Searching tracks by name query: ${query}`);
|
|
try {
|
|
const lowerQuery = query.toLowerCase();
|
|
const tracks = Array.from(this.tracks.values())
|
|
.filter(track =>
|
|
track.name.toLowerCase().includes(lowerQuery) ||
|
|
track.shortName.toLowerCase().includes(lowerQuery)
|
|
)
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
this.logger.info(`Found ${tracks.length} tracks matching search query: ${query}.`);
|
|
return tracks;
|
|
} catch (error) {
|
|
this.logger.error(`Error searching tracks by name query ${query}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async create(track: Track): Promise<Track> {
|
|
this.logger.debug(`Creating track: ${track.id}`);
|
|
try {
|
|
if (await this.exists(track.id)) {
|
|
this.logger.warn(`Track with ID ${track.id} already exists.`);
|
|
throw new Error(`Track with ID ${track.id} already exists`);
|
|
}
|
|
|
|
this.tracks.set(track.id, track);
|
|
this.logger.info(`Track ${track.id} created successfully.`);
|
|
return track;
|
|
} catch (error) {
|
|
this.logger.error(`Error creating track ${track.id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async update(track: Track): Promise<Track> {
|
|
this.logger.debug(`Updating track: ${track.id}`);
|
|
try {
|
|
if (!await this.exists(track.id)) {
|
|
this.logger.warn(`Track with ID ${track.id} not found for update.`);
|
|
throw new Error(`Track with ID ${track.id} not found`);
|
|
}
|
|
|
|
this.tracks.set(track.id, track);
|
|
this.logger.info(`Track ${track.id} updated successfully.`);
|
|
return track;
|
|
} catch (error) {
|
|
this.logger.error(`Error updating track ${track.id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async delete(id: string): Promise<void> {
|
|
this.logger.debug(`Deleting track: ${id}`);
|
|
try {
|
|
if (!await this.exists(id)) {
|
|
this.logger.warn(`Track with ID ${id} not found for deletion.`);
|
|
throw new Error(`Track with ID ${id} not found`);
|
|
}
|
|
|
|
this.tracks.delete(id);
|
|
this.logger.info(`Track ${id} deleted successfully.`);
|
|
} catch (error) {
|
|
this.logger.error(`Error deleting track ${id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async exists(id: string): Promise<boolean> {
|
|
this.logger.debug(`Checking existence of track with id: ${id}`);
|
|
try {
|
|
const exists = this.tracks.has(id);
|
|
this.logger.debug(`Track ${id} exists: ${exists}.`);
|
|
return exists;
|
|
} catch (error) {
|
|
this.logger.error(`Error checking existence of track with id ${id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility method to generate a new UUID
|
|
*/
|
|
static generateId(): string {
|
|
return uuidv4();
|
|
}
|
|
} |