refactor api modules

This commit is contained in:
2025-12-22 19:17:33 +01:00
parent c90b2166c1
commit 1333f5e907
100 changed files with 2226 additions and 1936 deletions

View File

@@ -24,7 +24,7 @@ import { SponsorProfileDTO } from './dtos/SponsorProfileDTO';
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
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')
@Controller('sponsors')
@@ -39,8 +39,7 @@ export class SponsorController {
type: GetEntitySponsorshipPricingResultDTO,
})
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
const presenter = await this.sponsorService.getEntitySponsorshipPricing();
return presenter.viewModel;
return await this.sponsorService.getEntitySponsorshipPricing();
}
@Get()
@@ -51,8 +50,7 @@ export class SponsorController {
type: GetSponsorsOutputDTO,
})
async getSponsors(): Promise<GetSponsorsOutputDTO> {
const presenter = await this.sponsorService.getSponsors();
return presenter.viewModel;
return await this.sponsorService.getSponsors();
}
@Post()
@@ -64,8 +62,7 @@ export class SponsorController {
type: CreateSponsorOutputDTO,
})
async createSponsor(@Body() input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
const presenter = await this.sponsorService.createSponsor(input);
return presenter.viewModel;
return await this.sponsorService.createSponsor(input);
}
@Get('dashboard/:sponsorId')
@@ -78,11 +75,10 @@ export class SponsorController {
@ApiResponse({ status: 404, description: 'Sponsor not found' })
async getSponsorDashboard(
@Param('sponsorId') sponsorId: string,
): Promise<SponsorDashboardDTO | null> {
const presenter = await this.sponsorService.getSponsorDashboard({
): Promise<SponsorDashboardDTO> {
return await this.sponsorService.getSponsorDashboard({
sponsorId,
} as GetSponsorDashboardQueryParamsDTO);
return presenter.viewModel;
}
@Get(':sponsorId/sponsorships')
@@ -97,11 +93,10 @@ export class SponsorController {
@ApiResponse({ status: 404, description: 'Sponsor not found' })
async getSponsorSponsorships(
@Param('sponsorId') sponsorId: string,
): Promise<SponsorSponsorshipsDTO | null> {
const presenter = await this.sponsorService.getSponsorSponsorships({
): Promise<SponsorSponsorshipsDTO> {
return await this.sponsorService.getSponsorSponsorships({
sponsorId,
} as GetSponsorSponsorshipsQueryParamsDTO);
return presenter.viewModel;
}
@Get(':sponsorId')
@@ -112,9 +107,8 @@ export class SponsorController {
type: GetSponsorOutputDTO,
})
@ApiResponse({ status: 404, description: 'Sponsor not found' })
async getSponsor(@Param('sponsorId') sponsorId: string): Promise<GetSponsorOutputDTO | null> {
const presenter = await this.sponsorService.getSponsor(sponsorId);
return presenter.viewModel;
async getSponsor(@Param('sponsorId') sponsorId: string): Promise<GetSponsorOutputDTO> {
return await this.sponsorService.getSponsor(sponsorId);
}
@Get('requests')
@@ -126,14 +120,13 @@ export class SponsorController {
})
async getPendingSponsorshipRequests(
@Query() query: { entityType: string; entityId: string },
): Promise<GetPendingSponsorshipRequestsOutputDTO | null> {
const presenter = await this.sponsorService.getPendingSponsorshipRequests(
): Promise<GetPendingSponsorshipRequestsOutputDTO> {
return await this.sponsorService.getPendingSponsorshipRequests(
query as {
entityType: import('@core/racing/domain/entities/SponsorshipRequest').SponsorableEntityType;
entityId: string;
},
);
return presenter.viewModel;
}
@Post('requests/:requestId/accept')
@@ -146,11 +139,10 @@ export class SponsorController {
@Param('requestId') requestId: string,
@Body() input: AcceptSponsorshipRequestInputDTO,
): Promise<AcceptSponsorshipRequestResultViewModel | null> {
const presenter = await this.sponsorService.acceptSponsorshipRequest(
return await this.sponsorService.acceptSponsorshipRequest(
requestId,
input.respondedBy,
);
return presenter.viewModel;
}
@Post('requests/:requestId/reject')
@@ -162,13 +154,12 @@ export class SponsorController {
async rejectSponsorshipRequest(
@Param('requestId') requestId: string,
@Body() input: RejectSponsorshipRequestInputDTO,
): Promise<RejectSponsorshipRequestResultDTO | null> {
const presenter = await this.sponsorService.rejectSponsorshipRequest(
): Promise<RejectSponsorshipRequestResult | null> {
return await this.sponsorService.rejectSponsorshipRequest(
requestId,
input.respondedBy,
input.reason,
);
return presenter.viewModel;
}
@Get('billing/:sponsorId')
@@ -181,8 +172,7 @@ export class SponsorController {
invoices: InvoiceDTO[];
stats: BillingStatsDTO;
}> {
const presenter = await this.sponsorService.getSponsorBilling(sponsorId);
return presenter.viewModel;
return await this.sponsorService.getSponsorBilling(sponsorId);
}
@Get('leagues/available')

View File

@@ -5,7 +5,8 @@ import { SponsorService } from './SponsorService';
import { NotificationService } from '@core/notifications/application/ports/NotificationService';
import type { IPaymentRepository } from '@core/payments/domain/repositories/IPaymentRepository';
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 { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
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 { ISponsorshipPricingRepository } from '@core/racing/domain/repositories/ISponsorshipPricingRepository';
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 { 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 { 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
export const SPONSOR_REPOSITORY_TOKEN = 'ISponsorRepository';
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 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
export const GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN = 'GetSponsorshipPricingUseCase';
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 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[] = [
SponsorService,
// Repositories
@@ -111,27 +149,94 @@ export const SponsorProviders: Provider[] = [
provide: LOGGER_TOKEN,
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
{
provide: GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
useFactory: () => new GetSponsorshipPricingUseCase(),
inject: [],
useFactory: (output: UseCaseOutputPort<any>) => new GetSponsorshipPricingUseCase(output),
inject: [GET_SPONSORSHIP_PRICING_OUTPUT_PORT_TOKEN],
},
{
provide: GET_SPONSORS_USE_CASE_TOKEN,
useFactory: (sponsorRepo: ISponsorRepository) => new GetSponsorsUseCase(sponsorRepo),
inject: [SPONSOR_REPOSITORY_TOKEN],
useFactory: (sponsorRepo: ISponsorRepository, output: UseCaseOutputPort<any>) => new GetSponsorsUseCase(sponsorRepo, output),
inject: [SPONSOR_REPOSITORY_TOKEN, GET_SPONSORS_OUTPUT_PORT_TOKEN],
},
{
provide: CREATE_SPONSOR_USE_CASE_TOKEN,
useFactory: (sponsorRepo: ISponsorRepository) => new CreateSponsorUseCase(sponsorRepo),
inject: [SPONSOR_REPOSITORY_TOKEN],
useFactory: (sponsorRepo: ISponsorRepository, logger: Logger, output: UseCaseOutputPort<any>) => new CreateSponsorUseCase(sponsorRepo, logger, output),
inject: [SPONSOR_REPOSITORY_TOKEN, LOGGER_TOKEN, CREATE_SPONSOR_OUTPUT_PORT_TOKEN],
},
{
provide: GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN,
useFactory: (sponsorRepo: ISponsorRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonRepo: ISeasonRepository, leagueRepo: ILeagueRepository, leagueMembershipRepo: ILeagueMembershipRepository, raceRepo: IRaceRepository) =>
new GetSponsorDashboardUseCase(sponsorRepo, seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo),
inject: [SPONSOR_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, SEASON_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN],
useFactory: (
sponsorRepo: ISponsorRepository,
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,
@@ -142,7 +247,8 @@ export const SponsorProviders: Provider[] = [
leagueRepo: ILeagueRepository,
leagueMembershipRepo: ILeagueMembershipRepository,
raceRepo: IRaceRepository,
) => new GetSponsorSponsorshipsUseCase(sponsorRepo, seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo),
output: UseCaseOutputPort<any>,
) => new GetSponsorSponsorshipsUseCase(sponsorRepo, seasonSponsorshipRepo, seasonRepo, leagueRepo, leagueMembershipRepo, raceRepo, output),
inject: [
SPONSOR_REPOSITORY_TOKEN,
SEASON_SPONSORSHIP_REPOSITORY_TOKEN,
@@ -150,41 +256,97 @@ export const SponsorProviders: Provider[] = [
LEAGUE_REPOSITORY_TOKEN,
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
RACE_REPOSITORY_TOKEN,
GET_SPONSOR_SPONSORSHIPS_OUTPUT_PORT_TOKEN,
],
},
{
provide: GET_SPONSOR_BILLING_USE_CASE_TOKEN,
useFactory: (paymentRepo: IPaymentRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository) =>
new GetSponsorBillingUseCase(paymentRepo, seasonSponsorshipRepo),
useFactory: (
paymentRepo: IPaymentRepository,
seasonSponsorshipRepo: ISeasonSponsorshipRepository,
) => {
return new GetSponsorBillingUseCase(paymentRepo, seasonSponsorshipRepo);
},
inject: ['IPaymentRepository', SEASON_SPONSORSHIP_REPOSITORY_TOKEN],
},
{
provide: GET_ENTITY_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
useFactory: (sponsorshipPricingRepo: ISponsorshipPricingRepository, sponsorshipRequestRepo: ISponsorshipRequestRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, logger: Logger) =>
new GetEntitySponsorshipPricingUseCase(sponsorshipPricingRepo, sponsorshipRequestRepo, seasonSponsorshipRepo, logger),
inject: [SPONSORSHIP_PRICING_REPOSITORY_TOKEN, SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
useFactory: (
sponsorshipPricingRepo: ISponsorshipPricingRepository,
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,
useFactory: (sponsorRepo: ISponsorRepository) => new GetSponsorUseCase(sponsorRepo),
inject: [SPONSOR_REPOSITORY_TOKEN],
useFactory: (sponsorRepo: ISponsorRepository, output: UseCaseOutputPort<any>) => new GetSponsorUseCase(sponsorRepo, output),
inject: [SPONSOR_REPOSITORY_TOKEN, GET_SPONSOR_OUTPUT_PORT_TOKEN],
},
{
provide: GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN,
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, sponsorRepo: ISponsorRepository) =>
new GetPendingSponsorshipRequestsUseCase(sponsorshipRequestRepo, sponsorRepo),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SPONSOR_REPOSITORY_TOKEN],
useFactory: (
sponsorshipRequestRepo: ISponsorshipRequestRepository,
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,
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, seasonSponsorshipRepo: ISeasonSponsorshipRepository, seasonRepo: ISeasonRepository, notificationService: NotificationService, paymentGateway: IPaymentGateway, walletRepository: IWalletRepository, leagueWalletRepository: ILeagueWalletRepository, logger: Logger) =>
new AcceptSponsorshipRequestUseCase(sponsorshipRequestRepo, seasonSponsorshipRepo, seasonRepo, notificationService, paymentGateway, walletRepository, leagueWalletRepository, logger),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, SEASON_SPONSORSHIP_REPOSITORY_TOKEN, SEASON_REPOSITORY_TOKEN, 'INotificationService', 'IPaymentGateway', 'IWalletRepository', 'ILeagueWalletRepository', LOGGER_TOKEN],
useFactory: (
sponsorshipRequestRepo: ISponsorshipRequestRepository,
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,
useFactory: (sponsorshipRequestRepo: ISponsorshipRequestRepository, logger: Logger) =>
new RejectSponsorshipRequestUseCase(sponsorshipRequestRepo, logger),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, LOGGER_TOKEN],
useFactory: (
sponsorshipRequestRepo: ISponsorshipRequestRepository,
logger: Logger,
output: UseCaseOutputPort<any>,
) => new RejectSponsorshipRequestUseCase(sponsorshipRequestRepo, logger, output),
inject: [SPONSORSHIP_REQUEST_REPOSITORY_TOKEN, LOGGER_TOKEN, REJECT_SPONSORSHIP_REQUEST_OUTPUT_PORT_TOKEN],
},
];
];

View File

@@ -1,15 +1,27 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
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 { GetSponsorBillingUseCase } from '@core/payments/application/use-cases/GetSponsorBillingUseCase';
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 { 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';
describe('SponsorService', () => {
@@ -23,8 +35,21 @@ describe('SponsorService', () => {
let getPendingSponsorshipRequestsUseCase: { execute: Mock };
let acceptSponsorshipRequestUseCase: { execute: Mock };
let rejectSponsorshipRequestUseCase: { execute: Mock };
let getSponsorBillingUseCase: { execute: Mock };
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(() => {
getSponsorshipPricingUseCase = { execute: vi.fn() };
getSponsorsUseCase = { execute: vi.fn() };
@@ -35,6 +60,7 @@ describe('SponsorService', () => {
getPendingSponsorshipRequestsUseCase = { execute: vi.fn() };
acceptSponsorshipRequestUseCase = { execute: vi.fn() };
rejectSponsorshipRequestUseCase = { execute: vi.fn() };
getSponsorBillingUseCase = { execute: vi.fn() };
logger = {
debug: vi.fn(),
info: vi.fn(),
@@ -42,6 +68,18 @@ describe('SponsorService', () => {
error: vi.fn(),
} 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(
getSponsorshipPricingUseCase as unknown as GetSponsorshipPricingUseCase,
getSponsorsUseCase as unknown as GetSponsorsUseCase,
@@ -52,28 +90,39 @@ describe('SponsorService', () => {
getPendingSponsorshipRequestsUseCase as unknown as GetPendingSponsorshipRequestsUseCase,
acceptSponsorshipRequestUseCase as unknown as AcceptSponsorshipRequestUseCase,
rejectSponsorshipRequestUseCase as unknown as RejectSponsorshipRequestUseCase,
getSponsorBillingUseCase as unknown as GetSponsorBillingUseCase,
logger,
getEntitySponsorshipPricingPresenter,
getSponsorsPresenter,
createSponsorPresenter,
getSponsorDashboardPresenter,
getSponsorSponsorshipsPresenter,
getSponsorPresenter,
getPendingSponsorshipRequestsPresenter,
acceptSponsorshipRequestPresenter,
rejectSponsorshipRequestPresenter,
sponsorBillingPresenter,
);
});
describe('getEntitySponsorshipPricing', () => {
it('returns presenter with pricing data on success', async () => {
it('returns pricing data on success', async () => {
const outputPort = {
entityType: 'season',
entityId: 'season-1',
pricing: [
{ id: 'tier-gold', level: 'Gold', price: 500, currency: 'USD' },
tiers: [
{ name: 'Gold', price: 500, benefits: ['Main slot'] },
],
};
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',
entityId: 'season-1',
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 () => {
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',
entityId: '',
pricing: [],
@@ -92,82 +141,93 @@ describe('SponsorService', () => {
});
describe('getSponsors', () => {
it('returns sponsors in presenter on success', async () => {
const outputPort = { sponsors: [{ id: 's1', name: 'S1', contactEmail: 's1@test', createdAt: new Date() }] };
it('returns sponsors on success', async () => {
const sponsors = [{ id: 's1', name: 'S1', contactEmail: 's1@test', createdAt: new Date() }];
const outputPort = { sponsors };
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 () => {
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', () => {
it('returns created sponsor in presenter on success', async () => {
const input = { name: 'Test', contactEmail: 'test@example.com' };
const outputPort = {
sponsor: {
id: 's1',
name: 'Test',
contactEmail: 'test@example.com',
createdAt: new Date(),
},
it('returns created sponsor on success', async () => {
const input: CreateSponsorInputDTO = { name: 'Test', contactEmail: 'test@example.com' };
const sponsor = {
id: 's1',
name: 'Test',
contactEmail: 'test@example.com',
createdAt: new Date(),
};
const outputPort = { sponsor };
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 () => {
const input = { name: 'Test', contactEmail: 'test@example.com' };
const input: CreateSponsorInputDTO = { name: 'Test', contactEmail: 'test@example.com' };
createSponsorUseCase.execute.mockResolvedValue(
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', () => {
it('returns dashboard in presenter on success', async () => {
const params = { sponsorId: 's1' };
it('returns dashboard on success', async () => {
const params: GetSponsorDashboardInput = { sponsorId: 's1' };
const outputPort = {
sponsorId: 's1',
sponsorName: 'S1',
metrics: {} as any,
metrics: {
impressions: 0,
impressionsChange: 0,
uniqueViewers: 0,
viewersChange: 0,
races: 0,
drivers: 0,
exposure: 0,
exposureChange: 0,
},
sponsoredLeagues: [],
investment: {} as any,
investment: {
activeSponsorships: 0,
totalInvestment: 0,
costPerThousandViews: 0,
},
};
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 () => {
const params = { sponsorId: 's1' };
it('throws on error', async () => {
const params: GetSponsorDashboardInput = { sponsorId: 's1' };
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
const presenter = await service.getSponsorDashboard(params as any);
expect(presenter.viewModel).toBeNull();
await expect(service.getSponsorDashboard(params)).rejects.toThrow('Sponsor dashboard not found');
});
});
describe('getSponsorSponsorships', () => {
it('returns sponsorships in presenter on success', async () => {
const params = { sponsorId: 's1' };
it('returns sponsorships on success', async () => {
const params: GetSponsorSponsorshipsInput = { sponsorId: 's1' };
const outputPort = {
sponsorId: 's1',
sponsorName: 'S1',
@@ -182,46 +242,43 @@ describe('SponsorService', () => {
};
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 () => {
const params = { sponsorId: 's1' };
it('throws on error', async () => {
const params: GetSponsorSponsorshipsInput = { sponsorId: 's1' };
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(
Result.err({ code: 'REPOSITORY_ERROR' }),
);
const presenter = await service.getSponsorSponsorships(params as any);
expect(presenter.viewModel).toBeNull();
await expect(service.getSponsorSponsorships(params)).rejects.toThrow('Sponsor sponsorships not found');
});
});
describe('getSponsor', () => {
it('returns sponsor in presenter when found', async () => {
it('returns sponsor when found', async () => {
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));
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';
getSponsorUseCase.execute.mockResolvedValue(Result.ok(null));
const presenter = await service.getSponsor(sponsorId);
expect(presenter.viewModel).toBeNull();
await expect(service.getSponsor(sponsorId)).rejects.toThrow('Sponsor not found');
});
});
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 outputPort = {
entityType: 'season',
@@ -231,9 +288,9 @@ describe('SponsorService', () => {
};
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 () => {
@@ -242,9 +299,9 @@ describe('SponsorService', () => {
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',
entityId: 'season-1',
requests: [],
@@ -253,8 +310,8 @@ describe('SponsorService', () => {
});
});
describe('acceptSponsorshipRequest', () => {
it('returns accept result in presenter on success', async () => {
describe('SponsorshipRequest', () => {
it('returns accept result on success', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
const outputPort = {
@@ -267,100 +324,114 @@ describe('SponsorService', () => {
};
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 respondedBy = 'u1';
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
);
const presenter = await service.acceptSponsorshipRequest(requestId, respondedBy);
expect(presenter.viewModel).toBeNull();
await expect(service.acceptSponsorshipRequest(requestId, respondedBy)).rejects.toThrow('Accept sponsorship request failed');
});
});
describe('rejectSponsorshipRequest', () => {
it('returns reject result in presenter on success', async () => {
it('returns reject result on success', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
const reason = 'Not interested';
const output = {
requestId,
status: 'rejected' as const,
rejectedAt: new Date(),
reason,
respondedAt: new Date(),
rejectionReason: reason,
};
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 respondedBy = 'u1';
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
);
const presenter = await service.rejectSponsorshipRequest(requestId, respondedBy);
expect(presenter.viewModel).toBeNull();
await expect(service.rejectSponsorshipRequest(requestId, respondedBy)).rejects.toThrow('Reject sponsorship request failed');
});
});
describe('getSponsorBilling', () => {
it('returns mock billing data in presenter', async () => {
const presenter = await service.getSponsorBilling('s1');
it('returns billing data', async () => {
// 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();
expect(presenter.viewModel?.paymentMethods).toBeInstanceOf(Array);
expect(presenter.viewModel?.invoices).toBeInstanceOf(Array);
expect(presenter.viewModel?.stats).toBeDefined();
const result = await service.getSponsorBilling('s1');
expect(result).not.toBeNull();
expect(result.paymentMethods).toBeInstanceOf(Array);
expect(result.invoices).toBeInstanceOf(Array);
expect(result.stats).toBeDefined();
});
});
describe('getAvailableLeagues', () => {
it('returns mock leagues in presenter', async () => {
const presenter = await service.getAvailableLeagues();
it('returns mock leagues', async () => {
const result = await service.getAvailableLeagues();
expect(presenter.viewModel).not.toBeNull();
expect(presenter.viewModel?.length).toBeGreaterThan(0);
expect(result).not.toBeNull();
expect(result.viewModel).not.toBeNull();
expect(result.viewModel?.length).toBeGreaterThan(0);
});
});
describe('getLeagueDetail', () => {
it('returns league detail in presenter', async () => {
const presenter = await service.getLeagueDetail('league-1');
it('returns league detail', async () => {
const result = await service.getLeagueDetail('league-1');
expect(presenter.viewModel).not.toBeNull();
expect(presenter.viewModel?.league.id).toBe('league-1');
expect(result).not.toBeNull();
expect(result.viewModel?.league.id).toBe('league-1');
});
});
describe('getSponsorSettings', () => {
it('returns settings in presenter', async () => {
const presenter = await service.getSponsorSettings('s1');
it('returns settings', async () => {
const result = await service.getSponsorSettings('s1');
expect(presenter.viewModel).not.toBeNull();
expect(presenter.viewModel?.profile).toBeDefined();
expect(presenter.viewModel?.notifications).toBeDefined();
expect(presenter.viewModel?.privacy).toBeDefined();
expect(result).not.toBeNull();
expect(result.viewModel?.profile).toBeDefined();
expect(result.viewModel?.notifications).toBeDefined();
expect(result.viewModel?.privacy).toBeDefined();
});
});
describe('updateSponsorSettings', () => {
it('returns success result in presenter', async () => {
const presenter = await service.updateSponsorSettings('s1', {});
it('returns success result', async () => {
const result = await service.updateSponsorSettings('s1', {});
expect(presenter.viewModel).toEqual({ success: true });
expect(result.viewModel).toEqual({ success: true });
});
});
});
});

View File

@@ -1,12 +1,14 @@
import { Injectable, Inject } from '@nestjs/common';
import { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
import { CreateSponsorOutputDTO } from './dtos/CreateSponsorOutputDTO';
import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQueryParamsDTO';
import { SponsorDashboardDTO } from './dtos/SponsorDashboardDTO';
import { GetSponsorSponsorshipsQueryParamsDTO } from './dtos/GetSponsorSponsorshipsQueryParamsDTO';
import { AcceptSponsorshipRequestInputDTO } from './dtos/AcceptSponsorshipRequestInputDTO';
import { RejectSponsorshipRequestInputDTO } from './dtos/RejectSponsorshipRequestInputDTO';
import { PaymentMethodDTO } from './dtos/PaymentMethodDTO';
import { InvoiceDTO } from './dtos/InvoiceDTO';
import { BillingStatsDTO } from './dtos/BillingStatsDTO';
import { SponsorSponsorshipsDTO } from './dtos/SponsorSponsorshipsDTO';
import { GetSponsorOutputDTO } from './dtos/GetSponsorOutputDTO';
import { GetPendingSponsorshipRequestsOutputDTO } from './dtos/GetPendingSponsorshipRequestsOutputDTO';
import { GetEntitySponsorshipPricingResultDTO } from './dtos/GetEntitySponsorshipPricingResultDTO';
import { GetSponsorsOutputDTO } from './dtos/GetSponsorsOutputDTO';
import { AvailableLeagueDTO } from './dtos/AvailableLeagueDTO';
import { LeagueDetailDTO } from './dtos/LeagueDetailDTO';
import { DriverDTO } from './dtos/DriverDTO';
@@ -14,6 +16,9 @@ import { RaceDTO } from './dtos/RaceDTO';
import { SponsorProfileDTO } from './dtos/SponsorProfileDTO';
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
import { PaymentMethodDTO } from './dtos/PaymentMethodDTO';
import { InvoiceDTO } from './dtos/InvoiceDTO';
import { BillingStatsDTO } from './dtos/BillingStatsDTO';
// Use cases
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 {
GetPendingSponsorshipRequestsUseCase,
GetPendingSponsorshipRequestsDTO,
GetPendingSponsorshipRequestsInput,
} from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
@@ -48,6 +53,8 @@ import { AvailableLeaguesPresenter } from './presenters/AvailableLeaguesPresente
import { LeagueDetailPresenter } from './presenters/LeagueDetailPresenter';
import { SponsorSettingsPresenter } from './presenters/SponsorSettingsPresenter';
import { SponsorSettingsUpdatePresenter } from './presenters/SponsorSettingsUpdatePresenter';
import { AcceptSponsorshipRequestResultViewModel } from './presenters/AcceptSponsorshipRequestPresenter';
import type { RejectSponsorshipRequestResult } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
// Tokens
import {
@@ -61,6 +68,16 @@ import {
ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
REJECT_SPONSORSHIP_REQUEST_USE_CASE_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';
@Injectable()
@@ -88,194 +105,152 @@ export class SponsorService {
private readonly getSponsorBillingUseCase: GetSponsorBillingUseCase,
@Inject(LOGGER_TOKEN)
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.');
const presenter = new GetEntitySponsorshipPricingPresenter();
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;
await this.getSponsorshipPricingUseCase.execute({});
return this.getEntitySponsorshipPricingPresenter.viewModel;
}
async getSponsors(): Promise<GetSponsorsOutputDTO> {
this.logger.debug('[SponsorService] Fetching sponsors.');
const presenter = new GetSponsorsPresenter();
const result = await this.getSponsorsUseCase.execute();
presenter.present(result);
return presenter.responseModel;
await this.getSponsorsUseCase.execute();
return this.getSponsorsPresenter.responseModel;
}
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorPresenter> {
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
this.logger.debug('[SponsorService] Creating sponsor.', { input });
const presenter = new CreateSponsorPresenter();
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;
await this.createSponsorUseCase.execute(input);
return this.createSponsorPresenter.viewModel;
}
async getSponsorDashboard(
params: GetSponsorDashboardQueryParamsDTO,
): Promise<GetSponsorDashboardPresenter> {
): Promise<SponsorDashboardDTO> {
this.logger.debug('[SponsorService] Fetching sponsor dashboard.', { params });
const presenter = new GetSponsorDashboardPresenter();
const result = await this.getSponsorDashboardUseCase.execute(params);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor dashboard.', result.error);
presenter.present(null);
return presenter;
await this.getSponsorDashboardUseCase.execute(params);
const result = this.getSponsorDashboardPresenter.viewModel;
if (!result) {
throw new Error('Sponsor dashboard not found');
}
presenter.present(result.value);
return presenter;
return result;
}
async getSponsorSponsorships(
params: GetSponsorSponsorshipsQueryParamsDTO,
): Promise<GetSponsorSponsorshipsPresenter> {
): Promise<SponsorSponsorshipsDTO> {
this.logger.debug('[SponsorService] Fetching sponsor sponsorships.', { params });
const presenter = new GetSponsorSponsorshipsPresenter();
const result = await this.getSponsorSponsorshipsUseCase.execute(params);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor sponsorships.', result.error);
presenter.present(null);
return presenter;
await this.getSponsorSponsorshipsUseCase.execute(params);
const result = this.getSponsorSponsorshipsPresenter.viewModel;
if (!result) {
throw new Error('Sponsor sponsorships not found');
}
presenter.present(result.value);
return presenter;
return result;
}
async getSponsor(sponsorId: string): Promise<GetSponsorPresenter> {
async getSponsor(sponsorId: string): Promise<GetSponsorOutputDTO> {
this.logger.debug('[SponsorService] Fetching sponsor.', { sponsorId });
const presenter = new GetSponsorPresenter();
const result = await this.getSponsorUseCase.execute({ sponsorId });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor.', result.error);
presenter.present(null);
return presenter;
await this.getSponsorUseCase.execute({ sponsorId });
const result = this.getSponsorPresenter.viewModel;
if (!result) {
throw new Error('Sponsor not found');
}
presenter.present(result.value);
return presenter;
return result;
}
async getPendingSponsorshipRequests(params: {
entityType: SponsorableEntityType;
entityId: string;
}): Promise<GetPendingSponsorshipRequestsPresenter> {
}): Promise<GetPendingSponsorshipRequestsOutputDTO> {
this.logger.debug('[SponsorService] Fetching pending sponsorship requests.', { params });
const presenter = new GetPendingSponsorshipRequestsPresenter();
const result = await this.getPendingSponsorshipRequestsUseCase.execute(
params as GetPendingSponsorshipRequestsDTO,
await this.getPendingSponsorshipRequestsUseCase.execute(
params as GetPendingSponsorshipRequestsInput,
);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch pending sponsorship requests.', result.error);
presenter.present({
entityType: params.entityType,
entityId: params.entityId,
requests: [],
totalCount: 0,
});
return presenter;
const result = this.getPendingSponsorshipRequestsPresenter.viewModel;
if (!result) {
throw new Error('Pending sponsorship requests not found');
}
presenter.present(result.value);
return presenter;
return result;
}
async acceptSponsorshipRequest(
requestId: string,
respondedBy: string,
): Promise<AcceptSponsorshipRequestPresenter> {
): Promise<AcceptSponsorshipRequestResultViewModel> {
this.logger.debug('[SponsorService] Accepting sponsorship request.', {
requestId,
respondedBy,
});
const presenter = new AcceptSponsorshipRequestPresenter();
const result = await this.acceptSponsorshipRequestUseCase.execute({
await this.acceptSponsorshipRequestUseCase.execute({
requestId,
respondedBy,
} as AcceptSponsorshipRequestInputDTO);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to accept sponsorship request.', result.error);
presenter.present(null);
return presenter;
});
const result = this.acceptSponsorshipRequestPresenter.viewModel;
if (!result) {
throw new Error('Accept sponsorship request failed');
}
presenter.present(result.value);
return presenter;
return result;
}
async rejectSponsorshipRequest(
requestId: string,
respondedBy: string,
reason?: string,
): Promise<RejectSponsorshipRequestPresenter> {
): Promise<RejectSponsorshipRequestResult> {
this.logger.debug('[SponsorService] Rejecting sponsorship request.', {
requestId,
respondedBy,
reason,
});
const presenter = new RejectSponsorshipRequestPresenter();
const result = await this.rejectSponsorshipRequestUseCase.execute({
const input: { requestId: string; respondedBy: string; reason?: string } = {
requestId,
respondedBy,
reason,
} as RejectSponsorshipRequestInputDTO);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to reject sponsorship request.', result.error);
presenter.present(null);
return presenter;
};
if (reason !== undefined) {
input.reason = reason;
}
presenter.present(result.value);
return presenter;
await this.rejectSponsorshipRequestUseCase.execute(input);
const result = this.rejectSponsorshipRequestPresenter.viewModel;
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 });
const result = await this.getSponsorBillingUseCase.execute({ sponsorId });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor billing.', result.error);
throw new Error(result.error.details?.message || 'Failed to fetch sponsor billing');
await this.getSponsorBillingUseCase.execute({ sponsorId });
const result = this.sponsorBillingPresenter.viewModel;
if (!result) {
throw new Error('Sponsor billing not found');
}
const presenter = new GetSponsorBillingPresenter();
presenter.present(result.value);
return presenter;
return result;
}
async getAvailableLeagues(): Promise<AvailableLeaguesPresenter> {
@@ -426,7 +401,7 @@ export class SponsorService {
website: 'https://acme-racing.com',
description:
'Premium sim racing equipment and accessories for competitive drivers.',
logoUrl: null,
logoUrl: '',
industry: 'Racing Equipment',
address: {
street: '123 Racing Boulevard',
@@ -479,4 +454,4 @@ export class SponsorService {
presenter.present({ success: true });
return presenter;
}
}
}

View File

@@ -4,19 +4,19 @@ import { IsString, IsEnum, IsOptional, IsNumber } from 'class-validator';
export class ActivityItemDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty({ enum: ['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()
@IsString()
message: string;
message: string = '';
@ApiProperty()
@IsString()
time: string;
time: string = '';
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -1,47 +1,47 @@
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 {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
name: string;
name: string = '';
@ApiProperty()
@IsString()
game: string;
game: string = '';
@ApiProperty()
@IsNumber()
drivers: number;
drivers: number = 0;
@ApiProperty()
@IsNumber()
avgViewsPerRace: number;
avgViewsPerRace: number = 0;
@ApiProperty({ type: Object })
mainSponsorSlot: {
available: boolean;
price: number;
};
} = { available: false, price: 0 };
@ApiProperty({ type: Object })
secondarySlots: {
available: number;
total: number;
price: number;
};
} = { available: 0, total: 0, price: 0 };
@ApiProperty()
@IsNumber()
rating: number;
rating: number = 0;
@ApiProperty({ enum: ['premium', 'standard', 'starter'] })
@IsEnum(['premium', 'standard', 'starter'])
tier: 'premium' | 'standard' | 'starter';
tier: 'premium' | 'standard' | 'starter' = 'standard';
@ApiProperty({ required: false })
@IsOptional()
@@ -50,9 +50,9 @@ export class AvailableLeagueDTO {
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
@IsEnum(['active', 'upcoming', 'completed'])
seasonStatus: 'active' | 'upcoming' | 'completed';
seasonStatus: 'active' | 'upcoming' | 'completed' = 'active';
@ApiProperty()
@IsString()
description: string;
description: string = '';
}

View File

@@ -4,25 +4,25 @@ import { IsNumber, IsDateString } from 'class-validator';
export class BillingStatsDTO {
@ApiProperty()
@IsNumber()
totalSpent: number;
totalSpent: number = 0;
@ApiProperty()
@IsNumber()
pendingAmount: number;
pendingAmount: number = 0;
@ApiProperty()
@IsDateString()
nextPaymentDate: string;
nextPaymentDate: string = '';
@ApiProperty()
@IsNumber()
nextPaymentAmount: number;
nextPaymentAmount: number = 0;
@ApiProperty()
@IsNumber()
activeSponsorships: number;
activeSponsorships: number = 0;
@ApiProperty()
@IsNumber()
averageMonthlySpend: number;
averageMonthlySpend: number = 0;
}

View File

@@ -3,5 +3,5 @@ import { SponsorDTO } from './SponsorDTO';
export class CreateSponsorOutputDTO {
@ApiProperty({ type: SponsorDTO })
sponsor: SponsorDTO;
sponsor: SponsorDTO = new SponsorDTO();
}

View File

@@ -4,29 +4,29 @@ import { IsString, IsNumber } from 'class-validator';
export class DriverDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
name: string;
name: string = '';
@ApiProperty()
@IsString()
country: string;
country: string = '';
@ApiProperty()
@IsNumber()
position: number;
position: number = 0;
@ApiProperty()
@IsNumber()
races: number;
races: number = 0;
@ApiProperty()
@IsNumber()
impressions: number;
impressions: number = 0;
@ApiProperty()
@IsString()
team: string;
team: string = '';
}

View File

@@ -3,11 +3,11 @@ import { SponsorshipPricingItemDTO } from './SponsorshipPricingItemDTO';
export class GetEntitySponsorshipPricingResultDTO {
@ApiProperty()
entityType: string;
entityType: string = '';
@ApiProperty()
entityId: string;
entityId: string = '';
@ApiProperty({ type: [SponsorshipPricingItemDTO] })
pricing: SponsorshipPricingItemDTO[];
pricing: SponsorshipPricingItemDTO[] = [];
}

View File

@@ -3,14 +3,14 @@ import { SponsorshipRequestDTO } from './SponsorshipRequestDTO';
export class GetPendingSponsorshipRequestsOutputDTO {
@ApiProperty()
entityType: string;
entityType: string = '';
@ApiProperty()
entityId: string;
entityId: string = '';
@ApiProperty({ type: [SponsorshipRequestDTO] })
requests: SponsorshipRequestDTO[];
requests: SponsorshipRequestDTO[] = [];
@ApiProperty()
totalCount: number;
totalCount: number = 0;
}

View File

@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
export class GetSponsorDashboardQueryParamsDTO {
@ApiProperty()
@IsString()
sponsorId: string;
sponsorId: string = '';
}

View File

@@ -3,5 +3,5 @@ import { SponsorDTO } from './SponsorDTO';
export class GetSponsorOutputDTO {
@ApiProperty({ type: SponsorDTO })
sponsor: SponsorDTO;
sponsor: SponsorDTO = new SponsorDTO();
}

View File

@@ -4,5 +4,5 @@ import { IsString } from 'class-validator';
export class GetSponsorSponsorshipsQueryParamsDTO {
@ApiProperty()
@IsString()
sponsorId: string;
sponsorId: string = '';
}

View File

@@ -3,5 +3,5 @@ import { SponsorDTO } from './SponsorDTO';
export class GetSponsorsOutputDTO {
@ApiProperty({ type: [SponsorDTO] })
sponsors: SponsorDTO[];
sponsors: SponsorDTO[] = [];
}

View File

@@ -4,45 +4,45 @@ import { IsString, IsEnum, IsNumber, IsDateString } from 'class-validator';
export class InvoiceDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
invoiceNumber: string;
invoiceNumber: string = '';
@ApiProperty()
@IsDateString()
date: string;
date: string = '';
@ApiProperty()
@IsDateString()
dueDate: string;
dueDate: string = '';
@ApiProperty()
@IsNumber()
amount: number;
amount: number = 0;
@ApiProperty()
@IsNumber()
vatAmount: number;
vatAmount: number = 0;
@ApiProperty()
@IsNumber()
totalAmount: number;
totalAmount: number = 0;
@ApiProperty({ enum: ['paid', 'pending', 'overdue', 'failed'] })
@IsEnum(['paid', 'pending', 'overdue', 'failed'])
status: 'paid' | 'pending' | 'overdue' | 'failed';
status: 'paid' | 'pending' | 'overdue' | 'failed' = 'pending';
@ApiProperty()
@IsString()
description: string;
description: string = '';
@ApiProperty({ enum: ['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()
@IsString()
pdfUrl: string;
pdfUrl: string = '';
}

View File

@@ -1,68 +1,68 @@
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 {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
name: string;
name: string = '';
@ApiProperty()
@IsString()
game: string;
game: string = '';
@ApiProperty({ enum: ['premium', 'standard', 'starter'] })
@IsEnum(['premium', 'standard', 'starter'])
tier: 'premium' | 'standard' | 'starter';
tier: 'premium' | 'standard' | 'starter' = 'standard';
@ApiProperty()
@IsString()
season: string;
season: string = '';
@ApiProperty()
@IsString()
description: string;
description: string = '';
@ApiProperty()
@IsNumber()
drivers: number;
drivers: number = 0;
@ApiProperty()
@IsNumber()
races: number;
races: number = 0;
@ApiProperty()
@IsNumber()
completedRaces: number;
completedRaces: number = 0;
@ApiProperty()
@IsNumber()
totalImpressions: number;
totalImpressions: number = 0;
@ApiProperty()
@IsNumber()
avgViewsPerRace: number;
avgViewsPerRace: number = 0;
@ApiProperty()
@IsNumber()
engagement: number;
engagement: number = 0;
@ApiProperty()
@IsNumber()
rating: number;
rating: number = 0;
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
@IsEnum(['active', 'upcoming', 'completed'])
seasonStatus: 'active' | 'upcoming' | 'completed';
seasonStatus: 'active' | 'upcoming' | 'completed' = 'active';
@ApiProperty({ type: Object })
seasonDates: {
start: string;
end: string;
};
} = { start: '', end: '' };
@ApiProperty({ type: Object, required: false })
@IsOptional()
@@ -84,5 +84,8 @@ export class LeagueDetailDTO {
price: number;
benefits: string[];
};
} = {
main: { available: false, price: 0, benefits: [] },
secondary: { available: 0, total: 0, price: 0, benefits: [] }
};
}

View File

@@ -4,25 +4,25 @@ import { IsBoolean } from 'class-validator';
export class NotificationSettingsDTO {
@ApiProperty()
@IsBoolean()
emailNewSponsorships: boolean;
emailNewSponsorships: boolean = false;
@ApiProperty()
@IsBoolean()
emailWeeklyReport: boolean;
emailWeeklyReport: boolean = false;
@ApiProperty()
@IsBoolean()
emailRaceAlerts: boolean;
emailRaceAlerts: boolean = false;
@ApiProperty()
@IsBoolean()
emailPaymentAlerts: boolean;
emailPaymentAlerts: boolean = false;
@ApiProperty()
@IsBoolean()
emailNewOpportunities: boolean;
emailNewOpportunities: boolean = false;
@ApiProperty()
@IsBoolean()
emailContractExpiry: boolean;
emailContractExpiry: boolean = false;
}

View File

@@ -4,15 +4,15 @@ import { IsString, IsEnum, IsBoolean, IsOptional, IsNumber } from 'class-validat
export class PaymentMethodDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty({ enum: ['card', 'bank', 'sepa'] })
@IsEnum(['card', 'bank', 'sepa'])
type: 'card' | 'bank' | 'sepa';
type: 'card' | 'bank' | 'sepa' = 'card';
@ApiProperty()
@IsString()
last4: string;
last4: string = '';
@ApiProperty({ required: false })
@IsOptional()
@@ -21,7 +21,7 @@ export class PaymentMethodDTO {
@ApiProperty()
@IsBoolean()
isDefault: boolean;
isDefault: boolean = false;
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -4,17 +4,17 @@ import { IsBoolean } from 'class-validator';
export class PrivacySettingsDTO {
@ApiProperty()
@IsBoolean()
publicProfile: boolean;
publicProfile: boolean = false;
@ApiProperty()
@IsBoolean()
showStats: boolean;
showStats: boolean = false;
@ApiProperty()
@IsBoolean()
showActiveSponsorships: boolean;
showActiveSponsorships: boolean = false;
@ApiProperty()
@IsBoolean()
allowDirectContact: boolean;
allowDirectContact: boolean = false;
}

View File

@@ -4,21 +4,21 @@ import { IsString, IsEnum, IsNumber, IsDateString } from 'class-validator';
export class RaceDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
name: string;
name: string = '';
@ApiProperty()
@IsDateString()
date: string;
date: string = '';
@ApiProperty()
@IsNumber()
views: number;
views: number = 0;
@ApiProperty({ enum: ['upcoming', 'completed'] })
@IsEnum(['upcoming', 'completed'])
status: 'upcoming' | 'completed';
status: 'upcoming' | 'completed' = 'upcoming';
}

View File

@@ -5,7 +5,7 @@ export class RejectSponsorshipRequestInputDTO {
@ApiProperty()
@IsString()
@IsNotEmpty()
respondedBy: string;
respondedBy: string = '';
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -4,21 +4,21 @@ import { IsString, IsEnum, IsNumber, IsDateString } from 'class-validator';
export class RenewalAlertDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
name: string;
name: string = '';
@ApiProperty({ enum: ['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()
@IsDateString()
renewDate: string;
renewDate: string = '';
@ApiProperty()
@IsNumber()
price: number;
price: number = 0;
}

View File

@@ -2,10 +2,10 @@ import { ApiProperty } from '@nestjs/swagger';
export class SponsorDTO {
@ApiProperty()
id: string;
id: string = '';
@ApiProperty()
name: string;
name: string = '';
@ApiProperty({ required: false })
contactEmail?: string;

View File

@@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsArray, IsObject } from 'class-validator';
import { IsString } from 'class-validator';
import { SponsorDashboardMetricsDTO } from './SponsorDashboardMetricsDTO';
import { SponsoredLeagueDTO } from './SponsoredLeagueDTO';
import { SponsorDashboardInvestmentDTO } from './SponsorDashboardInvestmentDTO';
@@ -10,20 +10,20 @@ import { RenewalAlertDTO } from './RenewalAlertDTO';
export class SponsorDashboardDTO {
@ApiProperty()
@IsString()
sponsorId: string;
sponsorId: string = '';
@ApiProperty()
@IsString()
sponsorName: string;
sponsorName: string = '';
@ApiProperty({ type: SponsorDashboardMetricsDTO })
metrics: SponsorDashboardMetricsDTO;
metrics: SponsorDashboardMetricsDTO = new SponsorDashboardMetricsDTO();
@ApiProperty({ type: [SponsoredLeagueDTO] })
sponsoredLeagues: SponsoredLeagueDTO[];
sponsoredLeagues: SponsoredLeagueDTO[] = [];
@ApiProperty({ type: SponsorDashboardInvestmentDTO })
investment: SponsorDashboardInvestmentDTO;
investment: SponsorDashboardInvestmentDTO = new SponsorDashboardInvestmentDTO();
@ApiProperty({ type: Object })
sponsorships: {
@@ -32,11 +32,17 @@ export class SponsorDashboardDTO {
drivers: SponsorshipDTO[];
races: SponsorshipDTO[];
platform: SponsorshipDTO[];
} = {
leagues: [],
teams: [],
drivers: [],
races: [],
platform: []
};
@ApiProperty({ type: [ActivityItemDTO] })
recentActivity: ActivityItemDTO[];
recentActivity: ActivityItemDTO[] = [];
@ApiProperty({ type: [RenewalAlertDTO] })
upcomingRenewals: RenewalAlertDTO[];
upcomingRenewals: RenewalAlertDTO[] = [];
}

View File

@@ -4,13 +4,13 @@ import { IsNumber } from 'class-validator';
export class SponsorDashboardInvestmentDTO {
@ApiProperty()
@IsNumber()
activeSponsorships: number;
activeSponsorships: number = 0;
@ApiProperty()
@IsNumber()
totalInvestment: number;
totalInvestment: number = 0;
@ApiProperty()
@IsNumber()
costPerThousandViews: number;
costPerThousandViews: number = 0;
}

View File

@@ -4,33 +4,33 @@ import { IsNumber } from 'class-validator';
export class SponsorDashboardMetricsDTO {
@ApiProperty()
@IsNumber()
impressions: number;
impressions: number = 0;
@ApiProperty()
@IsNumber()
impressionsChange: number;
impressionsChange: number = 0;
@ApiProperty()
@IsNumber()
uniqueViewers: number;
uniqueViewers: number = 0;
@ApiProperty()
@IsNumber()
viewersChange: number;
viewersChange: number = 0;
@ApiProperty()
@IsNumber()
races: number;
races: number = 0;
@ApiProperty()
@IsNumber()
drivers: number;
drivers: number = 0;
@ApiProperty()
@IsNumber()
exposure: number;
exposure: number = 0;
@ApiProperty()
@IsNumber()
exposureChange: number;
exposureChange: number = 0;
}

View File

@@ -1,30 +1,30 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsOptional, IsObject } from 'class-validator';
import { IsString, IsOptional } from 'class-validator';
export class SponsorProfileDTO {
@ApiProperty()
@IsString()
companyName: string;
companyName: string = '';
@ApiProperty()
@IsString()
contactName: string;
contactName: string = '';
@ApiProperty()
@IsString()
contactEmail: string;
contactEmail: string = '';
@ApiProperty()
@IsString()
contactPhone: string;
contactPhone: string = '';
@ApiProperty()
@IsString()
website: string;
website: string = '';
@ApiProperty()
@IsString()
description: string;
description: string = '';
@ApiProperty({ required: false })
@IsOptional()
@@ -33,7 +33,7 @@ export class SponsorProfileDTO {
@ApiProperty()
@IsString()
industry: string;
industry: string = '';
@ApiProperty({ type: Object })
address: {
@@ -41,16 +41,16 @@ export class SponsorProfileDTO {
city: string;
country: string;
postalCode: string;
};
} = { street: '', city: '', country: '', postalCode: '' };
@ApiProperty()
@IsString()
taxId: string;
taxId: string = '';
@ApiProperty({ type: Object })
socialLinks: {
twitter: string;
linkedin: string;
instagram: string;
};
} = { twitter: '', linkedin: '', instagram: '' };
}

View File

@@ -5,14 +5,14 @@ import { SponsorshipDetailDTO } from './SponsorshipDetailDTO';
export class SponsorSponsorshipsDTO {
@ApiProperty()
@IsString()
sponsorId: string;
sponsorId: string = '';
@ApiProperty()
@IsString()
sponsorName: string;
sponsorName: string = '';
@ApiProperty({ type: [SponsorshipDetailDTO] })
sponsorships: SponsorshipDetailDTO[];
sponsorships: SponsorshipDetailDTO[] = [];
@ApiProperty()
summary: {
@@ -21,5 +21,11 @@ export class SponsorSponsorshipsDTO {
totalInvestment: number;
totalPlatformFees: number;
currency: string;
} = {
totalSponsorships: 0,
activeSponsorships: 0,
totalInvestment: 0,
totalPlatformFees: 0,
currency: ''
};
}

View File

@@ -4,29 +4,29 @@ import { IsString, IsEnum, IsNumber } from 'class-validator';
export class SponsoredLeagueDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
name: string;
name: string = '';
@ApiProperty({ enum: ['main', 'secondary'] })
@IsEnum(['main', 'secondary'])
tier: 'main' | 'secondary';
tier: 'main' | 'secondary' = 'main';
@ApiProperty()
@IsNumber()
drivers: number;
drivers: number = 0;
@ApiProperty()
@IsNumber()
races: number;
races: number = 0;
@ApiProperty()
@IsNumber()
impressions: number;
impressions: number = 0;
@ApiProperty({ enum: ['active', 'upcoming', 'completed'] })
@IsEnum(['active', 'upcoming', 'completed'])
status: 'active' | 'upcoming' | 'completed';
status: 'active' | 'upcoming' | 'completed' = 'active';
}

View File

@@ -4,19 +4,19 @@ import { IsString, IsEnum, IsNumber, IsOptional, IsDateString } from 'class-vali
export class SponsorshipDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty({ enum: ['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()
@IsString()
entityId: string;
entityId: string = '';
@ApiProperty()
@IsString()
entityName: string;
entityName: string = '';
@ApiProperty({ enum: ['main', 'secondary'], required: false })
@IsOptional()
@@ -25,7 +25,7 @@ export class SponsorshipDTO {
@ApiProperty({ enum: ['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 })
@IsOptional()
@@ -44,19 +44,19 @@ export class SponsorshipDTO {
@ApiProperty()
@IsDateString()
startDate: string;
startDate: string = '';
@ApiProperty()
@IsDateString()
endDate: string;
endDate: string = '';
@ApiProperty()
@IsNumber()
price: number;
price: number = 0;
@ApiProperty()
@IsNumber()
impressions: number;
impressions: number = 0;
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -4,23 +4,23 @@ import { IsString, IsEnum, IsOptional, IsDate } from 'class-validator';
export class SponsorshipDetailDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
leagueId: string;
leagueId: string = '';
@ApiProperty()
@IsString()
leagueName: string;
leagueName: string = '';
@ApiProperty()
@IsString()
seasonId: string;
seasonId: string = '';
@ApiProperty()
@IsString()
seasonName: string;
seasonName: string = '';
@ApiProperty({ required: false })
@IsOptional()
@@ -34,29 +34,29 @@ export class SponsorshipDetailDTO {
@ApiProperty({ enum: ['main', 'secondary'] })
@IsEnum(['main', 'secondary'])
tier: 'main' | 'secondary';
tier: 'main' | 'secondary' = 'main';
@ApiProperty({ enum: ['pending', 'active', 'expired', 'cancelled'] })
@IsEnum(['pending', 'active', 'expired', 'cancelled'])
status: 'pending' | 'active' | 'expired' | 'cancelled';
status: 'pending' | 'active' | 'expired' | 'cancelled' = 'pending';
@ApiProperty()
pricing: {
amount: number;
currency: string;
};
} = { amount: 0, currency: '' };
@ApiProperty()
platformFee: {
amount: number;
currency: string;
};
} = { amount: 0, currency: '' };
@ApiProperty()
netAmount: {
amount: number;
currency: string;
};
} = { amount: 0, currency: '' };
@ApiProperty()
metrics: {
@@ -64,10 +64,10 @@ export class SponsorshipDetailDTO {
races: number;
completedRaces: number;
impressions: number;
};
} = { drivers: 0, races: 0, completedRaces: 0, impressions: 0 };
@ApiProperty()
createdAt: Date;
createdAt: Date = new Date();
@ApiProperty({ required: false })
@IsOptional()

View File

@@ -4,17 +4,17 @@ import { IsString, IsNumber } from 'class-validator';
export class SponsorshipPricingItemDTO {
@ApiProperty()
@IsString()
id: string;
id: string = '';
@ApiProperty()
@IsString()
level: string;
level: string = '';
@ApiProperty()
@IsNumber()
price: number;
price: number = 0;
@ApiProperty()
@IsString()
currency: string;
currency: string = '';
}

View File

@@ -2,38 +2,38 @@ import { ApiProperty } from '@nestjs/swagger';
export class SponsorshipRequestDTO {
@ApiProperty()
id: string;
id: string = '';
@ApiProperty()
sponsorId: string;
sponsorId: string = '';
@ApiProperty()
sponsorName: string;
sponsorName: string = '';
@ApiProperty({ required: false })
sponsorLogo?: string;
@ApiProperty()
tier: string;
tier: string = '';
@ApiProperty()
offeredAmount: number;
offeredAmount: number = 0;
@ApiProperty()
currency: string;
currency: string = '';
@ApiProperty()
formattedAmount: string;
formattedAmount: string = '';
@ApiProperty({ required: false })
message?: string;
@ApiProperty()
createdAt: Date;
createdAt: Date = new Date();
@ApiProperty()
platformFee: number;
platformFee: number = 0;
@ApiProperty()
netAmount: number;
netAmount: number = 0;
}

View File

@@ -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 {
requestId: string;
@@ -16,7 +16,7 @@ export class AcceptSponsorshipRequestPresenter {
this.result = null;
}
present(output: AcceptSponsorshipOutputPort | null) {
present(output: AcceptSponsorshipResult | null) {
if (!output) {
this.result = null;
return;

View File

@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { CreateSponsorPresenter } from './CreateSponsorPresenter';
import { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
describe('CreateSponsorPresenter', () => {
let presenter: CreateSponsorPresenter;
@@ -10,9 +11,23 @@ describe('CreateSponsorPresenter', () => {
describe('reset', () => {
it('should reset the result to null', () => {
const mockPort = { sponsor: { id: 'sponsor-1', name: 'Test Sponsor', contactEmail: 'test@example.com', createdAt: new Date() } };
presenter.present(mockPort);
expect(presenter.viewModel).toEqual({ sponsor: mockPort.sponsor });
const mockSponsor = Sponsor.create({
id: 'sponsor-1',
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();
expect(() => presenter.viewModel).toThrow('Presenter not presented');
@@ -21,11 +36,24 @@ describe('CreateSponsorPresenter', () => {
describe('present', () => {
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', () => {
const mockPort = { sponsor: { id: 'sponsor-1', name: 'Test Sponsor', contactEmail: 'test@example.com', createdAt: new Date() } };
presenter.present(mockPort);
const mockSponsor = Sponsor.create({
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', () => {
const mockPort = { sponsor: { id: 'sponsor-1', name: 'Test Sponsor', contactEmail: 'test@example.com', createdAt: new Date() } };
presenter.present(mockPort);
const mockSponsor = Sponsor.create({
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);
});
});
});

View File

@@ -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';
export class CreateSponsorPresenter {
@@ -8,16 +8,24 @@ export class CreateSponsorPresenter {
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 = {
sponsor: {
id: port.sponsor.id,
name: port.sponsor.name,
contactEmail: port.sponsor.contactEmail,
logoUrl: port.sponsor.logoUrl,
websiteUrl: port.sponsor.websiteUrl,
createdAt: port.sponsor.createdAt,
},
sponsor: sponsorData,
};
}

View File

@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { GetEntitySponsorshipPricingPresenter } from './GetEntitySponsorshipPricingPresenter';
import type { GetEntitySponsorshipPricingResult } from '@core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
describe('GetEntitySponsorshipPricingPresenter', () => {
let presenter: GetEntitySponsorshipPricingPresenter;
@@ -10,9 +11,20 @@ describe('GetEntitySponsorshipPricingPresenter', () => {
describe('reset', () => {
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);
expect(presenter.viewModel).toEqual(mockResult);
const expectedViewModel = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
expect(presenter.viewModel).toEqual(expectedViewModel);
presenter.reset();
expect(() => presenter.viewModel).toThrow('Presenter not presented');
@@ -21,11 +33,21 @@ describe('GetEntitySponsorshipPricingPresenter', () => {
describe('present', () => {
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);
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', () => {
const mockResult = { entityType: 'season', entityId: 'season-1', pricing: [] };
const mockResult: GetEntitySponsorshipPricingResult = {
entityType: 'season',
entityId: 'season-1',
acceptingApplications: true,
tiers: []
};
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', () => {
const mockResult = { entityType: 'season', entityId: 'season-1', pricing: [] };
const mockResult: GetEntitySponsorshipPricingResult = {
entityType: 'season',
entityId: 'season-1',
acceptingApplications: true,
tiers: []
};
presenter.present(mockResult);
expect(presenter.viewModel).toEqual(mockResult);
const expectedViewModel = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
expect(presenter.viewModel).toEqual(expectedViewModel);
});
});
});

View File

@@ -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';
export class GetEntitySponsorshipPricingPresenter {
@@ -8,7 +8,7 @@ export class GetEntitySponsorshipPricingPresenter {
this.result = null;
}
present(output: GetSponsorshipPricingOutputPort | null) {
present(output: GetEntitySponsorshipPricingResult | null) {
if (!output) {
this.result = {
entityType: 'season',
@@ -21,11 +21,11 @@ export class GetEntitySponsorshipPricingPresenter {
this.result = {
entityType: output.entityType,
entityId: output.entityId,
pricing: output.pricing.map(item => ({
id: item.id,
level: item.level,
pricing: output.tiers.map(item => ({
id: item.name,
level: item.name,
price: item.price,
currency: item.currency,
currency: 'USD',
})),
};
}

View File

@@ -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';
export class GetPendingSponsorshipRequestsPresenter {
@@ -8,7 +8,7 @@ export class GetPendingSponsorshipRequestsPresenter {
this.result = null;
}
present(outputPort: PendingSponsorshipRequestsOutputPort | null) {
present(outputPort: GetPendingSponsorshipRequestsResult | null) {
if (!outputPort) {
this.result = null;
return;
@@ -17,7 +17,30 @@ export class GetPendingSponsorshipRequestsPresenter {
this.result = {
entityType: outputPort.entityType,
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,
};
}

View File

@@ -1,5 +1,7 @@
import { describe, it, expect, beforeEach } from 'vitest';
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', () => {
let presenter: GetSponsorDashboardPresenter;
@@ -10,9 +12,58 @@ describe('GetSponsorDashboardPresenter', () => {
describe('reset', () => {
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);
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();
expect(() => presenter.viewModel).toThrow('Presenter not presented');
@@ -21,11 +72,59 @@ describe('GetSponsorDashboardPresenter', () => {
describe('present', () => {
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);
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', () => {
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);
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', () => {
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);
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);
});
});
});

View File

@@ -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';
export class GetSponsorDashboardPresenter {
@@ -8,8 +8,40 @@ export class GetSponsorDashboardPresenter {
this.result = null;
}
present(outputPort: SponsorDashboardOutputPort | null) {
this.result = outputPort ?? null;
present(outputPort: GetSponsorDashboardResult | 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 {

View File

@@ -1,5 +1,8 @@
import { describe, it, expect, beforeEach } from 'vitest';
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', () => {
let presenter: GetSponsorSponsorshipsPresenter;
@@ -10,9 +13,38 @@ describe('GetSponsorSponsorshipsPresenter', () => {
describe('reset', () => {
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);
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();
expect(() => presenter.viewModel).toThrow('Presenter not presented');
@@ -21,11 +53,39 @@ describe('GetSponsorSponsorshipsPresenter', () => {
describe('present', () => {
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);
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', () => {
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);
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', () => {
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);
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);
});
});
});

View File

@@ -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';
export class GetSponsorSponsorshipsPresenter {
@@ -8,8 +8,58 @@ export class GetSponsorSponsorshipsPresenter {
this.result = null;
}
present(outputPort: SponsorSponsorshipsOutputPort | null) {
this.result = outputPort ?? null;
present(outputPort: GetSponsorSponsorshipsResult | 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 {

View File

@@ -1,11 +1,6 @@
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 { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
describe('GetSponsorsPresenter', () => {
let presenter: GetSponsorsPresenter;
@@ -16,8 +11,8 @@ describe('GetSponsorsPresenter', () => {
describe('reset', () => {
it('should reset the model to null and cause responseModel to throw', () => {
const result = Result.ok<GetSponsorsResult, never>({ sponsors: [] });
presenter.present(result);
const sponsors: Sponsor[] = [];
presenter.present(sponsors);
expect(presenter.responseModel).toEqual({ sponsors: [] });
presenter.reset();
@@ -26,29 +21,24 @@ describe('GetSponsorsPresenter', () => {
});
describe('present', () => {
it('should map Result.ok sponsors to DTO responseModel', () => {
const result = Result.ok<GetSponsorsResult, never>({
sponsors: [
{
id: 'sponsor-1',
name: 'Sponsor One',
contactEmail: 's1@example.com',
logoUrl: 'logo1.png',
websiteUrl: 'https://one.example.com',
createdAt: new Date('2024-01-01T00:00:00Z'),
},
{
id: 'sponsor-2',
name: 'Sponsor Two',
contactEmail: 's2@example.com',
logoUrl: undefined,
websiteUrl: undefined,
createdAt: undefined,
},
],
});
it('should map sponsors to DTO responseModel', () => {
const sponsors: Sponsor[] = [
Sponsor.create({
id: 'sponsor-1',
name: 'Sponsor One',
contactEmail: 's1@example.com',
logoUrl: 'logo1.png',
websiteUrl: 'https://one.example.com',
createdAt: new Date('2024-01-01T00:00:00Z'),
}),
Sponsor.create({
id: 'sponsor-2',
name: 'Sponsor Two',
contactEmail: 's2@example.com',
}),
];
presenter.present(result);
presenter.present(sponsors);
expect(presenter.responseModel).toEqual({
sponsors: [
@@ -64,9 +54,7 @@ describe('GetSponsorsPresenter', () => {
id: 'sponsor-2',
name: 'Sponsor Two',
contactEmail: 's2@example.com',
logoUrl: undefined,
websiteUrl: undefined,
createdAt: undefined,
createdAt: expect.any(Date),
},
],
});
@@ -79,8 +67,8 @@ describe('GetSponsorsPresenter', () => {
});
it('should return the model when presented', () => {
const result = Result.ok<GetSponsorsResult, never>({ sponsors: [] });
presenter.present(result);
const sponsors: Sponsor[] = [];
presenter.present(sponsors);
expect(presenter.getResponseModel()).toEqual({ sponsors: [] });
});
@@ -91,16 +79,26 @@ describe('GetSponsorsPresenter', () => {
expect(() => presenter.responseModel).toThrow('Presenter not presented');
});
it('should fallback to empty sponsors list on error', () => {
const error = {
code: 'REPOSITORY_ERROR' as GetSponsorsErrorCode,
details: { message: 'DB error' },
} satisfies ApplicationErrorCode<GetSponsorsErrorCode, { message: string }>;
const result = Result.err<GetSponsorsResult, typeof error>(error);
it('should return the model when presented', () => {
const sponsors: Sponsor[] = [
Sponsor.create({
id: 'sponsor-1',
name: 'Sponsor One',
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),
},
],
});
});
});
});

View File

@@ -1,9 +1,4 @@
import type { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type {
GetSponsorsResult,
GetSponsorsErrorCode,
} from '@core/racing/application/use-cases/GetSponsorsUseCase';
import type { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
import { GetSponsorsOutputDTO } from '../dtos/GetSponsorsOutputDTO';
import type { SponsorDTO } from '../dtos/SponsorDTO';
@@ -14,29 +9,26 @@ export class GetSponsorsPresenter {
this.model = null;
}
present(
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();
present(sponsors: Sponsor[]): void {
this.model = {
sponsors: output.sponsors.map<SponsorDTO>((sponsor) => ({
id: sponsor.id,
name: sponsor.name,
contactEmail: sponsor.contactEmail,
logoUrl: sponsor.logoUrl,
websiteUrl: sponsor.websiteUrl,
createdAt: sponsor.createdAt,
})),
sponsors: sponsors.map<SponsorDTO>((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();
}
return sponsorData;
}),
};
}

View File

@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { GetSponsorshipPricingPresenter } from './GetSponsorshipPricingPresenter';
import type { GetSponsorshipPricingResult } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
describe('GetSponsorshipPricingPresenter', () => {
let presenter: GetSponsorshipPricingPresenter;
@@ -10,9 +11,19 @@ describe('GetSponsorshipPricingPresenter', () => {
describe('reset', () => {
it('should reset the result to null', () => {
const mockResult = { tiers: [] };
const mockResult: GetSponsorshipPricingResult = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
presenter.present(mockResult);
expect(presenter.viewModel).toEqual(mockResult);
const expectedViewModel = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
expect(presenter.viewModel).toEqual(expectedViewModel);
presenter.reset();
expect(() => presenter.viewModel).toThrow('Presenter not presented');
@@ -21,11 +32,20 @@ describe('GetSponsorshipPricingPresenter', () => {
describe('present', () => {
it('should store the result', () => {
const mockResult = { tiers: [] };
const mockResult: GetSponsorshipPricingResult = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
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', () => {
const mockResult = { tiers: [] };
const mockResult: GetSponsorshipPricingResult = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
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', () => {
const mockResult = { tiers: [] };
const mockResult: GetSponsorshipPricingResult = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
presenter.present(mockResult);
expect(presenter.viewModel).toEqual(mockResult);
const expectedViewModel = {
entityType: 'season',
entityId: 'season-1',
pricing: []
};
expect(presenter.viewModel).toEqual(expectedViewModel);
});
});
});

View File

@@ -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';
export class GetSponsorshipPricingPresenter {
present(outputPort: GetSponsorshipPricingOutputPort): GetEntitySponsorshipPricingResultDTO {
return {
private result: GetEntitySponsorshipPricingResultDTO | null = null;
reset() {
this.result = null;
}
present(outputPort: GetSponsorshipPricingResult): void {
this.result = {
entityType: outputPort.entityType,
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;
}
}

View File

@@ -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 {
private result: RejectSponsorshipRequestResultDTO | null = null;
private result: RejectSponsorshipRequestResult | null = null;
reset() {
this.result = null;
}
present(output: RejectSponsorshipRequestResultDTO | null) {
present(output: RejectSponsorshipRequestResult | null) {
this.result = output ?? null;
}
getViewModel(): RejectSponsorshipRequestResultDTO | null {
getViewModel(): RejectSponsorshipRequestResult | null {
return this.result;
}
get viewModel(): RejectSponsorshipRequestResultDTO | null {
get viewModel(): RejectSponsorshipRequestResult | null {
return this.result;
}
}