datastore-api 2.2.0 → 4.0.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.
@@ -0,0 +1,649 @@
1
+ /*
2
+ * datastore-simulator.ts
3
+ *
4
+ * Created by Dr. Maximillian Dornseif 2023-05-11 in huwawi3backend 18.16.3
5
+ * Copyright (c) 2023 HUDORA GmbH
6
+ */
7
+
8
+ /* eslint-disable @typescript-eslint/explicit-function-return-type */
9
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
10
+ /*
11
+ * index.ts
12
+ *
13
+ * Created by Dr. Maximillian Dornseif 2023-04-20 in huwawi3backend 18.13.0
14
+ * based on https://github.com/KoryNunn/datastore-mock 1.1.0 by korynunn
15
+ */
16
+
17
+ import {
18
+ DatastoreOptions,
19
+ DatastoreRequest,
20
+ Entity,
21
+ InsertCallback,
22
+ InsertResponse,
23
+ Key,
24
+ KeyToLegacyUrlSafeCallback,
25
+ Datastore as OrigDatastore,
26
+ TransactionOptions,
27
+ UpdateCallback,
28
+ UpdateResponse,
29
+ UpsertCallback,
30
+ UpsertResponse,
31
+ Query,
32
+ PathType,
33
+ } from '@google-cloud/datastore'
34
+ import { google } from '@google-cloud/datastore/build/protos/protos'
35
+ import { AggregateQuery } from '@google-cloud/datastore/build/src/aggregate'
36
+ import { Entities, entity } from '@google-cloud/datastore/build/src/entity'
37
+ import { RunQueryOptions, RunQueryResponse, RunQueryCallback } from '@google-cloud/datastore/build/src/query'
38
+ import {
39
+ AllocateIdsCallback,
40
+ AllocateIdsOptions,
41
+ AllocateIdsResponse,
42
+ CommitCallback,
43
+ CommitResponse,
44
+ CreateReadStreamOptions,
45
+ DeleteCallback,
46
+ DeleteResponse,
47
+ GetCallback,
48
+ GetResponse,
49
+ PrepareEntityObjectResponse,
50
+ RequestOptions,
51
+ SaveCallback,
52
+ SaveResponse,
53
+ } from '@google-cloud/datastore/build/src/request'
54
+ import { promisifyAll } from '@google-cloud/promisify'
55
+ import { assert } from 'assertate-debug'
56
+ import { CallOptions } from 'google-gax'
57
+ import * as is from 'is'
58
+
59
+ const urlSafeKey = new entity.URLSafeKey()
60
+
61
+ const KEY_SELECT = '__key__'
62
+
63
+ function filter(query: { filters: any[][] }, field: any, operator: any, value: any): any {
64
+ query.filters.push([field, operator, value])
65
+ return createQuery(query)
66
+ }
67
+
68
+ function limit(query: { limit: any }, limit: any): any {
69
+ query.limit = limit
70
+ return createQuery(query)
71
+ }
72
+
73
+ function select(query: { select: string | string[] }, fields: ConcatArray<never>) {
74
+ query.select = [].concat(fields)
75
+
76
+ if (query.select.length > 1 && query.select.includes(KEY_SELECT)) {
77
+ throw new Error('Cannot mix __key__ select with other fields')
78
+ }
79
+
80
+ return createQuery(query)
81
+ }
82
+
83
+ function createQuery(query: any): any {
84
+ return {
85
+ filter: filter.bind(null, query),
86
+ limit: limit.bind(null, query),
87
+ select: select.bind(null, query),
88
+ query,
89
+ }
90
+ }
91
+
92
+ export class Datastore extends OrigDatastore {
93
+ db: Map<string, any>
94
+ rnd = 0
95
+ engine = 'datastore-simulator'
96
+
97
+ constructor(options?: DatastoreOptions) {
98
+ super()
99
+ options = options || {}
100
+ this.clients_ = new Map()
101
+ // this.datastore = this as unknown as Datastore;
102
+ this.namespace = options.namespace
103
+ this.db = new Map()
104
+
105
+ options.projectId = options.projectId || process.env.DATASTORE_PROJECT_ID
106
+ }
107
+ _keySerializer(key: entity.Key) {
108
+ const newKey =
109
+ key.id === undefined
110
+ ? this.key(key.path)
111
+ : this.key([...key.path.slice(0, -1), this.int(key.path.slice(-1)[0])])
112
+ return JSON.stringify(newKey)
113
+ }
114
+
115
+ // export
116
+ // getIndexes getIndexes
117
+ // getProjectId
118
+ // index(id: string): Index {
119
+ // return new Index(this, id);
120
+ // }
121
+
122
+ allocateIds(key: entity.Key, options: AllocateIdsOptions | number): Promise<AllocateIdsResponse>
123
+ allocateIds(key: entity.Key, options: AllocateIdsOptions | number, callback: AllocateIdsCallback): void
124
+ allocateIds(
125
+ key: entity.Key,
126
+ options: AllocateIdsOptions | number,
127
+ callback?: AllocateIdsCallback
128
+ ): void | Promise<AllocateIdsResponse> {
129
+ options = typeof options === 'number' ? { allocations: options } : options
130
+ const allocations = options.allocations || 1
131
+ const result: entity.Key[] = []
132
+ const info = { keys: [] as any[] }
133
+
134
+ do {
135
+ const id = 5000000000000000 + this.rnd++
136
+ const newKey = this.key([...key.path.slice(0, -1), this.int(id)])
137
+ result.push(newKey)
138
+ info.keys.push({
139
+ partitionId: {
140
+ databaseId: '',
141
+ namespaceId: 'test',
142
+ projectId: 'huwawi3',
143
+ },
144
+ path: [
145
+ {
146
+ id: newKey.id,
147
+ idType: 'id',
148
+ kind: newKey.kind,
149
+ },
150
+ ],
151
+ })
152
+ } while (result.length < allocations)
153
+ callback!(null, result, info)
154
+ }
155
+
156
+ delete(keys: Entities, gaxOptions?: CallOptions): Promise<DeleteResponse>
157
+ delete(keys: Entities, callback: DeleteCallback): void
158
+ delete(keys: Entities, gaxOptions: CallOptions, callback: DeleteCallback): void
159
+ delete(
160
+ keys: entity.Key | entity.Key[],
161
+ gaxOptionsOrCallback?: CallOptions | DeleteCallback,
162
+ cb?: DeleteCallback
163
+ ): void | Promise<DeleteResponse> {
164
+ const gaxOptions = typeof gaxOptionsOrCallback === 'object' ? gaxOptionsOrCallback : {}
165
+ const callback = typeof gaxOptionsOrCallback === 'function' ? gaxOptionsOrCallback : cb!
166
+
167
+ const result: CommitResponse[] = []
168
+
169
+ for (const key of [keys].flat()) {
170
+ this.db.delete(this._keySerializer(key))
171
+ result.push({
172
+ mutationResults: [
173
+ {
174
+ key: null,
175
+ version: 1,
176
+ conflictDetected: false, // (boolean|null);
177
+ },
178
+ ],
179
+ indexUpdates: 1, // number|null);
180
+ } as unknown as CommitResponse)
181
+ }
182
+ setImmediate(() => callback(null, result.length === 1 ? result[0] : (result as any)))
183
+ }
184
+ get(keys: entity.Key | entity.Key[], options?: CreateReadStreamOptions): Promise<GetResponse>
185
+ get(keys: entity.Key | entity.Key[], callback: GetCallback): void
186
+ get(keys: entity.Key | entity.Key[], options: CreateReadStreamOptions, callback: GetCallback): void
187
+ get(
188
+ keys: entity.Key | entity.Key[],
189
+ optionsOrCallback?: CreateReadStreamOptions | GetCallback,
190
+ cb?: GetCallback
191
+ ): void | Promise<GetResponse> {
192
+ const options = typeof optionsOrCallback === 'object' && optionsOrCallback ? optionsOrCallback : {}
193
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb!
194
+
195
+ if ([keys].flat().length === 0) {
196
+ throw Error('At least one Key object is required.')
197
+ }
198
+
199
+ const result: any[] = []
200
+ let lastK
201
+ for (let key of [keys].flat()) {
202
+ // dedupe
203
+ const k = this._keySerializer(key)
204
+ if (k !== lastK && this.db.has(k)) {
205
+ result.push(this.db.get(k))
206
+ }
207
+ lastK = k
208
+ }
209
+
210
+ setImmediate(() => callback(null, Array.isArray(keys) ? result : (result[0] as any)))
211
+ }
212
+
213
+ runQuery(query: Query, options?: RunQueryOptions): Promise<RunQueryResponse>
214
+ runQuery(query: Query, options: RunQueryOptions, callback: RunQueryCallback): void
215
+ runQuery(query: Query, callback: RunQueryCallback): void
216
+ runQuery(
217
+ query: Query,
218
+ optionsOrCallback?: RunQueryOptions | RunQueryCallback,
219
+ cb?: RunQueryCallback
220
+ ): void | Promise<RunQueryResponse> {
221
+ const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}
222
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb!
223
+ assert(query.kinds.length === 1)
224
+ const kind = query.kinds[0]
225
+
226
+ const reply: any[] = []
227
+ const filtered = Array.from(this.db.entries()).filter(([ks, v]) => {
228
+ const k = JSON.parse(ks)
229
+ return k.kind === kind && k.namespace === query.namespace
230
+ })
231
+
232
+ if (query.filters.length === 0) {
233
+ for (let [ks, entity] of filtered) {
234
+ reply.push(entity)
235
+ }
236
+ }
237
+
238
+ for (let filter of query.filters) {
239
+ if (filter.name === '__key__' && filter.op === 'HAS_ANCESTOR') {
240
+ const parent = filter.val.path.join('⭕️')
241
+ for (let [ks, entity] of filtered) {
242
+ const k = JSON.parse(ks)
243
+ if (k.path.join('⭕️').startsWith(parent)) {
244
+ reply.push(entity)
245
+ }
246
+ }
247
+ } else if (filter.op === '=') {
248
+ for (let [ks, entity] of filtered) {
249
+ if (entity[filter.name] == filter.val) {
250
+ reply.push(entity)
251
+ }
252
+ }
253
+ } else if (filter.op === '>=') {
254
+ for (let [ks, entity] of filtered) {
255
+ if (entity[filter.name] >= filter.val) {
256
+ reply.push(entity)
257
+ }
258
+ }
259
+ } else if (filter.op === '<') {
260
+ for (let [ks, entity] of filtered) {
261
+ if (entity[filter.name] >= filter.val) {
262
+ reply.push(entity)
263
+ }
264
+ }
265
+ } else {
266
+ console.log('unknown filter', filter)
267
+ }
268
+ }
269
+
270
+ // TODO: handle query.limit
271
+
272
+ setImmediate(() => callback(null, reply, { moreResults: 'MORE_RESULTS_AFTER_LIMIT' }))
273
+ }
274
+
275
+ merge(entities: Entities): Promise<CommitResponse>
276
+ merge(entities: Entities, callback: SaveCallback): void
277
+ merge(entities: Entities, callback?: SaveCallback): void | Promise<CommitResponse> {
278
+ throw Error('not implemented')
279
+ }
280
+
281
+ insert(entities: Entities): Promise<InsertResponse>
282
+ insert(entities: Entities, callback: InsertCallback): void
283
+ insert(entities: Entities, callback?: InsertCallback): void | Promise<InsertResponse> {
284
+ entities = [entities]
285
+ .flat()
286
+ .map(DatastoreRequest.prepareEntityObject_)
287
+ .map((x: PrepareEntityObjectResponse) => {
288
+ x.method = 'insert'
289
+ return x
290
+ })
291
+
292
+ this.save(entities, callback!)
293
+ }
294
+
295
+ update(entities: Entities): Promise<UpdateResponse>
296
+ update(entities: Entities, callback: UpdateCallback): void
297
+ update(entities: Entities, callback?: UpdateCallback): void | Promise<UpdateResponse> {
298
+ entities = [entities]
299
+ .flat()
300
+ .map(DatastoreRequest.prepareEntityObject_)
301
+ .map((x: PrepareEntityObjectResponse) => {
302
+ x.method = 'update'
303
+ return x
304
+ })
305
+
306
+ this.save(entities, callback!)
307
+ }
308
+
309
+ upsert(entities: Entities): Promise<UpsertResponse>
310
+ upsert(entities: Entities, callback: UpsertCallback): void
311
+ upsert(entities: Entities, callback?: UpsertCallback): void | Promise<UpsertResponse> {
312
+ entities = [entities]
313
+ .flat()
314
+ .map(DatastoreRequest.prepareEntityObject_)
315
+ .map((x: PrepareEntityObjectResponse) => {
316
+ x.method = 'upsert'
317
+ return x
318
+ })
319
+
320
+ this.save(entities, callback!)
321
+ }
322
+
323
+ save(entities: Entities, gaxOptions?: CallOptions): Promise<SaveResponse>
324
+ save(entities: Entities, gaxOptions: CallOptions, callback: SaveCallback): void
325
+ save(entities: Entities, callback: SaveCallback): void
326
+ save(
327
+ entities: Entities,
328
+ gaxOptionsOrCallback?: CallOptions | SaveCallback,
329
+ cb?: SaveCallback
330
+ ): void | Promise<SaveResponse> {
331
+ const gaxOptions = typeof gaxOptionsOrCallback === 'object' ? gaxOptionsOrCallback : {}
332
+ const callback = typeof gaxOptionsOrCallback === 'function' ? gaxOptionsOrCallback : cb!
333
+
334
+ const methods: Record<string, boolean> = {
335
+ insert: true,
336
+ update: true,
337
+ upsert: true,
338
+ }
339
+ entities = [entities].flat()
340
+ // Iterate over the entity objects, build a proto from all keys and values,
341
+ // then place in the correct mutation array (insert, update, etc).
342
+ const result: CommitResponse[] = []
343
+ ;[entities]
344
+ .flat()
345
+ .map(DatastoreRequest.prepareEntityObject_)
346
+ .forEach((entityObject: Entity, index: number) => {
347
+ let method = 'upsert'
348
+ if (entityObject.method) {
349
+ if (methods[entityObject.method]) {
350
+ method = entityObject.method
351
+ } else {
352
+ throw new Error('Method ' + entityObject.method + ' not recognized.')
353
+ }
354
+ }
355
+ // TODO: generate key
356
+
357
+ // Numerical IDs are always encoded as string in the datastore
358
+ const newKey =
359
+ entityObject.key.id === undefined
360
+ ? this.key(entityObject.key.path)
361
+ : this.key([...entityObject.key.path.slice(0, -1), this.int(entityObject.key.path.slice(-1)[0])])
362
+
363
+ this.db.set(this._keySerializer(newKey), {
364
+ [Datastore.KEY]: newKey,
365
+ ...entityObject.data,
366
+ })
367
+
368
+ result.push({
369
+ mutationResults: [
370
+ {
371
+ key: null,
372
+ version: 1,
373
+ conflictDetected: false, // (boolean|null);
374
+ createTime: { nanos: 1, seconds: 2 },
375
+ updateTime: { nanos: 3, seconds: 4 },
376
+ },
377
+ ],
378
+ indexUpdates: 1, // number|null);
379
+ } as unknown as CommitResponse)
380
+ })
381
+ setImmediate(() => callback(null, result[0] as any))
382
+ }
383
+
384
+ static KEY: typeof entity.KEY_SYMBOL = entity.KEY_SYMBOL
385
+ KEY: typeof entity.KEY_SYMBOL = Datastore.KEY
386
+ static MORE_RESULTS_AFTER_CURSOR = 'MORE_RESULTS_AFTER_CURSOR'
387
+ MORE_RESULTS_AFTER_CURSOR = Datastore.MORE_RESULTS_AFTER_CURSOR
388
+ static MORE_RESULTS_AFTER_LIMIT = 'MORE_RESULTS_AFTER_LIMIT'
389
+ MORE_RESULTS_AFTER_LIMIT = Datastore.MORE_RESULTS_AFTER_LIMIT
390
+ static NO_MORE_RESULTS = 'NO_MORE_RESULTS'
391
+ NO_MORE_RESULTS = Datastore.NO_MORE_RESULTS
392
+
393
+ createQuery(kind?: string): Query
394
+ createQuery(kind?: string[]): Query
395
+ createQuery(namespace: string, kind: string): Query
396
+ createQuery(namespace: string, kind: string[]): Query
397
+ createQuery(namespaceOrKind?: string | string[], kind?: string | string[]): Query {
398
+ let namespace = namespaceOrKind as string
399
+ if (!kind) {
400
+ kind = namespaceOrKind
401
+ namespace = this.namespace!
402
+ }
403
+ return new Query(this as any, namespace, [kind].flat() as string[])
404
+ }
405
+ key(options: entity.KeyOptions): entity.Key
406
+ key(path: PathType[]): entity.Key
407
+ key(path: string): entity.Key
408
+ key(options: string | entity.KeyOptions | PathType[]): entity.Key {
409
+ const keyOptions = is.object(options)
410
+ ? (options as entity.KeyOptions)
411
+ : {
412
+ namespace: this.namespace,
413
+ path: [options].flat() as PathType[],
414
+ }
415
+ return new entity.Key(keyOptions)
416
+ }
417
+ static isKey(value?: unknown) {
418
+ return entity.isDsKey(value as any)
419
+ }
420
+ isKey(value?: unknown) {
421
+ return Datastore.isKey(value)
422
+ }
423
+
424
+ keyToLegacyUrlSafe(key: entity.Key, locationPrefix?: string): Promise<string>
425
+ keyToLegacyUrlSafe(key: entity.Key, callback: KeyToLegacyUrlSafeCallback): void
426
+ keyToLegacyUrlSafe(key: entity.Key, locationPrefix: string, callback: KeyToLegacyUrlSafeCallback): void
427
+ keyToLegacyUrlSafe(
428
+ key: entity.Key,
429
+ locationPrefixOrCallback?: string | KeyToLegacyUrlSafeCallback,
430
+ callback?: KeyToLegacyUrlSafeCallback
431
+ ): Promise<string> | void {
432
+ const locationPrefix = typeof locationPrefixOrCallback === 'string' ? locationPrefixOrCallback : ''
433
+ callback = typeof locationPrefixOrCallback === 'function' ? locationPrefixOrCallback : callback
434
+ this.auth.getProjectId((err: any, projectId: any) => {
435
+ if (err) {
436
+ setImmediate(() => callback!(err))
437
+ return
438
+ }
439
+ setImmediate(() => callback!(null, urlSafeKey.legacyEncode(projectId!, key, locationPrefix)))
440
+ })
441
+ }
442
+
443
+ keyFromLegacyUrlsafe(key: string): entity.Key {
444
+ return urlSafeKey.legacyDecode(key)
445
+ }
446
+ transaction(options?: TransactionOptions) {
447
+ return new Transaction(this as any, options)
448
+ }
449
+ }
450
+
451
+ promisifyAll(Datastore, {
452
+ exclude: [
453
+ 'createAggregationQuery',
454
+ 'double',
455
+ 'isDouble',
456
+ 'geoPoint',
457
+ 'getProjectId',
458
+ 'getSharedQueryOptions',
459
+ 'isGeoPoint',
460
+ 'index',
461
+ 'int',
462
+ 'isInt',
463
+ 'createQuery',
464
+ 'key',
465
+ 'isKey',
466
+ 'keyFromLegacyUrlsafe',
467
+ 'transaction',
468
+ ],
469
+ })
470
+
471
+ export default Datastore
472
+
473
+ class Transaction extends DatastoreRequest {
474
+ namespace?: string
475
+ readOnly: boolean
476
+ request: any // Function
477
+ modifiedEntities_: ModifiedEntities
478
+ skipCommit?: boolean
479
+ constructor(datastore: Datastore, options?: TransactionOptions) {
480
+ super()
481
+ /**
482
+ * @name Transaction#datastore
483
+ * @type {Datastore}
484
+ */
485
+ this.datastore = datastore
486
+
487
+ /**
488
+ * @name Transaction#namespace
489
+ * @type {string}
490
+ */
491
+ this.namespace = datastore.namespace
492
+
493
+ options = options || {}
494
+
495
+ this.id = options.id
496
+ this.readOnly = options.readOnly === true
497
+
498
+ // A queue for entity modifications made during the transaction.
499
+ this.modifiedEntities_ = []
500
+
501
+ // Queue the callbacks that process the API responses.
502
+ this.requestCallbacks_ = []
503
+
504
+ // Queue the requests to make when we send the transactional commit.
505
+ this.requests_ = []
506
+ }
507
+ commit(gaxOptions?: CallOptions): Promise<CommitResponse>
508
+ commit(callback: CommitCallback): void
509
+ commit(gaxOptions: CallOptions, callback: CommitCallback): void
510
+ commit(
511
+ gaxOptionsOrCallback?: CallOptions | CommitCallback,
512
+ cb?: CommitCallback
513
+ ): void | Promise<CommitResponse> {
514
+ const callback =
515
+ typeof gaxOptionsOrCallback === 'function'
516
+ ? gaxOptionsOrCallback
517
+ : typeof cb === 'function'
518
+ ? cb
519
+ : () => {}
520
+ const gaxOptions = typeof gaxOptionsOrCallback === 'object' ? gaxOptionsOrCallback : {}
521
+
522
+ if (this.skipCommit) {
523
+ setImmediate(callback)
524
+ return
525
+ }
526
+
527
+ const keys: Entities = {}
528
+
529
+ callback(null, undefined)
530
+ }
531
+
532
+ createQuery(kind?: string): Query
533
+ createQuery(kind?: string[]): Query
534
+ createQuery(namespace: string, kind: string): Query
535
+ createQuery(namespace: string, kind: string[]): Query
536
+ createQuery(namespaceOrKind?: string | string[], kind?: string | string[]): Query {
537
+ return this.datastore.createQuery.call(this, namespaceOrKind as string, kind as string[])
538
+ }
539
+
540
+ createAggregationQuery(query: Query): AggregateQuery {
541
+ return this.datastore.createAggregationQuery.call(this, query)
542
+ }
543
+ delete(entities?: Entities): any {
544
+ this.datastore.delete(entities)
545
+ }
546
+ insert(entities: Entities): void {
547
+ this.save(entities)
548
+ }
549
+
550
+ rollback(callback: RollbackCallback): void
551
+ rollback(gaxOptions?: CallOptions): Promise<RollbackResponse>
552
+ rollback(gaxOptions: CallOptions, callback: RollbackCallback): void
553
+ rollback(
554
+ gaxOptionsOrCallback?: CallOptions | RollbackCallback,
555
+ cb?: RollbackCallback
556
+ ): void | Promise<RollbackResponse> {
557
+ const gaxOptions = typeof gaxOptionsOrCallback === 'object' ? gaxOptionsOrCallback : {}
558
+ const callback = typeof gaxOptionsOrCallback === 'function' ? gaxOptionsOrCallback : cb!
559
+
560
+ callback(null, undefined)
561
+ }
562
+
563
+ run(options?: RunOptions): Promise<RunResponse>
564
+ run(callback: RunCallback): void
565
+ run(options: RunOptions, callback: RunCallback): void
566
+ run(optionsOrCallback?: RunOptions | RunCallback, cb?: RunCallback): void | Promise<RunResponse> {
567
+ const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {}
568
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb!
569
+
570
+ const reqOpts = {
571
+ transactionOptions: {},
572
+ } as RequestOptions
573
+
574
+ if (options.readOnly || this.readOnly) {
575
+ reqOpts.transactionOptions!.readOnly = {}
576
+ }
577
+
578
+ if (options.transactionId || this.id) {
579
+ reqOpts.transactionOptions!.readWrite = {
580
+ previousTransaction: options.transactionId || this.id,
581
+ }
582
+ }
583
+
584
+ if (options.transactionOptions) {
585
+ reqOpts.transactionOptions = options.transactionOptions
586
+ }
587
+
588
+ callback(null, this, undefined)
589
+ }
590
+ save(entities: Entities): void {
591
+ this.datastore.save(entities)
592
+ }
593
+
594
+ update(entities: Entities): void {
595
+ entities = [entities]
596
+ .flat()
597
+ .map(DatastoreRequest.prepareEntityObject_)
598
+ .map((x: PrepareEntityObjectResponse) => {
599
+ x.method = 'update'
600
+ return x
601
+ })
602
+
603
+ this.save(entities)
604
+ }
605
+
606
+ upsert(entities: Entities): void {
607
+ entities = [entities]
608
+ .flat()
609
+ .map(DatastoreRequest.prepareEntityObject_)
610
+ .map((x: PrepareEntityObjectResponse) => {
611
+ x.method = 'upsert'
612
+ return x
613
+ })
614
+
615
+ this.save(entities)
616
+ }
617
+ }
618
+
619
+ export type ModifiedEntities = Array<{
620
+ entity: { key: Entity }
621
+ method: string
622
+ args: Entity[]
623
+ }>
624
+ export type RunResponse = [Transaction, google.datastore.v1.IBeginTransactionResponse]
625
+ export interface RunCallback {
626
+ (
627
+ error: Error | null,
628
+ transaction: Transaction | null,
629
+ response?: google.datastore.v1.IBeginTransactionResponse
630
+ ): void
631
+ }
632
+ export interface RollbackCallback {
633
+ (error: Error | null, response?: google.datastore.v1.IRollbackResponse): void
634
+ }
635
+ export type RollbackResponse = [google.datastore.v1.IRollbackResponse]
636
+ export interface RunOptions {
637
+ readOnly?: boolean
638
+ transactionId?: string
639
+ transactionOptions?: TransactionOptions
640
+ gaxOptions?: CallOptions
641
+ }
642
+ /*! Developer Documentation
643
+ *
644
+ * All async methods (except for streams) will return a Promise in the event
645
+ * that a callback is omitted.
646
+ */
647
+ promisifyAll(Transaction, {
648
+ exclude: ['createAggregationQuery', 'createQuery', 'delete', 'insert', 'save', 'update', 'upsert'],
649
+ })
@@ -0,0 +1,20 @@
1
+ /*
2
+ * operators.ts
3
+ *
4
+ * Created by Dr. Maximillian Dornseif 2023-04-20 in huwawi3backend 18.13.0
5
+ * based on https://github.com/KoryNunn/datastore-mock 1.1.0 by korynunn
6
+ */
7
+
8
+ export default {
9
+ // @ts-ignore
10
+
11
+ '>': (a, b) => a > b,
12
+ // @ts-ignore
13
+ '<': (a, b) => a < b,
14
+ // @ts-ignore
15
+ '>=': (a, b) => a <= b,
16
+ // @ts-ignore
17
+ '<=': (a, b) => a >= b,
18
+ // @ts-ignore
19
+ '=': (a, b) => a === b,
20
+ }