A social knowledge tool for researchers built on ATProto
1import { Result, ok, err } from '../../../../../shared/core/Result';
2import { UseCase } from '../../../../../shared/core/UseCase';
3import { UseCaseError } from '../../../../../shared/core/UseCaseError';
4import { AppError } from '../../../../../shared/core/AppError';
5import { ICardRepository } from '../../../domain/ICardRepository';
6import { CardId } from '../../../domain/value-objects/CardId';
7import { CollectionId } from '../../../domain/value-objects/CollectionId';
8import { CuratorId } from '../../../domain/value-objects/CuratorId';
9import { CardLibraryService } from '../../../domain/services/CardLibraryService';
10import { CardCollectionService } from '../../../domain/services/CardCollectionService';
11import { AuthenticationError } from '../../../../../shared/core/AuthenticationError';
12
13export interface AddCardToLibraryDTO {
14 cardId: string;
15 curatorId: string;
16 collectionIds?: string[];
17}
18
19export interface AddCardToLibraryResponseDTO {
20 cardId: string;
21}
22
23export class ValidationError extends UseCaseError {
24 constructor(message: string) {
25 super(message);
26 }
27}
28
29export class AddCardToLibraryUseCase
30 implements
31 UseCase<
32 AddCardToLibraryDTO,
33 Result<
34 AddCardToLibraryResponseDTO,
35 ValidationError | AuthenticationError | AppError.UnexpectedError
36 >
37 >
38{
39 constructor(
40 private cardRepository: ICardRepository,
41 private cardLibraryService: CardLibraryService,
42 private cardCollectionService: CardCollectionService,
43 ) {}
44
45 async execute(
46 request: AddCardToLibraryDTO,
47 ): Promise<
48 Result<
49 AddCardToLibraryResponseDTO,
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 // Find the card
75 const cardResult = await this.cardRepository.findById(cardId);
76 if (cardResult.isErr()) {
77 return err(AppError.UnexpectedError.create(cardResult.error));
78 }
79
80 const card = cardResult.value;
81 if (!card) {
82 return err(new ValidationError(`Card not found: ${request.cardId}`));
83 }
84
85 // Add card to library using domain service
86 const addToLibraryResult = await this.cardLibraryService.addCardToLibrary(
87 card,
88 curatorId,
89 );
90 if (addToLibraryResult.isErr()) {
91 // Propagate authentication errors
92 if (addToLibraryResult.error instanceof AuthenticationError) {
93 return err(addToLibraryResult.error);
94 }
95 if (addToLibraryResult.error instanceof AppError.UnexpectedError) {
96 return err(addToLibraryResult.error);
97 }
98 return err(new ValidationError(addToLibraryResult.error.message));
99 }
100
101 // Handle collection additions if specified
102 if (request.collectionIds && request.collectionIds.length > 0) {
103 // Validate and create CollectionIds
104 const collectionIds: CollectionId[] = [];
105 for (const collectionIdStr of request.collectionIds) {
106 const collectionIdResult =
107 CollectionId.createFromString(collectionIdStr);
108 if (collectionIdResult.isErr()) {
109 return err(
110 new ValidationError(
111 `Invalid collection ID: ${collectionIdResult.error.message}`,
112 ),
113 );
114 }
115 collectionIds.push(collectionIdResult.value);
116 }
117
118 // Add card to collections using domain service
119 const addToCollectionsResult =
120 await this.cardCollectionService.addCardToCollections(
121 card,
122 collectionIds,
123 curatorId,
124 );
125 if (addToCollectionsResult.isErr()) {
126 // Propagate authentication errors
127 if (addToCollectionsResult.error instanceof AuthenticationError) {
128 return err(addToCollectionsResult.error);
129 }
130 if (
131 addToCollectionsResult.error instanceof AppError.UnexpectedError
132 ) {
133 return err(addToCollectionsResult.error);
134 }
135 return err(new ValidationError(addToCollectionsResult.error.message));
136 }
137 }
138
139 return ok({
140 cardId: card.cardId.getStringValue(),
141 });
142 } catch (error) {
143 return err(AppError.UnexpectedError.create(error));
144 }
145 }
146}