Skip to content

Commit 7905314

Browse files
committed
refactor(utils): add missing methods to InMemoryStorageProvider
- Add annotation Map for test isolation - Add snapshots Map for test isolation - Add searchHistory Map for test isolation - Implement addAnnotation, getAnnotations, updateAnnotation, deleteAnnotation - Implement toggleAnnotationVisibility, deleteAnnotationsByGraph - Implement saveSnapshot, getSnapshots, getSnapshot, deleteSnapshot, updateSnapshot - Implement pruneAutoSaveSnapshots, addSnapshot - Implement addSearchQuery, getSearchHistory, removeSearchQuery, clearSearchHistory - Update clear() to reset new Maps
1 parent d88b47b commit 7905314

File tree

1 file changed

+233
-0
lines changed

1 file changed

+233
-0
lines changed

packages/utils/src/storage/in-memory-storage-provider.ts

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import type {
2020
BatchAddResult,
2121
CatalogueStorageProvider,
2222
CreateListParams,
23+
GraphAnnotationStorage,
24+
GraphSnapshotStorage,
2325
ListStats,
2426
ShareAccessResult,
2527
} from './catalogue-storage-provider.js';
@@ -33,11 +35,17 @@ export class InMemoryStorageProvider implements CatalogueStorageProvider {
3335
private lists: Map<string, CatalogueList>;
3436
private entities: Map<string, CatalogueEntity>;
3537
private shares: Map<string, CatalogueShareRecord>;
38+
private annotations: Map<string, GraphAnnotationStorage>;
39+
private snapshots: Map<string, GraphSnapshotStorage>;
40+
private searchHistory: Map<string, { query: string; timestamp: Date }>;
3641

3742
constructor() {
3843
this.lists = new Map();
3944
this.entities = new Map();
4045
this.shares = new Map();
46+
this.annotations = new Map();
47+
this.snapshots = new Map();
48+
this.searchHistory = new Map();
4149
}
4250

4351
/**
@@ -48,6 +56,9 @@ export class InMemoryStorageProvider implements CatalogueStorageProvider {
4856
this.lists.clear();
4957
this.entities.clear();
5058
this.shares.clear();
59+
this.annotations.clear();
60+
this.snapshots.clear();
61+
this.searchHistory.clear();
5162
}
5263

5364
// ========== List Operations ==========
@@ -846,4 +857,226 @@ export class InMemoryStorageProvider implements CatalogueStorageProvider {
846857

847858
return addedIds;
848859
}
860+
861+
// ========== Annotation Operations ==========
862+
863+
async addAnnotation(annotation: Omit<GraphAnnotationStorage, 'id' | 'createdAt' | 'updatedAt'>): Promise<string> {
864+
const id = crypto.randomUUID();
865+
const timestamp = new Date();
866+
const storedAnnotation: GraphAnnotationStorage = {
867+
id,
868+
type: annotation.type,
869+
visible: annotation.visible ?? true,
870+
x: annotation.x,
871+
y: annotation.y,
872+
content: annotation.content,
873+
width: annotation.width,
874+
height: annotation.height,
875+
radius: annotation.radius,
876+
borderColor: annotation.borderColor,
877+
fillColor: annotation.fillColor,
878+
borderWidth: annotation.borderWidth,
879+
points: annotation.points,
880+
strokeColor: annotation.strokeColor,
881+
strokeWidth: annotation.strokeWidth,
882+
closed: annotation.closed,
883+
fontSize: annotation.fontSize,
884+
backgroundColor: annotation.backgroundColor,
885+
nodeId: annotation.nodeId,
886+
graphId: annotation.graphId,
887+
createdAt: timestamp,
888+
updatedAt: timestamp,
889+
};
890+
this.annotations.set(id, storedAnnotation);
891+
return id;
892+
}
893+
894+
async getAnnotations(graphId?: string): Promise<GraphAnnotationStorage[]> {
895+
const allAnnotations = Array.from(this.annotations.values());
896+
if (!graphId) {
897+
return allAnnotations;
898+
}
899+
return allAnnotations.filter(ann => ann.graphId === graphId);
900+
}
901+
902+
async updateAnnotation(annotationId: string, updates: {
903+
visible?: boolean;
904+
x?: number;
905+
y?: number;
906+
content?: string;
907+
}): Promise<void> {
908+
const annotation = this.annotations.get(annotationId);
909+
if (!annotation) {
910+
throw new Error('Annotation not found');
911+
}
912+
const updatedAnnotation: GraphAnnotationStorage = {
913+
...annotation,
914+
...updates,
915+
updatedAt: new Date(),
916+
};
917+
this.annotations.set(annotationId, updatedAnnotation);
918+
}
919+
920+
async deleteAnnotation(annotationId: string): Promise<void> {
921+
this.annotations.delete(annotationId);
922+
}
923+
924+
async toggleAnnotationVisibility(annotationId: string, visible: boolean): Promise<void> {
925+
const annotation = this.annotations.get(annotationId);
926+
if (!annotation) {
927+
throw new Error('Annotation not found');
928+
}
929+
const updatedAnnotation: GraphAnnotationStorage = {
930+
...annotation,
931+
visible,
932+
updatedAt: new Date(),
933+
};
934+
this.annotations.set(annotationId, updatedAnnotation);
935+
}
936+
937+
async deleteAnnotationsByGraph(graphId: string): Promise<void> {
938+
for (const [id, annotation] of this.annotations.entries()) {
939+
if (annotation.graphId === graphId) {
940+
this.annotations.delete(id);
941+
}
942+
}
943+
}
944+
945+
// ========== Snapshot Operations ==========
946+
947+
async saveSnapshot(snapshot: {
948+
name: string;
949+
nodes: string;
950+
edges: string;
951+
zoom: number;
952+
panX: number;
953+
panY: number;
954+
layoutType: string;
955+
nodePositions?: string;
956+
annotations?: string;
957+
isAutoSave?: boolean;
958+
}): Promise<string> {
959+
const id = crypto.randomUUID();
960+
const timestamp = new Date();
961+
const storedSnapshot: GraphSnapshotStorage = {
962+
id,
963+
name: snapshot.name,
964+
nodes: snapshot.nodes,
965+
edges: snapshot.edges,
966+
zoom: snapshot.zoom,
967+
panX: snapshot.panX,
968+
panY: snapshot.panY,
969+
layoutType: snapshot.layoutType,
970+
nodePositions: snapshot.nodePositions,
971+
annotations: snapshot.annotations,
972+
isAutoSave: snapshot.isAutoSave ?? false,
973+
createdAt: timestamp,
974+
updatedAt: timestamp,
975+
};
976+
this.snapshots.set(id, storedSnapshot);
977+
return id;
978+
}
979+
980+
async getSnapshots(): Promise<GraphSnapshotStorage[]> {
981+
return Array.from(this.snapshots.values());
982+
}
983+
984+
async getSnapshot(snapshotId: string): Promise<GraphSnapshotStorage | null> {
985+
return this.snapshots.get(snapshotId) ?? null;
986+
}
987+
988+
async deleteSnapshot(snapshotId: string): Promise<void> {
989+
this.snapshots.delete(snapshotId);
990+
}
991+
992+
async updateSnapshot(snapshotId: string, updates: {
993+
name?: string;
994+
nodes?: string;
995+
edges?: string;
996+
zoom?: number;
997+
panX?: number;
998+
panY?: number;
999+
layoutType?: string;
1000+
nodePositions?: string;
1001+
annotations?: string;
1002+
}): Promise<void> {
1003+
const snapshot = this.snapshots.get(snapshotId);
1004+
if (!snapshot) {
1005+
throw new Error('Snapshot not found');
1006+
}
1007+
const updatedSnapshot: GraphSnapshotStorage = {
1008+
...snapshot,
1009+
...updates,
1010+
updatedAt: new Date(),
1011+
};
1012+
this.snapshots.set(snapshotId, updatedSnapshot);
1013+
}
1014+
1015+
async pruneAutoSaveSnapshots(maxCount: number): Promise<void> {
1016+
const autoSnapshots = Array.from(this.snapshots.values())
1017+
.filter(snap => snap.isAutoSave)
1018+
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
1019+
1020+
if (autoSnapshots.length > maxCount) {
1021+
const toDelete = autoSnapshots.slice(0, autoSnapshots.length - maxCount);
1022+
for (const snap of toDelete) {
1023+
if (snap.id) {
1024+
this.snapshots.delete(snap.id);
1025+
}
1026+
}
1027+
}
1028+
}
1029+
1030+
async addSnapshot(snapshot: Omit<GraphSnapshotStorage, 'id' | 'createdAt' | 'updatedAt'>): Promise<string> {
1031+
const id = crypto.randomUUID();
1032+
const timestamp = new Date();
1033+
const storedSnapshot: GraphSnapshotStorage = {
1034+
id,
1035+
name: snapshot.name,
1036+
nodes: snapshot.nodes,
1037+
edges: snapshot.edges,
1038+
zoom: snapshot.zoom,
1039+
panX: snapshot.panX,
1040+
panY: snapshot.panY,
1041+
layoutType: snapshot.layoutType,
1042+
nodePositions: snapshot.nodePositions,
1043+
annotations: snapshot.annotations,
1044+
isAutoSave: snapshot.isAutoSave ?? false,
1045+
createdAt: timestamp,
1046+
updatedAt: timestamp,
1047+
};
1048+
this.snapshots.set(id, storedSnapshot);
1049+
return id;
1050+
}
1051+
1052+
// ========== Search History Operations ==========
1053+
1054+
async addSearchQuery(query: string, maxHistory: number = 50): Promise<void> {
1055+
const timestamp = new Date();
1056+
const id = crypto.randomUUID();
1057+
this.searchHistory.set(id, { query, timestamp });
1058+
1059+
// Prune old entries if exceeds max
1060+
const entries = Array.from(this.searchHistory.entries())
1061+
.sort(([, a], [, b]) => b.timestamp.getTime() - a.timestamp.getTime());
1062+
1063+
if (entries.length > maxHistory) {
1064+
for (const [idToDelete] of entries.slice(maxHistory)) {
1065+
this.searchHistory.delete(idToDelete);
1066+
}
1067+
}
1068+
}
1069+
1070+
async getSearchHistory(): Promise<Array<{ query: string; timestamp: Date }>> {
1071+
return Array.from(this.searchHistory.values())
1072+
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
1073+
}
1074+
1075+
async removeSearchQuery(queryId: string): Promise<void> {
1076+
this.searchHistory.delete(queryId);
1077+
}
1078+
1079+
async clearSearchHistory(): Promise<void> {
1080+
this.searchHistory.clear();
1081+
}
8491082
}

0 commit comments

Comments
 (0)