A social knowledge tool for researchers built on ATProto
1import { Result, ok, err } from '../../../../shared/core/Result';
2import { ICollectionRepository } from '../../domain/ICollectionRepository';
3import { Collection } from '../../domain/Collection';
4import { CollectionId } from '../../domain/value-objects/CollectionId';
5import { CardId } from '../../domain/value-objects/CardId';
6import { CuratorId } from '../../domain/value-objects/CuratorId';
7
8export class InMemoryCollectionRepository implements ICollectionRepository {
9 private static instance: InMemoryCollectionRepository;
10 private collections: Map<string, Collection> = new Map();
11
12 private constructor() {}
13
14 public static getInstance(): InMemoryCollectionRepository {
15 if (!InMemoryCollectionRepository.instance) {
16 InMemoryCollectionRepository.instance =
17 new InMemoryCollectionRepository();
18 }
19 return InMemoryCollectionRepository.instance;
20 }
21
22 private clone(collection: Collection): Collection {
23 // Simple clone - in a real implementation you'd want proper deep cloning
24 const collectionResult = Collection.create(
25 {
26 authorId: collection.authorId,
27 name: collection.name.value,
28 description: collection.description?.value,
29 accessType: collection.accessType,
30 collaboratorIds: collection.collaboratorIds,
31 cardLinks: collection.cardLinks,
32 cardCount: collection.cardCount,
33 publishedRecordId: collection.publishedRecordId,
34 createdAt: collection.createdAt,
35 updatedAt: collection.updatedAt,
36 },
37 collection.id,
38 );
39
40 if (collectionResult.isErr()) {
41 throw new Error(
42 `Failed to clone collection: ${collectionResult.error.message}`,
43 );
44 }
45
46 return collectionResult.value;
47 }
48
49 async findById(id: CollectionId): Promise<Result<Collection | null>> {
50 try {
51 const collection = this.collections.get(id.getStringValue());
52 return ok(collection ? this.clone(collection) : null);
53 } catch (error) {
54 return err(error as Error);
55 }
56 }
57
58 async findByCuratorId(curatorId: CuratorId): Promise<Result<Collection[]>> {
59 try {
60 const collections = Array.from(this.collections.values()).filter(
61 (collection) =>
62 collection.authorId.value === curatorId.value ||
63 collection.collaboratorIds.some((id) => id.value === curatorId.value),
64 );
65 return ok(collections.map((collection) => this.clone(collection)));
66 } catch (error) {
67 return err(error as Error);
68 }
69 }
70
71 async findByCardId(cardId: CardId): Promise<Result<Collection[]>> {
72 try {
73 const collections = Array.from(this.collections.values()).filter(
74 (collection) =>
75 collection.cardLinks.some(
76 (link) => link.cardId.getStringValue() === cardId.getStringValue(),
77 ),
78 );
79 return ok(collections.map((collection) => this.clone(collection)));
80 } catch (error) {
81 return err(error as Error);
82 }
83 }
84
85 async findByCuratorIdContainingCard(
86 authorId: CuratorId,
87 cardId: CardId,
88 ): Promise<Result<Collection[]>> {
89 try {
90 const collections = Array.from(this.collections.values()).filter(
91 (collection) =>
92 collection.authorId.value === authorId.value &&
93 collection.cardLinks.some(
94 (link) => link.cardId.getStringValue() === cardId.getStringValue(),
95 ),
96 );
97 return ok(collections.map((collection) => this.clone(collection)));
98 } catch (error) {
99 return err(error as Error);
100 }
101 }
102
103 async save(collection: Collection): Promise<Result<void>> {
104 try {
105 this.collections.set(
106 collection.collectionId.getStringValue(),
107 this.clone(collection),
108 );
109 return ok(undefined);
110 } catch (error) {
111 return err(error as Error);
112 }
113 }
114
115 async delete(collectionId: CollectionId): Promise<Result<void>> {
116 try {
117 this.collections.delete(collectionId.getStringValue());
118 return ok(undefined);
119 } catch (error) {
120 return err(error as Error);
121 }
122 }
123
124 // Helper methods for testing
125 public clear(): void {
126 this.collections.clear();
127 }
128
129 public getStoredCollection(id: CollectionId): Collection | undefined {
130 return this.collections.get(id.getStringValue());
131 }
132
133 public getAllCollections(): Collection[] {
134 return Array.from(this.collections.values()).map((collection) =>
135 this.clone(collection),
136 );
137 }
138}