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