This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / modules / cards / domain / services / CardCollectionService.ts
7.0 kB 236 lines
1import { Result, ok, err } from '../../../../shared/core/Result'; 2import { Card } from '../Card'; 3import { Collection } from '../Collection'; 4import { CuratorId } from '../value-objects/CuratorId'; 5import { CollectionId } from '../value-objects/CollectionId'; 6import { ICollectionRepository } from '../ICollectionRepository'; 7import { ICollectionPublisher } from '../../application/ports/ICollectionPublisher'; 8import { AppError } from '../../../../shared/core/AppError'; 9import { DomainService } from '../../../../shared/domain/DomainService'; 10import { AuthenticationError } from '../../../../shared/core/AuthenticationError'; 11 12export class CardCollectionValidationError extends Error { 13 constructor(message: string) { 14 super(message); 15 this.name = 'CardCollectionValidationError'; 16 } 17} 18 19export class CardCollectionService implements DomainService { 20 constructor( 21 private collectionRepository: ICollectionRepository, 22 private collectionPublisher: ICollectionPublisher, 23 ) {} 24 25 async addCardToCollection( 26 card: Card, 27 collectionId: CollectionId, 28 curatorId: CuratorId, 29 ): Promise< 30 Result< 31 Collection, 32 | CardCollectionValidationError 33 | AuthenticationError 34 | AppError.UnexpectedError 35 > 36 > { 37 try { 38 // Find the collection 39 const collectionResult = 40 await this.collectionRepository.findById(collectionId); 41 if (collectionResult.isErr()) { 42 return err(AppError.UnexpectedError.create(collectionResult.error)); 43 } 44 45 const collection = collectionResult.value; 46 if (!collection) { 47 return err( 48 new CardCollectionValidationError( 49 `Collection not found: ${collectionId.getStringValue()}`, 50 ), 51 ); 52 } 53 54 // Add card to collection 55 const addCardResult = collection.addCard(card.cardId, curatorId); 56 if (addCardResult.isErr()) { 57 return err( 58 new CardCollectionValidationError( 59 `Failed to add card to collection: ${addCardResult.error.message}`, 60 ), 61 ); 62 } 63 64 // Publish the collection link 65 const publishLinkResult = 66 await this.collectionPublisher.publishCardAddedToCollection( 67 card, 68 collection, 69 curatorId, 70 ); 71 if (publishLinkResult.isErr()) { 72 // Propagate authentication errors 73 if (publishLinkResult.error instanceof AuthenticationError) { 74 return err(publishLinkResult.error); 75 } 76 return err( 77 new CardCollectionValidationError( 78 `Failed to publish collection link: ${publishLinkResult.error.message}`, 79 ), 80 ); 81 } 82 83 // Mark the card link as published in the collection 84 collection.markCardLinkAsPublished(card.cardId, publishLinkResult.value); 85 86 // Save the updated collection 87 const saveCollectionResult = 88 await this.collectionRepository.save(collection); 89 if (saveCollectionResult.isErr()) { 90 return err(AppError.UnexpectedError.create(saveCollectionResult.error)); 91 } 92 93 return ok(collection); 94 } catch (error) { 95 return err(AppError.UnexpectedError.create(error)); 96 } 97 } 98 99 async addCardToCollections( 100 card: Card, 101 collectionIds: CollectionId[], 102 curatorId: CuratorId, 103 ): Promise< 104 Result< 105 Collection[], 106 | CardCollectionValidationError 107 | AuthenticationError 108 | AppError.UnexpectedError 109 > 110 > { 111 const updatedCollections: Collection[] = []; 112 113 for (const collectionId of collectionIds) { 114 const result = await this.addCardToCollection( 115 card, 116 collectionId, 117 curatorId, 118 ); 119 if (result.isErr()) { 120 return err(result.error); 121 } 122 updatedCollections.push(result.value); 123 } 124 return ok(updatedCollections); 125 } 126 127 async removeCardFromCollection( 128 card: Card, 129 collectionId: CollectionId, 130 curatorId: CuratorId, 131 ): Promise< 132 Result< 133 Collection | null, 134 | CardCollectionValidationError 135 | AuthenticationError 136 | AppError.UnexpectedError 137 > 138 > { 139 try { 140 // Find the collection 141 const collectionResult = 142 await this.collectionRepository.findById(collectionId); 143 if (collectionResult.isErr()) { 144 return err(AppError.UnexpectedError.create(collectionResult.error)); 145 } 146 147 const collection = collectionResult.value; 148 if (!collection) { 149 return err( 150 new CardCollectionValidationError( 151 `Collection not found: ${collectionId.getStringValue()}`, 152 ), 153 ); 154 } 155 156 // Check if card is in collection 157 const cardLink = collection.cardLinks.find((link) => 158 link.cardId.equals(card.cardId), 159 ); 160 if (!cardLink) { 161 // Card is not in collection, nothing to do 162 return ok(null); 163 } 164 165 // If the card link was published, unpublish it 166 if (cardLink.publishedRecordId) { 167 const unpublishLinkResult = 168 await this.collectionPublisher.unpublishCardAddedToCollection( 169 cardLink.publishedRecordId, 170 ); 171 if (unpublishLinkResult.isErr()) { 172 // Propagate authentication errors 173 if (unpublishLinkResult.error instanceof AuthenticationError) { 174 return err(unpublishLinkResult.error); 175 } 176 return err( 177 new CardCollectionValidationError( 178 `Failed to unpublish collection link: ${unpublishLinkResult.error.message}`, 179 ), 180 ); 181 } 182 } 183 184 // Remove card from collection 185 const removeCardResult = collection.removeCard(card.cardId, curatorId); 186 if (removeCardResult.isErr()) { 187 return err( 188 new CardCollectionValidationError( 189 `Failed to remove card from collection: ${removeCardResult.error.message}`, 190 ), 191 ); 192 } 193 194 // Save the updated collection 195 const saveCollectionResult = 196 await this.collectionRepository.save(collection); 197 if (saveCollectionResult.isErr()) { 198 return err(AppError.UnexpectedError.create(saveCollectionResult.error)); 199 } 200 201 return ok(collection); 202 } catch (error) { 203 return err(AppError.UnexpectedError.create(error)); 204 } 205 } 206 207 async removeCardFromCollections( 208 card: Card, 209 collectionIds: CollectionId[], 210 curatorId: CuratorId, 211 ): Promise< 212 Result< 213 Collection[], 214 | CardCollectionValidationError 215 | AuthenticationError 216 | AppError.UnexpectedError 217 > 218 > { 219 const updatedCollections: Collection[] = []; 220 221 for (const collectionId of collectionIds) { 222 const result = await this.removeCardFromCollection( 223 card, 224 collectionId, 225 curatorId, 226 ); 227 if (result.isErr()) { 228 return err(result.error); 229 } 230 if (result.value !== null) { 231 updatedCollections.push(result.value); 232 } 233 } 234 return ok(updatedCollections); 235 } 236}