A social knowledge tool for researchers built on ATProto
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}