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 { CardCollectionService } from '../../../domain/services/CardCollectionService';
10
11export interface RemoveCardFromCollectionDTO {
12 cardId: string;
13 collectionIds: string[];
14 curatorId: string;
15}
16
17export interface RemoveCardFromCollectionResponseDTO {
18 cardId: string;
19}
20
21export class ValidationError extends UseCaseError {
22 constructor(message: string) {
23 super(message);
24 }
25}
26
27export class RemoveCardFromCollectionUseCase
28 implements
29 UseCase<
30 RemoveCardFromCollectionDTO,
31 Result<
32 RemoveCardFromCollectionResponseDTO,
33 ValidationError | AppError.UnexpectedError
34 >
35 >
36{
37 constructor(
38 private cardRepository: ICardRepository,
39 private cardCollectionService: CardCollectionService,
40 ) {}
41
42 async execute(
43 request: RemoveCardFromCollectionDTO,
44 ): Promise<
45 Result<
46 RemoveCardFromCollectionResponseDTO,
47 ValidationError | AppError.UnexpectedError
48 >
49 > {
50 try {
51 // Validate and create CuratorId
52 const curatorIdResult = CuratorId.create(request.curatorId);
53 if (curatorIdResult.isErr()) {
54 return err(
55 new ValidationError(
56 `Invalid curator ID: ${curatorIdResult.error.message}`,
57 ),
58 );
59 }
60 const curatorId = curatorIdResult.value;
61
62 // Validate and create CardId
63 const cardIdResult = CardId.createFromString(request.cardId);
64 if (cardIdResult.isErr()) {
65 return err(
66 new ValidationError(`Invalid card ID: ${cardIdResult.error.message}`),
67 );
68 }
69 const cardId = cardIdResult.value;
70
71 // Validate and create CollectionIds
72 const collectionIds: CollectionId[] = [];
73 for (const collectionIdStr of request.collectionIds) {
74 const collectionIdResult =
75 CollectionId.createFromString(collectionIdStr);
76 if (collectionIdResult.isErr()) {
77 return err(
78 new ValidationError(
79 `Invalid collection ID: ${collectionIdResult.error.message}`,
80 ),
81 );
82 }
83 collectionIds.push(collectionIdResult.value);
84 }
85
86 // Find the card
87 const cardResult = await this.cardRepository.findById(cardId);
88 if (cardResult.isErr()) {
89 return err(AppError.UnexpectedError.create(cardResult.error));
90 }
91
92 const card = cardResult.value;
93 if (!card) {
94 return err(new ValidationError(`Card not found: ${request.cardId}`));
95 }
96
97 // Remove card from collections using domain service
98 const removeFromCollectionsResult =
99 await this.cardCollectionService.removeCardFromCollections(
100 card,
101 collectionIds,
102 curatorId,
103 );
104 if (removeFromCollectionsResult.isErr()) {
105 if (
106 removeFromCollectionsResult.error instanceof AppError.UnexpectedError
107 ) {
108 return err(removeFromCollectionsResult.error);
109 }
110 return err(
111 new ValidationError(removeFromCollectionsResult.error.message),
112 );
113 }
114
115 return ok({
116 cardId: card.cardId.getStringValue(),
117 });
118 } catch (error) {
119 return err(AppError.UnexpectedError.create(error));
120 }
121 }
122}