A social knowledge tool for researchers built on ATProto
at development 133 lines 4.3 kB view raw
1import { Result, ok, err } from '../../../../../shared/core/Result'; 2import { BaseUseCase } from '../../../../../shared/core/UseCase'; 3import { UseCaseError } from '../../../../../shared/core/UseCaseError'; 4import { AppError } from '../../../../../shared/core/AppError'; 5import { IEventPublisher } from '../../../../../shared/application/events/IEventPublisher'; 6import { ICardRepository } from '../../../domain/ICardRepository'; 7import { ICollectionRepository } from '../../../domain/ICollectionRepository'; 8import { CardId } from '../../../domain/value-objects/CardId'; 9import { CollectionId } from '../../../domain/value-objects/CollectionId'; 10import { CuratorId } from '../../../domain/value-objects/CuratorId'; 11import { CardCollectionService } from '../../../domain/services/CardCollectionService'; 12 13export interface AddCardToCollectionDTO { 14 cardId: string; 15 collectionIds: string[]; 16 curatorId: string; 17} 18 19export interface AddCardToCollectionResponseDTO { 20 cardId: string; 21} 22 23export class ValidationError extends UseCaseError { 24 constructor(message: string) { 25 super(message); 26 } 27} 28 29export class AddCardToCollectionUseCase extends BaseUseCase< 30 AddCardToCollectionDTO, 31 Result< 32 AddCardToCollectionResponseDTO, 33 ValidationError | AppError.UnexpectedError 34 > 35> { 36 constructor( 37 private cardRepository: ICardRepository, 38 private cardCollectionService: CardCollectionService, 39 eventPublisher: IEventPublisher, 40 ) { 41 super(eventPublisher); 42 } 43 44 async execute( 45 request: AddCardToCollectionDTO, 46 ): Promise< 47 Result< 48 AddCardToCollectionResponseDTO, 49 ValidationError | AppError.UnexpectedError 50 > 51 > { 52 try { 53 // Validate and create CuratorId 54 const curatorIdResult = CuratorId.create(request.curatorId); 55 if (curatorIdResult.isErr()) { 56 return err( 57 new ValidationError( 58 `Invalid curator ID: ${curatorIdResult.error.message}`, 59 ), 60 ); 61 } 62 const curatorId = curatorIdResult.value; 63 64 // Validate and create CardId 65 const cardIdResult = CardId.createFromString(request.cardId); 66 if (cardIdResult.isErr()) { 67 return err( 68 new ValidationError(`Invalid card ID: ${cardIdResult.error.message}`), 69 ); 70 } 71 const cardId = cardIdResult.value; 72 73 // Validate and create CollectionIds 74 const collectionIds: CollectionId[] = []; 75 for (const collectionIdStr of request.collectionIds) { 76 const collectionIdResult = 77 CollectionId.createFromString(collectionIdStr); 78 if (collectionIdResult.isErr()) { 79 return err( 80 new ValidationError( 81 `Invalid collection ID: ${collectionIdResult.error.message}`, 82 ), 83 ); 84 } 85 collectionIds.push(collectionIdResult.value); 86 } 87 88 // Find the card 89 const cardResult = await this.cardRepository.findById(cardId); 90 if (cardResult.isErr()) { 91 return err(AppError.UnexpectedError.create(cardResult.error)); 92 } 93 94 const card = cardResult.value; 95 if (!card) { 96 return err(new ValidationError(`Card not found: ${request.cardId}`)); 97 } 98 99 // Add card to collections using domain service 100 const addToCollectionsResult = 101 await this.cardCollectionService.addCardToCollections( 102 card, 103 collectionIds, 104 curatorId, 105 ); 106 if (addToCollectionsResult.isErr()) { 107 if (addToCollectionsResult.error instanceof AppError.UnexpectedError) { 108 return err(addToCollectionsResult.error); 109 } 110 return err(new ValidationError(addToCollectionsResult.error.message)); 111 } 112 113 // Publish events for all affected collections 114 const updatedCollections = addToCollectionsResult.value; 115 for (const collection of updatedCollections) { 116 const publishResult = await this.publishEventsForAggregate(collection); 117 if (publishResult.isErr()) { 118 console.error( 119 'Failed to publish events for collection:', 120 publishResult.error, 121 ); 122 // Don't fail the operation if event publishing fails 123 } 124 } 125 126 return ok({ 127 cardId: card.cardId.getStringValue(), 128 }); 129 } catch (error) { 130 return err(AppError.UnexpectedError.create(error)); 131 } 132 } 133}