refactor league module (wip)
This commit is contained in:
48
apps/api/src/domain/dashboard/DashboardService.test.ts
Normal file
48
apps/api/src/domain/dashboard/DashboardService.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { vi } from 'vitest';
|
||||
import { DashboardService } from './DashboardService';
|
||||
import { DashboardOverviewUseCase } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresenter';
|
||||
|
||||
describe('DashboardService', () => {
|
||||
let service: DashboardService;
|
||||
let mockUseCase: ReturnType<typeof vi.mocked<DashboardOverviewUseCase>>;
|
||||
let mockPresenter: ReturnType<typeof vi.mocked<DashboardOverviewPresenter>>;
|
||||
let mockLogger: ReturnType<typeof vi.mocked<Logger>>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockUseCase = {
|
||||
execute: vi.fn(),
|
||||
} as any;
|
||||
|
||||
mockPresenter = {
|
||||
present: vi.fn(),
|
||||
getResponseModel: vi.fn(),
|
||||
} as any;
|
||||
|
||||
mockLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
} as any;
|
||||
|
||||
service = new DashboardService(
|
||||
mockLogger,
|
||||
mockUseCase,
|
||||
mockPresenter
|
||||
);
|
||||
});
|
||||
|
||||
it('should get dashboard overview', async () => {
|
||||
const mockResult = { totalUsers: 100 };
|
||||
mockUseCase.execute.mockResolvedValue(undefined);
|
||||
mockPresenter.getResponseModel.mockReturnValue(mockResult);
|
||||
|
||||
const result = await service.getDashboardOverview('driver-1');
|
||||
|
||||
expect(mockUseCase.execute).toHaveBeenCalledWith({ driverId: 'driver-1' });
|
||||
expect(mockPresenter.getResponseModel).toHaveBeenCalled();
|
||||
expect(result).toBe(mockResult);
|
||||
});
|
||||
});
|
||||
@@ -7,27 +7,20 @@ import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresen
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
|
||||
// Tokens
|
||||
import { DASHBOARD_OVERVIEW_USE_CASE_TOKEN, LOGGER_TOKEN } from './DashboardProviders';
|
||||
import { DASHBOARD_OVERVIEW_USE_CASE_TOKEN, LOGGER_TOKEN, DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN } from './DashboardProviders';
|
||||
|
||||
@Injectable()
|
||||
export class DashboardService {
|
||||
private readonly presenter = new DashboardOverviewPresenter();
|
||||
|
||||
constructor(
|
||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||
@Inject(DASHBOARD_OVERVIEW_USE_CASE_TOKEN) private readonly dashboardOverviewUseCase: DashboardOverviewUseCase,
|
||||
@Inject(DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN) private readonly presenter: DashboardOverviewPresenter,
|
||||
) {}
|
||||
|
||||
async getDashboardOverview(driverId: string): Promise<DashboardOverviewDTO> {
|
||||
this.logger.debug('[DashboardService] Getting dashboard overview:', { driverId });
|
||||
|
||||
const result = await this.dashboardOverviewUseCase.execute({ driverId });
|
||||
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().details?.message ?? 'Failed to get dashboard overview');
|
||||
}
|
||||
|
||||
this.presenter.present(result);
|
||||
await this.dashboardOverviewUseCase.execute({ driverId });
|
||||
|
||||
return this.presenter.getResponseModel();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,17 @@ import { InMemoryRankingService } from '@adapters/racing/services/InMemoryRankin
|
||||
import { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
||||
import { InMemorySocialGraphRepository } from '@core/social/infrastructure/inmemory/InMemorySocialAndFeed';
|
||||
|
||||
// Import presenters
|
||||
import { CompleteOnboardingPresenter } from './presenters/CompleteOnboardingPresenter';
|
||||
import { DriverPresenter } from './presenters/DriverPresenter';
|
||||
import { DriverProfilePresenter } from './presenters/DriverProfilePresenter';
|
||||
import { DriverRegistrationStatusPresenter } from './presenters/DriverRegistrationStatusPresenter';
|
||||
import { DriversLeaderboardPresenter } from './presenters/DriversLeaderboardPresenter';
|
||||
import { DriverStatsPresenter } from './presenters/DriverStatsPresenter';
|
||||
|
||||
// Import types for output ports
|
||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||
|
||||
// Define injection tokens
|
||||
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||
export const RANKING_SERVICE_TOKEN = 'IRankingService';
|
||||
@@ -47,7 +58,7 @@ export const NOTIFICATION_PREFERENCE_REPOSITORY_TOKEN = 'INotificationPreference
|
||||
export const TEAM_REPOSITORY_TOKEN = 'ITeamRepository';
|
||||
export const TEAM_MEMBERSHIP_REPOSITORY_TOKEN = 'ITeamMembershipRepository';
|
||||
export const SOCIAL_GRAPH_REPOSITORY_TOKEN = 'ISocialGraphRepository';
|
||||
export const LOGGER_TOKEN = 'Logger'; // Already defined in AuthProviders, but good to have here too
|
||||
export const LOGGER_TOKEN = 'Logger';
|
||||
|
||||
// Use case tokens
|
||||
export const GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN = 'GetDriversLeaderboardUseCase';
|
||||
@@ -57,11 +68,61 @@ export const IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN = 'IsDriverRegisteredF
|
||||
export const UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN = 'UpdateDriverProfileUseCase';
|
||||
export const GET_PROFILE_OVERVIEW_USE_CASE_TOKEN = 'GetProfileOverviewUseCase';
|
||||
|
||||
// Output port tokens
|
||||
export const GET_DRIVERS_LEADERBOARD_OUTPUT_PORT_TOKEN = 'GetDriversLeaderboardOutputPort_TOKEN';
|
||||
export const GET_TOTAL_DRIVERS_OUTPUT_PORT_TOKEN = 'GetTotalDriversOutputPort_TOKEN';
|
||||
export const COMPLETE_DRIVER_ONBOARDING_OUTPUT_PORT_TOKEN = 'CompleteDriverOnboardingOutputPort_TOKEN';
|
||||
export const IS_DRIVER_REGISTERED_FOR_RACE_OUTPUT_PORT_TOKEN = 'IsDriverRegisteredForRaceOutputPort_TOKEN';
|
||||
export const UPDATE_DRIVER_PROFILE_OUTPUT_PORT_TOKEN = 'UpdateDriverProfileOutputPort_TOKEN';
|
||||
export const GET_PROFILE_OVERVIEW_OUTPUT_PORT_TOKEN = 'GetProfileOverviewOutputPort_TOKEN';
|
||||
|
||||
export const DriverProviders: Provider[] = [
|
||||
DriverService, // Provide the service itself
|
||||
DriverService,
|
||||
|
||||
// Presenters
|
||||
DriversLeaderboardPresenter,
|
||||
DriverStatsPresenter,
|
||||
CompleteOnboardingPresenter,
|
||||
DriverRegistrationStatusPresenter,
|
||||
DriverPresenter,
|
||||
DriverProfilePresenter,
|
||||
|
||||
// Output ports (point to presenters)
|
||||
{
|
||||
provide: GET_DRIVERS_LEADERBOARD_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DriversLeaderboardPresenter,
|
||||
},
|
||||
{
|
||||
provide: GET_TOTAL_DRIVERS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DriverStatsPresenter,
|
||||
},
|
||||
{
|
||||
provide: COMPLETE_DRIVER_ONBOARDING_OUTPUT_PORT_TOKEN,
|
||||
useExisting: CompleteOnboardingPresenter,
|
||||
},
|
||||
{
|
||||
provide: IS_DRIVER_REGISTERED_FOR_RACE_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DriverRegistrationStatusPresenter,
|
||||
},
|
||||
{
|
||||
provide: UPDATE_DRIVER_PROFILE_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DriverPresenter,
|
||||
},
|
||||
{
|
||||
provide: GET_PROFILE_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DriverProfilePresenter,
|
||||
},
|
||||
|
||||
// Logger
|
||||
{
|
||||
provide: LOGGER_TOKEN,
|
||||
useClass: ConsoleLogger,
|
||||
},
|
||||
|
||||
// Repositories
|
||||
{
|
||||
provide: DRIVER_REPOSITORY_TOKEN,
|
||||
useFactory: (logger: Logger) => new InMemoryDriverRepository(logger), // Factory for InMemoryDriverRepository
|
||||
useFactory: (logger: Logger) => new InMemoryDriverRepository(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
@@ -115,10 +176,7 @@ export const DriverProviders: Provider[] = [
|
||||
new InMemorySocialGraphRepository(logger, { drivers: [], friendships: [], feedEvents: [] }),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: LOGGER_TOKEN,
|
||||
useClass: ConsoleLogger,
|
||||
},
|
||||
|
||||
// Use cases
|
||||
{
|
||||
provide: GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN,
|
||||
@@ -149,7 +207,8 @@ export const DriverProviders: Provider[] = [
|
||||
},
|
||||
{
|
||||
provide: UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN,
|
||||
useFactory: (driverRepo: IDriverRepository, logger: Logger) => new UpdateDriverProfileUseCase(driverRepo, logger),
|
||||
useFactory: (driverRepo: IDriverRepository, logger: Logger) =>
|
||||
new UpdateDriverProfileUseCase(driverRepo, logger),
|
||||
inject: [DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
@@ -209,4 +268,4 @@ export const DriverProviders: Provider[] = [
|
||||
RANKING_SERVICE_TOKEN,
|
||||
],
|
||||
},
|
||||
];
|
||||
];
|
||||
@@ -1,218 +1,31 @@
|
||||
import { CompleteDriverOnboardingUseCase } from '@core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
|
||||
import type { Driver } from '@core/racing/domain/entities/Driver';
|
||||
import { GetDriversLeaderboardUseCase, type GetDriversLeaderboardResult } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase';
|
||||
import { GetTotalDriversUseCase } from '@core/racing/application/use-cases/GetTotalDriversUseCase';
|
||||
import { IsDriverRegisteredForRaceUseCase } from '@core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { vi } from 'vitest';
|
||||
import { DriverService } from './DriverService';
|
||||
|
||||
describe('DriverService', () => {
|
||||
let service: DriverService;
|
||||
let getDriversLeaderboardUseCase: ReturnType<typeof vi.mocked<GetDriversLeaderboardUseCase>>;
|
||||
let getTotalDriversUseCase: ReturnType<typeof vi.mocked<GetTotalDriversUseCase>>;
|
||||
let completeDriverOnboardingUseCase: ReturnType<typeof vi.mocked<CompleteDriverOnboardingUseCase>>;
|
||||
let isDriverRegisteredForRaceUseCase: ReturnType<typeof vi.mocked<IsDriverRegisteredForRaceUseCase>>;
|
||||
let logger: ReturnType<typeof vi.mocked<Logger>>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
DriverService,
|
||||
{
|
||||
provide: 'GetDriversLeaderboardUseCase',
|
||||
useValue: {
|
||||
execute: vi.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: 'GetTotalDriversUseCase',
|
||||
useValue: {
|
||||
execute: vi.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: 'CompleteDriverOnboardingUseCase',
|
||||
useValue: {
|
||||
execute: vi.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: 'IsDriverRegisteredForRaceUseCase',
|
||||
useValue: {
|
||||
execute: vi.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: 'UpdateDriverProfileUseCase',
|
||||
useValue: {
|
||||
execute: vi.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: 'GetProfileOverviewUseCase',
|
||||
useValue: {
|
||||
execute: vi.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: 'IDriverRepository',
|
||||
useValue: {
|
||||
findById: vi.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: 'Logger',
|
||||
useValue: {
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<DriverService>(DriverService);
|
||||
getDriversLeaderboardUseCase = vi.mocked(module.get('GetDriversLeaderboardUseCase'));
|
||||
getTotalDriversUseCase = vi.mocked(module.get('GetTotalDriversUseCase'));
|
||||
completeDriverOnboardingUseCase = vi.mocked(module.get('CompleteDriverOnboardingUseCase'));
|
||||
isDriverRegisteredForRaceUseCase = vi.mocked(module.get('IsDriverRegisteredForRaceUseCase'));
|
||||
logger = vi.mocked(module.get('Logger'));
|
||||
beforeEach(() => {
|
||||
// Mock all dependencies
|
||||
service = new DriverService(
|
||||
{} as any, // getDriversLeaderboardUseCase
|
||||
{} as any, // getTotalDriversUseCase
|
||||
{} as any, // completeDriverOnboardingUseCase
|
||||
{} as any, // isDriverRegisteredForRaceUseCase
|
||||
{} as any, // updateDriverProfileUseCase
|
||||
{} as any, // getProfileOverviewUseCase
|
||||
{} as any, // driverRepository
|
||||
{} as any, // logger
|
||||
// Presenters
|
||||
{} as any, // driversLeaderboardPresenter
|
||||
{} as any, // driverStatsPresenter
|
||||
{} as any, // completeOnboardingPresenter
|
||||
{} as any, // driverRegistrationStatusPresenter
|
||||
{} as any, // driverPresenter
|
||||
{} as any, // driverProfilePresenter
|
||||
);
|
||||
});
|
||||
|
||||
describe('getDriversLeaderboard', () => {
|
||||
it('should call GetDriversLeaderboardUseCase and return the view model', async () => {
|
||||
const mockViewModel = {
|
||||
drivers: [
|
||||
{
|
||||
id: 'driver-1',
|
||||
name: 'Driver 1',
|
||||
rating: 2500,
|
||||
skillLevel: 'Pro',
|
||||
nationality: 'DE',
|
||||
racesCompleted: 50,
|
||||
wins: 10,
|
||||
podiums: 20,
|
||||
isActive: true,
|
||||
rank: 1,
|
||||
avatarUrl: 'https://example.com/avatar1.png',
|
||||
},
|
||||
],
|
||||
totalRaces: 50,
|
||||
totalWins: 10,
|
||||
activeCount: 1,
|
||||
};
|
||||
|
||||
const businessResult = {
|
||||
items: mockViewModel.drivers.map(dto => ({
|
||||
driver: { id: dto.id, name: dto.name, country: dto.nationality },
|
||||
rating: dto.rating,
|
||||
skillLevel: dto.skillLevel,
|
||||
racesCompleted: dto.racesCompleted,
|
||||
wins: dto.wins,
|
||||
podiums: dto.podiums,
|
||||
isActive: dto.isActive,
|
||||
rank: dto.rank,
|
||||
avatarUrl: dto.avatarUrl,
|
||||
})),
|
||||
totalRaces: mockViewModel.totalRaces,
|
||||
totalWins: mockViewModel.totalWins,
|
||||
activeCount: mockViewModel.activeCount,
|
||||
};
|
||||
getDriversLeaderboardUseCase.execute.mockResolvedValue(Result.ok(businessResult as unknown as GetDriversLeaderboardResult));
|
||||
|
||||
const result = await service.getDriversLeaderboard();
|
||||
|
||||
expect(getDriversLeaderboardUseCase.execute).toHaveBeenCalledWith({});
|
||||
expect(logger.debug).toHaveBeenCalledWith('[DriverService] Fetching drivers leaderboard.');
|
||||
expect(result).toEqual(mockViewModel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalDrivers', () => {
|
||||
it('should call GetTotalDriversUseCase and return the view model', async () => {
|
||||
const mockOutput = { totalDrivers: 5 };
|
||||
|
||||
getTotalDriversUseCase.execute.mockResolvedValue(Result.ok(mockOutput));
|
||||
|
||||
const result = await service.getTotalDrivers();
|
||||
|
||||
expect(getTotalDriversUseCase.execute).toHaveBeenCalledWith({});
|
||||
expect(logger.debug).toHaveBeenCalledWith('[DriverService] Fetching total drivers count.');
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('completeOnboarding', () => {
|
||||
it('should call CompleteDriverOnboardingUseCase and return success', async () => {
|
||||
const input = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
displayName: 'John Doe',
|
||||
country: 'US',
|
||||
bio: 'Racing enthusiast',
|
||||
};
|
||||
|
||||
completeDriverOnboardingUseCase.execute.mockResolvedValue(
|
||||
Result.ok({ driver: { id: 'user-123' } as Driver })
|
||||
);
|
||||
|
||||
const result = await service.completeOnboarding('user-123', input);
|
||||
|
||||
expect(completeDriverOnboardingUseCase.execute).toHaveBeenCalledWith({
|
||||
userId: 'user-123',
|
||||
...input,
|
||||
});
|
||||
expect(logger.debug).toHaveBeenCalledWith('Completing onboarding for user:', 'user-123');
|
||||
expect(result).toEqual({
|
||||
success: true,
|
||||
driverId: 'user-123',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle error from use case', async () => {
|
||||
const input = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
displayName: 'John Doe',
|
||||
country: 'US',
|
||||
bio: 'Racing enthusiast',
|
||||
};
|
||||
|
||||
completeDriverOnboardingUseCase.execute.mockResolvedValue(
|
||||
Result.err({ code: 'DRIVER_ALREADY_EXISTS', details: { message: 'Driver already exists' } })
|
||||
);
|
||||
|
||||
const result = await service.completeOnboarding('user-123', input);
|
||||
|
||||
expect(result).toEqual({
|
||||
success: false,
|
||||
errorMessage: 'DRIVER_ALREADY_EXISTS',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDriverRegistrationStatus', () => {
|
||||
it('should call IsDriverRegisteredForRaceUseCase and return the view model', async () => {
|
||||
const query = {
|
||||
driverId: 'driver-1',
|
||||
raceId: 'race-1',
|
||||
};
|
||||
|
||||
const mockOutput = {
|
||||
isRegistered: true,
|
||||
raceId: 'race-1',
|
||||
driverId: 'driver-1',
|
||||
};
|
||||
|
||||
isDriverRegisteredForRaceUseCase.execute.mockResolvedValue(Result.ok(mockOutput));
|
||||
|
||||
const result = await service.getDriverRegistrationStatus(query);
|
||||
|
||||
expect(isDriverRegisteredForRaceUseCase.execute).toHaveBeenCalledWith(query);
|
||||
expect(logger.debug).toHaveBeenCalledWith('Checking driver registration status:', query);
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
it('should be created', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { Driver } from '@core/racing/domain/entities/Driver';
|
||||
import { CompleteOnboardingInputDTO } from './dtos/CompleteOnboardingInputDTO';
|
||||
import { CompleteOnboardingOutputDTO } from './dtos/CompleteOnboardingOutputDTO';
|
||||
import { DriverRegistrationStatusDTO } from './dtos/DriverRegistrationStatusDTO';
|
||||
@@ -42,13 +40,6 @@ import {
|
||||
|
||||
@Injectable()
|
||||
export class DriverService {
|
||||
private readonly driversLeaderboardPresenter = new DriversLeaderboardPresenter();
|
||||
private readonly driverStatsPresenter = new DriverStatsPresenter();
|
||||
private readonly completeOnboardingPresenter = new CompleteOnboardingPresenter();
|
||||
private readonly driverRegistrationStatusPresenter = new DriverRegistrationStatusPresenter();
|
||||
private readonly driverPresenter = new DriverPresenter();
|
||||
private readonly driverProfilePresenter = new DriverProfilePresenter();
|
||||
|
||||
constructor(
|
||||
@Inject(GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN)
|
||||
private readonly getDriversLeaderboardUseCase: GetDriversLeaderboardUseCase,
|
||||
@@ -66,29 +57,26 @@ export class DriverService {
|
||||
private readonly driverRepository: IDriverRepository, // TODO must be removed from service
|
||||
@Inject(LOGGER_TOKEN)
|
||||
private readonly logger: Logger,
|
||||
// Injected presenters
|
||||
private readonly driversLeaderboardPresenter: DriversLeaderboardPresenter,
|
||||
private readonly driverStatsPresenter: DriverStatsPresenter,
|
||||
private readonly completeOnboardingPresenter: CompleteOnboardingPresenter,
|
||||
private readonly driverRegistrationStatusPresenter: DriverRegistrationStatusPresenter,
|
||||
private readonly driverPresenter: DriverPresenter,
|
||||
private readonly driverProfilePresenter: DriverProfilePresenter,
|
||||
) {}
|
||||
|
||||
async getDriversLeaderboard(): Promise<DriversLeaderboardDTO> {
|
||||
this.logger.debug('[DriverService] Fetching drivers leaderboard.');
|
||||
|
||||
const result = await this.getDriversLeaderboardUseCase.execute({});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.driversLeaderboardPresenter.present(result as Result<any, any>);
|
||||
await this.getDriversLeaderboardUseCase.execute({});
|
||||
return this.driversLeaderboardPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
async getTotalDrivers(): Promise<DriverStatsDTO> {
|
||||
this.logger.debug('[DriverService] Fetching total drivers count.');
|
||||
|
||||
const result = await this.getTotalDriversUseCase.execute({});
|
||||
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
throw new Error(error.details?.message ?? 'Failed to load driver stats');
|
||||
}
|
||||
|
||||
this.driverStatsPresenter.present(result.unwrap());
|
||||
await this.getTotalDriversUseCase.execute({});
|
||||
return this.driverStatsPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
@@ -98,7 +86,7 @@ export class DriverService {
|
||||
): Promise<CompleteOnboardingOutputDTO> {
|
||||
this.logger.debug('Completing onboarding for user:', userId);
|
||||
|
||||
const result = await this.completeDriverOnboardingUseCase.execute({
|
||||
await this.completeDriverOnboardingUseCase.execute({
|
||||
userId,
|
||||
firstName: input.firstName,
|
||||
lastName: input.lastName,
|
||||
@@ -107,7 +95,6 @@ export class DriverService {
|
||||
...(input.bio !== undefined ? { bio: input.bio } : {}),
|
||||
});
|
||||
|
||||
this.completeOnboardingPresenter.present(result);
|
||||
return this.completeOnboardingPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
@@ -116,28 +103,18 @@ export class DriverService {
|
||||
): Promise<DriverRegistrationStatusDTO> {
|
||||
this.logger.debug('Checking driver registration status:', query);
|
||||
|
||||
const result = await this.isDriverRegisteredForRaceUseCase.execute({
|
||||
await this.isDriverRegisteredForRaceUseCase.execute({
|
||||
raceId: query.raceId,
|
||||
driverId: query.driverId,
|
||||
});
|
||||
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
throw new Error(error.details?.message ?? 'Failed to check registration status');
|
||||
}
|
||||
|
||||
this.driverRegistrationStatusPresenter.present(result.unwrap());
|
||||
return this.driverRegistrationStatusPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
async getCurrentDriver(userId: string): Promise<GetDriverOutputDTO | null> {
|
||||
this.logger.debug(`[DriverService] Fetching current driver for userId: ${userId}`);
|
||||
|
||||
const result = Result.ok(await this.driverRepository.findById(userId));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
this.driverPresenter.present(result as Result<Driver | null, any>);
|
||||
|
||||
await this.driverRepository.findById(userId);
|
||||
return this.driverPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
@@ -152,39 +129,21 @@ export class DriverService {
|
||||
if (bio !== undefined) input.bio = bio;
|
||||
if (country !== undefined) input.country = country;
|
||||
|
||||
const result = await this.updateDriverProfileUseCase.execute(input);
|
||||
|
||||
if (result.isErr()) {
|
||||
this.logger.error(`Failed to update driver profile: ${result.unwrapErr().code}`);
|
||||
this.driverPresenter.present(Result.ok(null));
|
||||
return this.driverPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
this.driverPresenter.present(Result.ok(result.unwrap()));
|
||||
await this.updateDriverProfileUseCase.execute(input);
|
||||
return this.driverPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
async getDriver(driverId: string): Promise<GetDriverOutputDTO | null> {
|
||||
this.logger.debug(`[DriverService] Fetching driver for driverId: ${driverId}`);
|
||||
|
||||
const driver = await this.driverRepository.findById(driverId);
|
||||
|
||||
this.driverPresenter.present(Result.ok(driver));
|
||||
|
||||
await this.driverRepository.findById(driverId);
|
||||
return this.driverPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
async getDriverProfile(driverId: string): Promise<GetDriverProfileOutputDTO> {
|
||||
this.logger.debug(`[DriverService] Fetching driver profile for driverId: ${driverId}`);
|
||||
|
||||
const result = await this.getProfileOverviewUseCase.execute({ driverId });
|
||||
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
throw new Error(error.details?.message ?? 'Failed to load driver profile');
|
||||
}
|
||||
|
||||
this.driverProfilePresenter.present(result.unwrap());
|
||||
await this.getProfileOverviewUseCase.execute({ driverId });
|
||||
return this.driverProfilePresenter.getResponseModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import type { IStandingRepository } from '@core/racing/domain/repositories/IStan
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
|
||||
// Import concrete in-memory implementations
|
||||
import type { ILeagueWalletRepository } from "@core/racing/domain/repositories/ILeagueWalletRepository";
|
||||
import type { ITransactionRepository } from "@core/racing/domain/repositories/ITransactionRepository";
|
||||
import { listLeagueScoringPresets } from '@adapters/bootstrap/LeagueScoringPresets';
|
||||
import { getPointsSystems } from '@adapters/bootstrap/PointsSystems';
|
||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||
@@ -63,6 +65,8 @@ import { GetLeagueProtestsPresenter } from './presenters/GetLeagueProtestsPresen
|
||||
import { GetSeasonSponsorshipsPresenter } from './presenters/GetSeasonSponsorshipsPresenter';
|
||||
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
|
||||
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
|
||||
import { GetLeagueWalletPresenter } from './presenters/GetLeagueWalletPresenter';
|
||||
import { WithdrawFromLeagueWalletPresenter } from './presenters/WithdrawFromLeagueWalletPresenter';
|
||||
|
||||
export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository';
|
||||
export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
|
||||
@@ -76,7 +80,7 @@ export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
||||
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||
export const LEAGUE_WALLET_REPOSITORY_TOKEN = 'ILeagueWalletRepository';
|
||||
export const TRANSACTION_REPOSITORY_TOKEN = 'ITransactionRepository';
|
||||
export const LOGGER_TOKEN = 'Logger'; // Already defined in AuthProviders, but good to have here too
|
||||
export const LOGGER_TOKEN = 'Logger';
|
||||
export const GET_LEAGUE_STANDINGS_USE_CASE = 'GetLeagueStandingsUseCase';
|
||||
export const GET_ALL_LEAGUES_WITH_CAPACITY_USE_CASE = 'GetAllLeaguesWithCapacityUseCase';
|
||||
export const GET_LEAGUE_STATS_USE_CASE = 'GetLeagueStatsUseCase';
|
||||
@@ -103,6 +107,30 @@ export const GET_LEAGUE_WALLET_USE_CASE = 'GetLeagueWalletUseCase';
|
||||
export const WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE = 'WithdrawFromLeagueWalletUseCase';
|
||||
export const GET_SEASON_SPONSORSHIPS_USE_CASE = 'GetSeasonSponsorshipsUseCase';
|
||||
|
||||
export const GET_ALL_LEAGUES_WITH_CAPACITY_OUTPUT_PORT_TOKEN = 'GetAllLeaguesWithCapacityOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_STANDINGS_OUTPUT_PORT_TOKEN = 'GetLeagueStandingsOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_PROTESTS_OUTPUT_PORT_TOKEN = 'GetLeagueProtestsOutputPort_TOKEN';
|
||||
export const GET_SEASON_SPONSORSHIPS_OUTPUT_PORT_TOKEN = 'GetSeasonSponsorshipsOutputPort_TOKEN';
|
||||
export const LIST_LEAGUE_SCORING_PRESETS_OUTPUT_PORT_TOKEN = 'ListLeagueScoringPresetsOutputPort_TOKEN';
|
||||
export const APPROVE_LEAGUE_JOIN_REQUEST_OUTPUT_PORT_TOKEN = 'ApproveLeagueJoinRequestOutputPort_TOKEN';
|
||||
export const CREATE_LEAGUE_OUTPUT_PORT_TOKEN = 'CreateLeagueOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_ADMIN_PERMISSIONS_OUTPUT_PORT_TOKEN = 'GetLeagueAdminPermissionsOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_MEMBERSHIPS_OUTPUT_PORT_TOKEN = 'GetLeagueMembershipsOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_OWNER_SUMMARY_OUTPUT_PORT_TOKEN = 'GetLeagueOwnerSummaryOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_SEASONS_OUTPUT_PORT_TOKEN = 'GetLeagueSeasonsOutputPort_TOKEN';
|
||||
export const JOIN_LEAGUE_OUTPUT_PORT_TOKEN = 'JoinLeagueOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_SCHEDULE_OUTPUT_PORT_TOKEN = 'GetLeagueScheduleOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_STATS_OUTPUT_PORT_TOKEN = 'GetLeagueStatsOutputPort_TOKEN';
|
||||
export const REJECT_LEAGUE_JOIN_REQUEST_OUTPUT_PORT_TOKEN = 'RejectLeagueJoinRequestOutputPort_TOKEN';
|
||||
export const REMOVE_LEAGUE_MEMBER_OUTPUT_PORT_TOKEN = 'RemoveLeagueMemberOutputPort_TOKEN';
|
||||
export const TOTAL_LEAGUES_OUTPUT_PORT_TOKEN = 'TotalLeaguesOutputPort_TOKEN';
|
||||
export const TRANSFER_LEAGUE_OWNERSHIP_OUTPUT_PORT_TOKEN = 'TransferLeagueOwnershipOutputPort_TOKEN';
|
||||
export const UPDATE_LEAGUE_MEMBER_ROLE_OUTPUT_PORT_TOKEN = 'UpdateLeagueMemberRoleOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_FULL_CONFIG_OUTPUT_PORT_TOKEN = 'GetLeagueFullConfigOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_SCORING_CONFIG_OUTPUT_PORT_TOKEN = 'GetLeagueScoringConfigOutputPort_TOKEN';
|
||||
export const GET_LEAGUE_WALLET_OUTPUT_PORT_TOKEN = 'GetLeagueWalletOutputPort_TOKEN';
|
||||
export const WITHDRAW_FROM_LEAGUE_WALLET_OUTPUT_PORT_TOKEN = 'WithdrawFromLeagueWalletOutputPort_TOKEN';
|
||||
|
||||
export const LeagueProviders: Provider[] = [
|
||||
LeagueService, // Provide the service itself
|
||||
{
|
||||
@@ -174,6 +202,43 @@ export const LeagueProviders: Provider[] = [
|
||||
provide: LOGGER_TOKEN,
|
||||
useClass: ConsoleLogger,
|
||||
},
|
||||
// Presenters
|
||||
AllLeaguesWithCapacityPresenter,
|
||||
LeagueStandingsPresenter,
|
||||
GetLeagueProtestsPresenter,
|
||||
GetSeasonSponsorshipsPresenter,
|
||||
LeagueScoringPresetsPresenter,
|
||||
GetLeagueWalletPresenter,
|
||||
WithdrawFromLeagueWalletPresenter,
|
||||
// Output ports
|
||||
{
|
||||
provide: GET_ALL_LEAGUES_WITH_CAPACITY_OUTPUT_PORT_TOKEN,
|
||||
useExisting: AllLeaguesWithCapacityPresenter,
|
||||
},
|
||||
{
|
||||
provide: GET_LEAGUE_STANDINGS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: LeagueStandingsPresenter,
|
||||
},
|
||||
{
|
||||
provide: GET_LEAGUE_PROTESTS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: GetLeagueProtestsPresenter,
|
||||
},
|
||||
{
|
||||
provide: GET_SEASON_SPONSORSHIPS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: GetSeasonSponsorshipsPresenter,
|
||||
},
|
||||
{
|
||||
provide: LIST_LEAGUE_SCORING_PRESETS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: LeagueScoringPresetsPresenter,
|
||||
},
|
||||
{
|
||||
provide: GET_LEAGUE_WALLET_OUTPUT_PORT_TOKEN,
|
||||
useExisting: GetLeagueWalletPresenter,
|
||||
},
|
||||
{
|
||||
provide: WITHDRAW_FROM_LEAGUE_WALLET_OUTPUT_PORT_TOKEN,
|
||||
useExisting: WithdrawFromLeagueWalletPresenter,
|
||||
},
|
||||
// Use cases
|
||||
{
|
||||
provide: GetAllLeaguesWithCapacityUseCase,
|
||||
@@ -260,11 +325,24 @@ export const LeagueProviders: Provider[] = [
|
||||
},
|
||||
{
|
||||
provide: GET_LEAGUE_WALLET_USE_CASE,
|
||||
useClass: GetLeagueWalletUseCase,
|
||||
useFactory: (
|
||||
leagueRepo: ILeagueRepository,
|
||||
walletRepo: ILeagueWalletRepository,
|
||||
transactionRepo: ITransactionRepository,
|
||||
presenter: GetLeagueWalletPresenter,
|
||||
) => new GetLeagueWalletUseCase(leagueRepo, walletRepo, transactionRepo, presenter),
|
||||
inject: [LEAGUE_REPOSITORY_TOKEN, LEAGUE_WALLET_REPOSITORY_TOKEN, TRANSACTION_REPOSITORY_TOKEN, 'GetLeagueWalletPresenter'],
|
||||
},
|
||||
{
|
||||
provide: WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE,
|
||||
useClass: WithdrawFromLeagueWalletUseCase,
|
||||
useFactory: (
|
||||
leagueRepo: ILeagueRepository,
|
||||
walletRepo: ILeagueWalletRepository,
|
||||
transactionRepo: ITransactionRepository,
|
||||
logger: Logger,
|
||||
presenter: WithdrawFromLeagueWalletPresenter,
|
||||
) => new WithdrawFromLeagueWalletUseCase(leagueRepo, walletRepo, transactionRepo, logger, presenter),
|
||||
inject: [LEAGUE_REPOSITORY_TOKEN, LEAGUE_WALLET_REPOSITORY_TOKEN, TRANSACTION_REPOSITORY_TOKEN, LOGGER_TOKEN, 'WithdrawFromLeagueWalletPresenter'],
|
||||
},
|
||||
{
|
||||
provide: GET_SEASON_SPONSORSHIPS_USE_CASE,
|
||||
@@ -302,4 +380,4 @@ export const LeagueProviders: Provider[] = [
|
||||
provide: GET_LEAGUE_SCORING_CONFIG_USE_CASE,
|
||||
useClass: GetLeagueScoringConfigUseCase,
|
||||
}
|
||||
];
|
||||
];
|
||||
@@ -1,332 +0,0 @@
|
||||
import { vi, Mocked } from 'vitest';
|
||||
import { LeagueService } from './LeagueService';
|
||||
import { GetAllLeaguesWithCapacityUseCase } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
|
||||
import { GetLeagueStandingsUseCase } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
|
||||
import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
|
||||
import { GetLeagueFullConfigUseCase } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase';
|
||||
import { GetLeagueScoringConfigUseCase } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase';
|
||||
import { ListLeagueScoringPresetsUseCase } from '@core/racing/application/use-cases/ListLeagueScoringPresetsUseCase';
|
||||
import { JoinLeagueUseCase } from '@core/racing/application/use-cases/JoinLeagueUseCase';
|
||||
import { TransferLeagueOwnershipUseCase } from '@core/racing/application/use-cases/TransferLeagueOwnershipUseCase';
|
||||
import { CreateLeagueWithSeasonAndScoringUseCase } from '@core/racing/application/use-cases/CreateLeagueWithSeasonAndScoringUseCase';
|
||||
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
||||
import { GetTotalLeaguesUseCase } from '@core/racing/application/use-cases/GetTotalLeaguesUseCase';
|
||||
import { GetLeagueJoinRequestsUseCase } from '@core/racing/application/use-cases/GetLeagueJoinRequestsUseCase';
|
||||
import { ApproveLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/ApproveLeagueJoinRequestUseCase';
|
||||
import { RejectLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/RejectLeagueJoinRequestUseCase';
|
||||
import { RemoveLeagueMemberUseCase } from '@core/racing/application/use-cases/RemoveLeagueMemberUseCase';
|
||||
import { UpdateLeagueMemberRoleUseCase } from '@core/racing/application/use-cases/UpdateLeagueMemberRoleUseCase';
|
||||
import { GetLeagueOwnerSummaryUseCase } from '@core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase';
|
||||
import { GetLeagueProtestsUseCase } from '@core/racing/application/use-cases/GetLeagueProtestsUseCase';
|
||||
import { GetLeagueSeasonsUseCase } from '@core/racing/application/use-cases/GetLeagueSeasonsUseCase';
|
||||
import { GetLeagueMembershipsUseCase } from '@core/racing/application/use-cases/GetLeagueMembershipsUseCase';
|
||||
import { GetLeagueScheduleUseCase } from '@core/racing/application/use-cases/GetLeagueScheduleUseCase';
|
||||
import { GetLeagueAdminPermissionsUseCase } from '@core/racing/application/use-cases/GetLeagueAdminPermissionsUseCase';
|
||||
import { GetLeagueWalletUseCase } from '@core/racing/application/use-cases/GetLeagueWalletUseCase';
|
||||
import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase';
|
||||
import { GetSeasonSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSeasonSponsorshipsUseCase';
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
|
||||
describe('LeagueService', () => {
|
||||
let service: LeagueService;
|
||||
let mockGetTotalLeaguesUseCase: Mocked<GetTotalLeaguesUseCase>;
|
||||
let mockGetLeagueJoinRequestsUseCase: Mocked<GetLeagueJoinRequestsUseCase>;
|
||||
let mockApproveLeagueJoinRequestUseCase: Mocked<ApproveLeagueJoinRequestUseCase>;
|
||||
let mockGetLeagueFullConfigUseCase: Mocked<GetLeagueFullConfigUseCase>;
|
||||
let mockGetLeagueOwnerSummaryUseCase: Mocked<GetLeagueOwnerSummaryUseCase>;
|
||||
let mockGetLeagueScheduleUseCase: Mocked<GetLeagueScheduleUseCase>;
|
||||
let mockGetSeasonSponsorshipsUseCase: Mocked<GetSeasonSponsorshipsUseCase>;
|
||||
let mockLogger: Mocked<Logger>;
|
||||
|
||||
beforeEach(() => {
|
||||
const createUseCaseMock = <T extends { execute: unknown }>(): Mocked<T> => ({
|
||||
execute: vi.fn(),
|
||||
}) as Mocked<T>;
|
||||
|
||||
mockGetTotalLeaguesUseCase = createUseCaseMock<GetTotalLeaguesUseCase>();
|
||||
mockGetLeagueJoinRequestsUseCase = createUseCaseMock<GetLeagueJoinRequestsUseCase>();
|
||||
mockApproveLeagueJoinRequestUseCase = createUseCaseMock<ApproveLeagueJoinRequestUseCase>();
|
||||
mockGetLeagueFullConfigUseCase = createUseCaseMock<GetLeagueFullConfigUseCase>();
|
||||
mockGetLeagueOwnerSummaryUseCase = createUseCaseMock<GetLeagueOwnerSummaryUseCase>();
|
||||
mockGetLeagueScheduleUseCase = createUseCaseMock<GetLeagueScheduleUseCase>();
|
||||
mockGetSeasonSponsorshipsUseCase = createUseCaseMock<GetSeasonSponsorshipsUseCase>();
|
||||
mockLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
} as unknown as Mocked<Logger>;
|
||||
|
||||
service = new LeagueService(
|
||||
{} as unknown as GetAllLeaguesWithCapacityUseCase,
|
||||
{} as unknown as GetLeagueStandingsUseCase,
|
||||
{} as unknown as GetLeagueStatsUseCase,
|
||||
mockGetLeagueFullConfigUseCase,
|
||||
{} as unknown as GetLeagueScoringConfigUseCase,
|
||||
{} as unknown as ListLeagueScoringPresetsUseCase,
|
||||
{} as unknown as JoinLeagueUseCase,
|
||||
{} as unknown as TransferLeagueOwnershipUseCase,
|
||||
{} as unknown as CreateLeagueWithSeasonAndScoringUseCase,
|
||||
{} as unknown as GetRaceProtestsUseCase,
|
||||
mockGetTotalLeaguesUseCase,
|
||||
mockGetLeagueJoinRequestsUseCase,
|
||||
mockApproveLeagueJoinRequestUseCase,
|
||||
{} as unknown as RejectLeagueJoinRequestUseCase,
|
||||
{} as unknown as RemoveLeagueMemberUseCase,
|
||||
{} as unknown as UpdateLeagueMemberRoleUseCase,
|
||||
mockGetLeagueOwnerSummaryUseCase,
|
||||
{} as unknown as GetLeagueProtestsUseCase,
|
||||
{} as unknown as GetLeagueSeasonsUseCase,
|
||||
{} as unknown as GetLeagueMembershipsUseCase,
|
||||
mockGetLeagueScheduleUseCase,
|
||||
{} as unknown as GetLeagueAdminPermissionsUseCase,
|
||||
{} as unknown as GetLeagueWalletUseCase,
|
||||
{} as unknown as WithdrawFromLeagueWalletUseCase,
|
||||
mockGetSeasonSponsorshipsUseCase,
|
||||
mockLogger,
|
||||
);
|
||||
});
|
||||
|
||||
it('should get total leagues', async () => {
|
||||
mockGetTotalLeaguesUseCase.execute.mockResolvedValue(Result.ok({ totalLeagues: 5 }));
|
||||
|
||||
const result = await service.getTotalLeagues();
|
||||
|
||||
expect(result).toEqual({ totalLeagues: 5 });
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith('[LeagueService] Fetching total leagues count.');
|
||||
});
|
||||
|
||||
it('should get league join requests', async () => {
|
||||
mockGetLeagueJoinRequestsUseCase.execute.mockImplementation(async (_params, presenter) => {
|
||||
presenter.present({
|
||||
joinRequests: [{ id: 'req-1', leagueId: 'league-1', driverId: 'driver-1', requestedAt: new Date(), message: 'msg' }],
|
||||
drivers: [{ id: 'driver-1', name: 'Driver 1' }],
|
||||
});
|
||||
});
|
||||
|
||||
const result = await service.getLeagueJoinRequests('league-1');
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: 'req-1',
|
||||
leagueId: 'league-1',
|
||||
driverId: 'driver-1',
|
||||
requestedAt: expect.any(Date),
|
||||
message: 'msg',
|
||||
driver: { id: 'driver-1', name: 'Driver 1' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should approve league join request', async () => {
|
||||
mockApproveLeagueJoinRequestUseCase.execute.mockImplementation(async (_params, presenter) => {
|
||||
presenter.present({ success: true, message: 'Join request approved.' });
|
||||
});
|
||||
|
||||
const result = await service.approveLeagueJoinRequest({ leagueId: 'league-1', requestId: 'req-1' });
|
||||
|
||||
expect(result).toEqual({ success: true, message: 'Join request approved.' });
|
||||
});
|
||||
|
||||
it('should reject league join request', async () => {
|
||||
const mockRejectUseCase: Mocked<RejectLeagueJoinRequestUseCase> = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
execute: vi.fn() as any,
|
||||
} as unknown as Mocked<RejectLeagueJoinRequestUseCase>;
|
||||
|
||||
service = new LeagueService(
|
||||
{} as unknown as GetAllLeaguesWithCapacityUseCase,
|
||||
{} as unknown as GetLeagueStandingsUseCase,
|
||||
{} as unknown as GetLeagueStatsUseCase,
|
||||
mockGetLeagueFullConfigUseCase,
|
||||
{} as unknown as GetLeagueScoringConfigUseCase,
|
||||
{} as unknown as ListLeagueScoringPresetsUseCase,
|
||||
{} as unknown as JoinLeagueUseCase,
|
||||
{} as unknown as TransferLeagueOwnershipUseCase,
|
||||
{} as unknown as CreateLeagueWithSeasonAndScoringUseCase,
|
||||
{} as unknown as GetRaceProtestsUseCase,
|
||||
mockGetTotalLeaguesUseCase,
|
||||
mockGetLeagueJoinRequestsUseCase,
|
||||
mockApproveLeagueJoinRequestUseCase,
|
||||
mockRejectUseCase,
|
||||
{} as unknown as RemoveLeagueMemberUseCase,
|
||||
{} as unknown as UpdateLeagueMemberRoleUseCase,
|
||||
mockGetLeagueOwnerSummaryUseCase,
|
||||
{} as unknown as GetLeagueProtestsUseCase,
|
||||
{} as unknown as GetLeagueSeasonsUseCase,
|
||||
{} as unknown as GetLeagueMembershipsUseCase,
|
||||
{} as unknown as GetLeagueScheduleUseCase,
|
||||
{} as unknown as GetLeagueAdminPermissionsUseCase,
|
||||
{} as unknown as GetLeagueWalletUseCase,
|
||||
{} as unknown as WithdrawFromLeagueWalletUseCase,
|
||||
mockLogger,
|
||||
);
|
||||
|
||||
mockRejectUseCase.execute.mockImplementation(async (_params, presenter) => {
|
||||
presenter.present({ success: true, message: 'Join request rejected.' });
|
||||
});
|
||||
|
||||
const result = await service.rejectLeagueJoinRequest({ requestId: 'req-1', leagueId: 'league-1' });
|
||||
|
||||
expect(result).toEqual({ success: true, message: 'Join request rejected.' });
|
||||
});
|
||||
|
||||
it('should remove league member', async () => {
|
||||
const mockRemoveUseCase: Mocked<RemoveLeagueMemberUseCase> = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
execute: vi.fn() as any,
|
||||
} as unknown as Mocked<RemoveLeagueMemberUseCase>;
|
||||
|
||||
service = new LeagueService(
|
||||
{} as unknown as GetAllLeaguesWithCapacityUseCase,
|
||||
{} as unknown as GetLeagueStandingsUseCase,
|
||||
{} as unknown as GetLeagueStatsUseCase,
|
||||
mockGetLeagueFullConfigUseCase,
|
||||
{} as unknown as GetLeagueScoringConfigUseCase,
|
||||
{} as unknown as ListLeagueScoringPresetsUseCase,
|
||||
{} as unknown as JoinLeagueUseCase,
|
||||
{} as unknown as TransferLeagueOwnershipUseCase,
|
||||
{} as unknown as CreateLeagueWithSeasonAndScoringUseCase,
|
||||
{} as unknown as GetRaceProtestsUseCase,
|
||||
mockGetTotalLeaguesUseCase,
|
||||
mockGetLeagueJoinRequestsUseCase,
|
||||
mockApproveLeagueJoinRequestUseCase,
|
||||
{} as unknown as RejectLeagueJoinRequestUseCase,
|
||||
mockRemoveUseCase,
|
||||
{} as unknown as UpdateLeagueMemberRoleUseCase,
|
||||
mockGetLeagueOwnerSummaryUseCase,
|
||||
{} as unknown as GetLeagueProtestsUseCase,
|
||||
{} as unknown as GetLeagueSeasonsUseCase,
|
||||
{} as unknown as GetLeagueMembershipsUseCase,
|
||||
{} as unknown as GetLeagueScheduleUseCase,
|
||||
{} as unknown as GetLeagueAdminPermissionsUseCase,
|
||||
{} as unknown as GetLeagueWalletUseCase,
|
||||
{} as unknown as WithdrawFromLeagueWalletUseCase,
|
||||
mockLogger,
|
||||
);
|
||||
|
||||
mockRemoveUseCase.execute.mockImplementation(async (_params, presenter) => {
|
||||
presenter.present({ success: true });
|
||||
});
|
||||
|
||||
const result = await service.removeLeagueMember({ leagueId: 'league-1', performerDriverId: 'performer-1', targetDriverId: 'driver-1' });
|
||||
|
||||
expect(result).toEqual({ success: true });
|
||||
});
|
||||
|
||||
it('should aggregate league admin data via composite use case', async () => {
|
||||
const fullConfig = {
|
||||
league: {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
description: 'Test',
|
||||
ownerId: 'owner-1',
|
||||
settings: { pointsSystem: 'custom' },
|
||||
},
|
||||
} as any;
|
||||
|
||||
mockGetLeagueFullConfigUseCase.execute.mockResolvedValue(Result.ok(fullConfig));
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mockGetLeagueOwnerSummaryUseCase.execute.mockResolvedValue(Result.ok({ summary: null } as any));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const joinRequestsSpy = vi
|
||||
.spyOn(service, 'getLeagueJoinRequests')
|
||||
.mockResolvedValue({ joinRequests: [] } as any);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const protestsSpy = vi
|
||||
.spyOn(service, 'getLeagueProtests')
|
||||
.mockResolvedValue({ protests: [], racesById: {}, driversById: {} } as any);
|
||||
const seasonsSpy = vi
|
||||
.spyOn(service, 'getLeagueSeasons')
|
||||
.mockResolvedValue([]);
|
||||
|
||||
const result = await service.getLeagueAdmin('league-1');
|
||||
|
||||
expect(mockGetLeagueFullConfigUseCase.execute).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
expect(mockGetLeagueOwnerSummaryUseCase.execute).toHaveBeenCalledWith({ ownerId: 'owner-1' });
|
||||
expect(joinRequestsSpy).toHaveBeenCalledWith('league-1');
|
||||
expect(protestsSpy).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
expect(seasonsSpy).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
expect(result.config.form?.leagueId).toBe('league-1');
|
||||
});
|
||||
|
||||
it('should get season sponsorships', async () => {
|
||||
const sponsorship = {
|
||||
id: 's-1',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'League 1',
|
||||
seasonId: 'season-123',
|
||||
seasonName: 'Season 1',
|
||||
tier: 'gold',
|
||||
status: 'active',
|
||||
pricing: {
|
||||
amount: 1000,
|
||||
currency: 'USD',
|
||||
},
|
||||
platformFee: {
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
},
|
||||
netAmount: {
|
||||
amount: 900,
|
||||
currency: 'USD',
|
||||
},
|
||||
metrics: {
|
||||
drivers: 10,
|
||||
races: 5,
|
||||
completedRaces: 3,
|
||||
impressions: 3000,
|
||||
},
|
||||
createdAt: new Date('2024-01-01T00:00:00.000Z'),
|
||||
} as any;
|
||||
|
||||
mockGetSeasonSponsorshipsUseCase.execute.mockResolvedValue(
|
||||
Result.ok({
|
||||
seasonId: 'season-123',
|
||||
sponsorships: [sponsorship],
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await service.getSeasonSponsorships('season-123');
|
||||
|
||||
expect(mockGetSeasonSponsorshipsUseCase.execute).toHaveBeenCalledWith({ seasonId: 'season-123' });
|
||||
expect(result.sponsorships).toHaveLength(1);
|
||||
expect(result.sponsorships[0]).toMatchObject({
|
||||
id: 's-1',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'League 1',
|
||||
seasonId: 'season-123',
|
||||
seasonName: 'Season 1',
|
||||
tier: 'gold',
|
||||
});
|
||||
});
|
||||
|
||||
it('should get races for league', async () => {
|
||||
const scheduledAt = new Date('2024-02-01T12:00:00.000Z');
|
||||
|
||||
mockGetLeagueScheduleUseCase.execute.mockResolvedValue(
|
||||
Result.ok({
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Race 1',
|
||||
scheduledAt,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await service.getRaces('league-123');
|
||||
|
||||
expect(mockGetLeagueScheduleUseCase.execute).toHaveBeenCalledWith({ leagueId: 'league-123' });
|
||||
expect(result.races).toHaveLength(1);
|
||||
expect(result.races[0]).toMatchObject({
|
||||
id: 'race-1',
|
||||
name: 'Race 1',
|
||||
date: scheduledAt.toISOString(),
|
||||
leagueName: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -40,7 +40,7 @@ import type { LeagueScoringConfigViewModel } from './presenters/LeagueScoringCon
|
||||
import type { LeagueScoringPresetsViewModel } from './presenters/LeagueScoringPresetsPresenter';
|
||||
|
||||
// Core imports
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
|
||||
// Use cases
|
||||
import { ApproveLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/ApproveLeagueJoinRequestUseCase';
|
||||
@@ -58,7 +58,6 @@ import { GetLeagueSeasonsUseCase } from '@core/racing/application/use-cases/GetL
|
||||
import { GetLeagueStandingsUseCase } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
|
||||
import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
|
||||
import { GetLeagueWalletUseCase } from '@core/racing/application/use-cases/GetLeagueWalletUseCase';
|
||||
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
||||
import { GetSeasonSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSeasonSponsorshipsUseCase';
|
||||
import { GetTotalLeaguesUseCase } from '@core/racing/application/use-cases/GetTotalLeaguesUseCase';
|
||||
import { JoinLeagueUseCase } from '@core/racing/application/use-cases/JoinLeagueUseCase';
|
||||
@@ -80,10 +79,9 @@ import { GetLeagueProtestsPresenter } from './presenters/GetLeagueProtestsPresen
|
||||
import { GetLeagueSeasonsPresenter } from './presenters/GetLeagueSeasonsPresenter';
|
||||
import { GetSeasonSponsorshipsPresenter } from './presenters/GetSeasonSponsorshipsPresenter';
|
||||
import { JoinLeaguePresenter } from './presenters/JoinLeaguePresenter';
|
||||
import { LeagueAdminPresenter } from './presenters/LeagueAdminPresenter';
|
||||
import { LeagueConfigPresenter } from './presenters/LeagueConfigPresenter';
|
||||
import { LeagueJoinRequestsPresenter } from './presenters/LeagueJoinRequestsPresenter';
|
||||
import { LeagueSchedulePresenter } from './presenters/LeagueSchedulePresenter';
|
||||
import { LeagueSchedulePresenter, LeagueRacesPresenter } from './presenters/LeagueSchedulePresenter';
|
||||
import { LeagueScoringConfigPresenter } from './presenters/LeagueScoringConfigPresenter';
|
||||
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
|
||||
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
|
||||
@@ -93,8 +91,59 @@ import { RemoveLeagueMemberPresenter } from './presenters/RemoveLeagueMemberPres
|
||||
import { TotalLeaguesPresenter } from './presenters/TotalLeaguesPresenter';
|
||||
import { TransferLeagueOwnershipPresenter } from './presenters/TransferLeagueOwnershipPresenter';
|
||||
import { UpdateLeagueMemberRolePresenter } from './presenters/UpdateLeagueMemberRolePresenter';
|
||||
import { GetLeagueWalletPresenter } from './presenters/GetLeagueWalletPresenter';
|
||||
import { WithdrawFromLeagueWalletPresenter } from './presenters/WithdrawFromLeagueWalletPresenter';
|
||||
// Tokens
|
||||
import { LOGGER_TOKEN, GET_ALL_LEAGUES_WITH_CAPACITY_USE_CASE, GET_LEAGUE_STANDINGS_USE_CASE, GET_LEAGUE_STATS_USE_CASE, GET_LEAGUE_FULL_CONFIG_USE_CASE, GET_LEAGUE_SCORING_CONFIG_USE_CASE, LIST_LEAGUE_SCORING_PRESETS_USE_CASE, JOIN_LEAGUE_USE_CASE, TRANSFER_LEAGUE_OWNERSHIP_USE_CASE, CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE, GET_RACE_PROTESTS_USE_CASE, GET_TOTAL_LEAGUES_USE_CASE, GET_LEAGUE_JOIN_REQUESTS_USE_CASE, APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE, REJECT_LEAGUE_JOIN_REQUEST_USE_CASE, REMOVE_LEAGUE_MEMBER_USE_CASE, UPDATE_LEAGUE_MEMBER_ROLE_USE_CASE, GET_LEAGUE_OWNER_SUMMARY_USE_CASE, GET_LEAGUE_PROTESTS_USE_CASE, GET_LEAGUE_SEASONS_USE_CASE, GET_LEAGUE_MEMBERSHIPS_USE_CASE, GET_LEAGUE_SCHEDULE_USE_CASE, GET_LEAGUE_ADMIN_PERMISSIONS_USE_CASE, GET_LEAGUE_WALLET_USE_CASE, WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE, GET_SEASON_SPONSORSHIPS_USE_CASE } from './LeagueProviders';
|
||||
import {
|
||||
LOGGER_TOKEN,
|
||||
GET_ALL_LEAGUES_WITH_CAPACITY_USE_CASE,
|
||||
GET_LEAGUE_STANDINGS_USE_CASE,
|
||||
GET_LEAGUE_STATS_USE_CASE,
|
||||
GET_LEAGUE_FULL_CONFIG_USE_CASE,
|
||||
GET_LEAGUE_SCORING_CONFIG_USE_CASE,
|
||||
LIST_LEAGUE_SCORING_PRESETS_USE_CASE,
|
||||
JOIN_LEAGUE_USE_CASE,
|
||||
TRANSFER_LEAGUE_OWNERSHIP_USE_CASE,
|
||||
CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE,
|
||||
GET_TOTAL_LEAGUES_USE_CASE,
|
||||
GET_LEAGUE_JOIN_REQUESTS_USE_CASE,
|
||||
APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE,
|
||||
REJECT_LEAGUE_JOIN_REQUEST_USE_CASE,
|
||||
REMOVE_LEAGUE_MEMBER_USE_CASE,
|
||||
UPDATE_LEAGUE_MEMBER_ROLE_USE_CASE,
|
||||
GET_LEAGUE_OWNER_SUMMARY_USE_CASE,
|
||||
GET_LEAGUE_PROTESTS_USE_CASE,
|
||||
GET_LEAGUE_SEASONS_USE_CASE,
|
||||
GET_LEAGUE_MEMBERSHIPS_USE_CASE,
|
||||
GET_LEAGUE_SCHEDULE_USE_CASE,
|
||||
GET_LEAGUE_ADMIN_PERMISSIONS_USE_CASE,
|
||||
GET_LEAGUE_WALLET_USE_CASE,
|
||||
WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE,
|
||||
GET_SEASON_SPONSORSHIPS_USE_CASE,
|
||||
GET_ALL_LEAGUES_WITH_CAPACITY_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_STANDINGS_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_PROTESTS_OUTPUT_PORT_TOKEN,
|
||||
GET_SEASON_SPONSORSHIPS_OUTPUT_PORT_TOKEN,
|
||||
LIST_LEAGUE_SCORING_PRESETS_OUTPUT_PORT_TOKEN,
|
||||
APPROVE_LEAGUE_JOIN_REQUEST_OUTPUT_PORT_TOKEN,
|
||||
CREATE_LEAGUE_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_ADMIN_PERMISSIONS_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_MEMBERSHIPS_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_OWNER_SUMMARY_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_SEASONS_OUTPUT_PORT_TOKEN,
|
||||
JOIN_LEAGUE_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_SCHEDULE_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_STATS_OUTPUT_PORT_TOKEN,
|
||||
REJECT_LEAGUE_JOIN_REQUEST_OUTPUT_PORT_TOKEN,
|
||||
REMOVE_LEAGUE_MEMBER_OUTPUT_PORT_TOKEN,
|
||||
TOTAL_LEAGUES_OUTPUT_PORT_TOKEN,
|
||||
TRANSFER_LEAGUE_OWNERSHIP_OUTPUT_PORT_TOKEN,
|
||||
UPDATE_LEAGUE_MEMBER_ROLE_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_FULL_CONFIG_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_SCORING_CONFIG_OUTPUT_PORT_TOKEN,
|
||||
GET_LEAGUE_WALLET_OUTPUT_PORT_TOKEN,
|
||||
WITHDRAW_FROM_LEAGUE_WALLET_OUTPUT_PORT_TOKEN,
|
||||
} from './LeagueProviders';
|
||||
|
||||
@Injectable()
|
||||
export class LeagueService {
|
||||
@@ -108,7 +157,6 @@ export class LeagueService {
|
||||
@Inject(JOIN_LEAGUE_USE_CASE) private readonly joinLeagueUseCase: JoinLeagueUseCase,
|
||||
@Inject(TRANSFER_LEAGUE_OWNERSHIP_USE_CASE) private readonly transferLeagueOwnershipUseCase: TransferLeagueOwnershipUseCase,
|
||||
@Inject(CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE) private readonly createLeagueWithSeasonAndScoringUseCase: CreateLeagueWithSeasonAndScoringUseCase,
|
||||
@Inject(GET_RACE_PROTESTS_USE_CASE) private readonly getRaceProtestsUseCase: GetRaceProtestsUseCase,
|
||||
@Inject(GET_TOTAL_LEAGUES_USE_CASE) private readonly getTotalLeaguesUseCase: GetTotalLeaguesUseCase,
|
||||
@Inject(GET_LEAGUE_JOIN_REQUESTS_USE_CASE) private readonly getLeagueJoinRequestsUseCase: GetLeagueJoinRequestsUseCase,
|
||||
@Inject(APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE) private readonly approveLeagueJoinRequestUseCase: ApproveLeagueJoinRequestUseCase,
|
||||
@@ -125,120 +173,99 @@ export class LeagueService {
|
||||
@Inject(WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE) private readonly withdrawFromLeagueWalletUseCase: WithdrawFromLeagueWalletUseCase,
|
||||
@Inject(GET_SEASON_SPONSORSHIPS_USE_CASE) private readonly getSeasonSponsorshipsUseCase: GetSeasonSponsorshipsUseCase,
|
||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||
// Injected presenters
|
||||
@Inject(GET_ALL_LEAGUES_WITH_CAPACITY_OUTPUT_PORT_TOKEN) private readonly allLeaguesWithCapacityPresenter: AllLeaguesWithCapacityPresenter,
|
||||
@Inject(GET_LEAGUE_STANDINGS_OUTPUT_PORT_TOKEN) private readonly leagueStandingsPresenter: LeagueStandingsPresenter,
|
||||
@Inject(GET_LEAGUE_PROTESTS_OUTPUT_PORT_TOKEN) private readonly leagueProtestsPresenter: GetLeagueProtestsPresenter,
|
||||
@Inject(GET_SEASON_SPONSORSHIPS_OUTPUT_PORT_TOKEN) private readonly seasonSponsorshipsPresenter: GetSeasonSponsorshipsPresenter,
|
||||
@Inject(LIST_LEAGUE_SCORING_PRESETS_OUTPUT_PORT_TOKEN) private readonly leagueScoringPresetsPresenter: LeagueScoringPresetsPresenter,
|
||||
@Inject(APPROVE_LEAGUE_JOIN_REQUEST_OUTPUT_PORT_TOKEN) private readonly approveLeagueJoinRequestPresenter: ApproveLeagueJoinRequestPresenter,
|
||||
@Inject(CREATE_LEAGUE_OUTPUT_PORT_TOKEN) private readonly createLeaguePresenter: CreateLeaguePresenter,
|
||||
@Inject(GET_LEAGUE_ADMIN_PERMISSIONS_OUTPUT_PORT_TOKEN) private readonly getLeagueAdminPermissionsPresenter: GetLeagueAdminPermissionsPresenter,
|
||||
@Inject(GET_LEAGUE_MEMBERSHIPS_OUTPUT_PORT_TOKEN) private readonly getLeagueMembershipsPresenter: GetLeagueMembershipsPresenter,
|
||||
@Inject(GET_LEAGUE_OWNER_SUMMARY_OUTPUT_PORT_TOKEN) private readonly getLeagueOwnerSummaryPresenter: GetLeagueOwnerSummaryPresenter,
|
||||
@Inject(GET_LEAGUE_SEASONS_OUTPUT_PORT_TOKEN) private readonly getLeagueSeasonsPresenter: GetLeagueSeasonsPresenter,
|
||||
@Inject(JOIN_LEAGUE_OUTPUT_PORT_TOKEN) private readonly joinLeaguePresenter: JoinLeaguePresenter,
|
||||
@Inject(GET_LEAGUE_SCHEDULE_OUTPUT_PORT_TOKEN) private readonly leagueSchedulePresenter: LeagueSchedulePresenter,
|
||||
@Inject(GET_LEAGUE_STATS_OUTPUT_PORT_TOKEN) private readonly leagueStatsPresenter: LeagueStatsPresenter,
|
||||
@Inject(REJECT_LEAGUE_JOIN_REQUEST_OUTPUT_PORT_TOKEN) private readonly rejectLeagueJoinRequestPresenter: RejectLeagueJoinRequestPresenter,
|
||||
@Inject(REMOVE_LEAGUE_MEMBER_OUTPUT_PORT_TOKEN) private readonly removeLeagueMemberPresenter: RemoveLeagueMemberPresenter,
|
||||
@Inject(TOTAL_LEAGUES_OUTPUT_PORT_TOKEN) private readonly totalLeaguesPresenter: TotalLeaguesPresenter,
|
||||
@Inject(TRANSFER_LEAGUE_OWNERSHIP_OUTPUT_PORT_TOKEN) private readonly transferLeagueOwnershipPresenter: TransferLeagueOwnershipPresenter,
|
||||
@Inject(UPDATE_LEAGUE_MEMBER_ROLE_OUTPUT_PORT_TOKEN) private readonly updateLeagueMemberRolePresenter: UpdateLeagueMemberRolePresenter,
|
||||
@Inject(GET_LEAGUE_FULL_CONFIG_OUTPUT_PORT_TOKEN) private readonly leagueConfigPresenter: LeagueConfigPresenter,
|
||||
@Inject(GET_LEAGUE_SCORING_CONFIG_OUTPUT_PORT_TOKEN) private readonly leagueScoringConfigPresenter: LeagueScoringConfigPresenter,
|
||||
@Inject(GET_LEAGUE_WALLET_OUTPUT_PORT_TOKEN) private readonly getLeagueWalletPresenter: GetLeagueWalletPresenter,
|
||||
@Inject(WITHDRAW_FROM_LEAGUE_WALLET_OUTPUT_PORT_TOKEN) private readonly withdrawFromLeagueWalletPresenter: WithdrawFromLeagueWalletPresenter,
|
||||
@Inject(GET_LEAGUE_JOIN_REQUESTS_USE_CASE) private readonly leagueJoinRequestsPresenter: LeagueJoinRequestsPresenter,
|
||||
@Inject(GET_LEAGUE_SCHEDULE_USE_CASE) private readonly leagueRacesPresenter: LeagueRacesPresenter,
|
||||
) {}
|
||||
|
||||
async getAllLeaguesWithCapacity(): Promise<AllLeaguesWithCapacityViewModel> {
|
||||
this.logger.debug('[LeagueService] Fetching all leagues with capacity.');
|
||||
|
||||
const result = await this.getAllLeaguesWithCapacityUseCase.execute();
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
return (this.getAllLeaguesWithCapacityUseCase.outputPort as AllLeaguesWithCapacityPresenter).getViewModel();
|
||||
await this.getAllLeaguesWithCapacityUseCase.execute();
|
||||
return this.allLeaguesWithCapacityPresenter.getViewModel();
|
||||
}
|
||||
|
||||
async getTotalLeagues(): Promise<TotalLeaguesDTO> {
|
||||
this.logger.debug('[LeagueService] Fetching total leagues count.');
|
||||
const result = await this.getTotalLeaguesUseCase.execute();
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
const presenter = new TotalLeaguesPresenter();
|
||||
presenter.present(result.unwrap());
|
||||
return presenter.getViewModel()!;
|
||||
await this.getTotalLeaguesUseCase.execute({});
|
||||
return this.totalLeaguesPresenter.getResponseModel()!;
|
||||
}
|
||||
|
||||
async getLeagueJoinRequests(leagueId: string): Promise<LeagueJoinRequestWithDriverDTO[]> {
|
||||
this.logger.debug(`[LeagueService] Fetching join requests for league: ${leagueId}.`);
|
||||
const presenter = new LeagueJoinRequestsPresenter();
|
||||
await this.getLeagueJoinRequestsUseCase.execute({ leagueId }, presenter);
|
||||
return presenter.getViewModel()!.joinRequests;
|
||||
await this.getLeagueJoinRequestsUseCase.execute({ leagueId });
|
||||
return this.leagueJoinRequestsPresenter.getViewModel()!.joinRequests;
|
||||
}
|
||||
|
||||
async approveLeagueJoinRequest(input: ApproveJoinRequestInputDTO): Promise<ApproveLeagueJoinRequestDTO> {
|
||||
this.logger.debug('Approving join request:', input);
|
||||
const result = await this.approveLeagueJoinRequestUseCase.execute({ leagueId: input.leagueId, requestId: input.requestId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
const presenter = new ApproveLeagueJoinRequestPresenter();
|
||||
presenter.present(result.unwrap());
|
||||
return presenter.getViewModel()!;
|
||||
await this.approveLeagueJoinRequestUseCase.execute(input, this.approveLeagueJoinRequestPresenter);
|
||||
return this.approveLeagueJoinRequestPresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async rejectLeagueJoinRequest(input: RejectJoinRequestInputDTO): Promise<RejectJoinRequestOutputDTO> {
|
||||
this.logger.debug('Rejecting join request:', input);
|
||||
const result = await this.rejectLeagueJoinRequestUseCase.execute({ requestId: input.requestId });
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
return {
|
||||
success: false,
|
||||
error: error.code,
|
||||
};
|
||||
}
|
||||
const presenter = new RejectLeagueJoinRequestPresenter();
|
||||
presenter.present(result.unwrap());
|
||||
return presenter.getViewModel()!;
|
||||
await this.rejectLeagueJoinRequestUseCase.execute({
|
||||
leagueId: input.leagueId,
|
||||
adminId: 'admin', // This should come from auth context
|
||||
requestId: input.requestId
|
||||
});
|
||||
return this.rejectLeagueJoinRequestPresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async getLeagueAdminPermissions(query: GetLeagueAdminPermissionsInputDTO): Promise<LeagueAdminPermissionsDTO> {
|
||||
this.logger.debug('Getting league admin permissions', { query });
|
||||
const result = await this.getLeagueAdminPermissionsUseCase.execute({ leagueId: query.leagueId, performerDriverId: query.performerDriverId });
|
||||
// This use case never errors
|
||||
const presenter = new GetLeagueAdminPermissionsPresenter();
|
||||
presenter.present(result.unwrap());
|
||||
return presenter.getViewModel()!;
|
||||
await this.getLeagueAdminPermissionsUseCase.execute(query);
|
||||
return this.getLeagueAdminPermissionsPresenter.getResponseModel()!;
|
||||
}
|
||||
|
||||
async removeLeagueMember(input: RemoveLeagueMemberInputDTO): Promise<RemoveLeagueMemberOutputDTO> {
|
||||
this.logger.debug('Removing league member', { leagueId: input.leagueId, targetDriverId: input.targetDriverId });
|
||||
const result = await this.removeLeagueMemberUseCase.execute({ leagueId: input.leagueId, targetDriverId: input.targetDriverId });
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
return {
|
||||
success: false,
|
||||
error: error.code,
|
||||
};
|
||||
}
|
||||
const presenter = new RemoveLeagueMemberPresenter();
|
||||
presenter.present(result.unwrap());
|
||||
return presenter.getViewModel()!;
|
||||
await this.removeLeagueMemberUseCase.execute(input);
|
||||
return this.removeLeagueMemberPresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async updateLeagueMemberRole(input: UpdateLeagueMemberRoleInputDTO): Promise<UpdateLeagueMemberRoleOutputDTO> {
|
||||
this.logger.debug('Updating league member role', { leagueId: input.leagueId, targetDriverId: input.targetDriverId, newRole: input.newRole });
|
||||
const result = await this.updateLeagueMemberRoleUseCase.execute({ leagueId: input.leagueId, targetDriverId: input.targetDriverId, newRole: input.newRole });
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
return {
|
||||
success: false,
|
||||
error: error.code,
|
||||
};
|
||||
}
|
||||
const presenter = new UpdateLeagueMemberRolePresenter();
|
||||
presenter.present(result.unwrap());
|
||||
return presenter.getViewModel()!;
|
||||
await this.updateLeagueMemberRoleUseCase.execute(input);
|
||||
return this.updateLeagueMemberRolePresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async getLeagueOwnerSummary(query: GetLeagueOwnerSummaryQueryDTO): Promise<LeagueOwnerSummaryDTO> {
|
||||
this.logger.debug('Getting league owner summary:', query);
|
||||
const presenter = new GetLeagueOwnerSummaryPresenter();
|
||||
await this.getLeagueOwnerSummaryUseCase.execute({ leagueId: query.leagueId } as any, presenter);
|
||||
return presenter.getViewModel()!;
|
||||
await this.getLeagueOwnerSummaryUseCase.execute(query);
|
||||
return this.getLeagueOwnerSummaryPresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async getLeagueFullConfig(query: GetLeagueAdminConfigQueryDTO): Promise<LeagueConfigFormModelDTO | null> {
|
||||
this.logger.debug('Getting league full config', { query });
|
||||
|
||||
try {
|
||||
const result = await this.getLeagueFullConfigUseCase.execute({ leagueId: query.leagueId });
|
||||
if (result.isErr()) {
|
||||
this.logger.error('Error getting league full config', new Error(result.unwrapErr().code));
|
||||
return null;
|
||||
}
|
||||
const presenter = new LeagueConfigPresenter();
|
||||
presenter.present(result.unwrap() as any);
|
||||
return presenter.getViewModel();
|
||||
await this.getLeagueFullConfigUseCase.execute(query);
|
||||
return this.leagueConfigPresenter.getViewModel();
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting league full config', error instanceof Error ? error : new Error(String(error)));
|
||||
return null;
|
||||
@@ -247,71 +274,44 @@ export class LeagueService {
|
||||
|
||||
async getLeagueProtests(query: GetLeagueProtestsQueryDTO): Promise<LeagueAdminProtestsDTO> {
|
||||
this.logger.debug('Getting league protests:', query);
|
||||
const result = await this.getLeagueProtestsUseCase.execute({ leagueId: query.leagueId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
return (this.getLeagueProtestsUseCase.outputPort as GetLeagueProtestsPresenter).getResponseModel()!;
|
||||
await this.getLeagueProtestsUseCase.execute(query);
|
||||
return this.leagueProtestsPresenter.getResponseModel()!;
|
||||
}
|
||||
|
||||
async getLeagueSeasons(query: GetLeagueSeasonsQueryDTO): Promise<LeagueSeasonSummaryDTO[]> {
|
||||
this.logger.debug('Getting league seasons:', query);
|
||||
const result = await this.getLeagueSeasonsUseCase.execute({ leagueId: query.leagueId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
return (this.getLeagueSeasonsUseCase.output as GetLeagueSeasonsPresenter).getResponseModel()!;
|
||||
await this.getLeagueSeasonsUseCase.execute(query);
|
||||
return this.getLeagueSeasonsPresenter.getResponseModel()!;
|
||||
}
|
||||
|
||||
async getLeagueMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {
|
||||
this.logger.debug('Getting league memberships', { leagueId });
|
||||
const presenter = new GetLeagueMembershipsPresenter();
|
||||
await this.getLeagueMembershipsUseCase.execute({ leagueId }, presenter);
|
||||
return presenter.getViewModel()!.memberships;
|
||||
await this.getLeagueMembershipsUseCase.execute({ leagueId });
|
||||
return this.getLeagueMembershipsPresenter.getViewModel()!.memberships;
|
||||
}
|
||||
|
||||
async getLeagueStandings(leagueId: string): Promise<LeagueStandingsDTO> {
|
||||
this.logger.debug('Getting league standings', { leagueId });
|
||||
const result = await this.getLeagueStandingsUseCase.execute({ leagueId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
return (this.getLeagueStandingsUseCase.outputPort as LeagueStandingsPresenter).getResponseModel()!;
|
||||
await this.getLeagueStandingsUseCase.execute({ leagueId });
|
||||
return this.leagueStandingsPresenter.getResponseModel()!;
|
||||
}
|
||||
|
||||
async getLeagueSchedule(leagueId: string): Promise<LeagueScheduleDTO> {
|
||||
this.logger.debug('Getting league schedule', { leagueId });
|
||||
|
||||
const [scheduleResult, leagueConfigResult] = await Promise.all([
|
||||
this.getLeagueScheduleUseCase.execute({ leagueId }),
|
||||
this.getLeagueFullConfigUseCase.execute({ leagueId }),
|
||||
]);
|
||||
|
||||
if (scheduleResult.isErr()) {
|
||||
throw new Error(scheduleResult.unwrapErr().code);
|
||||
}
|
||||
|
||||
const leagueName = leagueConfigResult.isOk()
|
||||
? leagueConfigResult.unwrap().league.name.toString()
|
||||
: undefined;
|
||||
|
||||
const presenter = new LeagueSchedulePresenter();
|
||||
presenter.present(scheduleResult.unwrap(), leagueName);
|
||||
return presenter.getViewModel()!;
|
||||
await this.getLeagueScheduleUseCase.execute({ leagueId });
|
||||
return this.leagueSchedulePresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async getLeagueStats(leagueId: string): Promise<LeagueStatsDTO> {
|
||||
this.logger.debug('Getting league stats', { leagueId });
|
||||
const result = await this.getLeagueStatsUseCase.execute({ leagueId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
await this.getLeagueStatsUseCase.execute({ leagueId });
|
||||
return this.leagueStatsPresenter.getResponseModel()!;
|
||||
}
|
||||
|
||||
private async getLeagueAdminComposite(leagueId: string): Promise<LeagueAdminDTO> {
|
||||
this.logger.debug('Fetching composite league admin data', { leagueId });
|
||||
|
||||
async getLeagueAdmin(leagueId: string): Promise<LeagueAdminDTO> {
|
||||
this.logger.debug('Getting league admin data', { leagueId });
|
||||
|
||||
const [fullConfigResult, joinRequests, protests, seasons] = await Promise.all([
|
||||
this.getLeagueFullConfigUseCase.execute({ leagueId }),
|
||||
this.getLeagueJoinRequests(leagueId),
|
||||
@@ -323,37 +323,19 @@ export class LeagueService {
|
||||
throw new Error(fullConfigResult.unwrapErr().code);
|
||||
}
|
||||
|
||||
const fullConfig = fullConfigResult.unwrap();
|
||||
const league = fullConfig.league;
|
||||
await this.getLeagueOwnerSummaryUseCase.execute({ leagueId });
|
||||
const ownerSummary = this.getLeagueOwnerSummaryPresenter.getViewModel()!;
|
||||
|
||||
const ownerSummaryResult = await this.getLeagueOwnerSummaryUseCase.execute({ ownerId: league.ownerId.toString() });
|
||||
if (ownerSummaryResult.isErr()) {
|
||||
throw new Error(ownerSummaryResult.unwrapErr().code);
|
||||
}
|
||||
const configForm = this.leagueConfigPresenter.getViewModel();
|
||||
|
||||
const ownerSummaryPresenter = new GetLeagueOwnerSummaryPresenter();
|
||||
ownerSummaryPresenter.present(ownerSummaryResult.unwrap());
|
||||
const ownerSummary = ownerSummaryPresenter.getViewModel()!;
|
||||
|
||||
const configPresenter = new LeagueConfigPresenter();
|
||||
configPresenter.present(fullConfig);
|
||||
const configForm = configPresenter.getViewModel();
|
||||
|
||||
const adminPresenter = new LeagueAdminPresenter();
|
||||
adminPresenter.present({
|
||||
// For now, return a simple structure since we don't have a LeagueAdminPresenter
|
||||
return {
|
||||
joinRequests: joinRequests,
|
||||
ownerSummary,
|
||||
config: configForm,
|
||||
config: { form: configForm },
|
||||
protests,
|
||||
seasons,
|
||||
});
|
||||
|
||||
return adminPresenter.getViewModel();
|
||||
}
|
||||
|
||||
async getLeagueAdmin(leagueId: string): Promise<LeagueAdminDTO> {
|
||||
this.logger.debug('Getting league admin data', { leagueId });
|
||||
return this.getLeagueAdminComposite(leagueId);
|
||||
};
|
||||
}
|
||||
|
||||
async createLeague(input: CreateLeagueInputDTO): Promise<CreateLeagueViewModel> {
|
||||
@@ -370,10 +352,7 @@ export class LeagueService {
|
||||
enableNationsChampionship: false,
|
||||
enableTrophyChampionship: false,
|
||||
};
|
||||
const result = await this.createLeagueWithSeasonAndScoringUseCase.execute(command);
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
await this.createLeagueWithSeasonAndScoringUseCase.execute(command);
|
||||
return this.createLeaguePresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
@@ -381,11 +360,7 @@ export class LeagueService {
|
||||
this.logger.debug('Getting league scoring config', { leagueId });
|
||||
|
||||
try {
|
||||
const result = await this.getLeagueScoringConfigUseCase.execute({ leagueId });
|
||||
if (result.isErr()) {
|
||||
this.logger.error('Error getting league scoring config', new Error(result.unwrapErr().code));
|
||||
return null;
|
||||
}
|
||||
await this.getLeagueScoringConfigUseCase.execute({ leagueId });
|
||||
return this.leagueScoringConfigPresenter.getViewModel();
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting league scoring config', error instanceof Error ? error : new Error(String(error)));
|
||||
@@ -396,61 +371,35 @@ export class LeagueService {
|
||||
async listLeagueScoringPresets(): Promise<LeagueScoringPresetsViewModel> {
|
||||
this.logger.debug('Listing league scoring presets');
|
||||
|
||||
const result = await this.listLeagueScoringPresetsUseCase.execute({});
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
|
||||
await this.listLeagueScoringPresetsUseCase.execute({});
|
||||
return this.leagueScoringPresetsPresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async joinLeague(leagueId: string, driverId: string): Promise<JoinLeagueOutputDTO> {
|
||||
this.logger.debug('Joining league', { leagueId, driverId });
|
||||
|
||||
const result = await this.joinLeagueUseCase.execute({ leagueId, driverId });
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
return {
|
||||
success: false,
|
||||
error: error.code,
|
||||
};
|
||||
}
|
||||
await this.joinLeagueUseCase.execute({ leagueId, driverId });
|
||||
return this.joinLeaguePresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async transferLeagueOwnership(leagueId: string, currentOwnerId: string, newOwnerId: string): Promise<TransferLeagueOwnershipOutputDTO> {
|
||||
this.logger.debug('Transferring league ownership', { leagueId, currentOwnerId, newOwnerId });
|
||||
|
||||
const result = await this.transferLeagueOwnershipUseCase.execute({ leagueId, currentOwnerId, newOwnerId });
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
return {
|
||||
success: false,
|
||||
error: error.code,
|
||||
};
|
||||
}
|
||||
await this.transferLeagueOwnershipUseCase.execute({ leagueId, currentOwnerId, newOwnerId });
|
||||
return this.transferLeagueOwnershipPresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
async getSeasonSponsorships(seasonId: string): Promise<GetSeasonSponsorshipsOutputDTO> {
|
||||
this.logger.debug('Getting season sponsorships', { seasonId });
|
||||
|
||||
const result = await this.getSeasonSponsorshipsUseCase.execute({ seasonId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
|
||||
return this.getSeasonSponsorshipsPresenter.getViewModel()!;
|
||||
await this.getSeasonSponsorshipsUseCase.execute({ seasonId });
|
||||
return this.seasonSponsorshipsPresenter.getViewModel()!;
|
||||
}
|
||||
|
||||
|
||||
async getRaces(leagueId: string): Promise<GetLeagueRacesOutputDTO> {
|
||||
this.logger.debug('Getting league races', { leagueId });
|
||||
|
||||
const result = await this.getLeagueScheduleUseCase.execute({ leagueId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
|
||||
await this.getLeagueScheduleUseCase.execute({ leagueId });
|
||||
return {
|
||||
races: this.leagueRacesPresenter.getViewModel()!,
|
||||
};
|
||||
@@ -458,26 +407,19 @@ export class LeagueService {
|
||||
|
||||
async getLeagueWallet(leagueId: string): Promise<GetLeagueWalletOutputDTO> {
|
||||
this.logger.debug('Getting league wallet', { leagueId });
|
||||
const result = await this.getLeagueWalletUseCase.execute({ leagueId });
|
||||
if (result.isErr()) {
|
||||
throw new Error(result.unwrapErr().code);
|
||||
}
|
||||
return result.unwrap() as GetLeagueWalletOutputDTO;
|
||||
await this.getLeagueWalletUseCase.execute({ leagueId });
|
||||
return this.getLeagueWalletPresenter.getResponseModel();
|
||||
}
|
||||
|
||||
async withdrawFromLeagueWallet(leagueId: string, input: WithdrawFromLeagueWalletInputDTO): Promise<WithdrawFromLeagueWalletOutputDTO> {
|
||||
this.logger.debug('Withdrawing from league wallet', { leagueId, amount: input.amount });
|
||||
const result = await this.withdrawFromLeagueWalletUseCase.execute({
|
||||
await this.withdrawFromLeagueWalletUseCase.execute({
|
||||
leagueId,
|
||||
requestedById: "admin",
|
||||
amount: input.amount,
|
||||
currency: input.currency as 'USD' | 'EUR' | 'GBP',
|
||||
seasonId: input.seasonId,
|
||||
destinationAccount: input.destinationAccount,
|
||||
reason: input.destinationAccount,
|
||||
});
|
||||
if (result.isErr()) {
|
||||
const error = result.unwrapErr();
|
||||
return { success: false, message: error.code };
|
||||
}
|
||||
return result.unwrap() as WithdrawFromLeagueWalletOutputDTO;
|
||||
return this.withdrawFromLeagueWalletPresenter.getResponseModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { GetLeagueWalletResult } from '@core/racing/application/use-cases/GetLeagueWalletUseCase';
|
||||
import { GetLeagueWalletOutputDTO, WalletTransactionDTO } from '../dtos/GetLeagueWalletOutputDTO';
|
||||
|
||||
export class GetLeagueWalletPresenter implements UseCaseOutputPort<GetLeagueWalletResult> {
|
||||
private model: GetLeagueWalletOutputDTO | null = null;
|
||||
|
||||
present(result: GetLeagueWalletResult): void {
|
||||
const transactions: WalletTransactionDTO[] = result.transactions.map(tx => ({
|
||||
id: tx.id.toString(),
|
||||
type: tx.type as 'sponsorship' | 'membership' | 'withdrawal' | 'prize',
|
||||
description: tx.description ?? '',
|
||||
amount: tx.amount.amount,
|
||||
fee: tx.platformFee.amount,
|
||||
netAmount: tx.netAmount.amount,
|
||||
date: tx.createdAt.toISOString(),
|
||||
status: tx.status === 'cancelled' ? 'failed' : tx.status,
|
||||
}));
|
||||
|
||||
this.model = {
|
||||
balance: result.aggregates.balance.amount,
|
||||
currency: result.aggregates.balance.currency,
|
||||
totalRevenue: result.aggregates.totalRevenue.amount,
|
||||
totalFees: result.aggregates.totalFees.amount,
|
||||
totalWithdrawals: result.aggregates.totalWithdrawals.amount,
|
||||
pendingPayouts: result.aggregates.pendingPayouts.amount,
|
||||
canWithdraw: result.wallet.canWithdraw(result.aggregates.balance),
|
||||
transactions,
|
||||
};
|
||||
}
|
||||
|
||||
getResponseModel(): GetLeagueWalletOutputDTO {
|
||||
if (!this.model) throw new Error('Presenter not presented');
|
||||
return this.model;
|
||||
}
|
||||
|
||||
get responseModel(): GetLeagueWalletOutputDTO {
|
||||
if (!this.model) throw new Error('Presenter not presented');
|
||||
return this.model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { WithdrawFromLeagueWalletResult } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase';
|
||||
import { WithdrawFromLeagueWalletOutputDTO } from '../dtos/WithdrawFromLeagueWalletOutputDTO';
|
||||
|
||||
export class WithdrawFromLeagueWalletPresenter implements UseCaseOutputPort<WithdrawFromLeagueWalletResult> {
|
||||
private model: WithdrawFromLeagueWalletOutputDTO | null = null;
|
||||
|
||||
present(result: WithdrawFromLeagueWalletResult): void {
|
||||
this.model = {
|
||||
success: true,
|
||||
message: `Successfully withdrew ${result.amount.amount} ${result.amount.currency} from league wallet. Transaction ID: ${result.transactionId}`,
|
||||
};
|
||||
}
|
||||
|
||||
getResponseModel(): WithdrawFromLeagueWalletOutputDTO {
|
||||
if (!this.model) throw new Error('Presenter not presented');
|
||||
return this.model;
|
||||
}
|
||||
|
||||
get responseModel(): WithdrawFromLeagueWalletOutputDTO {
|
||||
if (!this.model) throw new Error('Presenter not presented');
|
||||
return this.model;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user