datastore-api 6.0.2 → 6.2.0

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.
@@ -1,22 +1,22 @@
1
1
  /*
2
2
  * dstore.ts - Datastore Compatibility layer
3
- * Try to get a smoother api for transactions and such.
3
+ * Try to get a smoother API for transactions and such.
4
4
  * A little bit inspired by the Python2 ndb interface.
5
5
  * But without the ORM bits.
6
6
  *
7
7
  * In future https://github.com/graphql/dataloader might be used for batching.
8
8
  *
9
9
  * Created by Dr. Maximillian Dornseif 2021-12-05 in huwawi3backend 11.10.0
10
- * Copyright (c) 2021, 2022, 2023 Dr. Maximillian Dornseif
10
+ * Copyright (c) 2021, 2022, 2023, 2025 Dr. Maximillian Dornseif
11
11
  */
12
12
 
13
- import { AsyncLocalStorage } from 'async_hooks';
14
- import { setImmediate } from 'timers/promises';
13
+ import { AsyncLocalStorage } from 'async_hooks'
14
+ import { setImmediate } from 'timers/promises'
15
15
 
16
- import { Datastore, Key, PathType, Query, Transaction, PropertyFilter } from '@google-cloud/datastore';
17
- import { Entity, entity } from '@google-cloud/datastore/build/src/entity';
18
- import { Operator, RunQueryInfo, RunQueryResponse } from '@google-cloud/datastore/build/src/query';
19
- import { CommitResponse } from '@google-cloud/datastore/build/src/request';
16
+ import { Datastore, Key, PathType, Query, Transaction, PropertyFilter } from '@google-cloud/datastore'
17
+ import { Entity, entity } from '@google-cloud/datastore/build/src/entity'
18
+ import { Operator, RunQueryInfo, RunQueryResponse } from '@google-cloud/datastore/build/src/query'
19
+ import { CommitResponse } from '@google-cloud/datastore/build/src/request'
20
20
  import {
21
21
  assert,
22
22
  assertIsArray,
@@ -24,19 +24,19 @@ import {
24
24
  assertIsNumber,
25
25
  assertIsObject,
26
26
  assertIsString,
27
- } from 'assertate-debug';
28
- import Debug from 'debug';
29
- import promClient from 'prom-client';
30
- import { Writable } from 'ts-essentials';
27
+ } from 'assertate-debug'
28
+ import Debug from 'debug'
29
+ import promClient from 'prom-client'
30
+ import { Writable } from 'ts-essentials'
31
31
 
32
32
  /** @ignore */
33
- export { Datastore, Key, PathType, Query, Transaction } from '@google-cloud/datastore';
33
+ export { Datastore, Key, PathType, Query, Transaction } from '@google-cloud/datastore'
34
34
 
35
35
  /** @ignore */
36
- const debug = Debug('ds:api');
36
+ const debug = Debug('ds:api')
37
37
 
38
38
  /** @ignore */
39
- const transactionAsyncLocalStorage = new AsyncLocalStorage();
39
+ const transactionAsyncLocalStorage = new AsyncLocalStorage()
40
40
 
41
41
  // for HMR
42
42
  promClient.register.removeSingleMetric('dstore_requests_seconds')
@@ -46,25 +46,25 @@ const metricHistogram = new promClient.Histogram({
46
46
  name: 'dstore_requests_seconds',
47
47
  help: 'How long did Datastore operations take?',
48
48
  labelNames: ['operation'],
49
- });
49
+ })
50
50
  const metricFailureCounter = new promClient.Counter({
51
51
  name: 'dstore_failures_total',
52
52
  help: 'How many Datastore operations failed?',
53
53
  labelNames: ['operation'],
54
- });
54
+ })
55
55
 
56
56
  /** Use instead of Datastore.KEY
57
57
  *
58
58
  * Even better: use `_key` instead.
59
59
  */
60
- export const KEYSYM = Datastore.KEY;
60
+ export const KEYSYM = Datastore.KEY
61
61
 
62
- export type IGqlFilterTypes = boolean | string | number;
62
+ export type IGqlFilterTypes = boolean | string | number
63
63
 
64
64
  export type IGqlFilterSpec = {
65
- readonly eq: IGqlFilterTypes;
66
- };
67
- export type TGqlFilterList = Array<[string, Operator, DstorePropertyValues]>;
65
+ readonly eq: IGqlFilterTypes
66
+ }
67
+ export type TGqlFilterList = Array<[string, Operator, DstorePropertyValues]>
68
68
 
69
69
  /** Define what can be written into the Datastore */
70
70
  export type DstorePropertyValues =
@@ -77,11 +77,11 @@ export type DstorePropertyValues =
77
77
  | Buffer
78
78
  | Key
79
79
  | DstorePropertyValues[]
80
- | { [key: string]: DstorePropertyValues };
80
+ | { [key: string]: DstorePropertyValues }
81
81
 
82
82
  export interface IDstoreEntryWithoutKey {
83
83
  /** All User Data stored in the Datastore */
84
- [key: string]: DstorePropertyValues;
84
+ [key: string]: DstorePropertyValues
85
85
  }
86
86
 
87
87
  /** Represents what is actually stored inside the Datastore, called "Entity" by Google
@@ -89,39 +89,47 @@ export interface IDstoreEntryWithoutKey {
89
89
  */
90
90
  export interface IDstoreEntry extends IDstoreEntryWithoutKey {
91
91
  /* Datastore Key provided by [@google-cloud/datastore](https://github.com/googleapis/nodejs-datastore#readme) */
92
- readonly [Datastore.KEY]?: Key;
92
+ readonly [Datastore.KEY]?: Key
93
93
  /** [Datastore.KEY] key */
94
- _keyStr: string;
94
+ _keyStr: string
95
95
  }
96
96
 
97
97
  /** Represents the thing you pass to the save method. Also called "Entity" by Google */
98
98
  export type DstoreSaveEntity = {
99
- key: Key;
99
+ key: Key
100
100
  data: Omit<IDstoreEntry, '_keyStr' | Datastore['KEY']> &
101
- Partial<{
102
- _keyStr: string|undefined;
103
- [Datastore.KEY]: Key;
104
- }>;
105
- method?: 'insert' | 'update' | 'upsert';
106
- excludeLargeProperties?: boolean;
107
- excludeFromIndexes?: readonly string[];
108
- };
101
+ Partial<{
102
+ _keyStr: string | undefined;
103
+ [Datastore.KEY]: Key
104
+ }>
105
+ method?: 'insert' | 'update' | 'upsert'
106
+ excludeLargeProperties?: boolean
107
+ excludeFromIndexes?: readonly string[]
108
+ }
109
109
 
110
+ export interface IIterateParams {
111
+ kindName: string,
112
+ filters?: TGqlFilterList,
113
+ limit?: number,
114
+ ordering?: readonly string[],
115
+ selection?: readonly string[],
116
+ }
110
117
  type IDstore = {
111
118
  /** Accessible by Users of the library. Keep in mind that you will access outside transactions created by [[runInTransaction]]. */
112
- readonly datastore: Datastore;
113
- key: (path: ReadonlyArray<PathType>) => Key;
114
- keyFromSerialized: (text: string) => Key;
115
- keySerialize: (key: Key) => string;
116
- readKey: (entry: IDstoreEntry) => Key;
117
- get: (key: Key) => Promise<IDstoreEntry | null>;
118
- getMulti: (keys: ReadonlyArray<Key>) => Promise<ReadonlyArray<IDstoreEntry | null>>;
119
- set: (key: Key, entry: IDstoreEntry) => Promise<Key>;
120
- save: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>;
121
- insert: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>;
122
- update: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>;
123
- delete: (keys: readonly Key[]) => Promise<CommitResponse | undefined>;
124
- createQuery: (kind: string) => Query;
119
+ readonly datastore: Datastore
120
+ key: (path: ReadonlyArray<PathType>) => Key
121
+ keyFromSerialized: (text: string) => Key
122
+ keySerialize: (key: Key) => string
123
+ readKey: (entry: IDstoreEntry) => Key
124
+ get: (key: Key) => Promise<IDstoreEntry | null>
125
+ getMulti: (keys: ReadonlyArray<Key>) => Promise<ReadonlyArray<IDstoreEntry | null>>
126
+ set: (key: Key, entry: IDstoreEntry) => Promise<Key>
127
+ save: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>
128
+ insert: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>
129
+ update: (entities: readonly DstoreSaveEntity[]) => Promise<CommitResponse | undefined>
130
+ delete: (keys: readonly Key[]) => Promise<CommitResponse | undefined>
131
+ createQuery: (kind: string) => Query
132
+ runQuery: (query: Query | Omit<Query, 'run'>) => Promise<RunQueryResponse>
125
133
  query: (
126
134
  kind: string,
127
135
  filters?: TGqlFilterList,
@@ -129,11 +137,11 @@ type IDstore = {
129
137
  ordering?: readonly string[],
130
138
  selection?: readonly string[],
131
139
  cursor?: string
132
- ) => Promise<RunQueryResponse>;
133
- runQuery: (query: Query | Omit<Query, 'run'>) => Promise<RunQueryResponse>;
134
- allocateOneId: (kindName: string) => Promise<string>;
135
- runInTransaction: <T>(func: { (): Promise<T>; (): T }) => Promise<T>;
136
- };
140
+ ) => Promise<RunQueryResponse>
141
+ iterate: (options: IIterateParams) => AsyncIterable<IDstoreEntry>
142
+ allocateOneId: (kindName: string) => Promise<string>
143
+ runInTransaction: <T>(func: { (): Promise<T>; (): T }) => Promise<T>
144
+ }
137
145
 
138
146
  /** Dstore implements a slightly more accessible version of the [Google Cloud Datastore: Node.js Client](https://cloud.google.com/nodejs/docs/reference/datastore/latest)
139
147
 
@@ -179,13 +187,13 @@ export class Dstore implements IDstore {
179
187
  ```
180
188
  */
181
189
  constructor(readonly datastore: Datastore, readonly projectId?: string, readonly logger?: string) {
182
- assertIsObject(datastore);
183
- this.engines.push(this.engine);
190
+ assertIsObject(datastore)
191
+ this.engines.push(this.engine)
184
192
  }
185
193
 
186
194
  /** Gets the Datastore or the current Transaction. */
187
195
  private getDoT(): Transaction | Datastore {
188
- return (transactionAsyncLocalStorage.getStore() as Transaction) || this.datastore;
196
+ return (transactionAsyncLocalStorage.getStore() as Transaction) || this.datastore
189
197
  }
190
198
 
191
199
  /** `key()` creates a [[Key]] Object from a path.
@@ -197,7 +205,7 @@ export class Dstore implements IDstore {
197
205
  * @category Datastore Drop-In
198
206
  */
199
207
  key(path: readonly PathType[]): Key {
200
- return this.datastore.key(path as Writable<typeof path>);
208
+ return this.datastore.key(path as Writable<typeof path>)
201
209
  }
202
210
 
203
211
  /** `keyFromSerialized()` serializes [[Key]] to a string.
@@ -209,7 +217,7 @@ export class Dstore implements IDstore {
209
217
  * @category Datastore Drop-In
210
218
  */
211
219
  keySerialize(key: Key): string {
212
- return key ? this.urlSaveKey.legacyEncode(this.projectId ?? '', key) : '';
220
+ return key ? this.urlSaveKey.legacyEncode(this.projectId ?? '', key) : ''
213
221
  }
214
222
 
215
223
  /** `keyFromSerialized()` deserializes a string created with [[keySerialize]] to a [[Key]].
@@ -219,7 +227,7 @@ export class Dstore implements IDstore {
219
227
  * @category Datastore Drop-In
220
228
  */
221
229
  keyFromSerialized(text: string): Key {
222
- return this.urlSaveKey.legacyDecode(text);
230
+ return this.urlSaveKey.legacyDecode(text)
223
231
  }
224
232
 
225
233
  /** `readKey()` extracts the [[Key]] from an [[IDstoreEntry]].
@@ -230,20 +238,20 @@ export class Dstore implements IDstore {
230
238
  * @category Additional
231
239
  */
232
240
  readKey(ent: IDstoreEntry): Key {
233
- assertIsObject(ent);
234
- let ret = ent[Datastore.KEY];
241
+ assertIsObject(ent)
242
+ let ret = ent[Datastore.KEY]
235
243
  if (ent._keyStr && !ret) {
236
- ret = this.keyFromSerialized(ent._keyStr);
244
+ ret = this.keyFromSerialized(ent._keyStr)
237
245
  }
238
246
  assertIsObject(
239
247
  ret,
240
248
  'entity[Datastore.KEY]/entity._keyStr',
241
249
  `Entity is missing the datastore Key: ${JSON.stringify(ent)}`
242
- );
243
- return ret;
250
+ )
251
+ return ret
244
252
  }
245
253
 
246
- /** `fixKeys()` is called for all [[IDstoreEntry]]sa returned from [[Dstore]].
254
+ /** `fixKeys()` is called for all [[IDstoreEntry]] returned from [[Dstore]].
247
255
  *
248
256
  * Is ensures that besides `entity[Datastore.KEY]` there is `_keyStr` to be leveraged by [[readKey]].
249
257
  *
@@ -254,13 +262,13 @@ export class Dstore implements IDstore {
254
262
  ): Array<IDstoreEntry | undefined> {
255
263
  entities.forEach((x) => {
256
264
  if (!!x?.[Datastore.KEY] && x[Datastore.KEY]) {
257
- assertIsDefined(x[Datastore.KEY]);
258
- assertIsObject(x[Datastore.KEY]);
265
+ assertIsDefined(x[Datastore.KEY])
266
+ assertIsObject(x[Datastore.KEY])
259
267
  // Old TypesScript has problems with symbols as a property
260
- x._keyStr = this.keySerialize(x[Datastore.KEY] as Key);
268
+ x._keyStr = this.keySerialize(x[Datastore.KEY] as Key)
261
269
  }
262
- });
263
- return entities as Array<IDstoreEntry | undefined>;
270
+ })
271
+ return entities as Array<IDstoreEntry | undefined>
264
272
  }
265
273
 
266
274
  /** `get()` reads a [[IDstoreEntry]] from the Datastore.
@@ -279,11 +287,11 @@ export class Dstore implements IDstore {
279
287
  * @category Datastore Drop-In
280
288
  */
281
289
  async get(key: Key): Promise<IDstoreEntry | null> {
282
- assertIsObject(key);
283
- assert(!Array.isArray(key));
284
- assert(key.path.length % 2 == 0, `key.path must be complete: ${JSON.stringify(key.path)}`);
285
- const result = await this.getMulti([key]);
286
- return result?.[0] || null;
290
+ assertIsObject(key)
291
+ assert(!Array.isArray(key))
292
+ assert(key.path.length % 2 == 0, `key.path must be complete: ${JSON.stringify(key.path)}`)
293
+ const result = await this.getMulti([key])
294
+ return result?.[0] || null
287
295
  }
288
296
 
289
297
  /** `getMulti()` reads several [[IDstoreEntry]]s from the Datastore.
@@ -305,28 +313,28 @@ export class Dstore implements IDstore {
305
313
  */
306
314
  async getMulti(keys: readonly Key[]): Promise<Array<IDstoreEntry | null>> {
307
315
  // assertIsArray(keys);
308
- let results: (IDstoreEntry | null | undefined)[];
309
- const metricEnd = metricHistogram.startTimer();
316
+ let results: (IDstoreEntry | null | undefined)[]
317
+ const metricEnd = metricHistogram.startTimer()
310
318
  try {
311
319
  results = this.fixKeys(
312
320
  keys.length > 0 ? (await this.getDoT().get(keys as Writable<typeof keys>))?.[0] : []
313
- );
321
+ )
314
322
  } catch (error) {
315
- metricFailureCounter.inc({ operation: 'get' });
316
- await setImmediate();
317
- throw new DstoreError('datastore.getMulti error', error as Error, { keys });
323
+ metricFailureCounter.inc({ operation: 'get' })
324
+ await setImmediate()
325
+ throw new DstoreError('datastore.getMulti error', error as Error, { keys })
318
326
  } finally {
319
- metricEnd({ operation: 'get' });
327
+ metricEnd({ operation: 'get' })
320
328
  }
321
329
 
322
330
  // Sort resulting entities by the keys they were requested with.
323
- assertIsArray(results);
324
- const entities = results as IDstoreEntry[];
325
- const entitiesByKey: Record<string, IDstoreEntry> = {};
331
+ assertIsArray(results)
332
+ const entities = results as IDstoreEntry[]
333
+ const entitiesByKey: Record<string, IDstoreEntry> = {}
326
334
  entities.forEach((entity) => {
327
- entitiesByKey[JSON.stringify(entity[Datastore.KEY])] = entity;
328
- });
329
- return keys.map((key) => entitiesByKey[JSON.stringify(key)] || null);
335
+ entitiesByKey[JSON.stringify(entity[Datastore.KEY])] = entity
336
+ })
337
+ return keys.map((key) => entitiesByKey[JSON.stringify(key)] || null)
330
338
  }
331
339
 
332
340
  /** `set()` is addition to [[Datastore]]. It provides a classic Key-value Interface.
@@ -346,11 +354,11 @@ export class Dstore implements IDstore {
346
354
  * @category Additional
347
355
  */
348
356
  async set(key: Key, data: IDstoreEntryWithoutKey): Promise<Key> {
349
- assertIsObject(key);
350
- assertIsObject(data);
351
- const saveEntity = { key, data: { ...data, _keyStr: undefined } };
352
- await this.save([saveEntity]);
353
- return saveEntity.key;
357
+ assertIsObject(key)
358
+ assertIsObject(data)
359
+ const saveEntity = { key, data: { ...data, _keyStr: undefined } }
360
+ await this.save([saveEntity])
361
+ return saveEntity.key
354
362
  }
355
363
 
356
364
  /** `save()` is compatible to [Datastore.save()](https://cloud.google.com/nodejs/docs/reference/datastore/latest/datastore/datastore#_google_cloud_datastore_Datastore_save_member_1_).
@@ -387,32 +395,32 @@ export class Dstore implements IDstore {
387
395
  * @category Datastore Drop-In
388
396
  */
389
397
  async save(entities: readonly DstoreSaveEntity[]): Promise<CommitResponse | undefined> {
390
- assertIsArray(entities);
391
- let ret: CommitResponse | undefined;
392
- const metricEnd = metricHistogram.startTimer();
398
+ assertIsArray(entities)
399
+ let ret: CommitResponse | undefined
400
+ const metricEnd = metricHistogram.startTimer()
393
401
  try {
394
402
  // Within Transaction we don't get any answer here!
395
403
  // [ { mutationResults: [ [Object], [Object] ], indexUpdates: 51 } ]
396
404
  for (const e of entities) {
397
- assertIsObject(e.key);
398
- assertIsObject(e.data);
399
- this.fixKeys([e.data]);
400
- e.excludeLargeProperties = e.excludeLargeProperties === undefined ? true : e.excludeLargeProperties;
401
- e.data = { ...e.data, _keyStr: undefined };
405
+ assertIsObject(e.key)
406
+ assertIsObject(e.data)
407
+ this.fixKeys([e.data])
408
+ e.excludeLargeProperties = e.excludeLargeProperties === undefined ? true : e.excludeLargeProperties
409
+ e.data = { ...e.data, _keyStr: undefined }
402
410
  }
403
- ret = (await this.getDoT().save(entities)) || undefined;
411
+ ret = (await this.getDoT().save(entities)) || undefined
404
412
  for (const e of entities) {
405
- e.data[Datastore.KEY] = e.key;
406
- this.fixKeys([e.data]);
413
+ e.data[Datastore.KEY] = e.key
414
+ this.fixKeys([e.data])
407
415
  }
408
416
  } catch (error) {
409
- metricFailureCounter.inc({ operation: 'save' });
410
- await setImmediate();
411
- throw new DstoreError('datastore.save error', error as Error);
417
+ metricFailureCounter.inc({ operation: 'save' })
418
+ await setImmediate()
419
+ throw new DstoreError('datastore.save error', error as Error)
412
420
  } finally {
413
- metricEnd({ operation: 'save' });
421
+ metricEnd({ operation: 'save' })
414
422
  }
415
- return ret;
423
+ return ret
416
424
  }
417
425
 
418
426
  /** `insert()` is compatible to [Datastore.insert()](https://cloud.google.com/nodejs/docs/reference/datastore/latest/datastore/datastore#_google_cloud_datastore_Datastore_insert_member_1_).
@@ -434,20 +442,20 @@ export class Dstore implements IDstore {
434
442
  * @category Datastore Drop-In
435
443
  */
436
444
  async insert(entities: readonly DstoreSaveEntity[]): Promise<CommitResponse | undefined> {
437
- assertIsArray(entities);
438
- let ret: CommitResponse | undefined;
439
- const metricEnd = metricHistogram.startTimer();
445
+ assertIsArray(entities)
446
+ let ret: CommitResponse | undefined
447
+ const metricEnd = metricHistogram.startTimer()
440
448
  try {
441
- ret = (await this.getDoT().insert(entities)) || undefined;
449
+ ret = (await this.getDoT().insert(entities)) || undefined
442
450
  } catch (error) {
443
451
  // console.error(error)
444
- metricFailureCounter.inc({ operation: 'insert' });
445
- await setImmediate();
446
- throw new DstoreError('datastore.insert error', error as Error);
452
+ metricFailureCounter.inc({ operation: 'insert' })
453
+ await setImmediate()
454
+ throw new DstoreError('datastore.insert error', error as Error)
447
455
  } finally {
448
- metricEnd({ operation: 'insert' });
456
+ metricEnd({ operation: 'insert' })
449
457
  }
450
- return ret;
458
+ return ret
451
459
  }
452
460
 
453
461
  /** `update()` is compatible to [Datastore.update()](https://cloud.google.com/nodejs/docs/reference/datastore/latest/datastore/datastore#_google_cloud_datastore_Datastore_update_member_1_).
@@ -469,29 +477,29 @@ export class Dstore implements IDstore {
469
477
  * @category Datastore Drop-In
470
478
  */
471
479
  async update(entities: readonly DstoreSaveEntity[]): Promise<CommitResponse | undefined> {
472
- assertIsArray(entities);
480
+ assertIsArray(entities)
473
481
 
474
- entities.forEach((entity) => assertIsObject(entity.key));
482
+ entities.forEach((entity) => assertIsObject(entity.key))
475
483
  entities.forEach((entity) =>
476
484
  assert(
477
485
  entity.key.path.length % 2 == 0,
478
486
  `entity.key.path must be complete: ${JSON.stringify([entity.key.path, entity])}`
479
487
  )
480
- );
481
- let ret: CommitResponse | undefined;
482
- const metricEnd = metricHistogram.startTimer();
488
+ )
489
+ let ret: CommitResponse | undefined
490
+ const metricEnd = metricHistogram.startTimer()
483
491
 
484
492
  try {
485
- ret = (await this.getDoT().update(entities)) || undefined;
493
+ ret = (await this.getDoT().update(entities)) || undefined
486
494
  } catch (error) {
487
495
  // console.error(error)
488
- metricFailureCounter.inc({ operation: 'update' });
489
- await setImmediate();
490
- throw new DstoreError('datastore.update error', error as Error);
496
+ metricFailureCounter.inc({ operation: 'update' })
497
+ await setImmediate()
498
+ throw new DstoreError('datastore.update error', error as Error)
491
499
  } finally {
492
- metricEnd({ operation: 'update' });
500
+ metricEnd({ operation: 'update' })
493
501
  }
494
- return ret;
502
+ return ret
495
503
  }
496
504
 
497
505
  /** `delete()` is compatible to [Datastore.delete()].
@@ -508,23 +516,23 @@ export class Dstore implements IDstore {
508
516
  * @category Datastore Drop-In
509
517
  */
510
518
  async delete(keys: readonly Key[]): Promise<CommitResponse | undefined> {
511
- assertIsArray(keys);
512
- keys.forEach((key) => assertIsObject(key));
519
+ assertIsArray(keys)
520
+ keys.forEach((key) => assertIsObject(key))
513
521
  keys.forEach((key) =>
514
522
  assert(key.path.length % 2 == 0, `key.path must be complete: ${JSON.stringify(key.path)}`)
515
- );
516
- let ret;
517
- const metricEnd = metricHistogram.startTimer();
523
+ )
524
+ let ret
525
+ const metricEnd = metricHistogram.startTimer()
518
526
  try {
519
- ret = (await this.getDoT().delete(keys)) || undefined;
527
+ ret = (await this.getDoT().delete(keys)) || undefined
520
528
  } catch (error) {
521
- metricFailureCounter.inc({ operation: 'delete' });
522
- await setImmediate();
523
- throw new DstoreError('datastore.delete error', error as Error);
529
+ metricFailureCounter.inc({ operation: 'delete' })
530
+ await setImmediate()
531
+ throw new DstoreError('datastore.delete error', error as Error)
524
532
  } finally {
525
- metricEnd({ operation: 'delete' });
533
+ metricEnd({ operation: 'delete' })
526
534
  }
527
- return ret;
535
+ return ret
528
536
  }
529
537
 
530
538
  /** `createQuery()` creates an "empty" [[Query]] Object.
@@ -537,25 +545,25 @@ export class Dstore implements IDstore {
537
545
  */
538
546
  createQuery(kindName: string): Query {
539
547
  try {
540
- return this.getDoT().createQuery(kindName);
548
+ return this.getDoT().createQuery(kindName)
541
549
  } catch (error) {
542
- throw new DstoreError('datastore.createQuery error', error as Error);
550
+ throw new DstoreError('datastore.createQuery error', error as Error)
543
551
  }
544
552
  }
545
553
 
546
554
  async runQuery(query: Query | Omit<Query, 'run'>): Promise<RunQueryResponse> {
547
- let ret;
548
- const metricEnd = metricHistogram.startTimer();
555
+ let ret
556
+ const metricEnd = metricHistogram.startTimer()
549
557
  try {
550
- const [entities, info]: [Entity[], RunQueryInfo] = await this.getDoT().runQuery(query as Query);
551
- ret = [this.fixKeys(entities), info];
558
+ const [entities, info]: [Entity[], RunQueryInfo] = await this.getDoT().runQuery(query as Query)
559
+ ret = [this.fixKeys(entities), info]
552
560
  } catch (error) {
553
- await setImmediate();
554
- throw new DstoreError('datastore.runQuery error', error as Error);
561
+ await setImmediate()
562
+ throw new DstoreError('datastore.runQuery error', error as Error)
555
563
  } finally {
556
- metricEnd({ operation: 'query' });
564
+ metricEnd({ operation: 'query' })
557
565
  }
558
- return ret as RunQueryResponse;
566
+ return ret as RunQueryResponse
559
567
  }
560
568
 
561
569
  /** `query()` combined [[createQuery]] and [[runQuery]] in a single call.
@@ -568,6 +576,7 @@ export class Dstore implements IDstore {
568
576
  * @param selection selectionList of [[Query]] select() calls.
569
577
  * @param cursor unsupported so far.
570
578
  *
579
+ * @throws [[DstoreError]]
571
580
  * @category Datastore Drop-In
572
581
  */
573
582
  async query(
@@ -578,34 +587,90 @@ export class Dstore implements IDstore {
578
587
  selection: readonly string[] = [],
579
588
  cursor?: string
580
589
  ): Promise<RunQueryResponse> {
581
- assertIsString(kindName);
582
- assertIsArray(filters);
583
- assertIsNumber(limit);
590
+ assertIsString(kindName)
591
+ assertIsArray(filters)
592
+ assertIsNumber(limit)
584
593
  try {
585
- const q = this.createQuery(kindName);
594
+ const q = this.createQuery(kindName)
586
595
  for (const filterSpec of filters) {
587
- assertIsObject(filterSpec);
596
+ assertIsObject(filterSpec)
588
597
  // @ts-ignore
589
- q.filter(new PropertyFilter(...filterSpec));
598
+ q.filter(new PropertyFilter(...filterSpec))
590
599
  }
591
600
  for (const orderField of ordering) {
592
- q.order(orderField);
601
+ q.order(orderField)
593
602
  }
594
603
  if (limit > 0) {
595
- q.limit(limit);
604
+ q.limit(limit)
596
605
  }
597
606
  if (selection.length > 0) {
598
- q.select(selection as any);
607
+ q.select(selection as any)
608
+ }
609
+ return await this.runQuery(q)
610
+ } catch (error) {
611
+ await setImmediate()
612
+ throw new DstoreError('datastore.query error', error as Error, {
613
+ kindName,
614
+ filters,
615
+ limit,
616
+ ordering,
617
+ })
618
+ }
619
+ }
620
+
621
+
622
+ /** `iterate()` is a modernized version of `query()`.
623
+ *
624
+ * It takes a Parameter object and returns an AsyncIterable.
625
+ * Entities returned have been processed by `fixKeys()`.
626
+ *
627
+ * @param kindName Name of the [[Datastore]][Kind](https://cloud.google.com/datastore/docs/concepts/entities#kinds_and_identifiers) ("Table") which should be searched.
628
+ * @param filters List of [[Query]] filter() calls.
629
+ * @param limit Maximum Number of Results to return.
630
+ * @param ordering List of [[Query]] order() calls.
631
+ * @param selection selectionList of [[Query]] select() calls.
632
+ *
633
+ * @category Additional
634
+ */
635
+ async * iterate({
636
+ kindName,
637
+ filters = [],
638
+ limit = 2500,
639
+ ordering = [],
640
+ selection = [],
641
+ }: IIterateParams): AsyncIterable<IDstoreEntry> {
642
+ assertIsString(kindName)
643
+ assertIsArray(filters)
644
+ assertIsNumber(limit)
645
+ try {
646
+ const q = this.getDoT().createQuery(kindName)
647
+ for (const filterSpec of filters) {
648
+ assertIsObject(filterSpec)
649
+ // @ts-ignore
650
+ q.filter(new PropertyFilter(...filterSpec))
651
+ }
652
+ for (const orderField of ordering) {
653
+ q.order(orderField)
654
+ }
655
+ if (limit > 0) {
656
+ q.limit(limit)
657
+ }
658
+ if (selection.length > 0) {
659
+ q.select(selection as any)
660
+ }
661
+ for await (const entity of this.getDoT().runStream(q)) {
662
+ const ret = this.fixKeys([entity])[0]
663
+ assertIsDefined(ret, 'datastore.iterate: entity is undefined')
664
+ yield ret
599
665
  }
600
- return await this.runQuery(q);
601
666
  } catch (error) {
602
- await setImmediate();
667
+ await setImmediate()
603
668
  throw new DstoreError('datastore.query error', error as Error, {
604
669
  kindName,
605
670
  filters,
606
671
  limit,
607
672
  ordering,
608
- });
673
+ })
609
674
  }
610
675
  }
611
676
 
@@ -621,10 +686,10 @@ export class Dstore implements IDstore {
621
686
  * In fact the generated ID is namespaced via an incomplete [[Key]] of the given Kind.
622
687
  */
623
688
  async allocateOneId(kindName = 'Numbering'): Promise<string> {
624
- assertIsString(kindName);
625
- const ret = (await this.datastore.allocateIds(this.key([kindName]), 1))[0][0].id;
626
- assertIsString(ret);
627
- return ret;
689
+ assertIsString(kindName)
690
+ const ret = (await this.datastore.allocateIds(this.key([kindName]), 1))[0][0].id
691
+ assertIsString(ret)
692
+ return ret
628
693
  }
629
694
 
630
695
  /** This tries to give high level access to transactions.
@@ -642,27 +707,27 @@ export class Dstore implements IDstore {
642
707
  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.
643
708
  */
644
709
  async runInTransaction<T>(func: () => Promise<T>): Promise<T> {
645
- let ret;
646
- const transaction: Transaction = this.datastore.transaction();
710
+ let ret
711
+ const transaction: Transaction = this.datastore.transaction()
647
712
  await transactionAsyncLocalStorage.run(transaction, async () => {
648
- const [transactionInfo, transactionRunApiResponse] = await transaction.run();
649
- let commitApiResponse;
713
+ const [transactionInfo, transactionRunApiResponse] = await transaction.run()
714
+ let commitApiResponse
650
715
  try {
651
- ret = await func();
716
+ ret = await func()
652
717
  } catch (error) {
653
- const rollbackInfo = await transaction.rollback();
718
+ const rollbackInfo = await transaction.rollback()
654
719
  debug(
655
720
  'Transaction failed, rollback initiated: %O %O %O %O',
656
721
  transactionInfo,
657
722
  transactionRunApiResponse,
658
723
  rollbackInfo,
659
724
  error
660
- );
661
- await setImmediate();
662
- throw new DstoreError('datastore.transaction execution error', error as Error);
725
+ )
726
+ await setImmediate()
727
+ throw new DstoreError('datastore.transaction execution error', error as Error)
663
728
  }
664
729
  try {
665
- commitApiResponse = (await transaction.commit())[0];
730
+ commitApiResponse = (await transaction.commit())[0]
666
731
  } catch (error) {
667
732
  debug(
668
733
  'Transaction commit failed: %O %O %O %O ret: %O',
@@ -671,36 +736,36 @@ export class Dstore implements IDstore {
671
736
  commitApiResponse,
672
737
  error,
673
738
  ret
674
- );
675
- await setImmediate();
676
- throw new DstoreError('datastore.transaction execution error', error as Error);
739
+ )
740
+ await setImmediate()
741
+ throw new DstoreError('datastore.transaction execution error', error as Error)
677
742
  }
678
- });
679
- return ret as T;
743
+ })
744
+ return ret as T
680
745
  }
681
746
  }
682
747
 
683
748
  export class DstoreError extends Error {
684
- public readonly extensions: Record<string, unknown>;
685
- public readonly originalError: Error | undefined;
686
- readonly [key: string]: unknown;
749
+ public readonly extensions: Record<string, unknown>
750
+ public readonly originalError: Error | undefined
751
+ readonly [key: string]: unknown
687
752
 
688
753
  constructor(message: string, originalError: Error | undefined, extensions?: Record<string, unknown>) {
689
- super(`${message}: ${originalError?.message}`);
754
+ super(`${message}: ${originalError?.message}`)
690
755
 
691
756
  // if no name provided, use the default. defineProperty ensures that it stays non-enumerable
692
757
  if (!this.name) {
693
- Object.defineProperty(this, 'name', { value: 'DstoreError' });
758
+ Object.defineProperty(this, 'name', { value: 'DstoreError' })
694
759
  }
695
760
  // metadata: Metadata { internalRepr: Map(0) {}, options: {} },
696
- this.originalError = originalError;
697
- this.extensions = { ...extensions };
761
+ this.originalError = originalError
762
+ this.extensions = { ...extensions }
698
763
  this.stack =
699
764
  (this.stack?.split('\n')[0] || '') +
700
765
  '\n' +
701
766
  (originalError?.stack?.split('\n')?.slice(1)?.join('\n') || '') +
702
767
  '\n' +
703
- (this.stack?.split('\n')?.slice(1)?.join('\n') || '');
768
+ (this.stack?.split('\n')?.slice(1)?.join('\n') || '')
704
769
 
705
770
  // These are usually present on Datastore Errors
706
771
  // logger.error({ err: originalError, extensions }, message);