aboutsummaryrefslogtreecommitdiff
path: root/src/server/MemoryDatabase.ts
blob: 1432d91c456d088c0a2b6f96a18ce044d269e57a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import * as mongodb from 'mongodb';
import { DocumentsCollection, IDatabase } from './IDatabase';
import { Transferable } from './Message';

export class MemoryDatabase implements IDatabase {
    private db: { [collectionName: string]: { [id: string]: any } } = {};

    private getCollection(collectionName: string) {
        const collection = this.db[collectionName];
        if (collection) {
            return collection;
        }
        this.db[collectionName] = {};
        return {};
    }

    public getCollectionNames() {
        return Promise.resolve(Object.keys(this.db));
    }

    public update(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, _upsert?: boolean, collectionName = DocumentsCollection): Promise<void> {
        const collection = this.getCollection(collectionName);
        const set = '$set';
        if (set in value) {
            let currentVal = collection[id] ?? (collection[id] = {});
            const val = value[set];
            for (const key in val) {
                const keys = key.split('.');
                for (let i = 0; i < keys.length - 1; i++) {
                    const k = keys[i];
                    if (typeof currentVal[k] === 'object') {
                        currentVal = currentVal[k];
                    } else {
                        currentVal[k] = {};
                        currentVal = currentVal[k];
                    }
                }
                currentVal[keys[keys.length - 1]] = val[key];
            }
        } else {
            collection[id] = value;
        }
        callback(null as any, {} as any);
        return Promise.resolve(undefined);
    }

    public updateMany(/* query: any, update: any, collectionName = DocumentsCollection */): Promise<mongodb.UpdateResult> {
        throw new Error("Can't updateMany a MemoryDatabase");
    }

    public replace(id: string, value: any, callback: (err: mongodb.MongoError, res: mongodb.UpdateResult) => void, upsert?: boolean, collectionName = DocumentsCollection): void {
        this.update(id, value, callback, upsert, collectionName);
    }

    public delete(query: any, collectionName?: string): Promise<mongodb.DeleteResult>;
    // eslint-disable-next-line no-dupe-class-members
    public delete(id: string, collectionName?: string): Promise<mongodb.DeleteResult>;
    // eslint-disable-next-line no-dupe-class-members
    public delete(id: any, collectionName = DocumentsCollection): Promise<mongodb.DeleteResult> {
        const i = id.id ?? id;
        delete this.getCollection(collectionName)[i];

        return Promise.resolve({} as any);
    }

    public async dropSchema(...schemaNames: string[]): Promise<any> {
        const existing = await this.getCollectionNames();
        let valid: string[];
        if (schemaNames.length) {
            valid = schemaNames.filter(collection => existing.includes(collection));
        } else {
            valid = existing;
        }
        valid.forEach(schemaName => delete this.db[schemaName]);
        return Promise.resolve();
    }

    public insert(value: any, collectionName = DocumentsCollection): Promise<void> {
        const { id } = value;
        this.getCollection(collectionName)[id] = value;
        return Promise.resolve();
    }

    public getDocument(id: string, fn: (result?: Transferable) => void, collectionName = DocumentsCollection): void {
        fn(this.getCollection(collectionName)[id]);
    }
    public getDocuments(ids: string[], fn: (result: Transferable[]) => void, collectionName = DocumentsCollection): void {
        fn(ids.map(id => this.getCollection(collectionName)[id]));
    }

    public async visit(ids: string[], fn: (result: any) => string[] | Promise<string[]>, collectionName = DocumentsCollection): Promise<void> {
        const visited = new Set<string>();
        while (ids.length) {
            const count = Math.min(ids.length, 1000);
            const index = ids.length - count;
            const fetchIds = ids.splice(index, count).filter(id => !visited.has(id));
            if (fetchIds.length) {
                // eslint-disable-next-line no-await-in-loop
                const docs = await new Promise<{ [key: string]: any }[]>(res => {
                    this.getDocuments(fetchIds, res, collectionName);
                });
                // eslint-disable-next-line no-restricted-syntax
                for (const doc of docs) {
                    const { id } = doc;
                    visited.add(id);
                    // eslint-disable-next-line no-await-in-loop
                    ids.push(...(await fn(doc)));
                }
            }
        }
    }

    public query(): Promise<mongodb.FindCursor> {
        throw new Error("Can't query a MemoryDatabase");
    }
}