This repository has no description
3.9 kB
134 lines
1import { Result, ok, err } from '../../../../../shared/core/Result';
2import { UseCase } from '../../../../../shared/core/UseCase';
3import {
4 ICardQueryRepository,
5 CardSortField,
6 SortOrder,
7} from '../../../domain/ICardQueryRepository';
8import { URL } from '../../../domain/value-objects/URL';
9import { IProfileService } from '../../../domain/services/IProfileService';
10import { NoteCardDTO, PaginationDTO, CardSortingDTO } from '@semble/types';
11import { ProfileEnricher } from '../../services/ProfileEnricher';
12
13export interface GetNoteCardsForUrlQuery {
14 url: string;
15 callingUserId?: string;
16 page?: number;
17 limit?: number;
18 sortBy?: CardSortField;
19 sortOrder?: SortOrder;
20}
21
22export interface GetNoteCardsForUrlResult {
23 notes: NoteCardDTO[];
24 pagination: PaginationDTO;
25 sorting: CardSortingDTO;
26}
27
28export class ValidationError extends Error {
29 constructor(message: string) {
30 super(message);
31 this.name = 'ValidationError';
32 }
33}
34
35export class GetNoteCardsForUrlUseCase
36 implements UseCase<GetNoteCardsForUrlQuery, Result<GetNoteCardsForUrlResult>>
37{
38 constructor(
39 private cardQueryRepo: ICardQueryRepository,
40 private profileService: IProfileService,
41 ) {}
42
43 async execute(
44 query: GetNoteCardsForUrlQuery,
45 ): Promise<Result<GetNoteCardsForUrlResult>> {
46 // Validate URL
47 const urlResult = URL.create(query.url);
48 if (urlResult.isErr()) {
49 return err(
50 new ValidationError(`Invalid URL: ${urlResult.error.message}`),
51 );
52 }
53
54 // Set defaults
55 const page = query.page || 1;
56 const limit = Math.min(query.limit || 20, 100); // Cap at 100
57 const sortBy = query.sortBy || CardSortField.UPDATED_AT;
58 const sortOrder = query.sortOrder || SortOrder.DESC;
59
60 try {
61 // Execute query to get note cards for the URL (raw data with authorId)
62 const result = await this.cardQueryRepo.getNoteCardsForUrl(
63 urlResult.value.value,
64 {
65 page,
66 limit,
67 sortBy,
68 sortOrder,
69 },
70 );
71
72 // Enrich with author profiles using ProfileEnricher
73 const uniqueAuthorIds = Array.from(
74 new Set(result.items.map((item) => item.authorId)),
75 );
76
77 const profileEnricher = new ProfileEnricher(this.profileService);
78 const profileMapResult = await profileEnricher.buildProfileMap(
79 uniqueAuthorIds,
80 query.callingUserId,
81 {
82 skipFailures: false, // Fail if any profile fetch fails (preserving original behavior)
83 mapToUser: false, // Use inline profile (without isFollowing)
84 },
85 );
86
87 if (profileMapResult.isErr()) {
88 return err(
89 new Error(
90 `Failed to fetch author profiles: ${profileMapResult.error.message}`,
91 ),
92 );
93 }
94
95 const profileMap = profileMapResult.value;
96
97 // Map items with enriched author data
98 const enrichedNotes: NoteCardDTO[] = result.items.map((item) => {
99 const author = profileMap.get(item.authorId);
100 if (!author) {
101 throw new Error(`Profile not found for author ${item.authorId}`);
102 }
103 return {
104 id: item.id,
105 note: item.note,
106 author,
107 createdAt: item.createdAt.toISOString(),
108 updatedAt: item.updatedAt.toISOString(),
109 };
110 });
111
112 return ok({
113 notes: enrichedNotes,
114 pagination: {
115 currentPage: page,
116 totalPages: Math.ceil(result.totalCount / limit),
117 totalCount: result.totalCount,
118 hasMore: page * limit < result.totalCount,
119 limit,
120 },
121 sorting: {
122 sortBy,
123 sortOrder,
124 },
125 });
126 } catch (error) {
127 return err(
128 new Error(
129 `Failed to retrieve note cards for URL: ${error instanceof Error ? error.message : 'Unknown error'}`,
130 ),
131 );
132 }
133 }
134}