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 { ICollectionRepository } from '../../../domain/ICollectionRepository';
6import { CollectionId } from '../../../domain/value-objects/CollectionId';
7import { CuratorId } from '../../../domain/value-objects/CuratorId';
8import { ICollectionPublisher } from '../../ports/ICollectionPublisher';
9
10export interface DeleteCollectionDTO {
11 collectionId: string;
12 curatorId: string;
13}
14
15export interface DeleteCollectionResponseDTO {
16 collectionId: string;
17}
18
19export class ValidationError extends UseCaseError {
20 constructor(message: string) {
21 super(message);
22 }
23}
24
25export class DeleteCollectionUseCase
26 implements
27 UseCase<
28 DeleteCollectionDTO,
29 Result<
30 DeleteCollectionResponseDTO,
31 ValidationError | AppError.UnexpectedError
32 >
33 >
34{
35 constructor(
36 private collectionRepository: ICollectionRepository,
37 private collectionPublisher: ICollectionPublisher,
38 ) {}
39
40 async execute(
41 request: DeleteCollectionDTO,
42 ): Promise<
43 Result<
44 DeleteCollectionResponseDTO,
45 ValidationError | AppError.UnexpectedError
46 >
47 > {
48 try {
49 // Validate and create CuratorId
50 const curatorIdResult = CuratorId.create(request.curatorId);
51 if (curatorIdResult.isErr()) {
52 return err(
53 new ValidationError(
54 `Invalid curator ID: ${curatorIdResult.error.message}`,
55 ),
56 );
57 }
58 const curatorId = curatorIdResult.value;
59
60 // Validate and create CollectionId
61 const collectionIdResult = CollectionId.createFromString(
62 request.collectionId,
63 );
64 if (collectionIdResult.isErr()) {
65 return err(
66 new ValidationError(
67 `Invalid collection ID: ${collectionIdResult.error.message}`,
68 ),
69 );
70 }
71 const collectionId = collectionIdResult.value;
72
73 // Find the collection
74 const collectionResult =
75 await this.collectionRepository.findById(collectionId);
76 if (collectionResult.isErr()) {
77 return err(AppError.UnexpectedError.create(collectionResult.error));
78 }
79
80 const collection = collectionResult.value;
81 if (!collection) {
82 return err(
83 new ValidationError(`Collection not found: ${request.collectionId}`),
84 );
85 }
86
87 // Check if user is the author
88 if (!collection.authorId.equals(curatorId)) {
89 return err(
90 new ValidationError(
91 'Only the collection author can delete the collection',
92 ),
93 );
94 }
95
96 // Unpublish collection if it was published
97 if (collection.isPublished && collection.publishedRecordId) {
98 const unpublishResult = await this.collectionPublisher.unpublish(
99 collection.publishedRecordId,
100 );
101 if (unpublishResult.isErr()) {
102 return err(
103 new ValidationError(
104 `Failed to unpublish collection: ${unpublishResult.error.message}`,
105 ),
106 );
107 }
108 }
109
110 // Delete collection from repository
111 const deleteResult = await this.collectionRepository.delete(collectionId);
112 if (deleteResult.isErr()) {
113 return err(AppError.UnexpectedError.create(deleteResult.error));
114 }
115
116 return ok({
117 collectionId: collection.collectionId.getStringValue(),
118 });
119 } catch (error) {
120 return err(AppError.UnexpectedError.create(error));
121 }
122 }
123}