This repository has no description
1import { ICollectionPublisher } from '../../application/ports/ICollectionPublisher';
2import { Collection } from '../../domain/Collection';
3import { Card } from '../../domain/Card';
4import { PublishedRecordId } from '../../domain/value-objects/PublishedRecordId';
5import { ok, err, Result } from '../../../../shared/core/Result';
6import { UseCaseError } from '../../../../shared/core/UseCaseError';
7import { AppError } from '../../../../shared/core/AppError';
8import { CuratorId } from '../../domain/value-objects/CuratorId';
9import { EnvironmentConfigService } from 'src/shared/infrastructure/config/EnvironmentConfigService';
10
11export class FakeCollectionPublisher implements ICollectionPublisher {
12 private publishedCollections: Map<string, Collection> = new Map();
13 private publishedLinks: Map<
14 string,
15 { cardId: string; linkRecord: PublishedRecordId }[]
16 > = new Map();
17 private unpublishedCollections: Array<{ uri: string; cid: string }> = [];
18 private removedLinks: Array<{ cardId: string; collectionId: string }> = [];
19 private shouldFail: boolean = false;
20 private shouldFailUnpublish: boolean = false;
21 private collectionType =
22 new EnvironmentConfigService().getAtProtoCollections().collection;
23
24 private cardCollectionLinkType =
25 new EnvironmentConfigService().getAtProtoCollections().collectionLink;
26
27 async publish(
28 collection: Collection,
29 ): Promise<Result<PublishedRecordId, UseCaseError>> {
30 if (this.shouldFail) {
31 return err(
32 AppError.UnexpectedError.create(
33 new Error('Simulated collection publish failure'),
34 ),
35 );
36 }
37
38 const collectionId = collection.collectionId.getStringValue();
39
40 // Use the collection's author DID directly
41 const fakeDid = collection.authorId.value;
42
43 // Simulate publishing the collection record itself
44 const fakeCollectionUri = `at://${fakeDid}/${this.collectionType}/${collectionId}`;
45 const fakeCollectionCid = `fake-collection-cid-${collectionId}`;
46
47 const collectionRecord = PublishedRecordId.create({
48 uri: fakeCollectionUri,
49 cid: fakeCollectionCid,
50 });
51
52 // Store the published collection for inspection
53 this.publishedCollections.set(collectionId, collection);
54
55 console.log(
56 `[FakeCollectionPublisher] Published collection ${collectionId}`,
57 );
58
59 return ok(collectionRecord);
60 }
61
62 async publishCardAddedToCollection(
63 card: Card,
64 collection: Collection,
65 curatorId: CuratorId,
66 ): Promise<Result<PublishedRecordId, UseCaseError>> {
67 if (this.shouldFail) {
68 return err(
69 AppError.UnexpectedError.create(
70 new Error('Simulated card-collection link publish failure'),
71 ),
72 );
73 }
74
75 const collectionId = collection.collectionId.getStringValue();
76 const cardId = card.cardId.getStringValue();
77
78 // Simulate publishing a card-collection link
79 const fakeLinkUri = `at://${curatorId.value}/${this.cardCollectionLinkType}/${collectionId}-${cardId}`;
80 const fakeLinkCid = `fake-link-cid-${collectionId}-${cardId}`;
81
82 const linkRecord = PublishedRecordId.create({
83 uri: fakeLinkUri,
84 cid: fakeLinkCid,
85 });
86
87 // Store published link for inspection
88 const existingLinks = this.publishedLinks.get(collectionId) || [];
89 existingLinks.push({
90 cardId,
91 linkRecord,
92 });
93 this.publishedLinks.set(collectionId, existingLinks);
94
95 console.log(
96 `[FakeCollectionPublisher] Published card ${cardId} added to collection ${collectionId} link at ${fakeLinkUri}`,
97 );
98
99 return ok(linkRecord);
100 }
101
102 async unpublishCardAddedToCollection(
103 recordId: PublishedRecordId,
104 ): Promise<Result<void, UseCaseError>> {
105 if (this.shouldFailUnpublish) {
106 return err(
107 AppError.UnexpectedError.create(
108 new Error('Simulated card-collection link unpublish failure'),
109 ),
110 );
111 }
112
113 // Find and remove the link by its published record ID
114 for (const [collectionId, links] of this.publishedLinks.entries()) {
115 const linkIndex = links.findIndex(
116 (link) => link.linkRecord.uri === recordId.uri,
117 );
118 if (linkIndex !== -1) {
119 const removedLink = links.splice(linkIndex, 1)[0];
120 this.removedLinks.push({
121 cardId: removedLink!.cardId,
122 collectionId,
123 });
124 console.log(
125 `[FakeCollectionPublisher] Unpublished card-collection link ${recordId.uri}`,
126 );
127 return ok(undefined);
128 }
129 }
130
131 console.warn(
132 `[FakeCollectionPublisher] Card-collection link not found for unpublishing: ${recordId.uri}`,
133 );
134 return ok(undefined);
135 }
136
137 async unpublish(
138 recordId: PublishedRecordId,
139 ): Promise<Result<void, UseCaseError>> {
140 if (this.shouldFailUnpublish) {
141 return err(
142 AppError.UnexpectedError.create(
143 new Error('Simulated collection unpublish failure'),
144 ),
145 );
146 }
147
148 // Find and remove the collection by its published record ID
149 for (const [
150 collectionId,
151 collection,
152 ] of this.publishedCollections.entries()) {
153 if (collection.publishedRecordId?.uri === recordId.uri) {
154 this.publishedCollections.delete(collectionId);
155 this.publishedLinks.delete(collectionId);
156 this.unpublishedCollections.push({
157 uri: recordId.uri,
158 cid: recordId.cid,
159 });
160 console.log(
161 `[FakeCollectionPublisher] Unpublished collection ${recordId.uri}`,
162 );
163 return ok(undefined);
164 }
165 }
166 if (this.publishedCollections.size === 0) {
167 this.unpublishedCollections.push({
168 uri: recordId.uri,
169 cid: recordId.cid,
170 });
171 console.log(
172 `[FakeCollectionPublisher] Unpublished collection ${recordId.uri} (not found in published collections)`,
173 );
174 return ok(undefined);
175 }
176
177 console.warn(
178 `[FakeCollectionPublisher] Collection not found for unpublishing: ${recordId.uri}`,
179 );
180 return ok(undefined);
181 }
182
183 setShouldFail(shouldFail: boolean): void {
184 this.shouldFail = shouldFail;
185 }
186
187 setShouldFailUnpublish(shouldFailUnpublish: boolean): void {
188 this.shouldFailUnpublish = shouldFailUnpublish;
189 }
190
191 clear(): void {
192 this.publishedCollections.clear();
193 this.publishedLinks.clear();
194 this.unpublishedCollections = [];
195 this.removedLinks = [];
196 this.shouldFail = false;
197 this.shouldFailUnpublish = false;
198 }
199
200 getPublishedCollections(): Collection[] {
201 return Array.from(this.publishedCollections.values());
202 }
203
204 getPublishedLinksForCollection(
205 collectionId: string,
206 ): Array<{ cardId: string; linkRecord: PublishedRecordId }> {
207 return this.publishedLinks.get(collectionId) || [];
208 }
209
210 getUnpublishedCollections(): Array<{ uri: string; cid: string }> {
211 return this.unpublishedCollections;
212 }
213
214 getRemovedLinksForCollection(
215 collectionId: string,
216 ): Array<{ cardId: string; collectionId: string }> {
217 return this.removedLinks.filter(
218 (link) => link.collectionId === collectionId,
219 );
220 }
221
222 getAllRemovedLinks(): Array<{ cardId: string; collectionId: string }> {
223 return this.removedLinks;
224 }
225 getAllPublishedLinks() {
226 const allLinks: Array<{ cardId: string; linkRecord: PublishedRecordId }> =
227 [];
228 for (const links of this.publishedLinks.values()) {
229 allLinks.push(...links);
230 }
231 return allLinks;
232 }
233}