A social knowledge tool for researchers built on ATProto

feat: add CuratorId value object and include it in Annotation aggregate

+54
+7
src/modules/annotations/domain/aggregates/Annotation.ts
··· 7 7 import { Guard, IGuardArgument } from "src/shared/core/Guard"; 8 8 import { 9 9 AnnotationFieldId, 10 + AnnotationFieldId, 10 11 AnnotationId, 11 12 AnnotationNote, 12 13 AnnotationTemplateId, 14 + CuratorId, // Import the new value object 13 15 PublishedRecordId, 14 16 } from "../value-objects"; 15 17 ··· 19 21 >; 20 22 21 23 export interface AnnotationProps { 24 + curatorId: CuratorId; // Add the curator ID 22 25 url: URI; 23 26 annotationFieldId: AnnotationFieldId; 24 27 value: AnnotationValue; ··· 31 34 export class Annotation extends AggregateRoot<AnnotationProps> { 32 35 get annotationId(): AnnotationId { 33 36 return AnnotationId.create(this._id).getValue(); 37 + } 38 + get curatorId(): CuratorId { 39 + return this.props.curatorId; 34 40 } 35 41 get url(): URI { 36 42 return this.props.url; ··· 75 81 id?: UniqueEntityID 76 82 ): Result<Annotation> { 77 83 const guardArgs: IGuardArgument[] = [ 84 + { argument: props.curatorId, argumentName: "curatorId" }, // Add curatorId to guard 78 85 { argument: props.url, argumentName: "url" }, 79 86 { argument: props.annotationFieldId, argumentName: "annotationFieldId" }, 80 87 { argument: props.value, argumentName: "value" },
+47
src/modules/annotations/domain/value-objects/CuratorId.ts
··· 1 + import { ValueObject } from "../../../../shared/domain/ValueObject"; 2 + import { Result } from "../../../../shared/core/Result"; 3 + import { Guard } from "../../../../shared/core/Guard"; 4 + 5 + // Use the same validation as the generic DID value object 6 + // Allows did:plc and potentially did:web, adjust regex if needed 7 + const DID_REGEX = /^did:(plc:[a-zA-Z0-9]+|web:[a-zA-Z0-9.%-]+)$/; 8 + 9 + interface CuratorIdProps { 10 + value: string; // The DID string 11 + } 12 + 13 + /** 14 + * Represents the validated Decentralized Identifier (DID) of the curator 15 + * who created the annotation. 16 + */ 17 + export class CuratorId extends ValueObject<CuratorIdProps> { 18 + get value(): string { 19 + return this.props.value; 20 + } 21 + 22 + private constructor(props: CuratorIdProps) { 23 + super(props); 24 + } 25 + 26 + public static create(did: string): Result<CuratorId> { 27 + const guardResult = Guard.againstNullOrUndefined(did, "curatorId"); 28 + if (guardResult.isFailure) { 29 + return Result.fail<CuratorId>(guardResult.getErrorValue()); 30 + } 31 + 32 + const didTrimmed = did.trim(); 33 + if (didTrimmed.length === 0) { 34 + return Result.fail<CuratorId>("CuratorId cannot be empty."); 35 + } 36 + 37 + if (!DID_REGEX.test(didTrimmed)) { 38 + return Result.fail<CuratorId>(`Invalid CuratorId format (must be a valid DID): ${didTrimmed}`); 39 + } 40 + 41 + return Result.ok<CuratorId>(new CuratorId({ value: didTrimmed })); 42 + } 43 + 44 + toString(): string { 45 + return this.props.value; 46 + } 47 + }