This repository has no description
1import { Result, ok, err } from 'src/shared/core/Result';
2import { UseCase } from 'src/shared/core/UseCase';
3import { AppError } from 'src/shared/core/AppError';
4import { IAtUriResolutionService } from '../../../cards/domain/services/IAtUriResolutionService';
5import { ATUri } from '../../domain/ATUri';
6import { Record as CollectionLinkRemovalRecord } from '../../infrastructure/lexicon/types/network/cosmik/collectionLinkRemoval';
7import {
8 UpdateUrlCardAssociationsUseCase,
9 OperationContext,
10} from '../../../cards/application/useCases/commands/UpdateUrlCardAssociationsUseCase';
11
12export interface ProcessCollectionLinkRemovalFirehoseEventDTO {
13 atUri: string;
14 cid: string | null;
15 eventType: 'create' | 'update' | 'delete';
16 record?: CollectionLinkRemovalRecord;
17}
18
19const ENABLE_FIREHOSE_LOGGING = true;
20
21export class ProcessCollectionLinkRemovalFirehoseEventUseCase
22 implements UseCase<ProcessCollectionLinkRemovalFirehoseEventDTO, Result<void>>
23{
24 constructor(
25 private atUriResolutionService: IAtUriResolutionService,
26 private updateUrlCardAssociationsUseCase: UpdateUrlCardAssociationsUseCase,
27 ) {}
28
29 async execute(
30 request: ProcessCollectionLinkRemovalFirehoseEventDTO,
31 ): Promise<Result<void>> {
32 try {
33 if (ENABLE_FIREHOSE_LOGGING) {
34 console.log(
35 `[FirehoseWorker] Processing collection link removal event: ${request.atUri} (${request.eventType})`,
36 );
37 }
38
39 switch (request.eventType) {
40 case 'create':
41 return await this.handleCollectionLinkRemovalCreate(request);
42 case 'delete':
43 // For now, we don't handle deletion events for collectionLinkRemoval
44 if (ENABLE_FIREHOSE_LOGGING) {
45 console.log(
46 `[FirehoseWorker] Collection link removal delete event (not handled): ${request.atUri}`,
47 );
48 }
49 break;
50 case 'update':
51 // Collection link removals don't typically have update operations
52 if (ENABLE_FIREHOSE_LOGGING) {
53 console.log(
54 `[FirehoseWorker] Collection link removal update event (unusual): ${request.atUri}`,
55 );
56 }
57 break;
58 }
59
60 return ok(undefined);
61 } catch (error) {
62 return err(AppError.UnexpectedError.create(error));
63 }
64 }
65
66 private async handleCollectionLinkRemovalCreate(
67 request: ProcessCollectionLinkRemovalFirehoseEventDTO,
68 ): Promise<Result<void>> {
69 if (!request.record || !request.cid) {
70 if (ENABLE_FIREHOSE_LOGGING) {
71 console.warn(
72 `[FirehoseWorker] Collection link removal create event missing record or cid, skipping: ${request.atUri}`,
73 );
74 }
75 return ok(undefined);
76 }
77
78 try {
79 // Parse AT URI to extract curator DID (the person who published the removal)
80 const atUriResult = ATUri.create(request.atUri);
81 if (atUriResult.isErr()) {
82 if (ENABLE_FIREHOSE_LOGGING) {
83 console.warn(
84 `[FirehoseWorker] Invalid AT URI format: ${request.atUri} - ${atUriResult.error.message}`,
85 );
86 }
87 return ok(undefined);
88 }
89 const removerDid = atUriResult.value.did.value;
90
91 // Resolve the collection link that's being removed
92 const collectionLinkUri = request.record.removedLink.uri;
93 const linkInfoResult =
94 await this.atUriResolutionService.resolveCollectionLinkId(
95 collectionLinkUri,
96 );
97
98 if (linkInfoResult.isErr()) {
99 if (ENABLE_FIREHOSE_LOGGING) {
100 console.warn(
101 `[FirehoseWorker] Failed to resolve collection link - remover: ${removerDid}, collectionLinkUri: ${collectionLinkUri}, removalUri: ${request.atUri}`,
102 );
103 }
104 return ok(undefined);
105 }
106
107 if (!linkInfoResult.value) {
108 if (ENABLE_FIREHOSE_LOGGING) {
109 console.log(
110 `[FirehoseWorker] Collection link not found in our system (may have already been removed) - remover: ${removerDid}, collectionLinkUri: ${collectionLinkUri}, removalUri: ${request.atUri}`,
111 );
112 }
113 return ok(undefined);
114 }
115
116 const { cardId, collectionId } = linkInfoResult.value;
117
118 // Remove the card from the collection using the context flag to skip publishing
119 const result = await this.updateUrlCardAssociationsUseCase.execute({
120 cardId: cardId.getStringValue(),
121 curatorId: removerDid,
122 removeFromCollections: [collectionId.getStringValue()],
123 context: OperationContext.FIREHOSE_EVENT, // This tells the use case to skip publishing
124 });
125
126 if (result.isErr()) {
127 if (ENABLE_FIREHOSE_LOGGING) {
128 console.warn(
129 `[FirehoseWorker] Failed to remove card from collection - remover: ${removerDid}, cardId: ${cardId.getStringValue()}, collectionId: ${collectionId.getStringValue()}, removalUri: ${request.atUri}, error: ${result.error.message}`,
130 );
131 }
132 return ok(undefined);
133 }
134
135 if (ENABLE_FIREHOSE_LOGGING) {
136 console.log(
137 `[FirehoseWorker] Successfully removed card from collection via removal record - remover: ${removerDid}, cardId: ${cardId.getStringValue()}, collectionId: ${collectionId.getStringValue()}, removalUri: ${request.atUri}`,
138 );
139 }
140 return ok(undefined);
141 } catch (error) {
142 if (ENABLE_FIREHOSE_LOGGING) {
143 console.error(
144 `[FirehoseWorker] Error processing collection link removal create event - uri: ${request.atUri}, error: ${error}`,
145 );
146 }
147 return ok(undefined); // Don't fail the firehose processing
148 }
149 }
150}