refactor api modules
This commit is contained in:
@@ -1,24 +1,24 @@
|
|||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { HelloController } from './presentation/hello.controller';
|
|
||||||
import { HelloService } from './application/hello/hello.service';
|
|
||||||
import { AnalyticsModule } from './domain/analytics/AnalyticsModule';
|
import { AnalyticsModule } from './domain/analytics/AnalyticsModule';
|
||||||
import { DatabaseModule } from './infrastructure/database/database.module';
|
|
||||||
import { LoggingModule } from './infrastructure/logging/LoggingModule';
|
|
||||||
import { BootstrapModule } from './infrastructure/bootstrap/BootstrapModule';
|
|
||||||
import { AuthModule } from './domain/auth/AuthModule';
|
import { AuthModule } from './domain/auth/AuthModule';
|
||||||
|
import { BootstrapModule } from './domain/bootstrap/BootstrapModule';
|
||||||
import { DashboardModule } from './domain/dashboard/DashboardModule';
|
import { DashboardModule } from './domain/dashboard/DashboardModule';
|
||||||
import { LeagueModule } from './domain/league/LeagueModule';
|
import { DatabaseModule } from './domain/database/DatabaseModule';
|
||||||
import { RaceModule } from './domain/race/RaceModule';
|
|
||||||
import { ProtestsModule } from './domain/protests/ProtestsModule';
|
|
||||||
import { TeamModule } from './domain/team/TeamModule';
|
|
||||||
import { SponsorModule } from './domain/sponsor/SponsorModule';
|
|
||||||
import { DriverModule } from './domain/driver/DriverModule';
|
import { DriverModule } from './domain/driver/DriverModule';
|
||||||
|
import { HelloModule } from './domain/hello/HelloModule';
|
||||||
|
import { LeagueModule } from './domain/league/LeagueModule';
|
||||||
|
import { LoggingModule } from './domain/logging/LoggingModule';
|
||||||
import { MediaModule } from './domain/media/MediaModule';
|
import { MediaModule } from './domain/media/MediaModule';
|
||||||
import { PaymentsModule } from './domain/payments/PaymentsModule';
|
import { PaymentsModule } from './domain/payments/PaymentsModule';
|
||||||
|
import { ProtestsModule } from './domain/protests/ProtestsModule';
|
||||||
|
import { RaceModule } from './domain/race/RaceModule';
|
||||||
|
import { SponsorModule } from './domain/sponsor/SponsorModule';
|
||||||
|
import { TeamModule } from './domain/team/TeamModule';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
HelloModule,
|
||||||
DatabaseModule,
|
DatabaseModule,
|
||||||
LoggingModule,
|
LoggingModule,
|
||||||
BootstrapModule,
|
BootstrapModule,
|
||||||
@@ -34,7 +34,5 @@ import { PaymentsModule } from './domain/payments/PaymentsModule';
|
|||||||
MediaModule,
|
MediaModule,
|
||||||
PaymentsModule,
|
PaymentsModule,
|
||||||
],
|
],
|
||||||
controllers: [HelloController],
|
|
||||||
providers: [HelloService],
|
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { HelloService } from './hello.service';
|
|
||||||
import { HelloPresenter } from './presenters/HelloPresenter';
|
|
||||||
|
|
||||||
describe('HelloService', () => {
|
|
||||||
let service: HelloService;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
|
||||||
providers: [HelloService],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
service = module.get<HelloService>(HelloService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be defined', () => {
|
|
||||||
expect(service).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return "Hello World!"', () => {
|
|
||||||
const presenter = service.getHello();
|
|
||||||
expect(presenter.responseModel).toEqual({ message: 'Hello World!' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { Result } from '@core/shared/application/Result';
|
|
||||||
import { HelloPresenter, HelloResponseModel } from './presenters/HelloPresenter';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class HelloService {
|
|
||||||
constructor(private readonly presenter: HelloPresenter) {}
|
|
||||||
|
|
||||||
getHello(): HelloResponseModel {
|
|
||||||
const result = Result.ok('Hello World!');
|
|
||||||
this.presenter.present(result);
|
|
||||||
return this.presenter.responseModel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import type { Result } from '@core/shared/application/Result';
|
|
||||||
|
|
||||||
export interface HelloResponseModel {
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HelloPresenter {
|
|
||||||
private result: HelloResponseModel | null = null;
|
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
this.result = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
present(result: Result<string, Error>): void {
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw result.unwrapErr();
|
|
||||||
}
|
|
||||||
const message = result.unwrap();
|
|
||||||
this.result = { message };
|
|
||||||
}
|
|
||||||
|
|
||||||
get responseModel(): HelloResponseModel {
|
|
||||||
if (!this.result) {
|
|
||||||
throw new Error('HelloPresenter not presented');
|
|
||||||
}
|
|
||||||
return this.result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { EnsureInitialData } from '@/adapters/bootstrap/EnsureInitialData';
|
||||||
import { Module, OnModuleInit } from '@nestjs/common';
|
import { Module, OnModuleInit } from '@nestjs/common';
|
||||||
import { EnsureInitialData } from '../../../../../adapters/bootstrap/EnsureInitialData';
|
|
||||||
import { BootstrapProviders } from './BootstrapProviders';
|
import { BootstrapProviders } from './BootstrapProviders';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { AnalyticsSnapshotOrmEntity } from '../../../../..//persistence/typeorm/analytics/AnalyticsSnapshotOrmEntity';
|
|
||||||
import { EngagementOrmEntity } from '../../../../..//persistence/typeorm/analytics/EngagementOrmEntity';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -12,7 +10,7 @@ import { EngagementOrmEntity } from '../../../../..//persistence/typeorm/analyti
|
|||||||
username: process.env.DATABASE_USER || 'user',
|
username: process.env.DATABASE_USER || 'user',
|
||||||
password: process.env.DATABASE_PASSWORD || 'password',
|
password: process.env.DATABASE_PASSWORD || 'password',
|
||||||
database: process.env.DATABASE_NAME || 'gridpilot',
|
database: process.env.DATABASE_NAME || 'gridpilot',
|
||||||
entities: [AnalyticsSnapshotOrmEntity, EngagementOrmEntity],
|
// entities: [AnalyticsSnapshotOrmEntity, EngagementOrmEntity],
|
||||||
synchronize: true, // Use carefully in production
|
synchronize: true, // Use carefully in production
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
14
apps/api/src/domain/hello/HelloController.ts
Normal file
14
apps/api/src/domain/hello/HelloController.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import { HelloService } from './HelloService';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class HelloController {
|
||||||
|
constructor(private readonly helloService: HelloService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getHello() {
|
||||||
|
return this.helloService.getHello();
|
||||||
|
}
|
||||||
|
}
|
||||||
9
apps/api/src/domain/hello/HelloModule.ts
Normal file
9
apps/api/src/domain/hello/HelloModule.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { HelloController } from "./HelloController";
|
||||||
|
import { HelloService } from "./HelloService";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [HelloController],
|
||||||
|
exports: [HelloService],
|
||||||
|
})
|
||||||
|
export class HelloModule {}
|
||||||
11
apps/api/src/domain/hello/HelloService.ts
Normal file
11
apps/api/src/domain/hello/HelloService.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class HelloService {
|
||||||
|
|
||||||
|
getHello() {
|
||||||
|
return "Hello World";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,11 +13,15 @@ import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
|||||||
// Import use cases
|
// Import use cases
|
||||||
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
||||||
|
|
||||||
|
// Import presenters
|
||||||
|
import { ReviewProtestPresenter } from './presenters/ReviewProtestPresenter';
|
||||||
|
|
||||||
// Define injection tokens
|
// Define injection tokens
|
||||||
export const PROTEST_REPOSITORY_TOKEN = 'IProtestRepository';
|
export const PROTEST_REPOSITORY_TOKEN = 'IProtestRepository';
|
||||||
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
||||||
export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
|
export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
|
||||||
export const LOGGER_TOKEN = 'Logger';
|
export const LOGGER_TOKEN = 'Logger';
|
||||||
|
export const REVIEW_PROTEST_PRESENTER_TOKEN = 'ReviewProtestPresenter';
|
||||||
|
|
||||||
export const ProtestsProviders: Provider[] = [
|
export const ProtestsProviders: Provider[] = [
|
||||||
ProtestsService, // Provide the service itself
|
ProtestsService, // Provide the service itself
|
||||||
@@ -40,6 +44,26 @@ export const ProtestsProviders: Provider[] = [
|
|||||||
provide: LOGGER_TOKEN,
|
provide: LOGGER_TOKEN,
|
||||||
useClass: ConsoleLogger,
|
useClass: ConsoleLogger,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: REVIEW_PROTEST_PRESENTER_TOKEN,
|
||||||
|
useClass: ReviewProtestPresenter,
|
||||||
|
},
|
||||||
// Use cases
|
// Use cases
|
||||||
ReviewProtestUseCase,
|
{
|
||||||
|
provide: ReviewProtestUseCase,
|
||||||
|
useFactory: (
|
||||||
|
protestRepo: any,
|
||||||
|
raceRepo: any,
|
||||||
|
leagueMembershipRepo: any,
|
||||||
|
logger: Logger,
|
||||||
|
output: ReviewProtestPresenter,
|
||||||
|
) => new ReviewProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo, logger, output),
|
||||||
|
inject: [
|
||||||
|
PROTEST_REPOSITORY_TOKEN,
|
||||||
|
RACE_REPOSITORY_TOKEN,
|
||||||
|
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
||||||
|
LOGGER_TOKEN,
|
||||||
|
REVIEW_PROTEST_PRESENTER_TOKEN,
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
@@ -3,10 +3,10 @@ import { Result } from '@core/shared/application/Result';
|
|||||||
import type { Logger } from '@core/shared/application/Logger';
|
import type { Logger } from '@core/shared/application/Logger';
|
||||||
import type {
|
import type {
|
||||||
ReviewProtestUseCase,
|
ReviewProtestUseCase,
|
||||||
ReviewProtestResult,
|
|
||||||
ReviewProtestApplicationError,
|
ReviewProtestApplicationError,
|
||||||
} from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
} from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
||||||
import { ProtestsService } from './ProtestsService';
|
import { ProtestsService } from './ProtestsService';
|
||||||
|
import type { ReviewProtestPresenter } from './presenters/ReviewProtestPresenter';
|
||||||
import type { ReviewProtestResponseDTO } from './presenters/ReviewProtestPresenter';
|
import type { ReviewProtestResponseDTO } from './presenters/ReviewProtestPresenter';
|
||||||
|
|
||||||
describe('ProtestsService', () => {
|
describe('ProtestsService', () => {
|
||||||
@@ -17,6 +17,21 @@ describe('ProtestsService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
executeMock = vi.fn();
|
executeMock = vi.fn();
|
||||||
const reviewProtestUseCase = { execute: executeMock } as unknown as ReviewProtestUseCase;
|
const reviewProtestUseCase = { execute: executeMock } as unknown as ReviewProtestUseCase;
|
||||||
|
const reviewProtestPresenter = {
|
||||||
|
reset: vi.fn(),
|
||||||
|
setCommand: vi.fn(),
|
||||||
|
present: vi.fn(),
|
||||||
|
presentError: vi.fn(),
|
||||||
|
getResponseModel: vi.fn(),
|
||||||
|
get responseModel() {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
protestId: 'test',
|
||||||
|
stewardId: 'test',
|
||||||
|
decision: 'uphold' as const,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
} as unknown as ReviewProtestPresenter;
|
||||||
logger = {
|
logger = {
|
||||||
debug: vi.fn(),
|
debug: vi.fn(),
|
||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
@@ -24,7 +39,7 @@ describe('ProtestsService', () => {
|
|||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
} as unknown as Logger;
|
} as unknown as Logger;
|
||||||
|
|
||||||
service = new ProtestsService(reviewProtestUseCase, logger);
|
service = new ProtestsService(reviewProtestUseCase, reviewProtestPresenter, logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
const baseCommand = {
|
const baseCommand = {
|
||||||
@@ -35,15 +50,7 @@ describe('ProtestsService', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
it('returns DTO with success model on success', async () => {
|
it('returns DTO with success model on success', async () => {
|
||||||
const coreResult: ReviewProtestResult = {
|
executeMock.mockResolvedValue(Result.ok(undefined));
|
||||||
leagueId: 'league-1',
|
|
||||||
protestId: baseCommand.protestId,
|
|
||||||
status: 'upheld',
|
|
||||||
stewardId: baseCommand.stewardId,
|
|
||||||
decision: baseCommand.decision,
|
|
||||||
};
|
|
||||||
|
|
||||||
executeMock.mockResolvedValue(Result.ok<ReviewProtestResult, ReviewProtestApplicationError>(coreResult));
|
|
||||||
|
|
||||||
const dto = await service.reviewProtest(baseCommand);
|
const dto = await service.reviewProtest(baseCommand);
|
||||||
|
|
||||||
@@ -62,7 +69,7 @@ describe('ProtestsService', () => {
|
|||||||
details: { message: 'Protest not found' },
|
details: { message: 'Protest not found' },
|
||||||
};
|
};
|
||||||
|
|
||||||
executeMock.mockResolvedValue(Result.err<ReviewProtestResult, ReviewProtestApplicationError>(error));
|
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
|
||||||
|
|
||||||
const dto = await service.reviewProtest(baseCommand);
|
const dto = await service.reviewProtest(baseCommand);
|
||||||
|
|
||||||
@@ -79,7 +86,7 @@ describe('ProtestsService', () => {
|
|||||||
details: { message: 'Race not found for protest' },
|
details: { message: 'Race not found for protest' },
|
||||||
};
|
};
|
||||||
|
|
||||||
executeMock.mockResolvedValue(Result.err<ReviewProtestResult, ReviewProtestApplicationError>(error));
|
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
|
||||||
|
|
||||||
const dto = await service.reviewProtest(baseCommand);
|
const dto = await service.reviewProtest(baseCommand);
|
||||||
|
|
||||||
@@ -96,7 +103,7 @@ describe('ProtestsService', () => {
|
|||||||
details: { message: 'Steward is not authorized to review this protest' },
|
details: { message: 'Steward is not authorized to review this protest' },
|
||||||
};
|
};
|
||||||
|
|
||||||
executeMock.mockResolvedValue(Result.err<ReviewProtestResult, ReviewProtestApplicationError>(error));
|
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
|
||||||
|
|
||||||
const dto = await service.reviewProtest(baseCommand);
|
const dto = await service.reviewProtest(baseCommand);
|
||||||
|
|
||||||
@@ -114,7 +121,7 @@ describe('ProtestsService', () => {
|
|||||||
details: { message: 'Failed to review protest' },
|
details: { message: 'Failed to review protest' },
|
||||||
};
|
};
|
||||||
|
|
||||||
executeMock.mockResolvedValue(Result.err<ReviewProtestResult, ReviewProtestApplicationError>(error));
|
executeMock.mockResolvedValue(Result.err<void, ReviewProtestApplicationError>(error));
|
||||||
|
|
||||||
const dto = await service.reviewProtest(baseCommand);
|
const dto = await service.reviewProtest(baseCommand);
|
||||||
|
|
||||||
|
|||||||
@@ -8,17 +8,14 @@ import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewP
|
|||||||
import { ReviewProtestPresenter, type ReviewProtestResponseDTO } from './presenters/ReviewProtestPresenter';
|
import { ReviewProtestPresenter, type ReviewProtestResponseDTO } from './presenters/ReviewProtestPresenter';
|
||||||
|
|
||||||
// Tokens
|
// Tokens
|
||||||
import { LOGGER_TOKEN } from './ProtestsProviders';
|
import { LOGGER_TOKEN, REVIEW_PROTEST_PRESENTER_TOKEN } from './ProtestsProviders';
|
||||||
|
|
||||||
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
|
|
||||||
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
|
|
||||||
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
|
|
||||||
import { PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN } from './ProtestsProviders';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProtestsService {
|
export class ProtestsService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly reviewProtestUseCase: ReviewProtestUseCase,
|
private readonly reviewProtestUseCase: ReviewProtestUseCase,
|
||||||
|
@Inject(REVIEW_PROTEST_PRESENTER_TOKEN) private readonly reviewProtestPresenter: ReviewProtestPresenter,
|
||||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -30,11 +27,14 @@ export class ProtestsService {
|
|||||||
}): Promise<ReviewProtestResponseDTO> {
|
}): Promise<ReviewProtestResponseDTO> {
|
||||||
this.logger.debug('[ProtestsService] Reviewing protest:', command);
|
this.logger.debug('[ProtestsService] Reviewing protest:', command);
|
||||||
|
|
||||||
const result = await this.reviewProtestUseCase.execute(command);
|
// Set the command on the presenter so it can include stewardId and decision in the response
|
||||||
const presenter = new ReviewProtestPresenter();
|
this.reviewProtestPresenter.setCommand({
|
||||||
|
stewardId: command.stewardId,
|
||||||
|
decision: command.decision,
|
||||||
|
});
|
||||||
|
|
||||||
presenter.present(result);
|
await this.reviewProtestUseCase.execute(command);
|
||||||
|
|
||||||
return presenter.responseModel;
|
return this.reviewProtestPresenter.responseModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,16 +12,27 @@ export interface ReviewProtestResponseDTO {
|
|||||||
|
|
||||||
export class ReviewProtestPresenter implements UseCaseOutputPort<ReviewProtestResult> {
|
export class ReviewProtestPresenter implements UseCaseOutputPort<ReviewProtestResult> {
|
||||||
private model: ReviewProtestResponseDTO | null = null;
|
private model: ReviewProtestResponseDTO | null = null;
|
||||||
|
private command: { stewardId: string; decision: 'uphold' | 'dismiss' } | null = null;
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.model = null;
|
this.model = null;
|
||||||
|
this.command = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCommand(command: { stewardId: string; decision: 'uphold' | 'dismiss' }): void {
|
||||||
|
this.command = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(result: ReviewProtestResult): void {
|
present(result: ReviewProtestResult): void {
|
||||||
|
if (!this.command) {
|
||||||
|
throw new Error('Command must be set before presenting result');
|
||||||
|
}
|
||||||
|
|
||||||
this.model = {
|
this.model = {
|
||||||
success: true,
|
success: true,
|
||||||
protestId: result.protestId,
|
protestId: result.protestId,
|
||||||
decision: result.status === 'upheld' ? 'uphold' : 'dismiss',
|
stewardId: this.command.stewardId,
|
||||||
|
decision: this.command.decision,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,33 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { RaceController } from './RaceController';
|
import { RaceController } from './RaceController';
|
||||||
import { RaceService } from './RaceService';
|
import { RaceService } from './RaceService';
|
||||||
|
import { vi, Mocked } from 'vitest';
|
||||||
|
|
||||||
describe('RaceController', () => {
|
describe('RaceController', () => {
|
||||||
let controller: RaceController;
|
let controller: RaceController;
|
||||||
let service: jest.Mocked<RaceService>;
|
let service: Mocked<RaceService>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const mockService = {
|
const mockService = {
|
||||||
getAllRaces: jest.fn(),
|
getAllRaces: vi.fn(),
|
||||||
getTotalRaces: jest.fn(),
|
getTotalRaces: vi.fn(),
|
||||||
getRacesPageData: jest.fn(),
|
getRacesPageData: vi.fn(),
|
||||||
getAllRacesPageData: jest.fn(),
|
getAllRacesPageData: vi.fn(),
|
||||||
getRaceDetail: jest.fn(),
|
getRaceDetail: vi.fn(),
|
||||||
getRaceResultsDetail: jest.fn(),
|
getRaceResultsDetail: vi.fn(),
|
||||||
getRaceWithSOF: jest.fn(),
|
getRaceWithSOF: vi.fn(),
|
||||||
getRaceProtests: jest.fn(),
|
getRaceProtests: vi.fn(),
|
||||||
getRacePenalties: jest.fn(),
|
getRacePenalties: vi.fn(),
|
||||||
registerForRace: jest.fn(),
|
registerForRace: vi.fn(),
|
||||||
withdrawFromRace: jest.fn(),
|
withdrawFromRace: vi.fn(),
|
||||||
cancelRace: jest.fn(),
|
cancelRace: vi.fn(),
|
||||||
completeRace: jest.fn(),
|
completeRace: vi.fn(),
|
||||||
importRaceResults: jest.fn(),
|
importRaceResults: vi.fn(),
|
||||||
fileProtest: jest.fn(),
|
fileProtest: vi.fn(),
|
||||||
applyQuickPenalty: jest.fn(),
|
applyQuickPenalty: vi.fn(),
|
||||||
applyPenalty: jest.fn(),
|
applyPenalty: vi.fn(),
|
||||||
requestProtestDefense: jest.fn(),
|
requestProtestDefense: vi.fn(),
|
||||||
} as unknown as jest.Mocked<RaceService>;
|
} as unknown as Mocked<RaceService>;
|
||||||
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
controllers: [RaceController],
|
controllers: [RaceController],
|
||||||
@@ -39,7 +40,7 @@ describe('RaceController', () => {
|
|||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
controller = module.get<RaceController>(RaceController);
|
controller = module.get<RaceController>(RaceController);
|
||||||
service = module.get(RaceService) as jest.Mocked<RaceService>;
|
service = module.get(RaceService) as Mocked<RaceService>;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
@@ -48,25 +49,25 @@ describe('RaceController', () => {
|
|||||||
|
|
||||||
describe('getAllRaces', () => {
|
describe('getAllRaces', () => {
|
||||||
it('should return all races view model', async () => {
|
it('should return all races view model', async () => {
|
||||||
const mockViewModel = { races: [], filters: { statuses: [], leagues: [] } } as { races: unknown[]; filters: { statuses: unknown[]; leagues: unknown[] } };
|
const mockPresenter = { viewModel: { races: [], filters: { statuses: [], leagues: [] } } } as any;
|
||||||
service.getAllRaces.mockResolvedValue({ viewModel: mockViewModel } as unknown as ReturnType<RaceService['getAllRaces']>);
|
service.getAllRaces.mockResolvedValue(mockPresenter);
|
||||||
|
|
||||||
const result = await controller.getAllRaces();
|
const result = await controller.getAllRaces();
|
||||||
|
|
||||||
expect(service.getAllRaces).toHaveBeenCalled();
|
expect(service.getAllRaces).toHaveBeenCalled();
|
||||||
expect(result).toEqual(mockViewModel);
|
expect(result).toEqual(mockPresenter.viewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getTotalRaces', () => {
|
describe('getTotalRaces', () => {
|
||||||
it('should return total races count view model', async () => {
|
it('should return total races count view model', async () => {
|
||||||
const mockViewModel = { totalRaces: 5 } as { totalRaces: number };
|
const mockPresenter = { viewModel: { totalRaces: 5 } } as any;
|
||||||
service.getTotalRaces.mockResolvedValue({ viewModel: mockViewModel } as unknown as ReturnType<RaceService['getTotalRaces']>);
|
service.getTotalRaces.mockResolvedValue(mockPresenter);
|
||||||
|
|
||||||
const result = await controller.getTotalRaces();
|
const result = await controller.getTotalRaces();
|
||||||
|
|
||||||
expect(service.getTotalRaces).toHaveBeenCalled();
|
expect(service.getTotalRaces).toHaveBeenCalled();
|
||||||
expect(result).toEqual(mockViewModel);
|
expect(result).toEqual(mockPresenter.viewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -41,9 +41,10 @@ export class RaceController {
|
|||||||
|
|
||||||
@Get('page-data')
|
@Get('page-data')
|
||||||
@ApiOperation({ summary: 'Get races page data' })
|
@ApiOperation({ summary: 'Get races page data' })
|
||||||
|
@ApiQuery({ name: 'leagueId', description: 'League ID' })
|
||||||
@ApiResponse({ status: 200, description: 'Races page data', type: RacesPageDataDTO })
|
@ApiResponse({ status: 200, description: 'Races page data', type: RacesPageDataDTO })
|
||||||
async getRacesPageData(): Promise<RacesPageDataDTO> {
|
async getRacesPageData(@Query('leagueId') leagueId: string): Promise<RacesPageDataDTO> {
|
||||||
const presenter = await this.raceService.getRacesPageData();
|
const presenter = await this.raceService.getRacesPageData(leagueId);
|
||||||
return presenter.viewModel;
|
return presenter.viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ export class RaceController {
|
|||||||
@ApiParam({ name: 'raceId', description: 'Race ID' })
|
@ApiParam({ name: 'raceId', description: 'Race ID' })
|
||||||
@ApiResponse({ status: 200, description: 'Successfully cancelled race' })
|
@ApiResponse({ status: 200, description: 'Successfully cancelled race' })
|
||||||
async cancelRace(@Param('raceId') raceId: string): Promise<void> {
|
async cancelRace(@Param('raceId') raceId: string): Promise<void> {
|
||||||
const presenter = await this.raceService.cancelRace({ raceId });
|
const presenter = await this.raceService.cancelRace({ raceId }, '');
|
||||||
const viewModel = presenter.viewModel;
|
const viewModel = presenter.viewModel;
|
||||||
|
|
||||||
if (!viewModel.success) {
|
if (!viewModel.success) {
|
||||||
@@ -172,7 +173,7 @@ export class RaceController {
|
|||||||
@ApiParam({ name: 'raceId', description: 'Race ID' })
|
@ApiParam({ name: 'raceId', description: 'Race ID' })
|
||||||
@ApiResponse({ status: 200, description: 'Successfully re-opened race' })
|
@ApiResponse({ status: 200, description: 'Successfully re-opened race' })
|
||||||
async reopenRace(@Param('raceId') raceId: string): Promise<void> {
|
async reopenRace(@Param('raceId') raceId: string): Promise<void> {
|
||||||
const presenter = await this.raceService.reopenRace({ raceId });
|
const presenter = await this.raceService.reopenRace({ raceId }, '');
|
||||||
const viewModel = presenter.viewModel;
|
const viewModel = presenter.viewModel;
|
||||||
|
|
||||||
if (!viewModel.success) {
|
if (!viewModel.success) {
|
||||||
|
|||||||
@@ -2,54 +2,68 @@ import type { Provider } from '@nestjs/common';
|
|||||||
import { RaceService } from './RaceService';
|
import { RaceService } from './RaceService';
|
||||||
|
|
||||||
// Import core interfaces
|
// Import core interfaces
|
||||||
import type { Logger } from '@core/shared/application/Logger';
|
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
||||||
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
|
|
||||||
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
|
||||||
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
||||||
import type { IStandingRepository } from '@core/racing/domain/repositories/IStandingRepository';
|
|
||||||
import type { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
|
|
||||||
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
|
|
||||||
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
|
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
|
||||||
|
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
||||||
import type { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository';
|
import type { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository';
|
||||||
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
|
import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository';
|
||||||
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
import type { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
|
||||||
|
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
|
||||||
|
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
|
||||||
|
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 { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
import { getPointsSystems } from '@adapters/bootstrap/PointsSystems';
|
||||||
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||||
|
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
||||||
import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||||
import { InMemoryRaceRegistrationRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
|
||||||
import { InMemoryResultRepository } from '@adapters/racing/persistence/inmemory/InMemoryResultRepository';
|
|
||||||
import { InMemoryLeagueMembershipRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
import { InMemoryLeagueMembershipRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository';
|
||||||
|
import { InMemoryLeagueRepository } from '@adapters/racing/persistence/inmemory/InMemoryLeagueRepository';
|
||||||
import { InMemoryPenaltyRepository } from '@adapters/racing/persistence/inmemory/InMemoryPenaltyRepository';
|
import { InMemoryPenaltyRepository } from '@adapters/racing/persistence/inmemory/InMemoryPenaltyRepository';
|
||||||
import { InMemoryProtestRepository } from '@adapters/racing/persistence/inmemory/InMemoryProtestRepository';
|
import { InMemoryProtestRepository } from '@adapters/racing/persistence/inmemory/InMemoryProtestRepository';
|
||||||
|
import { InMemoryRaceRegistrationRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
||||||
|
import { InMemoryRaceRepository } from '@adapters/racing/persistence/inmemory/InMemoryRaceRepository';
|
||||||
|
import { InMemoryResultRepository } from '@adapters/racing/persistence/inmemory/InMemoryResultRepository';
|
||||||
import { InMemoryStandingRepository } from '@adapters/racing/persistence/inmemory/InMemoryStandingRepository';
|
import { InMemoryStandingRepository } from '@adapters/racing/persistence/inmemory/InMemoryStandingRepository';
|
||||||
import { InMemoryDriverRatingProvider } from '@adapters/racing/ports/InMemoryDriverRatingProvider';
|
import { InMemoryDriverRatingProvider } from '@adapters/racing/ports/InMemoryDriverRatingProvider';
|
||||||
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
|
||||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
|
||||||
|
|
||||||
// Import use cases
|
// Import use cases
|
||||||
import { GetAllRacesUseCase } from '@core/racing/application/use-cases/GetAllRacesUseCase';
|
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
|
||||||
import { GetTotalRacesUseCase } from '@core/racing/application/use-cases/GetTotalRacesUseCase';
|
|
||||||
import { ImportRaceResultsApiUseCase } from '@core/racing/application/use-cases/ImportRaceResultsApiUseCase';
|
|
||||||
import { GetRaceDetailUseCase } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
|
|
||||||
import { GetRacesPageDataUseCase } from '@core/racing/application/use-cases/GetRacesPageDataUseCase';
|
|
||||||
import { GetAllRacesPageDataUseCase } from '@core/racing/application/use-cases/GetAllRacesPageDataUseCase';
|
|
||||||
import { GetRaceResultsDetailUseCase } from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
|
|
||||||
import { GetRaceWithSOFUseCase } from '@core/racing/application/use-cases/GetRaceWithSOFUseCase';
|
|
||||||
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
|
||||||
import { GetRacePenaltiesUseCase } from '@core/racing/application/use-cases/GetRacePenaltiesUseCase';
|
|
||||||
import { RegisterForRaceUseCase } from '@core/racing/application/use-cases/RegisterForRaceUseCase';
|
|
||||||
import { WithdrawFromRaceUseCase } from '@core/racing/application/use-cases/WithdrawFromRaceUseCase';
|
|
||||||
import { CancelRaceUseCase } from '@core/racing/application/use-cases/CancelRaceUseCase';
|
import { CancelRaceUseCase } from '@core/racing/application/use-cases/CancelRaceUseCase';
|
||||||
import { CompleteRaceUseCase } from '@core/racing/application/use-cases/CompleteRaceUseCase';
|
import { CompleteRaceUseCase } from '@core/racing/application/use-cases/CompleteRaceUseCase';
|
||||||
import { ImportRaceResultsUseCase } from '@core/racing/application/use-cases/ImportRaceResultsUseCase';
|
|
||||||
import { FileProtestUseCase } from '@core/racing/application/use-cases/FileProtestUseCase';
|
import { FileProtestUseCase } from '@core/racing/application/use-cases/FileProtestUseCase';
|
||||||
|
import { GetAllRacesPageDataUseCase } from '@core/racing/application/use-cases/GetAllRacesPageDataUseCase';
|
||||||
|
import { GetAllRacesUseCase } from '@core/racing/application/use-cases/GetAllRacesUseCase';
|
||||||
|
import { GetRaceDetailUseCase } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
|
||||||
|
import { GetRacePenaltiesUseCase } from '@core/racing/application/use-cases/GetRacePenaltiesUseCase';
|
||||||
|
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
||||||
|
import { GetRaceResultsDetailUseCase } from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
|
||||||
|
import { GetRacesPageDataUseCase } from '@core/racing/application/use-cases/GetRacesPageDataUseCase';
|
||||||
|
import { GetRaceWithSOFUseCase } from '@core/racing/application/use-cases/GetRaceWithSOFUseCase';
|
||||||
|
import { GetTotalRacesUseCase } from '@core/racing/application/use-cases/GetTotalRacesUseCase';
|
||||||
|
import { ImportRaceResultsApiUseCase } from '@core/racing/application/use-cases/ImportRaceResultsApiUseCase';
|
||||||
|
import { ImportRaceResultsUseCase } from '@core/racing/application/use-cases/ImportRaceResultsUseCase';
|
||||||
import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPenaltyUseCase';
|
import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPenaltyUseCase';
|
||||||
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
|
import { RegisterForRaceUseCase } from '@core/racing/application/use-cases/RegisterForRaceUseCase';
|
||||||
|
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
|
||||||
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';
|
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';
|
||||||
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
||||||
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
|
import { WithdrawFromRaceUseCase } from '@core/racing/application/use-cases/WithdrawFromRaceUseCase';
|
||||||
|
|
||||||
|
// Import presenters
|
||||||
|
import { AllRacesPageDataPresenter } from './presenters/AllRacesPageDataPresenter';
|
||||||
|
import { CommandResultPresenter } from './presenters/CommandResultPresenter';
|
||||||
|
import { GetAllRacesPresenter } from './presenters/GetAllRacesPresenter';
|
||||||
|
import { GetTotalRacesPresenter } from './presenters/GetTotalRacesPresenter';
|
||||||
|
import { ImportRaceResultsApiPresenter } from './presenters/ImportRaceResultsApiPresenter';
|
||||||
|
import { RaceDetailPresenter } from './presenters/RaceDetailPresenter';
|
||||||
|
import { RacePenaltiesPresenter } from './presenters/RacePenaltiesPresenter';
|
||||||
|
import { RaceProtestsPresenter } from './presenters/RaceProtestsPresenter';
|
||||||
|
import { RaceResultsDetailPresenter } from './presenters/RaceResultsDetailPresenter';
|
||||||
|
import { RacesPageDataPresenter } from './presenters/RacesPageDataPresenter';
|
||||||
|
import { RaceWithSOFPresenter } from './presenters/RaceWithSOFPresenter';
|
||||||
|
|
||||||
// Define injection tokens
|
// Define injection tokens
|
||||||
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
||||||
@@ -65,6 +79,19 @@ export const DRIVER_RATING_PROVIDER_TOKEN = 'DriverRatingProvider';
|
|||||||
export const IMAGE_SERVICE_TOKEN = 'IImageServicePort';
|
export const IMAGE_SERVICE_TOKEN = 'IImageServicePort';
|
||||||
export const LOGGER_TOKEN = 'Logger';
|
export const LOGGER_TOKEN = 'Logger';
|
||||||
|
|
||||||
|
// Presenter tokens
|
||||||
|
export const GET_ALL_RACES_PRESENTER_TOKEN = 'GetAllRacesPresenter';
|
||||||
|
export const GET_TOTAL_RACES_PRESENTER_TOKEN = 'GetTotalRacesPresenter';
|
||||||
|
export const IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN = 'ImportRaceResultsApiPresenter';
|
||||||
|
export const RACE_DETAIL_PRESENTER_TOKEN = 'RaceDetailPresenter';
|
||||||
|
export const RACES_PAGE_DATA_PRESENTER_TOKEN = 'RacesPageDataPresenter';
|
||||||
|
export const ALL_RACES_PAGE_DATA_PRESENTER_TOKEN = 'AllRacesPageDataPresenter';
|
||||||
|
export const RACE_RESULTS_DETAIL_PRESENTER_TOKEN = 'RaceResultsDetailPresenter';
|
||||||
|
export const RACE_WITH_SOF_PRESENTER_TOKEN = 'RaceWithSOFPresenter';
|
||||||
|
export const RACE_PROTESTS_PRESENTER_TOKEN = 'RaceProtestsPresenter';
|
||||||
|
export const RACE_PENALTIES_PRESENTER_TOKEN = 'RacePenaltiesPresenter';
|
||||||
|
export const COMMAND_RESULT_PRESENTER_TOKEN = 'CommandResultPresenter';
|
||||||
|
|
||||||
export const RaceProviders: Provider[] = [
|
export const RaceProviders: Provider[] = [
|
||||||
RaceService,
|
RaceService,
|
||||||
{
|
{
|
||||||
@@ -109,7 +136,7 @@ export const RaceProviders: Provider[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: STANDING_REPOSITORY_TOKEN,
|
provide: STANDING_REPOSITORY_TOKEN,
|
||||||
useFactory: (logger: Logger) => new InMemoryStandingRepository(logger),
|
useFactory: (logger: Logger) => new InMemoryStandingRepository(logger, getPointsSystems()),
|
||||||
inject: [LOGGER_TOKEN],
|
inject: [LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -126,18 +153,105 @@ export const RaceProviders: Provider[] = [
|
|||||||
provide: LOGGER_TOKEN,
|
provide: LOGGER_TOKEN,
|
||||||
useClass: ConsoleLogger,
|
useClass: ConsoleLogger,
|
||||||
},
|
},
|
||||||
// Use cases
|
// Presenters
|
||||||
|
{
|
||||||
|
provide: GET_ALL_RACES_PRESENTER_TOKEN,
|
||||||
|
useClass: GetAllRacesPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GET_TOTAL_RACES_PRESENTER_TOKEN,
|
||||||
|
useClass: GetTotalRacesPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN,
|
||||||
|
useClass: ImportRaceResultsApiPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RACE_DETAIL_PRESENTER_TOKEN,
|
||||||
|
useFactory: (driverRatingProvider: DriverRatingProvider, imageService: any) =>
|
||||||
|
new RaceDetailPresenter(driverRatingProvider, imageService, { raceId: '', driverId: '' }),
|
||||||
|
inject: [DRIVER_RATING_PROVIDER_TOKEN, IMAGE_SERVICE_TOKEN],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||||
|
useClass: RacesPageDataPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ALL_RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||||
|
useClass: AllRacesPageDataPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RACE_RESULTS_DETAIL_PRESENTER_TOKEN,
|
||||||
|
useFactory: (imageService: any) => new RaceResultsDetailPresenter(imageService),
|
||||||
|
inject: [IMAGE_SERVICE_TOKEN],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RACE_WITH_SOF_PRESENTER_TOKEN,
|
||||||
|
useClass: RaceWithSOFPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RACE_PROTESTS_PRESENTER_TOKEN,
|
||||||
|
useClass: RaceProtestsPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RACE_PENALTIES_PRESENTER_TOKEN,
|
||||||
|
useClass: RacePenaltiesPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: COMMAND_RESULT_PRESENTER_TOKEN,
|
||||||
|
useClass: CommandResultPresenter,
|
||||||
|
},
|
||||||
|
// Use cases - using simplified approach since presenters need to be adapted
|
||||||
{
|
{
|
||||||
provide: GetAllRacesUseCase,
|
provide: GetAllRacesUseCase,
|
||||||
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository, logger: Logger) =>
|
useFactory: (
|
||||||
new GetAllRacesUseCase(raceRepo, leagueRepo, logger),
|
raceRepo: IRaceRepository,
|
||||||
|
leagueRepo: ILeagueRepository,
|
||||||
|
logger: Logger,
|
||||||
|
) => {
|
||||||
|
const useCase = new GetAllRacesUseCase(raceRepo, leagueRepo, logger);
|
||||||
|
// Create a simple wrapper that calls the presenter
|
||||||
|
const wrapper = {
|
||||||
|
present: (data: any) => {
|
||||||
|
const presenter = new GetAllRacesPresenter();
|
||||||
|
presenter.present(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
useCase.setOutput(wrapper as any);
|
||||||
|
return useCase;
|
||||||
|
},
|
||||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GetTotalRacesUseCase,
|
provide: GetTotalRacesUseCase,
|
||||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => new GetTotalRacesUseCase(raceRepo, logger),
|
useFactory: (raceRepo: IRaceRepository, logger: Logger) => {
|
||||||
|
const presenter = new GetTotalRacesPresenter();
|
||||||
|
return new GetTotalRacesUseCase(raceRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: ImportRaceResultsApiUseCase,
|
||||||
|
useFactory: (
|
||||||
|
raceRepo: IRaceRepository,
|
||||||
|
leagueRepo: ILeagueRepository,
|
||||||
|
resultRepo: IResultRepository,
|
||||||
|
driverRepo: IDriverRepository,
|
||||||
|
standingRepo: IStandingRepository,
|
||||||
|
logger: Logger,
|
||||||
|
) => {
|
||||||
|
const presenter = new ImportRaceResultsApiPresenter();
|
||||||
|
return new ImportRaceResultsApiUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
RACE_REPOSITORY_TOKEN,
|
||||||
|
LEAGUE_REPOSITORY_TOKEN,
|
||||||
|
RESULT_REPOSITORY_TOKEN,
|
||||||
|
DRIVER_REPOSITORY_TOKEN,
|
||||||
|
STANDING_REPOSITORY_TOKEN,
|
||||||
|
LOGGER_TOKEN,
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: GetRaceDetailUseCase,
|
provide: GetRaceDetailUseCase,
|
||||||
useFactory: (
|
useFactory: (
|
||||||
@@ -147,15 +261,24 @@ export const RaceProviders: Provider[] = [
|
|||||||
raceRegRepo: IRaceRegistrationRepository,
|
raceRegRepo: IRaceRegistrationRepository,
|
||||||
resultRepo: IResultRepository,
|
resultRepo: IResultRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
) =>
|
) => {
|
||||||
new GetRaceDetailUseCase(
|
const useCase = new GetRaceDetailUseCase(
|
||||||
raceRepo,
|
raceRepo,
|
||||||
leagueRepo,
|
leagueRepo,
|
||||||
driverRepo,
|
driverRepo,
|
||||||
raceRegRepo,
|
raceRegRepo,
|
||||||
resultRepo,
|
resultRepo,
|
||||||
leagueMembershipRepo,
|
leagueMembershipRepo,
|
||||||
),
|
);
|
||||||
|
const wrapper = {
|
||||||
|
present: (data: any) => {
|
||||||
|
const presenter = new RaceDetailPresenter({} as any, {} as any, {} as any);
|
||||||
|
presenter.present(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
useCase.setOutput(wrapper as any);
|
||||||
|
return useCase;
|
||||||
|
},
|
||||||
inject: [
|
inject: [
|
||||||
RACE_REPOSITORY_TOKEN,
|
RACE_REPOSITORY_TOKEN,
|
||||||
LEAGUE_REPOSITORY_TOKEN,
|
LEAGUE_REPOSITORY_TOKEN,
|
||||||
@@ -167,15 +290,27 @@ export const RaceProviders: Provider[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GetRacesPageDataUseCase,
|
provide: GetRacesPageDataUseCase,
|
||||||
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) =>
|
useFactory: (
|
||||||
new GetRacesPageDataUseCase(raceRepo, leagueRepo),
|
raceRepo: IRaceRepository,
|
||||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN],
|
leagueRepo: ILeagueRepository,
|
||||||
|
logger: Logger,
|
||||||
|
) => {
|
||||||
|
const presenter = new RacesPageDataPresenter();
|
||||||
|
return new GetRacesPageDataUseCase(raceRepo, leagueRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
|
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GetAllRacesPageDataUseCase,
|
provide: GetAllRacesPageDataUseCase,
|
||||||
useFactory: (raceRepo: IRaceRepository, leagueRepo: ILeagueRepository) =>
|
useFactory: (
|
||||||
new GetAllRacesPageDataUseCase(raceRepo, leagueRepo),
|
raceRepo: IRaceRepository,
|
||||||
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN],
|
leagueRepo: ILeagueRepository,
|
||||||
|
logger: Logger,
|
||||||
|
) => {
|
||||||
|
const presenter = new AllRacesPageDataPresenter();
|
||||||
|
return new GetAllRacesPageDataUseCase(raceRepo, leagueRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
|
inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GetRaceResultsDetailUseCase,
|
provide: GetRaceResultsDetailUseCase,
|
||||||
@@ -185,7 +320,10 @@ export const RaceProviders: Provider[] = [
|
|||||||
resultRepo: IResultRepository,
|
resultRepo: IResultRepository,
|
||||||
driverRepo: IDriverRepository,
|
driverRepo: IDriverRepository,
|
||||||
penaltyRepo: IPenaltyRepository,
|
penaltyRepo: IPenaltyRepository,
|
||||||
) => new GetRaceResultsDetailUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, penaltyRepo),
|
) => {
|
||||||
|
const presenter = new RaceResultsDetailPresenter({} as any);
|
||||||
|
return new GetRaceResultsDetailUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, penaltyRepo, presenter as any);
|
||||||
|
},
|
||||||
inject: [
|
inject: [
|
||||||
RACE_REPOSITORY_TOKEN,
|
RACE_REPOSITORY_TOKEN,
|
||||||
LEAGUE_REPOSITORY_TOKEN,
|
LEAGUE_REPOSITORY_TOKEN,
|
||||||
@@ -201,7 +339,10 @@ export const RaceProviders: Provider[] = [
|
|||||||
raceRegRepo: IRaceRegistrationRepository,
|
raceRegRepo: IRaceRegistrationRepository,
|
||||||
resultRepo: IResultRepository,
|
resultRepo: IResultRepository,
|
||||||
driverRatingProvider: DriverRatingProvider,
|
driverRatingProvider: DriverRatingProvider,
|
||||||
) => new GetRaceWithSOFUseCase(raceRepo, raceRegRepo, resultRepo, driverRatingProvider),
|
) => {
|
||||||
|
const presenter = new RaceWithSOFPresenter();
|
||||||
|
return new GetRaceWithSOFUseCase(raceRepo, raceRegRepo, resultRepo, driverRatingProvider as any, presenter as any);
|
||||||
|
},
|
||||||
inject: [
|
inject: [
|
||||||
RACE_REPOSITORY_TOKEN,
|
RACE_REPOSITORY_TOKEN,
|
||||||
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
||||||
@@ -211,14 +352,18 @@ export const RaceProviders: Provider[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GetRaceProtestsUseCase,
|
provide: GetRaceProtestsUseCase,
|
||||||
useFactory: (protestRepo: IProtestRepository, driverRepo: IDriverRepository) =>
|
useFactory: (protestRepo: IProtestRepository, driverRepo: IDriverRepository) => {
|
||||||
new GetRaceProtestsUseCase(protestRepo, driverRepo),
|
const presenter = new RaceProtestsPresenter();
|
||||||
|
return new GetRaceProtestsUseCase(protestRepo, driverRepo, presenter as any);
|
||||||
|
},
|
||||||
inject: [PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
|
inject: [PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GetRacePenaltiesUseCase,
|
provide: GetRacePenaltiesUseCase,
|
||||||
useFactory: (penaltyRepo: IPenaltyRepository, driverRepo: IDriverRepository) =>
|
useFactory: (penaltyRepo: IPenaltyRepository, driverRepo: IDriverRepository) => {
|
||||||
new GetRacePenaltiesUseCase(penaltyRepo, driverRepo),
|
const presenter = new RacePenaltiesPresenter();
|
||||||
|
return new GetRacePenaltiesUseCase(penaltyRepo, driverRepo, presenter as any);
|
||||||
|
},
|
||||||
inject: [PENALTY_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
|
inject: [PENALTY_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -227,17 +372,30 @@ export const RaceProviders: Provider[] = [
|
|||||||
raceRegRepo: IRaceRegistrationRepository,
|
raceRegRepo: IRaceRegistrationRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
) => new RegisterForRaceUseCase(raceRegRepo, leagueMembershipRepo, logger),
|
) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new RegisterForRaceUseCase(raceRegRepo, leagueMembershipRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
inject: [RACE_REGISTRATION_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
inject: [RACE_REGISTRATION_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: WithdrawFromRaceUseCase,
|
provide: WithdrawFromRaceUseCase,
|
||||||
useFactory: (raceRegRepo: IRaceRegistrationRepository) => new WithdrawFromRaceUseCase(raceRegRepo),
|
useFactory: (
|
||||||
inject: [RACE_REGISTRATION_REPOSITORY_TOKEN],
|
raceRepo: IRaceRepository,
|
||||||
|
raceRegRepo: IRaceRegistrationRepository,
|
||||||
|
logger: Logger,
|
||||||
|
) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new WithdrawFromRaceUseCase(raceRepo, raceRegRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
|
inject: [RACE_REPOSITORY_TOKEN, RACE_REGISTRATION_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: CancelRaceUseCase,
|
provide: CancelRaceUseCase,
|
||||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => new CancelRaceUseCase(raceRepo, logger),
|
useFactory: (raceRepo: IRaceRepository, logger: Logger) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new CancelRaceUseCase(raceRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -248,7 +406,10 @@ export const RaceProviders: Provider[] = [
|
|||||||
resultRepo: IResultRepository,
|
resultRepo: IResultRepository,
|
||||||
standingRepo: IStandingRepository,
|
standingRepo: IStandingRepository,
|
||||||
driverRatingProvider: DriverRatingProvider,
|
driverRatingProvider: DriverRatingProvider,
|
||||||
) => new CompleteRaceUseCase(raceRepo, raceRegRepo, resultRepo, standingRepo, driverRatingProvider),
|
) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new CompleteRaceUseCase(raceRepo, raceRegRepo, resultRepo, standingRepo, driverRatingProvider as any, presenter as any);
|
||||||
|
},
|
||||||
inject: [
|
inject: [
|
||||||
RACE_REPOSITORY_TOKEN,
|
RACE_REPOSITORY_TOKEN,
|
||||||
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
||||||
@@ -259,28 +420,12 @@ export const RaceProviders: Provider[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: ReopenRaceUseCase,
|
provide: ReopenRaceUseCase,
|
||||||
useFactory: (raceRepo: IRaceRepository, logger: Logger) => new ReopenRaceUseCase(raceRepo, logger),
|
useFactory: (raceRepo: IRaceRepository, logger: Logger) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new ReopenRaceUseCase(raceRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: ImportRaceResultsApiUseCase,
|
|
||||||
useFactory: (
|
|
||||||
raceRepo: IRaceRepository,
|
|
||||||
leagueRepo: ILeagueRepository,
|
|
||||||
resultRepo: IResultRepository,
|
|
||||||
driverRepo: IDriverRepository,
|
|
||||||
standingRepo: IStandingRepository,
|
|
||||||
logger: Logger,
|
|
||||||
) => new ImportRaceResultsApiUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger),
|
|
||||||
inject: [
|
|
||||||
RACE_REPOSITORY_TOKEN,
|
|
||||||
LEAGUE_REPOSITORY_TOKEN,
|
|
||||||
RESULT_REPOSITORY_TOKEN,
|
|
||||||
DRIVER_REPOSITORY_TOKEN,
|
|
||||||
STANDING_REPOSITORY_TOKEN,
|
|
||||||
LOGGER_TOKEN,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: ImportRaceResultsUseCase,
|
provide: ImportRaceResultsUseCase,
|
||||||
useFactory: (
|
useFactory: (
|
||||||
@@ -290,7 +435,10 @@ export const RaceProviders: Provider[] = [
|
|||||||
driverRepo: IDriverRepository,
|
driverRepo: IDriverRepository,
|
||||||
standingRepo: IStandingRepository,
|
standingRepo: IStandingRepository,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
) => new ImportRaceResultsUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger),
|
) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new ImportRaceResultsUseCase(raceRepo, leagueRepo, resultRepo, driverRepo, standingRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
inject: [
|
inject: [
|
||||||
RACE_REPOSITORY_TOKEN,
|
RACE_REPOSITORY_TOKEN,
|
||||||
LEAGUE_REPOSITORY_TOKEN,
|
LEAGUE_REPOSITORY_TOKEN,
|
||||||
@@ -306,7 +454,10 @@ export const RaceProviders: Provider[] = [
|
|||||||
protestRepo: IProtestRepository,
|
protestRepo: IProtestRepository,
|
||||||
raceRepo: IRaceRepository,
|
raceRepo: IRaceRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
) => new FileProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
|
) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new FileProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo, presenter as any);
|
||||||
|
},
|
||||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
|
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -316,13 +467,11 @@ export const RaceProviders: Provider[] = [
|
|||||||
raceRepo: IRaceRepository,
|
raceRepo: IRaceRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
) => new QuickPenaltyUseCase(penaltyRepo, raceRepo, leagueMembershipRepo, logger),
|
) => {
|
||||||
inject: [
|
const presenter = new CommandResultPresenter();
|
||||||
PENALTY_REPOSITORY_TOKEN,
|
return new QuickPenaltyUseCase(penaltyRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||||
RACE_REPOSITORY_TOKEN,
|
},
|
||||||
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
inject: [PENALTY_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
LOGGER_TOKEN,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: ApplyPenaltyUseCase,
|
provide: ApplyPenaltyUseCase,
|
||||||
@@ -332,14 +481,11 @@ export const RaceProviders: Provider[] = [
|
|||||||
raceRepo: IRaceRepository,
|
raceRepo: IRaceRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
) => new ApplyPenaltyUseCase(penaltyRepo, protestRepo, raceRepo, leagueMembershipRepo, logger),
|
) => {
|
||||||
inject: [
|
const presenter = new CommandResultPresenter();
|
||||||
PENALTY_REPOSITORY_TOKEN,
|
return new ApplyPenaltyUseCase(penaltyRepo, protestRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||||
PROTEST_REPOSITORY_TOKEN,
|
},
|
||||||
RACE_REPOSITORY_TOKEN,
|
inject: [PENALTY_REPOSITORY_TOKEN, PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
|
||||||
LOGGER_TOKEN,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: RequestProtestDefenseUseCase,
|
provide: RequestProtestDefenseUseCase,
|
||||||
@@ -347,8 +493,12 @@ export const RaceProviders: Provider[] = [
|
|||||||
protestRepo: IProtestRepository,
|
protestRepo: IProtestRepository,
|
||||||
raceRepo: IRaceRepository,
|
raceRepo: IRaceRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
) => new RequestProtestDefenseUseCase(protestRepo, raceRepo, leagueMembershipRepo),
|
logger: Logger,
|
||||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
|
) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new RequestProtestDefenseUseCase(protestRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
|
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: ReviewProtestUseCase,
|
provide: ReviewProtestUseCase,
|
||||||
@@ -356,7 +506,11 @@ export const RaceProviders: Provider[] = [
|
|||||||
protestRepo: IProtestRepository,
|
protestRepo: IProtestRepository,
|
||||||
raceRepo: IRaceRepository,
|
raceRepo: IRaceRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
) => new ReviewProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo),
|
logger: Logger,
|
||||||
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
|
) => {
|
||||||
|
const presenter = new CommandResultPresenter();
|
||||||
|
return new ReviewProtestUseCase(protestRepo, raceRepo, leagueMembershipRepo, logger, presenter as any);
|
||||||
|
},
|
||||||
|
inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
import { RaceService } from './RaceService';
|
|
||||||
import { GetAllRacesUseCase } from '@core/racing/application/use-cases/GetAllRacesUseCase';
|
|
||||||
import { GetTotalRacesUseCase } from '@core/racing/application/use-cases/GetTotalRacesUseCase';
|
|
||||||
import { ImportRaceResultsApiUseCase } from '@core/racing/application/use-cases/ImportRaceResultsApiUseCase';
|
|
||||||
import { GetRaceDetailUseCase } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
|
|
||||||
import { GetRacesPageDataUseCase } from '@core/racing/application/use-cases/GetRacesPageDataUseCase';
|
|
||||||
import { GetAllRacesPageDataUseCase } from '@core/racing/application/use-cases/GetAllRacesPageDataUseCase';
|
|
||||||
import { GetRaceResultsDetailUseCase } from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
|
|
||||||
import { GetRaceWithSOFUseCase } from '@core/racing/application/use-cases/GetRaceWithSOFUseCase';
|
|
||||||
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
|
|
||||||
import { GetRacePenaltiesUseCase } from '@core/racing/application/use-cases/GetRacePenaltiesUseCase';
|
|
||||||
import { RegisterForRaceUseCase } from '@core/racing/application/use-cases/RegisterForRaceUseCase';
|
|
||||||
import { WithdrawFromRaceUseCase } from '@core/racing/application/use-cases/WithdrawFromRaceUseCase';
|
|
||||||
import { CancelRaceUseCase } from '@core/racing/application/use-cases/CancelRaceUseCase';
|
|
||||||
import { CompleteRaceUseCase } from '@core/racing/application/use-cases/CompleteRaceUseCase';
|
|
||||||
import { FileProtestUseCase } from '@core/racing/application/use-cases/FileProtestUseCase';
|
|
||||||
import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPenaltyUseCase';
|
|
||||||
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
|
|
||||||
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';
|
|
||||||
import { ReviewProtestUseCase } from '@core/racing/application/use-cases/ReviewProtestUseCase';
|
|
||||||
import { ReopenRaceUseCase } from '@core/racing/application/use-cases/ReopenRaceUseCase';
|
|
||||||
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
|
||||||
import type { Logger } from '@core/shared/application/Logger';
|
|
||||||
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
|
||||||
import type { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
|
||||||
import { Result } from '@core/shared/application/Result';
|
|
||||||
|
|
||||||
// Minimal happy-path coverage to assert presenter usage
|
|
||||||
|
|
||||||
describe('RaceService', () => {
|
|
||||||
let service: RaceService;
|
|
||||||
let getAllRacesUseCase: jest.Mocked<GetAllRacesUseCase>;
|
|
||||||
let getTotalRacesUseCase: jest.Mocked<GetTotalRacesUseCase>;
|
|
||||||
let importRaceResultsApiUseCase: jest.Mocked<ImportRaceResultsApiUseCase>;
|
|
||||||
let getRaceDetailUseCase: jest.Mocked<GetRaceDetailUseCase>;
|
|
||||||
let getRacesPageDataUseCase: jest.Mocked<GetRacesPageDataUseCase>;
|
|
||||||
let getAllRacesPageDataUseCase: jest.Mocked<GetAllRacesPageDataUseCase>;
|
|
||||||
let getRaceResultsDetailUseCase: jest.Mocked<GetRaceResultsDetailUseCase>;
|
|
||||||
let getRaceWithSOFUseCase: jest.Mocked<GetRaceWithSOFUseCase>;
|
|
||||||
let getRaceProtestsUseCase: jest.Mocked<GetRaceProtestsUseCase>;
|
|
||||||
let getRacePenaltiesUseCase: jest.Mocked<GetRacePenaltiesUseCase>;
|
|
||||||
let registerForRaceUseCase: jest.Mocked<RegisterForRaceUseCase>;
|
|
||||||
let withdrawFromRaceUseCase: jest.Mocked<WithdrawFromRaceUseCase>;
|
|
||||||
let cancelRaceUseCase: jest.Mocked<CancelRaceUseCase>;
|
|
||||||
let completeRaceUseCase: jest.Mocked<CompleteRaceUseCase>;
|
|
||||||
let fileProtestUseCase: jest.Mocked<FileProtestUseCase>;
|
|
||||||
let quickPenaltyUseCase: jest.Mocked<QuickPenaltyUseCase>;
|
|
||||||
let applyPenaltyUseCase: jest.Mocked<ApplyPenaltyUseCase>;
|
|
||||||
let requestProtestDefenseUseCase: jest.Mocked<RequestProtestDefenseUseCase>;
|
|
||||||
let reviewProtestUseCase: jest.Mocked<ReviewProtestUseCase>;
|
|
||||||
let reopenRaceUseCase: jest.Mocked<ReopenRaceUseCase>;
|
|
||||||
let leagueRepository: jest.Mocked<ILeagueRepository>;
|
|
||||||
let logger: jest.Mocked<Logger>;
|
|
||||||
let driverRatingProvider: jest.Mocked<DriverRatingProvider>;
|
|
||||||
let imageService: jest.Mocked<IImageServicePort>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
getAllRacesUseCase = { execute: jest.fn() } as jest.Mocked<GetAllRacesUseCase>;
|
|
||||||
getTotalRacesUseCase = { execute: jest.fn() } as jest.Mocked<GetTotalRacesUseCase>;
|
|
||||||
importRaceResultsApiUseCase = { execute: jest.fn() } as jest.Mocked<ImportRaceResultsApiUseCase>;
|
|
||||||
getRaceDetailUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceDetailUseCase>;
|
|
||||||
getRacesPageDataUseCase = { execute: jest.fn() } as jest.Mocked<GetRacesPageDataUseCase>;
|
|
||||||
getAllRacesPageDataUseCase = { execute: jest.fn() } as jest.Mocked<GetAllRacesPageDataUseCase>;
|
|
||||||
getRaceResultsDetailUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceResultsDetailUseCase>;
|
|
||||||
getRaceWithSOFUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceWithSOFUseCase>;
|
|
||||||
getRaceProtestsUseCase = { execute: jest.fn() } as jest.Mocked<GetRaceProtestsUseCase>;
|
|
||||||
getRacePenaltiesUseCase = { execute: jest.fn() } as jest.Mocked<GetRacePenaltiesUseCase>;
|
|
||||||
registerForRaceUseCase = { execute: jest.fn() } as jest.Mocked<RegisterForRaceUseCase>;
|
|
||||||
withdrawFromRaceUseCase = { execute: jest.fn() } as jest.Mocked<WithdrawFromRaceUseCase>;
|
|
||||||
cancelRaceUseCase = { execute: jest.fn() } as jest.Mocked<CancelRaceUseCase>;
|
|
||||||
completeRaceUseCase = { execute: jest.fn() } as jest.Mocked<CompleteRaceUseCase>;
|
|
||||||
fileProtestUseCase = { execute: jest.fn() } as jest.Mocked<FileProtestUseCase>;
|
|
||||||
quickPenaltyUseCase = { execute: jest.fn() } as jest.Mocked<QuickPenaltyUseCase>;
|
|
||||||
applyPenaltyUseCase = { execute: jest.fn() } as jest.Mocked<ApplyPenaltyUseCase>;
|
|
||||||
requestProtestDefenseUseCase = { execute: jest.fn() } as jest.Mocked<RequestProtestDefenseUseCase>;
|
|
||||||
reviewProtestUseCase = { execute: jest.fn() } as jest.Mocked<ReviewProtestUseCase>;
|
|
||||||
reopenRaceUseCase = { execute: jest.fn() } as jest.Mocked<ReopenRaceUseCase>;
|
|
||||||
|
|
||||||
leagueRepository = {
|
|
||||||
findAll: jest.fn(),
|
|
||||||
} as jest.Mocked<ILeagueRepository>;
|
|
||||||
|
|
||||||
logger = {
|
|
||||||
debug: jest.fn(),
|
|
||||||
info: jest.fn(),
|
|
||||||
warn: jest.fn(),
|
|
||||||
error: jest.fn(),
|
|
||||||
} as jest.Mocked<Logger>;
|
|
||||||
|
|
||||||
driverRatingProvider = {
|
|
||||||
getDriverRating: jest.fn(),
|
|
||||||
} as jest.Mocked<DriverRatingProvider>;
|
|
||||||
|
|
||||||
imageService = {
|
|
||||||
getDriverAvatar: jest.fn(),
|
|
||||||
getTeamLogo: jest.fn(),
|
|
||||||
getLeagueCover: jest.fn(),
|
|
||||||
getLeagueLogo: jest.fn(),
|
|
||||||
} as jest.Mocked<IImageServicePort>;
|
|
||||||
|
|
||||||
service = new RaceService(
|
|
||||||
getAllRacesUseCase,
|
|
||||||
getTotalRacesUseCase,
|
|
||||||
importRaceResultsApiUseCase,
|
|
||||||
getRaceDetailUseCase,
|
|
||||||
getRacesPageDataUseCase,
|
|
||||||
getAllRacesPageDataUseCase,
|
|
||||||
getRaceResultsDetailUseCase,
|
|
||||||
getRaceWithSOFUseCase,
|
|
||||||
getRaceProtestsUseCase,
|
|
||||||
getRacePenaltiesUseCase,
|
|
||||||
registerForRaceUseCase,
|
|
||||||
withdrawFromRaceUseCase,
|
|
||||||
cancelRaceUseCase,
|
|
||||||
completeRaceUseCase,
|
|
||||||
fileProtestUseCase,
|
|
||||||
quickPenaltyUseCase,
|
|
||||||
applyPenaltyUseCase,
|
|
||||||
requestProtestDefenseUseCase,
|
|
||||||
reviewProtestUseCase,
|
|
||||||
reopenRaceUseCase,
|
|
||||||
leagueRepository,
|
|
||||||
logger,
|
|
||||||
driverRatingProvider,
|
|
||||||
imageService,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getAllRaces should return presenter with view model', async () => {
|
|
||||||
const output = {
|
|
||||||
races: [],
|
|
||||||
totalCount: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
(getAllRacesUseCase.execute as jest.Mock).mockResolvedValue(Result.ok(output));
|
|
||||||
|
|
||||||
const presenter = await service.getAllRaces();
|
|
||||||
const viewModel = presenter.getViewModel();
|
|
||||||
|
|
||||||
expect(getAllRacesUseCase.execute).toHaveBeenCalledWith();
|
|
||||||
expect(viewModel).not.toBeNull();
|
|
||||||
expect(viewModel).toMatchObject({ totalCount: 0 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('registerForRace should map success into CommandResultPresenter', async () => {
|
|
||||||
(registerForRaceUseCase.execute as jest.Mock).mockResolvedValue(Result.ok({}));
|
|
||||||
|
|
||||||
const presenter = await service.registerForRace({
|
|
||||||
raceId: 'race-1',
|
|
||||||
driverId: 'driver-1',
|
|
||||||
} as { raceId: string; driverId: string });
|
|
||||||
|
|
||||||
expect(registerForRaceUseCase.execute).toHaveBeenCalledWith({ raceId: 'race-1', driverId: 'driver-1' });
|
|
||||||
expect(presenter.viewModel.success).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('registerForRace should map error into CommandResultPresenter', async () => {
|
|
||||||
(registerForRaceUseCase.execute as jest.Mock).mockResolvedValue(Result.err({ code: 'FAILED_TO_REGISTER_FOR_RACE' as const }));
|
|
||||||
|
|
||||||
const presenter = await service.registerForRace({
|
|
||||||
raceId: 'race-1',
|
|
||||||
driverId: 'driver-1',
|
|
||||||
} as { raceId: string; driverId: string });
|
|
||||||
|
|
||||||
expect(presenter.viewModel.success).toBe(false);
|
|
||||||
expect(presenter.viewModel.errorCode).toBe('FAILED_TO_REGISTER_FOR_RACE');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { RacesPageOutputPort } from '@core/racing/application/ports/output/RacesPageOutputPort';
|
|
||||||
import type { RaceResultsDetailOutputPort } from '@core/racing/application/ports/output/RaceResultsDetailOutputPort';
|
|
||||||
import type { RaceWithSOFOutputPort } from '@core/racing/application/ports/output/RaceWithSOFOutputPort';
|
|
||||||
import type { RaceProtestsOutputPort } from '@core/racing/application/ports/output/RaceProtestsOutputPort';
|
|
||||||
import type { RacePenaltiesOutputPort } from '@core/racing/application/ports/output/RacePenaltiesOutputPort';
|
|
||||||
|
|
||||||
// DTOs
|
// DTOs
|
||||||
import { GetRaceDetailParamsDTO } from './dtos/GetRaceDetailParamsDTO';
|
import { GetRaceDetailParamsDTO } from './dtos/GetRaceDetailParamsDTO';
|
||||||
@@ -61,7 +56,23 @@ import { RequestProtestDefenseCommandDTO } from './dtos/RequestProtestDefenseCom
|
|||||||
import { ReviewProtestCommandDTO } from './dtos/ReviewProtestCommandDTO';
|
import { ReviewProtestCommandDTO } from './dtos/ReviewProtestCommandDTO';
|
||||||
|
|
||||||
// Tokens
|
// Tokens
|
||||||
import { DRIVER_RATING_PROVIDER_TOKEN, IMAGE_SERVICE_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN } from './RaceProviders';
|
import {
|
||||||
|
DRIVER_RATING_PROVIDER_TOKEN,
|
||||||
|
IMAGE_SERVICE_TOKEN,
|
||||||
|
LEAGUE_REPOSITORY_TOKEN,
|
||||||
|
LOGGER_TOKEN,
|
||||||
|
GET_ALL_RACES_PRESENTER_TOKEN,
|
||||||
|
GET_TOTAL_RACES_PRESENTER_TOKEN,
|
||||||
|
IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN,
|
||||||
|
RACE_DETAIL_PRESENTER_TOKEN,
|
||||||
|
RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||||
|
ALL_RACES_PAGE_DATA_PRESENTER_TOKEN,
|
||||||
|
RACE_RESULTS_DETAIL_PRESENTER_TOKEN,
|
||||||
|
RACE_WITH_SOF_PRESENTER_TOKEN,
|
||||||
|
RACE_PROTESTS_PRESENTER_TOKEN,
|
||||||
|
RACE_PENALTIES_PRESENTER_TOKEN,
|
||||||
|
COMMAND_RESULT_PRESENTER_TOKEN
|
||||||
|
} from './RaceProviders';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RaceService {
|
export class RaceService {
|
||||||
@@ -90,305 +101,137 @@ export class RaceService {
|
|||||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||||
@Inject(DRIVER_RATING_PROVIDER_TOKEN) private readonly driverRatingProvider: DriverRatingProvider,
|
@Inject(DRIVER_RATING_PROVIDER_TOKEN) private readonly driverRatingProvider: DriverRatingProvider,
|
||||||
@Inject(IMAGE_SERVICE_TOKEN) private readonly imageService: IImageServicePort,
|
@Inject(IMAGE_SERVICE_TOKEN) private readonly imageService: IImageServicePort,
|
||||||
|
// Injected presenters
|
||||||
|
@Inject(GET_ALL_RACES_PRESENTER_TOKEN) private readonly getAllRacesPresenter: GetAllRacesPresenter,
|
||||||
|
@Inject(GET_TOTAL_RACES_PRESENTER_TOKEN) private readonly getTotalRacesPresenter: GetTotalRacesPresenter,
|
||||||
|
@Inject(IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN) private readonly importRaceResultsApiPresenter: ImportRaceResultsApiPresenter,
|
||||||
|
@Inject(RACE_DETAIL_PRESENTER_TOKEN) private readonly raceDetailPresenter: RaceDetailPresenter,
|
||||||
|
@Inject(RACES_PAGE_DATA_PRESENTER_TOKEN) private readonly racesPageDataPresenter: RacesPageDataPresenter,
|
||||||
|
@Inject(ALL_RACES_PAGE_DATA_PRESENTER_TOKEN) private readonly allRacesPageDataPresenter: AllRacesPageDataPresenter,
|
||||||
|
@Inject(RACE_RESULTS_DETAIL_PRESENTER_TOKEN) private readonly raceResultsDetailPresenter: RaceResultsDetailPresenter,
|
||||||
|
@Inject(RACE_WITH_SOF_PRESENTER_TOKEN) private readonly raceWithSOFPresenter: RaceWithSOFPresenter,
|
||||||
|
@Inject(RACE_PROTESTS_PRESENTER_TOKEN) private readonly raceProtestsPresenter: RaceProtestsPresenter,
|
||||||
|
@Inject(RACE_PENALTIES_PRESENTER_TOKEN) private readonly racePenaltiesPresenter: RacePenaltiesPresenter,
|
||||||
|
@Inject(COMMAND_RESULT_PRESENTER_TOKEN) private readonly commandResultPresenter: CommandResultPresenter,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getAllRaces(): Promise<GetAllRacesPresenter> {
|
async getAllRaces(): Promise<GetAllRacesPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching all races.');
|
this.logger.debug('[RaceService] Fetching all races.');
|
||||||
|
await this.getAllRacesUseCase.execute({});
|
||||||
const presenter = new GetAllRacesPresenter();
|
return this.getAllRacesPresenter;
|
||||||
this.getAllRacesUseCase.setOutput(presenter);
|
|
||||||
|
|
||||||
const result = await this.getAllRacesUseCase.execute({});
|
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error(result.unwrapErr().code);
|
|
||||||
}
|
|
||||||
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTotalRaces(): Promise<GetTotalRacesPresenter> {
|
async getTotalRaces(): Promise<GetTotalRacesPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching total races count.');
|
this.logger.debug('[RaceService] Fetching total races count.');
|
||||||
const result = await this.getTotalRacesUseCase.execute({});
|
await this.getTotalRacesUseCase.execute({});
|
||||||
const presenter = new GetTotalRacesPresenter();
|
return this.getTotalRacesPresenter;
|
||||||
presenter.present(result);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async importRaceResults(input: ImportRaceResultsDTO): Promise<ImportRaceResultsApiPresenter> {
|
async importRaceResults(input: ImportRaceResultsDTO): Promise<ImportRaceResultsApiPresenter> {
|
||||||
this.logger.debug('Importing race results:', input);
|
this.logger.debug('Importing race results:', input);
|
||||||
const result = await this.importRaceResultsApiUseCase.execute({ raceId: input.raceId, resultsFileContent: input.resultsFileContent });
|
await this.importRaceResultsApiUseCase.execute({ raceId: input.raceId, resultsFileContent: input.resultsFileContent });
|
||||||
if (result.isErr()) {
|
return this.importRaceResultsApiPresenter;
|
||||||
throw new Error(result.unwrapErr().code);
|
|
||||||
}
|
|
||||||
const presenter = new ImportRaceResultsApiPresenter();
|
|
||||||
presenter.present(result.unwrap());
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRaceDetail(params: GetRaceDetailParamsDTO): Promise<RaceDetailPresenter> {
|
async getRaceDetail(params: GetRaceDetailParamsDTO): Promise<RaceDetailPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching race detail:', params);
|
this.logger.debug('[RaceService] Fetching race detail:', params);
|
||||||
|
await this.getRaceDetailUseCase.execute(params);
|
||||||
const presenter = new RaceDetailPresenter(this.driverRatingProvider, this.imageService, params);
|
return this.raceDetailPresenter;
|
||||||
this.getRaceDetailUseCase.setOutput(presenter);
|
|
||||||
|
|
||||||
const result = await this.getRaceDetailUseCase.execute(params);
|
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error('Failed to get race detail');
|
|
||||||
}
|
|
||||||
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRacesPageData(): Promise<RacesPageDataPresenter> {
|
async getRacesPageData(leagueId: string): Promise<RacesPageDataPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching races page data.');
|
this.logger.debug('[RaceService] Fetching races page data.');
|
||||||
|
await this.getRacesPageDataUseCase.execute({ leagueId });
|
||||||
const result = await this.getRacesPageDataUseCase.execute();
|
return this.racesPageDataPresenter;
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error('Failed to get races page data');
|
|
||||||
}
|
|
||||||
|
|
||||||
const presenter = new RacesPageDataPresenter(this.leagueRepository);
|
|
||||||
await presenter.present(result.value as RacesPageOutputPort);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllRacesPageData(): Promise<AllRacesPageDataPresenter> {
|
async getAllRacesPageData(): Promise<AllRacesPageDataPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching all races page data.');
|
this.logger.debug('[RaceService] Fetching all races page data.');
|
||||||
|
await this.getAllRacesPageDataUseCase.execute({});
|
||||||
const result = await this.getAllRacesPageDataUseCase.execute();
|
return this.allRacesPageDataPresenter;
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error('Failed to get all races page data');
|
|
||||||
}
|
|
||||||
|
|
||||||
const presenter = new AllRacesPageDataPresenter();
|
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRaceResultsDetail(raceId: string): Promise<RaceResultsDetailPresenter> {
|
async getRaceResultsDetail(raceId: string): Promise<RaceResultsDetailPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching race results detail:', { raceId });
|
this.logger.debug('[RaceService] Fetching race results detail:', { raceId });
|
||||||
|
await this.getRaceResultsDetailUseCase.execute({ raceId });
|
||||||
const result = await this.getRaceResultsDetailUseCase.execute({ raceId });
|
return this.raceResultsDetailPresenter;
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error('Failed to get race results detail');
|
|
||||||
}
|
|
||||||
|
|
||||||
const presenter = new RaceResultsDetailPresenter(this.imageService);
|
|
||||||
await presenter.present(result.value as RaceResultsDetailOutputPort);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRaceWithSOF(raceId: string): Promise<RaceWithSOFPresenter> {
|
async getRaceWithSOF(raceId: string): Promise<RaceWithSOFPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching race with SOF:', { raceId });
|
this.logger.debug('[RaceService] Fetching race with SOF:', { raceId });
|
||||||
|
await this.getRaceWithSOFUseCase.execute({ raceId });
|
||||||
const result = await this.getRaceWithSOFUseCase.execute({ raceId });
|
return this.raceWithSOFPresenter;
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error('Failed to get race with SOF');
|
|
||||||
}
|
|
||||||
|
|
||||||
const presenter = new RaceWithSOFPresenter();
|
|
||||||
presenter.present(result.value as RaceWithSOFOutputPort);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRaceProtests(raceId: string): Promise<RaceProtestsPresenter> {
|
async getRaceProtests(raceId: string): Promise<RaceProtestsPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching race protests:', { raceId });
|
this.logger.debug('[RaceService] Fetching race protests:', { raceId });
|
||||||
|
await this.getRaceProtestsUseCase.execute({ raceId });
|
||||||
const result = await this.getRaceProtestsUseCase.execute({ raceId });
|
return this.raceProtestsPresenter;
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error('Failed to get race protests');
|
|
||||||
}
|
|
||||||
|
|
||||||
const presenter = new RaceProtestsPresenter();
|
|
||||||
presenter.present(result.value as RaceProtestsOutputPort);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRacePenalties(raceId: string): Promise<RacePenaltiesPresenter> {
|
async getRacePenalties(raceId: string): Promise<RacePenaltiesPresenter> {
|
||||||
this.logger.debug('[RaceService] Fetching race penalties:', { raceId });
|
this.logger.debug('[RaceService] Fetching race penalties:', { raceId });
|
||||||
|
await this.getRacePenaltiesUseCase.execute({ raceId });
|
||||||
const result = await this.getRacePenaltiesUseCase.execute({ raceId });
|
return this.racePenaltiesPresenter;
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
throw new Error('Failed to get race penalties');
|
|
||||||
}
|
|
||||||
|
|
||||||
const presenter = new RacePenaltiesPresenter();
|
|
||||||
presenter.present(result.value as RacePenaltiesOutputPort);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerForRace(params: RegisterForRaceParamsDTO): Promise<CommandResultPresenter> {
|
async registerForRace(params: RegisterForRaceParamsDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Registering for race:', params);
|
this.logger.debug('[RaceService] Registering for race:', params);
|
||||||
|
await this.registerForRaceUseCase.execute(params);
|
||||||
const result = await this.registerForRaceUseCase.execute(params);
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_REGISTER_FOR_RACE', 'Failed to register for race');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async withdrawFromRace(params: WithdrawFromRaceParamsDTO): Promise<CommandResultPresenter> {
|
async withdrawFromRace(params: WithdrawFromRaceParamsDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Withdrawing from race:', params);
|
this.logger.debug('[RaceService] Withdrawing from race:', params);
|
||||||
|
await this.withdrawFromRaceUseCase.execute(params);
|
||||||
const result = await this.withdrawFromRaceUseCase.execute(params);
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_WITHDRAW_FROM_RACE', 'Failed to withdraw from race');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancelRace(params: RaceActionParamsDTO): Promise<CommandResultPresenter> {
|
async cancelRace(params: RaceActionParamsDTO, cancelledById: string): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Cancelling race:', params);
|
this.logger.debug('[RaceService] Cancelling race:', params);
|
||||||
|
await this.cancelRaceUseCase.execute({ raceId: params.raceId, cancelledById });
|
||||||
const result = await this.cancelRaceUseCase.execute({ raceId: params.raceId });
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_CANCEL_RACE', 'Failed to cancel race');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async completeRace(params: RaceActionParamsDTO): Promise<CommandResultPresenter> {
|
async completeRace(params: RaceActionParamsDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Completing race:', params);
|
this.logger.debug('[RaceService] Completing race:', params);
|
||||||
|
await this.completeRaceUseCase.execute({ raceId: params.raceId });
|
||||||
const result = await this.completeRaceUseCase.execute({ raceId: params.raceId });
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_COMPLETE_RACE', 'Failed to complete race');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async reopenRace(params: RaceActionParamsDTO): Promise<CommandResultPresenter> {
|
async reopenRace(params: RaceActionParamsDTO, reopenedById: string): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Re-opening race:', params);
|
this.logger.debug('[RaceService] Re-opening race:', params);
|
||||||
|
await this.reopenRaceUseCase.execute({ raceId: params.raceId, reopenedById });
|
||||||
const result = await this.reopenRaceUseCase.execute({ raceId: params.raceId });
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const errorCode = result.unwrapErr().code;
|
|
||||||
|
|
||||||
if (errorCode === 'RACE_ALREADY_SCHEDULED') {
|
|
||||||
this.logger.debug('[RaceService] Race is already scheduled, treating reopen as success.');
|
|
||||||
presenter.presentSuccess('Race already scheduled');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentFailure(errorCode ?? 'UNEXPECTED_ERROR', 'Unexpected error while reopening race');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fileProtest(command: FileProtestCommandDTO): Promise<CommandResultPresenter> {
|
async fileProtest(command: FileProtestCommandDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Filing protest:', command);
|
this.logger.debug('[RaceService] Filing protest:', command);
|
||||||
|
await this.fileProtestUseCase.execute(command);
|
||||||
const result = await this.fileProtestUseCase.execute(command);
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_FILE_PROTEST', 'Failed to file protest');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyQuickPenalty(command: QuickPenaltyCommandDTO): Promise<CommandResultPresenter> {
|
async applyQuickPenalty(command: QuickPenaltyCommandDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Applying quick penalty:', command);
|
this.logger.debug('[RaceService] Applying quick penalty:', command);
|
||||||
|
await this.quickPenaltyUseCase.execute(command);
|
||||||
const result = await this.quickPenaltyUseCase.execute(command);
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_APPLY_QUICK_PENALTY', 'Failed to apply quick penalty');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyPenalty(command: ApplyPenaltyCommandDTO): Promise<CommandResultPresenter> {
|
async applyPenalty(command: ApplyPenaltyCommandDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Applying penalty:', command);
|
this.logger.debug('[RaceService] Applying penalty:', command);
|
||||||
|
await this.applyPenaltyUseCase.execute(command);
|
||||||
const result = await this.applyPenaltyUseCase.execute(command);
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_APPLY_PENALTY', 'Failed to apply penalty');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async requestProtestDefense(command: RequestProtestDefenseCommandDTO): Promise<CommandResultPresenter> {
|
async requestProtestDefense(command: RequestProtestDefenseCommandDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Requesting protest defense:', command);
|
this.logger.debug('[RaceService] Requesting protest defense:', command);
|
||||||
|
await this.requestProtestDefenseUseCase.execute(command);
|
||||||
const result = await this.requestProtestDefenseUseCase.execute(command);
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_REQUEST_PROTEST_DEFENSE', 'Failed to request protest defense');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async reviewProtest(command: ReviewProtestCommandDTO): Promise<CommandResultPresenter> {
|
async reviewProtest(command: ReviewProtestCommandDTO): Promise<CommandResultPresenter> {
|
||||||
this.logger.debug('[RaceService] Reviewing protest:', command);
|
this.logger.debug('[RaceService] Reviewing protest:', command);
|
||||||
|
await this.reviewProtestUseCase.execute(command);
|
||||||
const result = await this.reviewProtestUseCase.execute(command);
|
return this.commandResultPresenter;
|
||||||
|
|
||||||
const presenter = new CommandResultPresenter();
|
|
||||||
if (result.isErr()) {
|
|
||||||
const error = result.unwrapErr();
|
|
||||||
presenter.presentFailure(error.code ?? 'FAILED_TO_REVIEW_PROTEST', 'Failed to review protest');
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.presentSuccess();
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,12 +28,28 @@ export class AllRacesListItemDTO {
|
|||||||
strengthOfField!: number | null;
|
strengthOfField!: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AllRacesFilterOptionsDTO {
|
export class AllRacesStatusFilterDTO {
|
||||||
@ApiProperty({ type: [{ value: String, label: String }] })
|
@ApiProperty()
|
||||||
statuses!: { value: AllRacesStatus; label: string }[];
|
value!: AllRacesStatus;
|
||||||
|
|
||||||
@ApiProperty({ type: [{ id: String, name: String }] })
|
@ApiProperty()
|
||||||
leagues!: { id: string; name: string }[];
|
label!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AllRacesLeagueFilterDTO {
|
||||||
|
@ApiProperty()
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
name!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AllRacesFilterOptionsDTO {
|
||||||
|
@ApiProperty({ type: [AllRacesStatusFilterDTO] })
|
||||||
|
statuses!: AllRacesStatusFilterDTO[];
|
||||||
|
|
||||||
|
@ApiProperty({ type: [AllRacesLeagueFilterDTO] })
|
||||||
|
leagues!: AllRacesLeagueFilterDTO[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AllRacesPageDTO {
|
export class AllRacesPageDTO {
|
||||||
|
|||||||
@@ -7,19 +7,19 @@ export interface CommandResultDTO {
|
|||||||
message?: string;
|
message?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommandApplicationError<E extends string = string> = ApplicationErrorCode<
|
export type CommandApplicationError = ApplicationErrorCode<
|
||||||
E,
|
string,
|
||||||
{ message: string }
|
{ message: string }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export class CommandResultPresenter<E extends string = string> {
|
export class CommandResultPresenter {
|
||||||
private model: CommandResultDTO | null = null;
|
private model: CommandResultDTO | null = null;
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.model = null;
|
this.model = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(result: Result<unknown, CommandApplicationError<E>>): void {
|
present(result: Result<unknown, CommandApplicationError>): void {
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
const error = result.unwrapErr();
|
const error = result.unwrapErr();
|
||||||
this.model = {
|
this.model = {
|
||||||
@@ -36,7 +36,7 @@ export class CommandResultPresenter<E extends string = string> {
|
|||||||
presentSuccess(message?: string): void {
|
presentSuccess(message?: string): void {
|
||||||
this.model = {
|
this.model = {
|
||||||
success: true,
|
success: true,
|
||||||
message,
|
...(message !== undefined && { message }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ export class CommandResultPresenter<E extends string = string> {
|
|||||||
this.model = {
|
this.model = {
|
||||||
success: false,
|
success: false,
|
||||||
errorCode,
|
errorCode,
|
||||||
message,
|
...(message !== undefined && { message }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,4 +59,8 @@ export class CommandResultPresenter<E extends string = string> {
|
|||||||
|
|
||||||
return this.model;
|
return this.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get viewModel(): CommandResultDTO {
|
||||||
|
return this.responseModel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ export class RaceDetailPresenter implements UseCaseOutputPort<GetRaceDetailResul
|
|||||||
track: output.race.track,
|
track: output.race.track,
|
||||||
car: output.race.car,
|
car: output.race.car,
|
||||||
scheduledAt: output.race.scheduledAt.toISOString(),
|
scheduledAt: output.race.scheduledAt.toISOString(),
|
||||||
sessionType: output.race.sessionType,
|
sessionType: output.race.sessionType.toString(),
|
||||||
status: output.race.status,
|
status: output.race.status.toString(),
|
||||||
strengthOfField: output.race.strengthOfField ?? null,
|
strengthOfField: output.race.strengthOfField ?? null,
|
||||||
registeredCount: output.race.registeredCount ?? undefined,
|
...(output.race.registeredCount !== undefined && { registeredCount: output.race.registeredCount }),
|
||||||
maxParticipants: output.race.maxParticipants ?? undefined,
|
...(output.race.maxParticipants !== undefined && { maxParticipants: output.race.maxParticipants }),
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -54,22 +54,22 @@ export class RaceDetailPresenter implements UseCaseOutputPort<GetRaceDetailResul
|
|||||||
name: output.league.name.toString(),
|
name: output.league.name.toString(),
|
||||||
description: output.league.description.toString(),
|
description: output.league.description.toString(),
|
||||||
settings: {
|
settings: {
|
||||||
maxDrivers: output.league.settings.maxDrivers ?? undefined,
|
...(output.league.settings.maxDrivers !== undefined && { maxDrivers: output.league.settings.maxDrivers }),
|
||||||
qualifyingFormat: output.league.settings.qualifyingFormat ?? undefined,
|
...(output.league.settings.qualifyingFormat !== undefined && { qualifyingFormat: output.league.settings.qualifyingFormat.toString() }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const entryListDTO: RaceDetailEntryDTO[] = await Promise.all(
|
const entryListDTO: RaceDetailEntryDTO[] = await Promise.all(
|
||||||
output.drivers.map(async driver => {
|
output.drivers.map(async driver => {
|
||||||
const ratingResult = await this.driverRatingProvider.getDriverRating({ driverId: driver.id });
|
const rating = this.driverRatingProvider.getRating(driver.id);
|
||||||
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
|
const avatarUrl = this.imageService.getDriverAvatar(driver.id);
|
||||||
return {
|
return {
|
||||||
id: driver.id,
|
id: driver.id,
|
||||||
name: driver.name.toString(),
|
name: driver.name.toString(),
|
||||||
country: driver.country.toString(),
|
country: driver.country.toString(),
|
||||||
avatarUrl: avatarResult.avatarUrl,
|
avatarUrl,
|
||||||
rating: ratingResult.rating,
|
rating,
|
||||||
isCurrentUser: driver.id === params.driverId,
|
isCurrentUser: driver.id === params.driverId,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ export class RaceProtestsPresenter {
|
|||||||
protestingDriverId: protest.protestingDriverId,
|
protestingDriverId: protest.protestingDriverId,
|
||||||
accusedDriverId: protest.accusedDriverId,
|
accusedDriverId: protest.accusedDriverId,
|
||||||
incident: {
|
incident: {
|
||||||
lap: protest.incident.lap,
|
lap: protest.incident.lap.toNumber(),
|
||||||
description: protest.incident.description,
|
description: protest.incident.description.toString(),
|
||||||
},
|
},
|
||||||
status: protest.status,
|
status: protest.status.toString(),
|
||||||
filedAt: protest.filedAt.toISOString(),
|
filedAt: protest.filedAt.toISOString(),
|
||||||
} as RaceProtestDTO));
|
} as RaceProtestDTO));
|
||||||
|
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ export class RaceResultsDetailPresenter {
|
|||||||
throw new Error(`Driver not found for result: ${singleResult.driverId}`);
|
throw new Error(`Driver not found for result: ${singleResult.driverId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatarResult = await this.imageService.getDriverAvatar({ driverId: driver.id });
|
const avatarUrl = this.imageService.getDriverAvatar(driver.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
driverId: singleResult.driverId.toString(),
|
driverId: singleResult.driverId.toString(),
|
||||||
driverName: driver.name.toString(),
|
driverName: driver.name.toString(),
|
||||||
avatarUrl: avatarResult.avatarUrl,
|
avatarUrl,
|
||||||
position: singleResult.position.toNumber(),
|
position: singleResult.position.toNumber(),
|
||||||
startPosition: singleResult.startPosition.toNumber(),
|
startPosition: singleResult.startPosition.toNumber(),
|
||||||
incidents: singleResult.incidents.toNumber(),
|
incidents: singleResult.incidents.toNumber(),
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { SponsorProfileDTO } from './dtos/SponsorProfileDTO';
|
|||||||
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
|
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
|
||||||
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
|
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
|
||||||
import type { AcceptSponsorshipRequestResultViewModel } from './presenters/AcceptSponsorshipRequestPresenter';
|
import type { AcceptSponsorshipRequestResultViewModel } from './presenters/AcceptSponsorshipRequestPresenter';
|
||||||
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
import type { RejectSponsorshipRequestResult } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
|
|
||||||
@ApiTags('sponsors')
|
@ApiTags('sponsors')
|
||||||
@Controller('sponsors')
|
@Controller('sponsors')
|
||||||
@@ -39,8 +39,7 @@ export class SponsorController {
|
|||||||
type: GetEntitySponsorshipPricingResultDTO,
|
type: GetEntitySponsorshipPricingResultDTO,
|
||||||
})
|
})
|
||||||
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
|
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
|
||||||
const presenter = await this.sponsorService.getEntitySponsorshipPricing();
|
return await this.sponsorService.getEntitySponsorshipPricing();
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@@ -51,8 +50,7 @@ export class SponsorController {
|
|||||||
type: GetSponsorsOutputDTO,
|
type: GetSponsorsOutputDTO,
|
||||||
})
|
})
|
||||||
async getSponsors(): Promise<GetSponsorsOutputDTO> {
|
async getSponsors(): Promise<GetSponsorsOutputDTO> {
|
||||||
const presenter = await this.sponsorService.getSponsors();
|
return await this.sponsorService.getSponsors();
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@@ -64,8 +62,7 @@ export class SponsorController {
|
|||||||
type: CreateSponsorOutputDTO,
|
type: CreateSponsorOutputDTO,
|
||||||
})
|
})
|
||||||
async createSponsor(@Body() input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
|
async createSponsor(@Body() input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
|
||||||
const presenter = await this.sponsorService.createSponsor(input);
|
return await this.sponsorService.createSponsor(input);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('dashboard/:sponsorId')
|
@Get('dashboard/:sponsorId')
|
||||||
@@ -78,11 +75,10 @@ export class SponsorController {
|
|||||||
@ApiResponse({ status: 404, description: 'Sponsor not found' })
|
@ApiResponse({ status: 404, description: 'Sponsor not found' })
|
||||||
async getSponsorDashboard(
|
async getSponsorDashboard(
|
||||||
@Param('sponsorId') sponsorId: string,
|
@Param('sponsorId') sponsorId: string,
|
||||||
): Promise<SponsorDashboardDTO | null> {
|
): Promise<SponsorDashboardDTO> {
|
||||||
const presenter = await this.sponsorService.getSponsorDashboard({
|
return await this.sponsorService.getSponsorDashboard({
|
||||||
sponsorId,
|
sponsorId,
|
||||||
} as GetSponsorDashboardQueryParamsDTO);
|
} as GetSponsorDashboardQueryParamsDTO);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':sponsorId/sponsorships')
|
@Get(':sponsorId/sponsorships')
|
||||||
@@ -97,11 +93,10 @@ export class SponsorController {
|
|||||||
@ApiResponse({ status: 404, description: 'Sponsor not found' })
|
@ApiResponse({ status: 404, description: 'Sponsor not found' })
|
||||||
async getSponsorSponsorships(
|
async getSponsorSponsorships(
|
||||||
@Param('sponsorId') sponsorId: string,
|
@Param('sponsorId') sponsorId: string,
|
||||||
): Promise<SponsorSponsorshipsDTO | null> {
|
): Promise<SponsorSponsorshipsDTO> {
|
||||||
const presenter = await this.sponsorService.getSponsorSponsorships({
|
return await this.sponsorService.getSponsorSponsorships({
|
||||||
sponsorId,
|
sponsorId,
|
||||||
} as GetSponsorSponsorshipsQueryParamsDTO);
|
} as GetSponsorSponsorshipsQueryParamsDTO);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':sponsorId')
|
@Get(':sponsorId')
|
||||||
@@ -112,9 +107,8 @@ export class SponsorController {
|
|||||||
type: GetSponsorOutputDTO,
|
type: GetSponsorOutputDTO,
|
||||||
})
|
})
|
||||||
@ApiResponse({ status: 404, description: 'Sponsor not found' })
|
@ApiResponse({ status: 404, description: 'Sponsor not found' })
|
||||||
async getSponsor(@Param('sponsorId') sponsorId: string): Promise<GetSponsorOutputDTO | null> {
|
async getSponsor(@Param('sponsorId') sponsorId: string): Promise<GetSponsorOutputDTO> {
|
||||||
const presenter = await this.sponsorService.getSponsor(sponsorId);
|
return await this.sponsorService.getSponsor(sponsorId);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('requests')
|
@Get('requests')
|
||||||
@@ -126,14 +120,13 @@ export class SponsorController {
|
|||||||
})
|
})
|
||||||
async getPendingSponsorshipRequests(
|
async getPendingSponsorshipRequests(
|
||||||
@Query() query: { entityType: string; entityId: string },
|
@Query() query: { entityType: string; entityId: string },
|
||||||
): Promise<GetPendingSponsorshipRequestsOutputDTO | null> {
|
): Promise<GetPendingSponsorshipRequestsOutputDTO> {
|
||||||
const presenter = await this.sponsorService.getPendingSponsorshipRequests(
|
return await this.sponsorService.getPendingSponsorshipRequests(
|
||||||
query as {
|
query as {
|
||||||
entityType: import('@core/racing/domain/entities/SponsorshipRequest').SponsorableEntityType;
|
entityType: import('@core/racing/domain/entities/SponsorshipRequest').SponsorableEntityType;
|
||||||
entityId: string;
|
entityId: string;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('requests/:requestId/accept')
|
@Post('requests/:requestId/accept')
|
||||||
@@ -146,11 +139,10 @@ export class SponsorController {
|
|||||||
@Param('requestId') requestId: string,
|
@Param('requestId') requestId: string,
|
||||||
@Body() input: AcceptSponsorshipRequestInputDTO,
|
@Body() input: AcceptSponsorshipRequestInputDTO,
|
||||||
): Promise<AcceptSponsorshipRequestResultViewModel | null> {
|
): Promise<AcceptSponsorshipRequestResultViewModel | null> {
|
||||||
const presenter = await this.sponsorService.acceptSponsorshipRequest(
|
return await this.sponsorService.acceptSponsorshipRequest(
|
||||||
requestId,
|
requestId,
|
||||||
input.respondedBy,
|
input.respondedBy,
|
||||||
);
|
);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('requests/:requestId/reject')
|
@Post('requests/:requestId/reject')
|
||||||
@@ -162,13 +154,12 @@ export class SponsorController {
|
|||||||
async rejectSponsorshipRequest(
|
async rejectSponsorshipRequest(
|
||||||
@Param('requestId') requestId: string,
|
@Param('requestId') requestId: string,
|
||||||
@Body() input: RejectSponsorshipRequestInputDTO,
|
@Body() input: RejectSponsorshipRequestInputDTO,
|
||||||
): Promise<RejectSponsorshipRequestResultDTO | null> {
|
): Promise<RejectSponsorshipRequestResult | null> {
|
||||||
const presenter = await this.sponsorService.rejectSponsorshipRequest(
|
return await this.sponsorService.rejectSponsorshipRequest(
|
||||||
requestId,
|
requestId,
|
||||||
input.respondedBy,
|
input.respondedBy,
|
||||||
input.reason,
|
input.reason,
|
||||||
);
|
);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('billing/:sponsorId')
|
@Get('billing/:sponsorId')
|
||||||
@@ -181,8 +172,7 @@ export class SponsorController {
|
|||||||
invoices: InvoiceDTO[];
|
invoices: InvoiceDTO[];
|
||||||
stats: BillingStatsDTO;
|
stats: BillingStatsDTO;
|
||||||
}> {
|
}> {
|
||||||
const presenter = await this.sponsorService.getSponsorBilling(sponsorId);
|
return await this.sponsorService.getSponsorBilling(sponsorId);
|
||||||
return presenter.viewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('leagues/available')
|
@Get('leagues/available')
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { SponsorService } from './SponsorService';
|
|||||||
import { NotificationService } from '@core/notifications/application/ports/NotificationService';
|
import { NotificationService } from '@core/notifications/application/ports/NotificationService';
|
||||||
import type { IPaymentRepository } from '@core/payments/domain/repositories/IPaymentRepository';
|
import type { IPaymentRepository } from '@core/payments/domain/repositories/IPaymentRepository';
|
||||||
import { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
|
import { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
|
||||||
import { IPaymentGateway } from '@core/payments/domain/ports/IPaymentGateway';
|
// Remove the missing import
|
||||||
|
// import { IPaymentGateway } from '@core/payments/domain/ports/IPaymentGateway';
|
||||||
import { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
|
import { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
|
||||||
import { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
import { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
|
||||||
import { ILeagueWalletRepository } from '@core/racing/domain/repositories/ILeagueWalletRepository';
|
import { ILeagueWalletRepository } from '@core/racing/domain/repositories/ILeagueWalletRepository';
|
||||||
@@ -15,7 +16,7 @@ import { ISeasonSponsorshipRepository } from '@core/racing/domain/repositories/I
|
|||||||
import { ISponsorRepository } from '@core/racing/domain/repositories/ISponsorRepository';
|
import { ISponsorRepository } from '@core/racing/domain/repositories/ISponsorRepository';
|
||||||
import { ISponsorshipPricingRepository } from '@core/racing/domain/repositories/ISponsorshipPricingRepository';
|
import { ISponsorshipPricingRepository } from '@core/racing/domain/repositories/ISponsorshipPricingRepository';
|
||||||
import { ISponsorshipRequestRepository } from '@core/racing/domain/repositories/ISponsorshipRequestRepository';
|
import { ISponsorshipRequestRepository } from '@core/racing/domain/repositories/ISponsorshipRequestRepository';
|
||||||
import type { Logger } from '@core/shared/application';
|
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||||
|
|
||||||
import { GetSponsorBillingUseCase } from '@core/payments/application/use-cases/GetSponsorBillingUseCase';
|
import { GetSponsorBillingUseCase } from '@core/payments/application/use-cases/GetSponsorBillingUseCase';
|
||||||
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
||||||
@@ -40,6 +41,18 @@ import { InMemorySponsorRepository } from '@adapters/racing/persistence/inmemory
|
|||||||
import { InMemorySponsorshipPricingRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorshipPricingRepository';
|
import { InMemorySponsorshipPricingRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorshipPricingRepository';
|
||||||
import { InMemorySponsorshipRequestRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorshipRequestRepository';
|
import { InMemorySponsorshipRequestRepository } from '@adapters/racing/persistence/inmemory/InMemorySponsorshipRequestRepository';
|
||||||
|
|
||||||
|
// Import presenters
|
||||||
|
import { GetEntitySponsorshipPricingPresenter } from './presenters/GetEntitySponsorshipPricingPresenter';
|
||||||
|
import { GetSponsorsPresenter } from './presenters/GetSponsorsPresenter';
|
||||||
|
import { CreateSponsorPresenter } from './presenters/CreateSponsorPresenter';
|
||||||
|
import { GetSponsorDashboardPresenter } from './presenters/GetSponsorDashboardPresenter';
|
||||||
|
import { GetSponsorSponsorshipsPresenter } from './presenters/GetSponsorSponsorshipsPresenter';
|
||||||
|
import { GetSponsorPresenter } from './presenters/GetSponsorPresenter';
|
||||||
|
import { GetPendingSponsorshipRequestsPresenter } from './presenters/GetPendingSponsorshipRequestsPresenter';
|
||||||
|
import { AcceptSponsorshipRequestPresenter } from './presenters/AcceptSponsorshipRequestPresenter';
|
||||||
|
import { RejectSponsorshipRequestPresenter } from './presenters/RejectSponsorshipRequestPresenter';
|
||||||
|
import { SponsorBillingPresenter } from './presenters/SponsorBillingPresenter';
|
||||||
|
|
||||||
// Define injection tokens
|
// Define injection tokens
|
||||||
export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository';
|
export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository';
|
||||||
export const SEASON_SPONSORSHIP_REPOSITORY_TOKEN = 'ISeasonSponsorshipRepository';
|
export const SEASON_SPONSORSHIP_REPOSITORY_TOKEN = 'ISeasonSponsorshipRepository';
|
||||||
@@ -51,6 +64,18 @@ export const SPONSORSHIP_PRICING_REPOSITORY_TOKEN = 'ISponsorshipPricingReposito
|
|||||||
export const SPONSORSHIP_REQUEST_REPOSITORY_TOKEN = 'ISponsorshipRequestRepository';
|
export const SPONSORSHIP_REQUEST_REPOSITORY_TOKEN = 'ISponsorshipRequestRepository';
|
||||||
export const LOGGER_TOKEN = 'Logger';
|
export const LOGGER_TOKEN = 'Logger';
|
||||||
|
|
||||||
|
// Presenter tokens
|
||||||
|
export const GET_ENTITY_SPONSORSHIP_PRICING_PRESENTER_TOKEN = 'GetEntitySponsorshipPricingPresenter';
|
||||||
|
export const GET_SPONSORS_PRESENTER_TOKEN = 'GetSponsorsPresenter';
|
||||||
|
export const CREATE_SPONSOR_PRESENTER_TOKEN = 'CreateSponsorPresenter';
|
||||||
|
export const GET_SPONSOR_DASHBOARD_PRESENTER_TOKEN = 'GetSponsorDashboardPresenter';
|
||||||
|
export const GET_SPONSOR_SPONSORSHIPS_PRESENTER_TOKEN = 'GetSponsorSponsorshipsPresenter';
|
||||||
|
export const GET_SPONSOR_PRESENTER_TOKEN = 'GetSponsorPresenter';
|
||||||
|
export const GET_PENDING_SPONSORSHIP_REQUESTS_PRESENTER_TOKEN = 'GetPendingSponsorshipRequestsPresenter';
|
||||||
|
export const ACCEPT_SPONSORSHIP_REQUEST_PRESENTER_TOKEN = 'AcceptSponsorshipRequestPresenter';
|
||||||
|
export const REJECT_SPONSORSHIP_REQUEST_PRESENTER_TOKEN = 'RejectSponsorshipRequestPresenter';
|
||||||
|
export const GET_SPONSOR_BILLING_PRESENTER_TOKEN = 'SponsorBillingPresenter';
|
||||||
|
|
||||||
// Use case / application service tokens
|
// Use case / application service tokens
|
||||||
export const GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN = 'GetSponsorshipPricingUseCase';
|
export const GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN = 'GetSponsorshipPricingUseCase';
|
||||||
export const GET_SPONSORS_USE_CASE_TOKEN = 'GetSponsorsUseCase';
|
export const GET_SPONSORS_USE_CASE_TOKEN = 'GetSponsorsUseCase';
|
||||||
@@ -64,6 +89,19 @@ export const ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN = 'AcceptSponsorshipReque
|
|||||||
export const REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN = 'RejectSponsorshipRequestUseCase';
|
export const REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN = 'RejectSponsorshipRequestUseCase';
|
||||||
export const GET_SPONSOR_BILLING_USE_CASE_TOKEN = 'GetSponsorBillingUseCase';
|
export const GET_SPONSOR_BILLING_USE_CASE_TOKEN = 'GetSponsorBillingUseCase';
|
||||||
|
|
||||||
|
// Output port tokens
|
||||||
|
export const GET_SPONSORSHIP_PRICING_OUTPUT_PORT_TOKEN = 'GetSponsorshipPricingOutputPort_TOKEN';
|
||||||
|
export const GET_SPONSORS_OUTPUT_PORT_TOKEN = 'GetSponsorsOutputPort_TOKEN';
|
||||||
|
export const CREATE_SPONSOR_OUTPUT_PORT_TOKEN = 'CreateSponsorOutputPort_TOKEN';
|
||||||
|
export const GET_SPONSOR_DASHBOARD_OUTPUT_PORT_TOKEN = 'GetSponsorDashboardOutputPort_TOKEN';
|
||||||
|
export const GET_SPONSOR_SPONSORSHIPS_OUTPUT_PORT_TOKEN = 'GetSponsorSponsorshipsOutputPort_TOKEN';
|
||||||
|
export const GET_ENTITY_SPONSORSHIP_PRICING_OUTPUT_PORT_TOKEN = 'GetEntitySponsorshipPricingOutputPort_TOKEN';
|
||||||
|
export const GET_SPONSOR_OUTPUT_PORT_TOKEN = 'GetSponsorOutputPort_TOKEN';
|
||||||
|
export const GET_PENDING_SPONSORSHIP_REQUESTS_OUTPUT_PORT_TOKEN = 'GetPendingSponsorshipRequestsOutputPort_TOKEN';
|
||||||
|
export const ACCEPT_SPONSORSHIP_REQUEST_OUTPUT_PORT_TOKEN = 'AcceptSponsorshipRequestOutputPort_TOKEN';
|
||||||
|
export const REJECT_SPONSORSHIP_REQUEST_OUTPUT_PORT_TOKEN = 'RejectSponsorshipRequestOutputPort_TOKEN';
|
||||||
|
export const GET_SPONSOR_BILLING_OUTPUT_PORT_TOKEN = 'GetSponsorBillingOutputPort_TOKEN';
|
||||||
|
|
||||||
export const SponsorProviders: Provider[] = [
|
export const SponsorProviders: Provider[] = [
|
||||||
SponsorService,
|
SponsorService,
|
||||||
// Repositories
|
// Repositories
|
||||||
@@ -111,27 +149,94 @@ export const SponsorProviders: Provider[] = [
|
|||||||
provide: LOGGER_TOKEN,
|
provide: LOGGER_TOKEN,
|
||||||
useClass: ConsoleLogger,
|
useClass: ConsoleLogger,
|
||||||
},
|
},
|
||||||
|
// Presenters
|
||||||
|
GetEntitySponsorshipPricingPresenter,
|
||||||
|
GetSponsorsPresenter,
|
||||||
|
CreateSponsorPresenter,
|
||||||
|
GetSponsorDashboardPresenter,
|
||||||
|
GetSponsorSponsorshipsPresenter,
|
||||||
|
GetSponsorPresenter,
|
||||||
|
GetPendingSponsorshipRequestsPresenter,
|
||||||
|
AcceptSponsorshipRequestPresenter,
|
||||||
|
RejectSponsorshipRequestPresenter,
|
||||||
|
SponsorBillingPresenter,
|
||||||
|
// Output ports
|
||||||
|
{
|
||||||
|
provide: GET_ENTITY_SPONSORSHIP_PRICING_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: GetEntitySponsorshipPricingPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GET_SPONSORS_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: GetSponsorsPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: CREATE_SPONSOR_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: CreateSponsorPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GET_SPONSOR_DASHBOARD_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: GetSponsorDashboardPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GET_SPONSOR_SPONSORSHIPS_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: GetSponsorSponsorshipsPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GET_SPONSOR_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: GetSponsorPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GET_PENDING_SPONSORSHIP_REQUESTS_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: GetPendingSponsorshipRequestsPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: ACCEPT_SPONSORSHIP_REQUEST_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: AcceptSponsorshipRequestPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: REJECT_SPONSORSHIP_REQUEST_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: RejectSponsorshipRequestPresenter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: GET_SPONSOR_BILLING_OUTPUT_PORT_TOKEN,
|
||||||
|
useExisting: SponsorBillingPresenter,
|
||||||
|
},
|
||||||
// Use cases
|
// Use cases
|
||||||
{
|
{
|
||||||
provide: GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
|
provide: GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
|
||||||
useFactory: () => new GetSponsorshipPricingUseCase(),
|
useFactory: (output: UseCaseOutputPort<any>) => new GetSponsorshipPricingUseCase(output),
|
||||||
inject: [],
|
inject: [GET_SPONSORSHIP_PRICING_OUTPUT_PORT_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GET_SPONSORS_USE_CASE_TOKEN,
|
provide: GET_SPONSORS_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorRepo: ISponsorRepository) => new GetSponsorsUseCase(sponsorRepo),
|
useFactory: (sponsorRepo: ISponsorRepository, output: UseCaseOutputPort<any>) => new GetSponsorsUseCase(sponsorRepo, output),
|
||||||
inject: [SPONSOR_REPOSITORY_TOKEN],
|
inject: [SPONSOR_REPOSITORY_TOKEN, GET_SPONSORS_OUTPUT_PORT_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: CREATE_SPONSOR_USE_CASE_TOKEN,
|
provide: CREATE_SPONSOR_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorRepo: ISponsorRepository) => new CreateSponsorUseCase(sponsorRepo),
|
useFactory: (sponsorRepo: ISponsorRepository, logger: Logger, output: UseCaseOutputPort<any>) => new CreateSponsorUseCase(sponsorRepo, logger, output),
|
||||||
inject: [SPONSOR_REPOSITORY_TOKEN],
|
inject: [SPONSOR_REPOSITORY_TOKEN, LOGGER_TOKEN, CREATE_SPONSOR_OUTPUT_PORT_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN,
|
provide: GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorRepo: ISponsorRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonRepo: ISeasonRepository, leagueRepo: ILeagueRepository, leagueMembershipRepo: ILeagueMembershipRepository, raceRepo: IRaceRepository) =>
|
useFactory: (
|
||||||
new GetSponsorDashboardUseCase(sponsorRepo, seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo),
|
sponsorRepo: ISponsorRepository,
|
||||||
inject: [SPONSOR_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, SEASON_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN],
|
seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
||||||
|
seasonRepo: ISeasonRepository,
|
||||||
|
leagueRepo: ILeagueRepository,
|
||||||
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
|
raceRepo: IRaceRepository,
|
||||||
|
output: UseCaseOutputPort<any>,
|
||||||
|
) => new GetSponsorDashboardUseCase(sponsorRepo, seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo, output),
|
||||||
|
inject: [
|
||||||
|
SPONSOR_REPOSITORY_TOKEN,
|
||||||
|
SEASON_SPONSORSHIP_REPOSITORY_TOKEN,
|
||||||
|
SEASON_REPOSITORY_TOKEN,
|
||||||
|
LEAGUE_REPOSITORY_TOKEN,
|
||||||
|
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
||||||
|
RACE_REPOSITORY_TOKEN,
|
||||||
|
GET_SPONSOR_DASHBOARD_OUTPUT_PORT_TOKEN,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN,
|
provide: GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN,
|
||||||
@@ -142,7 +247,8 @@ export const SponsorProviders: Provider[] = [
|
|||||||
leagueRepo: ILeagueRepository,
|
leagueRepo: ILeagueRepository,
|
||||||
leagueMembershipRepo: ILeagueMembershipRepository,
|
leagueMembershipRepo: ILeagueMembershipRepository,
|
||||||
raceRepo: IRaceRepository,
|
raceRepo: IRaceRepository,
|
||||||
) => new GetSponsorSponsorshipsUseCase(sponsorRepo, seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo),
|
output: UseCaseOutputPort<any>,
|
||||||
|
) => new GetSponsorSponsorshipsUseCase(sponsorRepo, seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo, output),
|
||||||
inject: [
|
inject: [
|
||||||
SPONSOR_REPOSITORY_TOKEN,
|
SPONSOR_REPOSITORY_TOKEN,
|
||||||
SEASON_SPONSORSHIP_REPOSITORY_TOKEN,
|
SEASON_SPONSORSHIP_REPOSITORY_TOKEN,
|
||||||
@@ -150,41 +256,97 @@ export const SponsorProviders: Provider[] = [
|
|||||||
LEAGUE_REPOSITORY_TOKEN,
|
LEAGUE_REPOSITORY_TOKEN,
|
||||||
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
||||||
RACE_REPOSITORY_TOKEN,
|
RACE_REPOSITORY_TOKEN,
|
||||||
|
GET_SPONSOR_SPONSORSHIPS_OUTPUT_PORT_TOKEN,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GET_SPONSOR_BILLING_USE_CASE_TOKEN,
|
provide: GET_SPONSOR_BILLING_USE_CASE_TOKEN,
|
||||||
useFactory: (paymentRepo: IPaymentRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository) =>
|
useFactory: (
|
||||||
new GetSponsorBillingUseCase(paymentRepo, seasonSponsorshipRepo),
|
paymentRepo: IPaymentRepository,
|
||||||
|
seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
||||||
|
) => {
|
||||||
|
return new GetSponsorBillingUseCase(paymentRepo, seasonSponsorshipRepo);
|
||||||
|
},
|
||||||
inject: ['IPaymentRepository', SEASON_SPONSORSHIP_REPOSITORY_TOKEN],
|
inject: ['IPaymentRepository', SEASON_SPONSORSHIP_REPOSITORY_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GET_ENTITY_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
|
provide: GET_ENTITY_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorshipPricingRepo: ISponsorshipPricingRepository, sponsorshipRequestRepo: ISponsorshipRequestRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, logger: Logger) =>
|
useFactory: (
|
||||||
new GetEntitySponsorshipPricingUseCase(sponsorshipPricingRepo, sponsorshipRequestRepo, seasonSponsorshipRepo, logger),
|
sponsorshipPricingRepo: ISponsorshipPricingRepository,
|
||||||
inject: [SPONSORSHIP_PRICING_REPOSITORY_TOKEN, SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||||
|
seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
||||||
|
logger: Logger,
|
||||||
|
output: UseCaseOutputPort<any>,
|
||||||
|
) => new GetEntitySponsorshipPricingUseCase(sponsorshipPricingRepo, sponsorshipRequestRepo, seasonSponsorshipRepo, logger, output),
|
||||||
|
inject: [
|
||||||
|
SPONSORSHIP_PRICING_REPOSITORY_TOKEN,
|
||||||
|
SPONSORSHIP_REQUEST_REPOSITORY_TOKEN,
|
||||||
|
SEASON_SPONSORSHIP_REPOSITORY_TOKEN,
|
||||||
|
LOGGER_TOKEN,
|
||||||
|
GET_ENTITY_SPONSORSHIP_PRICING_OUTPUT_PORT_TOKEN,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GET_SPONSOR_USE_CASE_TOKEN,
|
provide: GET_SPONSOR_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorRepo: ISponsorRepository) => new GetSponsorUseCase(sponsorRepo),
|
useFactory: (sponsorRepo: ISponsorRepository, output: UseCaseOutputPort<any>) => new GetSponsorUseCase(sponsorRepo, output),
|
||||||
inject: [SPONSOR_REPOSITORY_TOKEN],
|
inject: [SPONSOR_REPOSITORY_TOKEN, GET_SPONSOR_OUTPUT_PORT_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN,
|
provide: GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, sponsorRepo: ISponsorRepository) =>
|
useFactory: (
|
||||||
new GetPendingSponsorshipRequestsUseCase(sponsorshipRequestRepo, sponsorRepo),
|
sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||||
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SPONSOR_REPOSITORY_TOKEN],
|
sponsorRepo: ISponsorRepository,
|
||||||
|
output: UseCaseOutputPort<any>,
|
||||||
|
) => new GetPendingSponsorshipRequestsUseCase(sponsorshipRequestRepo, sponsorRepo, output),
|
||||||
|
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SPONSOR_REPOSITORY_TOKEN, GET_PENDING_SPONSORSHIP_REQUESTS_OUTPUT_PORT_TOKEN],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
provide: ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonRepo: ISeasonRepository, notificationService: NotificationService, paymentGateway: IPaymentGateway, walletRepository: IWalletRepository, leagueWalletRepository: ILeagueWalletRepository, logger: Logger) =>
|
useFactory: (
|
||||||
new AcceptSponsorshipRequestUseCase(sponsorshipRequestRepo, seasonSponsorshipRepo, seasonRepo, notificationService, paymentGateway, walletRepository, leagueWalletRepository, logger),
|
sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||||
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, SEASON_REPOSITORY_TOKEN, 'INotificationService', 'IPaymentGateway', 'IWalletRepository', 'ILeagueWalletRepository', LOGGER_TOKEN],
|
seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
||||||
|
seasonRepo: ISeasonRepository,
|
||||||
|
notificationService: NotificationService,
|
||||||
|
walletRepository: IWalletRepository,
|
||||||
|
leagueWalletRepository: ILeagueWalletRepository,
|
||||||
|
logger: Logger,
|
||||||
|
output: UseCaseOutputPort<any>,
|
||||||
|
) => {
|
||||||
|
// Create a mock payment processor function
|
||||||
|
const paymentProcessor = async (_input: any) => {
|
||||||
|
return { success: true, transactionId: `txn_${Date.now()}` };
|
||||||
|
};
|
||||||
|
|
||||||
|
return new AcceptSponsorshipRequestUseCase(
|
||||||
|
sponsorshipRequestRepo,
|
||||||
|
seasonSponsorshipRepo,
|
||||||
|
seasonRepo,
|
||||||
|
notificationService,
|
||||||
|
paymentProcessor,
|
||||||
|
walletRepository,
|
||||||
|
leagueWalletRepository,
|
||||||
|
logger,
|
||||||
|
output
|
||||||
|
);
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
SPONSORSHIP_REQUEST_REPOSITORY_TOKEN,
|
||||||
|
SEASON_SPONSORSHIP_REPOSITORY_TOKEN,
|
||||||
|
SEASON_REPOSITORY_TOKEN,
|
||||||
|
'INotificationService',
|
||||||
|
'IWalletRepository',
|
||||||
|
'ILeagueWalletRepository',
|
||||||
|
LOGGER_TOKEN,
|
||||||
|
ACCEPT_SPONSORSHIP_REQUEST_OUTPUT_PORT_TOKEN,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
provide: REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
||||||
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, logger: Logger) =>
|
useFactory: (
|
||||||
new RejectSponsorshipRequestUseCase(sponsorshipRequestRepo, logger),
|
sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||||
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
logger: Logger,
|
||||||
|
output: UseCaseOutputPort<any>,
|
||||||
|
) => new RejectSponsorshipRequestUseCase(sponsorshipRequestRepo, logger, output),
|
||||||
|
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, LOGGER_TOKEN, REJECT_SPONSORSHIP_REQUEST_OUTPUT_PORT_TOKEN],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -1,15 +1,27 @@
|
|||||||
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
|
import type { GetSponsorBillingUseCase } from '@core/payments/application/use-cases/GetSponsorBillingUseCase';
|
||||||
import { Result } from '@core/shared/application/Result';
|
|
||||||
import type { Logger } from '@core/shared/application';
|
|
||||||
import type { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
|
|
||||||
import type { GetSponsorsUseCase } from '@core/racing/application/use-cases/GetSponsorsUseCase';
|
|
||||||
import type { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateSponsorUseCase';
|
|
||||||
import type { GetSponsorDashboardUseCase } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
|
|
||||||
import type { GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
|
|
||||||
import type { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
|
|
||||||
import type { GetPendingSponsorshipRequestsUseCase } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
|
|
||||||
import type { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
import type { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
||||||
|
import type { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateSponsorUseCase';
|
||||||
|
import type { GetPendingSponsorshipRequestsUseCase } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
|
||||||
|
import type { GetSponsorDashboardInput, GetSponsorDashboardUseCase } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
|
||||||
|
import type { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
|
||||||
|
import type { GetSponsorSponsorshipsInput, GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
|
||||||
|
import type { GetSponsorsUseCase } from '@core/racing/application/use-cases/GetSponsorsUseCase';
|
||||||
|
import type { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
|
||||||
import type { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
import type { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
|
import type { Logger } from '@core/shared/application';
|
||||||
|
import { Result } from '@core/shared/application/Result';
|
||||||
|
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
||||||
|
import type { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
|
||||||
|
import { AcceptSponsorshipRequestPresenter } from './presenters/AcceptSponsorshipRequestPresenter';
|
||||||
|
import { CreateSponsorPresenter } from './presenters/CreateSponsorPresenter';
|
||||||
|
import { GetEntitySponsorshipPricingPresenter } from './presenters/GetEntitySponsorshipPricingPresenter';
|
||||||
|
import { GetPendingSponsorshipRequestsPresenter } from './presenters/GetPendingSponsorshipRequestsPresenter';
|
||||||
|
import { GetSponsorDashboardPresenter } from './presenters/GetSponsorDashboardPresenter';
|
||||||
|
import { GetSponsorPresenter } from './presenters/GetSponsorPresenter';
|
||||||
|
import { GetSponsorSponsorshipsPresenter } from './presenters/GetSponsorSponsorshipsPresenter';
|
||||||
|
import { GetSponsorsPresenter } from './presenters/GetSponsorsPresenter';
|
||||||
|
import { RejectSponsorshipRequestPresenter } from './presenters/RejectSponsorshipRequestPresenter';
|
||||||
|
import { SponsorBillingPresenter } from './presenters/SponsorBillingPresenter';
|
||||||
import { SponsorService } from './SponsorService';
|
import { SponsorService } from './SponsorService';
|
||||||
|
|
||||||
describe('SponsorService', () => {
|
describe('SponsorService', () => {
|
||||||
@@ -23,8 +35,21 @@ describe('SponsorService', () => {
|
|||||||
let getPendingSponsorshipRequestsUseCase: { execute: Mock };
|
let getPendingSponsorshipRequestsUseCase: { execute: Mock };
|
||||||
let acceptSponsorshipRequestUseCase: { execute: Mock };
|
let acceptSponsorshipRequestUseCase: { execute: Mock };
|
||||||
let rejectSponsorshipRequestUseCase: { execute: Mock };
|
let rejectSponsorshipRequestUseCase: { execute: Mock };
|
||||||
|
let getSponsorBillingUseCase: { execute: Mock };
|
||||||
let logger: Logger;
|
let logger: Logger;
|
||||||
|
|
||||||
|
// Presenters
|
||||||
|
let getEntitySponsorshipPricingPresenter: GetEntitySponsorshipPricingPresenter;
|
||||||
|
let getSponsorsPresenter: GetSponsorsPresenter;
|
||||||
|
let createSponsorPresenter: CreateSponsorPresenter;
|
||||||
|
let getSponsorDashboardPresenter: GetSponsorDashboardPresenter;
|
||||||
|
let getSponsorSponsorshipsPresenter: GetSponsorSponsorshipsPresenter;
|
||||||
|
let getSponsorPresenter: GetSponsorPresenter;
|
||||||
|
let getPendingSponsorshipRequestsPresenter: GetPendingSponsorshipRequestsPresenter;
|
||||||
|
let acceptSponsorshipRequestPresenter: AcceptSponsorshipRequestPresenter;
|
||||||
|
let rejectSponsorshipRequestPresenter: RejectSponsorshipRequestPresenter;
|
||||||
|
let sponsorBillingPresenter: SponsorBillingPresenter;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getSponsorshipPricingUseCase = { execute: vi.fn() };
|
getSponsorshipPricingUseCase = { execute: vi.fn() };
|
||||||
getSponsorsUseCase = { execute: vi.fn() };
|
getSponsorsUseCase = { execute: vi.fn() };
|
||||||
@@ -35,6 +60,7 @@ describe('SponsorService', () => {
|
|||||||
getPendingSponsorshipRequestsUseCase = { execute: vi.fn() };
|
getPendingSponsorshipRequestsUseCase = { execute: vi.fn() };
|
||||||
acceptSponsorshipRequestUseCase = { execute: vi.fn() };
|
acceptSponsorshipRequestUseCase = { execute: vi.fn() };
|
||||||
rejectSponsorshipRequestUseCase = { execute: vi.fn() };
|
rejectSponsorshipRequestUseCase = { execute: vi.fn() };
|
||||||
|
getSponsorBillingUseCase = { execute: vi.fn() };
|
||||||
logger = {
|
logger = {
|
||||||
debug: vi.fn(),
|
debug: vi.fn(),
|
||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
@@ -42,6 +68,18 @@ describe('SponsorService', () => {
|
|||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
} as unknown as Logger;
|
} as unknown as Logger;
|
||||||
|
|
||||||
|
// Initialize presenters
|
||||||
|
getEntitySponsorshipPricingPresenter = new GetEntitySponsorshipPricingPresenter();
|
||||||
|
getSponsorsPresenter = new GetSponsorsPresenter();
|
||||||
|
createSponsorPresenter = new CreateSponsorPresenter();
|
||||||
|
getSponsorDashboardPresenter = new GetSponsorDashboardPresenter();
|
||||||
|
getSponsorSponsorshipsPresenter = new GetSponsorSponsorshipsPresenter();
|
||||||
|
getSponsorPresenter = new GetSponsorPresenter();
|
||||||
|
getPendingSponsorshipRequestsPresenter = new GetPendingSponsorshipRequestsPresenter();
|
||||||
|
acceptSponsorshipRequestPresenter = new AcceptSponsorshipRequestPresenter();
|
||||||
|
rejectSponsorshipRequestPresenter = new RejectSponsorshipRequestPresenter();
|
||||||
|
sponsorBillingPresenter = new SponsorBillingPresenter();
|
||||||
|
|
||||||
service = new SponsorService(
|
service = new SponsorService(
|
||||||
getSponsorshipPricingUseCase as unknown as GetSponsorshipPricingUseCase,
|
getSponsorshipPricingUseCase as unknown as GetSponsorshipPricingUseCase,
|
||||||
getSponsorsUseCase as unknown as GetSponsorsUseCase,
|
getSponsorsUseCase as unknown as GetSponsorsUseCase,
|
||||||
@@ -52,28 +90,39 @@ describe('SponsorService', () => {
|
|||||||
getPendingSponsorshipRequestsUseCase as unknown as GetPendingSponsorshipRequestsUseCase,
|
getPendingSponsorshipRequestsUseCase as unknown as GetPendingSponsorshipRequestsUseCase,
|
||||||
acceptSponsorshipRequestUseCase as unknown as AcceptSponsorshipRequestUseCase,
|
acceptSponsorshipRequestUseCase as unknown as AcceptSponsorshipRequestUseCase,
|
||||||
rejectSponsorshipRequestUseCase as unknown as RejectSponsorshipRequestUseCase,
|
rejectSponsorshipRequestUseCase as unknown as RejectSponsorshipRequestUseCase,
|
||||||
|
getSponsorBillingUseCase as unknown as GetSponsorBillingUseCase,
|
||||||
logger,
|
logger,
|
||||||
|
getEntitySponsorshipPricingPresenter,
|
||||||
|
getSponsorsPresenter,
|
||||||
|
createSponsorPresenter,
|
||||||
|
getSponsorDashboardPresenter,
|
||||||
|
getSponsorSponsorshipsPresenter,
|
||||||
|
getSponsorPresenter,
|
||||||
|
getPendingSponsorshipRequestsPresenter,
|
||||||
|
acceptSponsorshipRequestPresenter,
|
||||||
|
rejectSponsorshipRequestPresenter,
|
||||||
|
sponsorBillingPresenter,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getEntitySponsorshipPricing', () => {
|
describe('getEntitySponsorshipPricing', () => {
|
||||||
it('returns presenter with pricing data on success', async () => {
|
it('returns pricing data on success', async () => {
|
||||||
const outputPort = {
|
const outputPort = {
|
||||||
entityType: 'season',
|
entityType: 'season',
|
||||||
entityId: 'season-1',
|
entityId: 'season-1',
|
||||||
pricing: [
|
tiers: [
|
||||||
{ id: 'tier-gold', level: 'Gold', price: 500, currency: 'USD' },
|
{ name: 'Gold', price: 500, benefits: ['Main slot'] },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
getSponsorshipPricingUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
getSponsorshipPricingUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
||||||
|
|
||||||
const presenter = await service.getEntitySponsorshipPricing();
|
const result = await service.getEntitySponsorshipPricing();
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({
|
expect(result).toEqual({
|
||||||
entityType: 'season',
|
entityType: 'season',
|
||||||
entityId: 'season-1',
|
entityId: 'season-1',
|
||||||
pricing: [
|
pricing: [
|
||||||
{ id: 'tier-gold', level: 'Gold', price: 500, currency: 'USD' },
|
{ id: 'Gold', level: 'Gold', price: 500, currency: 'USD' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -81,9 +130,9 @@ describe('SponsorService', () => {
|
|||||||
it('returns empty pricing on error', async () => {
|
it('returns empty pricing on error', async () => {
|
||||||
getSponsorshipPricingUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
|
getSponsorshipPricingUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
|
||||||
|
|
||||||
const presenter = await service.getEntitySponsorshipPricing();
|
const result = await service.getEntitySponsorshipPricing();
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({
|
expect(result).toEqual({
|
||||||
entityType: 'season',
|
entityType: 'season',
|
||||||
entityId: '',
|
entityId: '',
|
||||||
pricing: [],
|
pricing: [],
|
||||||
@@ -92,82 +141,93 @@ describe('SponsorService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getSponsors', () => {
|
describe('getSponsors', () => {
|
||||||
it('returns sponsors in presenter on success', async () => {
|
it('returns sponsors on success', async () => {
|
||||||
const outputPort = { sponsors: [{ id: 's1', name: 'S1', contactEmail: 's1@test', createdAt: new Date() }] };
|
const sponsors = [{ id: 's1', name: 'S1', contactEmail: 's1@test', createdAt: new Date() }];
|
||||||
|
const outputPort = { sponsors };
|
||||||
getSponsorsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
getSponsorsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
||||||
|
|
||||||
const presenter = await service.getSponsors();
|
const result = await service.getSponsors();
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({ sponsors: outputPort.sponsors });
|
expect(result).toEqual({ sponsors });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns empty list on error', async () => {
|
it('returns empty list on error', async () => {
|
||||||
getSponsorsUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
|
getSponsorsUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
|
||||||
|
|
||||||
const presenter = await service.getSponsors();
|
const result = await service.getSponsors();
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({ sponsors: [] });
|
expect(result).toEqual({ sponsors: [] });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createSponsor', () => {
|
describe('createSponsor', () => {
|
||||||
it('returns created sponsor in presenter on success', async () => {
|
it('returns created sponsor on success', async () => {
|
||||||
const input = { name: 'Test', contactEmail: 'test@example.com' };
|
const input: CreateSponsorInputDTO = { name: 'Test', contactEmail: 'test@example.com' };
|
||||||
const outputPort = {
|
const sponsor = {
|
||||||
sponsor: {
|
id: 's1',
|
||||||
id: 's1',
|
name: 'Test',
|
||||||
name: 'Test',
|
contactEmail: 'test@example.com',
|
||||||
contactEmail: 'test@example.com',
|
createdAt: new Date(),
|
||||||
createdAt: new Date(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
const outputPort = { sponsor };
|
||||||
createSponsorUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
createSponsorUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
||||||
|
|
||||||
const presenter = await service.createSponsor(input as any);
|
const result = await service.createSponsor(input);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({ sponsor: outputPort.sponsor });
|
expect(result).toEqual({ sponsor });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws on error', async () => {
|
it('throws on error', async () => {
|
||||||
const input = { name: 'Test', contactEmail: 'test@example.com' };
|
const input: CreateSponsorInputDTO = { name: 'Test', contactEmail: 'test@example.com' };
|
||||||
createSponsorUseCase.execute.mockResolvedValue(
|
createSponsorUseCase.execute.mockResolvedValue(
|
||||||
Result.err({ code: 'VALIDATION_ERROR', details: { message: 'Invalid' } }),
|
Result.err({ code: 'VALIDATION_ERROR', details: { message: 'Invalid' } }),
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(service.createSponsor(input as any)).rejects.toThrow('Invalid');
|
await expect(service.createSponsor(input)).rejects.toThrow('Invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getSponsorDashboard', () => {
|
describe('getSponsorDashboard', () => {
|
||||||
it('returns dashboard in presenter on success', async () => {
|
it('returns dashboard on success', async () => {
|
||||||
const params = { sponsorId: 's1' };
|
const params: GetSponsorDashboardInput = { sponsorId: 's1' };
|
||||||
const outputPort = {
|
const outputPort = {
|
||||||
sponsorId: 's1',
|
sponsorId: 's1',
|
||||||
sponsorName: 'S1',
|
sponsorName: 'S1',
|
||||||
metrics: {} as any,
|
metrics: {
|
||||||
|
impressions: 0,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 0,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 0,
|
||||||
|
drivers: 0,
|
||||||
|
exposure: 0,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
sponsoredLeagues: [],
|
sponsoredLeagues: [],
|
||||||
investment: {} as any,
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
||||||
|
|
||||||
const presenter = await service.getSponsorDashboard(params as any);
|
const result = await service.getSponsorDashboard(params);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(outputPort);
|
expect(result).toEqual(outputPort);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null in presenter on error', async () => {
|
it('throws on error', async () => {
|
||||||
const params = { sponsorId: 's1' };
|
const params: GetSponsorDashboardInput = { sponsorId: 's1' };
|
||||||
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
|
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
|
||||||
|
|
||||||
const presenter = await service.getSponsorDashboard(params as any);
|
await expect(service.getSponsorDashboard(params)).rejects.toThrow('Sponsor dashboard not found');
|
||||||
|
|
||||||
expect(presenter.viewModel).toBeNull();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getSponsorSponsorships', () => {
|
describe('getSponsorSponsorships', () => {
|
||||||
it('returns sponsorships in presenter on success', async () => {
|
it('returns sponsorships on success', async () => {
|
||||||
const params = { sponsorId: 's1' };
|
const params: GetSponsorSponsorshipsInput = { sponsorId: 's1' };
|
||||||
const outputPort = {
|
const outputPort = {
|
||||||
sponsorId: 's1',
|
sponsorId: 's1',
|
||||||
sponsorName: 'S1',
|
sponsorName: 'S1',
|
||||||
@@ -182,46 +242,43 @@ describe('SponsorService', () => {
|
|||||||
};
|
};
|
||||||
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
||||||
|
|
||||||
const presenter = await service.getSponsorSponsorships(params as any);
|
const result = await service.getSponsorSponsorships(params);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(outputPort);
|
expect(result).toEqual(outputPort);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null in presenter on error', async () => {
|
it('throws on error', async () => {
|
||||||
const params = { sponsorId: 's1' };
|
const params: GetSponsorSponsorshipsInput = { sponsorId: 's1' };
|
||||||
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(
|
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(
|
||||||
Result.err({ code: 'REPOSITORY_ERROR' }),
|
Result.err({ code: 'REPOSITORY_ERROR' }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const presenter = await service.getSponsorSponsorships(params as any);
|
await expect(service.getSponsorSponsorships(params)).rejects.toThrow('Sponsor sponsorships not found');
|
||||||
|
|
||||||
expect(presenter.viewModel).toBeNull();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getSponsor', () => {
|
describe('getSponsor', () => {
|
||||||
it('returns sponsor in presenter when found', async () => {
|
it('returns sponsor when found', async () => {
|
||||||
const sponsorId = 's1';
|
const sponsorId = 's1';
|
||||||
const output = { sponsor: { id: sponsorId, name: 'S1' } };
|
const sponsor = { id: sponsorId, name: 'S1' };
|
||||||
|
const output = { sponsor };
|
||||||
getSponsorUseCase.execute.mockResolvedValue(Result.ok(output));
|
getSponsorUseCase.execute.mockResolvedValue(Result.ok(output));
|
||||||
|
|
||||||
const presenter = await service.getSponsor(sponsorId);
|
const result = await service.getSponsor(sponsorId);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({ sponsor: output.sponsor });
|
expect(result).toEqual({ sponsor });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null in presenter when not found', async () => {
|
it('throws when not found', async () => {
|
||||||
const sponsorId = 's1';
|
const sponsorId = 's1';
|
||||||
getSponsorUseCase.execute.mockResolvedValue(Result.ok(null));
|
getSponsorUseCase.execute.mockResolvedValue(Result.ok(null));
|
||||||
|
|
||||||
const presenter = await service.getSponsor(sponsorId);
|
await expect(service.getSponsor(sponsorId)).rejects.toThrow('Sponsor not found');
|
||||||
|
|
||||||
expect(presenter.viewModel).toBeNull();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getPendingSponsorshipRequests', () => {
|
describe('getPendingSponsorshipRequests', () => {
|
||||||
it('returns requests in presenter on success', async () => {
|
it('returns requests on success', async () => {
|
||||||
const params = { entityType: 'season' as const, entityId: 'season-1' };
|
const params = { entityType: 'season' as const, entityId: 'season-1' };
|
||||||
const outputPort = {
|
const outputPort = {
|
||||||
entityType: 'season',
|
entityType: 'season',
|
||||||
@@ -231,9 +288,9 @@ describe('SponsorService', () => {
|
|||||||
};
|
};
|
||||||
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
||||||
|
|
||||||
const presenter = await service.getPendingSponsorshipRequests(params);
|
const result = await service.getPendingSponsorshipRequests(params);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(outputPort);
|
expect(result).toEqual(outputPort);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns empty result on error', async () => {
|
it('returns empty result on error', async () => {
|
||||||
@@ -242,9 +299,9 @@ describe('SponsorService', () => {
|
|||||||
Result.err({ code: 'REPOSITORY_ERROR' }),
|
Result.err({ code: 'REPOSITORY_ERROR' }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const presenter = await service.getPendingSponsorshipRequests(params);
|
const result = await service.getPendingSponsorshipRequests(params);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({
|
expect(result).toEqual({
|
||||||
entityType: 'season',
|
entityType: 'season',
|
||||||
entityId: 'season-1',
|
entityId: 'season-1',
|
||||||
requests: [],
|
requests: [],
|
||||||
@@ -253,8 +310,8 @@ describe('SponsorService', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('acceptSponsorshipRequest', () => {
|
describe('SponsorshipRequest', () => {
|
||||||
it('returns accept result in presenter on success', async () => {
|
it('returns accept result on success', async () => {
|
||||||
const requestId = 'r1';
|
const requestId = 'r1';
|
||||||
const respondedBy = 'u1';
|
const respondedBy = 'u1';
|
||||||
const outputPort = {
|
const outputPort = {
|
||||||
@@ -267,100 +324,114 @@ describe('SponsorService', () => {
|
|||||||
};
|
};
|
||||||
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(outputPort));
|
||||||
|
|
||||||
const presenter = await service.acceptSponsorshipRequest(requestId, respondedBy);
|
const result = await service.acceptSponsorshipRequest(requestId, respondedBy);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(outputPort);
|
expect(result).toEqual(outputPort);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null in presenter on error', async () => {
|
it('throws on error', async () => {
|
||||||
const requestId = 'r1';
|
const requestId = 'r1';
|
||||||
const respondedBy = 'u1';
|
const respondedBy = 'u1';
|
||||||
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(
|
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(
|
||||||
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
|
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const presenter = await service.acceptSponsorshipRequest(requestId, respondedBy);
|
await expect(service.acceptSponsorshipRequest(requestId, respondedBy)).rejects.toThrow('Accept sponsorship request failed');
|
||||||
|
|
||||||
expect(presenter.viewModel).toBeNull();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('rejectSponsorshipRequest', () => {
|
describe('rejectSponsorshipRequest', () => {
|
||||||
it('returns reject result in presenter on success', async () => {
|
it('returns reject result on success', async () => {
|
||||||
const requestId = 'r1';
|
const requestId = 'r1';
|
||||||
const respondedBy = 'u1';
|
const respondedBy = 'u1';
|
||||||
const reason = 'Not interested';
|
const reason = 'Not interested';
|
||||||
const output = {
|
const output = {
|
||||||
requestId,
|
requestId,
|
||||||
status: 'rejected' as const,
|
status: 'rejected' as const,
|
||||||
rejectedAt: new Date(),
|
respondedAt: new Date(),
|
||||||
reason,
|
rejectionReason: reason,
|
||||||
};
|
};
|
||||||
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(output));
|
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(output));
|
||||||
|
|
||||||
const presenter = await service.rejectSponsorshipRequest(requestId, respondedBy, reason);
|
const result = await service.rejectSponsorshipRequest(requestId, respondedBy, reason);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(output);
|
expect(result).toEqual(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null in presenter on error', async () => {
|
it('throws on error', async () => {
|
||||||
const requestId = 'r1';
|
const requestId = 'r1';
|
||||||
const respondedBy = 'u1';
|
const respondedBy = 'u1';
|
||||||
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(
|
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(
|
||||||
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
|
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const presenter = await service.rejectSponsorshipRequest(requestId, respondedBy);
|
await expect(service.rejectSponsorshipRequest(requestId, respondedBy)).rejects.toThrow('Reject sponsorship request failed');
|
||||||
|
|
||||||
expect(presenter.viewModel).toBeNull();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getSponsorBilling', () => {
|
describe('getSponsorBilling', () => {
|
||||||
it('returns mock billing data in presenter', async () => {
|
it('returns billing data', async () => {
|
||||||
const presenter = await service.getSponsorBilling('s1');
|
// Mock the use case to set up the presenter
|
||||||
|
getSponsorBillingUseCase.execute.mockImplementation(async () => {
|
||||||
|
sponsorBillingPresenter.present({
|
||||||
|
paymentMethods: [],
|
||||||
|
invoices: [],
|
||||||
|
stats: {
|
||||||
|
totalSpent: 0,
|
||||||
|
pendingAmount: 0,
|
||||||
|
nextPaymentDate: '',
|
||||||
|
nextPaymentAmount: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
averageMonthlySpend: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return Result.ok(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
expect(presenter.viewModel).not.toBeNull();
|
const result = await service.getSponsorBilling('s1');
|
||||||
expect(presenter.viewModel?.paymentMethods).toBeInstanceOf(Array);
|
|
||||||
expect(presenter.viewModel?.invoices).toBeInstanceOf(Array);
|
expect(result).not.toBeNull();
|
||||||
expect(presenter.viewModel?.stats).toBeDefined();
|
expect(result.paymentMethods).toBeInstanceOf(Array);
|
||||||
|
expect(result.invoices).toBeInstanceOf(Array);
|
||||||
|
expect(result.stats).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getAvailableLeagues', () => {
|
describe('getAvailableLeagues', () => {
|
||||||
it('returns mock leagues in presenter', async () => {
|
it('returns mock leagues', async () => {
|
||||||
const presenter = await service.getAvailableLeagues();
|
const result = await service.getAvailableLeagues();
|
||||||
|
|
||||||
expect(presenter.viewModel).not.toBeNull();
|
expect(result).not.toBeNull();
|
||||||
expect(presenter.viewModel?.length).toBeGreaterThan(0);
|
expect(result.viewModel).not.toBeNull();
|
||||||
|
expect(result.viewModel?.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getLeagueDetail', () => {
|
describe('getLeagueDetail', () => {
|
||||||
it('returns league detail in presenter', async () => {
|
it('returns league detail', async () => {
|
||||||
const presenter = await service.getLeagueDetail('league-1');
|
const result = await service.getLeagueDetail('league-1');
|
||||||
|
|
||||||
expect(presenter.viewModel).not.toBeNull();
|
expect(result).not.toBeNull();
|
||||||
expect(presenter.viewModel?.league.id).toBe('league-1');
|
expect(result.viewModel?.league.id).toBe('league-1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getSponsorSettings', () => {
|
describe('getSponsorSettings', () => {
|
||||||
it('returns settings in presenter', async () => {
|
it('returns settings', async () => {
|
||||||
const presenter = await service.getSponsorSettings('s1');
|
const result = await service.getSponsorSettings('s1');
|
||||||
|
|
||||||
expect(presenter.viewModel).not.toBeNull();
|
expect(result).not.toBeNull();
|
||||||
expect(presenter.viewModel?.profile).toBeDefined();
|
expect(result.viewModel?.profile).toBeDefined();
|
||||||
expect(presenter.viewModel?.notifications).toBeDefined();
|
expect(result.viewModel?.notifications).toBeDefined();
|
||||||
expect(presenter.viewModel?.privacy).toBeDefined();
|
expect(result.viewModel?.privacy).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateSponsorSettings', () => {
|
describe('updateSponsorSettings', () => {
|
||||||
it('returns success result in presenter', async () => {
|
it('returns success result', async () => {
|
||||||
const presenter = await service.updateSponsorSettings('s1', {});
|
const result = await service.updateSponsorSettings('s1', {});
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({ success: true });
|
expect(result.viewModel).toEqual({ success: true });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
import { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
|
import { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
|
||||||
|
import { CreateSponsorOutputDTO } from './dtos/CreateSponsorOutputDTO';
|
||||||
import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQueryParamsDTO';
|
import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQueryParamsDTO';
|
||||||
|
import { SponsorDashboardDTO } from './dtos/SponsorDashboardDTO';
|
||||||
import { GetSponsorSponsorshipsQueryParamsDTO } from './dtos/GetSponsorSponsorshipsQueryParamsDTO';
|
import { GetSponsorSponsorshipsQueryParamsDTO } from './dtos/GetSponsorSponsorshipsQueryParamsDTO';
|
||||||
import { AcceptSponsorshipRequestInputDTO } from './dtos/AcceptSponsorshipRequestInputDTO';
|
import { SponsorSponsorshipsDTO } from './dtos/SponsorSponsorshipsDTO';
|
||||||
import { RejectSponsorshipRequestInputDTO } from './dtos/RejectSponsorshipRequestInputDTO';
|
import { GetSponsorOutputDTO } from './dtos/GetSponsorOutputDTO';
|
||||||
import { PaymentMethodDTO } from './dtos/PaymentMethodDTO';
|
import { GetPendingSponsorshipRequestsOutputDTO } from './dtos/GetPendingSponsorshipRequestsOutputDTO';
|
||||||
import { InvoiceDTO } from './dtos/InvoiceDTO';
|
import { GetEntitySponsorshipPricingResultDTO } from './dtos/GetEntitySponsorshipPricingResultDTO';
|
||||||
import { BillingStatsDTO } from './dtos/BillingStatsDTO';
|
import { GetSponsorsOutputDTO } from './dtos/GetSponsorsOutputDTO';
|
||||||
import { AvailableLeagueDTO } from './dtos/AvailableLeagueDTO';
|
import { AvailableLeagueDTO } from './dtos/AvailableLeagueDTO';
|
||||||
import { LeagueDetailDTO } from './dtos/LeagueDetailDTO';
|
import { LeagueDetailDTO } from './dtos/LeagueDetailDTO';
|
||||||
import { DriverDTO } from './dtos/DriverDTO';
|
import { DriverDTO } from './dtos/DriverDTO';
|
||||||
@@ -14,6 +16,9 @@ import { RaceDTO } from './dtos/RaceDTO';
|
|||||||
import { SponsorProfileDTO } from './dtos/SponsorProfileDTO';
|
import { SponsorProfileDTO } from './dtos/SponsorProfileDTO';
|
||||||
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
|
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
|
||||||
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
|
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
|
||||||
|
import { PaymentMethodDTO } from './dtos/PaymentMethodDTO';
|
||||||
|
import { InvoiceDTO } from './dtos/InvoiceDTO';
|
||||||
|
import { BillingStatsDTO } from './dtos/BillingStatsDTO';
|
||||||
|
|
||||||
// Use cases
|
// Use cases
|
||||||
import { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
|
import { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
|
||||||
@@ -24,7 +29,7 @@ import { GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-case
|
|||||||
import { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
|
import { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
|
||||||
import {
|
import {
|
||||||
GetPendingSponsorshipRequestsUseCase,
|
GetPendingSponsorshipRequestsUseCase,
|
||||||
GetPendingSponsorshipRequestsDTO,
|
GetPendingSponsorshipRequestsInput,
|
||||||
} from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
|
} from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
|
||||||
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
||||||
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
@@ -48,6 +53,8 @@ import { AvailableLeaguesPresenter } from './presenters/AvailableLeaguesPresente
|
|||||||
import { LeagueDetailPresenter } from './presenters/LeagueDetailPresenter';
|
import { LeagueDetailPresenter } from './presenters/LeagueDetailPresenter';
|
||||||
import { SponsorSettingsPresenter } from './presenters/SponsorSettingsPresenter';
|
import { SponsorSettingsPresenter } from './presenters/SponsorSettingsPresenter';
|
||||||
import { SponsorSettingsUpdatePresenter } from './presenters/SponsorSettingsUpdatePresenter';
|
import { SponsorSettingsUpdatePresenter } from './presenters/SponsorSettingsUpdatePresenter';
|
||||||
|
import { AcceptSponsorshipRequestResultViewModel } from './presenters/AcceptSponsorshipRequestPresenter';
|
||||||
|
import type { RejectSponsorshipRequestResult } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
|
|
||||||
// Tokens
|
// Tokens
|
||||||
import {
|
import {
|
||||||
@@ -61,6 +68,16 @@ import {
|
|||||||
ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
||||||
REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
|
||||||
LOGGER_TOKEN,
|
LOGGER_TOKEN,
|
||||||
|
GET_ENTITY_SPONSORSHIP_PRICING_PRESENTER_TOKEN,
|
||||||
|
GET_SPONSORS_PRESENTER_TOKEN,
|
||||||
|
CREATE_SPONSOR_PRESENTER_TOKEN,
|
||||||
|
GET_SPONSOR_DASHBOARD_PRESENTER_TOKEN,
|
||||||
|
GET_SPONSOR_SPONSORSHIPS_PRESENTER_TOKEN,
|
||||||
|
GET_SPONSOR_PRESENTER_TOKEN,
|
||||||
|
GET_PENDING_SPONSORSHIP_REQUESTS_PRESENTER_TOKEN,
|
||||||
|
ACCEPT_SPONSORSHIP_REQUEST_PRESENTER_TOKEN,
|
||||||
|
REJECT_SPONSORSHIP_REQUEST_PRESENTER_TOKEN,
|
||||||
|
GET_SPONSOR_BILLING_PRESENTER_TOKEN,
|
||||||
} from './SponsorProviders';
|
} from './SponsorProviders';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -88,194 +105,152 @@ export class SponsorService {
|
|||||||
private readonly getSponsorBillingUseCase: GetSponsorBillingUseCase,
|
private readonly getSponsorBillingUseCase: GetSponsorBillingUseCase,
|
||||||
@Inject(LOGGER_TOKEN)
|
@Inject(LOGGER_TOKEN)
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
|
// Injected presenters
|
||||||
|
@Inject(GET_ENTITY_SPONSORSHIP_PRICING_PRESENTER_TOKEN)
|
||||||
|
private readonly getEntitySponsorshipPricingPresenter: GetEntitySponsorshipPricingPresenter,
|
||||||
|
@Inject(GET_SPONSORS_PRESENTER_TOKEN)
|
||||||
|
private readonly getSponsorsPresenter: GetSponsorsPresenter,
|
||||||
|
@Inject(CREATE_SPONSOR_PRESENTER_TOKEN)
|
||||||
|
private readonly createSponsorPresenter: CreateSponsorPresenter,
|
||||||
|
@Inject(GET_SPONSOR_DASHBOARD_PRESENTER_TOKEN)
|
||||||
|
private readonly getSponsorDashboardPresenter: GetSponsorDashboardPresenter,
|
||||||
|
@Inject(GET_SPONSOR_SPONSORSHIPS_PRESENTER_TOKEN)
|
||||||
|
private readonly getSponsorSponsorshipsPresenter: GetSponsorSponsorshipsPresenter,
|
||||||
|
@Inject(GET_SPONSOR_PRESENTER_TOKEN)
|
||||||
|
private readonly getSponsorPresenter: GetSponsorPresenter,
|
||||||
|
@Inject(GET_PENDING_SPONSORSHIP_REQUESTS_PRESENTER_TOKEN)
|
||||||
|
private readonly getPendingSponsorshipRequestsPresenter: GetPendingSponsorshipRequestsPresenter,
|
||||||
|
@Inject(ACCEPT_SPONSORSHIP_REQUEST_PRESENTER_TOKEN)
|
||||||
|
private readonly acceptSponsorshipRequestPresenter: AcceptSponsorshipRequestPresenter,
|
||||||
|
@Inject(REJECT_SPONSORSHIP_REQUEST_PRESENTER_TOKEN)
|
||||||
|
private readonly rejectSponsorshipRequestPresenter: RejectSponsorshipRequestPresenter,
|
||||||
|
@Inject(GET_SPONSOR_BILLING_PRESENTER_TOKEN)
|
||||||
|
private readonly sponsorBillingPresenter: SponsorBillingPresenter,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingPresenter> {
|
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
|
||||||
this.logger.debug('[SponsorService] Fetching sponsorship pricing.');
|
this.logger.debug('[SponsorService] Fetching sponsorship pricing.');
|
||||||
|
await this.getSponsorshipPricingUseCase.execute({});
|
||||||
const presenter = new GetEntitySponsorshipPricingPresenter();
|
return this.getEntitySponsorshipPricingPresenter.viewModel;
|
||||||
const result = await this.getSponsorshipPricingUseCase.execute();
|
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
this.logger.error('[SponsorService] Failed to fetch sponsorship pricing.', result.error);
|
|
||||||
presenter.present(null);
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSponsors(): Promise<GetSponsorsOutputDTO> {
|
async getSponsors(): Promise<GetSponsorsOutputDTO> {
|
||||||
this.logger.debug('[SponsorService] Fetching sponsors.');
|
this.logger.debug('[SponsorService] Fetching sponsors.');
|
||||||
|
await this.getSponsorsUseCase.execute();
|
||||||
const presenter = new GetSponsorsPresenter();
|
return this.getSponsorsPresenter.responseModel;
|
||||||
const result = await this.getSponsorsUseCase.execute();
|
|
||||||
|
|
||||||
presenter.present(result);
|
|
||||||
|
|
||||||
return presenter.responseModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorPresenter> {
|
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
|
||||||
this.logger.debug('[SponsorService] Creating sponsor.', { input });
|
this.logger.debug('[SponsorService] Creating sponsor.', { input });
|
||||||
|
await this.createSponsorUseCase.execute(input);
|
||||||
const presenter = new CreateSponsorPresenter();
|
return this.createSponsorPresenter.viewModel;
|
||||||
const result = await this.createSponsorUseCase.execute(input);
|
|
||||||
|
|
||||||
if (result.isErr()) {
|
|
||||||
this.logger.error('[SponsorService] Failed to create sponsor.', result.error);
|
|
||||||
throw new Error(result.error.details?.message || 'Failed to create sponsor');
|
|
||||||
}
|
|
||||||
|
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSponsorDashboard(
|
async getSponsorDashboard(
|
||||||
params: GetSponsorDashboardQueryParamsDTO,
|
params: GetSponsorDashboardQueryParamsDTO,
|
||||||
): Promise<GetSponsorDashboardPresenter> {
|
): Promise<SponsorDashboardDTO> {
|
||||||
this.logger.debug('[SponsorService] Fetching sponsor dashboard.', { params });
|
this.logger.debug('[SponsorService] Fetching sponsor dashboard.', { params });
|
||||||
|
await this.getSponsorDashboardUseCase.execute(params);
|
||||||
const presenter = new GetSponsorDashboardPresenter();
|
const result = this.getSponsorDashboardPresenter.viewModel;
|
||||||
const result = await this.getSponsorDashboardUseCase.execute(params);
|
if (!result) {
|
||||||
|
throw new Error('Sponsor dashboard not found');
|
||||||
if (result.isErr()) {
|
|
||||||
this.logger.error('[SponsorService] Failed to fetch sponsor dashboard.', result.error);
|
|
||||||
presenter.present(null);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSponsorSponsorships(
|
async getSponsorSponsorships(
|
||||||
params: GetSponsorSponsorshipsQueryParamsDTO,
|
params: GetSponsorSponsorshipsQueryParamsDTO,
|
||||||
): Promise<GetSponsorSponsorshipsPresenter> {
|
): Promise<SponsorSponsorshipsDTO> {
|
||||||
this.logger.debug('[SponsorService] Fetching sponsor sponsorships.', { params });
|
this.logger.debug('[SponsorService] Fetching sponsor sponsorships.', { params });
|
||||||
|
await this.getSponsorSponsorshipsUseCase.execute(params);
|
||||||
const presenter = new GetSponsorSponsorshipsPresenter();
|
const result = this.getSponsorSponsorshipsPresenter.viewModel;
|
||||||
const result = await this.getSponsorSponsorshipsUseCase.execute(params);
|
if (!result) {
|
||||||
|
throw new Error('Sponsor sponsorships not found');
|
||||||
if (result.isErr()) {
|
|
||||||
this.logger.error('[SponsorService] Failed to fetch sponsor sponsorships.', result.error);
|
|
||||||
presenter.present(null);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSponsor(sponsorId: string): Promise<GetSponsorPresenter> {
|
async getSponsor(sponsorId: string): Promise<GetSponsorOutputDTO> {
|
||||||
this.logger.debug('[SponsorService] Fetching sponsor.', { sponsorId });
|
this.logger.debug('[SponsorService] Fetching sponsor.', { sponsorId });
|
||||||
|
await this.getSponsorUseCase.execute({ sponsorId });
|
||||||
const presenter = new GetSponsorPresenter();
|
const result = this.getSponsorPresenter.viewModel;
|
||||||
const result = await this.getSponsorUseCase.execute({ sponsorId });
|
if (!result) {
|
||||||
|
throw new Error('Sponsor not found');
|
||||||
if (result.isErr()) {
|
|
||||||
this.logger.error('[SponsorService] Failed to fetch sponsor.', result.error);
|
|
||||||
presenter.present(null);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPendingSponsorshipRequests(params: {
|
async getPendingSponsorshipRequests(params: {
|
||||||
entityType: SponsorableEntityType;
|
entityType: SponsorableEntityType;
|
||||||
entityId: string;
|
entityId: string;
|
||||||
}): Promise<GetPendingSponsorshipRequestsPresenter> {
|
}): Promise<GetPendingSponsorshipRequestsOutputDTO> {
|
||||||
this.logger.debug('[SponsorService] Fetching pending sponsorship requests.', { params });
|
this.logger.debug('[SponsorService] Fetching pending sponsorship requests.', { params });
|
||||||
|
await this.getPendingSponsorshipRequestsUseCase.execute(
|
||||||
const presenter = new GetPendingSponsorshipRequestsPresenter();
|
params as GetPendingSponsorshipRequestsInput,
|
||||||
const result = await this.getPendingSponsorshipRequestsUseCase.execute(
|
|
||||||
params as GetPendingSponsorshipRequestsDTO,
|
|
||||||
);
|
);
|
||||||
|
const result = this.getPendingSponsorshipRequestsPresenter.viewModel;
|
||||||
if (result.isErr()) {
|
if (!result) {
|
||||||
this.logger.error('[SponsorService] Failed to fetch pending sponsorship requests.', result.error);
|
throw new Error('Pending sponsorship requests not found');
|
||||||
presenter.present({
|
|
||||||
entityType: params.entityType,
|
|
||||||
entityId: params.entityId,
|
|
||||||
requests: [],
|
|
||||||
totalCount: 0,
|
|
||||||
});
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async acceptSponsorshipRequest(
|
async acceptSponsorshipRequest(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
respondedBy: string,
|
respondedBy: string,
|
||||||
): Promise<AcceptSponsorshipRequestPresenter> {
|
): Promise<AcceptSponsorshipRequestResultViewModel> {
|
||||||
this.logger.debug('[SponsorService] Accepting sponsorship request.', {
|
this.logger.debug('[SponsorService] Accepting sponsorship request.', {
|
||||||
requestId,
|
requestId,
|
||||||
respondedBy,
|
respondedBy,
|
||||||
});
|
});
|
||||||
|
await this.acceptSponsorshipRequestUseCase.execute({
|
||||||
const presenter = new AcceptSponsorshipRequestPresenter();
|
|
||||||
const result = await this.acceptSponsorshipRequestUseCase.execute({
|
|
||||||
requestId,
|
requestId,
|
||||||
respondedBy,
|
respondedBy,
|
||||||
} as AcceptSponsorshipRequestInputDTO);
|
});
|
||||||
|
const result = this.acceptSponsorshipRequestPresenter.viewModel;
|
||||||
if (result.isErr()) {
|
if (!result) {
|
||||||
this.logger.error('[SponsorService] Failed to accept sponsorship request.', result.error);
|
throw new Error('Accept sponsorship request failed');
|
||||||
presenter.present(null);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async rejectSponsorshipRequest(
|
async rejectSponsorshipRequest(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
respondedBy: string,
|
respondedBy: string,
|
||||||
reason?: string,
|
reason?: string,
|
||||||
): Promise<RejectSponsorshipRequestPresenter> {
|
): Promise<RejectSponsorshipRequestResult> {
|
||||||
this.logger.debug('[SponsorService] Rejecting sponsorship request.', {
|
this.logger.debug('[SponsorService] Rejecting sponsorship request.', {
|
||||||
requestId,
|
requestId,
|
||||||
respondedBy,
|
respondedBy,
|
||||||
reason,
|
reason,
|
||||||
});
|
});
|
||||||
|
const input: { requestId: string; respondedBy: string; reason?: string } = {
|
||||||
const presenter = new RejectSponsorshipRequestPresenter();
|
|
||||||
const result = await this.rejectSponsorshipRequestUseCase.execute({
|
|
||||||
requestId,
|
requestId,
|
||||||
respondedBy,
|
respondedBy,
|
||||||
reason,
|
};
|
||||||
} as RejectSponsorshipRequestInputDTO);
|
if (reason !== undefined) {
|
||||||
|
input.reason = reason;
|
||||||
if (result.isErr()) {
|
|
||||||
this.logger.error('[SponsorService] Failed to reject sponsorship request.', result.error);
|
|
||||||
presenter.present(null);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
await this.rejectSponsorshipRequestUseCase.execute(input);
|
||||||
presenter.present(result.value);
|
const result = this.rejectSponsorshipRequestPresenter.viewModel;
|
||||||
return presenter;
|
if (!result) {
|
||||||
|
throw new Error('Reject sponsorship request failed');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSponsorBilling(sponsorId: string): Promise<GetSponsorBillingPresenter> {
|
async getSponsorBilling(sponsorId: string): Promise<{
|
||||||
|
paymentMethods: PaymentMethodDTO[];
|
||||||
|
invoices: InvoiceDTO[];
|
||||||
|
stats: BillingStatsDTO;
|
||||||
|
}> {
|
||||||
this.logger.debug('[SponsorService] Fetching sponsor billing.', { sponsorId });
|
this.logger.debug('[SponsorService] Fetching sponsor billing.', { sponsorId });
|
||||||
|
await this.getSponsorBillingUseCase.execute({ sponsorId });
|
||||||
const result = await this.getSponsorBillingUseCase.execute({ sponsorId });
|
const result = this.sponsorBillingPresenter.viewModel;
|
||||||
|
if (!result) {
|
||||||
if (result.isErr()) {
|
throw new Error('Sponsor billing not found');
|
||||||
this.logger.error('[SponsorService] Failed to fetch sponsor billing.', result.error);
|
|
||||||
throw new Error(result.error.details?.message || 'Failed to fetch sponsor billing');
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
const presenter = new GetSponsorBillingPresenter();
|
|
||||||
presenter.present(result.value);
|
|
||||||
return presenter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailableLeagues(): Promise<AvailableLeaguesPresenter> {
|
async getAvailableLeagues(): Promise<AvailableLeaguesPresenter> {
|
||||||
@@ -426,7 +401,7 @@ export class SponsorService {
|
|||||||
website: 'https://acme-racing.com',
|
website: 'https://acme-racing.com',
|
||||||
description:
|
description:
|
||||||
'Premium sim racing equipment and accessories for competitive drivers.',
|
'Premium sim racing equipment and accessories for competitive drivers.',
|
||||||
logoUrl: null,
|
logoUrl: '',
|
||||||
industry: 'Racing Equipment',
|
industry: 'Racing Equipment',
|
||||||
address: {
|
address: {
|
||||||
street: '123 Racing Boulevard',
|
street: '123 Racing Boulevard',
|
||||||
@@ -479,4 +454,4 @@ export class SponsorService {
|
|||||||
presenter.present({ success: true });
|
presenter.present({ success: true });
|
||||||
return presenter;
|
return presenter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,19 +4,19 @@ import { IsString, IsEnum, IsOptional, IsNumber } from 'class-validator';
|
|||||||
export class ActivityItemDTO {
|
export class ActivityItemDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['race', 'league', 'team', 'driver', 'platform'] })
|
@ApiProperty({ enum: ['race', 'league', 'team', 'driver', 'platform'] })
|
||||||
@IsEnum(['race', 'league', 'team', 'driver', 'platform'])
|
@IsEnum(['race', 'league', 'team', 'driver', 'platform'])
|
||||||
type: 'race' | 'league' | 'team' | 'driver' | 'platform';
|
type: 'race' | 'league' | 'team' | 'driver' | 'platform' = 'race';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
message: string;
|
message: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
time: string;
|
time: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsString, IsEnum, IsNumber, IsBoolean, IsOptional, IsDateString } from 'class-validator';
|
import { IsString, IsEnum, IsNumber, IsOptional, IsDateString } from 'class-validator';
|
||||||
|
|
||||||
export class AvailableLeagueDTO {
|
export class AvailableLeagueDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
game: string;
|
game: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
drivers: number;
|
drivers: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
avgViewsPerRace: number;
|
avgViewsPerRace: number = 0;
|
||||||
|
|
||||||
@ApiProperty({ type: Object })
|
@ApiProperty({ type: Object })
|
||||||
mainSponsorSlot: {
|
mainSponsorSlot: {
|
||||||
available: boolean;
|
available: boolean;
|
||||||
price: number;
|
price: number;
|
||||||
};
|
} = { available: false, price: 0 };
|
||||||
|
|
||||||
@ApiProperty({ type: Object })
|
@ApiProperty({ type: Object })
|
||||||
secondarySlots: {
|
secondarySlots: {
|
||||||
available: number;
|
available: number;
|
||||||
total: number;
|
total: number;
|
||||||
price: number;
|
price: number;
|
||||||
};
|
} = { available: 0, total: 0, price: 0 };
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
rating: number;
|
rating: number = 0;
|
||||||
|
|
||||||
@ApiProperty({ enum: ['premium', 'standard', 'starter'] })
|
@ApiProperty({ enum: ['premium', 'standard', 'starter'] })
|
||||||
@IsEnum(['premium', 'standard', 'starter'])
|
@IsEnum(['premium', 'standard', 'starter'])
|
||||||
tier: 'premium' | 'standard' | 'starter';
|
tier: 'premium' | 'standard' | 'starter' = 'standard';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -50,9 +50,9 @@ export class AvailableLeagueDTO {
|
|||||||
|
|
||||||
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
|
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
|
||||||
@IsEnum(['active', 'upcoming', 'completed'])
|
@IsEnum(['active', 'upcoming', 'completed'])
|
||||||
seasonStatus: 'active' | 'upcoming' | 'completed';
|
seasonStatus: 'active' | 'upcoming' | 'completed' = 'active';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
description: string;
|
description: string = '';
|
||||||
}
|
}
|
||||||
@@ -4,25 +4,25 @@ import { IsNumber, IsDateString } from 'class-validator';
|
|||||||
export class BillingStatsDTO {
|
export class BillingStatsDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
totalSpent: number;
|
totalSpent: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
pendingAmount: number;
|
pendingAmount: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
nextPaymentDate: string;
|
nextPaymentDate: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
nextPaymentAmount: number;
|
nextPaymentAmount: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
activeSponsorships: number;
|
activeSponsorships: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
averageMonthlySpend: number;
|
averageMonthlySpend: number = 0;
|
||||||
}
|
}
|
||||||
@@ -3,5 +3,5 @@ import { SponsorDTO } from './SponsorDTO';
|
|||||||
|
|
||||||
export class CreateSponsorOutputDTO {
|
export class CreateSponsorOutputDTO {
|
||||||
@ApiProperty({ type: SponsorDTO })
|
@ApiProperty({ type: SponsorDTO })
|
||||||
sponsor: SponsorDTO;
|
sponsor: SponsorDTO = new SponsorDTO();
|
||||||
}
|
}
|
||||||
@@ -4,29 +4,29 @@ import { IsString, IsNumber } from 'class-validator';
|
|||||||
export class DriverDTO {
|
export class DriverDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
country: string;
|
country: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
position: number;
|
position: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
races: number;
|
races: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
impressions: number;
|
impressions: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
team: string;
|
team: string = '';
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,11 @@ import { SponsorshipPricingItemDTO } from './SponsorshipPricingItemDTO';
|
|||||||
|
|
||||||
export class GetEntitySponsorshipPricingResultDTO {
|
export class GetEntitySponsorshipPricingResultDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
entityType: string;
|
entityType: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
entityId: string;
|
entityId: string = '';
|
||||||
|
|
||||||
@ApiProperty({ type: [SponsorshipPricingItemDTO] })
|
@ApiProperty({ type: [SponsorshipPricingItemDTO] })
|
||||||
pricing: SponsorshipPricingItemDTO[];
|
pricing: SponsorshipPricingItemDTO[] = [];
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,14 @@ import { SponsorshipRequestDTO } from './SponsorshipRequestDTO';
|
|||||||
|
|
||||||
export class GetPendingSponsorshipRequestsOutputDTO {
|
export class GetPendingSponsorshipRequestsOutputDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
entityType: string;
|
entityType: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
entityId: string;
|
entityId: string = '';
|
||||||
|
|
||||||
@ApiProperty({ type: [SponsorshipRequestDTO] })
|
@ApiProperty({ type: [SponsorshipRequestDTO] })
|
||||||
requests: SponsorshipRequestDTO[];
|
requests: SponsorshipRequestDTO[] = [];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalCount: number;
|
totalCount: number = 0;
|
||||||
}
|
}
|
||||||
@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
|
|||||||
export class GetSponsorDashboardQueryParamsDTO {
|
export class GetSponsorDashboardQueryParamsDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
sponsorId: string;
|
sponsorId: string = '';
|
||||||
}
|
}
|
||||||
@@ -3,5 +3,5 @@ import { SponsorDTO } from './SponsorDTO';
|
|||||||
|
|
||||||
export class GetSponsorOutputDTO {
|
export class GetSponsorOutputDTO {
|
||||||
@ApiProperty({ type: SponsorDTO })
|
@ApiProperty({ type: SponsorDTO })
|
||||||
sponsor: SponsorDTO;
|
sponsor: SponsorDTO = new SponsorDTO();
|
||||||
}
|
}
|
||||||
@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
|
|||||||
export class GetSponsorSponsorshipsQueryParamsDTO {
|
export class GetSponsorSponsorshipsQueryParamsDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
sponsorId: string;
|
sponsorId: string = '';
|
||||||
}
|
}
|
||||||
@@ -3,5 +3,5 @@ import { SponsorDTO } from './SponsorDTO';
|
|||||||
|
|
||||||
export class GetSponsorsOutputDTO {
|
export class GetSponsorsOutputDTO {
|
||||||
@ApiProperty({ type: [SponsorDTO] })
|
@ApiProperty({ type: [SponsorDTO] })
|
||||||
sponsors: SponsorDTO[];
|
sponsors: SponsorDTO[] = [];
|
||||||
}
|
}
|
||||||
@@ -4,45 +4,45 @@ import { IsString, IsEnum, IsNumber, IsDateString } from 'class-validator';
|
|||||||
export class InvoiceDTO {
|
export class InvoiceDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
invoiceNumber: string;
|
invoiceNumber: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
date: string;
|
date: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
dueDate: string;
|
dueDate: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
amount: number;
|
amount: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
vatAmount: number;
|
vatAmount: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
totalAmount: number;
|
totalAmount: number = 0;
|
||||||
|
|
||||||
@ApiProperty({ enum: ['paid', 'pending', 'overdue', 'failed'] })
|
@ApiProperty({ enum: ['paid', 'pending', 'overdue', 'failed'] })
|
||||||
@IsEnum(['paid', 'pending', 'overdue', 'failed'])
|
@IsEnum(['paid', 'pending', 'overdue', 'failed'])
|
||||||
status: 'paid' | 'pending' | 'overdue' | 'failed';
|
status: 'paid' | 'pending' | 'overdue' | 'failed' = 'pending';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
description: string;
|
description: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['league', 'team', 'driver', 'race', 'platform'] })
|
@ApiProperty({ enum: ['league', 'team', 'driver', 'race', 'platform'] })
|
||||||
@IsEnum(['league', 'team', 'driver', 'race', 'platform'])
|
@IsEnum(['league', 'team', 'driver', 'race', 'platform'])
|
||||||
sponsorshipType: 'league' | 'team' | 'driver' | 'race' | 'platform';
|
sponsorshipType: 'league' | 'team' | 'driver' | 'race' | 'platform' = 'league';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
pdfUrl: string;
|
pdfUrl: string = '';
|
||||||
}
|
}
|
||||||
@@ -1,68 +1,68 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsString, IsEnum, IsNumber, IsDateString, IsOptional } from 'class-validator';
|
import { IsString, IsEnum, IsNumber, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
export class LeagueDetailDTO {
|
export class LeagueDetailDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
game: string;
|
game: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['premium', 'standard', 'starter'] })
|
@ApiProperty({ enum: ['premium', 'standard', 'starter'] })
|
||||||
@IsEnum(['premium', 'standard', 'starter'])
|
@IsEnum(['premium', 'standard', 'starter'])
|
||||||
tier: 'premium' | 'standard' | 'starter';
|
tier: 'premium' | 'standard' | 'starter' = 'standard';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
season: string;
|
season: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
description: string;
|
description: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
drivers: number;
|
drivers: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
races: number;
|
races: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
completedRaces: number;
|
completedRaces: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
totalImpressions: number;
|
totalImpressions: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
avgViewsPerRace: number;
|
avgViewsPerRace: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
engagement: number;
|
engagement: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
rating: number;
|
rating: number = 0;
|
||||||
|
|
||||||
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
|
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
|
||||||
@IsEnum(['active', 'upcoming', 'completed'])
|
@IsEnum(['active', 'upcoming', 'completed'])
|
||||||
seasonStatus: 'active' | 'upcoming' | 'completed';
|
seasonStatus: 'active' | 'upcoming' | 'completed' = 'active';
|
||||||
|
|
||||||
@ApiProperty({ type: Object })
|
@ApiProperty({ type: Object })
|
||||||
seasonDates: {
|
seasonDates: {
|
||||||
start: string;
|
start: string;
|
||||||
end: string;
|
end: string;
|
||||||
};
|
} = { start: '', end: '' };
|
||||||
|
|
||||||
@ApiProperty({ type: Object, required: false })
|
@ApiProperty({ type: Object, required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -84,5 +84,8 @@ export class LeagueDetailDTO {
|
|||||||
price: number;
|
price: number;
|
||||||
benefits: string[];
|
benefits: string[];
|
||||||
};
|
};
|
||||||
|
} = {
|
||||||
|
main: { available: false, price: 0, benefits: [] },
|
||||||
|
secondary: { available: 0, total: 0, price: 0, benefits: [] }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -4,25 +4,25 @@ import { IsBoolean } from 'class-validator';
|
|||||||
export class NotificationSettingsDTO {
|
export class NotificationSettingsDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
emailNewSponsorships: boolean;
|
emailNewSponsorships: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
emailWeeklyReport: boolean;
|
emailWeeklyReport: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
emailRaceAlerts: boolean;
|
emailRaceAlerts: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
emailPaymentAlerts: boolean;
|
emailPaymentAlerts: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
emailNewOpportunities: boolean;
|
emailNewOpportunities: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
emailContractExpiry: boolean;
|
emailContractExpiry: boolean = false;
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,15 @@ import { IsString, IsEnum, IsBoolean, IsOptional, IsNumber } from 'class-validat
|
|||||||
export class PaymentMethodDTO {
|
export class PaymentMethodDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['card', 'bank', 'sepa'] })
|
@ApiProperty({ enum: ['card', 'bank', 'sepa'] })
|
||||||
@IsEnum(['card', 'bank', 'sepa'])
|
@IsEnum(['card', 'bank', 'sepa'])
|
||||||
type: 'card' | 'bank' | 'sepa';
|
type: 'card' | 'bank' | 'sepa' = 'card';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
last4: string;
|
last4: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -21,7 +21,7 @@ export class PaymentMethodDTO {
|
|||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
isDefault: boolean;
|
isDefault: boolean = false;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ import { IsBoolean } from 'class-validator';
|
|||||||
export class PrivacySettingsDTO {
|
export class PrivacySettingsDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
publicProfile: boolean;
|
publicProfile: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
showStats: boolean;
|
showStats: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
showActiveSponsorships: boolean;
|
showActiveSponsorships: boolean = false;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
allowDirectContact: boolean;
|
allowDirectContact: boolean = false;
|
||||||
}
|
}
|
||||||
@@ -4,21 +4,21 @@ import { IsString, IsEnum, IsNumber, IsDateString } from 'class-validator';
|
|||||||
export class RaceDTO {
|
export class RaceDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
date: string;
|
date: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
views: number;
|
views: number = 0;
|
||||||
|
|
||||||
@ApiProperty({ enum: ['upcoming', 'completed'] })
|
@ApiProperty({ enum: ['upcoming', 'completed'] })
|
||||||
@IsEnum(['upcoming', 'completed'])
|
@IsEnum(['upcoming', 'completed'])
|
||||||
status: 'upcoming' | 'completed';
|
status: 'upcoming' | 'completed' = 'upcoming';
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ export class RejectSponsorshipRequestInputDTO {
|
|||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
respondedBy: string;
|
respondedBy: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ import { IsString, IsEnum, IsNumber, IsDateString } from 'class-validator';
|
|||||||
export class RenewalAlertDTO {
|
export class RenewalAlertDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['league', 'team', 'driver', 'race', 'platform'] })
|
@ApiProperty({ enum: ['league', 'team', 'driver', 'race', 'platform'] })
|
||||||
@IsEnum(['league', 'team', 'driver', 'race', 'platform'])
|
@IsEnum(['league', 'team', 'driver', 'race', 'platform'])
|
||||||
type: 'league' | 'team' | 'driver' | 'race' | 'platform';
|
type: 'league' | 'team' | 'driver' | 'race' | 'platform' = 'league';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
renewDate: string;
|
renewDate: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
price: number;
|
price: number = 0;
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,10 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
export class SponsorDTO {
|
export class SponsorDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
name: string;
|
name: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
contactEmail?: string;
|
contactEmail?: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsString, IsArray, IsObject } from 'class-validator';
|
import { IsString } from 'class-validator';
|
||||||
import { SponsorDashboardMetricsDTO } from './SponsorDashboardMetricsDTO';
|
import { SponsorDashboardMetricsDTO } from './SponsorDashboardMetricsDTO';
|
||||||
import { SponsoredLeagueDTO } from './SponsoredLeagueDTO';
|
import { SponsoredLeagueDTO } from './SponsoredLeagueDTO';
|
||||||
import { SponsorDashboardInvestmentDTO } from './SponsorDashboardInvestmentDTO';
|
import { SponsorDashboardInvestmentDTO } from './SponsorDashboardInvestmentDTO';
|
||||||
@@ -10,20 +10,20 @@ import { RenewalAlertDTO } from './RenewalAlertDTO';
|
|||||||
export class SponsorDashboardDTO {
|
export class SponsorDashboardDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
sponsorId: string;
|
sponsorId: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
sponsorName: string;
|
sponsorName: string = '';
|
||||||
|
|
||||||
@ApiProperty({ type: SponsorDashboardMetricsDTO })
|
@ApiProperty({ type: SponsorDashboardMetricsDTO })
|
||||||
metrics: SponsorDashboardMetricsDTO;
|
metrics: SponsorDashboardMetricsDTO = new SponsorDashboardMetricsDTO();
|
||||||
|
|
||||||
@ApiProperty({ type: [SponsoredLeagueDTO] })
|
@ApiProperty({ type: [SponsoredLeagueDTO] })
|
||||||
sponsoredLeagues: SponsoredLeagueDTO[];
|
sponsoredLeagues: SponsoredLeagueDTO[] = [];
|
||||||
|
|
||||||
@ApiProperty({ type: SponsorDashboardInvestmentDTO })
|
@ApiProperty({ type: SponsorDashboardInvestmentDTO })
|
||||||
investment: SponsorDashboardInvestmentDTO;
|
investment: SponsorDashboardInvestmentDTO = new SponsorDashboardInvestmentDTO();
|
||||||
|
|
||||||
@ApiProperty({ type: Object })
|
@ApiProperty({ type: Object })
|
||||||
sponsorships: {
|
sponsorships: {
|
||||||
@@ -32,11 +32,17 @@ export class SponsorDashboardDTO {
|
|||||||
drivers: SponsorshipDTO[];
|
drivers: SponsorshipDTO[];
|
||||||
races: SponsorshipDTO[];
|
races: SponsorshipDTO[];
|
||||||
platform: SponsorshipDTO[];
|
platform: SponsorshipDTO[];
|
||||||
|
} = {
|
||||||
|
leagues: [],
|
||||||
|
teams: [],
|
||||||
|
drivers: [],
|
||||||
|
races: [],
|
||||||
|
platform: []
|
||||||
};
|
};
|
||||||
|
|
||||||
@ApiProperty({ type: [ActivityItemDTO] })
|
@ApiProperty({ type: [ActivityItemDTO] })
|
||||||
recentActivity: ActivityItemDTO[];
|
recentActivity: ActivityItemDTO[] = [];
|
||||||
|
|
||||||
@ApiProperty({ type: [RenewalAlertDTO] })
|
@ApiProperty({ type: [RenewalAlertDTO] })
|
||||||
upcomingRenewals: RenewalAlertDTO[];
|
upcomingRenewals: RenewalAlertDTO[] = [];
|
||||||
}
|
}
|
||||||
@@ -4,13 +4,13 @@ import { IsNumber } from 'class-validator';
|
|||||||
export class SponsorDashboardInvestmentDTO {
|
export class SponsorDashboardInvestmentDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
activeSponsorships: number;
|
activeSponsorships: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
totalInvestment: number;
|
totalInvestment: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
costPerThousandViews: number;
|
costPerThousandViews: number = 0;
|
||||||
}
|
}
|
||||||
@@ -4,33 +4,33 @@ import { IsNumber } from 'class-validator';
|
|||||||
export class SponsorDashboardMetricsDTO {
|
export class SponsorDashboardMetricsDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
impressions: number;
|
impressions: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
impressionsChange: number;
|
impressionsChange: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
uniqueViewers: number;
|
uniqueViewers: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
viewersChange: number;
|
viewersChange: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
races: number;
|
races: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
drivers: number;
|
drivers: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
exposure: number;
|
exposure: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
exposureChange: number;
|
exposureChange: number = 0;
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsString, IsOptional, IsObject } from 'class-validator';
|
import { IsString, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
export class SponsorProfileDTO {
|
export class SponsorProfileDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
companyName: string;
|
companyName: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
contactName: string;
|
contactName: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
contactEmail: string;
|
contactEmail: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
contactPhone: string;
|
contactPhone: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
website: string;
|
website: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
description: string;
|
description: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -33,7 +33,7 @@ export class SponsorProfileDTO {
|
|||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
industry: string;
|
industry: string = '';
|
||||||
|
|
||||||
@ApiProperty({ type: Object })
|
@ApiProperty({ type: Object })
|
||||||
address: {
|
address: {
|
||||||
@@ -41,16 +41,16 @@ export class SponsorProfileDTO {
|
|||||||
city: string;
|
city: string;
|
||||||
country: string;
|
country: string;
|
||||||
postalCode: string;
|
postalCode: string;
|
||||||
};
|
} = { street: '', city: '', country: '', postalCode: '' };
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
taxId: string;
|
taxId: string = '';
|
||||||
|
|
||||||
@ApiProperty({ type: Object })
|
@ApiProperty({ type: Object })
|
||||||
socialLinks: {
|
socialLinks: {
|
||||||
twitter: string;
|
twitter: string;
|
||||||
linkedin: string;
|
linkedin: string;
|
||||||
instagram: string;
|
instagram: string;
|
||||||
};
|
} = { twitter: '', linkedin: '', instagram: '' };
|
||||||
}
|
}
|
||||||
@@ -5,14 +5,14 @@ import { SponsorshipDetailDTO } from './SponsorshipDetailDTO';
|
|||||||
export class SponsorSponsorshipsDTO {
|
export class SponsorSponsorshipsDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
sponsorId: string;
|
sponsorId: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
sponsorName: string;
|
sponsorName: string = '';
|
||||||
|
|
||||||
@ApiProperty({ type: [SponsorshipDetailDTO] })
|
@ApiProperty({ type: [SponsorshipDetailDTO] })
|
||||||
sponsorships: SponsorshipDetailDTO[];
|
sponsorships: SponsorshipDetailDTO[] = [];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
summary: {
|
summary: {
|
||||||
@@ -21,5 +21,11 @@ export class SponsorSponsorshipsDTO {
|
|||||||
totalInvestment: number;
|
totalInvestment: number;
|
||||||
totalPlatformFees: number;
|
totalPlatformFees: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
|
} = {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
totalPlatformFees: 0,
|
||||||
|
currency: ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -4,29 +4,29 @@ import { IsString, IsEnum, IsNumber } from 'class-validator';
|
|||||||
export class SponsoredLeagueDTO {
|
export class SponsoredLeagueDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
name: string;
|
name: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['main', 'secondary'] })
|
@ApiProperty({ enum: ['main', 'secondary'] })
|
||||||
@IsEnum(['main', 'secondary'])
|
@IsEnum(['main', 'secondary'])
|
||||||
tier: 'main' | 'secondary';
|
tier: 'main' | 'secondary' = 'main';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
drivers: number;
|
drivers: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
races: number;
|
races: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
impressions: number;
|
impressions: number = 0;
|
||||||
|
|
||||||
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
|
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
|
||||||
@IsEnum(['active', 'upcoming', 'completed'])
|
@IsEnum(['active', 'upcoming', 'completed'])
|
||||||
status: 'active' | 'upcoming' | 'completed';
|
status: 'active' | 'upcoming' | 'completed' = 'active';
|
||||||
}
|
}
|
||||||
@@ -4,19 +4,19 @@ import { IsString, IsEnum, IsNumber, IsOptional, IsDateString } from 'class-vali
|
|||||||
export class SponsorshipDTO {
|
export class SponsorshipDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['leagues', 'teams', 'drivers', 'races', 'platform'] })
|
@ApiProperty({ enum: ['leagues', 'teams', 'drivers', 'races', 'platform'] })
|
||||||
@IsEnum(['leagues', 'teams', 'drivers', 'races', 'platform'])
|
@IsEnum(['leagues', 'teams', 'drivers', 'races', 'platform'])
|
||||||
type: 'leagues' | 'teams' | 'drivers' | 'races' | 'platform';
|
type: 'leagues' | 'teams' | 'drivers' | 'races' | 'platform' = 'leagues';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
entityId: string;
|
entityId: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
entityName: string;
|
entityName: string = '';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['main', 'secondary'], required: false })
|
@ApiProperty({ enum: ['main', 'secondary'], required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -25,7 +25,7 @@ export class SponsorshipDTO {
|
|||||||
|
|
||||||
@ApiProperty({ enum: ['active', 'pending_approval', 'approved', 'rejected', 'expired'] })
|
@ApiProperty({ enum: ['active', 'pending_approval', 'approved', 'rejected', 'expired'] })
|
||||||
@IsEnum(['active', 'pending_approval', 'approved', 'rejected', 'expired'])
|
@IsEnum(['active', 'pending_approval', 'approved', 'rejected', 'expired'])
|
||||||
status: 'active' | 'pending_approval' | 'approved' | 'rejected' | 'expired';
|
status: 'active' | 'pending_approval' | 'approved' | 'rejected' | 'expired' = 'pending_approval';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -44,19 +44,19 @@ export class SponsorshipDTO {
|
|||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
startDate: string;
|
startDate: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
endDate: string;
|
endDate: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
price: number;
|
price: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
impressions: number;
|
impressions: number = 0;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -4,23 +4,23 @@ import { IsString, IsEnum, IsOptional, IsDate } from 'class-validator';
|
|||||||
export class SponsorshipDetailDTO {
|
export class SponsorshipDetailDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
leagueId: string;
|
leagueId: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
leagueName: string;
|
leagueName: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
seasonId: string;
|
seasonId: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
seasonName: string;
|
seasonName: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -34,29 +34,29 @@ export class SponsorshipDetailDTO {
|
|||||||
|
|
||||||
@ApiProperty({ enum: ['main', 'secondary'] })
|
@ApiProperty({ enum: ['main', 'secondary'] })
|
||||||
@IsEnum(['main', 'secondary'])
|
@IsEnum(['main', 'secondary'])
|
||||||
tier: 'main' | 'secondary';
|
tier: 'main' | 'secondary' = 'main';
|
||||||
|
|
||||||
@ApiProperty({ enum: ['pending', 'active', 'expired', 'cancelled'] })
|
@ApiProperty({ enum: ['pending', 'active', 'expired', 'cancelled'] })
|
||||||
@IsEnum(['pending', 'active', 'expired', 'cancelled'])
|
@IsEnum(['pending', 'active', 'expired', 'cancelled'])
|
||||||
status: 'pending' | 'active' | 'expired' | 'cancelled';
|
status: 'pending' | 'active' | 'expired' | 'cancelled' = 'pending';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
pricing: {
|
pricing: {
|
||||||
amount: number;
|
amount: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
};
|
} = { amount: 0, currency: '' };
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
platformFee: {
|
platformFee: {
|
||||||
amount: number;
|
amount: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
};
|
} = { amount: 0, currency: '' };
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
netAmount: {
|
netAmount: {
|
||||||
amount: number;
|
amount: number;
|
||||||
currency: string;
|
currency: string;
|
||||||
};
|
} = { amount: 0, currency: '' };
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
metrics: {
|
metrics: {
|
||||||
@@ -64,10 +64,10 @@ export class SponsorshipDetailDTO {
|
|||||||
races: number;
|
races: number;
|
||||||
completedRaces: number;
|
completedRaces: number;
|
||||||
impressions: number;
|
impressions: number;
|
||||||
};
|
} = { drivers: 0, races: 0, completedRaces: 0, impressions: 0 };
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
createdAt: Date;
|
createdAt: Date = new Date();
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ import { IsString, IsNumber } from 'class-validator';
|
|||||||
export class SponsorshipPricingItemDTO {
|
export class SponsorshipPricingItemDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
level: string;
|
level: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
price: number;
|
price: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
currency: string;
|
currency: string = '';
|
||||||
}
|
}
|
||||||
@@ -2,38 +2,38 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
export class SponsorshipRequestDTO {
|
export class SponsorshipRequestDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
sponsorId: string;
|
sponsorId: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
sponsorName: string;
|
sponsorName: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
sponsorLogo?: string;
|
sponsorLogo?: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
tier: string;
|
tier: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
offeredAmount: number;
|
offeredAmount: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
currency: string;
|
currency: string = '';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
formattedAmount: string;
|
formattedAmount: string = '';
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
message?: string;
|
message?: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
createdAt: Date;
|
createdAt: Date = new Date();
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
platformFee: number;
|
platformFee: number = 0;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
netAmount: number;
|
netAmount: number = 0;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { AcceptSponsorshipOutputPort } from '@core/racing/application/ports/output/AcceptSponsorshipOutputPort';
|
import type { AcceptSponsorshipResult } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
|
||||||
|
|
||||||
export interface AcceptSponsorshipRequestResultViewModel {
|
export interface AcceptSponsorshipRequestResultViewModel {
|
||||||
requestId: string;
|
requestId: string;
|
||||||
@@ -16,7 +16,7 @@ export class AcceptSponsorshipRequestPresenter {
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(output: AcceptSponsorshipOutputPort | null) {
|
present(output: AcceptSponsorshipResult | null) {
|
||||||
if (!output) {
|
if (!output) {
|
||||||
this.result = null;
|
this.result = null;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { CreateSponsorPresenter } from './CreateSponsorPresenter';
|
import { CreateSponsorPresenter } from './CreateSponsorPresenter';
|
||||||
|
import { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
|
||||||
|
|
||||||
describe('CreateSponsorPresenter', () => {
|
describe('CreateSponsorPresenter', () => {
|
||||||
let presenter: CreateSponsorPresenter;
|
let presenter: CreateSponsorPresenter;
|
||||||
@@ -10,9 +11,23 @@ describe('CreateSponsorPresenter', () => {
|
|||||||
|
|
||||||
describe('reset', () => {
|
describe('reset', () => {
|
||||||
it('should reset the result to null', () => {
|
it('should reset the result to null', () => {
|
||||||
const mockPort = { sponsor: { id: 'sponsor-1', name: 'Test Sponsor', contactEmail: 'test@example.com', createdAt: new Date() } };
|
const mockSponsor = Sponsor.create({
|
||||||
presenter.present(mockPort);
|
id: 'sponsor-1',
|
||||||
expect(presenter.viewModel).toEqual({ sponsor: mockPort.sponsor });
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
presenter.present(mockSponsor);
|
||||||
|
|
||||||
|
const expectedViewModel = {
|
||||||
|
sponsor: {
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: mockSponsor.createdAt.toDate()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
|
|
||||||
presenter.reset();
|
presenter.reset();
|
||||||
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
||||||
@@ -21,11 +36,24 @@ describe('CreateSponsorPresenter', () => {
|
|||||||
|
|
||||||
describe('present', () => {
|
describe('present', () => {
|
||||||
it('should store the result', () => {
|
it('should store the result', () => {
|
||||||
const mockPort = { sponsor: { id: 'sponsor-1', name: 'Test Sponsor', contactEmail: 'test@example.com', createdAt: new Date() } };
|
const mockSponsor = Sponsor.create({
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
presenter.present(mockPort);
|
presenter.present(mockSponsor);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({ sponsor: mockPort.sponsor });
|
const expectedViewModel = {
|
||||||
|
sponsor: {
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: mockSponsor.createdAt.toDate()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,10 +63,23 @@ describe('CreateSponsorPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockPort = { sponsor: { id: 'sponsor-1', name: 'Test Sponsor', contactEmail: 'test@example.com', createdAt: new Date() } };
|
const mockSponsor = Sponsor.create({
|
||||||
presenter.present(mockPort);
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
presenter.present(mockSponsor);
|
||||||
|
|
||||||
expect(presenter.getViewModel()).toEqual({ sponsor: mockPort.sponsor });
|
const expectedViewModel = {
|
||||||
|
sponsor: {
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: mockSponsor.createdAt.toDate()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect(presenter.getViewModel()).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,10 +89,23 @@ describe('CreateSponsorPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockPort = { sponsor: { id: 'sponsor-1', name: 'Test Sponsor', contactEmail: 'test@example.com', createdAt: new Date() } };
|
const mockSponsor = Sponsor.create({
|
||||||
presenter.present(mockPort);
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
presenter.present(mockSponsor);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual({ sponsor: mockPort.sponsor });
|
const expectedViewModel = {
|
||||||
|
sponsor: {
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: mockSponsor.createdAt.toDate()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { CreateSponsorOutputPort } from '@core/racing/application/ports/output/CreateSponsorOutputPort';
|
import type { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
|
||||||
import type { CreateSponsorOutputDTO } from '../dtos/CreateSponsorOutputDTO';
|
import type { CreateSponsorOutputDTO } from '../dtos/CreateSponsorOutputDTO';
|
||||||
|
|
||||||
export class CreateSponsorPresenter {
|
export class CreateSponsorPresenter {
|
||||||
@@ -8,16 +8,24 @@ export class CreateSponsorPresenter {
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(port: CreateSponsorOutputPort) {
|
present(sponsor: Sponsor) {
|
||||||
|
const sponsorData: any = {
|
||||||
|
id: sponsor.id.toString(),
|
||||||
|
name: sponsor.name.toString(),
|
||||||
|
contactEmail: sponsor.contactEmail.toString(),
|
||||||
|
createdAt: sponsor.createdAt.toDate(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sponsor.logoUrl) {
|
||||||
|
sponsorData.logoUrl = sponsor.logoUrl.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sponsor.websiteUrl) {
|
||||||
|
sponsorData.websiteUrl = sponsor.websiteUrl.toString();
|
||||||
|
}
|
||||||
|
|
||||||
this.result = {
|
this.result = {
|
||||||
sponsor: {
|
sponsor: sponsorData,
|
||||||
id: port.sponsor.id,
|
|
||||||
name: port.sponsor.name,
|
|
||||||
contactEmail: port.sponsor.contactEmail,
|
|
||||||
logoUrl: port.sponsor.logoUrl,
|
|
||||||
websiteUrl: port.sponsor.websiteUrl,
|
|
||||||
createdAt: port.sponsor.createdAt,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { GetEntitySponsorshipPricingPresenter } from './GetEntitySponsorshipPricingPresenter';
|
import { GetEntitySponsorshipPricingPresenter } from './GetEntitySponsorshipPricingPresenter';
|
||||||
|
import type { GetEntitySponsorshipPricingResult } from '@core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
|
||||||
|
|
||||||
describe('GetEntitySponsorshipPricingPresenter', () => {
|
describe('GetEntitySponsorshipPricingPresenter', () => {
|
||||||
let presenter: GetEntitySponsorshipPricingPresenter;
|
let presenter: GetEntitySponsorshipPricingPresenter;
|
||||||
@@ -10,9 +11,20 @@ describe('GetEntitySponsorshipPricingPresenter', () => {
|
|||||||
|
|
||||||
describe('reset', () => {
|
describe('reset', () => {
|
||||||
it('should reset the result to null', () => {
|
it('should reset the result to null', () => {
|
||||||
const mockResult = { entityType: 'season', entityId: 'season-1', pricing: [] };
|
const mockResult: GetEntitySponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
acceptingApplications: true,
|
||||||
|
tiers: []
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
|
||||||
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
|
|
||||||
presenter.reset();
|
presenter.reset();
|
||||||
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
||||||
@@ -21,11 +33,21 @@ describe('GetEntitySponsorshipPricingPresenter', () => {
|
|||||||
|
|
||||||
describe('present', () => {
|
describe('present', () => {
|
||||||
it('should store the result', () => {
|
it('should store the result', () => {
|
||||||
const mockResult = { entityType: 'season', entityId: 'season-1', pricing: [] };
|
const mockResult: GetEntitySponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
acceptingApplications: true,
|
||||||
|
tiers: []
|
||||||
|
};
|
||||||
|
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,10 +57,20 @@ describe('GetEntitySponsorshipPricingPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { entityType: 'season', entityId: 'season-1', pricing: [] };
|
const mockResult: GetEntitySponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
acceptingApplications: true,
|
||||||
|
tiers: []
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.getViewModel()).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.getViewModel()).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,10 +80,20 @@ describe('GetEntitySponsorshipPricingPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { entityType: 'season', entityId: 'season-1', pricing: [] };
|
const mockResult: GetEntitySponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
acceptingApplications: true,
|
||||||
|
tiers: []
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { GetSponsorshipPricingOutputPort } from '@core/racing/application/ports/output/GetSponsorshipPricingOutputPort';
|
import type { GetEntitySponsorshipPricingResult } from '@core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
|
||||||
import { GetEntitySponsorshipPricingResultDTO } from '../dtos/GetEntitySponsorshipPricingResultDTO';
|
import { GetEntitySponsorshipPricingResultDTO } from '../dtos/GetEntitySponsorshipPricingResultDTO';
|
||||||
|
|
||||||
export class GetEntitySponsorshipPricingPresenter {
|
export class GetEntitySponsorshipPricingPresenter {
|
||||||
@@ -8,7 +8,7 @@ export class GetEntitySponsorshipPricingPresenter {
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(output: GetSponsorshipPricingOutputPort | null) {
|
present(output: GetEntitySponsorshipPricingResult | null) {
|
||||||
if (!output) {
|
if (!output) {
|
||||||
this.result = {
|
this.result = {
|
||||||
entityType: 'season',
|
entityType: 'season',
|
||||||
@@ -21,11 +21,11 @@ export class GetEntitySponsorshipPricingPresenter {
|
|||||||
this.result = {
|
this.result = {
|
||||||
entityType: output.entityType,
|
entityType: output.entityType,
|
||||||
entityId: output.entityId,
|
entityId: output.entityId,
|
||||||
pricing: output.pricing.map(item => ({
|
pricing: output.tiers.map(item => ({
|
||||||
id: item.id,
|
id: item.name,
|
||||||
level: item.level,
|
level: item.name,
|
||||||
price: item.price,
|
price: item.price,
|
||||||
currency: item.currency,
|
currency: 'USD',
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PendingSponsorshipRequestsOutputPort } from '@core/racing/application/ports/output/PendingSponsorshipRequestsOutputPort';
|
import type { GetPendingSponsorshipRequestsResult } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
|
||||||
import { GetPendingSponsorshipRequestsOutputDTO } from '../dtos/GetPendingSponsorshipRequestsOutputDTO';
|
import { GetPendingSponsorshipRequestsOutputDTO } from '../dtos/GetPendingSponsorshipRequestsOutputDTO';
|
||||||
|
|
||||||
export class GetPendingSponsorshipRequestsPresenter {
|
export class GetPendingSponsorshipRequestsPresenter {
|
||||||
@@ -8,7 +8,7 @@ export class GetPendingSponsorshipRequestsPresenter {
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(outputPort: PendingSponsorshipRequestsOutputPort | null) {
|
present(outputPort: GetPendingSponsorshipRequestsResult | null) {
|
||||||
if (!outputPort) {
|
if (!outputPort) {
|
||||||
this.result = null;
|
this.result = null;
|
||||||
return;
|
return;
|
||||||
@@ -17,7 +17,30 @@ export class GetPendingSponsorshipRequestsPresenter {
|
|||||||
this.result = {
|
this.result = {
|
||||||
entityType: outputPort.entityType,
|
entityType: outputPort.entityType,
|
||||||
entityId: outputPort.entityId,
|
entityId: outputPort.entityId,
|
||||||
requests: outputPort.requests,
|
requests: outputPort.requests.map(r => {
|
||||||
|
const request: any = {
|
||||||
|
id: r.request.id,
|
||||||
|
sponsorId: r.request.sponsorId,
|
||||||
|
sponsorName: r.sponsor?.name?.toString() || 'Unknown Sponsor',
|
||||||
|
tier: r.request.tier,
|
||||||
|
offeredAmount: r.financials.offeredAmount.amount,
|
||||||
|
currency: r.financials.offeredAmount.currency,
|
||||||
|
formattedAmount: `${r.financials.offeredAmount.amount} ${r.financials.offeredAmount.currency}`,
|
||||||
|
createdAt: r.request.createdAt,
|
||||||
|
platformFee: r.financials.platformFee.amount,
|
||||||
|
netAmount: r.financials.netAmount.amount,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (r.sponsor?.logoUrl) {
|
||||||
|
request.sponsorLogo = r.sponsor.logoUrl.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.request.message) {
|
||||||
|
request.message = r.request.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}),
|
||||||
totalCount: outputPort.totalCount,
|
totalCount: outputPort.totalCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { GetSponsorDashboardPresenter } from './GetSponsorDashboardPresenter';
|
import { GetSponsorDashboardPresenter } from './GetSponsorDashboardPresenter';
|
||||||
|
import type { GetSponsorDashboardResult } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
|
||||||
|
import { Money } from '@core/racing/domain/value-objects/Money';
|
||||||
|
|
||||||
describe('GetSponsorDashboardPresenter', () => {
|
describe('GetSponsorDashboardPresenter', () => {
|
||||||
let presenter: GetSponsorDashboardPresenter;
|
let presenter: GetSponsorDashboardPresenter;
|
||||||
@@ -10,9 +12,58 @@ describe('GetSponsorDashboardPresenter', () => {
|
|||||||
|
|
||||||
describe('reset', () => {
|
describe('reset', () => {
|
||||||
it('should reset the result to null', () => {
|
it('should reset the result to null', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', metrics: {}, sponsoredLeagues: [] };
|
const mockResult: GetSponsorDashboardResult = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
|
||||||
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
sponsorships: {
|
||||||
|
leagues: [],
|
||||||
|
teams: [],
|
||||||
|
drivers: [],
|
||||||
|
races: [],
|
||||||
|
platform: [],
|
||||||
|
},
|
||||||
|
recentActivity: [],
|
||||||
|
upcomingRenewals: [],
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
|
|
||||||
presenter.reset();
|
presenter.reset();
|
||||||
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
||||||
@@ -21,11 +72,59 @@ describe('GetSponsorDashboardPresenter', () => {
|
|||||||
|
|
||||||
describe('present', () => {
|
describe('present', () => {
|
||||||
it('should store the result', () => {
|
it('should store the result', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', metrics: {}, sponsoredLeagues: [] };
|
const mockResult: GetSponsorDashboardResult = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
sponsorships: {
|
||||||
|
leagues: [],
|
||||||
|
teams: [],
|
||||||
|
drivers: [],
|
||||||
|
races: [],
|
||||||
|
platform: [],
|
||||||
|
},
|
||||||
|
recentActivity: [],
|
||||||
|
upcomingRenewals: [],
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,10 +134,58 @@ describe('GetSponsorDashboardPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', metrics: {}, sponsoredLeagues: [] };
|
const mockResult: GetSponsorDashboardResult = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.getViewModel()).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
sponsorships: {
|
||||||
|
leagues: [],
|
||||||
|
teams: [],
|
||||||
|
drivers: [],
|
||||||
|
races: [],
|
||||||
|
platform: [],
|
||||||
|
},
|
||||||
|
recentActivity: [],
|
||||||
|
upcomingRenewals: [],
|
||||||
|
};
|
||||||
|
expect(presenter.getViewModel()).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,10 +195,58 @@ describe('GetSponsorDashboardPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', metrics: {}, sponsoredLeagues: [] };
|
const mockResult: GetSponsorDashboardResult = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
metrics: {
|
||||||
|
impressions: 1000,
|
||||||
|
impressionsChange: 0,
|
||||||
|
uniqueViewers: 700,
|
||||||
|
viewersChange: 0,
|
||||||
|
races: 5,
|
||||||
|
drivers: 10,
|
||||||
|
exposure: 50,
|
||||||
|
exposureChange: 0,
|
||||||
|
},
|
||||||
|
sponsoredLeagues: [],
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
costPerThousandViews: 0,
|
||||||
|
},
|
||||||
|
sponsorships: {
|
||||||
|
leagues: [],
|
||||||
|
teams: [],
|
||||||
|
drivers: [],
|
||||||
|
races: [],
|
||||||
|
platform: [],
|
||||||
|
},
|
||||||
|
recentActivity: [],
|
||||||
|
upcomingRenewals: [],
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SponsorDashboardOutputPort } from '@core/racing/application/ports/output/SponsorDashboardOutputPort';
|
import type { GetSponsorDashboardResult } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
|
||||||
import { SponsorDashboardDTO } from '../dtos/SponsorDashboardDTO';
|
import { SponsorDashboardDTO } from '../dtos/SponsorDashboardDTO';
|
||||||
|
|
||||||
export class GetSponsorDashboardPresenter {
|
export class GetSponsorDashboardPresenter {
|
||||||
@@ -8,8 +8,40 @@ export class GetSponsorDashboardPresenter {
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(outputPort: SponsorDashboardOutputPort | null) {
|
present(outputPort: GetSponsorDashboardResult | null) {
|
||||||
this.result = outputPort ?? null;
|
if (!outputPort) {
|
||||||
|
this.result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.result = {
|
||||||
|
sponsorId: outputPort.sponsorId,
|
||||||
|
sponsorName: outputPort.sponsorName,
|
||||||
|
metrics: outputPort.metrics,
|
||||||
|
sponsoredLeagues: outputPort.sponsoredLeagues.map(league => ({
|
||||||
|
id: league.leagueId,
|
||||||
|
name: league.leagueName,
|
||||||
|
tier: league.tier,
|
||||||
|
drivers: league.metrics.drivers,
|
||||||
|
races: league.metrics.races,
|
||||||
|
impressions: league.metrics.impressions,
|
||||||
|
status: league.status,
|
||||||
|
})),
|
||||||
|
investment: {
|
||||||
|
activeSponsorships: outputPort.investment.activeSponsorships,
|
||||||
|
totalInvestment: outputPort.investment.totalInvestment.amount,
|
||||||
|
costPerThousandViews: outputPort.investment.costPerThousandViews,
|
||||||
|
},
|
||||||
|
sponsorships: {
|
||||||
|
leagues: [],
|
||||||
|
teams: [],
|
||||||
|
drivers: [],
|
||||||
|
races: [],
|
||||||
|
platform: [],
|
||||||
|
},
|
||||||
|
recentActivity: [],
|
||||||
|
upcomingRenewals: [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewModel(): SponsorDashboardDTO | null {
|
getViewModel(): SponsorDashboardDTO | null {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { GetSponsorSponsorshipsPresenter } from './GetSponsorSponsorshipsPresenter';
|
import { GetSponsorSponsorshipsPresenter } from './GetSponsorSponsorshipsPresenter';
|
||||||
|
import type { GetSponsorSponsorshipsResult } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
|
||||||
|
import { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
|
||||||
|
import { Money } from '@core/racing/domain/value-objects/Money';
|
||||||
|
|
||||||
describe('GetSponsorSponsorshipsPresenter', () => {
|
describe('GetSponsorSponsorshipsPresenter', () => {
|
||||||
let presenter: GetSponsorSponsorshipsPresenter;
|
let presenter: GetSponsorSponsorshipsPresenter;
|
||||||
@@ -10,9 +13,38 @@ describe('GetSponsorSponsorshipsPresenter', () => {
|
|||||||
|
|
||||||
describe('reset', () => {
|
describe('reset', () => {
|
||||||
it('should reset the result to null', () => {
|
it('should reset the result to null', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', sponsorName: 'Test Sponsor', sponsorships: [] };
|
const mockSponsor = Sponsor.create({
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockResult: GetSponsorSponsorshipsResult = {
|
||||||
|
sponsor: mockSponsor,
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
totalPlatformFees: Money.create(0, 'USD'),
|
||||||
|
},
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
|
||||||
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
totalPlatformFees: 0,
|
||||||
|
currency: 'USD',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
|
|
||||||
presenter.reset();
|
presenter.reset();
|
||||||
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
||||||
@@ -21,11 +53,39 @@ describe('GetSponsorSponsorshipsPresenter', () => {
|
|||||||
|
|
||||||
describe('present', () => {
|
describe('present', () => {
|
||||||
it('should store the result', () => {
|
it('should store the result', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', sponsorName: 'Test Sponsor', sponsorships: [] };
|
const mockSponsor = Sponsor.create({
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockResult: GetSponsorSponsorshipsResult = {
|
||||||
|
sponsor: mockSponsor,
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
totalPlatformFees: Money.create(0, 'USD'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
totalPlatformFees: 0,
|
||||||
|
currency: 'USD',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,10 +95,38 @@ describe('GetSponsorSponsorshipsPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', sponsorName: 'Test Sponsor', sponsorships: [] };
|
const mockSponsor = Sponsor.create({
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockResult: GetSponsorSponsorshipsResult = {
|
||||||
|
sponsor: mockSponsor,
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
totalPlatformFees: Money.create(0, 'USD'),
|
||||||
|
},
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.getViewModel()).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
totalPlatformFees: 0,
|
||||||
|
currency: 'USD',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(presenter.getViewModel()).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,10 +136,38 @@ describe('GetSponsorSponsorshipsPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { sponsorId: 'sponsor-1', sponsorName: 'Test Sponsor', sponsorships: [] };
|
const mockSponsor = Sponsor.create({
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Test Sponsor',
|
||||||
|
contactEmail: 'test@example.com',
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockResult: GetSponsorSponsorshipsResult = {
|
||||||
|
sponsor: mockSponsor,
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: Money.create(0, 'USD'),
|
||||||
|
totalPlatformFees: Money.create(0, 'USD'),
|
||||||
|
},
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
sponsorId: 'sponsor-1',
|
||||||
|
sponsorName: 'Test Sponsor',
|
||||||
|
sponsorships: [],
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: 0,
|
||||||
|
activeSponsorships: 0,
|
||||||
|
totalInvestment: 0,
|
||||||
|
totalPlatformFees: 0,
|
||||||
|
currency: 'USD',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SponsorSponsorshipsOutputPort } from '@core/racing/application/ports/output/SponsorSponsorshipsOutputPort';
|
import type { GetSponsorSponsorshipsResult } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
|
||||||
import { SponsorSponsorshipsDTO } from '../dtos/SponsorSponsorshipsDTO';
|
import { SponsorSponsorshipsDTO } from '../dtos/SponsorSponsorshipsDTO';
|
||||||
|
|
||||||
export class GetSponsorSponsorshipsPresenter {
|
export class GetSponsorSponsorshipsPresenter {
|
||||||
@@ -8,8 +8,58 @@ export class GetSponsorSponsorshipsPresenter {
|
|||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(outputPort: SponsorSponsorshipsOutputPort | null) {
|
present(outputPort: GetSponsorSponsorshipsResult | null) {
|
||||||
this.result = outputPort ?? null;
|
if (!outputPort) {
|
||||||
|
this.result = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.result = {
|
||||||
|
sponsorId: outputPort.sponsor.id.toString(),
|
||||||
|
sponsorName: outputPort.sponsor.name.toString(),
|
||||||
|
sponsorships: outputPort.sponsorships.map(s => {
|
||||||
|
// Map status to DTO expected values
|
||||||
|
let status: 'pending' | 'active' | 'expired' | 'cancelled';
|
||||||
|
if (s.sponsorship.status === 'ended') {
|
||||||
|
status = 'expired';
|
||||||
|
} else if (s.sponsorship.status === 'pending' || s.sponsorship.status === 'active' || s.sponsorship.status === 'cancelled') {
|
||||||
|
status = s.sponsorship.status;
|
||||||
|
} else {
|
||||||
|
status = 'pending';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: s.sponsorship.id.toString(),
|
||||||
|
leagueId: s.league.id.toString(),
|
||||||
|
leagueName: s.league.name.toString(),
|
||||||
|
seasonId: s.season.id.toString(),
|
||||||
|
seasonName: s.season.name.toString(),
|
||||||
|
tier: s.sponsorship.tier,
|
||||||
|
status,
|
||||||
|
pricing: {
|
||||||
|
amount: s.financials.pricing.amount,
|
||||||
|
currency: s.financials.pricing.currency,
|
||||||
|
},
|
||||||
|
platformFee: {
|
||||||
|
amount: s.financials.platformFee.amount,
|
||||||
|
currency: s.financials.platformFee.currency,
|
||||||
|
},
|
||||||
|
netAmount: {
|
||||||
|
amount: s.financials.netAmount.amount,
|
||||||
|
currency: s.financials.netAmount.currency,
|
||||||
|
},
|
||||||
|
metrics: s.metrics,
|
||||||
|
createdAt: s.sponsorship.createdAt,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
summary: {
|
||||||
|
totalSponsorships: outputPort.summary.totalSponsorships,
|
||||||
|
activeSponsorships: outputPort.summary.activeSponsorships,
|
||||||
|
totalInvestment: outputPort.summary.totalInvestment.amount,
|
||||||
|
totalPlatformFees: outputPort.summary.totalPlatformFees.amount,
|
||||||
|
currency: outputPort.summary.totalInvestment.currency,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewModel(): SponsorSponsorshipsDTO | null {
|
getViewModel(): SponsorSponsorshipsDTO | null {
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { Result } from '@core/shared/application/Result';
|
|
||||||
import type {
|
|
||||||
GetSponsorsResult,
|
|
||||||
GetSponsorsErrorCode,
|
|
||||||
} from '@core/racing/application/use-cases/GetSponsorsUseCase';
|
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
|
||||||
import { GetSponsorsPresenter } from './GetSponsorsPresenter';
|
import { GetSponsorsPresenter } from './GetSponsorsPresenter';
|
||||||
|
import { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
|
||||||
|
|
||||||
describe('GetSponsorsPresenter', () => {
|
describe('GetSponsorsPresenter', () => {
|
||||||
let presenter: GetSponsorsPresenter;
|
let presenter: GetSponsorsPresenter;
|
||||||
@@ -16,8 +11,8 @@ describe('GetSponsorsPresenter', () => {
|
|||||||
|
|
||||||
describe('reset', () => {
|
describe('reset', () => {
|
||||||
it('should reset the model to null and cause responseModel to throw', () => {
|
it('should reset the model to null and cause responseModel to throw', () => {
|
||||||
const result = Result.ok<GetSponsorsResult, never>({ sponsors: [] });
|
const sponsors: Sponsor[] = [];
|
||||||
presenter.present(result);
|
presenter.present(sponsors);
|
||||||
expect(presenter.responseModel).toEqual({ sponsors: [] });
|
expect(presenter.responseModel).toEqual({ sponsors: [] });
|
||||||
|
|
||||||
presenter.reset();
|
presenter.reset();
|
||||||
@@ -26,29 +21,24 @@ describe('GetSponsorsPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('present', () => {
|
describe('present', () => {
|
||||||
it('should map Result.ok sponsors to DTO responseModel', () => {
|
it('should map sponsors to DTO responseModel', () => {
|
||||||
const result = Result.ok<GetSponsorsResult, never>({
|
const sponsors: Sponsor[] = [
|
||||||
sponsors: [
|
Sponsor.create({
|
||||||
{
|
id: 'sponsor-1',
|
||||||
id: 'sponsor-1',
|
name: 'Sponsor One',
|
||||||
name: 'Sponsor One',
|
contactEmail: 's1@example.com',
|
||||||
contactEmail: 's1@example.com',
|
logoUrl: 'logo1.png',
|
||||||
logoUrl: 'logo1.png',
|
websiteUrl: 'https://one.example.com',
|
||||||
websiteUrl: 'https://one.example.com',
|
createdAt: new Date('2024-01-01T00:00:00Z'),
|
||||||
createdAt: new Date('2024-01-01T00:00:00Z'),
|
}),
|
||||||
},
|
Sponsor.create({
|
||||||
{
|
id: 'sponsor-2',
|
||||||
id: 'sponsor-2',
|
name: 'Sponsor Two',
|
||||||
name: 'Sponsor Two',
|
contactEmail: 's2@example.com',
|
||||||
contactEmail: 's2@example.com',
|
}),
|
||||||
logoUrl: undefined,
|
];
|
||||||
websiteUrl: undefined,
|
|
||||||
createdAt: undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
presenter.present(result);
|
presenter.present(sponsors);
|
||||||
|
|
||||||
expect(presenter.responseModel).toEqual({
|
expect(presenter.responseModel).toEqual({
|
||||||
sponsors: [
|
sponsors: [
|
||||||
@@ -64,9 +54,7 @@ describe('GetSponsorsPresenter', () => {
|
|||||||
id: 'sponsor-2',
|
id: 'sponsor-2',
|
||||||
name: 'Sponsor Two',
|
name: 'Sponsor Two',
|
||||||
contactEmail: 's2@example.com',
|
contactEmail: 's2@example.com',
|
||||||
logoUrl: undefined,
|
createdAt: expect.any(Date),
|
||||||
websiteUrl: undefined,
|
|
||||||
createdAt: undefined,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -79,8 +67,8 @@ describe('GetSponsorsPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the model when presented', () => {
|
it('should return the model when presented', () => {
|
||||||
const result = Result.ok<GetSponsorsResult, never>({ sponsors: [] });
|
const sponsors: Sponsor[] = [];
|
||||||
presenter.present(result);
|
presenter.present(sponsors);
|
||||||
|
|
||||||
expect(presenter.getResponseModel()).toEqual({ sponsors: [] });
|
expect(presenter.getResponseModel()).toEqual({ sponsors: [] });
|
||||||
});
|
});
|
||||||
@@ -91,16 +79,26 @@ describe('GetSponsorsPresenter', () => {
|
|||||||
expect(() => presenter.responseModel).toThrow('Presenter not presented');
|
expect(() => presenter.responseModel).toThrow('Presenter not presented');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fallback to empty sponsors list on error', () => {
|
it('should return the model when presented', () => {
|
||||||
const error = {
|
const sponsors: Sponsor[] = [
|
||||||
code: 'REPOSITORY_ERROR' as GetSponsorsErrorCode,
|
Sponsor.create({
|
||||||
details: { message: 'DB error' },
|
id: 'sponsor-1',
|
||||||
} satisfies ApplicationErrorCode<GetSponsorsErrorCode, { message: string }>;
|
name: 'Sponsor One',
|
||||||
const result = Result.err<GetSponsorsResult, typeof error>(error);
|
contactEmail: 's1@example.com',
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
presenter.present(sponsors);
|
||||||
|
|
||||||
presenter.present(result);
|
expect(presenter.responseModel).toEqual({
|
||||||
|
sponsors: [
|
||||||
expect(presenter.responseModel).toEqual({ sponsors: [] });
|
{
|
||||||
|
id: 'sponsor-1',
|
||||||
|
name: 'Sponsor One',
|
||||||
|
contactEmail: 's1@example.com',
|
||||||
|
createdAt: expect.any(Date),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
import type { Result } from '@core/shared/application/Result';
|
import type { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
|
||||||
import type {
|
|
||||||
GetSponsorsResult,
|
|
||||||
GetSponsorsErrorCode,
|
|
||||||
} from '@core/racing/application/use-cases/GetSponsorsUseCase';
|
|
||||||
import { GetSponsorsOutputDTO } from '../dtos/GetSponsorsOutputDTO';
|
import { GetSponsorsOutputDTO } from '../dtos/GetSponsorsOutputDTO';
|
||||||
import type { SponsorDTO } from '../dtos/SponsorDTO';
|
import type { SponsorDTO } from '../dtos/SponsorDTO';
|
||||||
|
|
||||||
@@ -14,29 +9,26 @@ export class GetSponsorsPresenter {
|
|||||||
this.model = null;
|
this.model = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(
|
present(sponsors: Sponsor[]): void {
|
||||||
result: Result<
|
|
||||||
GetSponsorsResult,
|
|
||||||
ApplicationErrorCode<GetSponsorsErrorCode, { message: string }>
|
|
||||||
>,
|
|
||||||
): void {
|
|
||||||
if (result.isErr()) {
|
|
||||||
// For sponsor listing, fall back to empty list on error
|
|
||||||
this.model = { sponsors: [] };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = result.unwrap();
|
|
||||||
|
|
||||||
this.model = {
|
this.model = {
|
||||||
sponsors: output.sponsors.map<SponsorDTO>((sponsor) => ({
|
sponsors: sponsors.map<SponsorDTO>((sponsor) => {
|
||||||
id: sponsor.id,
|
const sponsorData: any = {
|
||||||
name: sponsor.name,
|
id: sponsor.id.toString(),
|
||||||
contactEmail: sponsor.contactEmail,
|
name: sponsor.name.toString(),
|
||||||
logoUrl: sponsor.logoUrl,
|
contactEmail: sponsor.contactEmail.toString(),
|
||||||
websiteUrl: sponsor.websiteUrl,
|
createdAt: sponsor.createdAt.toDate(),
|
||||||
createdAt: sponsor.createdAt,
|
};
|
||||||
})),
|
|
||||||
|
if (sponsor.logoUrl) {
|
||||||
|
sponsorData.logoUrl = sponsor.logoUrl.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sponsor.websiteUrl) {
|
||||||
|
sponsorData.websiteUrl = sponsor.websiteUrl.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sponsorData;
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { GetSponsorshipPricingPresenter } from './GetSponsorshipPricingPresenter';
|
import { GetSponsorshipPricingPresenter } from './GetSponsorshipPricingPresenter';
|
||||||
|
import type { GetSponsorshipPricingResult } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
|
||||||
|
|
||||||
describe('GetSponsorshipPricingPresenter', () => {
|
describe('GetSponsorshipPricingPresenter', () => {
|
||||||
let presenter: GetSponsorshipPricingPresenter;
|
let presenter: GetSponsorshipPricingPresenter;
|
||||||
@@ -10,9 +11,19 @@ describe('GetSponsorshipPricingPresenter', () => {
|
|||||||
|
|
||||||
describe('reset', () => {
|
describe('reset', () => {
|
||||||
it('should reset the result to null', () => {
|
it('should reset the result to null', () => {
|
||||||
const mockResult = { tiers: [] };
|
const mockResult: GetSponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
|
||||||
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
|
|
||||||
presenter.reset();
|
presenter.reset();
|
||||||
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
expect(() => presenter.viewModel).toThrow('Presenter not presented');
|
||||||
@@ -21,11 +32,20 @@ describe('GetSponsorshipPricingPresenter', () => {
|
|||||||
|
|
||||||
describe('present', () => {
|
describe('present', () => {
|
||||||
it('should store the result', () => {
|
it('should store the result', () => {
|
||||||
const mockResult = { tiers: [] };
|
const mockResult: GetSponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,10 +55,19 @@ describe('GetSponsorshipPricingPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { tiers: [] };
|
const mockResult: GetSponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.getViewModel()).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.getViewModel()).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,10 +77,19 @@ describe('GetSponsorshipPricingPresenter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return the result when presented', () => {
|
it('should return the result when presented', () => {
|
||||||
const mockResult = { tiers: [] };
|
const mockResult: GetSponsorshipPricingResult = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
presenter.present(mockResult);
|
presenter.present(mockResult);
|
||||||
|
|
||||||
expect(presenter.viewModel).toEqual(mockResult);
|
const expectedViewModel = {
|
||||||
|
entityType: 'season',
|
||||||
|
entityId: 'season-1',
|
||||||
|
pricing: []
|
||||||
|
};
|
||||||
|
expect(presenter.viewModel).toEqual(expectedViewModel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,12 +1,32 @@
|
|||||||
import type { GetSponsorshipPricingOutputPort } from '@core/racing/application/ports/output/GetSponsorshipPricingOutputPort';
|
import type { GetSponsorshipPricingResult } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
|
||||||
import { GetEntitySponsorshipPricingResultDTO } from '../dtos/GetEntitySponsorshipPricingResultDTO';
|
import { GetEntitySponsorshipPricingResultDTO } from '../dtos/GetEntitySponsorshipPricingResultDTO';
|
||||||
|
|
||||||
export class GetSponsorshipPricingPresenter {
|
export class GetSponsorshipPricingPresenter {
|
||||||
present(outputPort: GetSponsorshipPricingOutputPort): GetEntitySponsorshipPricingResultDTO {
|
private result: GetEntitySponsorshipPricingResultDTO | null = null;
|
||||||
return {
|
|
||||||
|
reset() {
|
||||||
|
this.result = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
present(outputPort: GetSponsorshipPricingResult): void {
|
||||||
|
this.result = {
|
||||||
entityType: outputPort.entityType,
|
entityType: outputPort.entityType,
|
||||||
entityId: outputPort.entityId,
|
entityId: outputPort.entityId,
|
||||||
pricing: outputPort.pricing,
|
pricing: outputPort.pricing.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
level: item.level,
|
||||||
|
price: item.price,
|
||||||
|
currency: item.currency,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getViewModel(): GetEntitySponsorshipPricingResultDTO | null {
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
get viewModel(): GetEntitySponsorshipPricingResultDTO {
|
||||||
|
if (!this.result) throw new Error('Presenter not presented');
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
import type { RejectSponsorshipRequestResult } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
|
||||||
|
|
||||||
export class RejectSponsorshipRequestPresenter {
|
export class RejectSponsorshipRequestPresenter {
|
||||||
private result: RejectSponsorshipRequestResultDTO | null = null;
|
private result: RejectSponsorshipRequestResult | null = null;
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
present(output: RejectSponsorshipRequestResultDTO | null) {
|
present(output: RejectSponsorshipRequestResult | null) {
|
||||||
this.result = output ?? null;
|
this.result = output ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewModel(): RejectSponsorshipRequestResultDTO | null {
|
getViewModel(): RejectSponsorshipRequestResult | null {
|
||||||
return this.result;
|
return this.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
get viewModel(): RejectSponsorshipRequestResultDTO | null {
|
get viewModel(): RejectSponsorshipRequestResult | null {
|
||||||
return this.result;
|
return this.result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { vi } from 'vitest';
|
|||||||
import { TeamController } from './TeamController';
|
import { TeamController } from './TeamController';
|
||||||
import { TeamService } from './TeamService';
|
import { TeamService } from './TeamService';
|
||||||
import type { Request } from 'express';
|
import type { Request } from 'express';
|
||||||
import { CreateTeamInputDTO, UpdateTeamInputDTO } from './dtos/CreateTeamInputDTO';
|
import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO';
|
||||||
|
import { UpdateTeamInput } from './dtos/TeamDto';
|
||||||
|
|
||||||
describe('TeamController', () => {
|
describe('TeamController', () => {
|
||||||
let controller: TeamController;
|
let controller: TeamController;
|
||||||
@@ -35,7 +36,7 @@ describe('TeamController', () => {
|
|||||||
|
|
||||||
describe('getAll', () => {
|
describe('getAll', () => {
|
||||||
it('should return all teams', async () => {
|
it('should return all teams', async () => {
|
||||||
const result = { teams: [] };
|
const result = { teams: [], totalCount: 0 };
|
||||||
service.getAll.mockResolvedValue(result);
|
service.getAll.mockResolvedValue(result);
|
||||||
|
|
||||||
const response = await controller.getAll();
|
const response = await controller.getAll();
|
||||||
@@ -49,12 +50,12 @@ describe('TeamController', () => {
|
|||||||
it('should return team details', async () => {
|
it('should return team details', async () => {
|
||||||
const teamId = 'team-123';
|
const teamId = 'team-123';
|
||||||
const userId = 'user-456';
|
const userId = 'user-456';
|
||||||
const result = { id: teamId, name: 'Team' };
|
const result = { team: { id: teamId, name: 'Team', tag: 'TAG', description: 'Desc', ownerId: 'owner', leagues: [] }, membership: null, canManage: false };
|
||||||
service.getDetails.mockResolvedValue(result);
|
service.getDetails.mockResolvedValue(result);
|
||||||
|
|
||||||
const mockReq: Partial<Request> = { ['user']: { userId } };
|
const mockReq = { user: { userId } } as any;
|
||||||
|
|
||||||
const response = await controller.getDetails(teamId, mockReq as Request);
|
const response = await controller.getDetails(teamId, mockReq);
|
||||||
|
|
||||||
expect(service.getDetails).toHaveBeenCalledWith(teamId, userId);
|
expect(service.getDetails).toHaveBeenCalledWith(teamId, userId);
|
||||||
expect(response).toEqual(result);
|
expect(response).toEqual(result);
|
||||||
@@ -64,7 +65,7 @@ describe('TeamController', () => {
|
|||||||
describe('getMembers', () => {
|
describe('getMembers', () => {
|
||||||
it('should return team members', async () => {
|
it('should return team members', async () => {
|
||||||
const teamId = 'team-123';
|
const teamId = 'team-123';
|
||||||
const result = { members: [] };
|
const result = { members: [], totalCount: 0, ownerCount: 0, managerCount: 0, memberCount: 0 };
|
||||||
service.getMembers.mockResolvedValue(result);
|
service.getMembers.mockResolvedValue(result);
|
||||||
|
|
||||||
const response = await controller.getMembers(teamId);
|
const response = await controller.getMembers(teamId);
|
||||||
@@ -77,7 +78,7 @@ describe('TeamController', () => {
|
|||||||
describe('getJoinRequests', () => {
|
describe('getJoinRequests', () => {
|
||||||
it('should return join requests', async () => {
|
it('should return join requests', async () => {
|
||||||
const teamId = 'team-123';
|
const teamId = 'team-123';
|
||||||
const result = { requests: [] };
|
const result = { requests: [], pendingCount: 0, totalCount: 0 };
|
||||||
service.getJoinRequests.mockResolvedValue(result);
|
service.getJoinRequests.mockResolvedValue(result);
|
||||||
|
|
||||||
const response = await controller.getJoinRequests(teamId);
|
const response = await controller.getJoinRequests(teamId);
|
||||||
@@ -89,14 +90,14 @@ describe('TeamController', () => {
|
|||||||
|
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
it('should create team', async () => {
|
it('should create team', async () => {
|
||||||
const input: CreateTeamInputDTO = { name: 'New Team' };
|
const input: CreateTeamInputDTO = { name: 'New Team', tag: 'TAG' };
|
||||||
const userId = 'user-123';
|
const userId = 'user-123';
|
||||||
const result = { teamId: 'team-456' };
|
const result = { id: 'team-456', success: true };
|
||||||
service.create.mockResolvedValue(result);
|
service.create.mockResolvedValue(result);
|
||||||
|
|
||||||
const mockReq: Partial<Request> = { ['user']: { userId } };
|
const mockReq = { user: { userId } } as any;
|
||||||
|
|
||||||
const response = await controller.create(input, mockReq as Request);
|
const response = await controller.create(input, mockReq);
|
||||||
|
|
||||||
expect(service.create).toHaveBeenCalledWith(input, userId);
|
expect(service.create).toHaveBeenCalledWith(input, userId);
|
||||||
expect(response).toEqual(result);
|
expect(response).toEqual(result);
|
||||||
@@ -106,14 +107,14 @@ describe('TeamController', () => {
|
|||||||
describe('update', () => {
|
describe('update', () => {
|
||||||
it('should update team', async () => {
|
it('should update team', async () => {
|
||||||
const teamId = 'team-123';
|
const teamId = 'team-123';
|
||||||
const input: UpdateTeamInputDTO = { name: 'Updated Team' };
|
|
||||||
const userId = 'user-456';
|
const userId = 'user-456';
|
||||||
|
const input: UpdateTeamInput = { name: 'Updated Team', updatedBy: userId };
|
||||||
const result = { success: true };
|
const result = { success: true };
|
||||||
service.update.mockResolvedValue(result);
|
service.update.mockResolvedValue(result);
|
||||||
|
|
||||||
const mockReq: Partial<Request> = { ['user']: { userId } };
|
const mockReq = { user: { userId } } as any;
|
||||||
|
|
||||||
const response = await controller.update(teamId, input, mockReq as Request);
|
const response = await controller.update(teamId, input, mockReq);
|
||||||
|
|
||||||
expect(service.update).toHaveBeenCalledWith(teamId, input, userId);
|
expect(service.update).toHaveBeenCalledWith(teamId, input, userId);
|
||||||
expect(response).toEqual(result);
|
expect(response).toEqual(result);
|
||||||
@@ -123,7 +124,7 @@ describe('TeamController', () => {
|
|||||||
describe('getDriverTeam', () => {
|
describe('getDriverTeam', () => {
|
||||||
it('should return driver team', async () => {
|
it('should return driver team', async () => {
|
||||||
const driverId = 'driver-123';
|
const driverId = 'driver-123';
|
||||||
const result = { teamId: 'team-456' };
|
const result = { team: { id: 'team-456', name: 'Team', tag: 'TAG', description: 'Desc', ownerId: 'owner', leagues: [] }, membership: { role: 'member' as const, joinedAt: '2023-01-01', isActive: true }, isOwner: false, canManage: false };
|
||||||
service.getDriverTeam.mockResolvedValue(result);
|
service.getDriverTeam.mockResolvedValue(result);
|
||||||
|
|
||||||
const response = await controller.getDriverTeam(driverId);
|
const response = await controller.getDriverTeam(driverId);
|
||||||
@@ -137,7 +138,7 @@ describe('TeamController', () => {
|
|||||||
it('should return team membership', async () => {
|
it('should return team membership', async () => {
|
||||||
const teamId = 'team-123';
|
const teamId = 'team-123';
|
||||||
const driverId = 'driver-456';
|
const driverId = 'driver-456';
|
||||||
const result = { role: 'member' };
|
const result = { role: 'member' as const, joinedAt: '2023-01-01', isActive: true };
|
||||||
service.getMembership.mockResolvedValue(result);
|
service.getMembership.mockResolvedValue(result);
|
||||||
|
|
||||||
const response = await controller.getMembership(teamId, driverId);
|
const response = await controller.getMembership(teamId, driverId);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { GetTeamMembersOutputDTO } from './dtos/GetTeamMembersOutputDTO';
|
|||||||
import { GetTeamJoinRequestsOutputDTO } from './dtos/GetTeamJoinRequestsOutputDTO';
|
import { GetTeamJoinRequestsOutputDTO } from './dtos/GetTeamJoinRequestsOutputDTO';
|
||||||
import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO';
|
import { CreateTeamInputDTO } from './dtos/CreateTeamInputDTO';
|
||||||
import { CreateTeamOutputDTO } from './dtos/CreateTeamOutputDTO';
|
import { CreateTeamOutputDTO } from './dtos/CreateTeamOutputDTO';
|
||||||
import { UpdateTeamInputDTO } from './dtos/UpdateTeamInputDTO';
|
import { UpdateTeamInput } from './dtos/TeamDto';
|
||||||
import { UpdateTeamOutputDTO } from './dtos/UpdateTeamOutputDTO';
|
import { UpdateTeamOutputDTO } from './dtos/UpdateTeamOutputDTO';
|
||||||
import { GetDriverTeamOutputDTO } from './dtos/GetDriverTeamOutputDTO';
|
import { GetDriverTeamOutputDTO } from './dtos/GetDriverTeamOutputDTO';
|
||||||
import { GetTeamMembershipOutputDTO } from './dtos/GetTeamMembershipOutputDTO';
|
import { GetTeamMembershipOutputDTO } from './dtos/GetTeamMembershipOutputDTO';
|
||||||
@@ -22,8 +22,7 @@ export class TeamController {
|
|||||||
@ApiOperation({ summary: 'Get all teams' })
|
@ApiOperation({ summary: 'Get all teams' })
|
||||||
@ApiResponse({ status: 200, description: 'List of all teams', type: GetAllTeamsOutputDTO })
|
@ApiResponse({ status: 200, description: 'List of all teams', type: GetAllTeamsOutputDTO })
|
||||||
async getAll(): Promise<GetAllTeamsOutputDTO> {
|
async getAll(): Promise<GetAllTeamsOutputDTO> {
|
||||||
const presenter = await this.teamService.getAll();
|
return await this.teamService.getAll();
|
||||||
return presenter.responseModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':teamId')
|
@Get(':teamId')
|
||||||
@@ -31,43 +30,38 @@ export class TeamController {
|
|||||||
@ApiResponse({ status: 200, description: 'Team details', type: GetTeamDetailsOutputDTO })
|
@ApiResponse({ status: 200, description: 'Team details', type: GetTeamDetailsOutputDTO })
|
||||||
@ApiResponse({ status: 404, description: 'Team not found' })
|
@ApiResponse({ status: 404, description: 'Team not found' })
|
||||||
async getDetails(@Param('teamId') teamId: string, @Req() req: Request): Promise<GetTeamDetailsOutputDTO | null> {
|
async getDetails(@Param('teamId') teamId: string, @Req() req: Request): Promise<GetTeamDetailsOutputDTO | null> {
|
||||||
const userId = req['user']?.userId;
|
const userId = (req as any)['user']?.userId;
|
||||||
const presenter = await this.teamService.getDetails(teamId, userId);
|
return await this.teamService.getDetails(teamId, userId);
|
||||||
return presenter.getResponseModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':teamId/members')
|
@Get(':teamId/members')
|
||||||
@ApiOperation({ summary: 'Get team members' })
|
@ApiOperation({ summary: 'Get team members' })
|
||||||
@ApiResponse({ status: 200, description: 'Team members', type: GetTeamMembersOutputDTO })
|
@ApiResponse({ status: 200, description: 'Team members', type: GetTeamMembersOutputDTO })
|
||||||
async getMembers(@Param('teamId') teamId: string): Promise<GetTeamMembersOutputDTO> {
|
async getMembers(@Param('teamId') teamId: string): Promise<GetTeamMembersOutputDTO> {
|
||||||
const presenter = await this.teamService.getMembers(teamId);
|
return await this.teamService.getMembers(teamId);
|
||||||
return presenter.getResponseModel()!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':teamId/join-requests')
|
@Get(':teamId/join-requests')
|
||||||
@ApiOperation({ summary: 'Get team join requests' })
|
@ApiOperation({ summary: 'Get team join requests' })
|
||||||
@ApiResponse({ status: 200, description: 'Team join requests', type: GetTeamJoinRequestsOutputDTO })
|
@ApiResponse({ status: 200, description: 'Team join requests', type: GetTeamJoinRequestsOutputDTO })
|
||||||
async getJoinRequests(@Param('teamId') teamId: string): Promise<GetTeamJoinRequestsOutputDTO> {
|
async getJoinRequests(@Param('teamId') teamId: string): Promise<GetTeamJoinRequestsOutputDTO> {
|
||||||
const presenter = await this.teamService.getJoinRequests(teamId);
|
return await this.teamService.getJoinRequests(teamId);
|
||||||
return presenter.getResponseModel()!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@ApiOperation({ summary: 'Create a new team' })
|
@ApiOperation({ summary: 'Create a new team' })
|
||||||
@ApiResponse({ status: 201, description: 'Team created', type: CreateTeamOutputDTO })
|
@ApiResponse({ status: 201, description: 'Team created', type: CreateTeamOutputDTO })
|
||||||
async create(@Body() input: CreateTeamInputDTO, @Req() req: Request): Promise<CreateTeamOutputDTO> {
|
async create(@Body() input: CreateTeamInputDTO, @Req() req: Request): Promise<CreateTeamOutputDTO> {
|
||||||
const userId = req['user']?.userId;
|
const userId = (req as any)['user']?.userId;
|
||||||
const presenter = await this.teamService.create(input, userId);
|
return await this.teamService.create(input, userId);
|
||||||
return presenter.responseModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':teamId')
|
@Patch(':teamId')
|
||||||
@ApiOperation({ summary: 'Update team' })
|
@ApiOperation({ summary: 'Update team' })
|
||||||
@ApiResponse({ status: 200, description: 'Team updated', type: UpdateTeamOutputDTO })
|
@ApiResponse({ status: 200, description: 'Team updated', type: UpdateTeamOutputDTO })
|
||||||
async update(@Param('teamId') teamId: string, @Body() input: UpdateTeamInputDTO, @Req() req: Request): Promise<UpdateTeamOutputDTO> {
|
async update(@Param('teamId') teamId: string, @Body() input: UpdateTeamInput, @Req() req: Request): Promise<UpdateTeamOutputDTO> {
|
||||||
const userId = req['user']?.userId;
|
const userId = (req as any)['user']?.userId;
|
||||||
const presenter = await this.teamService.update(teamId, input, userId);
|
return await this.teamService.update(teamId, input, userId);
|
||||||
return presenter.responseModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('driver/:driverId')
|
@Get('driver/:driverId')
|
||||||
@@ -75,8 +69,7 @@ export class TeamController {
|
|||||||
@ApiResponse({ status: 200, description: 'Driver\'s team', type: GetDriverTeamOutputDTO })
|
@ApiResponse({ status: 200, description: 'Driver\'s team', type: GetDriverTeamOutputDTO })
|
||||||
@ApiResponse({ status: 404, description: 'Team not found' })
|
@ApiResponse({ status: 404, description: 'Team not found' })
|
||||||
async getDriverTeam(@Param('driverId') driverId: string): Promise<GetDriverTeamOutputDTO | null> {
|
async getDriverTeam(@Param('driverId') driverId: string): Promise<GetDriverTeamOutputDTO | null> {
|
||||||
const presenter = await this.teamService.getDriverTeam(driverId);
|
return await this.teamService.getDriverTeam(driverId);
|
||||||
return presenter.getResponseModel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':teamId/members/:driverId')
|
@Get(':teamId/members/:driverId')
|
||||||
@@ -84,7 +77,6 @@ export class TeamController {
|
|||||||
@ApiResponse({ status: 200, description: 'Team membership', type: GetTeamMembershipOutputDTO })
|
@ApiResponse({ status: 200, description: 'Team membership', type: GetTeamMembershipOutputDTO })
|
||||||
@ApiResponse({ status: 404, description: 'Membership not found' })
|
@ApiResponse({ status: 404, description: 'Membership not found' })
|
||||||
async getMembership(@Param('teamId') teamId: string, @Param('driverId') driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
|
async getMembership(@Param('teamId') teamId: string, @Param('driverId') driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
|
||||||
const presenter = await this.teamService.getMembership(teamId, driverId);
|
return await this.teamService.getMembership(teamId, driverId);
|
||||||
return presenter.responseModel;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,6 @@ import { TeamService } from './TeamService';
|
|||||||
|
|
||||||
// Import core interfaces
|
// Import core interfaces
|
||||||
import type { Logger } from '@core/shared/application/Logger';
|
import type { Logger } from '@core/shared/application/Logger';
|
||||||
import type { ITeamRepository } from '@core/racing/domain/repositories/ITeamRepository';
|
|
||||||
import type { ITeamMembershipRepository } from '@core/racing/domain/repositories/ITeamMembershipRepository';
|
|
||||||
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
|
||||||
import type { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
|
||||||
|
|
||||||
// Import concrete in-memory implementations
|
// Import concrete in-memory implementations
|
||||||
import { InMemoryTeamRepository } from '@adapters/racing/persistence/inmemory/InMemoryTeamRepository';
|
import { InMemoryTeamRepository } from '@adapters/racing/persistence/inmemory/InMemoryTeamRepository';
|
||||||
@@ -15,15 +11,7 @@ import { InMemoryDriverRepository } from '@adapters/racing/persistence/inmemory/
|
|||||||
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
||||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||||
|
|
||||||
// Import use cases
|
// Use cases are imported and used directly in the service
|
||||||
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
|
|
||||||
import { GetTeamDetailsUseCase } from '@core/racing/application/use-cases/GetTeamDetailsUseCase';
|
|
||||||
import { GetTeamMembersUseCase } from '@core/racing/application/use-cases/GetTeamMembersUseCase';
|
|
||||||
import { GetTeamJoinRequestsUseCase } from '@core/racing/application/use-cases/GetTeamJoinRequestsUseCase';
|
|
||||||
import { CreateTeamUseCase } from '@core/racing/application/use-cases/CreateTeamUseCase';
|
|
||||||
import { UpdateTeamUseCase } from '@core/racing/application/use-cases/UpdateTeamUseCase';
|
|
||||||
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
|
|
||||||
import { GetTeamMembershipUseCase } from '@core/racing/application/use-cases/GetTeamMembershipUseCase';
|
|
||||||
|
|
||||||
// Define injection tokens
|
// Define injection tokens
|
||||||
export const TEAM_REPOSITORY_TOKEN = 'ITeamRepository';
|
export const TEAM_REPOSITORY_TOKEN = 'ITeamRepository';
|
||||||
@@ -58,53 +46,5 @@ export const TeamProviders: Provider[] = [
|
|||||||
provide: LOGGER_TOKEN,
|
provide: LOGGER_TOKEN,
|
||||||
useClass: ConsoleLogger,
|
useClass: ConsoleLogger,
|
||||||
},
|
},
|
||||||
// Use cases
|
// Use cases are created directly in the service
|
||||||
{
|
|
||||||
provide: GetAllTeamsUseCase,
|
|
||||||
useFactory: (teamRepo: ITeamRepository, membershipRepo: ITeamMembershipRepository, logger: Logger) =>
|
|
||||||
new GetAllTeamsUseCase(teamRepo, membershipRepo, logger),
|
|
||||||
inject: [TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: GetTeamDetailsUseCase,
|
|
||||||
useFactory: (teamRepo: ITeamRepository, membershipRepo: ITeamMembershipRepository) =>
|
|
||||||
new GetTeamDetailsUseCase(teamRepo, membershipRepo),
|
|
||||||
inject: [TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: GetTeamMembersUseCase,
|
|
||||||
useFactory: (membershipRepo: ITeamMembershipRepository, driverRepo: IDriverRepository, imageService: IImageServicePort, logger: Logger) =>
|
|
||||||
new GetTeamMembersUseCase(membershipRepo, driverRepo, imageService, logger),
|
|
||||||
inject: [TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, IMAGE_SERVICE_TOKEN, LOGGER_TOKEN],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: GetTeamJoinRequestsUseCase,
|
|
||||||
useFactory: (membershipRepo: ITeamMembershipRepository, driverRepo: IDriverRepository, imageService: IImageServicePort, logger: Logger) =>
|
|
||||||
new GetTeamJoinRequestsUseCase(membershipRepo, driverRepo, imageService, logger),
|
|
||||||
inject: [TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, IMAGE_SERVICE_TOKEN, LOGGER_TOKEN],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: CreateTeamUseCase,
|
|
||||||
useFactory: (teamRepo: ITeamRepository, membershipRepo: ITeamMembershipRepository) =>
|
|
||||||
new CreateTeamUseCase(teamRepo, membershipRepo),
|
|
||||||
inject: [TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: UpdateTeamUseCase,
|
|
||||||
useFactory: (teamRepo: ITeamRepository, membershipRepo: ITeamMembershipRepository) =>
|
|
||||||
new UpdateTeamUseCase(teamRepo, membershipRepo),
|
|
||||||
inject: [TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: GetDriverTeamUseCase,
|
|
||||||
useFactory: (teamRepo: ITeamRepository, membershipRepo: ITeamMembershipRepository, logger: Logger) =>
|
|
||||||
new GetDriverTeamUseCase(teamRepo, membershipRepo, logger),
|
|
||||||
inject: [TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: GetTeamMembershipUseCase,
|
|
||||||
useFactory: (membershipRepo: ITeamMembershipRepository, logger: Logger) =>
|
|
||||||
new GetTeamMembershipUseCase(membershipRepo, logger),
|
|
||||||
inject: [TEAM_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { vi } from 'vitest';
|
||||||
import { TeamService } from './TeamService';
|
import { TeamService } from './TeamService';
|
||||||
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
|
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
|
||||||
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
|
import { GetDriverTeamUseCase } from '@core/racing/application/use-cases/GetDriverTeamUseCase';
|
||||||
@@ -11,20 +12,20 @@ import { DriverTeamViewModel } from './dtos/TeamDto';
|
|||||||
|
|
||||||
describe('TeamService', () => {
|
describe('TeamService', () => {
|
||||||
let service: TeamService;
|
let service: TeamService;
|
||||||
let getAllTeamsUseCase: jest.Mocked<GetAllTeamsUseCase>;
|
let getAllTeamsUseCase: ReturnType<typeof vi.mocked<GetAllTeamsUseCase>>;
|
||||||
let getDriverTeamUseCase: jest.Mocked<GetDriverTeamUseCase>;
|
let getDriverTeamUseCase: ReturnType<typeof vi.mocked<GetDriverTeamUseCase>>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const mockGetAllTeamsUseCase = {
|
const mockGetAllTeamsUseCase = {
|
||||||
execute: jest.fn(),
|
execute: vi.fn(),
|
||||||
};
|
};
|
||||||
const mockGetDriverTeamUseCase = {
|
const mockGetDriverTeamUseCase = {
|
||||||
execute: jest.fn(),
|
execute: vi.fn(),
|
||||||
};
|
};
|
||||||
const mockLogger = {
|
const mockLogger = {
|
||||||
debug: jest.fn(),
|
debug: vi.fn(),
|
||||||
info: jest.fn(),
|
info: vi.fn(),
|
||||||
error: jest.fn(),
|
error: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
@@ -61,11 +62,11 @@ describe('TeamService', () => {
|
|||||||
getAllTeamsUseCase.execute.mockResolvedValue(mockResult as any);
|
getAllTeamsUseCase.execute.mockResolvedValue(mockResult as any);
|
||||||
|
|
||||||
const mockPresenter = {
|
const mockPresenter = {
|
||||||
present: jest.fn(),
|
present: vi.fn(),
|
||||||
getViewModel: jest.fn().mockReturnValue({ teams: [], totalCount: 0 }),
|
getViewModel: vi.fn().mockReturnValue({ teams: [], totalCount: 0 }),
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(AllTeamsPresenter as any) = jest.fn().mockImplementation(() => mockPresenter);
|
(AllTeamsPresenter as any) = vi.fn().mockImplementation(() => mockPresenter);
|
||||||
|
|
||||||
const result = await service.getAll();
|
const result = await service.getAll();
|
||||||
|
|
||||||
@@ -81,11 +82,11 @@ describe('TeamService', () => {
|
|||||||
getDriverTeamUseCase.execute.mockResolvedValue(mockResult as any);
|
getDriverTeamUseCase.execute.mockResolvedValue(mockResult as any);
|
||||||
|
|
||||||
const mockPresenter = {
|
const mockPresenter = {
|
||||||
present: jest.fn(),
|
present: vi.fn(),
|
||||||
getViewModel: jest.fn().mockReturnValue({} as DriverTeamViewModel),
|
getViewModel: vi.fn().mockReturnValue({} as DriverTeamViewModel),
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(DriverTeamPresenter as any) = jest.fn().mockImplementation(() => mockPresenter);
|
(DriverTeamPresenter as any) = vi.fn().mockImplementation(() => mockPresenter);
|
||||||
|
|
||||||
const result = await service.getDriverTeam('driver1');
|
const result = await service.getDriverTeam('driver1');
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import type { Logger } from '@core/shared/application/Logger';
|
|||||||
import type { ITeamRepository } from '@core/racing/domain/repositories/ITeamRepository';
|
import type { ITeamRepository } from '@core/racing/domain/repositories/ITeamRepository';
|
||||||
import type { ITeamMembershipRepository } from '@core/racing/domain/repositories/ITeamMembershipRepository';
|
import type { ITeamMembershipRepository } from '@core/racing/domain/repositories/ITeamMembershipRepository';
|
||||||
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
|
||||||
import type { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
|
||||||
|
|
||||||
// Use cases
|
// Use cases
|
||||||
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
|
import { GetAllTeamsUseCase } from '@core/racing/application/use-cases/GetAllTeamsUseCase';
|
||||||
@@ -38,7 +37,7 @@ import { CreateTeamPresenter } from './presenters/CreateTeamPresenter';
|
|||||||
import { UpdateTeamPresenter } from './presenters/UpdateTeamPresenter';
|
import { UpdateTeamPresenter } from './presenters/UpdateTeamPresenter';
|
||||||
|
|
||||||
// Tokens
|
// Tokens
|
||||||
import { TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, IMAGE_SERVICE_TOKEN, LOGGER_TOKEN } from './TeamProviders';
|
import { TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN } from './TeamProviders';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TeamService {
|
export class TeamService {
|
||||||
@@ -46,7 +45,6 @@ export class TeamService {
|
|||||||
@Inject(TEAM_REPOSITORY_TOKEN) private readonly teamRepository: ITeamRepository,
|
@Inject(TEAM_REPOSITORY_TOKEN) private readonly teamRepository: ITeamRepository,
|
||||||
@Inject(TEAM_MEMBERSHIP_REPOSITORY_TOKEN) private readonly membershipRepository: ITeamMembershipRepository,
|
@Inject(TEAM_MEMBERSHIP_REPOSITORY_TOKEN) private readonly membershipRepository: ITeamMembershipRepository,
|
||||||
@Inject(DRIVER_REPOSITORY_TOKEN) private readonly driverRepository: IDriverRepository,
|
@Inject(DRIVER_REPOSITORY_TOKEN) private readonly driverRepository: IDriverRepository,
|
||||||
@Inject(IMAGE_SERVICE_TOKEN) private readonly imageService: IImageServicePort,
|
|
||||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -57,11 +55,11 @@ export class TeamService {
|
|||||||
const useCase = new GetAllTeamsUseCase(this.teamRepository, this.membershipRepository, this.logger, presenter);
|
const useCase = new GetAllTeamsUseCase(this.teamRepository, this.membershipRepository, this.logger, presenter);
|
||||||
const result = await useCase.execute();
|
const result = await useCase.execute();
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
this.logger.error('Error fetching all teams', result.error?.details?.message || 'Unknown error');
|
this.logger.error('Error fetching all teams', new Error(result.error?.details?.message || 'Unknown error'));
|
||||||
return { teams: [], totalCount: 0 };
|
return { teams: [], totalCount: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
return presenter.responseModel;
|
return presenter.getResponseModel()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDetails(teamId: string, userId?: string): Promise<GetTeamDetailsOutputDTO | null> {
|
async getDetails(teamId: string, userId?: string): Promise<GetTeamDetailsOutputDTO | null> {
|
||||||
@@ -75,14 +73,14 @@ export class TeamService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return presenter.getResponseModel();
|
return presenter.getResponseModel()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMembers(teamId: string): Promise<GetTeamMembersOutputDTO> {
|
async getMembers(teamId: string): Promise<GetTeamMembersOutputDTO> {
|
||||||
this.logger.debug(`[TeamService] Fetching team members for teamId: ${teamId}`);
|
this.logger.debug(`[TeamService] Fetching team members for teamId: ${teamId}`);
|
||||||
|
|
||||||
const presenter = new TeamMembersPresenter();
|
const presenter = new TeamMembersPresenter();
|
||||||
const useCase = new GetTeamMembersUseCase(this.membershipRepository, this.driverRepository, this.imageService, this.logger, presenter);
|
const useCase = new GetTeamMembersUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, this.logger, presenter);
|
||||||
const result = await useCase.execute({ teamId });
|
const result = await useCase.execute({ teamId });
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
this.logger.error(`Error fetching team members for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
|
this.logger.error(`Error fetching team members for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`);
|
||||||
@@ -105,7 +103,7 @@ export class TeamService {
|
|||||||
const useCase = new GetTeamJoinRequestsUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, presenter);
|
const useCase = new GetTeamJoinRequestsUseCase(this.membershipRepository, this.driverRepository, this.teamRepository, presenter);
|
||||||
const result = await useCase.execute({ teamId });
|
const result = await useCase.execute({ teamId });
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
this.logger.error(new Error(`Error fetching team join requests for teamId: ${teamId}: ${result.error?.details?.message || 'Unknown error'}`));
|
this.logger.error(`Error fetching team join requests for teamId: ${teamId}`, new Error(result.error?.details?.message || 'Unknown error'));
|
||||||
return {
|
return {
|
||||||
requests: [],
|
requests: [],
|
||||||
pendingCount: 0,
|
pendingCount: 0,
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
|
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class CreateTeamInputDTO {
|
export class CreateTeamInputDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
tag: string;
|
tag!: string;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
export class CreateTeamOutputDTO {
|
export class CreateTeamOutputDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
success: boolean;
|
success!: boolean;
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,22 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
class TeamListItemDTO {
|
class TeamListItemDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
tag: string;
|
tag!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
description: string;
|
description!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
memberCount: number;
|
memberCount!: number;
|
||||||
|
|
||||||
@ApiProperty({ type: [String] })
|
@ApiProperty({ type: [String] })
|
||||||
leagues: string[];
|
leagues!: string[];
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||||
@@ -31,8 +31,8 @@ class TeamListItemDTO {
|
|||||||
|
|
||||||
export class GetAllTeamsOutputDTO {
|
export class GetAllTeamsOutputDTO {
|
||||||
@ApiProperty({ type: [TeamListItemDTO] })
|
@ApiProperty({ type: [TeamListItemDTO] })
|
||||||
teams: TeamListItemDTO[];
|
teams!: TeamListItemDTO[];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalCount: number;
|
totalCount!: number;
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,22 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
class TeamDTO {
|
class TeamDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
tag: string;
|
tag!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
description: string;
|
description!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
ownerId: string;
|
ownerId!: string;
|
||||||
|
|
||||||
@ApiProperty({ type: [String] })
|
@ApiProperty({ type: [String] })
|
||||||
leagues: string[];
|
leagues!: string[];
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
@@ -34,25 +34,25 @@ class TeamDTO {
|
|||||||
|
|
||||||
class MembershipDTO {
|
class MembershipDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
role: 'owner' | 'manager' | 'member';
|
role!: 'owner' | 'manager' | 'member';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
joinedAt: string;
|
joinedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isActive: boolean;
|
isActive!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetDriverTeamOutputDTO {
|
export class GetDriverTeamOutputDTO {
|
||||||
@ApiProperty({ type: TeamDTO })
|
@ApiProperty({ type: TeamDTO })
|
||||||
team: TeamDTO;
|
team!: TeamDTO;
|
||||||
|
|
||||||
@ApiProperty({ type: MembershipDTO })
|
@ApiProperty({ type: MembershipDTO })
|
||||||
membership: MembershipDTO;
|
membership!: MembershipDTO;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isOwner: boolean;
|
isOwner!: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
canManage: boolean;
|
canManage!: boolean;
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,22 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
class TeamDTO {
|
class TeamDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
tag: string;
|
tag!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
description: string;
|
description!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
ownerId: string;
|
ownerId!: string;
|
||||||
|
|
||||||
@ApiProperty({ type: [String] })
|
@ApiProperty({ type: [String] })
|
||||||
leagues: string[];
|
leagues!: string[];
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
@@ -34,22 +34,22 @@ class TeamDTO {
|
|||||||
|
|
||||||
class MembershipDTO {
|
class MembershipDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
role: 'owner' | 'manager' | 'member';
|
role!: 'owner' | 'manager' | 'member';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
joinedAt: string;
|
joinedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isActive: boolean;
|
isActive!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetTeamDetailsOutputDTO {
|
export class GetTeamDetailsOutputDTO {
|
||||||
@ApiProperty({ type: TeamDTO })
|
@ApiProperty({ type: TeamDTO })
|
||||||
team: TeamDTO;
|
team!: TeamDTO;
|
||||||
|
|
||||||
@ApiProperty({ type: MembershipDTO, nullable: true })
|
@ApiProperty({ type: MembershipDTO, nullable: true })
|
||||||
membership: MembershipDTO | null;
|
membership!: MembershipDTO | null;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
canManage: boolean;
|
canManage!: boolean;
|
||||||
}
|
}
|
||||||
@@ -2,34 +2,34 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
class TeamJoinRequestDTO {
|
class TeamJoinRequestDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
requestId: string;
|
requestId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverId: string;
|
driverId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverName: string;
|
driverName!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
teamId: string;
|
teamId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
status: 'pending' | 'approved' | 'rejected';
|
status!: 'pending' | 'approved' | 'rejected';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
requestedAt: string;
|
requestedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
avatarUrl: string;
|
avatarUrl!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetTeamJoinRequestsOutputDTO {
|
export class GetTeamJoinRequestsOutputDTO {
|
||||||
@ApiProperty({ type: [TeamJoinRequestDTO] })
|
@ApiProperty({ type: [TeamJoinRequestDTO] })
|
||||||
requests: TeamJoinRequestDTO[];
|
requests!: TeamJoinRequestDTO[];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
pendingCount: number;
|
pendingCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalCount: number;
|
totalCount!: number;
|
||||||
}
|
}
|
||||||
@@ -2,37 +2,37 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
class TeamMemberDTO {
|
class TeamMemberDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverId: string;
|
driverId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverName: string;
|
driverName!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
role: 'owner' | 'manager' | 'member';
|
role!: 'owner' | 'manager' | 'member';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
joinedAt: string;
|
joinedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isActive: boolean;
|
isActive!: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
avatarUrl: string;
|
avatarUrl!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetTeamMembersOutputDTO {
|
export class GetTeamMembersOutputDTO {
|
||||||
@ApiProperty({ type: [TeamMemberDTO] })
|
@ApiProperty({ type: [TeamMemberDTO] })
|
||||||
members: TeamMemberDTO[];
|
members!: TeamMemberDTO[];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalCount: number;
|
totalCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
ownerCount: number;
|
ownerCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
managerCount: number;
|
managerCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
memberCount: number;
|
memberCount!: number;
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,11 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
export class GetTeamMembershipOutputDTO {
|
export class GetTeamMembershipOutputDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
role: 'owner' | 'manager' | 'member';
|
role!: 'owner' | 'manager' | 'member';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
joinedAt: string;
|
joinedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isActive: boolean;
|
isActive!: boolean;
|
||||||
}
|
}
|
||||||
@@ -4,31 +4,31 @@ export type SkillLevel = 'beginner' | 'intermediate' | 'advanced' | 'pro';
|
|||||||
|
|
||||||
class TeamLeaderboardItemDTO {
|
class TeamLeaderboardItemDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
memberCount: number;
|
memberCount!: number;
|
||||||
|
|
||||||
@ApiProperty({ nullable: true })
|
@ApiProperty({ nullable: true })
|
||||||
rating: number | null;
|
rating!: number | null;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalWins: number;
|
totalWins!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalRaces: number;
|
totalRaces!: number;
|
||||||
|
|
||||||
@ApiProperty({ enum: ['beginner', 'intermediate', 'advanced', 'pro'] })
|
@ApiProperty({ enum: ['beginner', 'intermediate', 'advanced', 'pro'] })
|
||||||
performanceLevel: SkillLevel;
|
performanceLevel!: SkillLevel;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isRecruiting: boolean;
|
isRecruiting!: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
createdAt: string;
|
createdAt!: string;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -45,14 +45,14 @@ class TeamLeaderboardItemDTO {
|
|||||||
|
|
||||||
export class GetTeamsLeaderboardOutputDTO {
|
export class GetTeamsLeaderboardOutputDTO {
|
||||||
@ApiProperty({ type: [TeamLeaderboardItemDTO] })
|
@ApiProperty({ type: [TeamLeaderboardItemDTO] })
|
||||||
teams: TeamLeaderboardItemDTO[];
|
teams!: TeamLeaderboardItemDTO[];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
recruitingCount: number;
|
recruitingCount!: number;
|
||||||
|
|
||||||
@ApiProperty({ type: 'object', additionalProperties: { type: 'array', items: { $ref: '#/components/schemas/TeamLeaderboardItemDTO' } } })
|
@ApiProperty({ type: 'object', additionalProperties: { type: 'array', items: { $ref: '#/components/schemas/TeamLeaderboardItemDTO' } } })
|
||||||
groupsBySkillLevel: Record<SkillLevel, TeamLeaderboardItemDTO[]>;
|
groupsBySkillLevel!: Record<SkillLevel, TeamLeaderboardItemDTO[]>;
|
||||||
|
|
||||||
@ApiProperty({ type: [TeamLeaderboardItemDTO] })
|
@ApiProperty({ type: [TeamLeaderboardItemDTO] })
|
||||||
topTeams: TeamLeaderboardItemDTO[];
|
topTeams!: TeamLeaderboardItemDTO[];
|
||||||
}
|
}
|
||||||
@@ -3,22 +3,22 @@ import { IsString, IsNotEmpty, IsBoolean, IsOptional } from 'class-validator';
|
|||||||
|
|
||||||
export class TeamListItemViewModel {
|
export class TeamListItemViewModel {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
tag: string;
|
tag!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
description: string;
|
description!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
memberCount: number;
|
memberCount!: number;
|
||||||
|
|
||||||
@ApiProperty({ type: [String] })
|
@ApiProperty({ type: [String] })
|
||||||
leagues: string[];
|
leagues!: string[];
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
specialization?: 'endurance' | 'sprint' | 'mixed';
|
specialization?: 'endurance' | 'sprint' | 'mixed';
|
||||||
@@ -32,30 +32,30 @@ export class TeamListItemViewModel {
|
|||||||
|
|
||||||
export class AllTeamsViewModel {
|
export class AllTeamsViewModel {
|
||||||
@ApiProperty({ type: [TeamListItemViewModel] })
|
@ApiProperty({ type: [TeamListItemViewModel] })
|
||||||
teams: TeamListItemViewModel[];
|
teams!: TeamListItemViewModel[];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalCount: number;
|
totalCount!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeamViewModel {
|
export class TeamViewModel {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
id: string;
|
id!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
tag: string;
|
tag!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
description: string;
|
description!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
ownerId: string;
|
ownerId!: string;
|
||||||
|
|
||||||
@ApiProperty({ type: [String] })
|
@ApiProperty({ type: [String] })
|
||||||
leagues: string[];
|
leagues!: string[];
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
@@ -85,131 +85,131 @@ export enum MembershipStatus {
|
|||||||
|
|
||||||
export class MembershipViewModel {
|
export class MembershipViewModel {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
role: 'owner' | 'manager' | 'member';
|
role!: 'owner' | 'manager' | 'member';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
joinedAt: string;
|
joinedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isActive: boolean;
|
isActive!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DriverTeamViewModel {
|
export class DriverTeamViewModel {
|
||||||
@ApiProperty({ type: TeamViewModel })
|
@ApiProperty({ type: TeamViewModel })
|
||||||
team: TeamViewModel;
|
team!: TeamViewModel;
|
||||||
|
|
||||||
@ApiProperty({ type: MembershipViewModel })
|
@ApiProperty({ type: MembershipViewModel })
|
||||||
membership: MembershipViewModel;
|
membership!: MembershipViewModel;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isOwner: boolean;
|
isOwner!: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
canManage: boolean;
|
canManage!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetDriverTeamQuery {
|
export class GetDriverTeamQuery {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
teamId: string;
|
teamId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
driverId: string;
|
driverId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeamDetailsViewModel {
|
export class TeamDetailsViewModel {
|
||||||
@ApiProperty({ type: TeamViewModel })
|
@ApiProperty({ type: TeamViewModel })
|
||||||
team: TeamViewModel;
|
team!: TeamViewModel;
|
||||||
|
|
||||||
@ApiProperty({ type: MembershipViewModel, nullable: true })
|
@ApiProperty({ type: MembershipViewModel, nullable: true })
|
||||||
membership: MembershipViewModel | null;
|
membership!: MembershipViewModel | null;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
canManage: boolean;
|
canManage!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeamMemberViewModel {
|
export class TeamMemberViewModel {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverId: string;
|
driverId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverName: string;
|
driverName!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
role: 'owner' | 'manager' | 'member';
|
role!: 'owner' | 'manager' | 'member';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
joinedAt: string;
|
joinedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
isActive: boolean;
|
isActive!: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
avatarUrl: string;
|
avatarUrl!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeamMembersViewModel {
|
export class TeamMembersViewModel {
|
||||||
@ApiProperty({ type: [TeamMemberViewModel] })
|
@ApiProperty({ type: [TeamMemberViewModel] })
|
||||||
members: TeamMemberViewModel[];
|
members!: TeamMemberViewModel[];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalCount: number;
|
totalCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
ownerCount: number;
|
ownerCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
managerCount: number;
|
managerCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
memberCount: number;
|
memberCount!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeamJoinRequestViewModel {
|
export class TeamJoinRequestViewModel {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
requestId: string;
|
requestId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverId: string;
|
driverId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
driverName: string;
|
driverName!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
teamId: string;
|
teamId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
status: 'pending' | 'approved' | 'rejected';
|
status!: 'pending' | 'approved' | 'rejected';
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
requestedAt: string;
|
requestedAt!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
avatarUrl: string;
|
avatarUrl!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeamJoinRequestsViewModel {
|
export class TeamJoinRequestsViewModel {
|
||||||
@ApiProperty({ type: [TeamJoinRequestViewModel] })
|
@ApiProperty({ type: [TeamJoinRequestViewModel] })
|
||||||
requests: TeamJoinRequestViewModel[];
|
requests!: TeamJoinRequestViewModel[];
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
pendingCount: number;
|
pendingCount!: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
totalCount: number;
|
totalCount!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreateTeamInput {
|
export class CreateTeamInput {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
name: string;
|
name!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
tag: string;
|
tag!: string;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -218,17 +218,17 @@ export class CreateTeamInput {
|
|||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
ownerId: string;
|
ownerId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreateTeamOutput {
|
export class CreateTeamOutput {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
teamId: string;
|
teamId!: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
success: boolean;
|
success!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateTeamInput {
|
export class UpdateTeamInput {
|
||||||
@@ -254,19 +254,19 @@ export class UpdateTeamInput {
|
|||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
updatedBy: string;
|
updatedBy!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateTeamOutput {
|
export class UpdateTeamOutput {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
success: boolean;
|
success!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ApproveTeamJoinRequestInput {
|
export class ApproveTeamJoinRequestInput {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
requestId: string;
|
requestId!: string;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -277,13 +277,13 @@ export class ApproveTeamJoinRequestInput {
|
|||||||
export class ApproveTeamJoinRequestOutput {
|
export class ApproveTeamJoinRequestOutput {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
success: boolean;
|
success!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RejectTeamJoinRequestInput {
|
export class RejectTeamJoinRequestInput {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsString()
|
@IsString()
|
||||||
requestId: string;
|
requestId!: string;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -294,5 +294,5 @@ export class RejectTeamJoinRequestInput {
|
|||||||
export class RejectTeamJoinRequestOutput {
|
export class RejectTeamJoinRequestOutput {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
success: boolean;
|
success!: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
|
|
||||||
export class UpdateTeamOutputDTO {
|
export class UpdateTeamOutputDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
success: boolean;
|
success!: boolean;
|
||||||
}
|
}
|
||||||
@@ -16,4 +16,11 @@ export class TeamMembershipPresenter implements UseCaseOutputPort<GetTeamMembers
|
|||||||
getResponseModel(): GetTeamMembershipOutputDTO | null {
|
getResponseModel(): GetTeamMembershipOutputDTO | null {
|
||||||
return this.result;
|
return this.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get responseModel(): GetTeamMembershipOutputDTO {
|
||||||
|
if (!this.result) {
|
||||||
|
throw new Error('Presenter not presented');
|
||||||
|
}
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,107 +1,90 @@
|
|||||||
import type { TeamsLeaderboardOutputPort } from '@core/racing/application/ports/output/TeamsLeaderboardOutputPort';
|
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
||||||
|
import type { GetTeamsLeaderboardResult } from '@core/racing/application/use-cases/GetTeamsLeaderboardUseCase';
|
||||||
import type { GetTeamsLeaderboardOutputDTO } from '../dtos/GetTeamsLeaderboardOutputDTO';
|
import type { GetTeamsLeaderboardOutputDTO } from '../dtos/GetTeamsLeaderboardOutputDTO';
|
||||||
|
|
||||||
export class TeamsLeaderboardPresenter {
|
export class TeamsLeaderboardPresenter implements UseCaseOutputPort<GetTeamsLeaderboardResult> {
|
||||||
private result: GetTeamsLeaderboardOutputDTO | null = null;
|
private result: GetTeamsLeaderboardOutputDTO | null = null;
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.result = null;
|
this.result = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async present(outputPort: TeamsLeaderboardOutputPort): Promise<void> {
|
present(result: GetTeamsLeaderboardResult): void {
|
||||||
this.result = {
|
this.result = {
|
||||||
teams: outputPort.teams.map(team => ({
|
teams: result.items.map(item => ({
|
||||||
id: team.id,
|
id: item.team.id,
|
||||||
name: team.name,
|
name: item.team.name.toString(),
|
||||||
memberCount: team.memberCount,
|
memberCount: item.memberCount,
|
||||||
rating: team.rating,
|
rating: item.rating,
|
||||||
totalWins: team.totalWins,
|
totalWins: item.totalWins,
|
||||||
totalRaces: team.totalRaces,
|
totalRaces: item.totalRaces,
|
||||||
performanceLevel: team.performanceLevel,
|
performanceLevel: item.performanceLevel,
|
||||||
isRecruiting: team.isRecruiting,
|
isRecruiting: item.isRecruiting,
|
||||||
createdAt: team.createdAt.toISOString(),
|
createdAt: item.createdAt.toISOString(),
|
||||||
description: team.description,
|
description: item.team.description?.toString() || '',
|
||||||
specialization: team.specialization,
|
|
||||||
region: team.region,
|
|
||||||
languages: team.languages,
|
|
||||||
})),
|
})),
|
||||||
recruitingCount: outputPort.recruitingCount,
|
recruitingCount: result.recruitingCount,
|
||||||
groupsBySkillLevel: {
|
groupsBySkillLevel: {
|
||||||
beginner: outputPort.groupsBySkillLevel.beginner.map(team => ({
|
beginner: result.groupsBySkillLevel.beginner.map(item => ({
|
||||||
id: team.id,
|
id: item.team.id,
|
||||||
name: team.name,
|
name: item.team.name.toString(),
|
||||||
memberCount: team.memberCount,
|
memberCount: item.memberCount,
|
||||||
rating: team.rating,
|
rating: item.rating,
|
||||||
totalWins: team.totalWins,
|
totalWins: item.totalWins,
|
||||||
totalRaces: team.totalRaces,
|
totalRaces: item.totalRaces,
|
||||||
performanceLevel: team.performanceLevel,
|
performanceLevel: item.performanceLevel,
|
||||||
isRecruiting: team.isRecruiting,
|
isRecruiting: item.isRecruiting,
|
||||||
createdAt: team.createdAt.toISOString(),
|
createdAt: item.createdAt.toISOString(),
|
||||||
description: team.description,
|
description: item.team.description?.toString() || '',
|
||||||
specialization: team.specialization,
|
|
||||||
region: team.region,
|
|
||||||
languages: team.languages,
|
|
||||||
})),
|
})),
|
||||||
intermediate: outputPort.groupsBySkillLevel.intermediate.map(team => ({
|
intermediate: result.groupsBySkillLevel.intermediate.map(item => ({
|
||||||
id: team.id,
|
id: item.team.id,
|
||||||
name: team.name,
|
name: item.team.name.toString(),
|
||||||
memberCount: team.memberCount,
|
memberCount: item.memberCount,
|
||||||
rating: team.rating,
|
rating: item.rating,
|
||||||
totalWins: team.totalWins,
|
totalWins: item.totalWins,
|
||||||
totalRaces: team.totalRaces,
|
totalRaces: item.totalRaces,
|
||||||
performanceLevel: team.performanceLevel,
|
performanceLevel: item.performanceLevel,
|
||||||
isRecruiting: team.isRecruiting,
|
isRecruiting: item.isRecruiting,
|
||||||
createdAt: team.createdAt.toISOString(),
|
createdAt: item.createdAt.toISOString(),
|
||||||
description: team.description,
|
description: item.team.description?.toString() || '',
|
||||||
specialization: team.specialization,
|
|
||||||
region: team.region,
|
|
||||||
languages: team.languages,
|
|
||||||
})),
|
})),
|
||||||
advanced: outputPort.groupsBySkillLevel.advanced.map(team => ({
|
advanced: result.groupsBySkillLevel.advanced.map(item => ({
|
||||||
id: team.id,
|
id: item.team.id,
|
||||||
name: team.name,
|
name: item.team.name.toString(),
|
||||||
memberCount: team.memberCount,
|
memberCount: item.memberCount,
|
||||||
rating: team.rating,
|
rating: item.rating,
|
||||||
totalWins: team.totalWins,
|
totalWins: item.totalWins,
|
||||||
totalRaces: team.totalRaces,
|
totalRaces: item.totalRaces,
|
||||||
performanceLevel: team.performanceLevel,
|
performanceLevel: item.performanceLevel,
|
||||||
isRecruiting: team.isRecruiting,
|
isRecruiting: item.isRecruiting,
|
||||||
createdAt: team.createdAt.toISOString(),
|
createdAt: item.createdAt.toISOString(),
|
||||||
description: team.description,
|
description: item.team.description?.toString() || '',
|
||||||
specialization: team.specialization,
|
|
||||||
region: team.region,
|
|
||||||
languages: team.languages,
|
|
||||||
})),
|
})),
|
||||||
pro: outputPort.groupsBySkillLevel.pro.map(team => ({
|
pro: result.groupsBySkillLevel.pro.map(item => ({
|
||||||
id: team.id,
|
id: item.team.id,
|
||||||
name: team.name,
|
name: item.team.name.toString(),
|
||||||
memberCount: team.memberCount,
|
memberCount: item.memberCount,
|
||||||
rating: team.rating,
|
rating: item.rating,
|
||||||
totalWins: team.totalWins,
|
totalWins: item.totalWins,
|
||||||
totalRaces: team.totalRaces,
|
totalRaces: item.totalRaces,
|
||||||
performanceLevel: team.performanceLevel,
|
performanceLevel: item.performanceLevel,
|
||||||
isRecruiting: team.isRecruiting,
|
isRecruiting: item.isRecruiting,
|
||||||
createdAt: team.createdAt.toISOString(),
|
createdAt: item.createdAt.toISOString(),
|
||||||
description: team.description,
|
description: item.team.description?.toString() || '',
|
||||||
specialization: team.specialization,
|
|
||||||
region: team.region,
|
|
||||||
languages: team.languages,
|
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
topTeams: outputPort.topTeams.map(team => ({
|
topTeams: result.topItems.map(item => ({
|
||||||
id: team.id,
|
id: item.team.id,
|
||||||
name: team.name,
|
name: item.team.name.toString(),
|
||||||
memberCount: team.memberCount,
|
memberCount: item.memberCount,
|
||||||
rating: team.rating,
|
rating: item.rating,
|
||||||
totalWins: team.totalWins,
|
totalWins: item.totalWins,
|
||||||
totalRaces: team.totalRaces,
|
totalRaces: item.totalRaces,
|
||||||
performanceLevel: team.performanceLevel,
|
performanceLevel: item.performanceLevel,
|
||||||
isRecruiting: team.isRecruiting,
|
isRecruiting: item.isRecruiting,
|
||||||
createdAt: team.createdAt.toISOString(),
|
createdAt: item.createdAt.toISOString(),
|
||||||
description: team.description,
|
description: item.team.description?.toString() || '',
|
||||||
specialization: team.specialization,
|
|
||||||
region: team.region,
|
|
||||||
languages: team.languages,
|
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
import { Controller, Get } from '@nestjs/common';
|
|
||||||
import { HelloService } from '../application/hello/hello.service';
|
|
||||||
|
|
||||||
@Controller()
|
|
||||||
export class HelloController {
|
|
||||||
constructor(private readonly helloService: HelloService) {}
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
getHello(): { message: string } {
|
|
||||||
const presenter = this.helloService.getHello();
|
|
||||||
return presenter.responseModel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
|
||||||
import type { CreatePaymentResult } from '@core/payments/application/use-cases/CreatePaymentUseCase';
|
|
||||||
import type { CreatePaymentViewModel, PaymentDto } from './types';
|
|
||||||
|
|
||||||
export class CreatePaymentPresenter implements UseCaseOutputPort<CreatePaymentResult> {
|
|
||||||
private viewModel: CreatePaymentViewModel | null = null;
|
|
||||||
|
|
||||||
present(result: CreatePaymentResult): void {
|
|
||||||
this.viewModel = {
|
|
||||||
payment: this.mapPaymentToDto(result.payment),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getViewModel(): CreatePaymentViewModel | null {
|
|
||||||
return this.viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
this.viewModel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapPaymentToDto(payment: CreatePaymentResult['payment']): PaymentDto {
|
|
||||||
return {
|
|
||||||
id: payment.id,
|
|
||||||
type: payment.type,
|
|
||||||
amount: payment.amount,
|
|
||||||
platformFee: payment.platformFee,
|
|
||||||
netAmount: payment.netAmount,
|
|
||||||
payerId: payment.payerId,
|
|
||||||
payerType: payment.payerType,
|
|
||||||
leagueId: payment.leagueId,
|
|
||||||
seasonId: payment.seasonId,
|
|
||||||
status: payment.status,
|
|
||||||
createdAt: payment.createdAt,
|
|
||||||
completedAt: payment.completedAt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
|
||||||
import type { GetPaymentsResult } from '@core/payments/application/use-cases/GetPaymentsUseCase';
|
|
||||||
import type { GetPaymentsViewModel, PaymentDto } from './types';
|
|
||||||
|
|
||||||
export class GetPaymentsPresenter implements UseCaseOutputPort<GetPaymentsResult> {
|
|
||||||
private viewModel: GetPaymentsViewModel | null = null;
|
|
||||||
|
|
||||||
present(result: GetPaymentsResult): void {
|
|
||||||
this.viewModel = {
|
|
||||||
payments: result.payments.map(payment => this.mapPaymentToDto(payment)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getViewModel(): GetPaymentsViewModel | null {
|
|
||||||
return this.viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
this.viewModel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapPaymentToDto(payment: GetPaymentsResult['payments'][0]): PaymentDto {
|
|
||||||
return {
|
|
||||||
id: payment.id,
|
|
||||||
type: payment.type,
|
|
||||||
amount: payment.amount,
|
|
||||||
platformFee: payment.platformFee,
|
|
||||||
netAmount: payment.netAmount,
|
|
||||||
payerId: payment.payerId,
|
|
||||||
payerType: payment.payerType,
|
|
||||||
leagueId: payment.leagueId,
|
|
||||||
seasonId: payment.seasonId,
|
|
||||||
status: payment.status,
|
|
||||||
createdAt: payment.createdAt,
|
|
||||||
completedAt: payment.completedAt,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
|
||||||
import type { GetSponsorBillingResult } from '@core/payments/application/use-cases/GetSponsorBillingUseCase';
|
|
||||||
import type { SponsorBillingSummary } from './types';
|
|
||||||
|
|
||||||
export class GetSponsorBillingPresenter implements UseCaseOutputPort<GetSponsorBillingResult> {
|
|
||||||
private viewModel: SponsorBillingSummary | null = null;
|
|
||||||
|
|
||||||
present(result: GetSponsorBillingResult): void {
|
|
||||||
this.viewModel = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
getViewModel(): SponsorBillingSummary | null {
|
|
||||||
return this.viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
this.viewModel = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export * from './types';
|
|
||||||
export * from './CreatePaymentPresenter';
|
|
||||||
export * from './GetPaymentsPresenter';
|
|
||||||
export * from './GetSponsorBillingPresenter';
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
import type { PaymentType, PayerType, PaymentStatus } from '@core/payments/domain/entities/Payment';
|
|
||||||
import type { PrizeType } from '@core/payments/domain/entities/Prize';
|
|
||||||
import type { TransactionType, ReferenceType } from '@core/payments/domain/entities/Wallet';
|
|
||||||
import type { MembershipFeeType } from '@core/payments/domain/entities/MembershipFee';
|
|
||||||
import type { MemberPaymentStatus } from '@core/payments/domain/entities/MemberPayment';
|
|
||||||
|
|
||||||
// DTOs for API responses
|
|
||||||
|
|
||||||
export interface PaymentDto {
|
|
||||||
id: string;
|
|
||||||
type: PaymentType;
|
|
||||||
amount: number;
|
|
||||||
platformFee: number;
|
|
||||||
netAmount: number;
|
|
||||||
payerId: string;
|
|
||||||
payerType: PayerType;
|
|
||||||
leagueId: string;
|
|
||||||
seasonId: string | undefined;
|
|
||||||
status: PaymentStatus;
|
|
||||||
createdAt: Date;
|
|
||||||
completedAt: Date | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PrizeDto {
|
|
||||||
id: string;
|
|
||||||
leagueId: string;
|
|
||||||
seasonId: string;
|
|
||||||
position: number;
|
|
||||||
name: string;
|
|
||||||
amount: number;
|
|
||||||
type: PrizeType;
|
|
||||||
description: string | undefined;
|
|
||||||
awarded: boolean;
|
|
||||||
awardedTo: string | undefined;
|
|
||||||
awardedAt: Date | undefined;
|
|
||||||
createdAt: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WalletDto {
|
|
||||||
id: string;
|
|
||||||
leagueId: string;
|
|
||||||
balance: number;
|
|
||||||
totalRevenue: number;
|
|
||||||
totalPlatformFees: number;
|
|
||||||
totalWithdrawn: number;
|
|
||||||
currency: string;
|
|
||||||
createdAt: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TransactionDto {
|
|
||||||
id: string;
|
|
||||||
walletId: string;
|
|
||||||
type: TransactionType;
|
|
||||||
amount: number;
|
|
||||||
description: string;
|
|
||||||
referenceId: string | undefined;
|
|
||||||
referenceType: ReferenceType | undefined;
|
|
||||||
createdAt: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MembershipFeeDto {
|
|
||||||
id: string;
|
|
||||||
leagueId: string;
|
|
||||||
seasonId: string | undefined;
|
|
||||||
type: MembershipFeeType;
|
|
||||||
amount: number;
|
|
||||||
enabled: boolean;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MemberPaymentDto {
|
|
||||||
id: string;
|
|
||||||
feeId: string;
|
|
||||||
driverId: string;
|
|
||||||
amount: number;
|
|
||||||
platformFee: number;
|
|
||||||
netAmount: number;
|
|
||||||
status: MemberPaymentStatus;
|
|
||||||
dueDate: Date;
|
|
||||||
paidAt: Date | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// View Models
|
|
||||||
|
|
||||||
export interface CreatePaymentViewModel {
|
|
||||||
payment: PaymentDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetPaymentsViewModel {
|
|
||||||
payments: PaymentDto[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetPrizesViewModel {
|
|
||||||
prizes: PrizeDto[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreatePrizeViewModel {
|
|
||||||
prize: PrizeDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AwardPrizeViewModel {
|
|
||||||
prize: PrizeDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeletePrizeViewModel {
|
|
||||||
success: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetWalletViewModel {
|
|
||||||
wallet: WalletDto;
|
|
||||||
transactions: TransactionDto[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProcessWalletTransactionViewModel {
|
|
||||||
wallet: WalletDto;
|
|
||||||
transaction: TransactionDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetMembershipFeesViewModel {
|
|
||||||
fee: MembershipFeeDto | null;
|
|
||||||
payments: MemberPaymentDto[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpsertMembershipFeeViewModel {
|
|
||||||
fee: MembershipFeeDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdateMemberPaymentViewModel {
|
|
||||||
payment: MemberPaymentDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdatePaymentStatusViewModel {
|
|
||||||
payment: PaymentDto;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sponsor Billing
|
|
||||||
|
|
||||||
export interface SponsorBillingStats {
|
|
||||||
totalSpent: number;
|
|
||||||
pendingAmount: number;
|
|
||||||
nextPaymentDate: string | null;
|
|
||||||
nextPaymentAmount: number | null;
|
|
||||||
activeSponsorships: number;
|
|
||||||
averageMonthlySpend: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SponsorInvoiceSummary {
|
|
||||||
id: string;
|
|
||||||
invoiceNumber: string;
|
|
||||||
date: string;
|
|
||||||
dueDate: string;
|
|
||||||
amount: number;
|
|
||||||
vatAmount: number;
|
|
||||||
totalAmount: number;
|
|
||||||
status: 'paid' | 'pending' | 'overdue' | 'failed';
|
|
||||||
description: string;
|
|
||||||
sponsorshipType: 'league' | 'team' | 'driver' | 'race' | 'platform';
|
|
||||||
pdfUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SponsorPaymentMethodSummary {
|
|
||||||
id: string;
|
|
||||||
type: 'card' | 'bank' | 'sepa';
|
|
||||||
last4: string;
|
|
||||||
brand?: string;
|
|
||||||
isDefault: boolean;
|
|
||||||
expiryMonth?: number;
|
|
||||||
expiryYear?: number;
|
|
||||||
bankName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SponsorBillingSummary {
|
|
||||||
paymentMethods: SponsorPaymentMethodSummary[];
|
|
||||||
invoices: SponsorInvoiceSummary[];
|
|
||||||
stats: SponsorBillingStats;
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,54 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2017",
|
"baseUrl": ".",
|
||||||
"module": "commonjs",
|
"declaration": true,
|
||||||
"lib": ["es2022", "dom"],
|
"declarationMap": true,
|
||||||
"moduleResolution": "node",
|
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
"incremental": true,
|
||||||
|
"lib": [
|
||||||
|
"es2022",
|
||||||
|
"dom"
|
||||||
|
],
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
"noEmit": false,
|
"noEmit": false,
|
||||||
"noEmitOnError": true,
|
"noEmitOnError": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"declaration": true,
|
"outDir": "./dist",
|
||||||
"declarationMap": true,
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
],
|
||||||
|
"@adapters/*": [
|
||||||
|
"../../adapters/*"
|
||||||
|
],
|
||||||
|
"@core/*": [
|
||||||
|
"../../core/*"
|
||||||
|
],
|
||||||
|
"@testing/*": [
|
||||||
|
"../../testing/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"outDir": "./dist",
|
"strict": true,
|
||||||
"incremental": true,
|
"strictNullChecks": true,
|
||||||
"baseUrl": ".",
|
"target": "es2017",
|
||||||
"types": ["node", "express", "vitest/globals"],
|
"types": [
|
||||||
"paths": {
|
"node",
|
||||||
"@/*": ["./*"],
|
"express",
|
||||||
"@core/*": ["../../core/*"],
|
"vitest/globals"
|
||||||
"@adapters/*": ["../../adapters/*"],
|
]
|
||||||
"@testing/*": ["../../testing/*"]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*", "../../adapters/bootstrap/EnsureInitialData.ts"],
|
"exclude": [
|
||||||
"exclude": ["node_modules", "dist", "**/*.mock.ts"]
|
"node_modules",
|
||||||
}
|
"dist",
|
||||||
|
"**/*.mock.ts"
|
||||||
|
],
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -11,13 +11,14 @@ import { Result } from '@core/shared/application/Result';
|
|||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||||
import type { Driver } from '../../domain/entities/Driver';
|
import type { Driver } from '../../domain/entities/Driver';
|
||||||
|
import type { Penalty } from '../../domain/entities/penalty/Penalty';
|
||||||
|
|
||||||
export type GetRacePenaltiesInput = {
|
export type GetRacePenaltiesInput = {
|
||||||
raceId: string;
|
raceId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetRacePenaltiesResult = {
|
export type GetRacePenaltiesResult = {
|
||||||
penalties: unknown[];
|
penalties: Penalty[];
|
||||||
drivers: Driver[];
|
drivers: Driver[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user