This repository has no description
5.1 kB
171 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 {
11 UserProfileDTO,
12 UrlCardDTO,
13 PaginationDTO,
14 CardSortingDTO,
15} from '@semble/types';
16import { ProfileEnricher } from '../../services/ProfileEnricher';
17
18export interface GetLibrariesForUrlQuery {
19 url: string;
20 callingUserId?: string;
21 page?: number;
22 limit?: number;
23 sortBy?: CardSortField;
24 sortOrder?: SortOrder;
25}
26
27export interface LibraryForUrlDTO {
28 user: UserProfileDTO;
29 card: UrlCardDTO;
30}
31
32export interface GetLibrariesForUrlResult {
33 libraries: LibraryForUrlDTO[];
34 pagination: PaginationDTO;
35 sorting: CardSortingDTO;
36}
37
38export class ValidationError extends Error {
39 constructor(message: string) {
40 super(message);
41 this.name = 'ValidationError';
42 }
43}
44
45export class GetLibrariesForUrlUseCase
46 implements UseCase<GetLibrariesForUrlQuery, Result<GetLibrariesForUrlResult>>
47{
48 constructor(
49 private cardQueryRepo: ICardQueryRepository,
50 private profileService: IProfileService,
51 ) {}
52
53 async execute(
54 query: GetLibrariesForUrlQuery,
55 ): Promise<Result<GetLibrariesForUrlResult>> {
56 // Validate URL
57 const urlResult = URL.create(query.url);
58 if (urlResult.isErr()) {
59 return err(
60 new ValidationError(`Invalid URL: ${urlResult.error.message}`),
61 );
62 }
63
64 // Set defaults
65 const page = query.page || 1;
66 const limit = Math.min(query.limit || 20, 100); // Cap at 100
67 const sortBy = query.sortBy || CardSortField.CREATED_AT;
68 const sortOrder = query.sortOrder || SortOrder.DESC;
69
70 try {
71 // Execute query to get libraries with full card data
72 const result = await this.cardQueryRepo.getLibrariesForUrl(
73 urlResult.value.value,
74 {
75 page,
76 limit,
77 sortBy,
78 sortOrder,
79 },
80 );
81
82 // Enrich with user profiles using ProfileEnricher
83 const uniqueUserIds = Array.from(
84 new Set(result.items.map((item) => item.userId)),
85 );
86
87 const profileEnricher = new ProfileEnricher(this.profileService);
88 const profileMapResult = await profileEnricher.buildProfileMap(
89 uniqueUserIds,
90 query.callingUserId,
91 {
92 skipFailures: false, // Fail if any profile fetch fails (preserving original behavior)
93 mapToUser: false, // Use inline profile (without isFollowing)
94 },
95 );
96
97 if (profileMapResult.isErr()) {
98 return err(
99 new Error(
100 `Failed to fetch user profiles: ${profileMapResult.error.message}`,
101 ),
102 );
103 }
104
105 const profileMap = profileMapResult.value;
106
107 // Map items with enriched user data and card data
108 const enrichedLibraries: LibraryForUrlDTO[] = result.items.map((item) => {
109 const user = profileMap.get(item.userId);
110 if (!user) {
111 throw new Error(`Profile not found for user ${item.userId}`);
112 }
113
114 // Build card object
115 // Note: userId is the card author (it's their card in their library)
116 const card: UrlCardDTO = {
117 id: item.card.id,
118 type: 'URL',
119 url: item.card.url,
120 uri: item.card.uri,
121 cardContent: {
122 url: item.card.cardContent.url,
123 title: item.card.cardContent.title,
124 description: item.card.cardContent.description,
125 author: item.card.cardContent.author,
126 publishedDate: item.card.cardContent.publishedDate?.toISOString(),
127 siteName: item.card.cardContent.siteName,
128 imageUrl: item.card.cardContent.imageUrl,
129 type: item.card.cardContent.type,
130 retrievedAt: item.card.cardContent.retrievedAt?.toISOString(),
131 doi: item.card.cardContent.doi,
132 isbn: item.card.cardContent.isbn,
133 },
134 libraryCount: item.card.libraryCount,
135 urlLibraryCount: item.card.urlLibraryCount,
136 urlInLibrary: item.card.urlInLibrary,
137 createdAt: item.card.createdAt.toISOString(),
138 updatedAt: item.card.updatedAt.toISOString(),
139 author: user, // Card author is same as library user
140 note: item.card.note,
141 };
142
143 return {
144 user,
145 card,
146 };
147 });
148
149 return ok({
150 libraries: enrichedLibraries,
151 pagination: {
152 currentPage: page,
153 totalPages: Math.ceil(result.totalCount / limit),
154 totalCount: result.totalCount,
155 hasMore: page * limit < result.totalCount,
156 limit,
157 },
158 sorting: {
159 sortBy,
160 sortOrder,
161 },
162 });
163 } catch (error) {
164 return err(
165 new Error(
166 `Failed to retrieve libraries for URL: ${error instanceof Error ? error.message : 'Unknown error'}`,
167 ),
168 );
169 }
170 }
171}