A social knowledge tool for researchers built on ATProto
at development 117 lines 3.4 kB view raw
1import { CardId } from 'src/modules/cards/domain/value-objects/CardId'; 2import { err, ok, Result } from 'src/shared/core/Result'; 3import { UseCase } from 'src/shared/core/UseCase'; 4import { 5 ICardQueryRepository, 6 UrlCardView, 7 WithCollections, 8} from '../../../domain/ICardQueryRepository'; 9import { IProfileService } from '../../../domain/services/IProfileService'; 10 11export interface GetUrlCardViewQuery { 12 cardId: string; 13 callerDid?: string; 14} 15 16// Enriched data for the final use case result 17export type UrlCardViewResult = UrlCardView & 18 WithCollections & { 19 libraries: { 20 userId: string; 21 name: string; 22 handle: string; 23 avatarUrl?: string; 24 }[]; 25 }; 26 27export class ValidationError extends Error { 28 constructor(message: string) { 29 super(message); 30 this.name = 'ValidationError'; 31 } 32} 33 34export class CardNotFoundError extends Error { 35 constructor(message: string) { 36 super(message); 37 this.name = 'CardNotFoundError'; 38 } 39} 40 41export class GetUrlCardViewUseCase 42 implements UseCase<GetUrlCardViewQuery, Result<UrlCardViewResult>> 43{ 44 constructor( 45 private cardQueryRepo: ICardQueryRepository, 46 private profileService: IProfileService, 47 ) {} 48 49 async execute( 50 query: GetUrlCardViewQuery, 51 ): Promise<Result<UrlCardViewResult>> { 52 // Validate card ID 53 const cardIdResult = CardId.createFromString(query.cardId); 54 if (cardIdResult.isErr()) { 55 return err(new ValidationError('Invalid card ID')); 56 } 57 58 try { 59 // Get the URL card view data 60 const cardView = await this.cardQueryRepo.getUrlCardView(query.cardId); 61 62 if (!cardView) { 63 return err(new CardNotFoundError('URL card not found')); 64 } 65 66 // Get profiles for all users in libraries 67 const userIds = cardView.libraries.map((lib) => lib.userId); 68 const profilePromises = userIds.map((userId) => 69 this.profileService.getProfile(userId, query.callerDid), 70 ); 71 72 const profileResults = await Promise.all(profilePromises); 73 74 // Check if any profile fetches failed 75 const failedProfiles = profileResults.filter((result) => result.isErr()); 76 if (failedProfiles.length > 0) { 77 const firstError = failedProfiles[0]!.error; 78 return err( 79 new Error( 80 `Failed to fetch user profiles: ${firstError instanceof Error ? firstError.message : 'Unknown error'}`, 81 ), 82 ); 83 } 84 85 // Transform to result format with enriched profile data 86 const enrichedLibraries = cardView.libraries.map((lib, index) => { 87 const profileResult = profileResults[index]!; 88 if (profileResult.isErr()) { 89 throw new Error( 90 `Failed to fetch profile for user ${lib.userId}: ${profileResult.error instanceof Error ? profileResult.error.message : 'Unknown error'}`, 91 ); 92 } 93 const profile = profileResult.value; 94 95 return { 96 userId: lib.userId, 97 name: profile.name, 98 handle: profile.handle, 99 avatarUrl: profile.avatarUrl, 100 }; 101 }); 102 103 const result: UrlCardViewResult = { 104 ...cardView, 105 libraries: enrichedLibraries, 106 }; 107 108 return ok(result); 109 } catch (error) { 110 return err( 111 new Error( 112 `Failed to retrieve URL card view: ${error instanceof Error ? error.message : 'Unknown error'}`, 113 ), 114 ); 115 } 116 } 117}