refactor to adapters
This commit is contained in:
50
apps/api/src/application/analytics.module.ts
Normal file
50
apps/api/src/application/analytics.module.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { getDataSourceToken } from '@nestjs/typeorm';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
const ILogger_TOKEN = 'ILogger_TOKEN';
|
||||
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
||||
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
|
||||
|
||||
import { ILogger } from '@gridpilot/shared/logging/ILogger';
|
||||
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
|
||||
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
|
||||
|
||||
import { RecordPageViewUseCase } from '@gridpilot/analytics/application/use-cases/RecordPageViewUseCase';
|
||||
import { RecordEngagementUseCase } from '@gridpilot/analytics/application/use-cases/RecordEngagementUseCase';
|
||||
|
||||
import { InMemoryPageViewRepository } from '../../../../adapters/persistence/inmemory/analytics/InMemoryPageViewRepository';
|
||||
import { TypeOrmEngagementRepository } from '../../../../adapters/persistence/typeorm/analytics/TypeOrmEngagementRepository';
|
||||
import { ConsoleLogger } from '../../../../adapters/logging/ConsoleLogger';
|
||||
import { AnalyticsController } from '../../presentation/analytics.controller';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AnalyticsController],
|
||||
providers: [
|
||||
{
|
||||
provide: ILogger_TOKEN,
|
||||
useClass: ConsoleLogger,
|
||||
},
|
||||
{
|
||||
provide: IPAGE_VIEW_REPO_TOKEN,
|
||||
useClass: InMemoryPageViewRepository,
|
||||
},
|
||||
{
|
||||
provide: IENGAGEMENT_REPO_TOKEN,
|
||||
useFactory: (dataSource: DataSource) => new TypeOrmEngagementRepository(dataSource.manager),
|
||||
inject: [getDataSourceToken()],
|
||||
},
|
||||
{
|
||||
provide: RecordPageViewUseCase,
|
||||
useFactory: (repo: IPageViewRepository, logger: ILogger) => new RecordPageViewUseCase(repo, logger),
|
||||
inject: [IPAGE_VIEW_REPO_TOKEN, ILogger_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: RecordEngagementUseCase,
|
||||
useFactory: (repo: IEngagementRepository, logger: ILogger) => new RecordEngagementUseCase(repo, logger),
|
||||
inject: [IENGAGEMENT_REPO_TOKEN, ILogger_TOKEN],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AnalyticsModule {}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { PageViewEntity } from '../analytics/typeorm-page-view.entity';
|
||||
import { AnalyticsSnapshotOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/AnalyticsSnapshotOrmEntity';
|
||||
import { EngagementOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/EngagementOrmEntity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -11,9 +12,10 @@ import { PageViewEntity } from '../analytics/typeorm-page-view.entity';
|
||||
username: process.env.DATABASE_USER || 'user',
|
||||
password: process.env.DATABASE_PASSWORD || 'password',
|
||||
database: process.env.DATABASE_NAME || 'gridpilot',
|
||||
entities: [PageViewEntity],
|
||||
entities: [AnalyticsSnapshotOrmEntity, EngagementOrmEntity],
|
||||
synchronize: true, // Use carefully in production
|
||||
}),
|
||||
],
|
||||
exports: [TypeOrmModule],
|
||||
})
|
||||
export class DatabaseModule {}
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import { Controller, Post, Body, Res, HttpStatus } from '@nestjs/common';
|
||||
import { AnalyticsService } from '../application/analytics/analytics.service';
|
||||
import { RecordPageViewInput } from '../application/analytics/record-page-view.use-case';
|
||||
import { RecordEngagementInput, RecordEngagementOutput } from '../application/analytics/record-engagement.use-case';
|
||||
|
||||
import type { RecordPageViewInput, RecordPageViewOutput } from '@gridpilot/analytics/application/use-cases/RecordPageViewUseCase';
|
||||
import type { RecordEngagementInput, RecordEngagementOutput } from '@gridpilot/analytics/application/use-cases/RecordEngagementUseCase';
|
||||
import { RecordPageViewUseCase } from '@gridpilot/analytics/application/use-cases/RecordPageViewUseCase';
|
||||
import { RecordEngagementUseCase } from '@gridpilot/analytics/application/use-cases/RecordEngagementUseCase';
|
||||
import { Response } from 'express';
|
||||
|
||||
@Controller('analytics')
|
||||
export class AnalyticsController {
|
||||
constructor(private readonly analyticsService: AnalyticsService) {}
|
||||
constructor(
|
||||
private readonly recordPageViewUseCase: RecordPageViewUseCase,
|
||||
private readonly recordEngagementUseCase: RecordEngagementUseCase,
|
||||
) {}
|
||||
|
||||
@Post('page-view')
|
||||
async recordPageView(
|
||||
@Body() input: RecordPageViewInput,
|
||||
@Res() res: Response,
|
||||
): Promise<void> {
|
||||
const { pageViewId } = await this.analyticsService.recordPageView(input);
|
||||
res.status(HttpStatus.CREATED).json({ pageViewId });
|
||||
const output: RecordPageViewOutput = await this.recordPageViewUseCase.execute(input);
|
||||
res.status(HttpStatus.CREATED).json(output);
|
||||
}
|
||||
|
||||
@Post('engagement')
|
||||
@@ -22,7 +27,7 @@ export class AnalyticsController {
|
||||
@Body() input: RecordEngagementInput,
|
||||
@Res() res: Response,
|
||||
): Promise<void> {
|
||||
const output: RecordEngagementOutput = await this.analyticsService.recordEngagement(input);
|
||||
const output: RecordEngagementOutput = await this.recordEngagementUseCase.execute(input);
|
||||
res.status(HttpStatus.CREATED).json(output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import type { AuthService, AuthSession, SignupParams, LoginParams } from './AuthService';
|
||||
import type { AuthCallbackCommandDTO } from '@gridpilot/identity/application/dto/AuthCallbackCommandDTO';
|
||||
import type { StartAuthCommandDTO } from '@gridpilot/identity/application/dto/StartAuthCommandDTO';
|
||||
import { StartAuthUseCase } from '@gridpilot/identity/application/use-cases/StartAuthUseCase';
|
||||
import { GetCurrentUserSessionUseCase } from '@gridpilot/identity/application/use-cases/GetCurrentUserSessionUseCase';
|
||||
import { HandleAuthCallbackUseCase } from '@gridpilot/identity/application/use-cases/HandleAuthCallbackUseCase';
|
||||
import { LogoutUseCase } from '@gridpilot/identity/application/use-cases/LogoutUseCase';
|
||||
import { SignupWithEmailUseCase } from '@gridpilot/identity/application/use-cases/SignupWithEmailUseCase';
|
||||
import { LoginWithEmailUseCase } from '@gridpilot/identity/application/use-cases/LoginWithEmailUseCase';
|
||||
import { CookieIdentitySessionAdapter } from '@gridpilot/identity/infrastructure/session/CookieIdentitySessionAdapter';
|
||||
import { IracingDemoIdentityProviderAdapter } from '@gridpilot/identity/infrastructure/providers/IracingDemoIdentityProviderAdapter';
|
||||
import { InMemoryUserRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryUserRepository';
|
||||
import type { IUserRepository } from '@gridpilot/identity/domain/repositories/IUserRepository';
|
||||
import type { ILogger } from '@gridpilot/shared/logging/ILogger';
|
||||
|
||||
// Singleton user repository to persist across requests (in-memory demo)
|
||||
let userRepositoryInstance: IUserRepository | null = null;
|
||||
|
||||
function getUserRepository(logger: ILogger): IUserRepository {
|
||||
if (!userRepositoryInstance) {
|
||||
userRepositoryInstance = new InMemoryUserRepository(logger);
|
||||
}
|
||||
return userRepositoryInstance;
|
||||
}
|
||||
|
||||
export class InMemoryAuthService implements AuthService {
|
||||
private readonly logger: ILogger;
|
||||
|
||||
constructor(logger: ILogger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
async getCurrentSession(): Promise<AuthSession | null> {
|
||||
const sessionPort = new CookieIdentitySessionAdapter();
|
||||
const useCase = new GetCurrentUserSessionUseCase(sessionPort);
|
||||
return useCase.execute();
|
||||
}
|
||||
|
||||
async signupWithEmail(params: SignupParams): Promise<AuthSession> {
|
||||
const userRepository = getUserRepository();
|
||||
const sessionPort = new CookieIdentitySessionAdapter();
|
||||
const useCase = new SignupWithEmailUseCase(userRepository, sessionPort);
|
||||
|
||||
const result = await useCase.execute({
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
displayName: params.displayName,
|
||||
});
|
||||
|
||||
return result.session;
|
||||
}
|
||||
|
||||
async loginWithEmail(params: LoginParams): Promise<AuthSession> {
|
||||
const userRepository = getUserRepository();
|
||||
const sessionPort = new CookieIdentitySessionAdapter();
|
||||
const useCase = new LoginWithEmailUseCase(userRepository, sessionPort);
|
||||
|
||||
return useCase.execute({
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
});
|
||||
}
|
||||
|
||||
async startIracingAuthRedirect(
|
||||
returnTo?: string,
|
||||
): Promise<{ redirectUrl: string; state: string }> {
|
||||
const provider = new IracingDemoIdentityProviderAdapter();
|
||||
const useCase = new StartAuthUseCase(provider);
|
||||
|
||||
const command: StartAuthCommandDTO = returnTo
|
||||
? {
|
||||
provider: 'IRACING_DEMO',
|
||||
returnTo,
|
||||
}
|
||||
: {
|
||||
provider: 'IRACING_DEMO',
|
||||
};
|
||||
|
||||
return useCase.execute(command);
|
||||
}
|
||||
|
||||
async loginWithIracingCallback(params: {
|
||||
code: string;
|
||||
state: string;
|
||||
returnTo?: string;
|
||||
}): Promise<AuthSession> {
|
||||
const provider = new IracingDemoIdentityProviderAdapter();
|
||||
const sessionPort = new CookieIdentitySessionAdapter();
|
||||
const useCase = new HandleAuthCallbackUseCase(provider, sessionPort);
|
||||
|
||||
const command: AuthCallbackCommandDTO = params.returnTo
|
||||
? {
|
||||
provider: 'IRACING_DEMO',
|
||||
code: params.code,
|
||||
state: params.state,
|
||||
returnTo: params.returnTo,
|
||||
}
|
||||
: {
|
||||
provider: 'IRACING_DEMO',
|
||||
code: params.code,
|
||||
state: params.state,
|
||||
};
|
||||
|
||||
return useCase.execute(command);
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
const sessionPort = new CookieIdentitySessionAdapter();
|
||||
const useCase = new LogoutUseCase(sessionPort);
|
||||
await useCase.execute();
|
||||
}
|
||||
}
|
||||
@@ -37,33 +37,48 @@ import type { ILeagueMembershipRepository } from '@gridpilot/racing/domain/repos
|
||||
import type { IFeedRepository } from '@gridpilot/social/domain/repositories/IFeedRepository';
|
||||
import type { ISocialGraphRepository } from '@gridpilot/social/domain/repositories/ISocialGraphRepository';
|
||||
import type { ImageServicePort } from '@gridpilot/media';
|
||||
import type { ILogger } from '@gridpilot/shared/logging/ILogger';
|
||||
import { ConsoleLogger } from '@gridpilot/shared/logging/ConsoleLogger';
|
||||
import type { ILogger } from '@gridpilot/shared/logging';
|
||||
import { ConsoleLogger } from '../../../adapters/logging';
|
||||
import type { IPageViewRepository, IEngagementRepository } from '@gridpilot/analytics';
|
||||
import { InMemoryPageViewRepository, InMemoryEngagementRepository } from '@gridpilot/analytics/infrastructure/repositories';
|
||||
import { RecordPageViewUseCase, RecordEngagementUseCase } from '@gridpilot/analytics/application/use-cases';
|
||||
import type { AuthService } from './auth/AuthService';
|
||||
import { InMemoryAuthService } from './auth/InMemoryAuthService';
|
||||
import type { IUserRepository, StoredUser } from '@gridpilot/identity/domain/repositories/IUserRepository';
|
||||
import { InMemoryUserRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryUserRepository';
|
||||
import type { ISponsorAccountRepository, SponsorAccount } from '@gridpilot/identity/domain/repositories/ISponsorAccountRepository';
|
||||
import { InMemorySponsorAccountRepository } from '@gridpilot/identity/infrastructure/repositories/InMemorySponsorAccountRepository';
|
||||
import type { ILiveryRepository } from '@gridpilot/racing/domain/repositories/ILiveryRepository';
|
||||
import { InMemoryLiveryRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryLiveryRepository';
|
||||
import type { IChampionshipStandingRepository } from '@gridpilot/racing/domain/repositories/IChampionshipStandingRepository';
|
||||
import { InMemoryChampionshipStandingRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryScoringRepositories';
|
||||
import type { ILeagueWalletRepository } from '@gridpilot/racing/domain/repositories/ILeagueWalletRepository';
|
||||
import { InMemoryLeagueWalletRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryLeagueWalletRepository';
|
||||
import type { ITransactionRepository } from '@gridpilot/racing/domain/repositories/ITransactionRepository';
|
||||
import { InMemoryTransactionRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryTransactionRepository';
|
||||
import type { ISessionRepository } from '@gridpilot/racing/domain/repositories/ISessionRepository';
|
||||
import { InMemorySessionRepository } from '@gridpilot/racing/infrastructure/repositories/InMemorySessionRepository';
|
||||
import type { IAchievementRepository } from '@gridpilot/identity/domain/repositories/IAchievementRepository';
|
||||
import { InMemoryAchievementRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryAchievementRepository';
|
||||
import type { IUserRatingRepository } from '@gridpilot/identity/domain/repositories/IUserRatingRepository';
|
||||
import { InMemoryUserRatingRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryUserRatingRepository';
|
||||
import { ConsoleLogger } from '../../../adapters/logging';
|
||||
|
||||
import { DI_TOKENS } from './di-tokens';
|
||||
// Identity authentication use cases and ports
|
||||
import { StartAuthUseCase } from '@gridpilot/identity/application/use-cases/StartAuthUseCase';
|
||||
import { GetCurrentUserSessionUseCase } from '@gridpilot/identity/application/use-cases/GetCurrentUserSessionUseCase';
|
||||
import { HandleAuthCallbackUseCase } from '@gridpilot/identity/application/use-cases/HandleAuthCallbackUseCase';
|
||||
import { LogoutUseCase } from '@gridpilot/identity/application/use-cases/LogoutUseCase';
|
||||
import { SignupWithEmailUseCase } from '@gridpilot/identity/application/use-cases/SignupWithEmailUseCase';
|
||||
import { LoginWithEmailUseCase } from '@gridpilot/identity/application/use-cases/LoginWithEmailUseCase';
|
||||
import type { IdentityProvider } from '@gridpilot/identity/application/ports/IdentityProvider';
|
||||
import type { SessionPort } from '@gridpilot/identity/application/ports/SessionPort';
|
||||
import { IracingDemoIdentityProviderAdapter } from '../../../testing/fakes/identity/IracingDemoIdentityProviderAdapter';
|
||||
import { CookieIdentitySessionAdapter } from '@gridpilot/identity/infrastructure/session/CookieIdentitySessionAdapter';
|
||||
|
||||
import type { AuthService } from './auth/AuthService';
|
||||
import type { ILogger } from '@gridpilot/shared/logging';
|
||||
|
||||
// Repositories
|
||||
import type { IAchievementRepository } from '@gridpilot/identity/domain/repositories/IAchievementRepository';
|
||||
import { InMemoryAchievementRepository } from '../../adapters/persistence/inmemory/identity/InMemoryAchievementRepository';
|
||||
import type { IUserRatingRepository } from '@gridpilot/identity/domain/repositories/IUserRatingRepository';
|
||||
import { InMemoryUserRatingRepository } from '../../adapters/persistence/inmemory/identity/InMemoryUserRatingRepository';
|
||||
import type { ISponsorAccountRepository, SponsorAccount } from '@gridpilot/identity/domain/repositories/ISponsorAccountRepository';
|
||||
import { InMemorySponsorAccountRepository } from '../../adapters/persistence/inmemory/identity/InMemorySponsorAccountRepository';
|
||||
import type { IUserRepository, StoredUser } from '@gridpilot/identity/domain/repositories/IUserRepository';
|
||||
import { InMemoryUserRepository } from '../../adapters/persistence/inmemory/identity/InMemoryUserRepository';
|
||||
|
||||
import type { ILiveryRepository } from '@gridpilot/racing/domain/repositories/ILiveryRepository';
|
||||
import { InMemoryLiveryRepository } from '../../adapters/persistence/inmemory/racing/InMemoryLiveryRepository';
|
||||
import type { IChampionshipStandingRepository } from '@gridpilot/racing/domain/repositories/IChampionshipStandingRepository';
|
||||
import { InMemoryChampionshipStandingRepository } from '../../adapters/persistence/inmemory/racing/InMemoryScoringRepositories';
|
||||
import type { ILeagueWalletRepository } from '@gridpilot/racing/domain/repositories/ILeagueWalletRepository';
|
||||
import { InMemoryLeagueWalletRepository } from '../../adapters/persistence/inmemory/racing/InMemoryLeagueWalletRepository';
|
||||
import type { ITransactionRepository } from '@gridpilot/racing/domain/repositories/ITransactionRepository';
|
||||
import { InMemoryTransactionRepository } from '../../adapters/persistence/inmemory/racing/InMemoryTransactionRepository';
|
||||
import type { ISessionRepository } from '@gridpilot/racing/domain/repositories/ISessionRepository';
|
||||
import { InMemorySessionRepository } from '../../adapters/persistence/inmemory/racing/InMemorySessionRepository';
|
||||
|
||||
// Notifications
|
||||
import type { INotificationRepository, INotificationPreferenceRepository } from '@gridpilot/notifications/application';
|
||||
import {
|
||||
SendNotificationUseCase,
|
||||
@@ -77,10 +92,17 @@ import {
|
||||
InAppNotificationAdapter,
|
||||
} from '@gridpilot/notifications/infrastructure';
|
||||
|
||||
import type { IPageViewRepository, IEngagementRepository } from '@gridpilot/analytics';
|
||||
import { InMemoryPageViewRepository, InMemoryEngagementRepository } from '../../adapters/persistence/inmemory/analytics/InMemoryAnalyticsRepositories';
|
||||
import {
|
||||
RecordPageViewUseCase,
|
||||
RecordEngagementUseCase
|
||||
} from '@gridpilot/analytics/application/use-cases';
|
||||
|
||||
// Infrastructure repositories
|
||||
import { InMemoryDriverRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryDriverRepository';
|
||||
import { InMemoryLeagueRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryLeagueRepository';
|
||||
import { InMemoryRaceRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryRaceRepository';
|
||||
import { InMemoryDriverRepository } from '../../adapters/persistence/inmemory/racing/InMemoryDriverRepository';
|
||||
import { InMemoryLeagueRepository } from '../../adapters/persistence/inmemory/racing/InMemoryLeagueRepository';
|
||||
import { InMemoryRaceRepository } from '../../adapters/persistence/inmemory/racing/InMemoryRaceRepository';
|
||||
import { InMemoryResultRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryResultRepository';
|
||||
import { InMemoryStandingRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryStandingRepository';
|
||||
import { InMemoryPenaltyRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryPenaltyRepository';
|
||||
@@ -180,6 +202,7 @@ import { LeagueStatsPresenter } from './presenters/LeagueStatsPresenter';
|
||||
import { LeagueScoringConfigPresenter } from './presenters/LeagueScoringConfigPresenter';
|
||||
import { LeagueFullConfigPresenter } from './presenters/LeagueFullConfigPresenter';
|
||||
import { LeagueDriverSeasonStatsPresenter } from './presenters/LeagueDriverSeasonStatsPresenter';
|
||||
import { LeagueStandingsPresenter as ILeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
|
||||
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
|
||||
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
|
||||
import { RaceWithSOFPresenter } from './presenters/RaceWithSOFPresenter';
|
||||
@@ -245,6 +268,10 @@ export function configureDIContainer(): void {
|
||||
const driverStats: DemoDriverStatsMap = createDemoDriverStats(seedData.drivers);
|
||||
|
||||
// Register repositories
|
||||
container.registerInstance<ILeagueStandingsRepository>(
|
||||
DI_TOKENS.LeagueStandingsRepository,
|
||||
new LeagueStandingsRepositoryAdapter()
|
||||
);
|
||||
container.registerInstance<IDriverRepository>(
|
||||
DI_TOKENS.DriverRepository,
|
||||
new InMemoryDriverRepository(logger, seedData.drivers)
|
||||
@@ -920,6 +947,7 @@ export function configureDIContainer(): void {
|
||||
const raceRegistrationRepository = container.resolve<IRaceRegistrationRepository>(DI_TOKENS.RaceRegistrationRepository);
|
||||
const leagueMembershipRepository = container.resolve<ILeagueMembershipRepository>(DI_TOKENS.LeagueMembershipRepository);
|
||||
const standingRepository = container.resolve<IStandingRepository>(DI_TOKENS.StandingRepository);
|
||||
const leagueStandingsRepository = container.resolve<ILeagueStandingsRepository>(DI_TOKENS.LeagueStandingsRepository);
|
||||
const penaltyRepository = container.resolve<IPenaltyRepository>(DI_TOKENS.PenaltyRepository);
|
||||
const protestRepository = container.resolve<IProtestRepository>(DI_TOKENS.ProtestRepository);
|
||||
const teamRepository = container.resolve<ITeamRepository>(DI_TOKENS.TeamRepository);
|
||||
@@ -1095,10 +1123,14 @@ export function configureDIContainer(): void {
|
||||
new GetRaceRegistrationsUseCase(raceRegistrationRepository)
|
||||
);
|
||||
|
||||
const leagueStandingsPresenter = new LeagueStandingsPresenter();
|
||||
container.registerInstance(
|
||||
container.registerInstance<IGetLeagueStandingsUseCase>(
|
||||
DI_TOKENS.GetLeagueStandingsUseCase,
|
||||
new GetLeagueStandingsUseCase(standingRepository),
|
||||
new GetLeagueStandingsUseCaseImpl(leagueStandingsRepository),
|
||||
);
|
||||
|
||||
container.registerInstance<ILeagueStandingsPresenter>(
|
||||
DI_TOKENS.LeagueStandingsPresenter,
|
||||
new LeagueStandingsPresenter(container.resolve<IGetLeagueStandingsUseCase>(DI_TOKENS.GetLeagueStandingsUseCase)),
|
||||
);
|
||||
|
||||
container.registerInstance(
|
||||
|
||||
@@ -9,6 +9,7 @@ export const DI_TOKENS = {
|
||||
RaceRepository: Symbol.for('IRaceRepository'),
|
||||
ResultRepository: Symbol.for('IResultRepository'),
|
||||
StandingRepository: Symbol.for('IStandingRepository'),
|
||||
LeagueStandingsRepository: Symbol.for('ILeagueStandingsRepository'),
|
||||
PenaltyRepository: Symbol.for('IPenaltyRepository'),
|
||||
ProtestRepository: Symbol.for('IProtestRepository'),
|
||||
TeamRepository: Symbol.for('ITeamRepository'),
|
||||
@@ -50,6 +51,18 @@ export const DI_TOKENS = {
|
||||
Logger: Symbol.for('ILogger'),
|
||||
AuthService: Symbol.for('AuthService'),
|
||||
|
||||
// Auth dependencies
|
||||
IdentityProvider: Symbol.for('IdentityProvider'),
|
||||
SessionPort: Symbol.for('SessionPort'),
|
||||
|
||||
// Use Cases - Auth
|
||||
StartAuthUseCase: Symbol.for('StartAuthUseCase'),
|
||||
GetCurrentUserSessionUseCase: Symbol.for('GetCurrentUserSessionUseCase'),
|
||||
HandleAuthCallbackUseCase: Symbol.for('HandleAuthCallbackUseCase'),
|
||||
LogoutUseCase: Symbol.for('LogoutUseCase'),
|
||||
SignupWithEmailUseCase: Symbol.for('SignupWithEmailUseCase'),
|
||||
LoginWithEmailUseCase: Symbol.for('LoginWithEmailUseCase'),
|
||||
|
||||
// Use Cases - Analytics
|
||||
RecordPageViewUseCase: Symbol.for('RecordPageViewUseCase'),
|
||||
RecordEngagementUseCase: Symbol.for('RecordEngagementUseCase'),
|
||||
@@ -141,6 +154,7 @@ export const DI_TOKENS = {
|
||||
DriverStats: Symbol.for('DriverStats'),
|
||||
|
||||
// Presenters - Racing
|
||||
LeagueStandingsPresenter: Symbol.for('ILeagueStandingsPresenter'),
|
||||
RaceWithSOFPresenter: Symbol.for('IRaceWithSOFPresenter'),
|
||||
RaceProtestsPresenter: Symbol.for('IRaceProtestsPresenter'),
|
||||
RacePenaltiesPresenter: Symbol.for('IRacePenaltiesPresenter'),
|
||||
|
||||
@@ -1,41 +1,22 @@
|
||||
import type {
|
||||
ILeagueStandingsPresenter,
|
||||
LeagueStandingsResultDTO,
|
||||
LeagueStandingsViewModel,
|
||||
StandingItemViewModel,
|
||||
} from '@gridpilot/racing/application/presenters/ILeagueStandingsPresenter';
|
||||
import { GetLeagueStandingsUseCase, LeagueStandingsViewModel } from '@gridpilot/core/league/application/use-cases/GetLeagueStandingsUseCase';
|
||||
|
||||
export interface ILeagueStandingsPresenter {
|
||||
present(leagueId: string): Promise<void>;
|
||||
getViewModel(): LeagueStandingsViewModel | null;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
export class LeagueStandingsPresenter implements ILeagueStandingsPresenter {
|
||||
private viewModel: LeagueStandingsViewModel | null = null;
|
||||
|
||||
constructor(private getLeagueStandingsUseCase: GetLeagueStandingsUseCase) {}
|
||||
|
||||
reset(): void {
|
||||
this.viewModel = null;
|
||||
}
|
||||
|
||||
present(dto: LeagueStandingsResultDTO): void {
|
||||
const standingItems: StandingItemViewModel[] = dto.standings.map((standing) => {
|
||||
const raw = standing as unknown as {
|
||||
seasonId?: string;
|
||||
podiums?: number;
|
||||
};
|
||||
|
||||
return {
|
||||
id: standing.id,
|
||||
leagueId: standing.leagueId,
|
||||
seasonId: raw.seasonId ?? '',
|
||||
driverId: standing.driverId,
|
||||
position: standing.position,
|
||||
points: standing.points,
|
||||
wins: standing.wins,
|
||||
podiums: raw.podiums ?? 0,
|
||||
racesCompleted: standing.racesCompleted,
|
||||
};
|
||||
});
|
||||
|
||||
this.viewModel = {
|
||||
leagueId: dto.standings[0]?.leagueId ?? '',
|
||||
standings: standingItems,
|
||||
};
|
||||
async present(leagueId: string): Promise<void> {
|
||||
this.viewModel = await this.getLeagueStandingsUseCase.execute(leagueId);
|
||||
}
|
||||
|
||||
getViewModel(): LeagueStandingsViewModel | null {
|
||||
|
||||
@@ -28,13 +28,9 @@
|
||||
"@gridpilot/testing-support/*": ["../../core/testing-support/*"],
|
||||
"@gridpilot/media": ["../../core/media/index.ts"],
|
||||
"@gridpilot/media/*": ["../../core/media/*"],
|
||||
"@gridpilot/shared": ["../../core/shared/index.ts"],
|
||||
"@gridpilot/shared/application": ["../../core/shared/application"],
|
||||
"@gridpilot/shared/application/*": ["../../core/shared/application/*"],
|
||||
"@gridpilot/shared/presentation": ["../../core/shared/presentation"],
|
||||
"@gridpilot/shared/presentation/*": ["../../core/shared/presentation/*"],
|
||||
"@gridpilot/shared/domain": ["../../core/shared/domain"],
|
||||
"@gridpilot/shared/errors": ["../../core/shared/errors"]
|
||||
"@gridpilot/shared/logging": ["../../core/shared/logging"],
|
||||
"@gridpilot/shared/*": ["../../core/shared/*"],
|
||||
"@gridpilot/core/*": ["../../core/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
|
||||
Reference in New Issue
Block a user