resolve todos in website

This commit is contained in:
2025-12-20 12:22:48 +01:00
parent a87cf27fb9
commit 20588e1c0b
39 changed files with 1238 additions and 359 deletions

View File

@@ -1,5 +1,5 @@
import { Controller, Get, Post, Body, HttpCode, HttpStatus, Param, Query } from '@nestjs/common';
import { ApiTags, ApiResponse, ApiOperation, ApiParam, ApiQuery } from '@nestjs/swagger';
import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Query } from '@nestjs/common';
import { ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
import { RaceService } from './RaceService';
import { AllRacesPageDTO } from './dtos/AllRacesPageDTO';
import { RaceStatsDTO } from './dtos/RaceStatsDTO';
@@ -137,6 +137,15 @@ export class RaceController {
return this.raceService.completeRace({ raceId });
}
@Post(':raceId/reopen')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Re-open race' })
@ApiParam({ name: 'raceId', description: 'Race ID' })
@ApiResponse({ status: 200, description: 'Successfully re-opened race' })
async reopenRace(@Param('raceId') raceId: string): Promise<void> {
return this.raceService.reopenRace({ raceId });
}
@Post(':raceId/import-results')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Import race results' })
@@ -149,7 +158,6 @@ export class RaceController {
return this.raceService.importRaceResults({ raceId, ...body });
}
@Post('protests/file')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'File a protest' })

View File

@@ -1,19 +1,19 @@
import { Provider } from '@nestjs/common';
import type { Provider } from '@nestjs/common';
import { RaceService } from './RaceService';
// Import core interfaces
import type { Logger } from '@core/shared/application/Logger';
import { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
import { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
import { IStandingRepository } from '@core/racing/domain/repositories/IStandingRepository';
import { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
import { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
import { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
import { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository';
import { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
import { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
import { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
import type { IStandingRepository } from '@core/racing/domain/repositories/IStandingRepository';
import type { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
import type { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository';
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
import type { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
// Import concrete in-memory implementations
import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
@@ -50,6 +50,7 @@ import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPen
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
// Define injection tokens
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
@@ -129,7 +130,8 @@ export const RaceProviders: Provider[] = [
// Use cases
{
provide: GetAllRacesUseCase,
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository, logger: Logger) => new GetAllRacesUseCase(raceRepo, leagueRepo, logger),
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository, logger: Logger) =>
new GetAllRacesUseCase(raceRepo, leagueRepo, logger),
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
@@ -146,14 +148,15 @@ export const RaceProviders: Provider[] = [
raceRegRepo: IRaceRegistrationRepository,
resultRepo: IResultRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
) => new GetRaceDetailUseCase(
raceRepo,
leagueRepo,
driverRepo,
raceRegRepo,
resultRepo,
leagueMembershipRepo,
),
) =>
new GetRaceDetailUseCase(
raceRepo,
leagueRepo,
driverRepo,
raceRegRepo,
resultRepo,
leagueMembershipRepo,
),
inject: [
RACE_REPOSITORY_TOKEN,
LEAGUE_REPOSITORY_TOKEN,
@@ -165,12 +168,14 @@ export const RaceProviders: Provider[] = [
},
{
provide: GetRacesPageDataUseCase,
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) => new GetRacesPageDataUseCase(raceRepo, leagueRepo),
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) =>
new GetRacesPageDataUseCase(raceRepo, leagueRepo),
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN],
},
{
provide: GetAllRacesPageDataUseCase,
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) => new GetAllRacesPageDataUseCase(raceRepo, leagueRepo),
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) =>
new GetAllRacesPageDataUseCase(raceRepo, leagueRepo),
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN],
},
{
@@ -207,18 +212,23 @@ export const RaceProviders: Provider[] = [
},
{
provide: GetRaceProtestsUseCase,
useFactory: (protestRepo: IProtestRepository, driverRepo: IDriverRepository) => new GetRaceProtestsUseCase(protestRepo, driverRepo),
useFactory: (protestRepo: IProtestRepository, driverRepo: IDriverRepository) =>
new GetRaceProtestsUseCase(protestRepo, driverRepo),
inject: [PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
},
{
provide: GetRacePenaltiesUseCase,
useFactory: (penaltyRepo: IPenaltyRepository, driverRepo: IDriverRepository) => new GetRacePenaltiesUseCase(penaltyRepo, driverRepo),
useFactory: (penaltyRepo: IPenaltyRepository, driverRepo: IDriverRepository) =>
new GetRacePenaltiesUseCase(penaltyRepo, driverRepo),
inject: [PENALTY_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
},
{
provide: RegisterForRaceUseCase,
useFactory: (raceRegRepo: IRaceRegistrationRepository, leagueMembershipRepo: ILeagueMembershipRepository, logger: Logger) =>
new RegisterForRaceUseCase(raceRegRepo, leagueMembershipRepo, logger),
useFactory: (
raceRegRepo: IRaceRegistrationRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
logger: Logger,
) => new RegisterForRaceUseCase(raceRegRepo, leagueMembershipRepo, logger),
inject: [RACE_REGISTRATION_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
@@ -248,6 +258,11 @@ export const RaceProviders: Provider[] = [
DRIVER_RATING_PROVIDER_TOKEN,
],
},
{
provide: ReopenRaceUseCase,
useFactory: (raceRepo: IRaceRepository, logger: Logger) => new ReopenRaceUseCase(raceRepo, logger),
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
provide: ImportRaceResultsApiUseCase,
useFactory: (
@@ -257,14 +272,7 @@ export const RaceProviders: Provider[] = [
driverRepo: IDriverRepository,
standingRepo: IStandingRepository,
logger: Logger,
) => new ImportRaceResultsApiUseCase(
raceRepo,
leagueRepo,
resultRepo,
driverRepo,
standingRepo,
logger,
),
) => new ImportRaceResultsApiUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger),
inject: [
RACE_REPOSITORY_TOKEN,
LEAGUE_REPOSITORY_TOKEN,
@@ -283,14 +291,7 @@ export const RaceProviders: Provider[] = [
driverRepo: IDriverRepository,
standingRepo: IStandingRepository,
logger: Logger,
) => new ImportRaceResultsUseCase(
raceRepo,
leagueRepo,
resultRepo,
driverRepo,
standingRepo,
logger,
),
) => new ImportRaceResultsUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger),
inject: [
RACE_REPOSITORY_TOKEN,
LEAGUE_REPOSITORY_TOKEN,
@@ -302,32 +303,61 @@ export const RaceProviders: Provider[] = [
},
{
provide: FileProtestUseCase,
useFactory: (protestRepo: IProtestRepository, raceRepo: IRaceRepository, leagueMembershipRepo: ILeagueMembershipRepository) =>
new FileProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
useFactory: (
protestRepo: IProtestRepository,
raceRepo: IRaceRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
) => new FileProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
},
{
provide: QuickPenaltyUseCase,
useFactory: (penaltyRepo: IPenaltyRepository, raceRepo: IRaceRepository, leagueMembershipRepo: ILeagueMembershipRepository, logger: Logger) =>
new QuickPenaltyUseCase(penaltyRepo, raceRepo, leagueMembershipRepo, logger),
inject: [PENALTY_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
useFactory: (
penaltyRepo: IPenaltyRepository,
raceRepo: IRaceRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
logger: Logger,
) => new QuickPenaltyUseCase(penaltyRepo, raceRepo, leagueMembershipRepo, logger),
inject: [
PENALTY_REPOSITORY_TOKEN,
RACE_REPOSITORY_TOKEN,
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
LOGGER_TOKEN,
],
},
{
provide: ApplyPenaltyUseCase,
useFactory: (penaltyRepo: IPenaltyRepository, protestRepo: IProtestRepository, raceRepo: IRaceRepository, leagueMembershipRepo: ILeagueMembershipRepository, logger: Logger) =>
new ApplyPenaltyUseCase(penaltyRepo, protestRepo, raceRepo, leagueMembershipRepo, logger),
inject: [PENALTY_REPOSITORY_TOKEN, PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
useFactory: (
penaltyRepo: IPenaltyRepository,
protestRepo: IProtestRepository,
raceRepo: IRaceRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
logger: Logger,
) => new ApplyPenaltyUseCase(penaltyRepo, protestRepo, raceRepo, leagueMembershipRepo, logger),
inject: [
PENALTY_REPOSITORY_TOKEN,
PROTEST_REPOSITORY_TOKEN,
RACE_REPOSITORY_TOKEN,
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
LOGGER_TOKEN,
],
},
{
provide: RequestProtestDefenseUseCase,
useFactory: (protestRepo: IProtestRepository, raceRepo: IRaceRepository, leagueMembershipRepo: ILeagueMembershipRepository) =>
new RequestProtestDefenseUseCase(protestRepo, raceRepo, leagueMembershipRepo),
useFactory: (
protestRepo: IProtestRepository,
raceRepo: IRaceRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
) => new RequestProtestDefenseUseCase(protestRepo, raceRepo, leagueMembershipRepo),
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
},
{
provide: ReviewProtestUseCase,
useFactory: (protestRepo: IProtestRepository, raceRepo: IRaceRepository, leagueMembershipRepo: ILeagueMembershipRepository) =>
new ReviewProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
useFactory: (
protestRepo: IProtestRepository,
raceRepo: IRaceRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
) => new ReviewProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
},
];

View File

@@ -1,4 +1,4 @@
import { Injectable, Inject } from '@nestjs/common';
import { ConflictException, Inject, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common';
import type { AllRacesPageViewModel } from '@core/racing/application/presenters/IGetAllRacesPresenter';
import type { RaceDetailOutputPort } from '@core/racing/application/ports/output/RaceDetailOutputPort';
import type { RacesPageOutputPort } from '@core/racing/application/ports/output/RacesPageOutputPort';
@@ -47,6 +47,7 @@ import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPen
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
// Presenters
import { GetAllRacesPresenter } from './presenters/GetAllRacesPresenter';
@@ -61,7 +62,7 @@ import { RequestProtestDefenseCommandDTO } from './dtos/RequestProtestDefenseCom
import { ReviewProtestCommandDTO } from './dtos/ReviewProtestCommandDTO';
// Tokens
import { LOGGER_TOKEN, DRIVER_RATING_PROVIDER_TOKEN, IMAGE_SERVICE_TOKEN, LEAGUE_REPOSITORY_TOKEN } from './RaceProviders';
import { DRIVER_RATING_PROVIDER_TOKEN, IMAGE_SERVICE_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN } from './RaceProviders';
@Injectable()
export class RaceService {
@@ -85,6 +86,7 @@ export class RaceService {
private readonly applyPenaltyUseCase: ApplyPenaltyUseCase,
private readonly requestProtestDefenseUseCase: RequestProtestDefenseUseCase,
private readonly reviewProtestUseCase: ReviewProtestUseCase,
private readonly reopenRaceUseCase: ReopenRaceUseCase,
@Inject(LEAGUE_REPOSITORY_TOKEN) private readonly leagueRepository: ILeagueRepository,
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
@Inject(DRIVER_RATING_PROVIDER_TOKEN) private readonly driverRatingProvider: DriverRatingProvider,
@@ -130,60 +132,68 @@ export class RaceService {
throw new Error('Failed to get race detail');
}
const outputPort = result.value;
const outputPort = result.value as RaceDetailOutputPort;
// Map to DTO
const raceDTO = outputPort.race ? {
id: outputPort.race.id,
leagueId: outputPort.race.leagueId,
track: outputPort.race.track,
car: outputPort.race.car,
scheduledAt: outputPort.race.scheduledAt.toISOString(),
sessionType: outputPort.race.sessionType,
status: outputPort.race.status,
strengthOfField: outputPort.race.strengthOfField ?? null,
registeredCount: outputPort.race.registeredCount ?? undefined,
maxParticipants: outputPort.race.maxParticipants ?? undefined,
} : null;
const raceDTO = outputPort.race
? {
id: outputPort.race.id,
leagueId: outputPort.race.leagueId,
track: outputPort.race.track,
car: outputPort.race.car,
scheduledAt: outputPort.race.scheduledAt.toISOString(),
sessionType: outputPort.race.sessionType,
status: outputPort.race.status,
strengthOfField: outputPort.race.strengthOfField ?? null,
registeredCount: outputPort.race.registeredCount ?? undefined,
maxParticipants: outputPort.race.maxParticipants ?? undefined,
}
: null;
const leagueDTO = outputPort.league ? {
id: outputPort.league.id.toString(),
name: outputPort.league.name.toString(),
description: outputPort.league.description.toString(),
settings: {
maxDrivers: outputPort.league.settings.maxDrivers ?? undefined,
qualifyingFormat: outputPort.league.settings.qualifyingFormat ?? undefined,
},
} : null;
const leagueDTO = outputPort.league
? {
id: outputPort.league.id.toString(),
name: outputPort.league.name.toString(),
description: outputPort.league.description.toString(),
settings: {
maxDrivers: outputPort.league.settings.maxDrivers ?? undefined,
qualifyingFormat: outputPort.league.settings.qualifyingFormat ?? undefined,
},
}
: null;
const entryListDTO = await Promise.all(outputPort.drivers.map(async driver => {
const ratingResult = await this.driverRatingProvider.getDriverRating({ driverId: driver.id });
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
return {
id: driver.id,
name: driver.name.toString(),
country: driver.country.toString(),
avatarUrl: avatarResult.avatarUrl,
rating: ratingResult.rating,
isCurrentUser: driver.id === params.driverId,
};
}));
const entryListDTO = await Promise.all(
outputPort.drivers.map(async driver => {
const ratingResult = await this.driverRatingProvider.getDriverRating({ driverId: driver.id });
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
return {
id: driver.id,
name: driver.name.toString(),
country: driver.country.toString(),
avatarUrl: avatarResult.avatarUrl,
rating: ratingResult.rating,
isCurrentUser: driver.id === params.driverId,
};
}),
);
const registrationDTO = {
isUserRegistered: outputPort.isUserRegistered,
canRegister: outputPort.canRegister,
};
const userResultDTO = outputPort.userResult ? {
position: outputPort.userResult.position.toNumber(),
startPosition: outputPort.userResult.startPosition.toNumber(),
incidents: outputPort.userResult.incidents.toNumber(),
fastestLap: outputPort.userResult.fastestLap.toNumber(),
positionChange: outputPort.userResult.getPositionChange(),
isPodium: outputPort.userResult.isPodium(),
isClean: outputPort.userResult.isClean(),
ratingChange: this.calculateRatingChange(outputPort.userResult.position.toNumber()),
} : null;
const userResultDTO = outputPort.userResult
? {
position: outputPort.userResult.position.toNumber(),
startPosition: outputPort.userResult.startPosition.toNumber(),
incidents: outputPort.userResult.incidents.toNumber(),
fastestLap: outputPort.userResult.fastestLap.toNumber(),
positionChange: outputPort.userResult.getPositionChange(),
isPodium: outputPort.userResult.isPodium(),
isClean: outputPort.userResult.isClean(),
ratingChange: this.calculateRatingChange(outputPort.userResult.position.toNumber()),
}
: null;
return {
race: raceDTO,
@@ -203,7 +213,7 @@ export class RaceService {
throw new Error('Failed to get races page data');
}
const outputPort = result.value;
const outputPort = result.value as RacesPageOutputPort;
// Fetch leagues for league names
const allLeagues = await this.leagueRepository.findAll();
@@ -250,32 +260,34 @@ export class RaceService {
throw new Error('Failed to get race results detail');
}
const outputPort = result.value;
const outputPort = result.value as RaceResultsDetailOutputPort;
// Create a map of driverId to driver for easy lookup
const driverMap = new Map(outputPort.drivers.map(driver => [driver.id, driver]));
const resultsDTO = await Promise.all(outputPort.results.map(async (result) => {
const driver = driverMap.get(result.driverId.toString());
if (!driver) {
throw new Error(`Driver not found for result: ${result.driverId}`);
}
const resultsDTO = await Promise.all(
outputPort.results.map(async singleResult => {
const driver = driverMap.get(singleResult.driverId.toString());
if (!driver) {
throw new Error(`Driver not found for result: ${singleResult.driverId}`);
}
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
return {
driverId: result.driverId.toString(),
driverName: driver.name.toString(),
avatarUrl: avatarResult.avatarUrl,
position: result.position.toNumber(),
startPosition: result.startPosition.toNumber(),
incidents: result.incidents.toNumber(),
fastestLap: result.fastestLap.toNumber(),
positionChange: result.getPositionChange(),
isPodium: result.isPodium(),
isClean: result.isClean(),
};
}));
return {
driverId: singleResult.driverId.toString(),
driverName: driver.name.toString(),
avatarUrl: avatarResult.avatarUrl,
position: singleResult.position.toNumber(),
startPosition: singleResult.startPosition.toNumber(),
incidents: singleResult.incidents.toNumber(),
fastestLap: singleResult.fastestLap.toNumber(),
positionChange: singleResult.getPositionChange(),
isPodium: singleResult.isPodium(),
isClean: singleResult.isClean(),
};
}),
);
return {
raceId: outputPort.race.id,
@@ -293,7 +305,7 @@ export class RaceService {
throw new Error('Failed to get race with SOF');
}
const outputPort = result.value;
const outputPort = result.value as RaceWithSOFOutputPort;
// Map to DTO
return {
@@ -312,7 +324,7 @@ export class RaceService {
throw new Error('Failed to get race protests');
}
const outputPort = result.value;
const outputPort = result.value as RaceProtestsOutputPort;
const protestsDTO = outputPort.protests.map(protest => ({
id: protest.id,
@@ -346,7 +358,7 @@ export class RaceService {
throw new Error('Failed to get race penalties');
}
const outputPort = result.value;
const outputPort = result.value as RacePenaltiesOutputPort;
const penaltiesDTO = outputPort.penalties.map(penalty => ({
id: penalty.id,
@@ -410,7 +422,30 @@ export class RaceService {
}
}
async reopenRace(params: RaceActionParamsDTO): Promise<void> {
this.logger.debug('[RaceService] Re-opening race:', params);
const result = await this.reopenRaceUseCase.execute({ raceId: params.raceId });
if (result.isErr()) {
const errorCode = result.unwrapErr().code;
if (errorCode === 'RACE_NOT_FOUND') {
throw new NotFoundException('Race not found');
}
if (errorCode === 'CANNOT_REOPEN_RUNNING_RACE') {
throw new ConflictException('Cannot re-open a running race');
}
if (errorCode === 'RACE_ALREADY_SCHEDULED') {
this.logger.debug('[RaceService] Race is already scheduled, treating reopen as success.');
return;
}
throw new InternalServerErrorException(errorCode ?? 'UNEXPECTED_ERROR');
}
}
async fileProtest(command: FileProtestCommandDTO): Promise<void> {
this.logger.debug('[RaceService] Filing protest:', command);

View File

@@ -2,45 +2,44 @@ import { Provider } from '@nestjs/common';
import { SponsorService } from './SponsorService';
// Import core interfaces
import { ISponsorRepository } from '@core/racing/domain/repositories/ISponsorRepository';
import { ISeasonSponsorshipRepository } from '@core/racing/domain/repositories/ISeasonSponsorshipRepository';
import { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository';
import { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
import { NotificationService } from '@core/notifications/application/ports/NotificationService';
import type { IPaymentRepository } from '@core/payments/domain/repositories/IPaymentRepository';
import { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
import { IPaymentGateway } from '@core/racing/application/ports/IPaymentGateway';
import { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
import { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
import { ILeagueWalletRepository } from '@core/racing/domain/repositories/ILeagueWalletRepository';
import { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository';
import { ISeasonSponsorshipRepository } from '@core/racing/domain/repositories/ISeasonSponsorshipRepository';
import { ISponsorRepository } from '@core/racing/domain/repositories/ISponsorRepository';
import { ISponsorshipPricingRepository } from '@core/racing/domain/repositories/ISponsorshipPricingRepository';
import { ISponsorshipRequestRepository } from '@core/racing/domain/repositories/ISponsorshipRequestRepository';
import { INotificationService } from '@core/notifications/application/ports/INotificationService';
import { IPaymentGateway } from '@core/racing/application/ports/IPaymentGateway';
import { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
import type { IPaymentRepository } from '@core/payments/domain/repositories/IPaymentRepository';
import { ILeagueWalletRepository } from '@core/racing/domain/repositories/ILeagueWalletRepository';
import type { Logger } from '@core/shared/application';
// Import use cases / application services
import { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
import { GetSponsorsUseCase } from '@core/racing/application/use-cases/GetSponsorsUseCase';
import { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateSponsorUseCase';
import { GetSponsorDashboardUseCase } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
import { GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
import { GetEntitySponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
import { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
import { GetPendingSponsorshipRequestsUseCase } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
import { SponsorBillingService } from '@core/payments/application/services/SponsorBillingService';
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
import { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateSponsorUseCase';
import { GetEntitySponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
import { GetPendingSponsorshipRequestsUseCase } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import { GetSponsorDashboardUseCase } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
import { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
import { GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
import { GetSponsorsUseCase } from '@core/racing/application/use-cases/GetSponsorsUseCase';
import { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
// Import concrete in-memory implementations
import { InMemorySponsorRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorRepository';
import { InMemorySeasonSponsorshipRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository';
import { InMemorySeasonRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonRepository';
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
import { InMemoryLeagueMembershipRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
import { InMemorySeasonRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonRepository';
import { InMemorySeasonSponsorshipRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository';
import { InMemorySponsorRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorRepository';
import { InMemorySponsorshipPricingRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorshipPricingRepository';
import { InMemorySponsorshipRequestRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorshipRequestRepository';
import { InMemoryPaymentRepository } from '@adapters/payments/persistence/inmemory/InMemoryPaymentRepository';
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
// Define injection tokens
export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository';
@@ -179,7 +178,7 @@ export const SponsorProviders: Provider[] = [
},
{
provide: ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonRepo: ISeasonRepository, notificationService: INotificationService, paymentGateway: IPaymentGateway, walletRepository: IWalletRepository, leagueWalletRepository: ILeagueWalletRepository, logger: Logger) =>
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonRepo: ISeasonRepository, notificationService: NotificationService, paymentGateway: IPaymentGateway, walletRepository: IWalletRepository, leagueWalletRepository: ILeagueWalletRepository, logger: Logger) =>
new AcceptSponsorshipRequestUseCase(sponsorshipRequestRepo, seasonSponsorshipRepo, seasonRepo, notificationService, paymentGateway, walletRepository, leagueWalletRepository, logger),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, SEASON_REPOSITORY_TOKEN, 'INotificationService', 'IPaymentGateway', 'IWalletRepository', 'ILeagueWalletRepository', LOGGER_TOKEN],
},