refactor
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { vi, type Mock } from 'vitest';
|
||||
import { GetCurrentSessionUseCase } from './GetCurrentSessionUseCase';
|
||||
import { User } from '../../domain/entities/User';
|
||||
import { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
||||
|
||||
describe('GetCurrentSessionUseCase', () => {
|
||||
let useCase: GetCurrentSessionUseCase;
|
||||
let mockUserRepo: {
|
||||
findByEmail: Mock;
|
||||
findById: Mock;
|
||||
create: Mock;
|
||||
update: Mock;
|
||||
emailExists: Mock;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockUserRepo = {
|
||||
findByEmail: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
emailExists: vi.fn(),
|
||||
};
|
||||
useCase = new GetCurrentSessionUseCase(mockUserRepo as IUserRepository);
|
||||
});
|
||||
|
||||
it('should return User when user exists', async () => {
|
||||
const userId = 'user-123';
|
||||
const storedUser: StoredUser = {
|
||||
id: userId,
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
passwordHash: 'hash',
|
||||
salt: 'salt',
|
||||
primaryDriverId: 'driver-123',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
mockUserRepo.findById.mockResolvedValue(storedUser);
|
||||
|
||||
const result = await useCase.execute(userId);
|
||||
|
||||
expect(mockUserRepo.findById).toHaveBeenCalledWith(userId);
|
||||
expect(result).toBeInstanceOf(User);
|
||||
expect(result?.getId().value).toBe(userId);
|
||||
expect(result?.getDisplayName()).toBe('Test User');
|
||||
});
|
||||
|
||||
it('should return null when user does not exist', async () => {
|
||||
const userId = 'user-123';
|
||||
mockUserRepo.findById.mockResolvedValue(null);
|
||||
|
||||
const result = await useCase.execute(userId);
|
||||
|
||||
expect(mockUserRepo.findById).toHaveBeenCalledWith(userId);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { User } from '../../domain/entities/User';
|
||||
import { IUserRepository } from '../../domain/repositories/IUserRepository';
|
||||
// No direct import of apps/api DTOs in core module
|
||||
|
||||
/**
|
||||
@@ -7,9 +8,13 @@ import { User } from '../../domain/entities/User';
|
||||
* Retrieves the current user session information.
|
||||
*/
|
||||
export class GetCurrentSessionUseCase {
|
||||
constructor(private userRepo: IUserRepository) {}
|
||||
|
||||
async execute(userId: string): Promise<User | null> {
|
||||
// TODO: Implement actual logic to retrieve user and session data
|
||||
console.warn('GetCurrentSessionUseCase: Method not implemented.');
|
||||
return null;
|
||||
const stored = await this.userRepo.findById(userId);
|
||||
if (!stored) {
|
||||
return null;
|
||||
}
|
||||
return User.fromStored(stored);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { User } from '../../domain/entities/User';
|
||||
|
||||
export interface LoginWithIracingCallbackParams {
|
||||
code: string;
|
||||
state?: string;
|
||||
returnTo?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Application Use Case: LoginWithIracingCallbackUseCase
|
||||
*
|
||||
* Handles the callback after iRacing authentication.
|
||||
*/
|
||||
export class LoginWithIracingCallbackUseCase {
|
||||
async execute(params: LoginWithIracingCallbackParams): Promise<User> {
|
||||
// TODO: Implement actual logic for handling iRacing OAuth callback
|
||||
console.warn('LoginWithIracingCallbackUseCase: Method not implemented.');
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
export interface IracingAuthRedirectResult {
|
||||
redirectUrl: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Application Use Case: StartIracingAuthRedirectUseCase
|
||||
*
|
||||
* Initiates the iRacing authentication flow.
|
||||
*/
|
||||
export class StartIracingAuthRedirectUseCase {
|
||||
async execute(returnTo?: string): Promise<IracingAuthRedirectResult> {
|
||||
// TODO: Implement actual logic for initiating iRacing OAuth redirect
|
||||
console.warn('StartIracingAuthRedirectUseCase: Method not implemented.');
|
||||
return {
|
||||
redirectUrl: '/mock-iracing-redirect',
|
||||
state: 'mock-state',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Achievement, AchievementProps } from '@core/identity/domain/entities/Achievement';
|
||||
|
||||
export interface IAchievementRepository {
|
||||
save(achievement: Achievement): Promise<void>;
|
||||
findById(id: string): Promise<Achievement | null>;
|
||||
}
|
||||
|
||||
export class CreateAchievementUseCase {
|
||||
constructor(private readonly achievementRepository: IAchievementRepository) {}
|
||||
|
||||
async execute(props: Omit<AchievementProps, 'createdAt'>): Promise<Achievement> {
|
||||
const achievement = Achievement.create(props);
|
||||
await this.achievementRepository.save(achievement);
|
||||
return achievement;
|
||||
}
|
||||
}
|
||||
267
core/identity/domain/AchievementConstants.ts
Normal file
267
core/identity/domain/AchievementConstants.ts
Normal file
@@ -0,0 +1,267 @@
|
||||
import type { AchievementProps } from './entities/Achievement';
|
||||
|
||||
// Predefined achievements for drivers
|
||||
export const DRIVER_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'first-race',
|
||||
name: 'First Steps',
|
||||
description: 'Complete your first race',
|
||||
category: 'driver',
|
||||
rarity: 'common',
|
||||
points: 10,
|
||||
requirements: [{ type: 'races_completed', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-races',
|
||||
name: 'Getting Started',
|
||||
description: 'Complete 10 races',
|
||||
category: 'driver',
|
||||
rarity: 'common',
|
||||
points: 25,
|
||||
requirements: [{ type: 'races_completed', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'fifty-races',
|
||||
name: 'Regular Racer',
|
||||
description: 'Complete 50 races',
|
||||
category: 'driver',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'races_completed', value: 50, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'hundred-races',
|
||||
name: 'Veteran',
|
||||
description: 'Complete 100 races',
|
||||
category: 'driver',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'races_completed', value: 100, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'first-win',
|
||||
name: 'Victory Lane',
|
||||
description: 'Win your first race',
|
||||
category: 'driver',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'wins', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-wins',
|
||||
name: 'Serial Winner',
|
||||
description: 'Win 10 races',
|
||||
category: 'driver',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'wins', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'first-podium',
|
||||
name: 'Podium Finisher',
|
||||
description: 'Finish on the podium',
|
||||
category: 'driver',
|
||||
rarity: 'common',
|
||||
points: 25,
|
||||
requirements: [{ type: 'podiums', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'clean-streak-5',
|
||||
name: 'Clean Racer',
|
||||
description: 'Complete 5 consecutive races without incidents',
|
||||
category: 'driver',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'consecutive_clean', value: 5, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'clean-streak-10',
|
||||
name: 'Safety First',
|
||||
description: 'Complete 10 consecutive races without incidents',
|
||||
category: 'driver',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'consecutive_clean', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'championship-win',
|
||||
name: 'Champion',
|
||||
description: 'Win a championship',
|
||||
category: 'driver',
|
||||
rarity: 'epic',
|
||||
points: 200,
|
||||
requirements: [{ type: 'championships_won', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'triple-crown',
|
||||
name: 'Triple Crown',
|
||||
description: 'Win 3 championships',
|
||||
category: 'driver',
|
||||
rarity: 'legendary',
|
||||
points: 500,
|
||||
requirements: [{ type: 'championships_won', value: 3, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'elite-driver',
|
||||
name: 'Elite Driver',
|
||||
description: 'Reach Elite driver rating',
|
||||
category: 'driver',
|
||||
rarity: 'epic',
|
||||
points: 250,
|
||||
requirements: [{ type: 'rating_threshold', value: 90, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Predefined achievements for stewards
|
||||
export const STEWARD_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'first-protest',
|
||||
name: 'Justice Served',
|
||||
description: 'Handle your first protest',
|
||||
category: 'steward',
|
||||
rarity: 'common',
|
||||
points: 15,
|
||||
requirements: [{ type: 'protests_handled', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-protests',
|
||||
name: 'Fair Judge',
|
||||
description: 'Handle 10 protests',
|
||||
category: 'steward',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'protests_handled', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'fifty-protests',
|
||||
name: 'Senior Steward',
|
||||
description: 'Handle 50 protests',
|
||||
category: 'steward',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'protests_handled', value: 50, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'hundred-protests',
|
||||
name: 'Chief Steward',
|
||||
description: 'Handle 100 protests',
|
||||
category: 'steward',
|
||||
rarity: 'epic',
|
||||
points: 200,
|
||||
requirements: [{ type: 'protests_handled', value: 100, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'event-steward-10',
|
||||
name: 'Event Official',
|
||||
description: 'Steward 10 race events',
|
||||
category: 'steward',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'events_stewarded', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'trusted-steward',
|
||||
name: 'Trusted Steward',
|
||||
description: 'Achieve highly-trusted status',
|
||||
category: 'steward',
|
||||
rarity: 'rare',
|
||||
points: 150,
|
||||
requirements: [{ type: 'trust_threshold', value: 75, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Predefined achievements for admins
|
||||
export const ADMIN_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'first-league',
|
||||
name: 'League Founder',
|
||||
description: 'Create your first league',
|
||||
category: 'admin',
|
||||
rarity: 'common',
|
||||
points: 25,
|
||||
requirements: [{ type: 'leagues_managed', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'first-season',
|
||||
name: 'Season Organizer',
|
||||
description: 'Complete your first full season',
|
||||
category: 'admin',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'seasons_completed', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'five-seasons',
|
||||
name: 'Experienced Organizer',
|
||||
description: 'Complete 5 seasons',
|
||||
category: 'admin',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'seasons_completed', value: 5, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-seasons',
|
||||
name: 'Veteran Organizer',
|
||||
description: 'Complete 10 seasons',
|
||||
category: 'admin',
|
||||
rarity: 'epic',
|
||||
points: 200,
|
||||
requirements: [{ type: 'seasons_completed', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'large-league',
|
||||
name: 'Community Builder',
|
||||
description: 'Manage a league with 50+ members',
|
||||
category: 'admin',
|
||||
rarity: 'rare',
|
||||
points: 150,
|
||||
requirements: [{ type: 'members_managed', value: 50, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'huge-league',
|
||||
name: 'Empire Builder',
|
||||
description: 'Manage a league with 100+ members',
|
||||
category: 'admin',
|
||||
rarity: 'epic',
|
||||
points: 300,
|
||||
requirements: [{ type: 'members_managed', value: 100, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Community achievements (for all roles)
|
||||
export const COMMUNITY_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'community-leader',
|
||||
name: 'Community Leader',
|
||||
description: 'Achieve community leader trust level',
|
||||
category: 'community',
|
||||
rarity: 'legendary',
|
||||
points: 500,
|
||||
requirements: [{ type: 'trust_threshold', value: 90, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
@@ -124,269 +124,3 @@ export class Achievement implements IEntity<string> {
|
||||
return this.description;
|
||||
}
|
||||
}
|
||||
|
||||
// Predefined achievements for drivers
|
||||
export const DRIVER_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'first-race',
|
||||
name: 'First Steps',
|
||||
description: 'Complete your first race',
|
||||
category: 'driver',
|
||||
rarity: 'common',
|
||||
points: 10,
|
||||
requirements: [{ type: 'races_completed', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-races',
|
||||
name: 'Getting Started',
|
||||
description: 'Complete 10 races',
|
||||
category: 'driver',
|
||||
rarity: 'common',
|
||||
points: 25,
|
||||
requirements: [{ type: 'races_completed', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'fifty-races',
|
||||
name: 'Regular Racer',
|
||||
description: 'Complete 50 races',
|
||||
category: 'driver',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'races_completed', value: 50, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'hundred-races',
|
||||
name: 'Veteran',
|
||||
description: 'Complete 100 races',
|
||||
category: 'driver',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'races_completed', value: 100, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'first-win',
|
||||
name: 'Victory Lane',
|
||||
description: 'Win your first race',
|
||||
category: 'driver',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'wins', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-wins',
|
||||
name: 'Serial Winner',
|
||||
description: 'Win 10 races',
|
||||
category: 'driver',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'wins', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'first-podium',
|
||||
name: 'Podium Finisher',
|
||||
description: 'Finish on the podium',
|
||||
category: 'driver',
|
||||
rarity: 'common',
|
||||
points: 25,
|
||||
requirements: [{ type: 'podiums', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'clean-streak-5',
|
||||
name: 'Clean Racer',
|
||||
description: 'Complete 5 consecutive races without incidents',
|
||||
category: 'driver',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'consecutive_clean', value: 5, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'clean-streak-10',
|
||||
name: 'Safety First',
|
||||
description: 'Complete 10 consecutive races without incidents',
|
||||
category: 'driver',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'consecutive_clean', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'championship-win',
|
||||
name: 'Champion',
|
||||
description: 'Win a championship',
|
||||
category: 'driver',
|
||||
rarity: 'epic',
|
||||
points: 200,
|
||||
requirements: [{ type: 'championships_won', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'triple-crown',
|
||||
name: 'Triple Crown',
|
||||
description: 'Win 3 championships',
|
||||
category: 'driver',
|
||||
rarity: 'legendary',
|
||||
points: 500,
|
||||
requirements: [{ type: 'championships_won', value: 3, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'elite-driver',
|
||||
name: 'Elite Driver',
|
||||
description: 'Reach Elite driver rating',
|
||||
category: 'driver',
|
||||
rarity: 'epic',
|
||||
points: 250,
|
||||
requirements: [{ type: 'rating_threshold', value: 90, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Predefined achievements for stewards
|
||||
export const STEWARD_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'first-protest',
|
||||
name: 'Justice Served',
|
||||
description: 'Handle your first protest',
|
||||
category: 'steward',
|
||||
rarity: 'common',
|
||||
points: 15,
|
||||
requirements: [{ type: 'protests_handled', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-protests',
|
||||
name: 'Fair Judge',
|
||||
description: 'Handle 10 protests',
|
||||
category: 'steward',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'protests_handled', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'fifty-protests',
|
||||
name: 'Senior Steward',
|
||||
description: 'Handle 50 protests',
|
||||
category: 'steward',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'protests_handled', value: 50, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'hundred-protests',
|
||||
name: 'Chief Steward',
|
||||
description: 'Handle 100 protests',
|
||||
category: 'steward',
|
||||
rarity: 'epic',
|
||||
points: 200,
|
||||
requirements: [{ type: 'protests_handled', value: 100, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'event-steward-10',
|
||||
name: 'Event Official',
|
||||
description: 'Steward 10 race events',
|
||||
category: 'steward',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'events_stewarded', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'trusted-steward',
|
||||
name: 'Trusted Steward',
|
||||
description: 'Achieve highly-trusted status',
|
||||
category: 'steward',
|
||||
rarity: 'rare',
|
||||
points: 150,
|
||||
requirements: [{ type: 'trust_threshold', value: 75, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Predefined achievements for admins
|
||||
export const ADMIN_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'first-league',
|
||||
name: 'League Founder',
|
||||
description: 'Create your first league',
|
||||
category: 'admin',
|
||||
rarity: 'common',
|
||||
points: 25,
|
||||
requirements: [{ type: 'leagues_managed', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'first-season',
|
||||
name: 'Season Organizer',
|
||||
description: 'Complete your first full season',
|
||||
category: 'admin',
|
||||
rarity: 'uncommon',
|
||||
points: 50,
|
||||
requirements: [{ type: 'seasons_completed', value: 1, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'five-seasons',
|
||||
name: 'Experienced Organizer',
|
||||
description: 'Complete 5 seasons',
|
||||
category: 'admin',
|
||||
rarity: 'rare',
|
||||
points: 100,
|
||||
requirements: [{ type: 'seasons_completed', value: 5, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'ten-seasons',
|
||||
name: 'Veteran Organizer',
|
||||
description: 'Complete 10 seasons',
|
||||
category: 'admin',
|
||||
rarity: 'epic',
|
||||
points: 200,
|
||||
requirements: [{ type: 'seasons_completed', value: 10, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'large-league',
|
||||
name: 'Community Builder',
|
||||
description: 'Manage a league with 50+ members',
|
||||
category: 'admin',
|
||||
rarity: 'rare',
|
||||
points: 150,
|
||||
requirements: [{ type: 'members_managed', value: 50, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
{
|
||||
id: 'huge-league',
|
||||
name: 'Empire Builder',
|
||||
description: 'Manage a league with 100+ members',
|
||||
category: 'admin',
|
||||
rarity: 'epic',
|
||||
points: 300,
|
||||
requirements: [{ type: 'members_managed', value: 100, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
|
||||
// Community achievements (for all roles)
|
||||
export const COMMUNITY_ACHIEVEMENTS: Omit<AchievementProps, 'createdAt'>[] = [
|
||||
{
|
||||
id: 'community-leader',
|
||||
name: 'Community Leader',
|
||||
description: 'Achieve community leader trust level',
|
||||
category: 'community',
|
||||
rarity: 'legendary',
|
||||
points: 500,
|
||||
requirements: [{ type: 'trust_threshold', value: 90, operator: '>=' }],
|
||||
isSecret: false,
|
||||
},
|
||||
];
|
||||
@@ -4,8 +4,6 @@
|
||||
* Repository interface for User entity operations.
|
||||
*/
|
||||
|
||||
import type { AuthenticatedUserDTO } from '../../application/dto/AuthenticatedUserDTO';
|
||||
|
||||
export interface UserCredentials {
|
||||
email: string;
|
||||
passwordHash: string;
|
||||
|
||||
@@ -89,6 +89,18 @@ export class EntityMappers {
|
||||
|
||||
static toRaceDTO(race: Race | null): RaceDTO | null {
|
||||
if (!race) return null;
|
||||
|
||||
const sessionTypeMap = {
|
||||
practice: 'practice' as const,
|
||||
qualifying: 'qualifying' as const,
|
||||
q1: 'qualifying' as const,
|
||||
q2: 'qualifying' as const,
|
||||
q3: 'qualifying' as const,
|
||||
sprint: 'race' as const,
|
||||
main: 'race' as const,
|
||||
timeTrial: 'practice' as const,
|
||||
};
|
||||
|
||||
return {
|
||||
id: race.id,
|
||||
leagueId: race.leagueId,
|
||||
@@ -97,7 +109,7 @@ export class EntityMappers {
|
||||
trackId: race.trackId ?? '',
|
||||
car: race.car,
|
||||
carId: race.carId ?? '',
|
||||
sessionType: race.sessionType,
|
||||
sessionType: sessionTypeMap[race.sessionType.value],
|
||||
status: race.status,
|
||||
...(race.strengthOfField !== undefined
|
||||
? { strengthOfField: race.strengthOfField }
|
||||
@@ -112,6 +124,17 @@ export class EntityMappers {
|
||||
}
|
||||
|
||||
static toRaceDTOs(races: Race[]): RaceDTO[] {
|
||||
const sessionTypeMap = {
|
||||
practice: 'practice' as const,
|
||||
qualifying: 'qualifying' as const,
|
||||
q1: 'qualifying' as const,
|
||||
q2: 'qualifying' as const,
|
||||
q3: 'qualifying' as const,
|
||||
sprint: 'race' as const,
|
||||
main: 'race' as const,
|
||||
timeTrial: 'practice' as const,
|
||||
};
|
||||
|
||||
return races.map((race) => ({
|
||||
id: race.id,
|
||||
leagueId: race.leagueId,
|
||||
@@ -120,7 +143,7 @@ export class EntityMappers {
|
||||
trackId: race.trackId ?? '',
|
||||
car: race.car,
|
||||
carId: race.carId ?? '',
|
||||
sessionType: race.sessionType,
|
||||
sessionType: sessionTypeMap[race.sessionType.value],
|
||||
status: race.status,
|
||||
...(race.strengthOfField !== undefined
|
||||
? { strengthOfField: race.strengthOfField }
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
|
||||
import { AcceptSponsorshipRequestUseCase } from './AcceptSponsorshipRequestUseCase';
|
||||
import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository';
|
||||
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
|
||||
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
||||
import type { INotificationService } from '@core/notifications/application/ports/INotificationService';
|
||||
import type { IPaymentGateway } from '../ports/IPaymentGateway';
|
||||
import type { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
|
||||
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
|
||||
import { Season } from '../../domain/entities/Season';
|
||||
import { LeagueWallet } from '../../domain/entities/LeagueWallet';
|
||||
import { Money } from '../../domain/value-objects/Money';
|
||||
|
||||
describe('AcceptSponsorshipRequestUseCase', () => {
|
||||
let mockSponsorshipRequestRepo: {
|
||||
findById: Mock;
|
||||
update: Mock;
|
||||
};
|
||||
let mockSeasonSponsorshipRepo: {
|
||||
create: Mock;
|
||||
};
|
||||
let mockSeasonRepo: {
|
||||
findById: Mock;
|
||||
};
|
||||
let mockNotificationService: {
|
||||
sendNotification: Mock;
|
||||
};
|
||||
let mockPaymentGateway: {
|
||||
processPayment: Mock;
|
||||
};
|
||||
let mockWalletRepo: {
|
||||
findById: Mock;
|
||||
update: Mock;
|
||||
};
|
||||
let mockLeagueWalletRepo: {
|
||||
findById: Mock;
|
||||
update: Mock;
|
||||
};
|
||||
let mockLogger: {
|
||||
debug: Mock;
|
||||
info: Mock;
|
||||
warn: Mock;
|
||||
error: Mock;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockSponsorshipRequestRepo = {
|
||||
findById: vi.fn(),
|
||||
update: vi.fn(),
|
||||
};
|
||||
mockSeasonSponsorshipRepo = {
|
||||
create: vi.fn(),
|
||||
};
|
||||
mockSeasonRepo = {
|
||||
findById: vi.fn(),
|
||||
};
|
||||
mockNotificationService = {
|
||||
sendNotification: vi.fn(),
|
||||
};
|
||||
mockPaymentGateway = {
|
||||
processPayment: vi.fn(),
|
||||
};
|
||||
mockWalletRepo = {
|
||||
findById: vi.fn(),
|
||||
update: vi.fn(),
|
||||
};
|
||||
mockLeagueWalletRepo = {
|
||||
findById: vi.fn(),
|
||||
update: vi.fn(),
|
||||
};
|
||||
mockLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should send notification to sponsor, process payment, and update wallets when accepting season sponsorship', async () => {
|
||||
const useCase = new AcceptSponsorshipRequestUseCase(
|
||||
mockSponsorshipRequestRepo as unknown as ISponsorshipRequestRepository,
|
||||
mockSeasonSponsorshipRepo as unknown as ISeasonSponsorshipRepository,
|
||||
mockSeasonRepo as unknown as ISeasonRepository,
|
||||
mockNotificationService as unknown as INotificationService,
|
||||
mockPaymentGateway as unknown as IPaymentGateway,
|
||||
mockWalletRepo as unknown as IWalletRepository,
|
||||
mockLeagueWalletRepo as unknown as ILeagueWalletRepository,
|
||||
mockLogger as unknown as Logger,
|
||||
);
|
||||
|
||||
const request = SponsorshipRequest.create({
|
||||
id: 'req1',
|
||||
sponsorId: 'sponsor1',
|
||||
entityId: 'season1',
|
||||
entityType: 'season',
|
||||
tier: 'main',
|
||||
offeredAmount: Money.create(1000),
|
||||
status: 'pending',
|
||||
});
|
||||
|
||||
const season = Season.create({
|
||||
id: 'season1',
|
||||
leagueId: 'league1',
|
||||
gameId: 'game1',
|
||||
name: 'Season 1',
|
||||
startDate: new Date(),
|
||||
endDate: new Date(),
|
||||
});
|
||||
|
||||
mockSponsorshipRequestRepo.findById.mockResolvedValue(request);
|
||||
mockSeasonRepo.findById.mockResolvedValue(season);
|
||||
mockNotificationService.sendNotification.mockResolvedValue(undefined);
|
||||
mockPaymentGateway.processPayment.mockResolvedValue({
|
||||
success: true,
|
||||
transactionId: 'txn1',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
mockWalletRepo.findById.mockResolvedValue({
|
||||
id: 'sponsor1',
|
||||
leagueId: 'league1',
|
||||
balance: 2000,
|
||||
totalRevenue: 0,
|
||||
totalPlatformFees: 0,
|
||||
totalWithdrawn: 0,
|
||||
currency: 'USD',
|
||||
createdAt: new Date(),
|
||||
});
|
||||
const leagueWallet = LeagueWallet.create({
|
||||
id: 'league1',
|
||||
leagueId: 'league1',
|
||||
balance: Money.create(500),
|
||||
});
|
||||
mockLeagueWalletRepo.findById.mockResolvedValue(leagueWallet);
|
||||
|
||||
const result = await useCase.execute({
|
||||
requestId: 'req1',
|
||||
respondedBy: 'driver1',
|
||||
});
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(mockNotificationService.sendNotification).toHaveBeenCalledWith({
|
||||
recipientId: 'sponsor1',
|
||||
type: 'sponsorship_request_accepted',
|
||||
title: 'Sponsorship Accepted',
|
||||
body: 'Your sponsorship request for Season 1 has been accepted.',
|
||||
channel: 'in_app',
|
||||
urgency: 'toast',
|
||||
data: {
|
||||
requestId: 'req1',
|
||||
sponsorshipId: expect.any(String),
|
||||
},
|
||||
});
|
||||
expect(mockPaymentGateway.processPayment).toHaveBeenCalledWith(
|
||||
Money.create(1000),
|
||||
'sponsor1',
|
||||
'Sponsorship payment for season season1',
|
||||
{ requestId: 'req1' }
|
||||
);
|
||||
expect(mockWalletRepo.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: 'sponsor1',
|
||||
balance: 1000,
|
||||
})
|
||||
);
|
||||
expect(mockLeagueWalletRepo.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: 'league1',
|
||||
balance: expect.objectContaining({ amount: 1400 }),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -9,6 +9,10 @@ import type { Logger } from '@core/shared/application';
|
||||
import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository';
|
||||
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
|
||||
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
||||
import type { INotificationService } from '@core/notifications/application/ports/INotificationService';
|
||||
import type { IPaymentGateway } from '../ports/IPaymentGateway';
|
||||
import type { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
|
||||
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
||||
import { SeasonSponsorship } from '../../domain/entities/SeasonSponsorship';
|
||||
import type { AsyncUseCase } from '@core/shared/application';
|
||||
|
||||
@@ -32,6 +36,10 @@ export class AcceptSponsorshipRequestUseCase
|
||||
private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository,
|
||||
private readonly seasonSponsorshipRepo: ISeasonSponsorshipRepository,
|
||||
private readonly seasonRepository: ISeasonRepository,
|
||||
private readonly notificationService: INotificationService,
|
||||
private readonly paymentGateway: IPaymentGateway,
|
||||
private readonly walletRepository: IWalletRepository,
|
||||
private readonly leagueWalletRepository: ILeagueWalletRepository,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
@@ -79,12 +87,59 @@ export class AcceptSponsorshipRequestUseCase
|
||||
});
|
||||
await this.seasonSponsorshipRepo.create(sponsorship);
|
||||
this.logger.info(`Season sponsorship ${sponsorshipId} created for request ${dto.requestId}.`, { sponsorshipId, requestId: dto.requestId });
|
||||
}
|
||||
|
||||
// TODO: In a real implementation, we would:
|
||||
// 1. Create notification for the sponsor
|
||||
// 2. Process payment
|
||||
// 3. Update wallet balances
|
||||
// Notify the sponsor
|
||||
await this.notificationService.sendNotification({
|
||||
recipientId: request.sponsorId,
|
||||
type: 'sponsorship_request_accepted',
|
||||
title: 'Sponsorship Accepted',
|
||||
body: `Your sponsorship request for ${season.name} has been accepted.`,
|
||||
channel: 'in_app',
|
||||
urgency: 'toast',
|
||||
data: {
|
||||
requestId: request.id,
|
||||
sponsorshipId,
|
||||
},
|
||||
});
|
||||
|
||||
// Process payment
|
||||
const paymentResult = await this.paymentGateway.processPayment(
|
||||
request.offeredAmount,
|
||||
request.sponsorId,
|
||||
`Sponsorship payment for ${request.entityType} ${request.entityId}`,
|
||||
{ requestId: request.id }
|
||||
);
|
||||
if (!paymentResult.success) {
|
||||
this.logger.error(`Payment failed for sponsorship request ${request.id}: ${paymentResult.error}`, undefined, { requestId: request.id });
|
||||
throw new Error('Payment processing failed');
|
||||
}
|
||||
|
||||
// Update wallets
|
||||
const sponsorWallet = await this.walletRepository.findById(request.sponsorId);
|
||||
if (!sponsorWallet) {
|
||||
this.logger.error(`Sponsor wallet not found for ${request.sponsorId}`, undefined, { sponsorId: request.sponsorId });
|
||||
throw new Error('Sponsor wallet not found');
|
||||
}
|
||||
|
||||
const leagueWallet = await this.leagueWalletRepository.findById(season.leagueId);
|
||||
if (!leagueWallet) {
|
||||
this.logger.error(`League wallet not found for ${season.leagueId}`, undefined, { leagueId: season.leagueId });
|
||||
throw new Error('League wallet not found');
|
||||
}
|
||||
|
||||
const netAmount = acceptedRequest.getNetAmount();
|
||||
|
||||
// Deduct from sponsor wallet
|
||||
const updatedSponsorWallet = {
|
||||
...sponsorWallet,
|
||||
balance: sponsorWallet.balance - request.offeredAmount.amount,
|
||||
};
|
||||
await this.walletRepository.update(updatedSponsorWallet);
|
||||
|
||||
// Add to league wallet
|
||||
const updatedLeagueWallet = leagueWallet.addFunds(netAmount, paymentResult.transactionId!);
|
||||
await this.leagueWalletRepository.update(updatedLeagueWallet);
|
||||
}
|
||||
|
||||
this.logger.info(`Sponsorship request ${acceptedRequest.id} successfully accepted.`, { requestId: acceptedRequest.id, sponsorshipId });
|
||||
|
||||
@@ -97,8 +152,9 @@ export class AcceptSponsorshipRequestUseCase
|
||||
netAmount: acceptedRequest.getNetAmount().amount,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to accept sponsorship request ${dto.requestId}: ${error.message}`, { requestId: dto.requestId, error: error.message, stack: error.stack });
|
||||
throw error;
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
this.logger.error(`Failed to accept sponsorship request ${dto.requestId}: ${err.message}`, err, { requestId: dto.requestId });
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export class ApplyForSponsorshipUseCase
|
||||
// Validate sponsor exists
|
||||
const sponsor = await this.sponsorRepo.findById(dto.sponsorId);
|
||||
if (!sponsor) {
|
||||
this.logger.error('Sponsor not found', { sponsorId: dto.sponsorId });
|
||||
this.logger.error('Sponsor not found', undefined, { sponsorId: dto.sponsorId });
|
||||
throw new EntityNotFoundError({ entity: 'sponsor', id: dto.sponsorId });
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export class ApplyPenaltyUseCase
|
||||
|
||||
return { penaltyId: penalty.id };
|
||||
} catch (error) {
|
||||
this.logger.error('ApplyPenaltyUseCase: Failed to apply penalty', { command, error: error.message });
|
||||
this.logger.error('ApplyPenaltyUseCase: Failed to apply penalty', error, { command });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class ApproveTeamJoinRequestUseCase
|
||||
await this.membershipRepository.removeJoinRequest(requestId);
|
||||
this.logger.info(`Team join request with ID ${requestId} removed`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to approve team join request ${requestId}:`, error);
|
||||
this.logger.error(`Failed to approve team join request ${requestId}`, error instanceof Error ? error : new Error(String(error)));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export class CancelRaceUseCase
|
||||
await this.raceRepository.update(cancelledRace);
|
||||
this.logger.info(`[CancelRaceUseCase] Race ${raceId} cancelled successfully.`);
|
||||
} catch (error) {
|
||||
this.logger.error(`[CancelRaceUseCase] Error cancelling race ${raceId}:`, error);
|
||||
this.logger.error(`[CancelRaceUseCase] Error cancelling race ${raceId}`, error instanceof Error ? error : new Error(String(error)));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { UseCase } from '@core/shared/application/UseCase';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import type { IRaceEventRepository } from '../../domain/repositories/IRaceEventRepository';
|
||||
import type { IDomainEventPublisher } from '@core/shared/domain';
|
||||
import type { RaceEventStewardingClosedEvent } from '../../domain/events/RaceEventStewardingClosed';
|
||||
@@ -20,6 +21,8 @@ export class CloseRaceEventStewardingUseCase
|
||||
implements UseCase<CloseRaceEventStewardingCommand, void, void, void>
|
||||
{
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
|
||||
private readonly raceEventRepository: IRaceEventRepository,
|
||||
private readonly domainEventPublisher: IDomainEventPublisher,
|
||||
) {}
|
||||
@@ -58,7 +61,7 @@ export class CloseRaceEventStewardingUseCase
|
||||
await this.domainEventPublisher.publish(event);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Failed to close stewarding for race event ${raceEvent.id}:`, error);
|
||||
this.logger.error(`Failed to close stewarding for race event ${raceEvent.id}`, error instanceof Error ? error : new Error(String(error)));
|
||||
// In production, this would trigger alerts/monitoring
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@ export class CompleteRaceUseCaseWithRatings
|
||||
const completedRace = race.complete();
|
||||
await this.raceRepository.update(completedRace);
|
||||
this.logger.info(`Race ID: ${raceId} completed successfully.`);
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Error completing race ${raceId}: ${error.message}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Error completing race ${raceId}`, error instanceof Error ? error : new Error(String(error)));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Season } from '../../domain/entities/Season';
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
|
||||
import type { ILeagueScoringConfigRepository } from '../../domain/repositories/ILeagueScoringConfigRepository';
|
||||
import type { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
|
||||
import type { AsyncUseCase } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import type {
|
||||
@@ -129,11 +128,7 @@ export class CreateLeagueWithSeasonAndScoringUseCase
|
||||
this.logger.debug('CreateLeagueWithSeasonAndScoringUseCase completed successfully.', { result });
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logger.error('Error during CreateLeagueWithSeasonAndScoringUseCase execution.', {
|
||||
command,
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
});
|
||||
this.logger.error('Error during CreateLeagueWithSeasonAndScoringUseCase execution.', error, { command });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import type {
|
||||
AllTeamsResultDTO,
|
||||
} from '../presenters/IAllTeamsPresenter';
|
||||
import type { UseCase } from '@core/shared/application';
|
||||
import type { Team } from '../../domain/entities/Team';
|
||||
import { Logger } from "@core/shared/application";
|
||||
|
||||
/**
|
||||
@@ -54,7 +53,7 @@ export class GetAllTeamsUseCase
|
||||
presenter.present(dto);
|
||||
this.logger.info('Successfully retrieved all teams.');
|
||||
} catch (error) {
|
||||
this.logger.error('Error retrieving all teams:', error);
|
||||
this.logger.error('Error retrieving all teams', error instanceof Error ? error : new Error(String(error)));
|
||||
throw error; // Re-throw the error after logging
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { IGetLeagueAdminPermissionsPresenter, GetLeagueAdminPermissionsResultDTO, GetLeagueAdminPermissionsViewModel } from '../presenters/IGetLeagueAdminPermissionsPresenter';
|
||||
import type { IGetLeagueAdminPermissionsPresenter, GetLeagueAdminPermissionsViewModel } from '../presenters/IGetLeagueAdminPermissionsPresenter';
|
||||
import type { UseCase } from '@core/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueAdminPermissionsUseCaseParams {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { IGetLeagueAdminPresenter, GetLeagueAdminResultDTO, GetLeagueAdminViewModel } from '../presenters/IGetLeagueAdminPresenter';
|
||||
import { GetLeagueAdminPermissionsViewModel } from '../presenters/IGetLeagueAdminPermissionsPresenter';
|
||||
import type { IGetLeagueAdminPresenter } from '../presenters/IGetLeagueAdminPresenter';
|
||||
import type { UseCase } from '@core/shared/application/UseCase';
|
||||
|
||||
export interface GetLeagueAdminUseCaseParams {
|
||||
@@ -14,7 +15,7 @@ export interface GetLeagueAdminResultDTO {
|
||||
// Additional data would be populated by combining multiple use cases
|
||||
}
|
||||
|
||||
export class GetLeagueAdminUseCase implements UseCase<GetLeagueAdminUseCaseParams, GetLeagueAdminResultDTO, GetLeagueAdminViewModel, IGetLeagueAdminPresenter> {
|
||||
export class GetLeagueAdminUseCase implements UseCase<GetLeagueAdminUseCaseParams, GetLeagueAdminResultDTO, GetLeagueAdminPermissionsViewModel, IGetLeagueAdminPresenter> {
|
||||
constructor(
|
||||
private readonly leagueRepository: ILeagueRepository,
|
||||
) {}
|
||||
|
||||
Reference in New Issue
Block a user