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 { CollectionName } from '../../../domain/value-objects/CollectionName';
9import { CollectionDescription } from '../../../domain/value-objects/CollectionDescription';
10import { ICollectionPublisher } from '../../ports/ICollectionPublisher';
11import { AuthenticationError } from '../../../../../shared/core/AuthenticationError';
12
13export interface UpdateCollectionDTO {
14 collectionId: string;
15 name: string;
16 description?: string;
17 curatorId: string;
18}
19
20export interface UpdateCollectionResponseDTO {
21 collectionId: string;
22}
23
24export class ValidationError extends UseCaseError {
25 constructor(message: string) {
26 super(message);
27 }
28}
29
30export class UpdateCollectionUseCase
31 implements
32 UseCase<
33 UpdateCollectionDTO,
34 Result<
35 UpdateCollectionResponseDTO,
36 ValidationError | AuthenticationError | AppError.UnexpectedError
37 >
38 >
39{
40 constructor(
41 private collectionRepository: ICollectionRepository,
42 private collectionPublisher: ICollectionPublisher,
43 ) {}
44
45 async execute(
46 request: UpdateCollectionDTO,
47 ): Promise<
48 Result<
49 UpdateCollectionResponseDTO,
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 CollectionId
66 const collectionIdResult = CollectionId.createFromString(
67 request.collectionId,
68 );
69 if (collectionIdResult.isErr()) {
70 return err(
71 new ValidationError(
72 `Invalid collection ID: ${collectionIdResult.error.message}`,
73 ),
74 );
75 }
76 const collectionId = collectionIdResult.value;
77
78 // Find the collection
79 const collectionResult =
80 await this.collectionRepository.findById(collectionId);
81 if (collectionResult.isErr()) {
82 return err(AppError.UnexpectedError.create(collectionResult.error));
83 }
84
85 const collection = collectionResult.value;
86 if (!collection) {
87 return err(
88 new ValidationError(`Collection not found: ${request.collectionId}`),
89 );
90 }
91
92 // Check if user is the author
93 if (!collection.authorId.equals(curatorId)) {
94 return err(
95 new ValidationError(
96 'Only the collection author can update the collection',
97 ),
98 );
99 }
100
101 // Update collection details using domain method
102 const updateResult = collection.updateDetails(
103 request.name,
104 request.description,
105 );
106 if (updateResult.isErr()) {
107 return err(new ValidationError(updateResult.error.message));
108 }
109
110 // Save updated collection
111 const saveResult = await this.collectionRepository.save(collection);
112 if (saveResult.isErr()) {
113 return err(AppError.UnexpectedError.create(saveResult.error));
114 }
115
116 // Republish collection if it was already published
117 if (collection.isPublished) {
118 const republishResult =
119 await this.collectionPublisher.publish(collection);
120 if (republishResult.isErr()) {
121 // Propagate authentication errors
122 if (republishResult.error instanceof AuthenticationError) {
123 return err(republishResult.error);
124 }
125 return err(
126 new ValidationError(
127 `Failed to republish collection: ${republishResult.error.message}`,
128 ),
129 );
130 }
131
132 // Update published record ID
133 collection.markAsPublished(republishResult.value);
134
135 // Save collection with updated published record ID
136 const saveUpdatedResult =
137 await this.collectionRepository.save(collection);
138 if (saveUpdatedResult.isErr()) {
139 return err(AppError.UnexpectedError.create(saveUpdatedResult.error));
140 }
141 }
142
143 return ok({
144 collectionId: collection.collectionId.getStringValue(),
145 });
146 } catch (error) {
147 return err(AppError.UnexpectedError.create(error));
148 }
149 }
150}