This repository has no description
1import { Result, ok, err } from '../../../../../shared/core/Result';
2import { BaseUseCase } from '../../../../../shared/core/UseCase';
3import { UseCaseError } from '../../../../../shared/core/UseCaseError';
4import { AppError } from '../../../../../shared/core/AppError';
5import { IEventPublisher } from '../../../../../shared/application/events/IEventPublisher';
6import { ICardRepository } from '../../../domain/ICardRepository';
7import { ICollectionQueryRepository } from '../../../domain/ICollectionQueryRepository';
8import { ICollectionRepository } from '../../../domain/ICollectionRepository';
9import { IProfileService } from '../../../domain/services/IProfileService';
10import { CuratorId } from '../../../domain/value-objects/CuratorId';
11import { URL } from '../../../domain/value-objects/URL';
12import { CollectionId } from '../../../domain/value-objects/CollectionId';
13
14export interface GetUrlStatusForMyLibraryQuery {
15 url: string;
16 curatorId: string;
17}
18
19export interface CollectionInfo {
20 id: string;
21 uri?: string;
22 name: string;
23 description?: string;
24 author: {
25 id: string;
26 name: string;
27 handle: string;
28 avatarUrl?: string;
29 description?: string;
30 };
31 cardCount: number;
32 createdAt: string;
33 updatedAt: string;
34}
35
36export interface GetUrlStatusForMyLibraryResult {
37 cardId?: string;
38 collections?: CollectionInfo[];
39}
40
41export class ValidationError extends UseCaseError {
42 constructor(message: string) {
43 super(message);
44 }
45}
46
47export class GetUrlStatusForMyLibraryUseCase extends BaseUseCase<
48 GetUrlStatusForMyLibraryQuery,
49 Result<
50 GetUrlStatusForMyLibraryResult,
51 ValidationError | AppError.UnexpectedError
52 >
53> {
54 constructor(
55 private cardRepository: ICardRepository,
56 private collectionQueryRepository: ICollectionQueryRepository,
57 private collectionRepo: ICollectionRepository,
58 private profileService: IProfileService,
59 eventPublisher: IEventPublisher,
60 ) {
61 super(eventPublisher);
62 }
63
64 async execute(
65 query: GetUrlStatusForMyLibraryQuery,
66 ): Promise<
67 Result<
68 GetUrlStatusForMyLibraryResult,
69 ValidationError | AppError.UnexpectedError
70 >
71 > {
72 try {
73 // Validate and create CuratorId
74 const curatorIdResult = CuratorId.create(query.curatorId);
75 if (curatorIdResult.isErr()) {
76 return err(
77 new ValidationError(
78 `Invalid curator ID: ${curatorIdResult.error.message}`,
79 ),
80 );
81 }
82 const curatorId = curatorIdResult.value;
83
84 // Validate URL
85 const urlResult = URL.create(query.url);
86 if (urlResult.isErr()) {
87 return err(
88 new ValidationError(`Invalid URL: ${urlResult.error.message}`),
89 );
90 }
91 const url = urlResult.value;
92
93 // Check if user has a URL card with this URL
94 const existingCardResult =
95 await this.cardRepository.findUsersUrlCardByUrl(url, curatorId);
96 if (existingCardResult.isErr()) {
97 return err(AppError.UnexpectedError.create(existingCardResult.error));
98 }
99
100 const card = existingCardResult.value;
101 const result: GetUrlStatusForMyLibraryResult = {};
102
103 if (card) {
104 result.cardId = card.cardId.getStringValue();
105
106 // Get collections containing this card for the user
107 try {
108 const collections =
109 await this.collectionQueryRepository.getCollectionsContainingCardForUser(
110 card.cardId.getStringValue(),
111 curatorId.value,
112 );
113
114 // Enrich collections with full data
115 result.collections = await Promise.all(
116 collections.map(async (collection) => {
117 // Fetch full collection to get dates and cardCount
118 const collectionIdResult = CollectionId.createFromString(
119 collection.id,
120 );
121 if (collectionIdResult.isErr()) {
122 throw new Error(
123 `Invalid collection ID: ${collection.id}`,
124 );
125 }
126 const collectionResult = await this.collectionRepo.findById(
127 collectionIdResult.value,
128 );
129 if (collectionResult.isErr() || !collectionResult.value) {
130 throw new Error(
131 `Collection not found: ${collection.id}`,
132 );
133 }
134 const fullCollection = collectionResult.value;
135
136 // Fetch author profile
137 const authorProfileResult = await this.profileService.getProfile(
138 fullCollection.authorId.value,
139 );
140 if (authorProfileResult.isErr()) {
141 throw new Error(
142 `Failed to fetch author profile: ${authorProfileResult.error.message}`,
143 );
144 }
145 const authorProfile = authorProfileResult.value;
146
147 return {
148 id: collection.id,
149 uri: collection.uri,
150 name: collection.name,
151 description: collection.description,
152 author: {
153 id: authorProfile.id,
154 name: authorProfile.name,
155 handle: authorProfile.handle,
156 avatarUrl: authorProfile.avatarUrl,
157 description: authorProfile.bio,
158 },
159 cardCount: fullCollection.cardCount,
160 createdAt: fullCollection.createdAt.toISOString(),
161 updatedAt: fullCollection.updatedAt.toISOString(),
162 };
163 }),
164 );
165 } catch (error) {
166 return err(AppError.UnexpectedError.create(error));
167 }
168 }
169
170 return ok(result);
171 } catch (error) {
172 return err(AppError.UnexpectedError.create(error));
173 }
174 }
175}