A social knowledge tool for researchers built on ATProto
at main 116 lines 3.6 kB view raw
1import { Result, ok, err } from '../../../../../shared/core/Result'; 2import { UseCase } from '../../../../../shared/core/UseCase'; 3import { UseCaseError } from '../../../../../shared/core/UseCaseError'; 4import { AppError } from '../../../../../shared/core/AppError'; 5import { ICollectionRepository } from '../../../domain/ICollectionRepository'; 6import { Collection, CollectionAccessType } from '../../../domain/Collection'; 7import { CuratorId } from '../../../domain/value-objects/CuratorId'; 8import { ICollectionPublisher } from '../../ports/ICollectionPublisher'; 9import { AuthenticationError } from '../../../../../shared/core/AuthenticationError'; 10 11export interface CreateCollectionDTO { 12 name: string; 13 description?: string; 14 curatorId: string; 15} 16 17export interface CreateCollectionResponseDTO { 18 collectionId: string; 19} 20 21export class ValidationError extends UseCaseError { 22 constructor(message: string) { 23 super(message); 24 } 25} 26 27export class CreateCollectionUseCase 28 implements 29 UseCase< 30 CreateCollectionDTO, 31 Result< 32 CreateCollectionResponseDTO, 33 ValidationError | AuthenticationError | AppError.UnexpectedError 34 > 35 > 36{ 37 constructor( 38 private collectionRepository: ICollectionRepository, 39 private collectionPublisher: ICollectionPublisher, 40 ) {} 41 42 async execute( 43 request: CreateCollectionDTO, 44 ): Promise< 45 Result< 46 CreateCollectionResponseDTO, 47 ValidationError | AuthenticationError | AppError.UnexpectedError 48 > 49 > { 50 try { 51 // Validate and create CuratorId 52 const curatorIdResult = CuratorId.create(request.curatorId); 53 if (curatorIdResult.isErr()) { 54 return err( 55 new ValidationError( 56 `Invalid curator ID: ${curatorIdResult.error.message}`, 57 ), 58 ); 59 } 60 const curatorId = curatorIdResult.value; 61 62 // Create collection 63 const collectionResult = Collection.create({ 64 authorId: curatorId, 65 name: request.name, 66 description: request.description, 67 accessType: CollectionAccessType.CLOSED, 68 collaboratorIds: [], 69 createdAt: new Date(), 70 updatedAt: new Date(), 71 }); 72 73 if (collectionResult.isErr()) { 74 return err(new ValidationError(collectionResult.error.message)); 75 } 76 77 const collection = collectionResult.value; 78 79 // Save collection 80 const saveResult = await this.collectionRepository.save(collection); 81 if (saveResult.isErr()) { 82 return err(AppError.UnexpectedError.create(saveResult.error)); 83 } 84 85 // Publish collection 86 const publishResult = await this.collectionPublisher.publish(collection); 87 if (publishResult.isErr()) { 88 // Propagate authentication errors 89 if (publishResult.error instanceof AuthenticationError) { 90 return err(publishResult.error); 91 } 92 return err( 93 new ValidationError( 94 `Failed to publish collection: ${publishResult.error.message}`, 95 ), 96 ); 97 } 98 99 // Mark collection as published 100 collection.markAsPublished(publishResult.value); 101 102 // Save updated collection with published record ID 103 const saveUpdatedResult = 104 await this.collectionRepository.save(collection); 105 if (saveUpdatedResult.isErr()) { 106 return err(AppError.UnexpectedError.create(saveUpdatedResult.error)); 107 } 108 109 return ok({ 110 collectionId: collection.collectionId.getStringValue(), 111 }); 112 } catch (error) { 113 return err(AppError.UnexpectedError.create(error)); 114 } 115 } 116}