refactor use cases
This commit is contained in:
@@ -2,21 +2,15 @@ import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import {
|
||||
DeleteMediaUseCase,
|
||||
type DeleteMediaInput,
|
||||
type DeleteMediaResult,
|
||||
type DeleteMediaErrorCode,
|
||||
} from './DeleteMediaUseCase';
|
||||
import type { IMediaRepository } from '../../domain/repositories/IMediaRepository';
|
||||
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { Media } from '../../domain/entities/Media';
|
||||
|
||||
interface TestOutputPort extends UseCaseOutputPort<DeleteMediaResult> {
|
||||
present: Mock;
|
||||
result?: DeleteMediaResult;
|
||||
}
|
||||
|
||||
describe('DeleteMediaUseCase', () => {
|
||||
let mediaRepo: {
|
||||
findById: Mock;
|
||||
@@ -26,7 +20,6 @@ describe('DeleteMediaUseCase', () => {
|
||||
deleteMedia: Mock;
|
||||
};
|
||||
let logger: Logger;
|
||||
let output: TestOutputPort;
|
||||
let useCase: DeleteMediaUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -46,16 +39,9 @@ describe('DeleteMediaUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
output = {
|
||||
present: vi.fn((result: DeleteMediaResult) => {
|
||||
output.result = result;
|
||||
}),
|
||||
} as unknown as TestOutputPort;
|
||||
|
||||
useCase = new DeleteMediaUseCase(
|
||||
mediaRepo as unknown as IMediaRepository,
|
||||
mediaStorage as unknown as MediaStoragePort,
|
||||
output,
|
||||
logger,
|
||||
);
|
||||
});
|
||||
@@ -74,10 +60,9 @@ describe('DeleteMediaUseCase', () => {
|
||||
{ message: string }
|
||||
>;
|
||||
expect(err.code).toBe('MEDIA_NOT_FOUND');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('deletes media from storage and repository on success', async () => {
|
||||
it('returns DeleteMediaResult on success', async () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'file.png',
|
||||
@@ -98,7 +83,9 @@ describe('DeleteMediaUseCase', () => {
|
||||
expect(mediaRepo.findById).toHaveBeenCalledWith('media-1');
|
||||
expect(mediaStorage.deleteMedia).toHaveBeenCalledWith(media.url.value);
|
||||
expect(mediaRepo.delete).toHaveBeenCalledWith('media-1');
|
||||
expect(output.present).toHaveBeenCalledWith({
|
||||
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({
|
||||
mediaId: 'media-1',
|
||||
deleted: true,
|
||||
});
|
||||
@@ -117,6 +104,5 @@ describe('DeleteMediaUseCase', () => {
|
||||
{ message: string }
|
||||
>;
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type { IMediaRepository } from '../../domain/repositories/IMediaRepository';
|
||||
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
|
||||
@@ -30,11 +30,10 @@ export class DeleteMediaUseCase {
|
||||
constructor(
|
||||
private readonly mediaRepo: IMediaRepository,
|
||||
private readonly mediaStorage: MediaStoragePort,
|
||||
private readonly output: UseCaseOutputPort<DeleteMediaResult>,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(input: DeleteMediaInput): Promise<Result<void, DeleteMediaApplicationError>> {
|
||||
async execute(input: DeleteMediaInput): Promise<Result<DeleteMediaResult, DeleteMediaApplicationError>> {
|
||||
this.logger.info('[DeleteMediaUseCase] Deleting media', {
|
||||
mediaId: input.mediaId,
|
||||
});
|
||||
@@ -43,7 +42,7 @@ export class DeleteMediaUseCase {
|
||||
const media = await this.mediaRepo.findById(input.mediaId);
|
||||
|
||||
if (!media) {
|
||||
return Result.err<void, DeleteMediaApplicationError>({
|
||||
return Result.err<DeleteMediaResult, DeleteMediaApplicationError>({
|
||||
code: 'MEDIA_NOT_FOUND',
|
||||
details: { message: 'Media not found' },
|
||||
});
|
||||
@@ -52,16 +51,14 @@ export class DeleteMediaUseCase {
|
||||
await this.mediaStorage.deleteMedia(media.url.value);
|
||||
await this.mediaRepo.delete(input.mediaId);
|
||||
|
||||
this.output.present({
|
||||
mediaId: input.mediaId,
|
||||
deleted: true,
|
||||
});
|
||||
|
||||
this.logger.info('[DeleteMediaUseCase] Media deleted successfully', {
|
||||
mediaId: input.mediaId,
|
||||
});
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok({
|
||||
mediaId: input.mediaId,
|
||||
deleted: true,
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
@@ -69,7 +66,7 @@ export class DeleteMediaUseCase {
|
||||
mediaId: input.mediaId,
|
||||
});
|
||||
|
||||
return Result.err<void, DeleteMediaApplicationError>({
|
||||
return Result.err<DeleteMediaResult, DeleteMediaApplicationError>({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: err.message || 'Unexpected repository error' },
|
||||
});
|
||||
|
||||
@@ -2,27 +2,20 @@ import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import {
|
||||
GetAvatarUseCase,
|
||||
type GetAvatarInput,
|
||||
type GetAvatarResult,
|
||||
type GetAvatarErrorCode,
|
||||
} from './GetAvatarUseCase';
|
||||
import type { IAvatarRepository } from '../../domain/repositories/IAvatarRepository';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { Avatar } from '../../domain/entities/Avatar';
|
||||
|
||||
interface TestOutputPort extends UseCaseOutputPort<GetAvatarResult> {
|
||||
present: Mock;
|
||||
result?: GetAvatarResult;
|
||||
}
|
||||
|
||||
describe('GetAvatarUseCase', () => {
|
||||
let avatarRepo: {
|
||||
findActiveByDriverId: Mock;
|
||||
save: Mock;
|
||||
};
|
||||
let logger: Logger;
|
||||
let output: TestOutputPort;
|
||||
let useCase: GetAvatarUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -38,15 +31,8 @@ describe('GetAvatarUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
output = {
|
||||
present: vi.fn((result: GetAvatarResult) => {
|
||||
output.result = result;
|
||||
}),
|
||||
} as unknown as TestOutputPort;
|
||||
|
||||
useCase = new GetAvatarUseCase(
|
||||
avatarRepo as unknown as IAvatarRepository,
|
||||
output,
|
||||
logger,
|
||||
);
|
||||
});
|
||||
@@ -65,10 +51,9 @@ describe('GetAvatarUseCase', () => {
|
||||
{ message: string }
|
||||
>;
|
||||
expect(err.code).toBe('AVATAR_NOT_FOUND');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('presents avatar details when avatar exists', async () => {
|
||||
it('returns GetAvatarResult when avatar exists', async () => {
|
||||
const avatar = Avatar.create({
|
||||
id: 'avatar-1',
|
||||
driverId: 'driver-1',
|
||||
@@ -82,7 +67,9 @@ describe('GetAvatarUseCase', () => {
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(avatarRepo.findActiveByDriverId).toHaveBeenCalledWith('driver-1');
|
||||
expect(output.present).toHaveBeenCalledWith({
|
||||
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({
|
||||
avatar: {
|
||||
id: avatar.id,
|
||||
driverId: avatar.driverId,
|
||||
@@ -105,6 +92,5 @@ describe('GetAvatarUseCase', () => {
|
||||
{ message: string }
|
||||
>;
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import type { IAvatarRepository } from '../../domain/repositories/IAvatarRepository';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
|
||||
@@ -32,11 +32,10 @@ export type GetAvatarApplicationError = ApplicationErrorCode<
|
||||
export class GetAvatarUseCase {
|
||||
constructor(
|
||||
private readonly avatarRepo: IAvatarRepository,
|
||||
private readonly output: UseCaseOutputPort<GetAvatarResult>,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(input: GetAvatarInput): Promise<Result<void, GetAvatarApplicationError>> {
|
||||
async execute(input: GetAvatarInput): Promise<Result<GetAvatarResult, GetAvatarApplicationError>> {
|
||||
this.logger.info('[GetAvatarUseCase] Getting avatar', {
|
||||
driverId: input.driverId,
|
||||
});
|
||||
@@ -45,13 +44,13 @@ export class GetAvatarUseCase {
|
||||
const avatar = await this.avatarRepo.findActiveByDriverId(input.driverId);
|
||||
|
||||
if (!avatar) {
|
||||
return Result.err({
|
||||
return Result.err<GetAvatarResult, GetAvatarApplicationError>({
|
||||
code: 'AVATAR_NOT_FOUND',
|
||||
details: { message: 'Avatar not found' },
|
||||
});
|
||||
}
|
||||
|
||||
this.output.present({
|
||||
return Result.ok({
|
||||
avatar: {
|
||||
id: avatar.id,
|
||||
driverId: avatar.driverId,
|
||||
@@ -59,8 +58,6 @@ export class GetAvatarUseCase {
|
||||
selectedAt: avatar.selectedAt,
|
||||
},
|
||||
});
|
||||
|
||||
return Result.ok(undefined);
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
@@ -68,7 +65,7 @@ export class GetAvatarUseCase {
|
||||
driverId: input.driverId,
|
||||
});
|
||||
|
||||
return Result.err({
|
||||
return Result.err<GetAvatarResult, GetAvatarApplicationError>({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: err.message ?? 'Unexpected repository error' },
|
||||
});
|
||||
|
||||
@@ -2,26 +2,19 @@ import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import {
|
||||
GetMediaUseCase,
|
||||
type GetMediaInput,
|
||||
type GetMediaResult,
|
||||
type GetMediaErrorCode,
|
||||
} from './GetMediaUseCase';
|
||||
import type { IMediaRepository } from '../../domain/repositories/IMediaRepository';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { Media } from '../../domain/entities/Media';
|
||||
|
||||
interface TestOutputPort extends UseCaseOutputPort<GetMediaResult> {
|
||||
present: Mock;
|
||||
result?: GetMediaResult;
|
||||
}
|
||||
|
||||
describe('GetMediaUseCase', () => {
|
||||
let mediaRepo: {
|
||||
findById: Mock;
|
||||
};
|
||||
let logger: Logger;
|
||||
let output: TestOutputPort;
|
||||
let useCase: GetMediaUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -36,15 +29,8 @@ describe('GetMediaUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
output = {
|
||||
present: vi.fn((result: GetMediaResult) => {
|
||||
output.result = result;
|
||||
}),
|
||||
} as unknown as TestOutputPort;
|
||||
|
||||
useCase = new GetMediaUseCase(
|
||||
mediaRepo as unknown as IMediaRepository,
|
||||
output,
|
||||
logger,
|
||||
);
|
||||
});
|
||||
@@ -60,10 +46,9 @@ describe('GetMediaUseCase', () => {
|
||||
expect(result.isErr()).toBe(true);
|
||||
const err = result.unwrapErr() as ApplicationErrorCode<GetMediaErrorCode, { message: string }>;
|
||||
expect(err.code).toBe('MEDIA_NOT_FOUND');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('presents media details when media exists', async () => {
|
||||
it('returns GetMediaResult when media exists', async () => {
|
||||
const media = Media.create({
|
||||
id: 'media-1',
|
||||
filename: 'file.png',
|
||||
@@ -82,7 +67,9 @@ describe('GetMediaUseCase', () => {
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mediaRepo.findById).toHaveBeenCalledWith('media-1');
|
||||
expect(output.present).toHaveBeenCalledWith({
|
||||
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({
|
||||
media: {
|
||||
id: media.id,
|
||||
filename: media.filename,
|
||||
@@ -109,4 +96,4 @@ describe('GetMediaUseCase', () => {
|
||||
const err = result.unwrapErr() as ApplicationErrorCode<GetMediaErrorCode, { message: string }>;
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import type { IMediaRepository } from '../../domain/repositories/IMediaRepository';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
|
||||
@@ -33,13 +33,12 @@ export type GetMediaErrorCode = 'MEDIA_NOT_FOUND' | 'REPOSITORY_ERROR';
|
||||
export class GetMediaUseCase {
|
||||
constructor(
|
||||
private readonly mediaRepo: IMediaRepository,
|
||||
private readonly output: UseCaseOutputPort<GetMediaResult>,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
input: GetMediaInput,
|
||||
): Promise<Result<void, ApplicationErrorCode<GetMediaErrorCode, { message: string }>>> {
|
||||
): Promise<Result<GetMediaResult, ApplicationErrorCode<GetMediaErrorCode, { message: string }>>> {
|
||||
this.logger.info('[GetMediaUseCase] Getting media', {
|
||||
mediaId: input.mediaId,
|
||||
});
|
||||
@@ -48,7 +47,7 @@ export class GetMediaUseCase {
|
||||
const media = await this.mediaRepo.findById(input.mediaId);
|
||||
|
||||
if (!media) {
|
||||
return Result.err<void, ApplicationErrorCode<GetMediaErrorCode, { message: string }>>({
|
||||
return Result.err<GetMediaResult, ApplicationErrorCode<GetMediaErrorCode, { message: string }>>({
|
||||
code: 'MEDIA_NOT_FOUND',
|
||||
details: { message: 'Media not found' },
|
||||
});
|
||||
@@ -70,16 +69,14 @@ export class GetMediaUseCase {
|
||||
mediaResult.metadata = media.metadata;
|
||||
}
|
||||
|
||||
this.output.present({ media: mediaResult });
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok({ media: mediaResult });
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
this.logger.error('[GetMediaUseCase] Error getting media', err, {
|
||||
mediaId: input.mediaId,
|
||||
});
|
||||
|
||||
return Result.err<void, ApplicationErrorCode<GetMediaErrorCode, { message: string }>>({
|
||||
return Result.err<GetMediaResult, ApplicationErrorCode<GetMediaErrorCode, { message: string }>>({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: err.message },
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import {
|
||||
@@ -16,16 +16,10 @@ vi.mock('uuid', () => ({
|
||||
v4: () => 'request-1',
|
||||
}));
|
||||
|
||||
interface TestOutputPort extends UseCaseOutputPort<RequestAvatarGenerationResult> {
|
||||
present: Mock;
|
||||
result?: RequestAvatarGenerationResult;
|
||||
}
|
||||
|
||||
describe('RequestAvatarGenerationUseCase', () => {
|
||||
let avatarRepo: { save: Mock };
|
||||
let faceValidation: { validateFacePhoto: Mock };
|
||||
let avatarGeneration: { generateAvatars: Mock };
|
||||
let output: TestOutputPort;
|
||||
let logger: Logger;
|
||||
let useCase: RequestAvatarGenerationUseCase;
|
||||
|
||||
@@ -42,12 +36,6 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
generateAvatars: vi.fn(),
|
||||
};
|
||||
|
||||
output = {
|
||||
present: vi.fn((result: RequestAvatarGenerationResult) => {
|
||||
output.result = result;
|
||||
}),
|
||||
} as unknown as TestOutputPort;
|
||||
|
||||
logger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
@@ -59,12 +47,11 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
avatarRepo as unknown as IAvatarGenerationRepository,
|
||||
faceValidation as unknown as FaceValidationPort,
|
||||
avatarGeneration as unknown as AvatarGenerationPort,
|
||||
output,
|
||||
logger,
|
||||
);
|
||||
});
|
||||
|
||||
it('completes generation and presents avatar URLs', async () => {
|
||||
it('returns RequestAvatarGenerationResult on success', async () => {
|
||||
faceValidation.validateFacePhoto.mockResolvedValue({
|
||||
isValid: true,
|
||||
hasFace: true,
|
||||
@@ -92,7 +79,8 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
expect(avatarGeneration.generateAvatars).toHaveBeenCalled();
|
||||
expect(avatarRepo.save).toHaveBeenCalledTimes(4);
|
||||
|
||||
expect(output.present).toHaveBeenCalledWith({
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({
|
||||
requestId: 'request-1',
|
||||
status: 'completed',
|
||||
avatarUrls: ['https://example.com/a.png', 'https://example.com/b.png'],
|
||||
@@ -113,7 +101,7 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
suitColor: 'red' as unknown as RequestAvatarGenerationInput['suitColor'],
|
||||
};
|
||||
|
||||
const result: Result<void, ApplicationErrorCode<RequestAvatarGenerationErrorCode, { message: string }>> =
|
||||
const result: Result<RequestAvatarGenerationResult, ApplicationErrorCode<RequestAvatarGenerationErrorCode, { message: string }>> =
|
||||
await useCase.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
@@ -122,7 +110,6 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
expect(err.details?.message).toBe('Bad image');
|
||||
|
||||
expect(avatarGeneration.generateAvatars).not.toHaveBeenCalled();
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect(avatarRepo.save).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
@@ -145,7 +132,7 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
suitColor: 'red' as unknown as RequestAvatarGenerationInput['suitColor'],
|
||||
};
|
||||
|
||||
const result: Result<void, ApplicationErrorCode<RequestAvatarGenerationErrorCode, { message: string }>> =
|
||||
const result: Result<RequestAvatarGenerationResult, ApplicationErrorCode<RequestAvatarGenerationErrorCode, { message: string }>> =
|
||||
await useCase.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
@@ -153,7 +140,6 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
expect(err.code).toBe('GENERATION_FAILED');
|
||||
expect(err.details?.message).toBe('Generation service down');
|
||||
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect(avatarRepo.save).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
@@ -166,7 +152,7 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
suitColor: 'red' as unknown as RequestAvatarGenerationInput['suitColor'],
|
||||
};
|
||||
|
||||
const result: Result<void, ApplicationErrorCode<RequestAvatarGenerationErrorCode, { message: string }>> =
|
||||
const result: Result<RequestAvatarGenerationResult, ApplicationErrorCode<RequestAvatarGenerationErrorCode, { message: string }>> =
|
||||
await useCase.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
@@ -174,7 +160,6 @@ describe('RequestAvatarGenerationUseCase', () => {
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(err.details?.message).toBe('DB error');
|
||||
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect((logger.error as unknown as Mock)).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import type { IAvatarGenerationRepository } from '../../domain/repositories/IAvatarGenerationRepository';
|
||||
import type { FaceValidationPort } from '../ports/FaceValidationPort';
|
||||
import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { AvatarGenerationRequest } from '../../domain/entities/AvatarGenerationRequest';
|
||||
import type { RacingSuitColor } from '../../domain/types/AvatarGenerationRequest';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
@@ -42,13 +42,12 @@ export class RequestAvatarGenerationUseCase {
|
||||
private readonly avatarRepo: IAvatarGenerationRepository,
|
||||
private readonly faceValidation: FaceValidationPort,
|
||||
private readonly avatarGeneration: AvatarGenerationPort,
|
||||
private readonly output: UseCaseOutputPort<RequestAvatarGenerationResult>,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
input: RequestAvatarGenerationInput,
|
||||
): Promise<Result<void, RequestAvatarGenerationApplicationError>> {
|
||||
): Promise<Result<RequestAvatarGenerationResult, RequestAvatarGenerationApplicationError>> {
|
||||
this.logger.info('[RequestAvatarGenerationUseCase] Starting avatar generation request', {
|
||||
userId: input.userId,
|
||||
suitColor: input.suitColor,
|
||||
@@ -82,7 +81,7 @@ export class RequestAvatarGenerationUseCase {
|
||||
request.fail(errorMessage);
|
||||
await this.avatarRepo.save(request);
|
||||
|
||||
return Result.err<void, RequestAvatarGenerationApplicationError>({
|
||||
return Result.err<RequestAvatarGenerationResult, RequestAvatarGenerationApplicationError>({
|
||||
code: 'FACE_VALIDATION_FAILED',
|
||||
details: { message: errorMessage },
|
||||
});
|
||||
@@ -106,7 +105,7 @@ export class RequestAvatarGenerationUseCase {
|
||||
request.fail(errorMessage);
|
||||
await this.avatarRepo.save(request);
|
||||
|
||||
return Result.err<void, RequestAvatarGenerationApplicationError>({
|
||||
return Result.err<RequestAvatarGenerationResult, RequestAvatarGenerationApplicationError>({
|
||||
code: 'GENERATION_FAILED',
|
||||
details: { message: errorMessage },
|
||||
});
|
||||
@@ -116,19 +115,17 @@ export class RequestAvatarGenerationUseCase {
|
||||
request.completeWithAvatars(avatarUrls);
|
||||
await this.avatarRepo.save(request);
|
||||
|
||||
this.output.present({
|
||||
requestId,
|
||||
status: 'completed',
|
||||
avatarUrls,
|
||||
});
|
||||
|
||||
this.logger.info('[RequestAvatarGenerationUseCase] Avatar generation completed', {
|
||||
requestId,
|
||||
userId: input.userId,
|
||||
avatarCount: avatarUrls.length,
|
||||
});
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok({
|
||||
requestId,
|
||||
status: 'completed',
|
||||
avatarUrls,
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
@@ -136,7 +133,7 @@ export class RequestAvatarGenerationUseCase {
|
||||
userId: input.userId,
|
||||
});
|
||||
|
||||
return Result.err({
|
||||
return Result.err<RequestAvatarGenerationResult, RequestAvatarGenerationApplicationError>({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: err.message ?? 'Internal error occurred during avatar generation' },
|
||||
});
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import {
|
||||
SelectAvatarUseCase,
|
||||
type SelectAvatarErrorCode,
|
||||
type SelectAvatarInput,
|
||||
type SelectAvatarResult,
|
||||
} from './SelectAvatarUseCase';
|
||||
import type { IAvatarGenerationRepository } from '../../domain/repositories/IAvatarGenerationRepository';
|
||||
import { AvatarGenerationRequest } from '../../domain/entities/AvatarGenerationRequest';
|
||||
|
||||
interface TestOutputPort extends UseCaseOutputPort<SelectAvatarResult> {
|
||||
present: Mock;
|
||||
result?: SelectAvatarResult;
|
||||
}
|
||||
|
||||
describe('SelectAvatarUseCase', () => {
|
||||
let avatarRepo: { findById: Mock; save: Mock };
|
||||
let logger: Logger;
|
||||
let output: TestOutputPort;
|
||||
let useCase: SelectAvatarUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -35,15 +28,8 @@ describe('SelectAvatarUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
output = {
|
||||
present: vi.fn((result: SelectAvatarResult) => {
|
||||
output.result = result;
|
||||
}),
|
||||
} as unknown as TestOutputPort;
|
||||
|
||||
useCase = new SelectAvatarUseCase(
|
||||
avatarRepo as unknown as IAvatarGenerationRepository,
|
||||
output,
|
||||
logger,
|
||||
);
|
||||
});
|
||||
@@ -60,7 +46,6 @@ describe('SelectAvatarUseCase', () => {
|
||||
|
||||
const err = result.unwrapErr() as ApplicationErrorCode<SelectAvatarErrorCode, { message: string }>;
|
||||
expect(err.code).toBe('REQUEST_NOT_FOUND');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns REQUEST_NOT_COMPLETED when request is not completed', async () => {
|
||||
@@ -81,10 +66,9 @@ describe('SelectAvatarUseCase', () => {
|
||||
expect(err.code).toBe('REQUEST_NOT_COMPLETED');
|
||||
|
||||
expect(avatarRepo.save).not.toHaveBeenCalled();
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('selects avatar and presents selected URL when request is completed', async () => {
|
||||
it('returns SelectAvatarResult when request is completed', async () => {
|
||||
const request = AvatarGenerationRequest.create({
|
||||
id: 'req-1',
|
||||
userId: 'user-1',
|
||||
@@ -103,7 +87,9 @@ describe('SelectAvatarUseCase', () => {
|
||||
expect(request.selectedAvatarUrl).toBe('https://example.com/b.png');
|
||||
|
||||
expect(avatarRepo.save).toHaveBeenCalledWith(request);
|
||||
expect(output.present).toHaveBeenCalledWith({
|
||||
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({
|
||||
requestId: 'req-1',
|
||||
selectedAvatarUrl: 'https://example.com/b.png',
|
||||
});
|
||||
@@ -120,7 +106,6 @@ describe('SelectAvatarUseCase', () => {
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(err.details?.message).toBe('DB error');
|
||||
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect((logger.error as unknown as Mock)).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import type { IAvatarGenerationRepository } from '../../domain/repositories/IAvatarGenerationRepository';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
|
||||
@@ -32,11 +32,10 @@ export type SelectAvatarApplicationError = ApplicationErrorCode<
|
||||
export class SelectAvatarUseCase {
|
||||
constructor(
|
||||
private readonly avatarRepo: IAvatarGenerationRepository,
|
||||
private readonly output: UseCaseOutputPort<SelectAvatarResult>,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(input: SelectAvatarInput): Promise<Result<void, SelectAvatarApplicationError>> {
|
||||
async execute(input: SelectAvatarInput): Promise<Result<SelectAvatarResult, SelectAvatarApplicationError>> {
|
||||
this.logger.info('[SelectAvatarUseCase] Selecting avatar', {
|
||||
requestId: input.requestId,
|
||||
selectedIndex: input.selectedIndex,
|
||||
@@ -46,14 +45,14 @@ export class SelectAvatarUseCase {
|
||||
const request = await this.avatarRepo.findById(input.requestId);
|
||||
|
||||
if (!request) {
|
||||
return Result.err<void, SelectAvatarApplicationError>({
|
||||
return Result.err<SelectAvatarResult, SelectAvatarApplicationError>({
|
||||
code: 'REQUEST_NOT_FOUND',
|
||||
details: { message: 'Avatar generation request not found' },
|
||||
});
|
||||
}
|
||||
|
||||
if (request.status !== 'completed') {
|
||||
return Result.err<void, SelectAvatarApplicationError>({
|
||||
return Result.err<SelectAvatarResult, SelectAvatarApplicationError>({
|
||||
code: 'REQUEST_NOT_COMPLETED',
|
||||
details: { message: 'Avatar generation is not completed yet' },
|
||||
});
|
||||
@@ -64,17 +63,15 @@ export class SelectAvatarUseCase {
|
||||
|
||||
const selectedAvatarUrl = request.selectedAvatarUrl!;
|
||||
|
||||
this.output.present({
|
||||
requestId: input.requestId,
|
||||
selectedAvatarUrl,
|
||||
});
|
||||
|
||||
this.logger.info('[SelectAvatarUseCase] Avatar selected successfully', {
|
||||
requestId: input.requestId,
|
||||
selectedAvatarUrl,
|
||||
});
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok({
|
||||
requestId: input.requestId,
|
||||
selectedAvatarUrl,
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
@@ -82,7 +79,7 @@ export class SelectAvatarUseCase {
|
||||
requestId: input.requestId,
|
||||
});
|
||||
|
||||
return Result.err({
|
||||
return Result.err<SelectAvatarResult, SelectAvatarApplicationError>({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: err.message ?? 'Unexpected repository error' },
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import {
|
||||
@@ -15,15 +15,9 @@ vi.mock('uuid', () => ({
|
||||
v4: () => 'avatar-1',
|
||||
}));
|
||||
|
||||
interface TestOutputPort extends UseCaseOutputPort<UpdateAvatarResult> {
|
||||
present: Mock;
|
||||
result?: UpdateAvatarResult;
|
||||
}
|
||||
|
||||
describe('UpdateAvatarUseCase', () => {
|
||||
let avatarRepo: { findActiveByDriverId: Mock; save: Mock };
|
||||
let logger: Logger;
|
||||
let output: TestOutputPort;
|
||||
let useCase: UpdateAvatarUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -39,15 +33,8 @@ describe('UpdateAvatarUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
output = {
|
||||
present: vi.fn((result: UpdateAvatarResult) => {
|
||||
output.result = result;
|
||||
}),
|
||||
} as unknown as TestOutputPort;
|
||||
|
||||
useCase = new UpdateAvatarUseCase(
|
||||
avatarRepo as unknown as IAvatarRepository,
|
||||
output,
|
||||
logger,
|
||||
);
|
||||
});
|
||||
@@ -73,7 +60,8 @@ describe('UpdateAvatarUseCase', () => {
|
||||
expect(saved.mediaUrl.value).toBe('https://example.com/avatar.png');
|
||||
expect(saved.isActive).toBe(true);
|
||||
|
||||
expect(output.present).toHaveBeenCalledWith({ avatarId: 'avatar-1', driverId: 'driver-1' });
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({ avatarId: 'avatar-1', driverId: 'driver-1' });
|
||||
});
|
||||
|
||||
it('deactivates current avatar before saving new avatar', async () => {
|
||||
@@ -105,7 +93,8 @@ describe('UpdateAvatarUseCase', () => {
|
||||
expect(secondSaved.id).toBe('avatar-1');
|
||||
expect(secondSaved.isActive).toBe(true);
|
||||
|
||||
expect(output.present).toHaveBeenCalledWith({ avatarId: 'avatar-1', driverId: 'driver-1' });
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({ avatarId: 'avatar-1', driverId: 'driver-1' });
|
||||
});
|
||||
|
||||
it('returns REPOSITORY_ERROR when repository throws', async () => {
|
||||
@@ -116,7 +105,7 @@ describe('UpdateAvatarUseCase', () => {
|
||||
mediaUrl: 'https://example.com/avatar.png',
|
||||
};
|
||||
|
||||
const result: Result<void, ApplicationErrorCode<UpdateAvatarErrorCode, { message: string }>> =
|
||||
const result: Result<UpdateAvatarResult, ApplicationErrorCode<UpdateAvatarErrorCode, { message: string }>> =
|
||||
await useCase.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
@@ -124,7 +113,6 @@ describe('UpdateAvatarUseCase', () => {
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(err.details?.message).toBe('DB error');
|
||||
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect((logger.error as unknown as Mock)).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
* Handles the business logic for updating a driver's avatar.
|
||||
*/
|
||||
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
@@ -32,11 +32,10 @@ export type UpdateAvatarApplicationError = ApplicationErrorCode<
|
||||
export class UpdateAvatarUseCase {
|
||||
constructor(
|
||||
private readonly avatarRepo: IAvatarRepository,
|
||||
private readonly output: UseCaseOutputPort<UpdateAvatarResult>,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(input: UpdateAvatarInput): Promise<Result<void, UpdateAvatarApplicationError>> {
|
||||
async execute(input: UpdateAvatarInput): Promise<Result<UpdateAvatarResult, UpdateAvatarApplicationError>> {
|
||||
this.logger.info('[UpdateAvatarUseCase] Updating avatar', {
|
||||
driverId: input.driverId,
|
||||
mediaUrl: input.mediaUrl,
|
||||
@@ -58,17 +57,15 @@ export class UpdateAvatarUseCase {
|
||||
|
||||
await this.avatarRepo.save(newAvatar);
|
||||
|
||||
this.output.present({
|
||||
avatarId: avatarId.toString(),
|
||||
driverId: input.driverId,
|
||||
});
|
||||
|
||||
this.logger.info('[UpdateAvatarUseCase] Avatar updated successfully', {
|
||||
driverId: input.driverId,
|
||||
avatarId: avatarId.toString(),
|
||||
});
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok({
|
||||
avatarId: avatarId.toString(),
|
||||
driverId: input.driverId,
|
||||
});
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
|
||||
@@ -76,7 +73,7 @@ export class UpdateAvatarUseCase {
|
||||
driverId: input.driverId,
|
||||
});
|
||||
|
||||
return Result.err({
|
||||
return Result.err<UpdateAvatarResult, UpdateAvatarApplicationError>({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: err.message ?? 'Internal error occurred while updating avatar' },
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { Readable } from 'node:stream';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import {
|
||||
@@ -18,16 +18,10 @@ vi.mock('uuid', () => ({
|
||||
v4: () => 'media-1',
|
||||
}));
|
||||
|
||||
interface TestOutputPort extends UseCaseOutputPort<UploadMediaResult> {
|
||||
present: Mock;
|
||||
result?: UploadMediaResult;
|
||||
}
|
||||
|
||||
describe('UploadMediaUseCase', () => {
|
||||
let mediaRepo: { save: Mock };
|
||||
let mediaStorage: { uploadMedia: Mock };
|
||||
let logger: Logger;
|
||||
let output: TestOutputPort;
|
||||
let useCase: UploadMediaUseCase;
|
||||
|
||||
const baseFile: MulterFile = {
|
||||
@@ -59,16 +53,9 @@ describe('UploadMediaUseCase', () => {
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
output = {
|
||||
present: vi.fn((result: UploadMediaResult) => {
|
||||
output.result = result;
|
||||
}),
|
||||
} as unknown as TestOutputPort;
|
||||
|
||||
useCase = new UploadMediaUseCase(
|
||||
mediaRepo as unknown as IMediaRepository,
|
||||
mediaStorage as unknown as MediaStoragePort,
|
||||
output,
|
||||
logger,
|
||||
);
|
||||
});
|
||||
@@ -80,7 +67,7 @@ describe('UploadMediaUseCase', () => {
|
||||
});
|
||||
|
||||
const input: UploadMediaInput = { file: baseFile, uploadedBy: 'user-1' };
|
||||
const result: Result<void, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>> =
|
||||
const result: Result<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>> =
|
||||
await useCase.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
@@ -89,10 +76,9 @@ describe('UploadMediaUseCase', () => {
|
||||
expect(err.details?.message).toBe('Upload error');
|
||||
|
||||
expect(mediaRepo.save).not.toHaveBeenCalled();
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates media and presents mediaId/url on success (includes metadata)', async () => {
|
||||
it('returns UploadMediaResult on success (includes metadata)', async () => {
|
||||
mediaStorage.uploadMedia.mockResolvedValue({
|
||||
success: true,
|
||||
url: 'https://example.com/media.png',
|
||||
@@ -128,7 +114,8 @@ describe('UploadMediaUseCase', () => {
|
||||
expect(saved.uploadedBy).toBe('user-1');
|
||||
expect(saved.metadata).toEqual({ foo: 'bar' });
|
||||
|
||||
expect(output.present).toHaveBeenCalledWith({
|
||||
const successResult = result.unwrap();
|
||||
expect(successResult).toEqual({
|
||||
mediaId: 'media-1',
|
||||
url: 'https://example.com/media.png',
|
||||
});
|
||||
@@ -142,7 +129,7 @@ describe('UploadMediaUseCase', () => {
|
||||
mediaRepo.save.mockRejectedValue(new Error('DB error'));
|
||||
|
||||
const input: UploadMediaInput = { file: baseFile, uploadedBy: 'user-1' };
|
||||
const result: Result<void, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>> =
|
||||
const result: Result<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>> =
|
||||
await useCase.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
@@ -150,7 +137,6 @@ describe('UploadMediaUseCase', () => {
|
||||
expect(err.code).toBe('REPOSITORY_ERROR');
|
||||
expect(err.details?.message).toBe('DB error');
|
||||
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect((logger.error as unknown as Mock)).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type { IMediaRepository } from '../../domain/repositories/IMediaRepository';
|
||||
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { Result } from '@core/shared/application/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { Media } from '../../domain/entities/Media';
|
||||
@@ -45,13 +45,12 @@ export class UploadMediaUseCase {
|
||||
constructor(
|
||||
private readonly mediaRepo: IMediaRepository,
|
||||
private readonly mediaStorage: MediaStoragePort,
|
||||
private readonly output: UseCaseOutputPort<UploadMediaResult>,
|
||||
private readonly logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(
|
||||
input: UploadMediaInput,
|
||||
): Promise<Result<void, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>> {
|
||||
): Promise<Result<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>> {
|
||||
this.logger.info('[UploadMediaUseCase] Starting media upload', {
|
||||
filename: input.file.originalname,
|
||||
size: input.file.size,
|
||||
@@ -74,7 +73,7 @@ export class UploadMediaUseCase {
|
||||
const uploadResult = await this.mediaStorage.uploadMedia(input.file.buffer, uploadOptions);
|
||||
|
||||
if (!uploadResult.success || !uploadResult.url) {
|
||||
return Result.err<void, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>({
|
||||
return Result.err<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>({
|
||||
code: 'UPLOAD_FAILED',
|
||||
details: {
|
||||
message:
|
||||
@@ -116,21 +115,20 @@ export class UploadMediaUseCase {
|
||||
mediaId,
|
||||
url: uploadResult.url,
|
||||
};
|
||||
this.output.present(result);
|
||||
|
||||
this.logger.info('[UploadMediaUseCase] Media uploaded successfully', {
|
||||
mediaId,
|
||||
url: uploadResult.url,
|
||||
});
|
||||
|
||||
return Result.ok(undefined);
|
||||
return Result.ok(result);
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
this.logger.error('[UploadMediaUseCase] Error uploading media', err, {
|
||||
filename: input.file.originalname,
|
||||
});
|
||||
|
||||
return Result.err<void, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>({
|
||||
return Result.err<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>({
|
||||
code: 'REPOSITORY_ERROR',
|
||||
details: { message: err.message },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user