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