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 { CuratorId } from '../../../domain/value-objects/CuratorId';
8import { CardTypeEnum } from '../../../domain/value-objects/CardType';
9import { CardContent } from '../../../domain/value-objects/CardContent';
10import { ICardPublisher } from '../../ports/ICardPublisher';
11
12export interface UpdateNoteCardDTO {
13 cardId: string;
14 note: string;
15 curatorId: string;
16}
17
18export interface UpdateNoteCardResponseDTO {
19 cardId: string;
20}
21
22export class ValidationError extends UseCaseError {
23 constructor(message: string) {
24 super(message);
25 }
26}
27
28export class UpdateNoteCardUseCase
29 implements
30 UseCase<
31 UpdateNoteCardDTO,
32 Result<
33 UpdateNoteCardResponseDTO,
34 ValidationError | AppError.UnexpectedError
35 >
36 >
37{
38 constructor(
39 private cardRepository: ICardRepository,
40 private cardPublisher: ICardPublisher,
41 ) {}
42
43 async execute(
44 request: UpdateNoteCardDTO,
45 ): Promise<
46 Result<
47 UpdateNoteCardResponseDTO,
48 ValidationError | 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 // Find the card
73 const cardResult = await this.cardRepository.findById(cardId);
74 if (cardResult.isErr()) {
75 return err(AppError.UnexpectedError.create(cardResult.error));
76 }
77
78 const card = cardResult.value;
79 if (!card) {
80 return err(new ValidationError(`Card not found: ${request.cardId}`));
81 }
82
83 // Verify it's a note card
84 if (card.type.value !== CardTypeEnum.NOTE) {
85 return err(
86 new ValidationError('Card is not a note card and cannot be updated'),
87 );
88 }
89
90 // Get the note content and verify authorship
91 const noteContent = card.content.noteContent;
92 if (!noteContent) {
93 return err(
94 new ValidationError('Card does not have valid note content'),
95 );
96 }
97
98 if (!card.curatorId.equals(curatorId)) {
99 return err(
100 new ValidationError('Only the author can update this note card'),
101 );
102 }
103
104 // Update the note content
105 const updatedNoteContentResult = noteContent.updateText(request.note);
106 if (updatedNoteContentResult.isErr()) {
107 return err(new ValidationError(updatedNoteContentResult.error.message));
108 }
109
110 // Create new card content with updated note
111 const updatedCardContentResult = CardContent.createNoteContent(
112 request.note,
113 );
114 if (updatedCardContentResult.isErr()) {
115 return err(new ValidationError(updatedCardContentResult.error.message));
116 }
117
118 // Update the card content
119 const updateResult = card.updateContent(updatedCardContentResult.value);
120 if (updateResult.isErr()) {
121 return err(new ValidationError(updateResult.error.message));
122 }
123
124 // Publish the updated card to library first
125 const publishResult = await this.cardPublisher.publishCardToLibrary(
126 card,
127 curatorId,
128 card.publishedRecordId,
129 );
130 if (publishResult.isErr()) {
131 return err(AppError.UnexpectedError.create(publishResult.error));
132 }
133
134 // Mark the card in library as published with the new record ID
135 const markPublishedResult = card.markCardInLibraryAsPublished(
136 curatorId,
137 publishResult.value,
138 );
139 if (markPublishedResult.isErr()) {
140 return err(new ValidationError(markPublishedResult.error.message));
141 }
142
143 // Save the updated card to repository
144 const saveResult = await this.cardRepository.save(card);
145 if (saveResult.isErr()) {
146 return err(AppError.UnexpectedError.create(saveResult.error));
147 }
148
149 return ok({
150 cardId: card.cardId.getStringValue(),
151 });
152 } catch (error) {
153 return err(AppError.UnexpectedError.create(error));
154 }
155 }
156}