module creation
This commit is contained in:
49
apps/api/src/modules/driver/DriverController.ts
Normal file
49
apps/api/src/modules/driver/DriverController.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Controller, Get, Post, Body, Req, Param } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
|
||||
import { DriverService } from './DriverService';
|
||||
import { DriversLeaderboardViewModel, DriverStatsDto, CompleteOnboardingInput, CompleteOnboardingOutput, GetDriverRegistrationStatusQuery, DriverRegistrationStatusViewModel } from './dto/DriverDto';
|
||||
|
||||
@ApiTags('drivers')
|
||||
@Controller('drivers')
|
||||
export class DriverController {
|
||||
constructor(private readonly driverService: DriverService) {}
|
||||
|
||||
@Get('leaderboard')
|
||||
@ApiOperation({ summary: 'Get drivers leaderboard' })
|
||||
@ApiResponse({ status: 200, description: 'List of drivers for the leaderboard', type: DriversLeaderboardViewModel })
|
||||
async getDriversLeaderboard(): Promise<DriversLeaderboardViewModel> {
|
||||
return this.driverService.getDriversLeaderboard();
|
||||
}
|
||||
|
||||
@Get('total-drivers')
|
||||
@ApiOperation({ summary: 'Get the total number of drivers' })
|
||||
@ApiResponse({ status: 200, description: 'Total number of drivers', type: DriverStatsDto })
|
||||
async getTotalDrivers(): Promise<DriverStatsDto> {
|
||||
return this.driverService.getTotalDrivers();
|
||||
}
|
||||
|
||||
@Post('complete-onboarding')
|
||||
@ApiOperation({ summary: 'Complete driver onboarding for a user' })
|
||||
@ApiResponse({ status: 200, description: 'Onboarding complete', type: CompleteOnboardingOutput })
|
||||
async completeOnboarding(
|
||||
@Body() input: CompleteOnboardingInput,
|
||||
@Req() req: Request,
|
||||
): Promise<CompleteOnboardingOutput> {
|
||||
// Assuming userId is available from the request (e.g., via auth middleware)
|
||||
const userId = req['user'].userId; // Placeholder for actual user extraction
|
||||
return this.driverService.completeOnboarding(userId, input);
|
||||
}
|
||||
|
||||
@Get(':driverId/races/:raceId/registration-status')
|
||||
@ApiOperation({ summary: 'Get driver registration status for a specific race' })
|
||||
@ApiResponse({ status: 200, description: 'Driver registration status', type: DriverRegistrationStatusViewModel })
|
||||
async getDriverRegistrationStatus(
|
||||
@Param('driverId') driverId: string,
|
||||
@Param('raceId') raceId: string,
|
||||
): Promise<DriverRegistrationStatusViewModel> {
|
||||
return this.driverService.getDriverRegistrationStatus({ driverId, raceId });
|
||||
}
|
||||
|
||||
// Add other Driver endpoints here based on other presenters
|
||||
}
|
||||
10
apps/api/src/modules/driver/DriverModule.ts
Normal file
10
apps/api/src/modules/driver/DriverModule.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DriverService } from './DriverService';
|
||||
import { DriverController } from './DriverController';
|
||||
|
||||
@Module({
|
||||
controllers: [DriverController],
|
||||
providers: [DriverService],
|
||||
exports: [DriverService],
|
||||
})
|
||||
export class DriverModule {}
|
||||
75
apps/api/src/modules/driver/DriverProviders.ts
Normal file
75
apps/api/src/modules/driver/DriverProviders.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Provider } from '@nestjs/common';
|
||||
import { DriverService } from './DriverService';
|
||||
|
||||
// Import core interfaces
|
||||
import { IDriverRepository } from '../../../../core/racing/domain/repositories/IDriverRepository';
|
||||
import { IRankingService } from '../../../../core/racing/domain/services/IRankingService';
|
||||
import { IDriverStatsService } from '../../../../core/racing/domain/services/IDriverStatsService';
|
||||
import { DriverRatingProvider } from '../../../../core/racing/application/ports/DriverRatingProvider';
|
||||
import { IImageServicePort } from '../../../../core/racing/application/ports/IImageServicePort';
|
||||
import { IRaceRegistrationRepository } from '../../../../core/racing/domain/repositories/IRaceRegistrationRepository';
|
||||
import { INotificationPreferenceRepository } from '../../../../core/notifications/domain/repositories/INotificationPreferenceRepository';
|
||||
import { ILogger } from '../../../../core/shared/logging/ILogger';
|
||||
|
||||
// Import concrete in-memory implementations
|
||||
import { InMemoryDriverRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryDriverRepository';
|
||||
import { InMemoryRankingService } from '../../../adapters/racing/services/InMemoryRankingService';
|
||||
import { InMemoryDriverStatsService } from '../../../adapters/racing/services/InMemoryDriverStatsService';
|
||||
import { InMemoryDriverRatingProvider } from '../../../adapters/racing/ports/InMemoryDriverRatingProvider';
|
||||
import { InMemoryImageServiceAdapter } from '../../../adapters/media/ports/InMemoryImageServiceAdapter';
|
||||
import { InMemoryRaceRegistrationRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRegistrationRepository';
|
||||
import { InMemoryNotificationPreferenceRepository } from '../../../adapters/notifications/persistence/inmemory/InMemoryNotificationPreferenceRepository';
|
||||
import { ConsoleLogger } from '../../../adapters/logging/ConsoleLogger';
|
||||
|
||||
// Define injection tokens
|
||||
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||
export const RANKING_SERVICE_TOKEN = 'IRankingService';
|
||||
export const DRIVER_STATS_SERVICE_TOKEN = 'IDriverStatsService';
|
||||
export const DRIVER_RATING_PROVIDER_TOKEN = 'DriverRatingProvider';
|
||||
export const IMAGE_SERVICE_PORT_TOKEN = 'IImageServicePort';
|
||||
export const RACE_REGISTRATION_REPOSITORY_TOKEN = 'IRaceRegistrationRepository';
|
||||
export const NOTIFICATION_PREFERENCE_REPOSITORY_TOKEN = 'INotificationPreferenceRepository';
|
||||
export const LOGGER_TOKEN = 'ILogger'; // Already defined in AuthProviders, but good to have here too
|
||||
|
||||
export const DriverProviders: Provider[] = [
|
||||
DriverService, // Provide the service itself
|
||||
{
|
||||
provide: DRIVER_REPOSITORY_TOKEN,
|
||||
useFactory: (logger: ILogger) => new InMemoryDriverRepository(logger), // Factory for InMemoryDriverRepository
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: RANKING_SERVICE_TOKEN,
|
||||
useFactory: (logger: ILogger) => new InMemoryRankingService(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: DRIVER_STATS_SERVICE_TOKEN,
|
||||
useFactory: (logger: ILogger) => new InMemoryDriverStatsService(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: DRIVER_RATING_PROVIDER_TOKEN,
|
||||
useFactory: (logger: ILogger) => new InMemoryDriverRatingProvider(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: IMAGE_SERVICE_PORT_TOKEN,
|
||||
useFactory: (logger: ILogger) => new InMemoryImageServiceAdapter(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: RACE_REGISTRATION_REPOSITORY_TOKEN,
|
||||
useFactory: (logger: ILogger) => new InMemoryRaceRegistrationRepository(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: NOTIFICATION_PREFERENCE_REPOSITORY_TOKEN,
|
||||
useFactory: (logger: ILogger) => new InMemoryNotificationPreferenceRepository(logger),
|
||||
inject: [LOGGER_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: LOGGER_TOKEN,
|
||||
useClass: ConsoleLogger,
|
||||
},
|
||||
];
|
||||
46
apps/api/src/modules/driver/DriverService.ts
Normal file
46
apps/api/src/modules/driver/DriverService.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DriversLeaderboardViewModel, DriverStatsDto, CompleteOnboardingInput, CompleteOnboardingOutput, GetDriverRegistrationStatusQuery, DriverRegistrationStatusViewModel, DriverLeaderboardItemViewModel } from './dto/DriverDto';
|
||||
|
||||
@Injectable()
|
||||
export class DriverService {
|
||||
|
||||
constructor() {}
|
||||
|
||||
async getDriversLeaderboard(): Promise<DriversLeaderboardViewModel> {
|
||||
console.log('[DriverService] Returning mock driver leaderboard.');
|
||||
const drivers: DriverLeaderboardItemViewModel[] = [
|
||||
{ id: 'driver-1', name: 'Mock Driver 1', rating: 2500, skillLevel: 'Pro', nationality: 'DE', racesCompleted: 50, wins: 10, podiums: 20, isActive: true, rank: 1, avatarUrl: 'https://cdn.example.com/avatars/driver-1.png' },
|
||||
{ id: 'driver-2', name: 'Mock Driver 2', rating: 2400, skillLevel: 'Amateur', nationality: 'US', racesCompleted: 40, wins: 5, podiums: 15, isActive: true, rank: 2, avatarUrl: 'https://cdn.example.com/avatars/driver-2.png' },
|
||||
];
|
||||
return {
|
||||
drivers: drivers.sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0)),
|
||||
totalRaces: drivers.reduce((sum, item) => sum + (item.racesCompleted ?? 0), 0),
|
||||
totalWins: drivers.reduce((sum, item) => sum + (item.wins ?? 0), 0),
|
||||
activeCount: drivers.filter(d => d.isActive).length,
|
||||
};
|
||||
}
|
||||
|
||||
async getTotalDrivers(): Promise<DriverStatsDto> {
|
||||
console.log('[DriverService] Returning mock total drivers.');
|
||||
return {
|
||||
totalDrivers: 2,
|
||||
};
|
||||
}
|
||||
|
||||
async completeOnboarding(userId: string, input: CompleteOnboardingInput): Promise<CompleteOnboardingOutput> {
|
||||
console.log('Completing onboarding for user:', userId, input);
|
||||
return {
|
||||
success: true,
|
||||
driverId: `driver-${userId}-onboarded`,
|
||||
};
|
||||
}
|
||||
|
||||
async getDriverRegistrationStatus(query: GetDriverRegistrationStatusQuery): Promise<DriverRegistrationStatusViewModel> {
|
||||
console.log('Checking driver registration status:', query);
|
||||
return {
|
||||
isRegistered: false, // Mock response
|
||||
raceId: query.raceId,
|
||||
driverId: query.driverId,
|
||||
};
|
||||
}
|
||||
}
|
||||
138
apps/api/src/modules/driver/dto/DriverDto.ts
Normal file
138
apps/api/src/modules/driver/dto/DriverDto.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsString, IsNotEmpty, IsOptional, IsBoolean } from 'class-validator';
|
||||
|
||||
export class DriverLeaderboardItemViewModel {
|
||||
@ApiProperty()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
rating: number;
|
||||
|
||||
@ApiProperty()
|
||||
skillLevel: string; // Assuming skillLevel is a string like 'Rookie', 'Pro', etc.
|
||||
|
||||
@ApiProperty()
|
||||
nationality: string;
|
||||
|
||||
@ApiProperty()
|
||||
racesCompleted: number;
|
||||
|
||||
@ApiProperty()
|
||||
wins: number;
|
||||
|
||||
@ApiProperty()
|
||||
podiums: number;
|
||||
|
||||
@ApiProperty()
|
||||
isActive: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
rank: number;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
avatarUrl?: string;
|
||||
}
|
||||
|
||||
export class DriversLeaderboardViewModel {
|
||||
@ApiProperty({ type: [DriverLeaderboardItemViewModel] })
|
||||
drivers: DriverLeaderboardItemViewModel[];
|
||||
|
||||
@ApiProperty()
|
||||
totalRaces: number;
|
||||
|
||||
@ApiProperty()
|
||||
totalWins: number;
|
||||
|
||||
@ApiProperty()
|
||||
activeCount: number;
|
||||
}
|
||||
|
||||
export class DriverStatsDto {
|
||||
@ApiProperty()
|
||||
totalDrivers: number;
|
||||
}
|
||||
|
||||
export class CompleteOnboardingInput {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
firstName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
displayName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
country: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
timezone?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
bio?: string;
|
||||
}
|
||||
|
||||
export class CompleteOnboardingOutput {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
success: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
driverId?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsString()
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
export class GetDriverRegistrationStatusQuery {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
raceId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
driverId: string;
|
||||
}
|
||||
|
||||
export class DriverRegistrationStatusViewModel {
|
||||
@ApiProperty()
|
||||
@IsBoolean()
|
||||
isRegistered: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
raceId: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
driverId: string;
|
||||
}
|
||||
|
||||
export class DriverDto {
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
name: string; // Display name or full name
|
||||
}
|
||||
|
||||
// Add other DTOs for driver-related logic as needed
|
||||
Reference in New Issue
Block a user