@sprucelabs/data-stores 25.7.6 → 25.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cursors/BatchCursor.d.ts +5 -3
- package/build/cursors/BatchCursor.js +12 -5
- package/build/esm/cursors/BatchCursor.d.ts +5 -3
- package/build/esm/cursors/BatchCursor.js +12 -5
- package/build/esm/factories/StoreFactory.d.ts +3 -3
- package/build/esm/stores/AbstractStore.d.ts +5 -4
- package/build/esm/stores/AbstractStore.js +14 -5
- package/build/esm/tests/databaseAssertUtil.d.ts +2 -2
- package/build/esm/types/database.types.d.ts +11 -0
- package/build/esm/types/stores.types.d.ts +2 -1
- package/build/esm/utilities/mongo.utility.js +7 -3
- package/build/factories/StoreFactory.d.ts +3 -3
- package/build/stores/AbstractStore.d.ts +5 -4
- package/build/stores/AbstractStore.js +14 -5
- package/build/tests/databaseAssertUtil.d.ts +2 -2
- package/build/types/database.types.d.ts +11 -0
- package/build/types/stores.types.d.ts +2 -1
- package/build/utilities/mongo.utility.js +7 -3
- package/package.json +1 -1
|
@@ -3,14 +3,16 @@ import AbstractStore from '../stores/AbstractStore';
|
|
|
3
3
|
import { QueryOptions } from '../types/query.types';
|
|
4
4
|
import { PrepareOptions } from '../types/stores.types';
|
|
5
5
|
export default class BatchCursorImpl<ResponseRecord> implements BatchCursor<ResponseRecord> {
|
|
6
|
+
static Class?: new (...args: any[]) => BatchCursor<any>;
|
|
6
7
|
private store;
|
|
7
8
|
private options?;
|
|
8
|
-
|
|
9
|
+
protected query?: Record<string, any>;
|
|
9
10
|
private nextHandler?;
|
|
10
|
-
private
|
|
11
|
+
private lastId?;
|
|
12
|
+
protected constructor(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions);
|
|
11
13
|
[Symbol.asyncIterator](): AsyncIterator<ResponseRecord, any, undefined>;
|
|
12
14
|
static Cursor<Response>(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions): BatchCursor<Response>;
|
|
13
|
-
setOnNextResults(cb:
|
|
15
|
+
setOnNextResults(cb: OnNextResultsHandler<ResponseRecord>): void;
|
|
14
16
|
getTotalRecords(): Promise<number>;
|
|
15
17
|
next(): Promise<ResponseRecord[] | null>;
|
|
16
18
|
private bumpCursorPosition;
|
|
@@ -27,8 +27,9 @@ class BatchCursorImpl {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
static Cursor(store, query, options) {
|
|
30
|
+
var _a;
|
|
30
31
|
(0, schema_1.assertOptions)({ store }, ['store'], 'You need to pass a store to BatchCursor.Cursor()');
|
|
31
|
-
return new this(store, query, options);
|
|
32
|
+
return new ((_a = this.Class) !== null && _a !== void 0 ? _a : this)(store, query, options);
|
|
32
33
|
}
|
|
33
34
|
setOnNextResults(cb) {
|
|
34
35
|
this.nextHandler = cb;
|
|
@@ -37,9 +38,15 @@ class BatchCursorImpl {
|
|
|
37
38
|
return this.store.count(Object.assign({}, this.query));
|
|
38
39
|
}
|
|
39
40
|
async next() {
|
|
40
|
-
var _a;
|
|
41
|
-
const
|
|
42
|
-
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const _c = (_a = this.options) !== null && _a !== void 0 ? _a : {}, { batchSize = 10 } = _c, rest = __rest(_c, ["batchSize"]);
|
|
43
|
+
let query = Object.assign({}, this.query);
|
|
44
|
+
if (this.lastId) {
|
|
45
|
+
query = {
|
|
46
|
+
$and: [(_b = this.query) !== null && _b !== void 0 ? _b : {}, { id: { $gt: this.lastId } }],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const matches = await this.store.find(query, {
|
|
43
50
|
limit: batchSize,
|
|
44
51
|
}, Object.assign({}, rest));
|
|
45
52
|
if (matches.length === 0) {
|
|
@@ -57,7 +64,7 @@ class BatchCursorImpl {
|
|
|
57
64
|
if (!this.query) {
|
|
58
65
|
this.query = {};
|
|
59
66
|
}
|
|
60
|
-
this.
|
|
67
|
+
this.lastId = last.id;
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
}
|
|
@@ -3,14 +3,16 @@ import AbstractStore from '../stores/AbstractStore';
|
|
|
3
3
|
import { QueryOptions } from '../types/query.types';
|
|
4
4
|
import { PrepareOptions } from '../types/stores.types';
|
|
5
5
|
export default class BatchCursorImpl<ResponseRecord> implements BatchCursor<ResponseRecord> {
|
|
6
|
+
static Class?: new (...args: any[]) => BatchCursor<any>;
|
|
6
7
|
private store;
|
|
7
8
|
private options?;
|
|
8
|
-
|
|
9
|
+
protected query?: Record<string, any>;
|
|
9
10
|
private nextHandler?;
|
|
10
|
-
private
|
|
11
|
+
private lastId?;
|
|
12
|
+
protected constructor(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions);
|
|
11
13
|
[Symbol.asyncIterator](): AsyncIterator<ResponseRecord, any, undefined>;
|
|
12
14
|
static Cursor<Response>(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions): BatchCursor<Response>;
|
|
13
|
-
setOnNextResults(cb:
|
|
15
|
+
setOnNextResults(cb: OnNextResultsHandler<ResponseRecord>): void;
|
|
14
16
|
getTotalRecords(): Promise<number>;
|
|
15
17
|
next(): Promise<ResponseRecord[] | null>;
|
|
16
18
|
private bumpCursorPosition;
|
|
@@ -34,8 +34,9 @@ export default class BatchCursorImpl {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
static Cursor(store, query, options) {
|
|
37
|
+
var _a;
|
|
37
38
|
assertOptions({ store }, ['store'], 'You need to pass a store to BatchCursor.Cursor()');
|
|
38
|
-
return new this(store, query, options);
|
|
39
|
+
return new ((_a = this.Class) !== null && _a !== void 0 ? _a : this)(store, query, options);
|
|
39
40
|
}
|
|
40
41
|
setOnNextResults(cb) {
|
|
41
42
|
this.nextHandler = cb;
|
|
@@ -46,10 +47,16 @@ export default class BatchCursorImpl {
|
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
next() {
|
|
49
|
-
var _a;
|
|
50
|
+
var _a, _b;
|
|
50
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
-
const
|
|
52
|
-
|
|
52
|
+
const _c = (_a = this.options) !== null && _a !== void 0 ? _a : {}, { batchSize = 10 } = _c, rest = __rest(_c, ["batchSize"]);
|
|
53
|
+
let query = Object.assign({}, this.query);
|
|
54
|
+
if (this.lastId) {
|
|
55
|
+
query = {
|
|
56
|
+
$and: [(_b = this.query) !== null && _b !== void 0 ? _b : {}, { id: { $gt: this.lastId } }],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const matches = yield this.store.find(query, {
|
|
53
60
|
limit: batchSize,
|
|
54
61
|
}, Object.assign({}, rest));
|
|
55
62
|
if (matches.length === 0) {
|
|
@@ -68,7 +75,7 @@ export default class BatchCursorImpl {
|
|
|
68
75
|
if (!this.query) {
|
|
69
76
|
this.query = {};
|
|
70
77
|
}
|
|
71
|
-
this.
|
|
78
|
+
this.lastId = last.id;
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Database } from '../types/database.types';
|
|
2
|
-
import {
|
|
2
|
+
import { DataStore, StoreMap, StoreName, StoreOptions } from '../types/stores.types';
|
|
3
3
|
interface StoreContructor {
|
|
4
|
-
Store(o: any): Promise<
|
|
4
|
+
Store(o: any): Promise<DataStore> | DataStore;
|
|
5
5
|
}
|
|
6
6
|
export default class StoreFactory {
|
|
7
7
|
private storeMap;
|
|
@@ -18,7 +18,7 @@ export default class StoreFactory {
|
|
|
18
18
|
Store<Name extends StoreName, Options extends StoreOptions<Name>>(name: Name, options?: Options): Promise<StoreMap[Name]>;
|
|
19
19
|
getStoreNames(): StoreName[];
|
|
20
20
|
setStoreClass(name: string, Class: StoreContructor): void;
|
|
21
|
-
setStore(name: StoreName, store:
|
|
21
|
+
setStore(name: StoreName, store: DataStore | null): void;
|
|
22
22
|
getStore<Name extends StoreName>(name: Name): Promise<StoreMap[Name]>;
|
|
23
23
|
static reset(): void;
|
|
24
24
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Schema, SchemaFieldNames, SchemaPartialValues, SchemaPublicFieldNames, SchemaValues } from '@sprucelabs/schema';
|
|
2
2
|
import { FindBatchOptions } from '../cursors/BatchCursor';
|
|
3
3
|
import AbstractMutexer from '../mutexers/AbstractMutexer';
|
|
4
|
-
import { Database } from '../types/database.types';
|
|
4
|
+
import { DataStorePlugin, Database } from '../types/database.types';
|
|
5
5
|
import { QueryBuilder, QueryOptions } from '../types/query.types';
|
|
6
|
-
import { PrepareOptions, PrepareResults, SaveOperations,
|
|
6
|
+
import { PrepareOptions, PrepareResults, SaveOperations, DataStore } from '../types/stores.types';
|
|
7
7
|
export default abstract class AbstractStore<FullSchema extends Schema, CreateSchema extends Schema = FullSchema, UpdateSchema extends Schema = CreateSchema, DatabaseSchema extends Schema = FullSchema, DatabaseRecord = SchemaValues<DatabaseSchema> & {
|
|
8
8
|
id: string;
|
|
9
|
-
}, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements
|
|
9
|
+
}, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements DataStore {
|
|
10
10
|
abstract readonly name: string;
|
|
11
11
|
protected abstract collectionName: string;
|
|
12
12
|
protected abstract createSchema: CreateSchema;
|
|
@@ -17,6 +17,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
|
|
|
17
17
|
protected db: Database;
|
|
18
18
|
protected primaryFieldNames: string[];
|
|
19
19
|
protected shouldMapLowerCaseToCamelCase: boolean;
|
|
20
|
+
protected plugins: DataStorePlugin[];
|
|
20
21
|
initialize?(): Promise<void>;
|
|
21
22
|
protected prepareRecord?<IncludePrivateFields extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(record: DatabaseRecord, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<PrepareResults<FullSchema, IncludePrivateFields>>;
|
|
22
23
|
protected willCreate?(values: CreateRecord): Promise<Omit<DatabaseRecord, 'id'>>;
|
|
@@ -33,7 +34,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
|
|
|
33
34
|
protected prepareAndNormalizeRecord<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(record: any, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>>;
|
|
34
35
|
private mapCasing;
|
|
35
36
|
create<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(values: CreateRecord[], options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>[]>;
|
|
36
|
-
createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema
|
|
37
|
+
createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> & {}>;
|
|
37
38
|
private get primaryFieldName();
|
|
38
39
|
findOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(query: QueryBuilder<QueryRecord>, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> | null>;
|
|
39
40
|
count(query?: QueryBuilder<QueryRecord>): Promise<number>;
|
|
@@ -30,6 +30,7 @@ export default class AbstractStore extends AbstractMutexer {
|
|
|
30
30
|
super();
|
|
31
31
|
this.primaryFieldNames = ['id'];
|
|
32
32
|
this.shouldMapLowerCaseToCamelCase = false;
|
|
33
|
+
this.plugins = [];
|
|
33
34
|
this.db = db;
|
|
34
35
|
if (collectionName) {
|
|
35
36
|
this.setCollectionName(collectionName);
|
|
@@ -100,7 +101,7 @@ export default class AbstractStore extends AbstractMutexer {
|
|
|
100
101
|
});
|
|
101
102
|
}
|
|
102
103
|
createOne(values, options) {
|
|
103
|
-
var _a, _b;
|
|
104
|
+
var _a, _b, _c;
|
|
104
105
|
return __awaiter(this, void 0, void 0, function* () {
|
|
105
106
|
try {
|
|
106
107
|
//@ts-ignore
|
|
@@ -114,10 +115,15 @@ export default class AbstractStore extends AbstractMutexer {
|
|
|
114
115
|
const toSave = normalizeSchemaValues(this.databaseSchema,
|
|
115
116
|
//@ts-ignore
|
|
116
117
|
databaseRecord, { shouldCreateEntityInstances: false });
|
|
118
|
+
let mixinValuesOnReturn = {};
|
|
119
|
+
for (const plugin of this.plugins) {
|
|
120
|
+
const r = yield ((_b = plugin.willCreateOne) === null || _b === void 0 ? void 0 : _b.call(plugin, toSave));
|
|
121
|
+
mixinValuesOnReturn = Object.assign(Object.assign({}, mixinValuesOnReturn), r === null || r === void 0 ? void 0 : r.valuesToMixinBeforeReturning);
|
|
122
|
+
}
|
|
117
123
|
const record = yield this.db.createOne(this.collectionName, toSave);
|
|
118
|
-
yield ((
|
|
124
|
+
yield ((_c = this.didCreate) === null || _c === void 0 ? void 0 : _c.call(this, record));
|
|
119
125
|
const normalized = yield this.prepareAndNormalizeRecord(record, options);
|
|
120
|
-
return normalized;
|
|
126
|
+
return Object.assign(Object.assign({}, normalized), mixinValuesOnReturn);
|
|
121
127
|
}
|
|
122
128
|
catch (err) {
|
|
123
129
|
const coded = errorUtil.transformToSpruceErrors(err, new SpruceError({
|
|
@@ -213,7 +219,7 @@ export default class AbstractStore extends AbstractMutexer {
|
|
|
213
219
|
});
|
|
214
220
|
}
|
|
215
221
|
findOneAndUpdate(query, updates, notFoundHandler, options = {}) {
|
|
216
|
-
var _a;
|
|
222
|
+
var _a, _b;
|
|
217
223
|
return __awaiter(this, void 0, void 0, function* () {
|
|
218
224
|
const { ops, updates: initialUpdates } = this.pluckOperations(updates);
|
|
219
225
|
try {
|
|
@@ -245,8 +251,11 @@ export default class AbstractStore extends AbstractMutexer {
|
|
|
245
251
|
for (const { name, value } of ops) {
|
|
246
252
|
normalizedValues[name] = value;
|
|
247
253
|
}
|
|
254
|
+
for (const plugin of this.plugins) {
|
|
255
|
+
yield ((_a = plugin.willUpdateOne) === null || _a === void 0 ? void 0 : _a.call(plugin, query, normalizedValues));
|
|
256
|
+
}
|
|
248
257
|
const results = yield this.db.updateOne(this.collectionName, query, normalizedValues);
|
|
249
|
-
yield ((
|
|
258
|
+
yield ((_b = this.didUpdate) === null || _b === void 0 ? void 0 : _b.call(this, current, results));
|
|
250
259
|
return this.prepareAndNormalizeRecord(results, options);
|
|
251
260
|
}
|
|
252
261
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database, Index, TestConnect, UniqueIndex } from '../types/database.types';
|
|
2
|
-
import {
|
|
2
|
+
import { DataStore } from '../types/stores.types';
|
|
3
3
|
declare const databaseAssertUtil: {
|
|
4
4
|
collectionName: string;
|
|
5
5
|
runSuite(connect: TestConnect, tests?: string[]): Promise<void>;
|
|
@@ -79,6 +79,6 @@ declare const databaseAssertUtil: {
|
|
|
79
79
|
_assertThrowsExpectedNotFoundOnUpdateOne(db: Database, query: Record<string, any>): Promise<void>;
|
|
80
80
|
_assert$orReturnsExpectedTotalRecords(db: Database, $or: Record<string, any>[], expected: number): Promise<void>;
|
|
81
81
|
_assertCanCreateMultiFieldIndex(connect: TestConnect, fields: string[]): Promise<void>;
|
|
82
|
-
assertHasLowerCaseToCamelCaseMappingEnabled(store:
|
|
82
|
+
assertHasLowerCaseToCamelCaseMappingEnabled(store: DataStore): void;
|
|
83
83
|
};
|
|
84
84
|
export default databaseAssertUtil;
|
|
@@ -39,3 +39,14 @@ export type TestConnect = (connectionString?: string, dbName?: string) => Promis
|
|
|
39
39
|
connectionStringWithRandomBadDatabaseName: string;
|
|
40
40
|
badDatabaseName: string;
|
|
41
41
|
}>;
|
|
42
|
+
export interface DataStorePlugin {
|
|
43
|
+
willCreateOne?: (values: Record<string, any>) => Promise<void | DataStorePluginHookResponse>;
|
|
44
|
+
willUpdateOne?: (query: Record<string, any>, updates: Record<string, any>) => Promise<void | DataStorePluginWillUpdateOneResponse>;
|
|
45
|
+
getName(): string;
|
|
46
|
+
}
|
|
47
|
+
export interface DataStorePluginHookResponse {
|
|
48
|
+
valuesToMixinBeforeReturning?: Record<string, any>;
|
|
49
|
+
}
|
|
50
|
+
export interface DataStorePluginWillUpdateOneResponse {
|
|
51
|
+
query?: Record<string, any>;
|
|
52
|
+
}
|
|
@@ -8,8 +8,9 @@ export interface UniversalStoreOptions {
|
|
|
8
8
|
db: Database;
|
|
9
9
|
storeFactory: StoreFactory;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface DataStore {
|
|
12
12
|
initialize?(): Promise<void>;
|
|
13
|
+
getCollectionName?(): string;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* @deprecated SimplifiedStoreFactory -> SimpleStoreFactory
|
|
@@ -15,7 +15,10 @@ const mongoUtil = {
|
|
|
15
15
|
mapQuery(query, options = {}) {
|
|
16
16
|
const q = query || {};
|
|
17
17
|
const opts = options;
|
|
18
|
-
|
|
18
|
+
const shouldTransformIdToObjectId = opts.shouldTransformToObjectId !== false;
|
|
19
|
+
if (Object.prototype.hasOwnProperty.call(q, 'id') &&
|
|
20
|
+
q.id === undefined &&
|
|
21
|
+
shouldTransformIdToObjectId) {
|
|
19
22
|
throw new SpruceError({
|
|
20
23
|
code: 'MONGO_ID_MAPPING_ERROR',
|
|
21
24
|
friendlyMessage: '`id` cannot be undefined',
|
|
@@ -30,8 +33,9 @@ const mongoUtil = {
|
|
|
30
33
|
normalizedValues.$or = $or.map((o) => this.mapQuery(o, options));
|
|
31
34
|
}
|
|
32
35
|
else if (typeof id === 'string') {
|
|
33
|
-
normalizedValues._id =
|
|
34
|
-
|
|
36
|
+
normalizedValues._id = !shouldTransformIdToObjectId
|
|
37
|
+
? id
|
|
38
|
+
: new ObjectId(id);
|
|
35
39
|
}
|
|
36
40
|
else if (id) {
|
|
37
41
|
normalizedValues._id = mapNestedIdValues(id, options);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Database } from '../types/database.types';
|
|
2
|
-
import {
|
|
2
|
+
import { DataStore, StoreMap, StoreName, StoreOptions } from '../types/stores.types';
|
|
3
3
|
interface StoreContructor {
|
|
4
|
-
Store(o: any): Promise<
|
|
4
|
+
Store(o: any): Promise<DataStore> | DataStore;
|
|
5
5
|
}
|
|
6
6
|
export default class StoreFactory {
|
|
7
7
|
private storeMap;
|
|
@@ -18,7 +18,7 @@ export default class StoreFactory {
|
|
|
18
18
|
Store<Name extends StoreName, Options extends StoreOptions<Name>>(name: Name, options?: Options): Promise<StoreMap[Name]>;
|
|
19
19
|
getStoreNames(): StoreName[];
|
|
20
20
|
setStoreClass(name: string, Class: StoreContructor): void;
|
|
21
|
-
setStore(name: StoreName, store:
|
|
21
|
+
setStore(name: StoreName, store: DataStore | null): void;
|
|
22
22
|
getStore<Name extends StoreName>(name: Name): Promise<StoreMap[Name]>;
|
|
23
23
|
static reset(): void;
|
|
24
24
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Schema, SchemaFieldNames, SchemaPartialValues, SchemaPublicFieldNames, SchemaValues } from '@sprucelabs/schema';
|
|
2
2
|
import { FindBatchOptions } from '../cursors/BatchCursor';
|
|
3
3
|
import AbstractMutexer from '../mutexers/AbstractMutexer';
|
|
4
|
-
import { Database } from '../types/database.types';
|
|
4
|
+
import { DataStorePlugin, Database } from '../types/database.types';
|
|
5
5
|
import { QueryBuilder, QueryOptions } from '../types/query.types';
|
|
6
|
-
import { PrepareOptions, PrepareResults, SaveOperations,
|
|
6
|
+
import { PrepareOptions, PrepareResults, SaveOperations, DataStore } from '../types/stores.types';
|
|
7
7
|
export default abstract class AbstractStore<FullSchema extends Schema, CreateSchema extends Schema = FullSchema, UpdateSchema extends Schema = CreateSchema, DatabaseSchema extends Schema = FullSchema, DatabaseRecord = SchemaValues<DatabaseSchema> & {
|
|
8
8
|
id: string;
|
|
9
|
-
}, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements
|
|
9
|
+
}, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements DataStore {
|
|
10
10
|
abstract readonly name: string;
|
|
11
11
|
protected abstract collectionName: string;
|
|
12
12
|
protected abstract createSchema: CreateSchema;
|
|
@@ -17,6 +17,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
|
|
|
17
17
|
protected db: Database;
|
|
18
18
|
protected primaryFieldNames: string[];
|
|
19
19
|
protected shouldMapLowerCaseToCamelCase: boolean;
|
|
20
|
+
protected plugins: DataStorePlugin[];
|
|
20
21
|
initialize?(): Promise<void>;
|
|
21
22
|
protected prepareRecord?<IncludePrivateFields extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(record: DatabaseRecord, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<PrepareResults<FullSchema, IncludePrivateFields>>;
|
|
22
23
|
protected willCreate?(values: CreateRecord): Promise<Omit<DatabaseRecord, 'id'>>;
|
|
@@ -33,7 +34,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
|
|
|
33
34
|
protected prepareAndNormalizeRecord<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(record: any, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>>;
|
|
34
35
|
private mapCasing;
|
|
35
36
|
create<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(values: CreateRecord[], options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>[]>;
|
|
36
|
-
createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema
|
|
37
|
+
createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> & {}>;
|
|
37
38
|
private get primaryFieldName();
|
|
38
39
|
findOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(query: QueryBuilder<QueryRecord>, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> | null>;
|
|
39
40
|
count(query?: QueryBuilder<QueryRecord>): Promise<number>;
|
|
@@ -49,6 +49,7 @@ class AbstractStore extends AbstractMutexer_1.default {
|
|
|
49
49
|
super();
|
|
50
50
|
this.primaryFieldNames = ['id'];
|
|
51
51
|
this.shouldMapLowerCaseToCamelCase = false;
|
|
52
|
+
this.plugins = [];
|
|
52
53
|
this.db = db;
|
|
53
54
|
if (collectionName) {
|
|
54
55
|
this.setCollectionName(collectionName);
|
|
@@ -115,7 +116,7 @@ class AbstractStore extends AbstractMutexer_1.default {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
async createOne(values, options) {
|
|
118
|
-
var _a, _b;
|
|
119
|
+
var _a, _b, _c;
|
|
119
120
|
try {
|
|
120
121
|
//@ts-ignore
|
|
121
122
|
(0, schema_1.validateSchemaValues)(this.createSchema, values);
|
|
@@ -128,10 +129,15 @@ class AbstractStore extends AbstractMutexer_1.default {
|
|
|
128
129
|
const toSave = (0, schema_1.normalizeSchemaValues)(this.databaseSchema,
|
|
129
130
|
//@ts-ignore
|
|
130
131
|
databaseRecord, { shouldCreateEntityInstances: false });
|
|
132
|
+
let mixinValuesOnReturn = {};
|
|
133
|
+
for (const plugin of this.plugins) {
|
|
134
|
+
const r = await ((_b = plugin.willCreateOne) === null || _b === void 0 ? void 0 : _b.call(plugin, toSave));
|
|
135
|
+
mixinValuesOnReturn = Object.assign(Object.assign({}, mixinValuesOnReturn), r === null || r === void 0 ? void 0 : r.valuesToMixinBeforeReturning);
|
|
136
|
+
}
|
|
131
137
|
const record = await this.db.createOne(this.collectionName, toSave);
|
|
132
|
-
await ((
|
|
138
|
+
await ((_c = this.didCreate) === null || _c === void 0 ? void 0 : _c.call(this, record));
|
|
133
139
|
const normalized = await this.prepareAndNormalizeRecord(record, options);
|
|
134
|
-
return normalized;
|
|
140
|
+
return Object.assign(Object.assign({}, normalized), mixinValuesOnReturn);
|
|
135
141
|
}
|
|
136
142
|
catch (err) {
|
|
137
143
|
const coded = error_utility_1.default.transformToSpruceErrors(err, new SpruceError_1.default({
|
|
@@ -212,7 +218,7 @@ class AbstractStore extends AbstractMutexer_1.default {
|
|
|
212
218
|
return this.db.update(this.collectionName, query, updates);
|
|
213
219
|
}
|
|
214
220
|
async findOneAndUpdate(query, updates, notFoundHandler, options = {}) {
|
|
215
|
-
var _a;
|
|
221
|
+
var _a, _b;
|
|
216
222
|
const { ops, updates: initialUpdates } = this.pluckOperations(updates);
|
|
217
223
|
try {
|
|
218
224
|
const isScrambled = this.isScrambled(initialUpdates);
|
|
@@ -243,8 +249,11 @@ class AbstractStore extends AbstractMutexer_1.default {
|
|
|
243
249
|
for (const { name, value } of ops) {
|
|
244
250
|
normalizedValues[name] = value;
|
|
245
251
|
}
|
|
252
|
+
for (const plugin of this.plugins) {
|
|
253
|
+
await ((_a = plugin.willUpdateOne) === null || _a === void 0 ? void 0 : _a.call(plugin, query, normalizedValues));
|
|
254
|
+
}
|
|
246
255
|
const results = await this.db.updateOne(this.collectionName, query, normalizedValues);
|
|
247
|
-
await ((
|
|
256
|
+
await ((_b = this.didUpdate) === null || _b === void 0 ? void 0 : _b.call(this, current, results));
|
|
248
257
|
return this.prepareAndNormalizeRecord(results, options);
|
|
249
258
|
}
|
|
250
259
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database, Index, TestConnect, UniqueIndex } from '../types/database.types';
|
|
2
|
-
import {
|
|
2
|
+
import { DataStore } from '../types/stores.types';
|
|
3
3
|
declare const databaseAssertUtil: {
|
|
4
4
|
collectionName: string;
|
|
5
5
|
runSuite(connect: TestConnect, tests?: string[]): Promise<void>;
|
|
@@ -79,6 +79,6 @@ declare const databaseAssertUtil: {
|
|
|
79
79
|
_assertThrowsExpectedNotFoundOnUpdateOne(db: Database, query: Record<string, any>): Promise<void>;
|
|
80
80
|
_assert$orReturnsExpectedTotalRecords(db: Database, $or: Record<string, any>[], expected: number): Promise<void>;
|
|
81
81
|
_assertCanCreateMultiFieldIndex(connect: TestConnect, fields: string[]): Promise<void>;
|
|
82
|
-
assertHasLowerCaseToCamelCaseMappingEnabled(store:
|
|
82
|
+
assertHasLowerCaseToCamelCaseMappingEnabled(store: DataStore): void;
|
|
83
83
|
};
|
|
84
84
|
export default databaseAssertUtil;
|
|
@@ -39,3 +39,14 @@ export type TestConnect = (connectionString?: string, dbName?: string) => Promis
|
|
|
39
39
|
connectionStringWithRandomBadDatabaseName: string;
|
|
40
40
|
badDatabaseName: string;
|
|
41
41
|
}>;
|
|
42
|
+
export interface DataStorePlugin {
|
|
43
|
+
willCreateOne?: (values: Record<string, any>) => Promise<void | DataStorePluginHookResponse>;
|
|
44
|
+
willUpdateOne?: (query: Record<string, any>, updates: Record<string, any>) => Promise<void | DataStorePluginWillUpdateOneResponse>;
|
|
45
|
+
getName(): string;
|
|
46
|
+
}
|
|
47
|
+
export interface DataStorePluginHookResponse {
|
|
48
|
+
valuesToMixinBeforeReturning?: Record<string, any>;
|
|
49
|
+
}
|
|
50
|
+
export interface DataStorePluginWillUpdateOneResponse {
|
|
51
|
+
query?: Record<string, any>;
|
|
52
|
+
}
|
|
@@ -8,8 +8,9 @@ export interface UniversalStoreOptions {
|
|
|
8
8
|
db: Database;
|
|
9
9
|
storeFactory: StoreFactory;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface DataStore {
|
|
12
12
|
initialize?(): Promise<void>;
|
|
13
|
+
getCollectionName?(): string;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* @deprecated SimplifiedStoreFactory -> SimpleStoreFactory
|
|
@@ -20,7 +20,10 @@ const mongoUtil = {
|
|
|
20
20
|
mapQuery(query, options = {}) {
|
|
21
21
|
const q = query || {};
|
|
22
22
|
const opts = options;
|
|
23
|
-
|
|
23
|
+
const shouldTransformIdToObjectId = opts.shouldTransformToObjectId !== false;
|
|
24
|
+
if (Object.prototype.hasOwnProperty.call(q, 'id') &&
|
|
25
|
+
q.id === undefined &&
|
|
26
|
+
shouldTransformIdToObjectId) {
|
|
24
27
|
throw new SpruceError_1.default({
|
|
25
28
|
code: 'MONGO_ID_MAPPING_ERROR',
|
|
26
29
|
friendlyMessage: '`id` cannot be undefined',
|
|
@@ -35,8 +38,9 @@ const mongoUtil = {
|
|
|
35
38
|
normalizedValues.$or = $or.map((o) => this.mapQuery(o, options));
|
|
36
39
|
}
|
|
37
40
|
else if (typeof id === 'string') {
|
|
38
|
-
normalizedValues._id =
|
|
39
|
-
|
|
41
|
+
normalizedValues._id = !shouldTransformIdToObjectId
|
|
42
|
+
? id
|
|
43
|
+
: new mongodb_1.ObjectId(id);
|
|
40
44
|
}
|
|
41
45
|
else if (id) {
|
|
42
46
|
normalizedValues._id = mapNestedIdValues(id, options);
|