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