refactor league module (wip)

This commit is contained in:
2025-12-22 12:57:10 +01:00
parent 9da528d5bd
commit 03dc81b0ba
39 changed files with 546 additions and 405 deletions

View File

@@ -4,8 +4,7 @@
* In-memory implementation of IAnalyticsSnapshotRepository for development/testing. * In-memory implementation of IAnalyticsSnapshotRepository for development/testing.
*/ */
import type { IAnalyticsSnapshotRepository } from '../../domain/repositories/IAnalyticsSnapshotRepository'; import { AnalyticsSnapshot, IAnalyticsSnapshotRepository, SnapshotEntityType, SnapshotPeriod } from '@core/analytics';
import { AnalyticsSnapshot, type SnapshotPeriod, type SnapshotEntityType } from '../../domain/entities/AnalyticsSnapshot';
import { Logger } from '@core/shared/application'; import { Logger } from '@core/shared/application';
export class InMemoryAnalyticsSnapshotRepository implements IAnalyticsSnapshotRepository { export class InMemoryAnalyticsSnapshotRepository implements IAnalyticsSnapshotRepository {

View File

@@ -1,9 +1,15 @@
import type { LogLevel } from '@core/automation/application/ports/LoggerLogLevel'; enum LogLevel {
DEBUG = 'debug',
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
FATAL = 'fatal'
} // TODO move to core
export type LogEnvironment = 'development' | 'production' | 'test'; export type LogEnvironment = 'development' | 'production' | 'test';
export interface LoggingEnvironmentConfig { export interface LoggingEnvironmentConfig {
level: LogLevel; level: LogLevel; // TODO
prettyPrint: boolean; prettyPrint: boolean;
fileOutput: boolean; fileOutput: boolean;
filePath?: string; filePath?: string;
@@ -46,19 +52,19 @@ function getDefaultsForEnvironment(env: LogEnvironment): LoggingEnvironmentConfi
switch (env) { switch (env) {
case 'development': case 'development':
return { return {
level: 'debug', level: LogLevel.DEBUG,
prettyPrint: true, prettyPrint: true,
fileOutput: false, fileOutput: false,
}; };
case 'production': case 'production':
return { return {
level: 'info', level: LogLevel.ERROR,
prettyPrint: false, prettyPrint: false,
fileOutput: true, fileOutput: true,
}; };
case 'test': case 'test':
return { return {
level: 'warn', level: LogLevel.WARN,
prettyPrint: false, prettyPrint: false,
fileOutput: false, fileOutput: false,
}; };

View File

@@ -0,0 +1,35 @@
export const pointsSystems: Record<string, Record<number, number>> = {
'f1-2024': {
1: 25,
2: 18,
3: 15,
4: 12,
5: 10,
6: 8,
7: 6,
8: 4,
9: 2,
10: 1,
},
indycar: {
1: 50,
2: 40,
3: 35,
4: 32,
5: 30,
6: 28,
7: 26,
8: 24,
9: 22,
10: 20,
11: 19,
12: 18,
13: 17,
14: 16,
15: 15,
},
};
export function getPointsSystems(): Record<string, Record<number, number>> {
return { ...pointsSystems };
}

View File

@@ -4,17 +4,11 @@
* In-memory implementation of IAchievementRepository * In-memory implementation of IAchievementRepository
*/ */
import { Logger } from '@core/shared/application'; import { Achievement, AchievementCategory, IAchievementRepository, UserAchievement } from "@core/identity";
import { import { ADMIN_ACHIEVEMENTS, COMMUNITY_ACHIEVEMENTS, DRIVER_ACHIEVEMENTS, STEWARD_ACHIEVEMENTS } from "@core/identity/domain/AchievementConstants";
Achievement, import { Logger } from "@core/shared/application";
AchievementCategory,
DRIVER_ACHIEVEMENTS,
STEWARD_ACHIEVEMENTS,
ADMIN_ACHIEVEMENTS,
COMMUNITY_ACHIEVEMENTS,
} from '../../domain/entities/Achievement';
import { UserAchievement } from '../../domain/entities/UserAchievement';
import type { IAchievementRepository } from '../../domain/repositories/IAchievementRepository';
export class InMemoryAchievementRepository implements IAchievementRepository { export class InMemoryAchievementRepository implements IAchievementRepository {
private achievements: Map<string, Achievement> = new Map(); private achievements: Map<string, Achievement> = new Map();

View File

@@ -1,10 +1,9 @@
import { User } from '@core/identity/domain/entities/User';
import { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository'; import { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import { IUserRepository, StoredUser } from '@core/identity/domain/repositories/IUserRepository'; import { IUserRepository, StoredUser } from '@core/identity/domain/repositories/IUserRepository';
import { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService'; import { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';
import { User } from '@core/identity/domain/entities/User';
import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress'; import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress';
import { randomUUID } from 'crypto';
import { Logger } from '@core/shared/application'; import { Logger } from '@core/shared/application';
export class InMemoryAuthRepository implements IAuthRepository { export class InMemoryAuthRepository implements IAuthRepository {

View File

@@ -4,9 +4,7 @@
* In-memory implementation of ISponsorAccountRepository for development/testing. * In-memory implementation of ISponsorAccountRepository for development/testing.
*/ */
import type { ISponsorAccountRepository } from '../../domain/repositories/ISponsorAccountRepository'; import { ISponsorAccountRepository, SponsorAccount, UserId } from '@core/identity';
import type { SponsorAccount } from '../../domain/entities/SponsorAccount';
import type { UserId } from '../../domain/value-objects/UserId';
import { Logger } from '@core/shared/application'; import { Logger } from '@core/shared/application';
export class InMemorySponsorAccountRepository implements ISponsorAccountRepository { export class InMemorySponsorAccountRepository implements ISponsorAccountRepository {

View File

@@ -4,8 +4,7 @@
* In-memory implementation of IUserRatingRepository * In-memory implementation of IUserRatingRepository
*/ */
import { UserRating } from '../../domain/value-objects/UserRating'; import { IUserRatingRepository, UserRating } from '@core/identity';
import type { IUserRatingRepository } from '../../domain/repositories/IUserRatingRepository';
import { Logger } from '@core/shared/application'; import { Logger } from '@core/shared/application';
export class InMemoryUserRatingRepository implements IUserRatingRepository { export class InMemoryUserRatingRepository implements IUserRatingRepository {

View File

@@ -5,7 +5,6 @@ import { LeagueProviders } from './LeagueProviders';
describe('LeagueController (integration)', () => { describe('LeagueController (integration)', () => {
let controller: LeagueController; let controller: LeagueController;
let service: LeagueService;
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
@@ -14,7 +13,6 @@ describe('LeagueController (integration)', () => {
}).compile(); }).compile();
controller = module.get<LeagueController>(LeagueController); controller = module.get<LeagueController>(LeagueController);
service = module.get<LeagueService>(LeagueService);
}); });
it('should get total leagues', async () => { it('should get total leagues', async () => {
@@ -37,7 +35,7 @@ describe('LeagueController (integration)', () => {
expect(Array.isArray(result.standings)).toBe(true); expect(Array.isArray(result.standings)).toBe(true);
} catch (error) { } catch (error) {
// Expected for non-existent league // Expected for non-existent league
expect(error.message).toContain('not found'); expect(error).toBeInstanceOf(Error);
} }
}); });
}); });

View File

@@ -30,6 +30,7 @@ import { GetLeagueAdminConfigQueryDTO } from './dtos/GetLeagueAdminConfigQueryDT
import { GetLeagueProtestsQueryDTO } from './dtos/GetLeagueProtestsQueryDTO'; import { GetLeagueProtestsQueryDTO } from './dtos/GetLeagueProtestsQueryDTO';
import { GetLeagueSeasonsQueryDTO } from './dtos/GetLeagueSeasonsQueryDTO'; import { GetLeagueSeasonsQueryDTO } from './dtos/GetLeagueSeasonsQueryDTO';
import { GetLeagueWalletOutputDTO } from './dtos/GetLeagueWalletOutputDTO'; import { GetLeagueWalletOutputDTO } from './dtos/GetLeagueWalletOutputDTO';
import { TotalLeaguesDTO } from './dtos/TotalLeaguesDTO';
import { WithdrawFromLeagueWalletInputDTO } from './dtos/WithdrawFromLeagueWalletInputDTO'; import { WithdrawFromLeagueWalletInputDTO } from './dtos/WithdrawFromLeagueWalletInputDTO';
import { WithdrawFromLeagueWalletOutputDTO } from './dtos/WithdrawFromLeagueWalletOutputDTO'; import { WithdrawFromLeagueWalletOutputDTO } from './dtos/WithdrawFromLeagueWalletOutputDTO';
@@ -47,8 +48,8 @@ export class LeagueController {
@Get('total-leagues') @Get('total-leagues')
@ApiOperation({ summary: 'Get the total number of leagues' }) @ApiOperation({ summary: 'Get the total number of leagues' })
@ApiResponse({ status: 200, description: 'Total number of leagues', type: LeagueStatsDTO }) @ApiResponse({ status: 200, description: 'Total number of leagues', type: TotalLeaguesDTO })
async getTotalLeagues(): Promise<LeagueStatsDTO> { async getTotalLeagues(): Promise<TotalLeaguesDTO> {
return this.leagueService.getTotalLeagues(); return this.leagueService.getTotalLeagues();
} }

View File

@@ -2,61 +2,68 @@ import { Provider } from '@nestjs/common';
import { LeagueService } from './LeagueService'; import { LeagueService } from './LeagueService';
// Import core interfaces // Import core interfaces
import type { Logger } from '@core/shared/application/Logger';
import type { ISeasonSponsorshipRepository } from '@core/racing/domain/repositories/ISeasonSponsorshipRepository';
import type { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository';
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository'; import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import type { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository';
import type { ISeasonSponsorshipRepository } from '@core/racing/domain/repositories/ISeasonSponsorshipRepository';
import type { IStandingRepository } from '@core/racing/domain/repositories/IStandingRepository';
import type { Logger } from '@core/shared/application/Logger';
// Import concrete in-memory implementations // Import concrete in-memory implementations
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository'; import { listLeagueScoringPresets } from '@adapters/bootstrap/LeagueScoringPresets';
import { InMemoryLeagueMembershipRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository'; import { getPointsSystems } from '@adapters/bootstrap/PointsSystems';
import { InMemoryLeagueStandingsRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueStandingsRepository'; import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
import { InMemorySeasonRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonRepository'; import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/InMemoryDriverRepository';
import { InMemorySeasonSponsorshipRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository';
import { InMemoryLeagueScoringConfigRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueScoringConfigRepository';
import { InMemoryGameRepository } from '@adapters/racing/persistence/inmemory/InMemoryGameRepository'; import { InMemoryGameRepository } from '@adapters/racing/persistence/inmemory/InMemoryGameRepository';
import { InMemoryLeagueMembershipRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
import { InMemoryLeagueScoringConfigRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueScoringConfigRepository';
import { InMemoryLeagueStandingsRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueStandingsRepository';
import { InMemoryLeagueWalletRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueWalletRepository';
import { InMemoryProtestRepository } from '@adapters/racing/persistence/inmemory/InMemoryProtestRepository'; import { InMemoryProtestRepository } from '@adapters/racing/persistence/inmemory/InMemoryProtestRepository';
import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository'; import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/InMemoryDriverRepository'; import { InMemorySeasonRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonRepository';
import { InMemorySeasonSponsorshipRepository } from '@adapters/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository';
import { InMemoryStandingRepository } from '@adapters/racing/persistence/inmemory/InMemoryStandingRepository'; import { InMemoryStandingRepository } from '@adapters/racing/persistence/inmemory/InMemoryStandingRepository';
import { InMemoryLeagueWalletRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueWalletRepository';
import { InMemoryTransactionRepository } from '@adapters/racing/persistence/inmemory/InMemoryTransactionRepository'; import { InMemoryTransactionRepository } from '@adapters/racing/persistence/inmemory/InMemoryTransactionRepository';
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
import { listLeagueScoringPresets } from '@adapters/bootstrap/LeagueScoringPresets';
// Import use cases // Import use cases
import { GetAllLeaguesWithCapacityUseCase } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
import { GetLeagueStandingsUseCase } from '@core/league/application/use-cases/GetLeagueStandingsUseCase';
import { GetLeagueStandingsUseCaseImpl } from '@core/league/application/use-cases/GetLeagueStandingsUseCaseImpl';
import { GetLeagueFullConfigUseCase } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase';
import { GetSeasonSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSeasonSponsorshipsUseCase';
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 { ApproveLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/ApproveLeagueJoinRequestUseCase';
import { RejectLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/RejectLeagueJoinRequestUseCase'; import { CreateLeagueWithSeasonAndScoringUseCase } from '@core/racing/application/use-cases/CreateLeagueWithSeasonAndScoringUseCase';
import { RemoveLeagueMemberUseCase } from '@core/racing/application/use-cases/RemoveLeagueMemberUseCase'; import { GetAllLeaguesWithCapacityUseCase } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
import { UpdateLeagueMemberRoleUseCase } from '@core/racing/application/use-cases/UpdateLeagueMemberRoleUseCase'; import { GetLeagueAdminPermissionsUseCase } from '@core/racing/application/use-cases/GetLeagueAdminPermissionsUseCase';
import { GetLeagueFullConfigUseCase } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase';
import { GetLeagueJoinRequestsUseCase } from '@core/racing/application/use-cases/GetLeagueJoinRequestsUseCase';
import { GetLeagueMembershipsUseCase } from '@core/racing/application/use-cases/GetLeagueMembershipsUseCase';
import { GetLeagueOwnerSummaryUseCase } from '@core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase'; import { GetLeagueOwnerSummaryUseCase } from '@core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase';
import { GetLeagueProtestsUseCase } from '@core/racing/application/use-cases/GetLeagueProtestsUseCase'; 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 { GetLeagueScheduleUseCase } from '@core/racing/application/use-cases/GetLeagueScheduleUseCase';
import { GetLeagueScoringConfigUseCase } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase';
import { GetLeagueSeasonsUseCase } from '@core/racing/application/use-cases/GetLeagueSeasonsUseCase';
import { GetLeagueStandingsUseCase } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase'; import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
import { GetLeagueAdminPermissionsUseCase } from '@core/racing/application/use-cases/GetLeagueAdminPermissionsUseCase';
import { GetLeagueWalletUseCase } from '@core/racing/application/use-cases/GetLeagueWalletUseCase'; 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';
import { ListLeagueScoringPresetsUseCase } from '@core/racing/application/use-cases/ListLeagueScoringPresetsUseCase';
import { RejectLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/RejectLeagueJoinRequestUseCase';
import { RemoveLeagueMemberUseCase } from '@core/racing/application/use-cases/RemoveLeagueMemberUseCase';
import { TransferLeagueOwnershipUseCase } from '@core/racing/application/use-cases/TransferLeagueOwnershipUseCase';
import { UpdateLeagueMemberRoleUseCase } from '@core/racing/application/use-cases/UpdateLeagueMemberRoleUseCase';
import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase'; import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase';
// Import presenters // Import presenters
import { AllLeaguesWithCapacityPresenter } from './presenters/AllLeaguesWithCapacityPresenter'; import { AllLeaguesWithCapacityPresenter } from './presenters/AllLeaguesWithCapacityPresenter';
import { GetLeagueProtestsPresenter } from './presenters/GetLeagueProtestsPresenter'; import { GetLeagueProtestsPresenter } from './presenters/GetLeagueProtestsPresenter';
import { GetSeasonSponsorshipsPresenter } from './presenters/GetSeasonSponsorshipsPresenter';
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
// Define injection tokens
export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository'; export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository';
export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository'; export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
export const LEAGUE_STANDINGS_REPOSITORY_TOKEN = 'ILeagueStandingsRepository'; export const LEAGUE_STANDINGS_REPOSITORY_TOKEN = 'ILeagueStandingsRepository';
@@ -71,6 +78,30 @@ export const LEAGUE_WALLET_REPOSITORY_TOKEN = 'ILeagueWalletRepository';
export const TRANSACTION_REPOSITORY_TOKEN = 'ITransactionRepository'; 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'; // Already defined in AuthProviders, but good to have here too
export const GET_LEAGUE_STANDINGS_USE_CASE = 'GetLeagueStandingsUseCase'; 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';
export const GET_LEAGUE_FULL_CONFIG_USE_CASE = 'GetLeagueFullConfigUseCase';
export const GET_LEAGUE_SCORING_CONFIG_USE_CASE = 'GetLeagueScoringConfigUseCase';
export const LIST_LEAGUE_SCORING_PRESETS_USE_CASE = 'ListLeagueScoringPresetsUseCase';
export const JOIN_LEAGUE_USE_CASE = 'JoinLeagueUseCase';
export const TRANSFER_LEAGUE_OWNERSHIP_USE_CASE = 'TransferLeagueOwnershipUseCase';
export const CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE = 'CreateLeagueWithSeasonAndScoringUseCase';
export const GET_RACE_PROTESTS_USE_CASE = 'GetRaceProtestsUseCase';
export const GET_TOTAL_LEAGUES_USE_CASE = 'GetTotalLeaguesUseCase';
export const GET_LEAGUE_JOIN_REQUESTS_USE_CASE = 'GetLeagueJoinRequestsUseCase';
export const APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE = 'ApproveLeagueJoinRequestUseCase';
export const REJECT_LEAGUE_JOIN_REQUEST_USE_CASE = 'RejectLeagueJoinRequestUseCase';
export const REMOVE_LEAGUE_MEMBER_USE_CASE = 'RemoveLeagueMemberUseCase';
export const UPDATE_LEAGUE_MEMBER_ROLE_USE_CASE = 'UpdateLeagueMemberRoleUseCase';
export const GET_LEAGUE_OWNER_SUMMARY_USE_CASE = 'GetLeagueOwnerSummaryUseCase';
export const GET_LEAGUE_PROTESTS_USE_CASE = 'GetLeagueProtestsUseCase';
export const GET_LEAGUE_SEASONS_USE_CASE = 'GetLeagueSeasonsUseCase';
export const GET_LEAGUE_MEMBERSHIPS_USE_CASE = 'GetLeagueMembershipsUseCase';
export const GET_LEAGUE_SCHEDULE_USE_CASE = 'GetLeagueScheduleUseCase';
export const GET_LEAGUE_ADMIN_PERMISSIONS_USE_CASE = 'GetLeagueAdminPermissionsUseCase';
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 LeagueProviders: Provider[] = [ export const LeagueProviders: Provider[] = [
LeagueService, // Provide the service itself LeagueService, // Provide the service itself
@@ -91,7 +122,7 @@ export const LeagueProviders: Provider[] = [
}, },
{ {
provide: STANDING_REPOSITORY_TOKEN, provide: STANDING_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryStandingRepository(logger), // Factory for InMemoryStandingRepository useFactory: (logger: Logger) => new InMemoryStandingRepository(logger, getPointsSystems()), // Factory for InMemoryStandingRepository
inject: [LOGGER_TOKEN], inject: [LOGGER_TOKEN],
}, },
{ {
@@ -143,15 +174,6 @@ export const LeagueProviders: Provider[] = [
provide: LOGGER_TOKEN, provide: LOGGER_TOKEN,
useClass: ConsoleLogger, useClass: ConsoleLogger,
}, },
// Presenters
{
provide: 'AllLeaguesWithCapacityPresenter',
useClass: AllLeaguesWithCapacityPresenter,
},
{
provide: 'GetLeagueProtestsPresenter',
useClass: GetLeagueProtestsPresenter,
},
// Use cases // Use cases
{ {
provide: GetAllLeaguesWithCapacityUseCase, provide: GetAllLeaguesWithCapacityUseCase,
@@ -161,21 +183,56 @@ export const LeagueProviders: Provider[] = [
}, },
{ {
provide: GET_LEAGUE_STANDINGS_USE_CASE, provide: GET_LEAGUE_STANDINGS_USE_CASE,
useClass: GetLeagueStandingsUseCaseImpl, useFactory: (standingRepo: IStandingRepository, driverRepo: IDriverRepository, presenter: LeagueStandingsPresenter) =>
new GetLeagueStandingsUseCase(standingRepo, driverRepo, presenter),
inject: [STANDING_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, 'LeagueStandingsPresenter'],
}, },
GetLeagueStatsUseCase,
GetLeagueFullConfigUseCase,
CreateLeagueWithSeasonAndScoringUseCase,
GetRaceProtestsUseCase,
GetTotalLeaguesUseCase,
GetLeagueJoinRequestsUseCase,
ApproveLeagueJoinRequestUseCase,
RejectLeagueJoinRequestUseCase,
RemoveLeagueMemberUseCase,
UpdateLeagueMemberRoleUseCase,
GetLeagueOwnerSummaryUseCase,
{ {
provide: GetLeagueProtestsUseCase, provide: GET_LEAGUE_STATS_USE_CASE,
useClass: GetLeagueStatsUseCase,
},
{
provide: GET_LEAGUE_FULL_CONFIG_USE_CASE,
useClass: GetLeagueFullConfigUseCase,
},
{
provide: CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE,
useClass: CreateLeagueWithSeasonAndScoringUseCase,
},
{
provide: GET_RACE_PROTESTS_USE_CASE,
useClass: GetRaceProtestsUseCase,
},
{
provide: GET_TOTAL_LEAGUES_USE_CASE,
useClass: GetTotalLeaguesUseCase,
},
{
provide: GET_LEAGUE_JOIN_REQUESTS_USE_CASE,
useClass: GetLeagueJoinRequestsUseCase,
},
{
provide: APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE,
useClass: ApproveLeagueJoinRequestUseCase,
},
{
provide: REJECT_LEAGUE_JOIN_REQUEST_USE_CASE,
useClass: RejectLeagueJoinRequestUseCase,
},
{
provide: REMOVE_LEAGUE_MEMBER_USE_CASE,
useClass: RemoveLeagueMemberUseCase,
},
{
provide: UPDATE_LEAGUE_MEMBER_ROLE_USE_CASE,
useClass: UpdateLeagueMemberRoleUseCase,
},
{
provide: GET_LEAGUE_OWNER_SUMMARY_USE_CASE,
useClass: GetLeagueOwnerSummaryUseCase,
},
{
provide: GET_LEAGUE_PROTESTS_USE_CASE,
useFactory: ( useFactory: (
raceRepo: IRaceRepository, raceRepo: IRaceRepository,
protestRepo: IProtestRepository, protestRepo: IProtestRepository,
@@ -185,32 +242,64 @@ export const LeagueProviders: Provider[] = [
) => new GetLeagueProtestsUseCase(raceRepo, protestRepo, driverRepo, leagueRepo, presenter), ) => new GetLeagueProtestsUseCase(raceRepo, protestRepo, driverRepo, leagueRepo, presenter),
inject: [RACE_REPOSITORY_TOKEN, PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, 'GetLeagueProtestsPresenter'], inject: [RACE_REPOSITORY_TOKEN, PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, 'GetLeagueProtestsPresenter'],
}, },
GetLeagueSeasonsUseCase,
GetLeagueMembershipsUseCase,
GetLeagueScheduleUseCase,
GetLeagueStatsUseCase,
GetLeagueAdminPermissionsUseCase,
GetLeagueWalletUseCase,
WithdrawFromLeagueWalletUseCase,
{ {
provide: GetSeasonSponsorshipsUseCase, provide: GET_LEAGUE_SEASONS_USE_CASE,
useClass: GetLeagueSeasonsUseCase,
},
{
provide: GET_LEAGUE_MEMBERSHIPS_USE_CASE,
useClass: GetLeagueMembershipsUseCase,
},
{
provide: GET_LEAGUE_SCHEDULE_USE_CASE,
useClass: GetLeagueScheduleUseCase,
},
{
provide: GET_LEAGUE_ADMIN_PERMISSIONS_USE_CASE,
useClass: GetLeagueAdminPermissionsUseCase,
},
{
provide: GET_LEAGUE_WALLET_USE_CASE,
useClass: GetLeagueWalletUseCase,
},
{
provide: WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE,
useClass: WithdrawFromLeagueWalletUseCase,
},
{
provide: GET_SEASON_SPONSORSHIPS_USE_CASE,
useFactory: ( useFactory: (
seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository,
seasonRepo: ISeasonRepository, seasonRepo: ISeasonRepository,
leagueRepo: ILeagueRepository, leagueRepo: ILeagueRepository,
leagueMembershipRepo: ILeagueMembershipRepository, leagueMembershipRepo: ILeagueMembershipRepository,
raceRepo: IRaceRepository, raceRepo: IRaceRepository,
) => new GetSeasonSponsorshipsUseCase(seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo), presenter: GetSeasonSponsorshipsPresenter,
) => new GetSeasonSponsorshipsUseCase(seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo, presenter),
inject: [ inject: [
'ISeasonSponsorshipRepository', 'ISeasonSponsorshipRepository',
SEASON_REPOSITORY_TOKEN, SEASON_REPOSITORY_TOKEN,
LEAGUE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN,
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
RACE_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN,
'GetSeasonSponsorshipsPresenter',
], ],
}, },
{ { // TODO wtf is this here? doesn't look like it adhers to our concepts
provide: ListLeagueScoringPresetsUseCase, provide: LIST_LEAGUE_SCORING_PRESETS_USE_CASE,
useFactory: () => new ListLeagueScoringPresetsUseCase(listLeagueScoringPresets()), useFactory: (presenter: LeagueScoringPresetsPresenter) => new ListLeagueScoringPresetsUseCase(listLeagueScoringPresets(), presenter),
inject: ['LeagueScoringPresetsPresenter'],
}, },
{
provide: JOIN_LEAGUE_USE_CASE,
useClass: JoinLeagueUseCase,
},
{
provide: TRANSFER_LEAGUE_OWNERSHIP_USE_CASE,
useClass: TransferLeagueOwnershipUseCase,
},
{
provide: GET_LEAGUE_SCORING_CONFIG_USE_CASE,
useClass: GetLeagueScoringConfigUseCase,
}
]; ];

View File

@@ -1,6 +1,7 @@
import { vi, Mocked } from 'vitest';
import { LeagueService } from './LeagueService'; import { LeagueService } from './LeagueService';
import { GetAllLeaguesWithCapacityUseCase } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase'; import { GetAllLeaguesWithCapacityUseCase } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
import { GetLeagueStandingsUseCase } from '@core/league/application/use-cases/GetLeagueStandingsUseCase'; import { GetLeagueStandingsUseCase } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase'; import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
import { GetLeagueFullConfigUseCase } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase'; import { GetLeagueFullConfigUseCase } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase';
import { GetLeagueScoringConfigUseCase } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase'; import { GetLeagueScoringConfigUseCase } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase';
@@ -29,20 +30,19 @@ import { Result } from '@core/shared/application/Result';
describe('LeagueService', () => { describe('LeagueService', () => {
let service: LeagueService; let service: LeagueService;
let mockGetTotalLeaguesUseCase: jest.Mocked<GetTotalLeaguesUseCase>; let mockGetTotalLeaguesUseCase: Mocked<GetTotalLeaguesUseCase>;
let mockGetLeagueJoinRequestsUseCase: jest.Mocked<GetLeagueJoinRequestsUseCase>; let mockGetLeagueJoinRequestsUseCase: Mocked<GetLeagueJoinRequestsUseCase>;
let mockApproveLeagueJoinRequestUseCase: jest.Mocked<ApproveLeagueJoinRequestUseCase>; let mockApproveLeagueJoinRequestUseCase: Mocked<ApproveLeagueJoinRequestUseCase>;
let mockGetLeagueFullConfigUseCase: jest.Mocked<GetLeagueFullConfigUseCase>; let mockGetLeagueFullConfigUseCase: Mocked<GetLeagueFullConfigUseCase>;
let mockGetLeagueOwnerSummaryUseCase: jest.Mocked<GetLeagueOwnerSummaryUseCase>; let mockGetLeagueOwnerSummaryUseCase: Mocked<GetLeagueOwnerSummaryUseCase>;
let mockGetLeagueScheduleUseCase: jest.Mocked<GetLeagueScheduleUseCase>; let mockGetLeagueScheduleUseCase: Mocked<GetLeagueScheduleUseCase>;
let mockGetSeasonSponsorshipsUseCase: jest.Mocked<GetSeasonSponsorshipsUseCase>; let mockGetSeasonSponsorshipsUseCase: Mocked<GetSeasonSponsorshipsUseCase>;
let mockLogger: jest.Mocked<Logger>; let mockLogger: Mocked<Logger>;
beforeEach(() => { beforeEach(() => {
const createUseCaseMock = <T extends { execute: unknown }>(): jest.Mocked<T> => ({ const createUseCaseMock = <T extends { execute: unknown }>(): Mocked<T> => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any execute: vi.fn(),
execute: jest.fn() as any, }) as Mocked<T>;
}) as jest.Mocked<T>;
mockGetTotalLeaguesUseCase = createUseCaseMock<GetTotalLeaguesUseCase>(); mockGetTotalLeaguesUseCase = createUseCaseMock<GetTotalLeaguesUseCase>();
mockGetLeagueJoinRequestsUseCase = createUseCaseMock<GetLeagueJoinRequestsUseCase>(); mockGetLeagueJoinRequestsUseCase = createUseCaseMock<GetLeagueJoinRequestsUseCase>();
@@ -52,11 +52,11 @@ describe('LeagueService', () => {
mockGetLeagueScheduleUseCase = createUseCaseMock<GetLeagueScheduleUseCase>(); mockGetLeagueScheduleUseCase = createUseCaseMock<GetLeagueScheduleUseCase>();
mockGetSeasonSponsorshipsUseCase = createUseCaseMock<GetSeasonSponsorshipsUseCase>(); mockGetSeasonSponsorshipsUseCase = createUseCaseMock<GetSeasonSponsorshipsUseCase>();
mockLogger = { mockLogger = {
debug: jest.fn(), debug: vi.fn(),
info: jest.fn(), info: vi.fn(),
warn: jest.fn(), warn: vi.fn(),
error: jest.fn(), error: vi.fn(),
} as unknown as jest.Mocked<Logger>; } as unknown as Mocked<Logger>;
service = new LeagueService( service = new LeagueService(
{} as unknown as GetAllLeaguesWithCapacityUseCase, {} as unknown as GetAllLeaguesWithCapacityUseCase,
@@ -130,10 +130,10 @@ describe('LeagueService', () => {
}); });
it('should reject league join request', async () => { it('should reject league join request', async () => {
const mockRejectUseCase: jest.Mocked<RejectLeagueJoinRequestUseCase> = { const mockRejectUseCase: Mocked<RejectLeagueJoinRequestUseCase> = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
execute: jest.fn() as any, execute: vi.fn() as any,
} as unknown as jest.Mocked<RejectLeagueJoinRequestUseCase>; } as unknown as Mocked<RejectLeagueJoinRequestUseCase>;
service = new LeagueService( service = new LeagueService(
{} as unknown as GetAllLeaguesWithCapacityUseCase, {} as unknown as GetAllLeaguesWithCapacityUseCase,
@@ -173,10 +173,10 @@ describe('LeagueService', () => {
}); });
it('should remove league member', async () => { it('should remove league member', async () => {
const mockRemoveUseCase: jest.Mocked<RemoveLeagueMemberUseCase> = { const mockRemoveUseCase: Mocked<RemoveLeagueMemberUseCase> = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
execute: jest.fn() as any, execute: vi.fn() as any,
} as unknown as jest.Mocked<RemoveLeagueMemberUseCase>; } as unknown as Mocked<RemoveLeagueMemberUseCase>;
service = new LeagueService( service = new LeagueService(
{} as unknown as GetAllLeaguesWithCapacityUseCase, {} as unknown as GetAllLeaguesWithCapacityUseCase,
@@ -227,15 +227,18 @@ describe('LeagueService', () => {
} as any; } as any;
mockGetLeagueFullConfigUseCase.execute.mockResolvedValue(Result.ok(fullConfig)); mockGetLeagueFullConfigUseCase.execute.mockResolvedValue(Result.ok(fullConfig));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mockGetLeagueOwnerSummaryUseCase.execute.mockResolvedValue(Result.ok({ summary: null } as any)); mockGetLeagueOwnerSummaryUseCase.execute.mockResolvedValue(Result.ok({ summary: null } as any));
const joinRequestsSpy = jest // eslint-disable-next-line @typescript-eslint/no-explicit-any
const joinRequestsSpy = vi
.spyOn(service, 'getLeagueJoinRequests') .spyOn(service, 'getLeagueJoinRequests')
.mockResolvedValue({ joinRequests: [] } as any); .mockResolvedValue({ joinRequests: [] } as any);
const protestsSpy = jest // eslint-disable-next-line @typescript-eslint/no-explicit-any
const protestsSpy = vi
.spyOn(service, 'getLeagueProtests') .spyOn(service, 'getLeagueProtests')
.mockResolvedValue({ protests: [], racesById: {}, driversById: {} } as any); .mockResolvedValue({ protests: [], racesById: {}, driversById: {} } as any);
const seasonsSpy = jest const seasonsSpy = vi
.spyOn(service, 'getLeagueSeasons') .spyOn(service, 'getLeagueSeasons')
.mockResolvedValue([]); .mockResolvedValue([]);

View File

@@ -70,6 +70,8 @@ import { UpdateLeagueMemberRoleUseCase } from '@core/racing/application/use-case
import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase'; import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase';
// API Presenters // API Presenters
import { AllLeaguesWithCapacityPresenter } from './presenters/AllLeaguesWithCapacityPresenter';
import { ApproveLeagueJoinRequestPresenter } from './presenters/ApproveLeagueJoinRequestPresenter';
import { CreateLeaguePresenter } from './presenters/CreateLeaguePresenter'; import { CreateLeaguePresenter } from './presenters/CreateLeaguePresenter';
import { GetLeagueAdminPermissionsPresenter } from './presenters/GetLeagueAdminPermissionsPresenter'; import { GetLeagueAdminPermissionsPresenter } from './presenters/GetLeagueAdminPermissionsPresenter';
import { GetLeagueMembershipsPresenter } from './presenters/GetLeagueMembershipsPresenter'; import { GetLeagueMembershipsPresenter } from './presenters/GetLeagueMembershipsPresenter';
@@ -81,7 +83,7 @@ import { JoinLeaguePresenter } from './presenters/JoinLeaguePresenter';
import { LeagueAdminPresenter } from './presenters/LeagueAdminPresenter'; import { LeagueAdminPresenter } from './presenters/LeagueAdminPresenter';
import { LeagueConfigPresenter } from './presenters/LeagueConfigPresenter'; import { LeagueConfigPresenter } from './presenters/LeagueConfigPresenter';
import { LeagueJoinRequestsPresenter } from './presenters/LeagueJoinRequestsPresenter'; import { LeagueJoinRequestsPresenter } from './presenters/LeagueJoinRequestsPresenter';
import { LeagueRacesPresenter, LeagueSchedulePresenter } from './presenters/LeagueSchedulePresenter'; import { LeagueSchedulePresenter } from './presenters/LeagueSchedulePresenter';
import { LeagueScoringConfigPresenter } from './presenters/LeagueScoringConfigPresenter'; import { LeagueScoringConfigPresenter } from './presenters/LeagueScoringConfigPresenter';
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter'; import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter'; import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
@@ -92,36 +94,36 @@ import { TotalLeaguesPresenter } from './presenters/TotalLeaguesPresenter';
import { TransferLeagueOwnershipPresenter } from './presenters/TransferLeagueOwnershipPresenter'; import { TransferLeagueOwnershipPresenter } from './presenters/TransferLeagueOwnershipPresenter';
import { UpdateLeagueMemberRolePresenter } from './presenters/UpdateLeagueMemberRolePresenter'; import { UpdateLeagueMemberRolePresenter } from './presenters/UpdateLeagueMemberRolePresenter';
// Tokens // Tokens
import { LOGGER_TOKEN } 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_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';
@Injectable() @Injectable()
export class LeagueService { export class LeagueService {
constructor( constructor(
private readonly getAllLeaguesWithCapacityUseCase: GetAllLeaguesWithCapacityUseCase, @Inject(GET_ALL_LEAGUES_WITH_CAPACITY_USE_CASE) private readonly getAllLeaguesWithCapacityUseCase: GetAllLeaguesWithCapacityUseCase,
private readonly getLeagueStandingsUseCase: GetLeagueStandingsUseCase, @Inject(GET_LEAGUE_STANDINGS_USE_CASE) private readonly getLeagueStandingsUseCase: GetLeagueStandingsUseCase,
private readonly getLeagueStatsUseCase: GetLeagueStatsUseCase, @Inject(GET_LEAGUE_STATS_USE_CASE) private readonly getLeagueStatsUseCase: GetLeagueStatsUseCase,
private readonly getLeagueFullConfigUseCase: GetLeagueFullConfigUseCase, @Inject(GET_LEAGUE_FULL_CONFIG_USE_CASE) private readonly getLeagueFullConfigUseCase: GetLeagueFullConfigUseCase,
private readonly getLeagueScoringConfigUseCase: GetLeagueScoringConfigUseCase, @Inject(GET_LEAGUE_SCORING_CONFIG_USE_CASE) private readonly getLeagueScoringConfigUseCase: GetLeagueScoringConfigUseCase,
private readonly listLeagueScoringPresetsUseCase: ListLeagueScoringPresetsUseCase, @Inject(LIST_LEAGUE_SCORING_PRESETS_USE_CASE) private readonly listLeagueScoringPresetsUseCase: ListLeagueScoringPresetsUseCase,
private readonly joinLeagueUseCase: JoinLeagueUseCase, @Inject(JOIN_LEAGUE_USE_CASE) private readonly joinLeagueUseCase: JoinLeagueUseCase,
private readonly transferLeagueOwnershipUseCase: TransferLeagueOwnershipUseCase, @Inject(TRANSFER_LEAGUE_OWNERSHIP_USE_CASE) private readonly transferLeagueOwnershipUseCase: TransferLeagueOwnershipUseCase,
private readonly createLeagueWithSeasonAndScoringUseCase: CreateLeagueWithSeasonAndScoringUseCase, @Inject(CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE) private readonly createLeagueWithSeasonAndScoringUseCase: CreateLeagueWithSeasonAndScoringUseCase,
private readonly getRaceProtestsUseCase: GetRaceProtestsUseCase, @Inject(GET_RACE_PROTESTS_USE_CASE) private readonly getRaceProtestsUseCase: GetRaceProtestsUseCase,
private readonly getTotalLeaguesUseCase: GetTotalLeaguesUseCase, @Inject(GET_TOTAL_LEAGUES_USE_CASE) private readonly getTotalLeaguesUseCase: GetTotalLeaguesUseCase,
private readonly getLeagueJoinRequestsUseCase: GetLeagueJoinRequestsUseCase, @Inject(GET_LEAGUE_JOIN_REQUESTS_USE_CASE) private readonly getLeagueJoinRequestsUseCase: GetLeagueJoinRequestsUseCase,
private readonly approveLeagueJoinRequestUseCase: ApproveLeagueJoinRequestUseCase, @Inject(APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE) private readonly approveLeagueJoinRequestUseCase: ApproveLeagueJoinRequestUseCase,
private readonly rejectLeagueJoinRequestUseCase: RejectLeagueJoinRequestUseCase, @Inject(REJECT_LEAGUE_JOIN_REQUEST_USE_CASE) private readonly rejectLeagueJoinRequestUseCase: RejectLeagueJoinRequestUseCase,
private readonly removeLeagueMemberUseCase: RemoveLeagueMemberUseCase, @Inject(REMOVE_LEAGUE_MEMBER_USE_CASE) private readonly removeLeagueMemberUseCase: RemoveLeagueMemberUseCase,
private readonly updateLeagueMemberRoleUseCase: UpdateLeagueMemberRoleUseCase, @Inject(UPDATE_LEAGUE_MEMBER_ROLE_USE_CASE) private readonly updateLeagueMemberRoleUseCase: UpdateLeagueMemberRoleUseCase,
private readonly getLeagueOwnerSummaryUseCase: GetLeagueOwnerSummaryUseCase, @Inject(GET_LEAGUE_OWNER_SUMMARY_USE_CASE) private readonly getLeagueOwnerSummaryUseCase: GetLeagueOwnerSummaryUseCase,
private readonly getLeagueProtestsUseCase: GetLeagueProtestsUseCase, @Inject(GET_LEAGUE_PROTESTS_USE_CASE) private readonly getLeagueProtestsUseCase: GetLeagueProtestsUseCase,
private readonly getLeagueSeasonsUseCase: GetLeagueSeasonsUseCase, @Inject(GET_LEAGUE_SEASONS_USE_CASE) private readonly getLeagueSeasonsUseCase: GetLeagueSeasonsUseCase,
private readonly getLeagueMembershipsUseCase: GetLeagueMembershipsUseCase, @Inject(GET_LEAGUE_MEMBERSHIPS_USE_CASE) private readonly getLeagueMembershipsUseCase: GetLeagueMembershipsUseCase,
private readonly getLeagueScheduleUseCase: GetLeagueScheduleUseCase, @Inject(GET_LEAGUE_SCHEDULE_USE_CASE) private readonly getLeagueScheduleUseCase: GetLeagueScheduleUseCase,
private readonly getLeagueAdminPermissionsUseCase: GetLeagueAdminPermissionsUseCase, @Inject(GET_LEAGUE_ADMIN_PERMISSIONS_USE_CASE) private readonly getLeagueAdminPermissionsUseCase: GetLeagueAdminPermissionsUseCase,
private readonly getLeagueWalletUseCase: GetLeagueWalletUseCase, @Inject(GET_LEAGUE_WALLET_USE_CASE) private readonly getLeagueWalletUseCase: GetLeagueWalletUseCase,
private readonly withdrawFromLeagueWalletUseCase: WithdrawFromLeagueWalletUseCase, @Inject(WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE) private readonly withdrawFromLeagueWalletUseCase: WithdrawFromLeagueWalletUseCase,
private readonly getSeasonSponsorshipsUseCase: GetSeasonSponsorshipsUseCase, @Inject(GET_SEASON_SPONSORSHIPS_USE_CASE) private readonly getSeasonSponsorshipsUseCase: GetSeasonSponsorshipsUseCase,
@Inject(LOGGER_TOKEN) private readonly logger: Logger, @Inject(LOGGER_TOKEN) private readonly logger: Logger,
) {} ) {}
@@ -132,7 +134,7 @@ export class LeagueService {
if (result.isErr()) { if (result.isErr()) {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
return this.getAllLeaguesWithCapacityUseCase.outputPort.present(result); // TODO wrong, must use presenter return (this.getAllLeaguesWithCapacityUseCase.outputPort as AllLeaguesWithCapacityPresenter).getViewModel();
} }
async getTotalLeagues(): Promise<TotalLeaguesDTO> { async getTotalLeagues(): Promise<TotalLeaguesDTO> {
@@ -148,12 +150,8 @@ export class LeagueService {
async getLeagueJoinRequests(leagueId: string): Promise<LeagueJoinRequestWithDriverDTO[]> { async getLeagueJoinRequests(leagueId: string): Promise<LeagueJoinRequestWithDriverDTO[]> {
this.logger.debug(`[LeagueService] Fetching join requests for league: ${leagueId}.`); this.logger.debug(`[LeagueService] Fetching join requests for league: ${leagueId}.`);
const result = await this.getLeagueJoinRequestsUseCase.execute({ leagueId });
if (result.isErr()) {
throw new Error(result.unwrapErr().code);
}
const presenter = new LeagueJoinRequestsPresenter(); const presenter = new LeagueJoinRequestsPresenter();
presenter.present(result.unwrap()); await this.getLeagueJoinRequestsUseCase.execute({ leagueId }, presenter);
return presenter.getViewModel()!.joinRequests; return presenter.getViewModel()!.joinRequests;
} }
@@ -224,12 +222,8 @@ export class LeagueService {
async getLeagueOwnerSummary(query: GetLeagueOwnerSummaryQueryDTO): Promise<LeagueOwnerSummaryDTO> { async getLeagueOwnerSummary(query: GetLeagueOwnerSummaryQueryDTO): Promise<LeagueOwnerSummaryDTO> {
this.logger.debug('Getting league owner summary:', query); this.logger.debug('Getting league owner summary:', query);
const result = await this.getLeagueOwnerSummaryUseCase.execute({ ownerId: query.ownerId });
if (result.isErr()) {
throw new Error(result.unwrapErr().code);
}
const presenter = new GetLeagueOwnerSummaryPresenter(); const presenter = new GetLeagueOwnerSummaryPresenter();
presenter.present(result.unwrap()); await this.getLeagueOwnerSummaryUseCase.execute({ leagueId: query.leagueId } as any, presenter);
return presenter.getViewModel()!; return presenter.getViewModel()!;
} }
@@ -243,7 +237,7 @@ export class LeagueService {
return null; return null;
} }
const presenter = new LeagueConfigPresenter(); const presenter = new LeagueConfigPresenter();
presenter.present(result.unwrap()); presenter.present(result.unwrap() as any);
return presenter.getViewModel(); return presenter.getViewModel();
} catch (error) { } catch (error) {
this.logger.error('Error getting league full config', error instanceof Error ? error : new Error(String(error))); this.logger.error('Error getting league full config', error instanceof Error ? error : new Error(String(error)));
@@ -271,12 +265,8 @@ export class LeagueService {
async getLeagueMemberships(leagueId: string): Promise<LeagueMembershipsDTO> { async getLeagueMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {
this.logger.debug('Getting league memberships', { leagueId }); this.logger.debug('Getting league memberships', { leagueId });
const result = await this.getLeagueMembershipsUseCase.execute({ leagueId });
if (result.isErr()) {
throw new Error(result.unwrapErr().code);
}
const presenter = new GetLeagueMembershipsPresenter(); const presenter = new GetLeagueMembershipsPresenter();
presenter.present(result.unwrap()); await this.getLeagueMembershipsUseCase.execute({ leagueId }, presenter);
return presenter.getViewModel()!.memberships; return presenter.getViewModel()!.memberships;
} }
@@ -286,9 +276,7 @@ export class LeagueService {
if (result.isErr()) { if (result.isErr()) {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
const presenter = new LeagueStandingsPresenter(); return (this.getLeagueStandingsUseCase.outputPort as LeagueStandingsPresenter).getResponseModel()!;
presenter.present(result.unwrap());
return presenter.getViewModel()!;
} }
async getLeagueSchedule(leagueId: string): Promise<LeagueScheduleDTO> { async getLeagueSchedule(leagueId: string): Promise<LeagueScheduleDTO> {
@@ -318,9 +306,7 @@ export class LeagueService {
if (result.isErr()) { if (result.isErr()) {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
const presenter = new LeagueStatsPresenter(); return this.leagueStatsPresenter.getResponseModel()!;
presenter.present(result.unwrap());
return presenter.getViewModel()!;
} }
private async getLeagueAdminComposite(leagueId: string): Promise<LeagueAdminDTO> { private async getLeagueAdminComposite(leagueId: string): Promise<LeagueAdminDTO> {
@@ -388,23 +374,19 @@ export class LeagueService {
if (result.isErr()) { if (result.isErr()) {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
const presenter = new CreateLeaguePresenter(); return this.createLeaguePresenter.getViewModel()!;
presenter.present(result.unwrap());
return presenter.getViewModel()!;
} }
async getLeagueScoringConfig(leagueId: string): Promise<LeagueScoringConfigViewModel | null> { async getLeagueScoringConfig(leagueId: string): Promise<LeagueScoringConfigViewModel | null> {
this.logger.debug('Getting league scoring config', { leagueId }); this.logger.debug('Getting league scoring config', { leagueId });
const presenter = new LeagueScoringConfigPresenter();
try { try {
const result = await this.getLeagueScoringConfigUseCase.execute({ leagueId }); const result = await this.getLeagueScoringConfigUseCase.execute({ leagueId });
if (result.isErr()) { if (result.isErr()) {
this.logger.error('Error getting league scoring config', new Error(result.unwrapErr().code)); this.logger.error('Error getting league scoring config', new Error(result.unwrapErr().code));
return null; return null;
} }
presenter.present(result.unwrap()); return this.leagueScoringConfigPresenter.getViewModel();
return presenter.getViewModel();
} catch (error) { } catch (error) {
this.logger.error('Error getting league scoring config', error instanceof Error ? error : new Error(String(error))); this.logger.error('Error getting league scoring config', error instanceof Error ? error : new Error(String(error)));
return null; return null;
@@ -414,14 +396,12 @@ export class LeagueService {
async listLeagueScoringPresets(): Promise<LeagueScoringPresetsViewModel> { async listLeagueScoringPresets(): Promise<LeagueScoringPresetsViewModel> {
this.logger.debug('Listing league scoring presets'); this.logger.debug('Listing league scoring presets');
const result = await this.listLeagueScoringPresetsUseCase.execute(); const result = await this.listLeagueScoringPresetsUseCase.execute({});
if (result.isErr()) { if (result.isErr()) {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
const presenter = new LeagueScoringPresetsPresenter(); return this.leagueScoringPresetsPresenter.getViewModel()!;
await presenter.present(result.unwrap());
return presenter.getViewModel()!;
} }
async joinLeague(leagueId: string, driverId: string): Promise<JoinLeagueOutputDTO> { async joinLeague(leagueId: string, driverId: string): Promise<JoinLeagueOutputDTO> {
@@ -435,9 +415,7 @@ export class LeagueService {
error: error.code, error: error.code,
}; };
} }
const presenter = new JoinLeaguePresenter(); return this.joinLeaguePresenter.getViewModel()!;
presenter.present(result.unwrap());
return presenter.getViewModel()!;
} }
async transferLeagueOwnership(leagueId: string, currentOwnerId: string, newOwnerId: string): Promise<TransferLeagueOwnershipOutputDTO> { async transferLeagueOwnership(leagueId: string, currentOwnerId: string, newOwnerId: string): Promise<TransferLeagueOwnershipOutputDTO> {
@@ -451,9 +429,7 @@ export class LeagueService {
error: error.code, error: error.code,
}; };
} }
const presenter = new TransferLeagueOwnershipPresenter(); return this.transferLeagueOwnershipPresenter.getViewModel()!;
presenter.present(result.unwrap());
return presenter.getViewModel()!;
} }
async getSeasonSponsorships(seasonId: string): Promise<GetSeasonSponsorshipsOutputDTO> { async getSeasonSponsorships(seasonId: string): Promise<GetSeasonSponsorshipsOutputDTO> {
@@ -464,9 +440,7 @@ export class LeagueService {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
const presenter = new GetSeasonSponsorshipsPresenter(); return this.getSeasonSponsorshipsPresenter.getViewModel()!;
presenter.present(result.unwrap());
return presenter.getViewModel()!;
} }
async getRaces(leagueId: string): Promise<GetLeagueRacesOutputDTO> { async getRaces(leagueId: string): Promise<GetLeagueRacesOutputDTO> {
@@ -477,11 +451,8 @@ export class LeagueService {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
const presenter = new LeagueRacesPresenter();
presenter.present(result.unwrap());
return { return {
races: presenter.getViewModel()!, races: this.leagueRacesPresenter.getViewModel()!,
}; };
} }
@@ -491,7 +462,7 @@ export class LeagueService {
if (result.isErr()) { if (result.isErr()) {
throw new Error(result.unwrapErr().code); throw new Error(result.unwrapErr().code);
} }
return result.unwrap(); return result.unwrap() as GetLeagueWalletOutputDTO;
} }
async withdrawFromLeagueWallet(leagueId: string, input: WithdrawFromLeagueWalletInputDTO): Promise<WithdrawFromLeagueWalletOutputDTO> { async withdrawFromLeagueWallet(leagueId: string, input: WithdrawFromLeagueWalletInputDTO): Promise<WithdrawFromLeagueWalletOutputDTO> {
@@ -499,17 +470,14 @@ export class LeagueService {
const result = await this.withdrawFromLeagueWalletUseCase.execute({ const result = await this.withdrawFromLeagueWalletUseCase.execute({
leagueId, leagueId,
amount: input.amount, amount: input.amount,
currency: input.currency, currency: input.currency as 'USD' | 'EUR' | 'GBP',
seasonId: input.seasonId, seasonId: input.seasonId,
destinationAccount: input.destinationAccount, destinationAccount: input.destinationAccount,
}); });
if (result.isErr()) { if (result.isErr()) {
const error = result.unwrapErr(); const error = result.unwrapErr();
if (error.code === 'WITHDRAWAL_NOT_ALLOWED') { return { success: false, message: error.code };
return { success: false, message: error.code };
}
throw new Error(error.code);
} }
return result.unwrap(); return result.unwrap() as WithdrawFromLeagueWalletOutputDTO;
} }
} }

View File

@@ -1,13 +1,12 @@
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import { Result } from '@core/shared/application/Result';
import { AllLeaguesWithCapacityDTO, LeagueWithCapacityDTO } from '../dtos/AllLeaguesWithCapacityDTO'; import { AllLeaguesWithCapacityDTO, LeagueWithCapacityDTO } from '../dtos/AllLeaguesWithCapacityDTO';
import type { GetAllLeaguesWithCapacityResult } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase'; import type { GetAllLeaguesWithCapacityResult } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
export class AllLeaguesWithCapacityPresenter implements UseCaseOutputPort<GetAllLeaguesWithCapacityResult, 'REPOSITORY_ERROR'> { export class AllLeaguesWithCapacityPresenter implements UseCaseOutputPort<GetAllLeaguesWithCapacityResult> {
present(result: Result<GetAllLeaguesWithCapacityResult, ApplicationErrorCode<'REPOSITORY_ERROR'>>): AllLeaguesWithCapacityDTO { private result: AllLeaguesWithCapacityDTO | null = null;
const output = result.unwrap();
const leagues: LeagueWithCapacityDTO[] = output.leagues.map(league => ({ present(result: GetAllLeaguesWithCapacityResult): void {
const leagues: LeagueWithCapacityDTO[] = result.leagues.map(league => ({
id: league.league.id.toString(), id: league.league.id.toString(),
name: league.league.name.toString(), name: league.league.name.toString(),
description: league.league.description?.toString() || '', description: league.league.description?.toString() || '',
@@ -17,9 +16,14 @@ export class AllLeaguesWithCapacityPresenter implements UseCaseOutputPort<GetAll
usedSlots: league.currentDrivers, usedSlots: league.currentDrivers,
socialLinks: league.league.socialLinks || {}, socialLinks: league.league.socialLinks || {},
})); }));
return { this.result = {
leagues, leagues,
totalCount: leagues.length, totalCount: leagues.length,
}; };
} }
getViewModel(): AllLeaguesWithCapacityDTO {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
} }

View File

@@ -1,14 +1,16 @@
import type { ApproveLeagueJoinRequestResultPort } from '@core/racing/application/ports/output/ApproveLeagueJoinRequestResultPort';
import type { UseCaseOutputPort } from '@core/shared/application';
import { ApproveLeagueJoinRequestResult } from '@core/racing/application/use-cases/ApproveLeagueJoinRequestUseCase';
import type { ApproveLeagueJoinRequestDTO } from '../dtos/ApproveLeagueJoinRequestDTO'; import type { ApproveLeagueJoinRequestDTO } from '../dtos/ApproveLeagueJoinRequestDTO';
export class ApproveLeagueJoinRequestPresenter { export class ApproveLeagueJoinRequestPresenter implements UseCaseOutputPort<ApproveLeagueJoinRequestResult> {
private result: ApproveLeagueJoinRequestDTO | null = null; private result: ApproveLeagueJoinRequestDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: ApproveLeagueJoinRequestResultPort) { present(output: ApproveLeagueJoinRequestResult) {
this.result = output; this.result = output;
} }

View File

@@ -1,4 +1,4 @@
import type { CreateLeagueWithSeasonAndScoringOutputPort } from '@core/racing/application/ports/output/CreateLeagueWithSeasonAndScoringOutputPort'; import { CreateLeagueWithSeasonAndScoringResult } from '@core/racing/application/use-cases/CreateLeagueWithSeasonAndScoringUseCase';
import type { CreateLeagueViewModel } from '../dtos/CreateLeagueDTO'; import type { CreateLeagueViewModel } from '../dtos/CreateLeagueDTO';
export class CreateLeaguePresenter { export class CreateLeaguePresenter {
@@ -8,9 +8,9 @@ export class CreateLeaguePresenter {
this.result = null; this.result = null;
} }
present(dto: CreateLeagueWithSeasonAndScoringOutputPort): void { present(dto: CreateLeagueWithSeasonAndScoringResult): void {
this.result = { this.result = {
leagueId: dto.leagueId, leagueId: dto.league.id.toString(),
success: true, success: true,
}; };
} }

View File

@@ -1,22 +1,22 @@
import type { GetLeagueAdminPermissionsOutputPort } from '@core/racing/application/ports/output/GetLeagueAdminPermissionsOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application';
import type { GetLeagueAdminPermissionsResult } from '@core/racing/application/use-cases/GetLeagueAdminPermissionsUseCase';
import { LeagueAdminPermissionsDTO } from '../dtos/LeagueAdminPermissionsDTO'; import { LeagueAdminPermissionsDTO } from '../dtos/LeagueAdminPermissionsDTO';
import type { Presenter } from '@core/shared/presentation';
export class GetLeagueAdminPermissionsPresenter implements Presenter<GetLeagueAdminPermissionsOutputPort, LeagueAdminPermissionsDTO> { export class GetLeagueAdminPermissionsPresenter implements UseCaseOutputPort<GetLeagueAdminPermissionsResult> {
private result: LeagueAdminPermissionsDTO | null = null; private result: LeagueAdminPermissionsDTO | null = null;
reset(): void { reset(): void {
this.result = null; this.result = null;
} }
present(port: GetLeagueAdminPermissionsOutputPort): void { present(port: GetLeagueAdminPermissionsResult): void {
this.result = { this.result = {
canRemoveMember: port.canRemoveMember, canRemoveMember: port.permissions.canManageMembers,
canUpdateRoles: port.canUpdateRoles, canUpdateRoles: port.permissions.canManageMembers,
}; };
} }
getViewModel(): LeagueAdminPermissionsDTO | null { getResponseModel(): LeagueAdminPermissionsDTO | null {
return this.result; return this.result;
} }
} }

View File

@@ -1,28 +1,34 @@
import { GetLeagueMembershipsPresenter } from './GetLeagueMembershipsPresenter'; import { GetLeagueMembershipsPresenter } from './GetLeagueMembershipsPresenter';
import type { GetLeagueMembershipsOutputPort } from '@core/racing/application/ports/output/GetLeagueMembershipsOutputPort'; import type { GetLeagueMembershipsResult } from '@core/racing/application/use-cases/GetLeagueMembershipsUseCase';
describe('GetLeagueMembershipsPresenter', () => { describe('GetLeagueMembershipsPresenter', () => {
it('presents memberships correctly', () => { it('presents memberships correctly', () => {
const presenter = new GetLeagueMembershipsPresenter(); const presenter = new GetLeagueMembershipsPresenter();
const output: GetLeagueMembershipsOutputPort = { const output: GetLeagueMembershipsResult = {
memberships: { // eslint-disable-next-line @typescript-eslint/no-explicit-any
members: [ league: {} as any,
{ memberships: [
{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
membership: {
driverId: 'driver-1', driverId: 'driver-1',
driver: { id: 'driver-1', name: 'John Doe' },
role: 'member', role: 'member',
joinedAt: new Date('2023-01-01'), // eslint-disable-next-line @typescript-eslint/no-explicit-any
}, joinedAt: {} as any,
], // eslint-disable-next-line @typescript-eslint/no-explicit-any
}, } as any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
driver: { id: 'driver-1', name: 'John Doe' } as any,
},
],
}; };
presenter.present(output); presenter.present(output);
const vm = presenter.getViewModel(); const vm = presenter.getViewModel()!;
expect(vm).not.toBeNull(); expect(vm).not.toBeNull();
expect(vm!.memberships.members).toHaveLength(1); expect(vm!.memberships.members).toHaveLength(1);
expect(vm!.memberships.members[0].driverId).toBe('driver-1'); expect(vm!.memberships.members[0]!.driverId).toBe('driver-1');
expect(vm!.memberships.members[0].driver.name).toBe('John Doe'); expect(vm!.memberships.members[0]!.driver.name).toBe('John Doe');
}); });
}); });

View File

@@ -1,24 +1,35 @@
import type { GetLeagueMembershipsOutputPort } from '@core/racing/application/ports/output/GetLeagueMembershipsOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application';
import { LeagueMembershipsDTO, LeagueMemberDTO } from '../dtos/LeagueMembershipsDTO'; import { GetLeagueMembershipsResult } from '@core/racing/application/use-cases/GetLeagueMembershipsUseCase';
import { LeagueMembershipsDTO } from '../dtos/LeagueMembershipsDTO';
import type { LeagueMemberDTO } from '../dtos/LeagueMemberDTO';
export interface GetLeagueMembershipsViewModel { export interface GetLeagueMembershipsViewModel {
memberships: LeagueMembershipsDTO; memberships: LeagueMembershipsDTO;
} }
export class GetLeagueMembershipsPresenter { export class GetLeagueMembershipsPresenter implements UseCaseOutputPort<GetLeagueMembershipsResult> {
private result: GetLeagueMembershipsViewModel | null = null; private result: GetLeagueMembershipsViewModel | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: GetLeagueMembershipsOutputPort) { present(result: GetLeagueMembershipsResult) {
const members: LeagueMemberDTO[] = output.memberships.members.map(member => ({ const members: LeagueMemberDTO[] = result.memberships
driverId: member.driverId, .filter(({ driver }) => driver !== null)
driver: member.driver, .map(({ membership, driver }) => ({
role: member.role, driverId: membership.driverId.toString(),
joinedAt: member.joinedAt, driver: {
})); id: driver!.id,
iracingId: driver!.iracingId.toString(),
name: driver!.name.toString(),
country: driver!.country.toString(),
joinedAt: driver!.joinedAt.toDate().toISOString(),
...(driver!.bio ? { bio: driver!.bio.toString() } : {}),
},
role: membership.role.toString() as 'owner' | 'manager' | 'member',
joinedAt: membership.joinedAt.toDate(),
}));
this.result = { this.result = {
memberships: { memberships: {
members, members,

View File

@@ -66,7 +66,7 @@ export class GetLeagueProtestsPresenter implements Presenter<GetLeagueProtestsRe
iracingId: protestingDriver.iracingId.toString(), iracingId: protestingDriver.iracingId.toString(),
name: protestingDriver.name.toString(), name: protestingDriver.name.toString(),
country: protestingDriver.country.toString(), country: protestingDriver.country.toString(),
bio: protestingDriver.bio?.toString(), bio: protestingDriver.bio?.toString() || '',
joinedAt: protestingDriver.joinedAt.toDate().toISOString(), joinedAt: protestingDriver.joinedAt.toDate().toISOString(),
}; };
} }
@@ -76,8 +76,8 @@ export class GetLeagueProtestsPresenter implements Presenter<GetLeagueProtestsRe
iracingId: accusedDriver.iracingId.toString(), iracingId: accusedDriver.iracingId.toString(),
name: accusedDriver.name.toString(), name: accusedDriver.name.toString(),
country: accusedDriver.country.toString(), country: accusedDriver.country.toString(),
bio: accusedDriver.bio?.toString(), bio: accusedDriver.bio?.toString() || '',
joinedAt: accusedDriver.joinedAt.toISOString(), joinedAt: accusedDriver.joinedAt.toDate().toISOString(),
}; };
} }
} }

View File

@@ -1,16 +1,34 @@
import type { GetSeasonSponsorshipsOutputPort } from '@core/racing/application/ports/output/GetSeasonSponsorshipsOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { GetSeasonSponsorshipsResult } from '@core/racing/application/use-cases/GetSeasonSponsorshipsUseCase';
import { GetSeasonSponsorshipsOutputDTO } from '../dtos/GetSeasonSponsorshipsOutputDTO'; import { GetSeasonSponsorshipsOutputDTO } from '../dtos/GetSeasonSponsorshipsOutputDTO';
import { SponsorshipDetailDTO } from '../../sponsor/dtos/SponsorshipDetailDTO';
export class GetSeasonSponsorshipsPresenter { export class GetSeasonSponsorshipsPresenter implements UseCaseOutputPort<GetSeasonSponsorshipsResult> {
private result: GetSeasonSponsorshipsOutputDTO | null = null; private result: GetSeasonSponsorshipsOutputDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: GetSeasonSponsorshipsOutputPort) { present(result: GetSeasonSponsorshipsResult): void {
this.result = { this.result = {
sponsorships: output?.sponsorships ?? [], sponsorships: result.sponsorships.map(s => ({
id: s.id,
leagueId: s.leagueId.toString(),
leagueName: s.leagueName.toString(),
seasonId: s.seasonId,
seasonName: s.seasonName,
seasonStartDate: s.seasonStartDate,
seasonEndDate: s.seasonEndDate,
tier: s.tier as 'main' | 'secondary',
status: s.status as 'pending' | 'active' | 'expired' | 'cancelled',
pricing: s.pricing,
platformFee: s.platformFee,
netAmount: s.netAmount,
metrics: s.metrics,
createdAt: s.createdAt,
activatedAt: s.activatedAt,
} as SponsorshipDetailDTO)),
}; };
} }

View File

@@ -1,17 +1,18 @@
import type { JoinLeagueOutputPort } from '@core/racing/application/ports/output/JoinLeagueOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { JoinLeagueResult } from '@core/racing/application/use-cases/JoinLeagueUseCase';
import type { JoinLeagueOutputDTO } from '../dtos/JoinLeagueOutputDTO'; import type { JoinLeagueOutputDTO } from '../dtos/JoinLeagueOutputDTO';
export class JoinLeaguePresenter { export class JoinLeaguePresenter implements UseCaseOutputPort<JoinLeagueResult> {
private result: JoinLeagueOutputDTO | null = null; private result: JoinLeagueOutputDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: JoinLeagueOutputPort) { present(result: JoinLeagueResult): void {
this.result = { this.result = {
success: true, success: true,
membershipId: output.membershipId, membershipId: result.membership.id.toString(),
}; };
} }

View File

@@ -1,4 +1,9 @@
import { LeagueAdminDTO } from '../dtos/LeagueAdminDTO'; import { LeagueAdminDTO } from '../dtos/LeagueAdminDTO';
import { LeagueJoinRequestDTO } from '../dtos/LeagueJoinRequestDTO';
import { LeagueOwnerSummaryDTO } from '../dtos/LeagueOwnerSummaryDTO';
import { LeagueConfigFormModelDTO } from '../dtos/LeagueConfigFormModelDTO';
import { LeagueAdminProtestsDTO } from '../dtos/LeagueAdminProtestsDTO';
import { LeagueSeasonSummaryDTO } from '../dtos/LeagueSeasonSummaryDTO';
export class LeagueAdminPresenter { export class LeagueAdminPresenter {
private result: LeagueAdminDTO | null = null; private result: LeagueAdminDTO | null = null;
@@ -15,11 +20,11 @@ export class LeagueAdminPresenter {
seasons: unknown[]; seasons: unknown[];
}) { }) {
this.result = { this.result = {
joinRequests: data.joinRequests, joinRequests: data.joinRequests as LeagueJoinRequestDTO[],
ownerSummary: data.ownerSummary, ownerSummary: data.ownerSummary as LeagueOwnerSummaryDTO | null,
config: { form: data.config }, config: { form: data.config as LeagueConfigFormModelDTO | null },
protests: data.protests, protests: data.protests as LeagueAdminProtestsDTO,
seasons: data.seasons, seasons: data.seasons as LeagueSeasonSummaryDTO[],
}; };
} }

View File

@@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { LeagueConfigPresenter } from './LeagueConfigPresenter'; import { LeagueConfigPresenter } from './LeagueConfigPresenter';
import type { LeagueFullConfigOutputPort } from '@core/racing/application/ports/output/LeagueFullConfigOutputPort';
describe('LeagueConfigPresenter', () => { describe('LeagueConfigPresenter', () => {
const createFullConfig = (overrides: Partial<LeagueFullConfigOutputPort> = {}): LeagueFullConfigOutputPort => { const createFullConfig = (overrides: Partial<any> = {}): any => {
const base: LeagueFullConfigOutputPort = { const base: any = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
league: { league: {
id: 'league-1', id: 'league-1',
@@ -65,8 +65,8 @@ describe('LeagueConfigPresenter', () => {
const presenter = new LeagueConfigPresenter(); const presenter = new LeagueConfigPresenter();
const fullConfig = createFullConfig(); const fullConfig = createFullConfig();
presenter.present(fullConfig); presenter.present({ config: fullConfig });
const vm = presenter.getViewModel(); const vm = presenter.getViewModel()!;
expect(vm).not.toBeNull(); expect(vm).not.toBeNull();
expect(vm!.leagueId).toBe('league-1'); expect(vm!.leagueId).toBe('league-1');

View File

@@ -1,15 +1,17 @@
import { LeagueFullConfigOutputPort } from '@core/racing/application/ports/output/LeagueFullConfigOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { GetLeagueFullConfigResult } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase';
import { LeagueConfigFormModelDTO } from '../dtos/LeagueConfigFormModelDTO'; import { LeagueConfigFormModelDTO } from '../dtos/LeagueConfigFormModelDTO';
import type { Presenter } from '@core/shared/presentation';
export class LeagueConfigPresenter implements Presenter<LeagueFullConfigOutputPort, LeagueConfigFormModelDTO> { export class LeagueConfigPresenter implements UseCaseOutputPort<GetLeagueFullConfigResult> {
private result: LeagueConfigFormModelDTO | null = null; private result: LeagueConfigFormModelDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(dto: LeagueFullConfigOutputPort) { present(result: GetLeagueFullConfigResult): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const dto = result.config as any;
const league = dto.league; const league = dto.league;
const settings = league.settings; const settings = league.settings;
const stewarding = dto.activeSeason?.stewardingConfig; const stewarding = dto.activeSeason?.stewardingConfig;

View File

@@ -1,10 +1,10 @@
import { LeagueJoinRequestsPresenter } from './LeagueJoinRequestsPresenter'; import { LeagueJoinRequestsPresenter } from './LeagueJoinRequestsPresenter';
import type { GetLeagueJoinRequestsOutputPort } from '@core/racing/application/ports/output/GetLeagueJoinRequestsOutputPort'; import type { GetLeagueJoinRequestsResult } from '@core/racing/application/use-cases/GetLeagueJoinRequestsUseCase';
describe('LeagueJoinRequestsPresenter', () => { describe('LeagueJoinRequestsPresenter', () => {
it('presents join requests correctly', () => { it('presents join requests correctly', () => {
const presenter = new LeagueJoinRequestsPresenter(); const presenter = new LeagueJoinRequestsPresenter();
const output: GetLeagueJoinRequestsOutputPort = { const output: GetLeagueJoinRequestsResult = {
joinRequests: [ joinRequests: [
{ {
id: 'req-1', id: 'req-1',
@@ -12,17 +12,18 @@ describe('LeagueJoinRequestsPresenter', () => {
driverId: 'driver-1', driverId: 'driver-1',
requestedAt: new Date('2023-01-01'), requestedAt: new Date('2023-01-01'),
message: 'Please accept me', message: 'Please accept me',
driver: { id: 'driver-1', name: 'John Doe' }, // eslint-disable-next-line @typescript-eslint/no-explicit-any
driver: { id: 'driver-1', name: 'John Doe' } as any,
}, },
], ],
}; };
presenter.present(output); presenter.present(output);
const vm = presenter.getViewModel(); const vm = presenter.getViewModel()!;
expect(vm).not.toBeNull(); expect(vm).not.toBeNull();
expect(vm!.joinRequests).toHaveLength(1); expect(vm.joinRequests).toHaveLength(1);
expect(vm!.joinRequests[0].id).toBe('req-1'); expect(vm.joinRequests[0]!.id).toBe('req-1');
expect(vm!.joinRequests[0].driver.name).toBe('John Doe'); expect(vm.joinRequests[0]!.driver.name).toBe('John Doe');
}); });
}); });

View File

@@ -1,25 +1,29 @@
import type { GetLeagueJoinRequestsOutputPort } from '@core/racing/application/ports/output/GetLeagueJoinRequestsOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application';
import { GetLeagueJoinRequestsResult } from '@core/racing/application/use-cases/GetLeagueJoinRequestsUseCase';
import { LeagueJoinRequestWithDriverDTO } from '../dtos/LeagueJoinRequestWithDriverDTO'; import { LeagueJoinRequestWithDriverDTO } from '../dtos/LeagueJoinRequestWithDriverDTO';
export interface LeagueJoinRequestsViewModel { export interface LeagueJoinRequestsViewModel {
joinRequests: LeagueJoinRequestWithDriverDTO[]; joinRequests: LeagueJoinRequestWithDriverDTO[];
} }
export class LeagueJoinRequestsPresenter { export class LeagueJoinRequestsPresenter implements UseCaseOutputPort<GetLeagueJoinRequestsResult> {
private result: LeagueJoinRequestsViewModel | null = null; private result: LeagueJoinRequestsViewModel | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: GetLeagueJoinRequestsOutputPort) { present(result: GetLeagueJoinRequestsResult) {
const joinRequests: LeagueJoinRequestWithDriverDTO[] = output.joinRequests.map(request => ({ const joinRequests: LeagueJoinRequestWithDriverDTO[] = result.joinRequests.map(item => ({
id: request.id, id: item.id,
leagueId: request.leagueId, leagueId: item.leagueId,
driverId: request.driverId, driverId: item.driverId,
requestedAt: request.requestedAt, requestedAt: item.requestedAt,
message: request.message, ...(item.message ? { message: item.message } : {}),
driver: request.driver, driver: {
id: item.driver.id,
name: item.driver.name.toString(),
},
})); }));
this.result = { this.result = {
joinRequests, joinRequests,

View File

@@ -1,42 +1,33 @@
import { LeagueOwnerSummaryPresenter } from './LeagueOwnerSummaryPresenter'; import { LeagueOwnerSummaryPresenter } from './LeagueOwnerSummaryPresenter';
import type { GetLeagueOwnerSummaryOutputPort } from '@core/racing/application/ports/output/GetLeagueOwnerSummaryOutputPort'; import type { GetLeagueOwnerSummaryResult } from '@core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase';
describe('LeagueOwnerSummaryPresenter', () => { describe('LeagueOwnerSummaryPresenter', () => {
it('presents owner summary correctly', () => { it('presents owner summary correctly', () => {
const presenter = new LeagueOwnerSummaryPresenter(); const presenter = new LeagueOwnerSummaryPresenter();
const output: GetLeagueOwnerSummaryOutputPort = { const output: GetLeagueOwnerSummaryResult = {
summary: { // eslint-disable-next-line @typescript-eslint/no-explicit-any
driver: { league: {} as any,
id: 'driver-1', // eslint-disable-next-line @typescript-eslint/no-explicit-any
iracingId: '12345', owner: {
name: 'John Doe', id: 'driver-1',
country: 'US', iracingId: '12345',
bio: 'Racing enthusiast', name: 'John Doe',
joinedAt: '2023-01-01', country: 'US',
}, bio: 'Racing enthusiast',
rating: 1500, // eslint-disable-next-line @typescript-eslint/no-explicit-any
rank: 100, joinedAt: {} as any,
}, // eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
rating: 1500,
rank: 100,
}; };
presenter.present(output); presenter.present(output);
const vm = presenter.getViewModel(); const vm = presenter.getViewModel()!;
expect(vm).not.toBeNull(); expect(vm).not.toBeNull();
expect(vm!.driver.id).toBe('driver-1'); expect(vm.driver.id).toBe('driver-1');
expect(vm!.rating).toBe(1500); expect(vm.rating).toBe(1500);
expect(vm!.rank).toBe(100); expect(vm.rank).toBe(100);
});
it('handles null summary', () => {
const presenter = new LeagueOwnerSummaryPresenter();
const output: GetLeagueOwnerSummaryOutputPort = {
summary: null,
};
presenter.present(output);
const vm = presenter.getViewModel();
expect(vm).toBeNull();
}); });
}); });

View File

@@ -1,29 +1,26 @@
import type { GetLeagueOwnerSummaryOutputPort } from '@core/racing/application/ports/output/GetLeagueOwnerSummaryOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application';
import { GetLeagueOwnerSummaryResult } from '@core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase';
import { LeagueOwnerSummaryDTO } from '../dtos/LeagueOwnerSummaryDTO'; import { LeagueOwnerSummaryDTO } from '../dtos/LeagueOwnerSummaryDTO';
export class LeagueOwnerSummaryPresenter { export class LeagueOwnerSummaryPresenter implements UseCaseOutputPort<GetLeagueOwnerSummaryResult> {
private result: LeagueOwnerSummaryDTO | null = null; private result: LeagueOwnerSummaryDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: GetLeagueOwnerSummaryOutputPort) { present(result: GetLeagueOwnerSummaryResult) {
if (!output.summary) {
this.result = null;
return;
}
this.result = { this.result = {
driver: { driver: {
id: output.summary.driver.id, id: result.owner.id,
iracingId: output.summary.driver.iracingId, iracingId: result.owner.iracingId.toString(),
name: output.summary.driver.name, name: result.owner.name.toString(),
country: output.summary.driver.country, country: result.owner.country.toString(),
bio: output.summary.driver.bio, joinedAt: result.owner.joinedAt.toDate().toISOString(),
joinedAt: output.summary.driver.joinedAt, ...(result.owner.bio ? { bio: result.owner.bio.toString() } : {}),
}, },
rating: output.summary.rating, rating: result.rating,
rank: output.summary.rank, rank: result.rank,
}; };
} }

View File

@@ -1,21 +1,22 @@
import { GetLeagueScheduleOutputPort } from '@core/racing/application/ports/output/GetLeagueScheduleOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application';
import { GetLeagueScheduleResult } from '@core/racing/application/use-cases/GetLeagueScheduleUseCase';
import { LeagueScheduleDTO } from '../dtos/LeagueScheduleDTO'; import { LeagueScheduleDTO } from '../dtos/LeagueScheduleDTO';
import { RaceDTO } from '../../race/dtos/RaceDTO'; import { RaceDTO } from '../../race/dtos/RaceDTO';
export class LeagueSchedulePresenter { export class LeagueSchedulePresenter implements UseCaseOutputPort<GetLeagueScheduleResult> {
private result: LeagueScheduleDTO | null = null; private result: LeagueScheduleDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: GetLeagueScheduleOutputPort, leagueName?: string) { present(result: GetLeagueScheduleResult, leagueName?: string) {
this.result = { this.result = {
races: output.races.map<RaceDTO>(race => ({ races: result.races.map(race => ({
id: race.id, id: race.race.id,
name: race.name, name: `${race.race.track} - ${race.race.car}`,
date: race.scheduledAt.toISOString(), date: race.race.scheduledAt.toISOString(),
leagueName, ...(leagueName ? { leagueName } : {}),
})), })),
}; };
} }
@@ -25,19 +26,19 @@ export class LeagueSchedulePresenter {
} }
} }
export class LeagueRacesPresenter { export class LeagueRacesPresenter implements UseCaseOutputPort<GetLeagueScheduleResult> {
private result: RaceDTO[] | null = null; private result: RaceDTO[] | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: GetLeagueScheduleOutputPort, leagueName?: string) { present(result: GetLeagueScheduleResult, leagueName?: string) {
this.result = output.races.map<RaceDTO>(race => ({ this.result = result.races.map(race => ({
id: race.id, id: race.race.id,
name: race.name, name: `${race.race.track} - ${race.race.car}`,
date: race.scheduledAt.toISOString(), date: race.race.scheduledAt.toISOString(),
leagueName, ...(leagueName ? { leagueName } : {}),
})); }));
} }

View File

@@ -1,7 +1,7 @@
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { GetLeagueScoringConfigResult } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase';
import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig'; import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
import type { BonusRule } from '@core/racing/domain/types/BonusRule'; import type { BonusRule } from '@core/racing/domain/types/BonusRule';
import type { LeagueScoringConfigOutputPort } from '@core/racing/application/ports/output/LeagueScoringConfigOutputPort';
import type { LeagueScoringPresetOutputPort } from '@core/racing/application/ports/output/LeagueScoringPresetOutputPort';
export interface LeagueScoringChampionshipViewModel { export interface LeagueScoringChampionshipViewModel {
id: string; id: string;
@@ -24,33 +24,31 @@ export interface LeagueScoringConfigViewModel {
championships: LeagueScoringChampionshipViewModel[]; championships: LeagueScoringChampionshipViewModel[];
} }
export class LeagueScoringConfigPresenter { export class LeagueScoringConfigPresenter implements UseCaseOutputPort<GetLeagueScoringConfigResult> {
private viewModel: LeagueScoringConfigViewModel | null = null; private viewModel: LeagueScoringConfigViewModel | null = null;
reset(): void { reset(): void {
this.viewModel = null; this.viewModel = null;
} }
present(data: LeagueScoringConfigOutputPort): LeagueScoringConfigViewModel { present(result: GetLeagueScoringConfigResult): void {
const championships: LeagueScoringChampionshipViewModel[] = const championships: LeagueScoringChampionshipViewModel[] =
data.championships.map((champ) => this.mapChampionship(champ)); result.scoringConfig.championships.map((champ) => this.mapChampionship(champ));
const dropPolicySummary = const dropPolicySummary =
data.preset?.dropPolicySummary ?? result.preset?.dropPolicySummary ??
this.deriveDropPolicyDescriptionFromChampionships(data.championships); this.deriveDropPolicyDescriptionFromChampionships(result.scoringConfig.championships);
this.viewModel = { this.viewModel = {
leagueId: data.leagueId, leagueId: result.league.id.toString(),
seasonId: data.seasonId, seasonId: result.season.id,
gameId: data.gameId, gameId: result.game.id.toString(),
gameName: data.gameName, gameName: result.game.name.toString(),
scoringPresetId: data.scoringPresetId ?? 'custom', scoringPresetId: result.scoringConfig.scoringPresetId?.toString() ?? 'custom',
scoringPresetName: data.preset?.name ?? 'Custom', scoringPresetName: result.preset?.name ?? 'Custom',
dropPolicySummary, dropPolicySummary,
championships, championships,
}; };
return this.viewModel;
} }
getViewModel(): LeagueScoringConfigViewModel | null { getViewModel(): LeagueScoringConfigViewModel | null {

View File

@@ -1,22 +1,22 @@
import type { LeagueScoringPresetsOutputPort } from '@core/racing/application/ports/output/LeagueScoringPresetsOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { LeagueScoringPresetOutputPort } from '@core/racing/application/ports/output/LeagueScoringPresetOutputPort'; import type { ListLeagueScoringPresetsResult, LeagueScoringPreset } from '@core/racing/application/use-cases/ListLeagueScoringPresetsUseCase';
export interface LeagueScoringPresetsViewModel { export interface LeagueScoringPresetsViewModel {
presets: LeagueScoringPresetOutputPort[]; presets: LeagueScoringPreset[];
totalCount: number; totalCount: number;
} }
export class LeagueScoringPresetsPresenter { export class LeagueScoringPresetsPresenter implements UseCaseOutputPort<ListLeagueScoringPresetsResult> {
private viewModel: LeagueScoringPresetsViewModel | null = null; private viewModel: LeagueScoringPresetsViewModel | null = null;
reset(): void { reset(): void {
this.viewModel = null; this.viewModel = null;
} }
present(output: LeagueScoringPresetsOutputPort): void { present(result: ListLeagueScoringPresetsResult): void {
this.viewModel = { this.viewModel = {
presets: output.presets, presets: result.presets,
totalCount: output.presets.length, totalCount: result.presets.length,
}; };
} }

View File

@@ -1,21 +1,24 @@
import { LeagueStandingsOutputPort } from '@core/racing/application/ports/output/LeagueStandingsOutputPort'; import type { GetLeagueStandingsResult } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
import { LeagueStandingsDTO } from '../dtos/LeagueStandingsDTO'; import { LeagueStandingsDTO } from '../dtos/LeagueStandingsDTO';
import type { Presenter } from '@core/shared/presentation'; import type { Presenter } from '@core/shared/presentation';
export class LeagueStandingsPresenter implements Presenter<LeagueStandingsOutputPort, LeagueStandingsDTO> { export class LeagueStandingsPresenter implements Presenter<GetLeagueStandingsResult, LeagueStandingsDTO> {
private result: LeagueStandingsDTO | null = null; private result: LeagueStandingsDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(dto: LeagueStandingsOutputPort) { present(dto: GetLeagueStandingsResult) {
const standings = dto.standings.map(standing => ({ const standings = dto.standings.map(standing => ({
driverId: standing.driverId, driverId: standing.driverId,
driver: { driver: {
id: standing.driver.id, id: standing.driver.id,
name: standing.driver.name, iracingId: standing.driver.iracingId.toString(),
// Add other DriverDto fields if needed, but for now just id and name name: standing.driver.name.toString(),
country: standing.driver.country.toString(),
...(standing.driver.bio ? { bio: standing.driver.bio.toString() } : {}),
joinedAt: standing.driver.joinedAt.toString(),
}, },
points: standing.points, points: standing.points,
rank: standing.rank, rank: standing.rank,
@@ -23,7 +26,7 @@ export class LeagueStandingsPresenter implements Presenter<LeagueStandingsOutput
this.result = { standings }; this.result = { standings };
} }
getViewModel(): LeagueStandingsDTO { getResponseModel(): LeagueStandingsDTO {
if (!this.result) throw new Error('Presenter not presented'); if (!this.result) throw new Error('Presenter not presented');
return this.result; return this.result;
} }

View File

@@ -1,19 +1,23 @@
import { LeagueStatsOutputPort } from '@core/racing/application/ports/output/LeagueStatsOutputPort'; import type { GetLeagueStatsResult } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
import { LeagueStatsDTO } from '../dtos/LeagueStatsDTO'; import { LeagueStatsDTO } from '../dtos/LeagueStatsDTO';
import type { Presenter } from '@core/shared/presentation'; import type { Presenter } from '@core/shared/presentation';
export class LeagueStatsPresenter implements Presenter<LeagueStatsOutputPort, LeagueStatsDTO> { export class LeagueStatsPresenter implements Presenter<GetLeagueStatsResult, LeagueStatsDTO> {
private result: LeagueStatsDTO | null = null; private result: LeagueStatsDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(dto: LeagueStatsOutputPort) { present(dto: GetLeagueStatsResult) {
this.result = dto; this.result = {
totalMembers: dto.driverCount,
totalRaces: dto.raceCount,
averageRating: dto.averageRating,
};
} }
getViewModel(): LeagueStatsDTO | null { getResponseModel(): LeagueStatsDTO | null {
return this.result; return this.result;
} }
} }

View File

@@ -1,17 +1,18 @@
import type { RejectLeagueJoinRequestOutputPort } from '@core/racing/application/ports/output/RejectLeagueJoinRequestOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { RejectLeagueJoinRequestResult } from '@core/racing/application/use-cases/RejectLeagueJoinRequestUseCase';
import type { RejectJoinRequestOutputDTO } from '../dtos/RejectJoinRequestOutputDTO'; import type { RejectJoinRequestOutputDTO } from '../dtos/RejectJoinRequestOutputDTO';
export class RejectLeagueJoinRequestPresenter { export class RejectLeagueJoinRequestPresenter implements UseCaseOutputPort<RejectLeagueJoinRequestResult> {
private result: RejectJoinRequestOutputDTO | null = null; private result: RejectJoinRequestOutputDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: RejectLeagueJoinRequestOutputPort) { present(_result: RejectLeagueJoinRequestResult): void {
this.result = { this.result = {
success: output.success, success: true,
message: output.message, message: 'Join request rejected successfully',
}; };
} }

View File

@@ -1,16 +1,17 @@
import type { RemoveLeagueMemberOutputPort } from '@core/racing/application/ports/output/RemoveLeagueMemberOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { RemoveLeagueMemberResult } from '@core/racing/application/use-cases/RemoveLeagueMemberUseCase';
import type { RemoveLeagueMemberOutputDTO } from '../dtos/RemoveLeagueMemberOutputDTO'; import type { RemoveLeagueMemberOutputDTO } from '../dtos/RemoveLeagueMemberOutputDTO';
export class RemoveLeagueMemberPresenter { export class RemoveLeagueMemberPresenter implements UseCaseOutputPort<RemoveLeagueMemberResult> {
private result: RemoveLeagueMemberOutputDTO | null = null; private result: RemoveLeagueMemberOutputDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: RemoveLeagueMemberOutputPort) { present(_result: RemoveLeagueMemberResult): void {
this.result = { this.result = {
success: output.success, success: true,
}; };
} }

View File

@@ -1,4 +1,4 @@
import { GetTotalLeaguesOutputPort } from '@core/racing/application/ports/output/GetTotalLeaguesOutputPort'; import type { GetTotalLeaguesResult } from '@core/racing/application/use-cases/GetTotalLeaguesUseCase';
import { TotalLeaguesDTO } from '../dtos/TotalLeaguesDTO'; import { TotalLeaguesDTO } from '../dtos/TotalLeaguesDTO';
export class TotalLeaguesPresenter { export class TotalLeaguesPresenter {
@@ -8,13 +8,13 @@ export class TotalLeaguesPresenter {
this.result = null; this.result = null;
} }
present(output: GetTotalLeaguesOutputPort) { present(output: GetTotalLeaguesResult) {
this.result = { this.result = {
totalLeagues: output.totalLeagues, totalLeagues: output.totalLeagues,
}; };
} }
getViewModel(): TotalLeaguesDTO | null { getResponseModel(): TotalLeaguesDTO | null {
return this.result; return this.result;
} }
} }

View File

@@ -1,16 +1,17 @@
import type { TransferLeagueOwnershipOutputPort } from '@core/racing/application/ports/output/TransferLeagueOwnershipOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { TransferLeagueOwnershipResult } from '@core/racing/application/use-cases/TransferLeagueOwnershipUseCase';
import type { TransferLeagueOwnershipOutputDTO } from '../dtos/TransferLeagueOwnershipOutputDTO'; import type { TransferLeagueOwnershipOutputDTO } from '../dtos/TransferLeagueOwnershipOutputDTO';
export class TransferLeagueOwnershipPresenter { export class TransferLeagueOwnershipPresenter implements UseCaseOutputPort<TransferLeagueOwnershipResult> {
private result: TransferLeagueOwnershipOutputDTO | null = null; private result: TransferLeagueOwnershipOutputDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: TransferLeagueOwnershipOutputPort) { present(_result: TransferLeagueOwnershipResult): void {
this.result = { this.result = {
success: output.success, success: true,
}; };
} }

View File

@@ -1,16 +1,17 @@
import type { UpdateLeagueMemberRoleOutputPort } from '@core/racing/application/ports/output/UpdateLeagueMemberRoleOutputPort'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { UpdateLeagueMemberRoleResult } from '@core/racing/application/use-cases/UpdateLeagueMemberRoleUseCase';
import type { UpdateLeagueMemberRoleOutputDTO } from '../dtos/UpdateLeagueMemberRoleOutputDTO'; import type { UpdateLeagueMemberRoleOutputDTO } from '../dtos/UpdateLeagueMemberRoleOutputDTO';
export class UpdateLeagueMemberRolePresenter { export class UpdateLeagueMemberRolePresenter implements UseCaseOutputPort<UpdateLeagueMemberRoleResult> {
private result: UpdateLeagueMemberRoleOutputDTO | null = null; private result: UpdateLeagueMemberRoleOutputDTO | null = null;
reset() { reset() {
this.result = null; this.result = null;
} }
present(output: UpdateLeagueMemberRoleOutputPort) { present(_result: UpdateLeagueMemberRoleResult): void {
this.result = { this.result = {
success: output.success, success: true,
}; };
} }

View File

@@ -18,11 +18,11 @@ export interface ApproveLeagueJoinRequestResult {
export class ApproveLeagueJoinRequestUseCase { export class ApproveLeagueJoinRequestUseCase {
constructor( constructor(
private readonly leagueMembershipRepository: ILeagueMembershipRepository, private readonly leagueMembershipRepository: ILeagueMembershipRepository,
private readonly output: UseCaseOutputPort<ApproveLeagueJoinRequestResult>,
) {} ) {}
async execute( async execute(
input: ApproveLeagueJoinRequestInput, input: ApproveLeagueJoinRequestInput,
output: UseCaseOutputPort<ApproveLeagueJoinRequestResult>,
): Promise<Result<void, ApplicationErrorCode<'JOIN_REQUEST_NOT_FOUND'>>> { ): Promise<Result<void, ApplicationErrorCode<'JOIN_REQUEST_NOT_FOUND'>>> {
const requests = await this.leagueMembershipRepository.getJoinRequests(input.leagueId); const requests = await this.leagueMembershipRepository.getJoinRequests(input.leagueId);
const request = requests.find(r => r.id === input.requestId); const request = requests.find(r => r.id === input.requestId);
@@ -41,7 +41,7 @@ export class ApproveLeagueJoinRequestUseCase {
}); });
const result: ApproveLeagueJoinRequestResult = { success: true, message: 'Join request approved.' }; const result: ApproveLeagueJoinRequestResult = { success: true, message: 'Join request approved.' };
this.output.present(result); output.present(result);
return Result.ok(undefined); return Result.ok(undefined);
} }