module cleanup

This commit is contained in:
2025-12-19 01:22:45 +01:00
parent d617654928
commit d0fac9e6c1
135 changed files with 5104 additions and 1315 deletions

View File

@@ -0,0 +1,181 @@
import { Test, TestingModule } from '@nestjs/testing';
import { vi } from 'vitest';
import { MediaController } from './MediaController';
import { MediaService } from './MediaService';
import type { Response } from 'express';
import { RequestAvatarGenerationInputDTO } from './dtos/RequestAvatarGenerationInputDTO';
import { UploadMediaInputDTO } from './dtos/UploadMediaInputDTO';
describe('MediaController', () => {
let controller: MediaController;
let service: ReturnType<typeof vi.mocked<MediaService>>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [MediaController],
providers: [
{
provide: MediaService,
useValue: {
requestAvatarGeneration: vi.fn(),
uploadMedia: vi.fn(),
getMedia: vi.fn(),
deleteMedia: vi.fn(),
getAvatar: vi.fn(),
updateAvatar: vi.fn(),
},
},
],
}).compile();
controller = module.get<MediaController>(MediaController);
service = vi.mocked(module.get(MediaService));
});
describe('requestAvatarGeneration', () => {
it('should request avatar generation and return 201 on success', async () => {
const input: RequestAvatarGenerationInputDTO = { driverId: 'driver-123' };
const result = { success: true, jobId: 'job-123' };
service.requestAvatarGeneration.mockResolvedValue(result);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.requestAvatarGeneration(input, mockRes);
expect(service.requestAvatarGeneration).toHaveBeenCalledWith(input);
expect(mockRes.status).toHaveBeenCalledWith(201);
expect(mockRes.json).toHaveBeenCalledWith(result);
});
it('should return 400 on failure', async () => {
const input: RequestAvatarGenerationInputDTO = { driverId: 'driver-123' };
const result = { success: false, error: 'Error' };
service.requestAvatarGeneration.mockResolvedValue(result);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.requestAvatarGeneration(input, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(400);
expect(mockRes.json).toHaveBeenCalledWith(result);
});
});
describe('uploadMedia', () => {
it('should upload media and return 201 on success', async () => {
const file: Express.Multer.File = { filename: 'file.jpg' } as Express.Multer.File;
const input: UploadMediaInputDTO = { type: 'image' };
const result = { success: true, mediaId: 'media-123' };
service.uploadMedia.mockResolvedValue(result);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.uploadMedia(file, input, mockRes);
expect(service.uploadMedia).toHaveBeenCalledWith({ ...input, file });
expect(mockRes.status).toHaveBeenCalledWith(201);
expect(mockRes.json).toHaveBeenCalledWith(result);
});
});
describe('getMedia', () => {
it('should return media if found', async () => {
const mediaId = 'media-123';
const result = { id: mediaId, url: 'url' };
service.getMedia.mockResolvedValue(result);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.getMedia(mediaId, mockRes);
expect(service.getMedia).toHaveBeenCalledWith(mediaId);
expect(mockRes.status).toHaveBeenCalledWith(200);
expect(mockRes.json).toHaveBeenCalledWith(result);
});
it('should return 404 if not found', async () => {
const mediaId = 'media-123';
service.getMedia.mockResolvedValue(null);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.getMedia(mediaId, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(404);
expect(mockRes.json).toHaveBeenCalledWith({ error: 'Media not found' });
});
});
describe('deleteMedia', () => {
it('should delete media', async () => {
const mediaId = 'media-123';
const result = { success: true };
service.deleteMedia.mockResolvedValue(result);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.deleteMedia(mediaId, mockRes);
expect(service.deleteMedia).toHaveBeenCalledWith(mediaId);
expect(mockRes.status).toHaveBeenCalledWith(200);
expect(mockRes.json).toHaveBeenCalledWith(result);
});
});
describe('getAvatar', () => {
it('should return avatar if found', async () => {
const driverId = 'driver-123';
const result = { url: 'avatar.jpg' };
service.getAvatar.mockResolvedValue(result);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.getAvatar(driverId, mockRes);
expect(service.getAvatar).toHaveBeenCalledWith(driverId);
expect(mockRes.status).toHaveBeenCalledWith(200);
expect(mockRes.json).toHaveBeenCalledWith(result);
});
});
describe('updateAvatar', () => {
it('should update avatar', async () => {
const driverId = 'driver-123';
const input = { url: 'new-avatar.jpg' };
const result = { success: true };
service.updateAvatar.mockResolvedValue(result);
const mockRes: ReturnType<typeof vi.mocked<Response>> = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as unknown as ReturnType<typeof vi.mocked<Response>>;
await controller.updateAvatar(driverId, input, mockRes);
expect(service.updateAvatar).toHaveBeenCalledWith(driverId, input);
expect(mockRes.status).toHaveBeenCalledWith(200);
expect(mockRes.json).toHaveBeenCalledWith(result);
});
});
});

View File

@@ -3,25 +3,19 @@ import { ApiTags, ApiResponse, ApiOperation, ApiParam, ApiConsumes } from '@nest
import { Response } from 'express';
import { FileInterceptor } from '@nestjs/platform-express';
import { MediaService } from './MediaService';
import type { RequestAvatarGenerationInputDTO } from './dtos/RequestAvatarGenerationInputDTO';
import type { RequestAvatarGenerationOutputDTO } from './dtos/RequestAvatarGenerationOutputDTO';
import type { UploadMediaInputDTO } from './dtos/UploadMediaInputDTO';
import type { UploadMediaOutputDTO } from './dtos/UploadMediaOutputDTO';
import type { GetMediaOutputDTO } from './dtos/GetMediaOutputDTO';
import type { DeleteMediaOutputDTO } from './dtos/DeleteMediaOutputDTO';
import type { GetAvatarOutputDTO } from './dtos/GetAvatarOutputDTO';
import type { UpdateAvatarInputDTO } from './dtos/UpdateAvatarInputDTO';
import type { UpdateAvatarOutputDTO } from './dtos/UpdateAvatarOutputDTO';
import { RequestAvatarGenerationInputDTO } from './dtos/RequestAvatarGenerationInputDTO';
import { RequestAvatarGenerationOutputDTO } from './dtos/RequestAvatarGenerationOutputDTO';
import { UploadMediaInputDTO } from './dtos/UploadMediaInputDTO';
import { UploadMediaOutputDTO } from './dtos/UploadMediaOutputDTO';
import { GetMediaOutputDTO } from './dtos/GetMediaOutputDTO';
import { DeleteMediaOutputDTO } from './dtos/DeleteMediaOutputDTO';
import { GetAvatarOutputDTO } from './dtos/GetAvatarOutputDTO';
import { UpdateAvatarInputDTO } from './dtos/UpdateAvatarInputDTO';
import { UpdateAvatarOutputDTO } from './dtos/UpdateAvatarOutputDTO';
type RequestAvatarGenerationInput = RequestAvatarGenerationInputDTO;
type RequestAvatarGenerationOutput = RequestAvatarGenerationOutputDTO;
type UploadMediaInput = UploadMediaInputDTO;
type UploadMediaOutput = UploadMediaOutputDTO;
type GetMediaOutput = GetMediaOutputDTO;
type DeleteMediaOutput = DeleteMediaOutputDTO;
type GetAvatarOutput = GetAvatarOutputDTO;
type UpdateAvatarInput = UpdateAvatarInputDTO;
type UpdateAvatarOutput = UpdateAvatarOutputDTO;
@ApiTags('media')
@Controller('media')
@@ -30,7 +24,7 @@ export class MediaController {
@Post('avatar/generate')
@ApiOperation({ summary: 'Request avatar generation' })
@ApiResponse({ status: 201, description: 'Avatar generation request submitted', type: RequestAvatarGenerationOutput })
@ApiResponse({ status: 201, description: 'Avatar generation request submitted', type: RequestAvatarGenerationOutputDTO })
async requestAvatarGeneration(
@Body() input: RequestAvatarGenerationInput,
@Res() res: Response,
@@ -47,7 +41,7 @@ export class MediaController {
@UseInterceptors(FileInterceptor('file'))
@ApiOperation({ summary: 'Upload media file' })
@ApiConsumes('multipart/form-data')
@ApiResponse({ status: 201, description: 'Media uploaded successfully', type: UploadMediaOutput })
@ApiResponse({ status: 201, description: 'Media uploaded successfully', type: UploadMediaOutputDTO })
async uploadMedia(
@UploadedFile() file: Express.Multer.File,
@Body() input: UploadMediaInput,
@@ -64,7 +58,7 @@ export class MediaController {
@Get(':mediaId')
@ApiOperation({ summary: 'Get media by ID' })
@ApiParam({ name: 'mediaId', description: 'Media ID' })
@ApiResponse({ status: 200, description: 'Media details', type: GetMediaOutput })
@ApiResponse({ status: 200, description: 'Media details', type: GetMediaOutputDTO })
async getMedia(
@Param('mediaId') mediaId: string,
@Res() res: Response,
@@ -80,7 +74,7 @@ export class MediaController {
@Delete(':mediaId')
@ApiOperation({ summary: 'Delete media by ID' })
@ApiParam({ name: 'mediaId', description: 'Media ID' })
@ApiResponse({ status: 200, description: 'Media deleted', type: DeleteMediaOutput })
@ApiResponse({ status: 200, description: 'Media deleted', type: DeleteMediaOutputDTO })
async deleteMedia(
@Param('mediaId') mediaId: string,
@Res() res: Response,
@@ -92,7 +86,7 @@ export class MediaController {
@Get('avatar/:driverId')
@ApiOperation({ summary: 'Get avatar for driver' })
@ApiParam({ name: 'driverId', description: 'Driver ID' })
@ApiResponse({ status: 200, description: 'Avatar details', type: GetAvatarOutput })
@ApiResponse({ status: 200, description: 'Avatar details', type: GetAvatarOutputDTO })
async getAvatar(
@Param('driverId') driverId: string,
@Res() res: Response,
@@ -108,7 +102,7 @@ export class MediaController {
@Put('avatar/:driverId')
@ApiOperation({ summary: 'Update avatar for driver' })
@ApiParam({ name: 'driverId', description: 'Driver ID' })
@ApiResponse({ status: 200, description: 'Avatar updated', type: UpdateAvatarOutput })
@ApiResponse({ status: 200, description: 'Avatar updated', type: UpdateAvatarOutputDTO })
async updateAvatar(
@Param('driverId') driverId: string,
@Body() input: UpdateAvatarInput,

View File

@@ -0,0 +1,30 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MediaModule } from './MediaModule';
import { MediaController } from './MediaController';
import { MediaService } from './MediaService';
describe('MediaModule', () => {
let module: TestingModule;
beforeEach(async () => {
module = await Test.createTestingModule({
imports: [MediaModule],
}).compile();
});
it('should compile the module', () => {
expect(module).toBeDefined();
});
it('should provide MediaController', () => {
const controller = module.get<MediaController>(MediaController);
expect(controller).toBeDefined();
expect(controller).toBeInstanceOf(MediaController);
});
it('should provide MediaService', () => {
const service = module.get<MediaService>(MediaService);
expect(service).toBeDefined();
expect(service).toBeInstanceOf(MediaService);
});
});

View File

@@ -3,39 +3,82 @@ import { MediaService } from './MediaService';
// Import core interfaces
import { IAvatarGenerationRepository } from '@core/media/domain/repositories/IAvatarGenerationRepository';
import { IMediaRepository } from '@core/media/domain/repositories/IMediaRepository';
import { IAvatarRepository } from '@core/media/domain/repositories/IAvatarRepository';
import { FaceValidationPort } from '@core/media/application/ports/FaceValidationPort';
import { AvatarGenerationPort } from '@core/media/application/ports/AvatarGenerationPort';
import { MediaStoragePort } from '@core/media/application/ports/MediaStoragePort';
import type { Logger } from '@core/shared/application';
// Import use cases
import { RequestAvatarGenerationUseCase } from '@core/media/application/use-cases/RequestAvatarGenerationUseCase';
import { UploadMediaUseCase } from '@core/media/application/use-cases/UploadMediaUseCase';
import { GetMediaUseCase } from '@core/media/application/use-cases/GetMediaUseCase';
import { DeleteMediaUseCase } from '@core/media/application/use-cases/DeleteMediaUseCase';
import { GetAvatarUseCase } from '@core/media/application/use-cases/GetAvatarUseCase';
import { UpdateAvatarUseCase } from '@core/media/application/use-cases/UpdateAvatarUseCase';
// Define injection tokens
export const AVATAR_GENERATION_REPOSITORY_TOKEN = 'IAvatarGenerationRepository';
export const MEDIA_REPOSITORY_TOKEN = 'IMediaRepository';
export const AVATAR_REPOSITORY_TOKEN = 'IAvatarRepository';
export const FACE_VALIDATION_PORT_TOKEN = 'FaceValidationPort';
export const AVATAR_GENERATION_PORT_TOKEN = 'AvatarGenerationPort';
export const MEDIA_STORAGE_PORT_TOKEN = 'MediaStoragePort';
export const LOGGER_TOKEN = 'Logger';
// Use case tokens
export const REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN = 'RequestAvatarGenerationUseCase';
export const UPLOAD_MEDIA_USE_CASE_TOKEN = 'UploadMediaUseCase';
export const GET_MEDIA_USE_CASE_TOKEN = 'GetMediaUseCase';
export const DELETE_MEDIA_USE_CASE_TOKEN = 'DeleteMediaUseCase';
export const GET_AVATAR_USE_CASE_TOKEN = 'GetAvatarUseCase';
export const UPDATE_AVATAR_USE_CASE_TOKEN = 'UpdateAvatarUseCase';
import type { AvatarGenerationRequest } from '@core/media/domain/entities/AvatarGenerationRequest';
import type { Media } from '@core/media/domain/entities/Media';
import type { Avatar } from '@core/media/domain/entities/Avatar';
import type { FaceValidationResult } from '@core/media/application/ports/FaceValidationPort';
import type { AvatarGenerationResult } from '@core/media/application/ports/AvatarGenerationPort';
import type { UploadResult } from '@core/media/application/ports/MediaStoragePort';
// Mock implementations
class MockAvatarGenerationRepository implements IAvatarGenerationRepository {
async save(_request: any): Promise<void> {}
async findById(_id: string): Promise<any | null> { return null; }
async findByUserId(_userId: string): Promise<any[]> { return []; }
async findLatestByUserId(_userId: string): Promise<any | null> { return null; }
async delete(_id: string): Promise<void> {}
async save(): Promise<void> {}
async findById(): Promise<AvatarGenerationRequest | null> { return null; }
async findByUserId(): Promise<AvatarGenerationRequest[]> { return []; }
async findLatestByUserId(): Promise<AvatarGenerationRequest | null> { return null; }
async delete(): Promise<void> {}
}
class MockMediaRepository implements IMediaRepository {
async save(): Promise<void> {}
async findById(): Promise<Media | null> { return null; }
async findByUploadedBy(): Promise<Media[]> { return []; }
async delete(): Promise<void> {}
}
class MockAvatarRepository implements IAvatarRepository {
async save(): Promise<void> {}
async findById(): Promise<Avatar | null> { return null; }
async findActiveByDriverId(): Promise<Avatar | null> { return null; }
async findByDriverId(): Promise<Avatar[]> { return []; }
async delete(): Promise<void> {}
}
class MockFaceValidationAdapter implements FaceValidationPort {
async validateFacePhoto(data: string): Promise<any> {
return { isValid: true, hasFace: true, faceCount: 1 };
async validateFacePhoto(): Promise<FaceValidationResult> {
return {
isValid: true,
hasFace: true,
faceCount: 1,
confidence: 0.95,
};
}
}
class MockAvatarGenerationAdapter implements AvatarGenerationPort {
async generateAvatars(options: any): Promise<any> {
async generateAvatars(): Promise<AvatarGenerationResult> {
return {
success: true,
avatars: [
@@ -47,11 +90,22 @@ class MockAvatarGenerationAdapter implements AvatarGenerationPort {
}
}
class MockMediaStorageAdapter implements MediaStoragePort {
async uploadMedia(): Promise<UploadResult> {
return {
success: true,
url: 'https://cdn.example.com/media/mock-file.png',
filename: 'mock-file.png',
};
}
async deleteMedia(): Promise<void> {}
}
class MockLogger implements Logger {
debug(message: string, meta?: any): void {}
info(message: string, meta?: any): void {}
warn(message: string, meta?: any): void {}
error(message: string, error?: Error): void {}
debug(): void {}
info(): void {}
warn(): void {}
error(): void {}
}
export const MediaProviders: Provider[] = [
@@ -60,6 +114,14 @@ export const MediaProviders: Provider[] = [
provide: AVATAR_GENERATION_REPOSITORY_TOKEN,
useClass: MockAvatarGenerationRepository,
},
{
provide: MEDIA_REPOSITORY_TOKEN,
useClass: MockMediaRepository,
},
{
provide: AVATAR_REPOSITORY_TOKEN,
useClass: MockAvatarRepository,
},
{
provide: FACE_VALIDATION_PORT_TOKEN,
useClass: MockFaceValidationAdapter,
@@ -68,6 +130,10 @@ export const MediaProviders: Provider[] = [
provide: AVATAR_GENERATION_PORT_TOKEN,
useClass: MockAvatarGenerationAdapter,
},
{
provide: MEDIA_STORAGE_PORT_TOKEN,
useClass: MockMediaStorageAdapter,
},
{
provide: LOGGER_TOKEN,
useClass: MockLogger,
@@ -79,4 +145,34 @@ export const MediaProviders: Provider[] = [
new RequestAvatarGenerationUseCase(avatarRepo, faceValidation, avatarGeneration, logger),
inject: [AVATAR_GENERATION_REPOSITORY_TOKEN, FACE_VALIDATION_PORT_TOKEN, AVATAR_GENERATION_PORT_TOKEN, LOGGER_TOKEN],
},
{
provide: UPLOAD_MEDIA_USE_CASE_TOKEN,
useFactory: (mediaRepo: IMediaRepository, mediaStorage: MediaStoragePort, logger: Logger) =>
new UploadMediaUseCase(mediaRepo, mediaStorage, logger),
inject: [MEDIA_REPOSITORY_TOKEN, MEDIA_STORAGE_PORT_TOKEN, LOGGER_TOKEN],
},
{
provide: GET_MEDIA_USE_CASE_TOKEN,
useFactory: (mediaRepo: IMediaRepository, logger: Logger) =>
new GetMediaUseCase(mediaRepo, logger),
inject: [MEDIA_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
provide: DELETE_MEDIA_USE_CASE_TOKEN,
useFactory: (mediaRepo: IMediaRepository, mediaStorage: MediaStoragePort, logger: Logger) =>
new DeleteMediaUseCase(mediaRepo, mediaStorage, logger),
inject: [MEDIA_REPOSITORY_TOKEN, MEDIA_STORAGE_PORT_TOKEN, LOGGER_TOKEN],
},
{
provide: GET_AVATAR_USE_CASE_TOKEN,
useFactory: (avatarRepo: IAvatarRepository, logger: Logger) =>
new GetAvatarUseCase(avatarRepo, logger),
inject: [AVATAR_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
provide: UPDATE_AVATAR_USE_CASE_TOKEN,
useFactory: (avatarRepo: IAvatarRepository, logger: Logger) =>
new UpdateAvatarUseCase(avatarRepo, logger),
inject: [AVATAR_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
];

View File

@@ -8,6 +8,7 @@ import type { DeleteMediaOutputDTO } from './dtos/DeleteMediaOutputDTO';
import type { GetAvatarOutputDTO } from './dtos/GetAvatarOutputDTO';
import type { UpdateAvatarInputDTO } from './dtos/UpdateAvatarInputDTO';
import type { UpdateAvatarOutputDTO } from './dtos/UpdateAvatarOutputDTO';
import type { RacingSuitColor } from '@core/media/domain/types/AvatarGenerationRequest';
type RequestAvatarGenerationInput = RequestAvatarGenerationInputDTO;
type RequestAvatarGenerationOutput = RequestAvatarGenerationOutputDTO;
@@ -21,18 +22,41 @@ type UpdateAvatarOutput = UpdateAvatarOutputDTO;
// Use cases
import { RequestAvatarGenerationUseCase } from '@core/media/application/use-cases/RequestAvatarGenerationUseCase';
import { UploadMediaUseCase } from '@core/media/application/use-cases/UploadMediaUseCase';
import { GetMediaUseCase } from '@core/media/application/use-cases/GetMediaUseCase';
import { DeleteMediaUseCase } from '@core/media/application/use-cases/DeleteMediaUseCase';
import { GetAvatarUseCase } from '@core/media/application/use-cases/GetAvatarUseCase';
import { UpdateAvatarUseCase } from '@core/media/application/use-cases/UpdateAvatarUseCase';
// Presenters
import { RequestAvatarGenerationPresenter } from './presenters/RequestAvatarGenerationPresenter';
import { UploadMediaPresenter } from './presenters/UploadMediaPresenter';
import { GetMediaPresenter } from './presenters/GetMediaPresenter';
import { DeleteMediaPresenter } from './presenters/DeleteMediaPresenter';
import { GetAvatarPresenter } from './presenters/GetAvatarPresenter';
import { UpdateAvatarPresenter } from './presenters/UpdateAvatarPresenter';
// Tokens
import { REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN, LOGGER_TOKEN } from './MediaProviders';
import {
REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN,
UPLOAD_MEDIA_USE_CASE_TOKEN,
GET_MEDIA_USE_CASE_TOKEN,
DELETE_MEDIA_USE_CASE_TOKEN,
GET_AVATAR_USE_CASE_TOKEN,
UPDATE_AVATAR_USE_CASE_TOKEN,
LOGGER_TOKEN
} from './MediaProviders';
import type { Logger } from '@core/shared/application';
@Injectable()
export class MediaService {
constructor(
@Inject(REQUEST_AVATAR_GENERATION_USE_CASE_TOKEN) private readonly requestAvatarGenerationUseCase: RequestAvatarGenerationUseCase,
@Inject(UPLOAD_MEDIA_USE_CASE_TOKEN) private readonly uploadMediaUseCase: UploadMediaUseCase,
@Inject(GET_MEDIA_USE_CASE_TOKEN) private readonly getMediaUseCase: GetMediaUseCase,
@Inject(DELETE_MEDIA_USE_CASE_TOKEN) private readonly deleteMediaUseCase: DeleteMediaUseCase,
@Inject(GET_AVATAR_USE_CASE_TOKEN) private readonly getAvatarUseCase: GetAvatarUseCase,
@Inject(UPDATE_AVATAR_USE_CASE_TOKEN) private readonly updateAvatarUseCase: UpdateAvatarUseCase,
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
) {}
@@ -43,46 +67,118 @@ export class MediaService {
await this.requestAvatarGenerationUseCase.execute({
userId: input.userId,
facePhotoData: input.facePhotoData,
suitColor: input.suitColor as any,
suitColor: input.suitColor as RacingSuitColor,
}, presenter);
return presenter.viewModel;
}
async uploadMedia(input: UploadMediaInput & { file: Express.Multer.File }): Promise<UploadMediaOutput> {
this.logger.debug('[MediaService] Uploading media.');
// TODO: Implement media upload logic
return {
success: true,
mediaId: 'placeholder-media-id',
url: 'placeholder-url',
};
const presenter = new UploadMediaPresenter();
await this.uploadMediaUseCase.execute({
file: input.file,
uploadedBy: input.userId, // Assuming userId is the uploader
metadata: input.metadata,
}, presenter);
const result = presenter.viewModel;
if (result.success) {
return {
success: true,
mediaId: result.mediaId!,
url: result.url!,
};
} else {
return {
success: false,
errorMessage: result.errorMessage || 'Upload failed',
};
}
}
async getMedia(mediaId: string): Promise<GetMediaOutput | null> {
this.logger.debug(`[MediaService] Getting media: ${mediaId}`);
// TODO: Implement get media logic
const presenter = new GetMediaPresenter();
await this.getMediaUseCase.execute({ mediaId }, presenter);
const result = presenter.viewModel;
if (result.success && result.media) {
return {
success: true,
mediaId: result.media.id,
filename: result.media.filename,
originalName: result.media.originalName,
mimeType: result.media.mimeType,
size: result.media.size,
url: result.media.url,
type: result.media.type,
uploadedBy: result.media.uploadedBy,
uploadedAt: result.media.uploadedAt,
metadata: result.media.metadata,
};
}
return null;
}
async deleteMedia(mediaId: string): Promise<DeleteMediaOutput> {
this.logger.debug(`[MediaService] Deleting media: ${mediaId}`);
// TODO: Implement delete media logic
const presenter = new DeleteMediaPresenter();
await this.deleteMediaUseCase.execute({ mediaId }, presenter);
const result = presenter.viewModel;
return {
success: true,
success: result.success,
errorMessage: result.errorMessage,
};
}
async getAvatar(driverId: string): Promise<GetAvatarOutput | null> {
this.logger.debug(`[MediaService] Getting avatar for driver: ${driverId}`);
// TODO: Implement get avatar logic
const presenter = new GetAvatarPresenter();
await this.getAvatarUseCase.execute({ driverId }, presenter);
const result = presenter.viewModel;
if (result.success && result.avatar) {
return {
success: true,
avatarId: result.avatar.id,
driverId: result.avatar.driverId,
mediaUrl: result.avatar.mediaUrl,
selectedAt: result.avatar.selectedAt,
};
}
return null;
}
async updateAvatar(driverId: string, input: UpdateAvatarInput): Promise<UpdateAvatarOutput> {
this.logger.debug(`[MediaService] Updating avatar for driver: ${driverId}`);
// TODO: Implement update avatar logic
const presenter = new UpdateAvatarPresenter();
await this.updateAvatarUseCase.execute({
driverId,
mediaUrl: input.mediaUrl,
}, presenter);
const result = presenter.viewModel;
return {
success: true,
success: result.success,
errorMessage: result.errorMessage,
};
}
}

View File

@@ -3,7 +3,7 @@ import { IsString, IsOptional } from 'class-validator';
export class UploadMediaInputDTO {
@ApiProperty({ type: 'string', format: 'binary' })
file: any; // File upload handled by multer
file: Express.Multer.File;
@ApiProperty()
@IsString()

View File

@@ -0,0 +1,14 @@
import type { IDeleteMediaPresenter, DeleteMediaResult } from '@core/media/application/presenters/IDeleteMediaPresenter';
export class DeleteMediaPresenter implements IDeleteMediaPresenter {
private result: DeleteMediaResult | null = null;
present(result: DeleteMediaResult) {
this.result = result;
}
get viewModel(): DeleteMediaResult {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,14 @@
import type { IGetAvatarPresenter, GetAvatarResult } from '@core/media/application/presenters/IGetAvatarPresenter';
export class GetAvatarPresenter implements IGetAvatarPresenter {
private result: GetAvatarResult | null = null;
present(result: GetAvatarResult) {
this.result = result;
}
get viewModel(): GetAvatarResult {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,14 @@
import type { IGetMediaPresenter, GetMediaResult } from '@core/media/application/presenters/IGetMediaPresenter';
export class GetMediaPresenter implements IGetMediaPresenter {
private result: GetMediaResult | null = null;
present(result: GetMediaResult) {
this.result = result;
}
get viewModel(): GetMediaResult {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -1,6 +1,8 @@
import { RequestAvatarGenerationOutput } from '../dto/MediaDto';
import type { RequestAvatarGenerationOutputDTO } from '../dtos/RequestAvatarGenerationOutputDTO';
import type { IRequestAvatarGenerationPresenter, RequestAvatarGenerationResultDTO } from '@core/media/application/presenters/IRequestAvatarGenerationPresenter';
type RequestAvatarGenerationOutput = RequestAvatarGenerationOutputDTO;
export class RequestAvatarGenerationPresenter implements IRequestAvatarGenerationPresenter {
private result: RequestAvatarGenerationOutput | null = null;

View File

@@ -0,0 +1,14 @@
import type { IUpdateAvatarPresenter, UpdateAvatarResult } from '@core/media/application/presenters/IUpdateAvatarPresenter';
export class UpdateAvatarPresenter implements IUpdateAvatarPresenter {
private result: UpdateAvatarResult | null = null;
present(result: UpdateAvatarResult) {
this.result = result;
}
get viewModel(): UpdateAvatarResult {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,14 @@
import type { IUploadMediaPresenter, UploadMediaResult } from '@core/media/application/presenters/IUploadMediaPresenter';
export class UploadMediaPresenter implements IUploadMediaPresenter {
private result: UploadMediaResult | null = null;
present(result: UploadMediaResult) {
this.result = result;
}
get viewModel(): UploadMediaResult {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}