This repository has no description
4.0 kB
139 lines
1import { err, ok, Result } from 'src/shared/core/Result';
2import { UseCase } from 'src/shared/core/UseCase';
3import {
4 ICardQueryRepository,
5 CardSortField,
6 SortOrder,
7 WithCollections,
8 UrlCardView,
9} from '../../../domain/ICardQueryRepository';
10import { UrlType } from '../../../domain/value-objects/UrlType';
11import { DIDOrHandle } from 'src/modules/atproto/domain/DIDOrHandle';
12import { IIdentityResolutionService } from 'src/modules/atproto/domain/services/IIdentityResolutionService';
13import { IProfileService } from '../../../domain/services/IProfileService';
14import { User } from '@semble/types';
15import { ProfileEnricher } from '../../services/ProfileEnricher';
16
17export interface GetUrlCardsQuery {
18 userId: string;
19 callingUserId?: string;
20 page?: number;
21 limit?: number;
22 sortBy?: CardSortField;
23 sortOrder?: SortOrder;
24 urlType?: UrlType;
25}
26
27// Enriched data for the final use case result
28export type UrlCardListItemDTO = Omit<UrlCardView, 'authorId'> & {
29 author: User;
30};
31export interface GetUrlCardsResult {
32 cards: UrlCardListItemDTO[];
33 pagination: {
34 currentPage: number;
35 totalPages: number;
36 totalCount: number;
37 hasMore: boolean;
38 limit: number;
39 };
40 sorting: {
41 sortBy: CardSortField;
42 sortOrder: SortOrder;
43 };
44}
45
46export class ValidationError extends Error {
47 constructor(message: string) {
48 super(message);
49 this.name = 'ValidationError';
50 }
51}
52
53export class GetUrlCardsUseCase
54 implements UseCase<GetUrlCardsQuery, Result<GetUrlCardsResult>>
55{
56 constructor(
57 private cardQueryRepo: ICardQueryRepository,
58 private identityResolver: IIdentityResolutionService,
59 private profileService: IProfileService,
60 ) {}
61
62 async execute(query: GetUrlCardsQuery): Promise<Result<GetUrlCardsResult>> {
63 // Set defaults
64 const page = query.page || 1;
65 const limit = Math.min(query.limit || 20, 100); // Cap at 100
66 const sortBy = query.sortBy || CardSortField.UPDATED_AT;
67 const sortOrder = query.sortOrder || SortOrder.DESC;
68 const urlType = query.urlType;
69
70 // Parse and validate user identifier
71 const identifierResult = DIDOrHandle.create(query.userId);
72 if (identifierResult.isErr()) {
73 return err(new ValidationError('Invalid user identifier'));
74 }
75
76 // Resolve to DID
77 const didResult = await this.identityResolver.resolveToDID(
78 identifierResult.value,
79 );
80 if (didResult.isErr()) {
81 return err(
82 new ValidationError(
83 `Could not resolve user identifier: ${didResult.error.message}`,
84 ),
85 );
86 }
87
88 try {
89 // Execute query to get raw card data using the resolved DID
90 const result = await this.cardQueryRepo.getUrlCardsOfUser(
91 didResult.value.value,
92 {
93 page,
94 limit,
95 sortBy,
96 sortOrder,
97 urlType,
98 },
99 query.callingUserId,
100 );
101
102 // Enrich cards with author profiles using ProfileEnricher utility
103 const profileEnricher = new ProfileEnricher(this.profileService);
104 const enrichResult = await profileEnricher.enrichWithAuthors(
105 result.items,
106 (item) => item.authorId,
107 query.callingUserId,
108 { mapToUser: false }, // Use inline profile (without isFollowing)
109 );
110
111 if (enrichResult.isErr()) {
112 return err(enrichResult.error);
113 }
114
115 const enrichedCards = enrichResult.value;
116
117 return ok({
118 cards: enrichedCards,
119 pagination: {
120 currentPage: page,
121 totalPages: Math.ceil(result.totalCount / limit),
122 totalCount: result.totalCount,
123 hasMore: page * limit < result.totalCount,
124 limit,
125 },
126 sorting: {
127 sortBy,
128 sortOrder,
129 },
130 });
131 } catch (error) {
132 return err(
133 new Error(
134 `Failed to retrieve URL cards: ${error instanceof Error ? error.message : 'Unknown error'}`,
135 ),
136 );
137 }
138 }
139}