datastore-api 4.0.0 → 4.0.3
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/dist/datastore-api.cjs.development.js +210 -206
- package/dist/datastore-api.cjs.development.js.map +1 -1
- package/dist/datastore-api.cjs.production.min.js +1 -1
- package/dist/datastore-api.cjs.production.min.js.map +1 -1
- package/dist/datastore-api.esm.js +211 -207
- package/dist/datastore-api.esm.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +1 -1
- package/src/lib/dstore-api-cloud.spec.ts +123 -123
- package/src/lib/dstore-api-emulator.spec.ts +131 -131
- package/src/lib/dstore-api-simulator.spec.ts +132 -132
- package/src/lib/dstore-api.ts +188 -188
- package/src/mock/index.ts +228 -228
- package/src/mock/operators.ts +1 -1
package/src/lib/dstore-api.ts
CHANGED
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
* Copyright (c) 2021, 2022, 2023 Dr. Maximillian Dornseif
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { AsyncLocalStorage } from 'async_hooks'
|
|
13
|
-
import { setImmediate } from 'timers/promises'
|
|
12
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
13
|
+
import { setImmediate } from 'timers/promises';
|
|
14
14
|
|
|
15
|
-
import { Datastore, Key, PathType, Query, Transaction } from '@google-cloud/datastore'
|
|
16
|
-
import { Entity, entity } from '@google-cloud/datastore/build/src/entity'
|
|
17
|
-
import { Operator, RunQueryInfo, RunQueryResponse } from '@google-cloud/datastore/build/src/query'
|
|
18
|
-
import { CommitResponse } from '@google-cloud/datastore/build/src/request'
|
|
15
|
+
import { Datastore, Key, PathType, Query, Transaction, PropertyFilter } from '@google-cloud/datastore';
|
|
16
|
+
import { Entity, entity } from '@google-cloud/datastore/build/src/entity';
|
|
17
|
+
import { Operator, RunQueryInfo, RunQueryResponse } from '@google-cloud/datastore/build/src/query';
|
|
18
|
+
import { CommitResponse } from '@google-cloud/datastore/build/src/request';
|
|
19
19
|
import {
|
|
20
20
|
assert,
|
|
21
21
|
assertIsArray,
|
|
@@ -23,44 +23,44 @@ import {
|
|
|
23
23
|
assertIsNumber,
|
|
24
24
|
assertIsObject,
|
|
25
25
|
assertIsString,
|
|
26
|
-
} from 'assertate-debug'
|
|
27
|
-
import Debug from 'debug'
|
|
28
|
-
import promClient from 'prom-client'
|
|
29
|
-
import { Writable } from 'ts-essentials'
|
|
26
|
+
} from 'assertate-debug';
|
|
27
|
+
import Debug from 'debug';
|
|
28
|
+
import promClient from 'prom-client';
|
|
29
|
+
import { Writable } from 'ts-essentials';
|
|
30
30
|
|
|
31
31
|
/** @ignore */
|
|
32
|
-
export { Datastore, Key, PathType, Query, Transaction } from '@google-cloud/datastore'
|
|
32
|
+
export { Datastore, Key, PathType, Query, Transaction } from '@google-cloud/datastore';
|
|
33
33
|
|
|
34
34
|
/** @ignore */
|
|
35
|
-
const debug = Debug('ds:api')
|
|
35
|
+
const debug = Debug('ds:api');
|
|
36
36
|
|
|
37
37
|
/** @ignore */
|
|
38
|
-
const transactionAsyncLocalStorage = new AsyncLocalStorage()
|
|
38
|
+
const transactionAsyncLocalStorage = new AsyncLocalStorage();
|
|
39
39
|
|
|
40
40
|
/** @ignore */
|
|
41
41
|
const metricHistogram = new promClient.Histogram({
|
|
42
42
|
name: 'dstore_requests_seconds',
|
|
43
43
|
help: 'How long did Datastore operations take?',
|
|
44
44
|
labelNames: ['operation'],
|
|
45
|
-
})
|
|
45
|
+
});
|
|
46
46
|
const metricFailureCounter = new promClient.Counter({
|
|
47
47
|
name: 'dstore_failures_total',
|
|
48
48
|
help: 'How many Datastore operations failed?',
|
|
49
49
|
labelNames: ['operation'],
|
|
50
|
-
})
|
|
50
|
+
});
|
|
51
51
|
|
|
52
52
|
/** Use instead of Datastore.KEY
|
|
53
53
|
*
|
|
54
54
|
* Even better: use `_key` instead.
|
|
55
55
|
*/
|
|
56
|
-
export const KEYSYM = Datastore.KEY
|
|
56
|
+
export const KEYSYM = Datastore.KEY;
|
|
57
57
|
|
|
58
|
-
export type IGqlFilterTypes = boolean | string | number
|
|
58
|
+
export type IGqlFilterTypes = boolean | string | number;
|
|
59
59
|
|
|
60
60
|
export type IGqlFilterSpec = {
|
|
61
|
-
readonly eq: IGqlFilterTypes
|
|
62
|
-
}
|
|
63
|
-
export type TGqlFilterList = Array<[string, Operator, DstorePropertyValues]
|
|
61
|
+
readonly eq: IGqlFilterTypes;
|
|
62
|
+
};
|
|
63
|
+
export type TGqlFilterList = Array<[string, Operator, DstorePropertyValues]>;
|
|
64
64
|
|
|
65
65
|
/** Define what can be written into the Datastore */
|
|
66
66
|
export type DstorePropertyValues =
|
|
@@ -73,11 +73,11 @@ export type DstorePropertyValues =
|
|
|
73
73
|
| Buffer
|
|
74
74
|
| Key
|
|
75
75
|
| DstorePropertyValues[]
|
|
76
|
-
| { [key: string]: DstorePropertyValues }
|
|
76
|
+
| { [key: string]: DstorePropertyValues };
|
|
77
77
|
|
|
78
78
|
export interface IDstoreEntryWithoutKey {
|
|
79
79
|
/** All User Data stored in the Datastore */
|
|
80
|
-
[key: string]: DstorePropertyValues
|
|
80
|
+
[key: string]: DstorePropertyValues;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/** Represents what is actually stored inside the Datastore, called "Entity" by Google
|
|
@@ -85,41 +85,41 @@ export interface IDstoreEntryWithoutKey {
|
|
|
85
85
|
*/
|
|
86
86
|
export interface IDstoreEntry extends IDstoreEntryWithoutKey {
|
|
87
87
|
/* Datastore key provided by [@google-cloud/datastore](https://github.com/googleapis/nodejs-datastore#readme) */
|
|
88
|
-
readonly [Datastore.KEY]?: Key
|
|
88
|
+
readonly [Datastore.KEY]?: Key;
|
|
89
89
|
/** [Datastore.KEY] key */
|
|
90
|
-
_keyStr: string
|
|
90
|
+
_keyStr: string;
|
|
91
91
|
/** All User Data stored in the Datastore */
|
|
92
|
-
[key: string]: DstorePropertyValues
|
|
92
|
+
[key: string]: DstorePropertyValues;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/** Represents the thing you pass to the save method. Also called "Entity" by Google */
|
|
96
96
|
export type DstoreSaveEntity = {
|
|
97
|
-
key: Key
|
|
97
|
+
key: Key;
|
|
98
98
|
data: Omit<IDstoreEntry, '_keyStr' | Datastore['KEY']> &
|
|
99
99
|
Partial<{
|
|
100
|
-
_keyStr: string
|
|
101
|
-
[Datastore.KEY]: Key
|
|
102
|
-
}
|
|
103
|
-
method?: 'insert' | 'update' | 'upsert'
|
|
104
|
-
excludeLargeProperties?: boolean
|
|
105
|
-
excludeFromIndexes?: readonly string[]
|
|
106
|
-
}
|
|
100
|
+
_keyStr: string;
|
|
101
|
+
[Datastore.KEY]: Key;
|
|
102
|
+
}>;
|
|
103
|
+
method?: 'insert' | 'update' | 'upsert';
|
|
104
|
+
excludeLargeProperties?: boolean;
|
|
105
|
+
excludeFromIndexes?: readonly string[];
|
|
106
|
+
};
|
|
107
107
|
|
|
108
108
|
type IDstore = {
|
|
109
109
|
/** Accessible by Users of the library. Keep in mind that you will access outside transactions created by [[runInTransaction]]. */
|
|
110
|
-
readonly datastore: Datastore
|
|
111
|
-
key: (path: ReadonlyArray<PathType>) => Key
|
|
112
|
-
keyFromSerialized: (text: string) => Key
|
|
113
|
-
keySerialize: (key: Key) => string
|
|
114
|
-
readKey: (entry: IDstoreEntry) => Key
|
|
115
|
-
get: (key: Key) => Promise<IDstoreEntry | null
|
|
116
|
-
getMulti: (keys: ReadonlyArray<Key>) => Promise<ReadonlyArray<IDstoreEntry | null
|
|
117
|
-
set: (key: Key, entry: IDstoreEntry) => Promise<Key
|
|
118
|
-
save: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined
|
|
119
|
-
insert: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined
|
|
120
|
-
update: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined
|
|
121
|
-
delete: (keys: readonly Key[]) => Promise<CommitResponse | undefined
|
|
122
|
-
createQuery: (kind: string) => Query
|
|
110
|
+
readonly datastore: Datastore;
|
|
111
|
+
key: (path: ReadonlyArray<PathType>) => Key;
|
|
112
|
+
keyFromSerialized: (text: string) => Key;
|
|
113
|
+
keySerialize: (key: Key) => string;
|
|
114
|
+
readKey: (entry: IDstoreEntry) => Key;
|
|
115
|
+
get: (key: Key) => Promise<IDstoreEntry | null>;
|
|
116
|
+
getMulti: (keys: ReadonlyArray<Key>) => Promise<ReadonlyArray<IDstoreEntry | null>>;
|
|
117
|
+
set: (key: Key, entry: IDstoreEntry) => Promise<Key>;
|
|
118
|
+
save: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>;
|
|
119
|
+
insert: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>;
|
|
120
|
+
update: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>;
|
|
121
|
+
delete: (keys: readonly Key[]) => Promise<CommitResponse | undefined>;
|
|
122
|
+
createQuery: (kind: string) => Query;
|
|
123
123
|
query: (
|
|
124
124
|
kind: string,
|
|
125
125
|
filters?: TGqlFilterList,
|
|
@@ -127,11 +127,11 @@ type IDstore = {
|
|
|
127
127
|
ordering?: readonly string[],
|
|
128
128
|
selection?: readonly string[],
|
|
129
129
|
cursor?: string
|
|
130
|
-
) => Promise<RunQueryResponse
|
|
131
|
-
runQuery: (query: Query | Omit<Query, 'run'>) => Promise<RunQueryResponse
|
|
132
|
-
allocateOneId: (kindName: string) => Promise<string
|
|
133
|
-
runInTransaction: <T>(func: { (): Promise<T>; (): T }) => Promise<T
|
|
134
|
-
}
|
|
130
|
+
) => Promise<RunQueryResponse>;
|
|
131
|
+
runQuery: (query: Query | Omit<Query, 'run'>) => Promise<RunQueryResponse>;
|
|
132
|
+
allocateOneId: (kindName: string) => Promise<string>;
|
|
133
|
+
runInTransaction: <T>(func: { (): Promise<T>; (): T }) => Promise<T>;
|
|
134
|
+
};
|
|
135
135
|
|
|
136
136
|
/** Dstore implements a slightly more accessible version of the [Google Cloud Datastore: Node.js Client](https://cloud.google.com/nodejs/docs/reference/datastore/latest)
|
|
137
137
|
|
|
@@ -157,9 +157,9 @@ Main differences:
|
|
|
157
157
|
This documentation also tries to document the little known idiosyncrasies of the [@google-cloud/datastore](https://github.com/googleapis/nodejs-datastore) library. See the corresponding functions.
|
|
158
158
|
*/
|
|
159
159
|
export class Dstore implements IDstore {
|
|
160
|
-
engine = 'Dstore'
|
|
161
|
-
engines: string[] = []
|
|
162
|
-
private readonly urlSaveKey = new entity.URLSafeKey()
|
|
160
|
+
engine = 'Dstore';
|
|
161
|
+
engines: string[] = [];
|
|
162
|
+
private readonly urlSaveKey = new entity.URLSafeKey();
|
|
163
163
|
|
|
164
164
|
/** Generate a Dstore instance for a specific [[Datastore]] instance.
|
|
165
165
|
|
|
@@ -177,13 +177,13 @@ export class Dstore implements IDstore {
|
|
|
177
177
|
```
|
|
178
178
|
*/
|
|
179
179
|
constructor(readonly datastore: Datastore, readonly projectId?: string, readonly logger?: string) {
|
|
180
|
-
assertIsObject(datastore)
|
|
181
|
-
this.engines.push(this.engine)
|
|
180
|
+
assertIsObject(datastore);
|
|
181
|
+
this.engines.push(this.engine);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/** Gets the Datastore or the current Transaction. */
|
|
185
185
|
private getDoT(): Transaction | Datastore {
|
|
186
|
-
return (transactionAsyncLocalStorage.getStore() as Transaction) || this.datastore
|
|
186
|
+
return (transactionAsyncLocalStorage.getStore() as Transaction) || this.datastore;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
/** `key()` creates a [[Key]] Object from a path.
|
|
@@ -195,7 +195,7 @@ export class Dstore implements IDstore {
|
|
|
195
195
|
* @category Datastore Drop-In
|
|
196
196
|
*/
|
|
197
197
|
key(path: readonly PathType[]): Key {
|
|
198
|
-
return this.datastore.key(path as Writable<typeof path>)
|
|
198
|
+
return this.datastore.key(path as Writable<typeof path>);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
/** `keyFromSerialized()` serializes [[Key]] to a string.
|
|
@@ -207,7 +207,7 @@ export class Dstore implements IDstore {
|
|
|
207
207
|
* @category Datastore Drop-In
|
|
208
208
|
*/
|
|
209
209
|
keySerialize(key: Key): string {
|
|
210
|
-
return key ? this.urlSaveKey.legacyEncode(this.projectId ?? '', key) : ''
|
|
210
|
+
return key ? this.urlSaveKey.legacyEncode(this.projectId ?? '', key) : '';
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
/** `keyFromSerialized()` deserializes a string created with [[keySerialize]] to a [[Key]].
|
|
@@ -217,7 +217,7 @@ export class Dstore implements IDstore {
|
|
|
217
217
|
* @category Datastore Drop-In
|
|
218
218
|
*/
|
|
219
219
|
keyFromSerialized(text: string): Key {
|
|
220
|
-
return this.urlSaveKey.legacyDecode(text)
|
|
220
|
+
return this.urlSaveKey.legacyDecode(text);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
/** `readKey()` extracts the [[Key]] from an [[IDstoreEntry]].
|
|
@@ -228,17 +228,17 @@ export class Dstore implements IDstore {
|
|
|
228
228
|
* @category Additional
|
|
229
229
|
*/
|
|
230
230
|
readKey(ent: IDstoreEntry): Key {
|
|
231
|
-
assertIsObject(ent)
|
|
232
|
-
let ret = ent[Datastore.KEY]
|
|
231
|
+
assertIsObject(ent);
|
|
232
|
+
let ret = ent[Datastore.KEY];
|
|
233
233
|
if (ent._keyStr && !ret) {
|
|
234
|
-
ret = this.keyFromSerialized(ent._keyStr)
|
|
234
|
+
ret = this.keyFromSerialized(ent._keyStr);
|
|
235
235
|
}
|
|
236
236
|
assertIsObject(
|
|
237
237
|
ret,
|
|
238
238
|
'entity[Datastore.KEY]/entity._keyStr',
|
|
239
239
|
`Entity is missing the datastore Key: ${JSON.stringify(ent)}`
|
|
240
|
-
)
|
|
241
|
-
return ret
|
|
240
|
+
);
|
|
241
|
+
return ret;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
/** `fixKeys()` is called for all [[IDstoreEntry]]sa returned from [[Dstore]].
|
|
@@ -252,13 +252,13 @@ export class Dstore implements IDstore {
|
|
|
252
252
|
): Array<IDstoreEntry | undefined> {
|
|
253
253
|
entities.forEach((x) => {
|
|
254
254
|
if (!!x?.[Datastore.KEY] && x[Datastore.KEY]) {
|
|
255
|
-
assertIsDefined(x[Datastore.KEY])
|
|
256
|
-
assertIsObject(x[Datastore.KEY])
|
|
255
|
+
assertIsDefined(x[Datastore.KEY]);
|
|
256
|
+
assertIsObject(x[Datastore.KEY]);
|
|
257
257
|
// Old TypesScript has problems with symbols as a property
|
|
258
|
-
x._keyStr = this.keySerialize(x[Datastore.KEY] as Key)
|
|
258
|
+
x._keyStr = this.keySerialize(x[Datastore.KEY] as Key);
|
|
259
259
|
}
|
|
260
|
-
})
|
|
261
|
-
return entities as Array<IDstoreEntry | undefined
|
|
260
|
+
});
|
|
261
|
+
return entities as Array<IDstoreEntry | undefined>;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
/** `get()` reads a [[IDstoreEntry]] from the Datastore.
|
|
@@ -277,11 +277,11 @@ export class Dstore implements IDstore {
|
|
|
277
277
|
* @category Datastore Drop-In
|
|
278
278
|
*/
|
|
279
279
|
async get(key: Key): Promise<IDstoreEntry | null> {
|
|
280
|
-
assertIsObject(key)
|
|
281
|
-
assert(!Array.isArray(key))
|
|
282
|
-
assert(key.path.length % 2 == 0, `key.path must be complete: ${JSON.stringify(key.path)}`)
|
|
283
|
-
const result = await this.getMulti([key])
|
|
284
|
-
return result?.[0] || null
|
|
280
|
+
assertIsObject(key);
|
|
281
|
+
assert(!Array.isArray(key));
|
|
282
|
+
assert(key.path.length % 2 == 0, `key.path must be complete: ${JSON.stringify(key.path)}`);
|
|
283
|
+
const result = await this.getMulti([key]);
|
|
284
|
+
return result?.[0] || null;
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
/** `getMulti()` reads several [[IDstoreEntry]]s from the Datastore.
|
|
@@ -303,28 +303,28 @@ export class Dstore implements IDstore {
|
|
|
303
303
|
*/
|
|
304
304
|
async getMulti(keys: readonly Key[]): Promise<Array<IDstoreEntry | null>> {
|
|
305
305
|
// assertIsArray(keys);
|
|
306
|
-
let results: (IDstoreEntry | null | undefined)[]
|
|
307
|
-
const metricEnd = metricHistogram.startTimer()
|
|
306
|
+
let results: (IDstoreEntry | null | undefined)[];
|
|
307
|
+
const metricEnd = metricHistogram.startTimer();
|
|
308
308
|
try {
|
|
309
309
|
results = this.fixKeys(
|
|
310
310
|
keys.length > 0 ? (await this.getDoT().get(keys as Writable<typeof keys>))?.[0] : []
|
|
311
|
-
)
|
|
311
|
+
);
|
|
312
312
|
} catch (error) {
|
|
313
|
-
metricFailureCounter.inc({ operation: 'get' })
|
|
314
|
-
await setImmediate()
|
|
315
|
-
throw new DstoreError('datastore.getMulti error', error as Error, { keys })
|
|
313
|
+
metricFailureCounter.inc({ operation: 'get' });
|
|
314
|
+
await setImmediate();
|
|
315
|
+
throw new DstoreError('datastore.getMulti error', error as Error, { keys });
|
|
316
316
|
} finally {
|
|
317
|
-
metricEnd({ operation: 'get' })
|
|
317
|
+
metricEnd({ operation: 'get' });
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
// Sort resulting entities by the keys they were requested with.
|
|
321
|
-
assertIsArray(results)
|
|
322
|
-
const entities = results as IDstoreEntry[]
|
|
323
|
-
const entitiesByKey: Record<string, IDstoreEntry> = {}
|
|
321
|
+
assertIsArray(results);
|
|
322
|
+
const entities = results as IDstoreEntry[];
|
|
323
|
+
const entitiesByKey: Record<string, IDstoreEntry> = {};
|
|
324
324
|
entities.forEach((entity) => {
|
|
325
|
-
entitiesByKey[JSON.stringify(entity[Datastore.KEY])] = entity
|
|
326
|
-
})
|
|
327
|
-
return keys.map((key) => entitiesByKey[JSON.stringify(key)] || null)
|
|
325
|
+
entitiesByKey[JSON.stringify(entity[Datastore.KEY])] = entity;
|
|
326
|
+
});
|
|
327
|
+
return keys.map((key) => entitiesByKey[JSON.stringify(key)] || null);
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
/** `set()` is addition to [[Datastore]]. It provides a classic Key-value Interface.
|
|
@@ -344,11 +344,11 @@ export class Dstore implements IDstore {
|
|
|
344
344
|
* @category Additional
|
|
345
345
|
*/
|
|
346
346
|
async set(key: Key, data: IDstoreEntryWithoutKey): Promise<Key> {
|
|
347
|
-
assertIsObject(key)
|
|
348
|
-
assertIsObject(data)
|
|
349
|
-
const saveEntity = { key, data }
|
|
350
|
-
await this.save([saveEntity])
|
|
351
|
-
return saveEntity.key
|
|
347
|
+
assertIsObject(key);
|
|
348
|
+
assertIsObject(data);
|
|
349
|
+
const saveEntity = { key, data };
|
|
350
|
+
await this.save([saveEntity]);
|
|
351
|
+
return saveEntity.key;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
/** `save()` is compatible to [Datastore.save()](https://cloud.google.com/nodejs/docs/reference/datastore/latest/datastore/datastore#_google_cloud_datastore_Datastore_save_member_1_).
|
|
@@ -385,32 +385,32 @@ export class Dstore implements IDstore {
|
|
|
385
385
|
* @category Datastore Drop-In
|
|
386
386
|
*/
|
|
387
387
|
async save(entities: readonly DstoreSaveEntity[]): Promise<CommitResponse | undefined> {
|
|
388
|
-
assertIsArray(entities)
|
|
389
|
-
let ret: CommitResponse | undefined
|
|
390
|
-
const metricEnd = metricHistogram.startTimer()
|
|
388
|
+
assertIsArray(entities);
|
|
389
|
+
let ret: CommitResponse | undefined;
|
|
390
|
+
const metricEnd = metricHistogram.startTimer();
|
|
391
391
|
try {
|
|
392
392
|
// Within Transaction we don't get any answer here!
|
|
393
393
|
// [ { mutationResults: [ [Object], [Object] ], indexUpdates: 51 } ]
|
|
394
394
|
for (const e of entities) {
|
|
395
|
-
assertIsObject(e.key)
|
|
396
|
-
assertIsObject(e.data)
|
|
397
|
-
this.fixKeys([e.data])
|
|
398
|
-
e.excludeLargeProperties = e.excludeLargeProperties === undefined ? true : e.excludeLargeProperties
|
|
399
|
-
e.data = { ...e.data, _keyStr: undefined }
|
|
395
|
+
assertIsObject(e.key);
|
|
396
|
+
assertIsObject(e.data);
|
|
397
|
+
this.fixKeys([e.data]);
|
|
398
|
+
e.excludeLargeProperties = e.excludeLargeProperties === undefined ? true : e.excludeLargeProperties;
|
|
399
|
+
e.data = { ...e.data, _keyStr: undefined };
|
|
400
400
|
}
|
|
401
|
-
ret = (await this.getDoT().save(entities)) || undefined
|
|
401
|
+
ret = (await this.getDoT().save(entities)) || undefined;
|
|
402
402
|
for (const e of entities) {
|
|
403
|
-
e.data[Datastore.KEY] = e.key
|
|
404
|
-
this.fixKeys([e.data])
|
|
403
|
+
e.data[Datastore.KEY] = e.key;
|
|
404
|
+
this.fixKeys([e.data]);
|
|
405
405
|
}
|
|
406
406
|
} catch (error) {
|
|
407
|
-
metricFailureCounter.inc({ operation: 'save' })
|
|
408
|
-
await setImmediate()
|
|
409
|
-
throw new DstoreError('datastore.save error', error as Error)
|
|
407
|
+
metricFailureCounter.inc({ operation: 'save' });
|
|
408
|
+
await setImmediate();
|
|
409
|
+
throw new DstoreError('datastore.save error', error as Error);
|
|
410
410
|
} finally {
|
|
411
|
-
metricEnd({ operation: 'save' })
|
|
411
|
+
metricEnd({ operation: 'save' });
|
|
412
412
|
}
|
|
413
|
-
return ret
|
|
413
|
+
return ret;
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
/** `insert()` is compatible to [Datastore.insert()](https://cloud.google.com/nodejs/docs/reference/datastore/latest/datastore/datastore#_google_cloud_datastore_Datastore_insert_member_1_).
|
|
@@ -432,20 +432,20 @@ export class Dstore implements IDstore {
|
|
|
432
432
|
* @category Datastore Drop-In
|
|
433
433
|
*/
|
|
434
434
|
async insert(entities: readonly DstoreSaveEntity[]): Promise<CommitResponse | undefined> {
|
|
435
|
-
assertIsArray(entities)
|
|
436
|
-
let ret: CommitResponse | undefined
|
|
437
|
-
const metricEnd = metricHistogram.startTimer()
|
|
435
|
+
assertIsArray(entities);
|
|
436
|
+
let ret: CommitResponse | undefined;
|
|
437
|
+
const metricEnd = metricHistogram.startTimer();
|
|
438
438
|
try {
|
|
439
|
-
ret = (await this.getDoT().insert(entities)) || undefined
|
|
439
|
+
ret = (await this.getDoT().insert(entities)) || undefined;
|
|
440
440
|
} catch (error) {
|
|
441
441
|
// console.error(error)
|
|
442
|
-
metricFailureCounter.inc({ operation: 'insert' })
|
|
443
|
-
await setImmediate()
|
|
444
|
-
throw new DstoreError('datastore.insert error', error as Error)
|
|
442
|
+
metricFailureCounter.inc({ operation: 'insert' });
|
|
443
|
+
await setImmediate();
|
|
444
|
+
throw new DstoreError('datastore.insert error', error as Error);
|
|
445
445
|
} finally {
|
|
446
|
-
metricEnd({ operation: 'insert' })
|
|
446
|
+
metricEnd({ operation: 'insert' });
|
|
447
447
|
}
|
|
448
|
-
return ret
|
|
448
|
+
return ret;
|
|
449
449
|
}
|
|
450
450
|
|
|
451
451
|
/** `update()` is compatible to [Datastore.update()](https://cloud.google.com/nodejs/docs/reference/datastore/latest/datastore/datastore#_google_cloud_datastore_Datastore_update_member_1_).
|
|
@@ -467,29 +467,29 @@ export class Dstore implements IDstore {
|
|
|
467
467
|
* @category Datastore Drop-In
|
|
468
468
|
*/
|
|
469
469
|
async update(entities: readonly DstoreSaveEntity[]): Promise<CommitResponse | undefined> {
|
|
470
|
-
assertIsArray(entities)
|
|
470
|
+
assertIsArray(entities);
|
|
471
471
|
|
|
472
|
-
entities.forEach((entity) => assertIsObject(entity.key))
|
|
472
|
+
entities.forEach((entity) => assertIsObject(entity.key));
|
|
473
473
|
entities.forEach((entity) =>
|
|
474
474
|
assert(
|
|
475
475
|
entity.key.path.length % 2 == 0,
|
|
476
476
|
`entity.key.path must be complete: ${JSON.stringify([entity.key.path, entity])}`
|
|
477
477
|
)
|
|
478
|
-
)
|
|
479
|
-
let ret: CommitResponse | undefined
|
|
480
|
-
const metricEnd = metricHistogram.startTimer()
|
|
478
|
+
);
|
|
479
|
+
let ret: CommitResponse | undefined;
|
|
480
|
+
const metricEnd = metricHistogram.startTimer();
|
|
481
481
|
|
|
482
482
|
try {
|
|
483
|
-
ret = (await this.getDoT().update(entities)) || undefined
|
|
483
|
+
ret = (await this.getDoT().update(entities)) || undefined;
|
|
484
484
|
} catch (error) {
|
|
485
485
|
// console.error(error)
|
|
486
|
-
metricFailureCounter.inc({ operation: 'update' })
|
|
487
|
-
await setImmediate()
|
|
488
|
-
throw new DstoreError('datastore.update error', error as Error)
|
|
486
|
+
metricFailureCounter.inc({ operation: 'update' });
|
|
487
|
+
await setImmediate();
|
|
488
|
+
throw new DstoreError('datastore.update error', error as Error);
|
|
489
489
|
} finally {
|
|
490
|
-
metricEnd({ operation: 'update' })
|
|
490
|
+
metricEnd({ operation: 'update' });
|
|
491
491
|
}
|
|
492
|
-
return ret
|
|
492
|
+
return ret;
|
|
493
493
|
}
|
|
494
494
|
|
|
495
495
|
/** `delete()` is compatible to [Datastore.delete()].
|
|
@@ -506,23 +506,23 @@ export class Dstore implements IDstore {
|
|
|
506
506
|
* @category Datastore Drop-In
|
|
507
507
|
*/
|
|
508
508
|
async delete(keys: readonly Key[]): Promise<CommitResponse | undefined> {
|
|
509
|
-
assertIsArray(keys)
|
|
510
|
-
keys.forEach((key) => assertIsObject(key))
|
|
509
|
+
assertIsArray(keys);
|
|
510
|
+
keys.forEach((key) => assertIsObject(key));
|
|
511
511
|
keys.forEach((key) =>
|
|
512
512
|
assert(key.path.length % 2 == 0, `key.path must be complete: ${JSON.stringify(key.path)}`)
|
|
513
|
-
)
|
|
514
|
-
let ret
|
|
515
|
-
const metricEnd = metricHistogram.startTimer()
|
|
513
|
+
);
|
|
514
|
+
let ret;
|
|
515
|
+
const metricEnd = metricHistogram.startTimer();
|
|
516
516
|
try {
|
|
517
|
-
ret = (await this.getDoT().delete(keys)) || undefined
|
|
517
|
+
ret = (await this.getDoT().delete(keys)) || undefined;
|
|
518
518
|
} catch (error) {
|
|
519
|
-
metricFailureCounter.inc({ operation: 'delete' })
|
|
520
|
-
await setImmediate()
|
|
521
|
-
throw new DstoreError('datastore.delete error', error as Error)
|
|
519
|
+
metricFailureCounter.inc({ operation: 'delete' });
|
|
520
|
+
await setImmediate();
|
|
521
|
+
throw new DstoreError('datastore.delete error', error as Error);
|
|
522
522
|
} finally {
|
|
523
|
-
metricEnd({ operation: 'delete' })
|
|
523
|
+
metricEnd({ operation: 'delete' });
|
|
524
524
|
}
|
|
525
|
-
return ret
|
|
525
|
+
return ret;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
/** `createQuery()` creates an "empty" [[Query]] Object.
|
|
@@ -535,25 +535,25 @@ export class Dstore implements IDstore {
|
|
|
535
535
|
*/
|
|
536
536
|
createQuery(kindName: string): Query {
|
|
537
537
|
try {
|
|
538
|
-
return this.getDoT().createQuery(kindName)
|
|
538
|
+
return this.getDoT().createQuery(kindName);
|
|
539
539
|
} catch (error) {
|
|
540
|
-
throw new DstoreError('datastore.createQuery error', error as Error)
|
|
540
|
+
throw new DstoreError('datastore.createQuery error', error as Error);
|
|
541
541
|
}
|
|
542
542
|
}
|
|
543
543
|
|
|
544
544
|
async runQuery(query: Query | Omit<Query, 'run'>): Promise<RunQueryResponse> {
|
|
545
|
-
let ret
|
|
546
|
-
const metricEnd = metricHistogram.startTimer()
|
|
545
|
+
let ret;
|
|
546
|
+
const metricEnd = metricHistogram.startTimer();
|
|
547
547
|
try {
|
|
548
|
-
const [entities, info]: [Entity[], RunQueryInfo] = await this.getDoT().runQuery(query as Query)
|
|
549
|
-
ret = [this.fixKeys(entities), info]
|
|
548
|
+
const [entities, info]: [Entity[], RunQueryInfo] = await this.getDoT().runQuery(query as Query);
|
|
549
|
+
ret = [this.fixKeys(entities), info];
|
|
550
550
|
} catch (error) {
|
|
551
|
-
await setImmediate()
|
|
552
|
-
throw new DstoreError('datastore.runQuery error', error as Error)
|
|
551
|
+
await setImmediate();
|
|
552
|
+
throw new DstoreError('datastore.runQuery error', error as Error);
|
|
553
553
|
} finally {
|
|
554
|
-
metricEnd({ operation: 'query' })
|
|
554
|
+
metricEnd({ operation: 'query' });
|
|
555
555
|
}
|
|
556
|
-
return ret as RunQueryResponse
|
|
556
|
+
return ret as RunQueryResponse;
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
/** `query()` combined [[createQuery]] and [[runQuery]] in a single call.
|
|
@@ -576,34 +576,34 @@ export class Dstore implements IDstore {
|
|
|
576
576
|
selection: readonly string[] = [],
|
|
577
577
|
cursor?: string
|
|
578
578
|
): Promise<RunQueryResponse> {
|
|
579
|
-
assertIsString(kindName)
|
|
580
|
-
assertIsArray(filters)
|
|
581
|
-
assertIsNumber(limit)
|
|
579
|
+
assertIsString(kindName);
|
|
580
|
+
assertIsArray(filters);
|
|
581
|
+
assertIsNumber(limit);
|
|
582
582
|
try {
|
|
583
|
-
const q = this.createQuery(kindName)
|
|
583
|
+
const q = this.createQuery(kindName);
|
|
584
584
|
for (const filterSpec of filters) {
|
|
585
|
-
assertIsObject(filterSpec)
|
|
585
|
+
assertIsObject(filterSpec);
|
|
586
586
|
// @ts-ignore
|
|
587
|
-
q.filter(...filterSpec)
|
|
587
|
+
q.filter(new PropertyFilter(...filterSpec));
|
|
588
588
|
}
|
|
589
589
|
for (const orderField of ordering) {
|
|
590
|
-
q.order(orderField)
|
|
590
|
+
q.order(orderField);
|
|
591
591
|
}
|
|
592
592
|
if (limit > 0) {
|
|
593
|
-
q.limit(limit)
|
|
593
|
+
q.limit(limit);
|
|
594
594
|
}
|
|
595
595
|
if (selection.length > 0) {
|
|
596
|
-
q.select(selection as any)
|
|
596
|
+
q.select(selection as any);
|
|
597
597
|
}
|
|
598
|
-
return await this.runQuery(q)
|
|
598
|
+
return await this.runQuery(q);
|
|
599
599
|
} catch (error) {
|
|
600
|
-
await setImmediate()
|
|
600
|
+
await setImmediate();
|
|
601
601
|
throw new DstoreError('datastore.query error', error as Error, {
|
|
602
602
|
kindName,
|
|
603
603
|
filters,
|
|
604
604
|
limit,
|
|
605
605
|
ordering,
|
|
606
|
-
})
|
|
606
|
+
});
|
|
607
607
|
}
|
|
608
608
|
}
|
|
609
609
|
|
|
@@ -619,10 +619,10 @@ export class Dstore implements IDstore {
|
|
|
619
619
|
* In fact the generated ID is namespaced via an incomplete [[Key]] of the given Kind.
|
|
620
620
|
*/
|
|
621
621
|
async allocateOneId(kindName = 'Numbering'): Promise<string> {
|
|
622
|
-
assertIsString(kindName)
|
|
623
|
-
const ret = (await this.datastore.allocateIds(this.key([kindName]), 1))[0][0].id
|
|
624
|
-
assertIsString(ret)
|
|
625
|
-
return ret
|
|
622
|
+
assertIsString(kindName);
|
|
623
|
+
const ret = (await this.datastore.allocateIds(this.key([kindName]), 1))[0][0].id;
|
|
624
|
+
assertIsString(ret);
|
|
625
|
+
return ret;
|
|
626
626
|
}
|
|
627
627
|
|
|
628
628
|
/** This tries to give high level access to transactions.
|
|
@@ -640,27 +640,27 @@ export class Dstore implements IDstore {
|
|
|
640
640
|
Most Applications today are running on "Firestore in Datastore Mode". Beware that the Datastore-Emulator fails with `error: 3 INVALID_ARGUMENT: Only ancestor queries are allowed inside transactions.` during [[runQuery]] while the Datastore on Google Infrastructure does not have such an restriction anymore as of 2022.
|
|
641
641
|
*/
|
|
642
642
|
async runInTransaction<T>(func: () => Promise<T>): Promise<T> {
|
|
643
|
-
let ret
|
|
644
|
-
const transaction: Transaction = this.datastore.transaction()
|
|
643
|
+
let ret;
|
|
644
|
+
const transaction: Transaction = this.datastore.transaction();
|
|
645
645
|
await transactionAsyncLocalStorage.run(transaction, async () => {
|
|
646
|
-
const [transactionInfo, transactionRunApiResponse] = await transaction.run()
|
|
647
|
-
let commitApiResponse
|
|
646
|
+
const [transactionInfo, transactionRunApiResponse] = await transaction.run();
|
|
647
|
+
let commitApiResponse;
|
|
648
648
|
try {
|
|
649
|
-
ret = await func()
|
|
649
|
+
ret = await func();
|
|
650
650
|
} catch (error) {
|
|
651
|
-
const rollbackInfo = await transaction.rollback()
|
|
651
|
+
const rollbackInfo = await transaction.rollback();
|
|
652
652
|
debug(
|
|
653
653
|
'Transaction failed, rollback initiated: %O %O %O %O',
|
|
654
654
|
transactionInfo,
|
|
655
655
|
transactionRunApiResponse,
|
|
656
656
|
rollbackInfo,
|
|
657
657
|
error
|
|
658
|
-
)
|
|
659
|
-
await setImmediate()
|
|
660
|
-
throw new DstoreError('datastore.transaction execution error', error as Error)
|
|
658
|
+
);
|
|
659
|
+
await setImmediate();
|
|
660
|
+
throw new DstoreError('datastore.transaction execution error', error as Error);
|
|
661
661
|
}
|
|
662
662
|
try {
|
|
663
|
-
commitApiResponse = (await transaction.commit())[0]
|
|
663
|
+
commitApiResponse = (await transaction.commit())[0];
|
|
664
664
|
} catch (error) {
|
|
665
665
|
debug(
|
|
666
666
|
'Transaction commit failed: %O %O %O %O ret: %O',
|
|
@@ -669,36 +669,36 @@ export class Dstore implements IDstore {
|
|
|
669
669
|
commitApiResponse,
|
|
670
670
|
error,
|
|
671
671
|
ret
|
|
672
|
-
)
|
|
673
|
-
await setImmediate()
|
|
674
|
-
throw new DstoreError('datastore.transaction execution error', error as Error)
|
|
672
|
+
);
|
|
673
|
+
await setImmediate();
|
|
674
|
+
throw new DstoreError('datastore.transaction execution error', error as Error);
|
|
675
675
|
}
|
|
676
|
-
})
|
|
677
|
-
return ret as T
|
|
676
|
+
});
|
|
677
|
+
return ret as T;
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
680
|
|
|
681
681
|
export class DstoreError extends Error {
|
|
682
|
-
public readonly extensions: Record<string, unknown
|
|
682
|
+
public readonly extensions: Record<string, unknown>;
|
|
683
683
|
public readonly originalError: Error | undefined;
|
|
684
|
-
readonly [key: string]: unknown
|
|
684
|
+
readonly [key: string]: unknown;
|
|
685
685
|
|
|
686
686
|
constructor(message: string, originalError: Error | undefined, extensions?: Record<string, unknown>) {
|
|
687
|
-
super(`${message}: ${originalError?.message}`)
|
|
687
|
+
super(`${message}: ${originalError?.message}`);
|
|
688
688
|
|
|
689
689
|
// if no name provided, use the default. defineProperty ensures that it stays non-enumerable
|
|
690
690
|
if (!this.name) {
|
|
691
|
-
Object.defineProperty(this, 'name', { value: 'DstoreError' })
|
|
691
|
+
Object.defineProperty(this, 'name', { value: 'DstoreError' });
|
|
692
692
|
}
|
|
693
693
|
// metadata: Metadata { internalRepr: Map(0) {}, options: {} },
|
|
694
|
-
this.originalError = originalError
|
|
695
|
-
this.extensions = { ...extensions }
|
|
694
|
+
this.originalError = originalError;
|
|
695
|
+
this.extensions = { ...extensions };
|
|
696
696
|
this.stack =
|
|
697
697
|
(this.stack?.split('\n')[0] || '') +
|
|
698
698
|
'\n' +
|
|
699
699
|
(originalError?.stack?.split('\n')?.slice(1)?.join('\n') || '') +
|
|
700
700
|
'\n' +
|
|
701
|
-
(this.stack?.split('\n')?.slice(1)?.join('\n') || '')
|
|
701
|
+
(this.stack?.split('\n')?.slice(1)?.join('\n') || '');
|
|
702
702
|
|
|
703
703
|
// These are usually present on Datastore Errors
|
|
704
704
|
// logger.error({ err: originalError, extensions }, message);
|