diff --git a/apps/api/src/domain/driver/DriverProviders.ts b/apps/api/src/domain/driver/DriverProviders.ts index 18bc1ba4a..2409e75cd 100644 --- a/apps/api/src/domain/driver/DriverProviders.ts +++ b/apps/api/src/domain/driver/DriverProviders.ts @@ -149,24 +149,25 @@ export const DriverProviders: Provider[] = [ driverStatsService: IDriverStatsService, imageService: IImageServicePort, logger: Logger, - ) => new GetDriversLeaderboardUseCase(driverRepo, rankingService, driverStatsService, (driverId: string) => Promise.resolve(imageService.getDriverAvatar(driverId)), logger), - inject: [DRIVER_REPOSITORY_TOKEN, RANKING_SERVICE_TOKEN, DRIVER_STATS_SERVICE_TOKEN, IMAGE_SERVICE_PORT_TOKEN, LOGGER_TOKEN], + output: UseCaseOutputPort, + ) => new GetDriversLeaderboardUseCase(driverRepo, rankingService, driverStatsService, (driverId: string) => Promise.resolve(imageService.getDriverAvatar(driverId)), logger, output), + inject: [DRIVER_REPOSITORY_TOKEN, RANKING_SERVICE_TOKEN, DRIVER_STATS_SERVICE_TOKEN, IMAGE_SERVICE_PORT_TOKEN, LOGGER_TOKEN, GET_DRIVERS_LEADERBOARD_OUTPUT_PORT_TOKEN], }, { provide: GET_TOTAL_DRIVERS_USE_CASE_TOKEN, - useFactory: (driverRepo: IDriverRepository) => new GetTotalDriversUseCase(driverRepo), - inject: [DRIVER_REPOSITORY_TOKEN], + useFactory: (driverRepo: IDriverRepository, output: UseCaseOutputPort) => new GetTotalDriversUseCase(driverRepo, output), + inject: [DRIVER_REPOSITORY_TOKEN, GET_TOTAL_DRIVERS_OUTPUT_PORT_TOKEN], }, { provide: COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN, - useFactory: (driverRepo: IDriverRepository, logger: Logger) => new CompleteDriverOnboardingUseCase(driverRepo, logger), - inject: [DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN], + useFactory: (driverRepo: IDriverRepository, logger: Logger, output: UseCaseOutputPort) => new CompleteDriverOnboardingUseCase(driverRepo, logger, output), + inject: [DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN, COMPLETE_DRIVER_ONBOARDING_OUTPUT_PORT_TOKEN], }, { provide: IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN, - useFactory: (registrationRepo: IRaceRegistrationRepository, logger: Logger) => - new IsDriverRegisteredForRaceUseCase(registrationRepo, logger), - inject: [RACE_REGISTRATION_REPOSITORY_TOKEN, LOGGER_TOKEN], + useFactory: (registrationRepo: IRaceRegistrationRepository, logger: Logger, output: UseCaseOutputPort) => + new IsDriverRegisteredForRaceUseCase(registrationRepo, logger, output), + inject: [RACE_REGISTRATION_REPOSITORY_TOKEN, LOGGER_TOKEN, IS_DRIVER_REGISTERED_FOR_RACE_OUTPUT_PORT_TOKEN], }, { provide: UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN, diff --git a/apps/api/src/domain/driver/DriverService.ts b/apps/api/src/domain/driver/DriverService.ts index b95d7a075..606fb86a2 100644 --- a/apps/api/src/domain/driver/DriverService.ts +++ b/apps/api/src/domain/driver/DriverService.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { Result } from '@core/shared/application/Result'; import { CompleteOnboardingInputDTO } from './dtos/CompleteOnboardingInputDTO'; import { CompleteOnboardingOutputDTO } from './dtos/CompleteOnboardingOutputDTO'; import { DriverRegistrationStatusDTO } from './dtos/DriverRegistrationStatusDTO'; @@ -69,14 +70,20 @@ export class DriverService { async getDriversLeaderboard(): Promise { this.logger.debug('[DriverService] Fetching drivers leaderboard.'); - await this.getDriversLeaderboardUseCase.execute({}); + const result = await this.getDriversLeaderboardUseCase.execute({}); + if (result.isErr()) { + throw new Error(result.unwrapErr().details.message); + } return this.driversLeaderboardPresenter.getResponseModel(); } async getTotalDrivers(): Promise { this.logger.debug('[DriverService] Fetching total drivers count.'); - await this.getTotalDriversUseCase.execute({}); + const result = await this.getTotalDriversUseCase.execute({}); + if (result.isErr()) { + throw new Error(result.unwrapErr().details.message); + } return this.driverStatsPresenter.getResponseModel(); } @@ -86,7 +93,7 @@ export class DriverService { ): Promise { this.logger.debug('Completing onboarding for user:', userId); - await this.completeDriverOnboardingUseCase.execute({ + const result = await this.completeDriverOnboardingUseCase.execute({ userId, firstName: input.firstName, lastName: input.lastName, @@ -95,6 +102,9 @@ export class DriverService { ...(input.bio !== undefined ? { bio: input.bio } : {}), }); + if (result.isErr()) { + throw new Error(result.unwrapErr().details.message); + } return this.completeOnboardingPresenter.getResponseModel(); } @@ -103,18 +113,22 @@ export class DriverService { ): Promise { this.logger.debug('Checking driver registration status:', query); - await this.isDriverRegisteredForRaceUseCase.execute({ + const result = await this.isDriverRegisteredForRaceUseCase.execute({ raceId: query.raceId, driverId: query.driverId, }); + if (result.isErr()) { + throw new Error(result.unwrapErr().details.message); + } return this.driverRegistrationStatusPresenter.getResponseModel(); } async getCurrentDriver(userId: string): Promise { this.logger.debug(`[DriverService] Fetching current driver for userId: ${userId}`); - await this.driverRepository.findById(userId); + const driver = await this.driverRepository.findById(userId); + this.driverPresenter.present(Result.ok(driver)); return this.driverPresenter.getResponseModel(); } @@ -136,14 +150,18 @@ export class DriverService { async getDriver(driverId: string): Promise { this.logger.debug(`[DriverService] Fetching driver for driverId: ${driverId}`); - await this.driverRepository.findById(driverId); + const driver = await this.driverRepository.findById(driverId); + this.driverPresenter.present(Result.ok(driver)); return this.driverPresenter.getResponseModel(); } async getDriverProfile(driverId: string): Promise { this.logger.debug(`[DriverService] Fetching driver profile for driverId: ${driverId}`); - await this.getProfileOverviewUseCase.execute({ driverId }); + const result = await this.getProfileOverviewUseCase.execute({ driverId }); + if (result.isErr()) { + throw new Error(result.unwrapErr().details.message); + } return this.driverProfilePresenter.getResponseModel(); } } \ No newline at end of file diff --git a/apps/api/src/domain/driver/presenters/CompleteOnboardingPresenter.ts b/apps/api/src/domain/driver/presenters/CompleteOnboardingPresenter.ts index 7d9fd11d2..127b2ebe0 100644 --- a/apps/api/src/domain/driver/presenters/CompleteOnboardingPresenter.ts +++ b/apps/api/src/domain/driver/presenters/CompleteOnboardingPresenter.ts @@ -1,17 +1,10 @@ -import { Result } from '@core/shared/application/Result'; import type { CompleteOnboardingOutputDTO } from '../dtos/CompleteOnboardingOutputDTO'; +import type { CompleteDriverOnboardingResult } from '@core/racing/application/use-cases/CompleteDriverOnboardingUseCase'; export class CompleteOnboardingPresenter { private responseModel: CompleteOnboardingOutputDTO | null = null; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - present(result: Result): void { - if (result.isErr()) { - const error = result.unwrapErr(); - throw new Error(error.details?.message ?? 'Failed to complete onboarding'); - } - - const data = result.unwrap(); + present(data: CompleteDriverOnboardingResult): void { this.responseModel = { success: true, driverId: data.driver.id, diff --git a/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.test.ts b/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.test.ts index e872f196c..92fbc3346 100644 --- a/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.test.ts +++ b/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.test.ts @@ -1,5 +1,4 @@ import { GetDriversLeaderboardResult } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase'; -import { Result } from '@core/shared/application/Result'; import { beforeEach, describe, expect, it } from 'vitest'; import { DriversLeaderboardPresenter } from './DriversLeaderboardPresenter'; import type { Driver } from '@core/racing/domain/entities/Driver'; @@ -54,9 +53,7 @@ describe('DriversLeaderboardPresenter', () => { activeCount: 2, }; - const result = Result.ok(coreResult); - - presenter.present(result); + presenter.present(coreResult); const output = presenter.getResponseModel(); diff --git a/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.ts b/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.ts index efb3fc21c..09225e756 100644 --- a/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.ts +++ b/apps/api/src/domain/driver/presenters/DriversLeaderboardPresenter.ts @@ -1,21 +1,12 @@ import { DriversLeaderboardDTO } from '../dtos/DriversLeaderboardDTO'; -import { Result } from '@core/shared/application/Result'; -import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { GetDriversLeaderboardResult, - GetDriversLeaderboardErrorCode, } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase'; export class DriversLeaderboardPresenter { private responseModel: DriversLeaderboardDTO | null = null; - present(result: Result>): void { - if (result.isErr()) { - const error = result.unwrapErr(); - throw new Error(error.details?.message ?? 'Failed to get drivers leaderboard'); - } - - const data = result.unwrap(); + present(data: GetDriversLeaderboardResult): void { this.responseModel = { drivers: data.items.map(item => ({ id: item.driver.id, diff --git a/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.ts b/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.ts index 48bd9e57c..54656aaa2 100644 --- a/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.ts +++ b/core/racing/application/use-cases/CompleteDriverOnboardingUseCase.ts @@ -2,7 +2,7 @@ import type { IDriverRepository } from '../../domain/repositories/IDriverReposit import { Driver } from '../../domain/entities/Driver'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; -import type { UseCase } from '@core/shared/application'; +import type { UseCase, UseCaseOutputPort } from '@core/shared/application'; import type { Logger } from '@core/shared/application/Logger'; export interface CompleteDriverOnboardingInput { @@ -30,15 +30,16 @@ export type CompleteDriverOnboardingApplicationError = ApplicationErrorCode< /** * Use Case for completing driver onboarding. */ -export class CompleteDriverOnboardingUseCase implements UseCase { +export class CompleteDriverOnboardingUseCase implements UseCase { constructor( private readonly driverRepository: IDriverRepository, private readonly logger: Logger, + private readonly output: UseCaseOutputPort, ) {} async execute( input: CompleteDriverOnboardingInput, - ): Promise> { + ): Promise> { try { const existing = await this.driverRepository.findById(input.userId); if (existing) { @@ -59,7 +60,8 @@ export class CompleteDriverOnboardingUseCase implements UseCase { +export class GetDriversLeaderboardUseCase implements UseCase { constructor( private readonly driverRepository: IDriverRepository, private readonly rankingService: IRankingService, private readonly driverStatsService: IDriverStatsService, private readonly getDriverAvatar: (driverId: string) => Promise, private readonly logger: Logger, + private readonly output: UseCaseOutputPort, ) {} async execute( input: GetDriversLeaderboardInput, ): Promise< Result< - GetDriversLeaderboardResult, + void, ApplicationErrorCode > > { @@ -105,7 +106,9 @@ export class GetDriversLeaderboardUseCase implements UseCase { +export class GetTotalDriversUseCase implements UseCase { constructor( private readonly driverRepository: IDriverRepository, + private readonly output: UseCaseOutputPort, ) {} async execute( _input: GetTotalDriversInput, - ): Promise>> { + ): Promise>> { void _input; try { const drivers = await this.driverRepository.findAll(); const result: GetTotalDriversResult = { totalDrivers: drivers.length }; - return Result.ok(result); + this.output.present(result); + + return Result.ok(void 0); } catch (error) { const message = (error as Error | undefined)?.message ?? 'Failed to compute total drivers'; diff --git a/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.ts b/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.ts index c2b3afe4d..60bffeee7 100644 --- a/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.ts +++ b/core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase.ts @@ -1,5 +1,5 @@ import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository'; -import type { Logger, UseCase } from '@core/shared/application'; +import type { Logger, UseCase, UseCaseOutputPort } from '@core/shared/application'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; @@ -26,20 +26,22 @@ export type IsDriverRegisteredForRaceResult = { * * Checks if a driver is registered for a specific race. */ -export class IsDriverRegisteredForRaceUseCase implements UseCase { +export class IsDriverRegisteredForRaceUseCase implements UseCase { constructor( private readonly registrationRepository: IRaceRegistrationRepository, private readonly logger: Logger, + private readonly output: UseCaseOutputPort, ) {} - async execute(params: IsDriverRegisteredForRaceInput): Promise> { + async execute(params: IsDriverRegisteredForRaceInput): Promise> { this.logger.debug('IsDriverRegisteredForRaceUseCase:execute', { params }); const { raceId, driverId } = params; try { const isRegistered = await this.registrationRepository.isRegistered(raceId, driverId); - return Result.ok({ isRegistered, raceId, driverId }); + this.output.present({ isRegistered, raceId, driverId }); + return Result.ok(void 0); } catch (error) { this.logger.error( 'IsDriverRegisteredForRaceUseCase:execution error',