@forge/kvs 1.2.7-next.0-experimental-2682d7a → 1.2.7-next.0-experimental-d997307

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.
@@ -30,23 +30,62 @@ describe('KVS', () => {
30
30
  body: JSON.stringify({ key: 'foo' })
31
31
  }));
32
32
  });
33
- it('should get with metadata fields correctly', async () => {
34
- const response = new Response(JSON.stringify({ key: 'foo', value: 'bar', createdAt: 1718236800, updatedAt: 1718236800 }), {
33
+ it('should get with metadata fields correctly for get', async () => {
34
+ const response = new Response(JSON.stringify({
35
+ key: 'foo',
36
+ value: 'bar',
37
+ createdAt: 1718236800,
38
+ updatedAt: 1718236800,
39
+ expireTime: '2025-01-12T13:00:00Z'
40
+ }), {
35
41
  status: 200,
36
42
  headers: { 'x-trace-id': traceId }
37
43
  });
38
44
  const { sut, apiClient } = prepare(response);
39
- const rs = await sut.get('foo', { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] });
45
+ const rs = await sut.get('foo', {
46
+ metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
47
+ });
40
48
  expect(rs).toEqual({
41
49
  key: 'foo',
42
50
  value: 'bar',
43
51
  createdAt: 1718236800,
44
- updatedAt: 1718236800
52
+ updatedAt: 1718236800,
53
+ expireTime: '2025-01-12T13:00:00Z'
45
54
  });
46
55
  expect(apiClient).toHaveBeenCalledWith('/api/v1/get', expect.objectContaining({
47
56
  body: JSON.stringify({
48
57
  key: 'foo',
49
- options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] }
58
+ options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
59
+ })
60
+ }));
61
+ });
62
+ it('should get with metadata fields correctly for entity.get', async () => {
63
+ const response = new Response(JSON.stringify({
64
+ key: 'key-foo',
65
+ value: { foo: 'bar' },
66
+ createdAt: 1718236800,
67
+ updatedAt: 1718236800,
68
+ expireTime: '2025-01-12T13:00:00Z'
69
+ }), {
70
+ status: 200,
71
+ headers: { 'x-trace-id': traceId }
72
+ });
73
+ const { sut, apiClient } = prepare(response);
74
+ const rs = await sut.entity('foo').get('key-foo', {
75
+ metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
76
+ });
77
+ expect(rs).toEqual({
78
+ key: 'key-foo',
79
+ value: { foo: 'bar' },
80
+ createdAt: 1718236800,
81
+ updatedAt: 1718236800,
82
+ expireTime: '2025-01-12T13:00:00Z'
83
+ });
84
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/get', expect.objectContaining({
85
+ body: JSON.stringify({
86
+ entityName: 'foo',
87
+ key: 'key-foo',
88
+ options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
50
89
  })
51
90
  }));
52
91
  });
@@ -104,22 +143,31 @@ describe('KVS', () => {
104
143
  }));
105
144
  });
106
145
  it('should getSecret with metadata fields correctly', async () => {
107
- const response = new Response(JSON.stringify({ key: 'foo', value: 'bar', createdAt: 1718236800, updatedAt: 1718236800 }), {
146
+ const response = new Response(JSON.stringify({
147
+ key: 'foo',
148
+ value: 'bar',
149
+ createdAt: 1718236800,
150
+ updatedAt: 1718236800,
151
+ expireTime: '2025-01-12T13:00:00Z'
152
+ }), {
108
153
  status: 200,
109
154
  headers: { 'x-trace-id': traceId }
110
155
  });
111
156
  const { sut, apiClient } = prepare(response);
112
- const rs = await sut.getSecret('foo', { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] });
157
+ const rs = await sut.getSecret('foo', {
158
+ metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
159
+ });
113
160
  expect(rs).toEqual({
114
161
  key: 'foo',
115
162
  value: 'bar',
116
163
  createdAt: 1718236800,
117
- updatedAt: 1718236800
164
+ updatedAt: 1718236800,
165
+ expireTime: '2025-01-12T13:00:00Z'
118
166
  });
119
167
  expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/get', expect.objectContaining({
120
168
  body: JSON.stringify({
121
169
  key: 'foo',
122
- options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] }
170
+ options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
123
171
  })
124
172
  }));
125
173
  });
@@ -148,25 +196,32 @@ describe('KVS', () => {
148
196
  }));
149
197
  });
150
198
  it('should getEntity with metadata fields correctly', async () => {
151
- const response = new Response(JSON.stringify({ key: 'foo', value: { name: 'Jane Doe' }, createdAt: 1718236800, updatedAt: 1718236800 }), {
199
+ const response = new Response(JSON.stringify({
200
+ key: 'foo',
201
+ value: { name: 'Jane Doe' },
202
+ createdAt: 1718236800,
203
+ updatedAt: 1718236800,
204
+ expireTime: '2025-01-12T13:00:00Z'
205
+ }), {
152
206
  status: 200,
153
207
  headers: { 'x-trace-id': traceId }
154
208
  });
155
209
  const { sut, apiClient } = prepare(response);
156
210
  const rs = await sut
157
211
  .entity('employees')
158
- .get('foo', { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] });
212
+ .get('foo', { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] });
159
213
  expect(rs).toEqual({
160
214
  key: 'foo',
161
215
  value: { name: 'Jane Doe' },
162
216
  createdAt: 1718236800,
163
- updatedAt: 1718236800
217
+ updatedAt: 1718236800,
218
+ expireTime: '2025-01-12T13:00:00Z'
164
219
  });
165
220
  expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/get', expect.objectContaining({
166
221
  body: JSON.stringify({
167
222
  entityName: 'employees',
168
223
  key: 'foo',
169
- options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT] }
224
+ options: { metadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME] }
170
225
  })
171
226
  }));
172
227
  });
@@ -194,6 +249,93 @@ describe('KVS', () => {
194
249
  body: JSON.stringify({ key: 'foo', value: 'bar' })
195
250
  }));
196
251
  });
252
+ it('should set correctly with TTL', async () => {
253
+ const response = new Response(undefined, {
254
+ status: 204,
255
+ headers: { 'x-trace-id': traceId }
256
+ });
257
+ const { sut, apiClient } = prepare(response);
258
+ const rs = await sut.set('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' } });
259
+ expect(rs).toBeUndefined();
260
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
261
+ body: JSON.stringify({ key: 'foo', value: 'bar', options: { ttl: { value: 5, unit: 'MINUTES' } } })
262
+ }));
263
+ });
264
+ it('should set correctly with keyPolicy "FAIL_IF_EXISTS"', async () => {
265
+ const response = new Response(undefined, {
266
+ status: 204,
267
+ headers: { 'x-trace-id': traceId }
268
+ });
269
+ const { sut, apiClient } = prepare(response);
270
+ const rs = await sut.set('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' });
271
+ expect(rs).toBeUndefined();
272
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
273
+ body: JSON.stringify({
274
+ key: 'foo',
275
+ value: 'bar',
276
+ options: { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' }
277
+ })
278
+ }));
279
+ });
280
+ it('should set correctly with keyPolicy "OVERRIDE" and return value', async () => {
281
+ const responseBody = {
282
+ key: 'foo',
283
+ value: 'bar',
284
+ createdAt: Date.now(),
285
+ updatedAt: Date.now(),
286
+ expireTime: '2025-01-12T13:00:00Z'
287
+ };
288
+ const response = new Response(JSON.stringify(responseBody), {
289
+ status: 200,
290
+ headers: { 'x-trace-id': traceId }
291
+ });
292
+ const { sut, apiClient } = prepare(response);
293
+ const rs = await sut.set('foo', 'bar', {
294
+ ttl: { value: 5, unit: 'MINUTES' },
295
+ keyPolicy: 'OVERRIDE',
296
+ returnValue: 'LATEST',
297
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
298
+ });
299
+ expect(rs).toEqual(responseBody);
300
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
301
+ body: JSON.stringify({
302
+ key: 'foo',
303
+ value: 'bar',
304
+ options: {
305
+ ttl: { value: 5, unit: 'MINUTES' },
306
+ keyPolicy: 'OVERRIDE',
307
+ returnValue: 'LATEST',
308
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
309
+ }
310
+ })
311
+ }));
312
+ });
313
+ it('should set and return undefined when no previous value exists', async () => {
314
+ const response = new Response(undefined, {
315
+ status: 204,
316
+ headers: { 'x-trace-id': traceId }
317
+ });
318
+ const { sut, apiClient } = prepare(response);
319
+ const rs = await sut.set('foo', 'bar', {
320
+ ttl: { value: 5, unit: 'MINUTES' },
321
+ keyPolicy: 'OVERRIDE',
322
+ returnValue: 'PREVIOUS',
323
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
324
+ });
325
+ expect(rs).toEqual(undefined);
326
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
327
+ body: JSON.stringify({
328
+ key: 'foo',
329
+ value: 'bar',
330
+ options: {
331
+ ttl: { value: 5, unit: 'MINUTES' },
332
+ keyPolicy: 'OVERRIDE',
333
+ returnValue: 'PREVIOUS',
334
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
335
+ }
336
+ })
337
+ }));
338
+ });
197
339
  it('should setSecret correctly', async () => {
198
340
  const response = new Response(undefined, {
199
341
  status: 204,
@@ -206,6 +348,93 @@ describe('KVS', () => {
206
348
  body: JSON.stringify({ key: 'foo', value: 'bar' })
207
349
  }));
208
350
  });
351
+ it('should setSecret correctly with TTL', async () => {
352
+ const response = new Response(undefined, {
353
+ status: 204,
354
+ headers: { 'x-trace-id': traceId }
355
+ });
356
+ const { sut, apiClient } = prepare(response);
357
+ const rs = await sut.setSecret('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' } });
358
+ expect(rs).toBeUndefined();
359
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
360
+ body: JSON.stringify({ key: 'foo', value: 'bar', options: { ttl: { value: 5, unit: 'MINUTES' } } })
361
+ }));
362
+ });
363
+ it('should setSecret correctly with keyPolicy "FAIL_IF_EXISTS"', async () => {
364
+ const response = new Response(undefined, {
365
+ status: 204,
366
+ headers: { 'x-trace-id': traceId }
367
+ });
368
+ const { sut, apiClient } = prepare(response);
369
+ const rs = await sut.setSecret('foo', 'bar', { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' });
370
+ expect(rs).toBeUndefined();
371
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
372
+ body: JSON.stringify({
373
+ key: 'foo',
374
+ value: 'bar',
375
+ options: { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' }
376
+ })
377
+ }));
378
+ });
379
+ it('should setSecret correctly with keyPolicy "OVERRIDE" and return value', async () => {
380
+ const responseBody = {
381
+ key: 'foo',
382
+ value: 'bar',
383
+ createdAt: Date.now(),
384
+ updatedAt: Date.now(),
385
+ expireTime: '2025-01-12T13:00:00Z'
386
+ };
387
+ const response = new Response(JSON.stringify(responseBody), {
388
+ status: 200,
389
+ headers: { 'x-trace-id': traceId }
390
+ });
391
+ const { sut, apiClient } = prepare(response);
392
+ const rs = await sut.setSecret('foo', 'bar', {
393
+ ttl: { value: 5, unit: 'MINUTES' },
394
+ keyPolicy: 'OVERRIDE',
395
+ returnValue: 'LATEST',
396
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
397
+ });
398
+ expect(rs).toEqual(responseBody);
399
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
400
+ body: JSON.stringify({
401
+ key: 'foo',
402
+ value: 'bar',
403
+ options: {
404
+ ttl: { value: 5, unit: 'MINUTES' },
405
+ keyPolicy: 'OVERRIDE',
406
+ returnValue: 'LATEST',
407
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
408
+ }
409
+ })
410
+ }));
411
+ });
412
+ it('should setSecret and return undefined when no previous value exists', async () => {
413
+ const response = new Response(undefined, {
414
+ status: 204,
415
+ headers: { 'x-trace-id': traceId }
416
+ });
417
+ const { sut, apiClient } = prepare(response);
418
+ const rs = await sut.setSecret('foo', 'bar', {
419
+ ttl: { value: 5, unit: 'MINUTES' },
420
+ keyPolicy: 'OVERRIDE',
421
+ returnValue: 'PREVIOUS',
422
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
423
+ });
424
+ expect(rs).toEqual(undefined);
425
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
426
+ body: JSON.stringify({
427
+ key: 'foo',
428
+ value: 'bar',
429
+ options: {
430
+ ttl: { value: 5, unit: 'MINUTES' },
431
+ keyPolicy: 'OVERRIDE',
432
+ returnValue: 'PREVIOUS',
433
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
434
+ }
435
+ })
436
+ }));
437
+ });
209
438
  it('should setEntity correctly', async () => {
210
439
  const response = new Response(undefined, {
211
440
  status: 204,
@@ -218,6 +447,103 @@ describe('KVS', () => {
218
447
  body: JSON.stringify({ entityName: 'employees', key: 'foo', value: { name: 'Jane Doe' } })
219
448
  }));
220
449
  });
450
+ it('should setEntity correctly with TTL', async () => {
451
+ const response = new Response(undefined, {
452
+ status: 204,
453
+ headers: { 'x-trace-id': traceId }
454
+ });
455
+ const { sut, apiClient } = prepare(response);
456
+ const rs = await sut.entity('employees').set('foo', { name: 'Jane Doe' }, { ttl: { value: 5, unit: 'MINUTES' } });
457
+ expect(rs).toBeUndefined();
458
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
459
+ body: JSON.stringify({
460
+ entityName: 'employees',
461
+ key: 'foo',
462
+ value: { name: 'Jane Doe' },
463
+ options: { ttl: { value: 5, unit: 'MINUTES' } }
464
+ })
465
+ }));
466
+ });
467
+ it('should setEntity correctly with "FAIL_IF_EXISTS"', async () => {
468
+ const response = new Response(undefined, {
469
+ status: 204,
470
+ headers: { 'x-trace-id': traceId }
471
+ });
472
+ const { sut, apiClient } = prepare(response);
473
+ const rs = await sut
474
+ .entity('employees')
475
+ .set('foo', { name: 'Jane Doe' }, { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' });
476
+ expect(rs).toBeUndefined();
477
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
478
+ body: JSON.stringify({
479
+ entityName: 'employees',
480
+ key: 'foo',
481
+ value: { name: 'Jane Doe' },
482
+ options: { ttl: { value: 5, unit: 'MINUTES' }, keyPolicy: 'FAIL_IF_EXISTS' }
483
+ })
484
+ }));
485
+ });
486
+ it('should setEntity correctly with "OVERRIDE" and return value', async () => {
487
+ const responseBody = {
488
+ key: 'foo',
489
+ value: 'bar',
490
+ createdAt: Date.now(),
491
+ updatedAt: Date.now(),
492
+ expireTime: '2025-01-12T13:00:00Z'
493
+ };
494
+ const response = new Response(JSON.stringify(responseBody), {
495
+ status: 200,
496
+ headers: { 'x-trace-id': traceId }
497
+ });
498
+ const { sut, apiClient } = prepare(response);
499
+ const rs = await sut.entity('employees').set('foo', { name: 'Jane Doe' }, {
500
+ ttl: { value: 5, unit: 'MINUTES' },
501
+ keyPolicy: 'OVERRIDE',
502
+ returnValue: 'LATEST',
503
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
504
+ });
505
+ expect(rs).toEqual(responseBody);
506
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
507
+ body: JSON.stringify({
508
+ entityName: 'employees',
509
+ key: 'foo',
510
+ value: { name: 'Jane Doe' },
511
+ options: {
512
+ ttl: { value: 5, unit: 'MINUTES' },
513
+ keyPolicy: 'OVERRIDE',
514
+ returnValue: 'LATEST',
515
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
516
+ }
517
+ })
518
+ }));
519
+ });
520
+ it('should setEntity and return undefined when no previous value exists', async () => {
521
+ const response = new Response(undefined, {
522
+ status: 204,
523
+ headers: { 'x-trace-id': traceId }
524
+ });
525
+ const { sut, apiClient } = prepare(response);
526
+ const rs = await sut.entity('employees').set('foo', { name: 'Jane Doe' }, {
527
+ ttl: { value: 5, unit: 'MINUTES' },
528
+ keyPolicy: 'OVERRIDE',
529
+ returnValue: 'PREVIOUS',
530
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
531
+ });
532
+ expect(rs).toBeUndefined();
533
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
534
+ body: JSON.stringify({
535
+ entityName: 'employees',
536
+ key: 'foo',
537
+ value: { name: 'Jane Doe' },
538
+ options: {
539
+ ttl: { value: 5, unit: 'MINUTES' },
540
+ keyPolicy: 'OVERRIDE',
541
+ returnValue: 'PREVIOUS',
542
+ returnMetadataFields: [types_1.MetadataField.CREATED_AT, types_1.MetadataField.UPDATED_AT, types_1.MetadataField.EXPIRE_TIME]
543
+ }
544
+ })
545
+ }));
546
+ });
221
547
  it('should delete correctly', async () => {
222
548
  const response = new Response(undefined, {
223
549
  status: 204,
@@ -314,6 +640,36 @@ describe('KVS', () => {
314
640
  })
315
641
  }));
316
642
  });
643
+ it('should query correctly with TTL fields', async () => {
644
+ const response = new Response(JSON.stringify({
645
+ cursor: 'third-page',
646
+ data: [{ key: 'foo', value: 'bar', expireTime: '2025-01-12T13:00:00Z' }]
647
+ }), {
648
+ status: 200,
649
+ headers: { 'x-trace-id': traceId }
650
+ });
651
+ const { sut, apiClient } = prepare(response);
652
+ const rs = await sut
653
+ .query({ metadataFields: [types_1.MetadataField.EXPIRE_TIME] })
654
+ .cursor('second-page')
655
+ .limit(1)
656
+ .where('key', conditions_1.WhereConditions.beginsWith('fo'))
657
+ .getMany();
658
+ expect(rs).toEqual({
659
+ results: [{ key: 'foo', value: 'bar', expireTime: '2025-01-12T13:00:00Z' }],
660
+ nextCursor: 'third-page'
661
+ });
662
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/query', expect.objectContaining({
663
+ body: JSON.stringify({
664
+ limit: 1,
665
+ after: 'second-page',
666
+ where: [{ property: 'key', condition: 'BEGINS_WITH', values: ['fo'] }],
667
+ options: {
668
+ metadataFields: [types_1.MetadataField.EXPIRE_TIME]
669
+ }
670
+ })
671
+ }));
672
+ });
317
673
  it('should getOne out of a list of results', async () => {
318
674
  const response = new Response(JSON.stringify({
319
675
  data: [
@@ -343,25 +699,28 @@ describe('KVS', () => {
343
699
  body: JSON.stringify({ limit: 1 })
344
700
  }));
345
701
  });
346
- it('should query entity correctly and pass partition with index', async () => {
347
- const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
702
+ it('should query entity correctly and pass partition with index and metadata fields', async () => {
703
+ const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' }, expireTime: '2025-01-12T13:00:00Z' }] }), {
348
704
  status: 200,
349
705
  headers: { 'x-trace-id': traceId }
350
706
  });
351
707
  const { sut, apiClient } = prepare(response);
352
708
  const rs = await sut
353
709
  .entity('employees')
354
- .query()
710
+ .query({ metadataFields: [types_1.MetadataField.EXPIRE_TIME] })
355
711
  .index('by-employmentyear', { partition: [2000] })
356
712
  .getMany();
357
713
  expect(rs).toEqual({
358
- results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
714
+ results: [{ key: 'foo', value: { name: 'Jane Doe' }, expireTime: '2025-01-12T13:00:00Z' }]
359
715
  });
360
716
  expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
361
717
  body: JSON.stringify({
362
718
  entityName: 'employees',
363
719
  indexName: 'by-employmentyear',
364
- partition: [2000]
720
+ partition: [2000],
721
+ options: {
722
+ metadataFields: [types_1.MetadataField.EXPIRE_TIME]
723
+ }
365
724
  })
366
725
  }));
367
726
  });
@@ -658,6 +1017,24 @@ describe('KVS', () => {
658
1017
  })
659
1018
  }));
660
1019
  });
1020
+ it('should sumbit transaction correctly with TTL', async () => {
1021
+ const response = new Response(undefined, {
1022
+ status: 200,
1023
+ headers: { 'x-trace-id': traceId }
1024
+ });
1025
+ const { sut, apiClient } = prepare(response);
1026
+ await sut
1027
+ .transact()
1028
+ .set('foo', 'bar', undefined, { ttl: { unit: 'SECONDS', value: 5 } })
1029
+ .delete('bar')
1030
+ .execute();
1031
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/transaction', expect.objectContaining({
1032
+ body: JSON.stringify({
1033
+ set: [{ key: 'foo', value: 'bar', options: { ttl: { unit: 'SECONDS', value: 5 } } }],
1034
+ delete: [{ key: 'bar' }]
1035
+ })
1036
+ }));
1037
+ });
661
1038
  it('should batchSet correctly with mixed entity and non-entity items', async () => {
662
1039
  const response = new Response(JSON.stringify({
663
1040
  successfulKeys: [{ key: 'foo', entityName: 'employees' }, { key: 'bar' }],
@@ -693,6 +1070,43 @@ describe('KVS', () => {
693
1070
  body: JSON.stringify(items)
694
1071
  }));
695
1072
  });
1073
+ it('should batchSet correctly with mixed entity and non-entity items with TTL', async () => {
1074
+ const response = new Response(JSON.stringify({
1075
+ successfulKeys: [{ key: 'foo', entityName: 'employees' }, { key: 'bar' }, { key: 'xxx' }],
1076
+ failedKeys: [
1077
+ {
1078
+ key: 'baz',
1079
+ entityName: 'departments',
1080
+ error: { code: 'VALIDATION_ERROR', message: 'Invalid value' }
1081
+ }
1082
+ ]
1083
+ }), {
1084
+ status: 200,
1085
+ headers: { 'x-trace-id': traceId }
1086
+ });
1087
+ const { sut, apiClient } = prepare(response);
1088
+ const ttl = { value: 10, unit: 'MINUTES' };
1089
+ const items = [
1090
+ { key: 'foo', value: 'John Doe', entityName: 'employees', options: { ttl } },
1091
+ { key: 'bar', value: 'simple value', options: { ttl } },
1092
+ { key: 'baz', value: 'IT Department', entityName: 'departments', options: { ttl } },
1093
+ { key: 'xxx', value: 'yyy', options: { ttl } }
1094
+ ];
1095
+ const rs = await sut.batchSet(items);
1096
+ expect(rs).toEqual({
1097
+ successfulKeys: [{ key: 'foo', entityName: 'employees' }, { key: 'bar' }, { key: 'xxx' }],
1098
+ failedKeys: [
1099
+ {
1100
+ key: 'baz',
1101
+ entityName: 'departments',
1102
+ error: { code: 'VALIDATION_ERROR', message: 'Invalid value' }
1103
+ }
1104
+ ]
1105
+ });
1106
+ expect(apiClient).toHaveBeenCalledWith('/api/v1/batch/set', expect.objectContaining({
1107
+ body: JSON.stringify(items)
1108
+ }));
1109
+ });
696
1110
  it('should handle batchSet with all successful keys', async () => {
697
1111
  const response = new Response(JSON.stringify({
698
1112
  successfulKeys: [{ key: 'foo' }, { key: 'bar' }],
@@ -1,9 +1,10 @@
1
- import { EntityFilterClauses, AndFilter, EntityQueryBuilder, IndexQueryBuilder, Filter, FilterItem, IndexOptions, OrFilter } from './interfaces/entity-query';
1
+ import { EntityFilterClauses, EntityQueryOptions, AndFilter, EntityQueryBuilder, IndexQueryBuilder, Filter, FilterItem, IndexOptions, OrFilter } from './interfaces/entity-query';
2
2
  import { StorageApi } from './storage-api';
3
3
  export declare class KvsIndexQueryBuilder<T> implements IndexQueryBuilder<T> {
4
4
  private readonly entityName;
5
5
  private readonly storageApi;
6
- constructor(entityName: string, storageApi: StorageApi);
6
+ private readonly options?;
7
+ constructor(entityName: string, storageApi: StorageApi, options?: Partial<EntityQueryOptions<T>> | undefined);
7
8
  index(name: string, indexOptions?: IndexOptions<T>): EntityQueryBuilder<T>;
8
9
  }
9
10
  export declare abstract class BaseFilter<T> {
@@ -1 +1 @@
1
- {"version":3,"file":"entity-query.d.ts","sourceRoot":"","sources":["../src/entity-query.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAGnB,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,MAAM,EACN,UAAU,EACV,YAAY,EACZ,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,qBAAa,oBAAoB,CAAC,CAAC,CAAE,YAAW,iBAAiB,CAAC,CAAC,CAAC;IAEhE,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU;IAGzC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC;CAQ3E;AAwDD,8BAAsB,UAAU,CAAC,CAAC;IACpB,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAA3B,KAAK,GAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAM;IAExD,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAI/B,QAAQ;CAGhB;AAED,qBAAa,aAAa,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,MAAM,CAAC,CAAC,CAAC;IACtE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAIxE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,eAAe,CAAC,CAAC,CAAC;CAGvE;AAED,cAAM,gBAAgB,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,SAAS,CAAC,CAAC,CAAC;IACrE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,IAAI;CAI1D;AAED,cAAM,eAAe,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,QAAQ,CAAC,CAAC,CAAC;IACnE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,IAAI;CAIzD"}
1
+ {"version":3,"file":"entity-query.d.ts","sourceRoot":"","sources":["../src/entity-query.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAElB,SAAS,EACT,kBAAkB,EAClB,iBAAiB,EACjB,MAAM,EACN,UAAU,EACV,YAAY,EACZ,QAAQ,EACT,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,qBAAa,oBAAoB,CAAC,CAAC,CAAE,YAAW,iBAAiB,CAAC,CAAC,CAAC;IAEhE,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAFR,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,4CAAgC;IAG3D,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC;CAU3E;AA4DD,8BAAsB,UAAU,CAAC,CAAC;IACpB,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAA3B,KAAK,GAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAM;IAExD,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAI/B,QAAQ;CAGhB;AAED,qBAAa,aAAa,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,MAAM,CAAC,CAAC,CAAC;IACtE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAIxE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,eAAe,CAAC,CAAC,CAAC;CAGvE;AAED,cAAM,gBAAgB,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,SAAS,CAAC,CAAC,CAAC;IACrE,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,IAAI;CAI1D;AAED,cAAM,eAAe,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAE,YAAW,QAAQ,CAAC,CAAC,CAAC;IACnE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,IAAI;CAIzD"}
@@ -4,12 +4,15 @@ exports.FilterBuilder = exports.BaseFilter = exports.KvsIndexQueryBuilder = void
4
4
  class KvsIndexQueryBuilder {
5
5
  entityName;
6
6
  storageApi;
7
- constructor(entityName, storageApi) {
7
+ options;
8
+ constructor(entityName, storageApi, options) {
8
9
  this.entityName = entityName;
9
10
  this.storageApi = storageApi;
11
+ this.options = options;
10
12
  }
11
13
  index(name, indexOptions) {
12
14
  return new KvsEntityQueryBuilder(this.storageApi, {
15
+ ...this.options,
13
16
  entityName: this.entityName,
14
17
  indexName: name,
15
18
  partition: indexOptions?.partition,
@@ -54,16 +57,20 @@ class KvsEntityQueryBuilder {
54
57
  return undefined;
55
58
  }
56
59
  async getMany() {
57
- const { filters, filterOperator, ...rest } = this.queryOptions;
60
+ const maybeOptions = {
61
+ ...(this.queryOptions?.metadataFields ? { options: { metadataFields: this.queryOptions.metadataFields } } : {})
62
+ };
63
+ const { filters, filterOperator, metadataFields, ...rest } = this.queryOptions;
58
64
  if (filters && filterOperator && filters.length > 0) {
59
65
  return this.storageApi.queryEntity({
60
66
  ...rest,
67
+ ...maybeOptions,
61
68
  filters: {
62
69
  [filterOperator]: filters
63
70
  }
64
71
  });
65
72
  }
66
- return this.storageApi.queryEntity(rest);
73
+ return this.storageApi.queryEntity({ ...rest, ...maybeOptions });
67
74
  }
68
75
  }
69
76
  class BaseFilter {
package/out/entity.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { IndexQueryBuilder } from './interfaces/entity-query';
1
+ import { EntityQueryOptions, IndexQueryBuilder } from './interfaces/entity-query';
2
2
  import { KvsEntity } from './interfaces/kvs';
3
- import { GetOptions, GetResult } from './interfaces/types';
3
+ import { GetOptions, GetResult, SetOptions, PolicySetOptions, OverrideAndReturnSetOptions, SetResult } from './interfaces/types';
4
4
  import { StorageApi } from './storage-api';
5
5
  export declare class EntityImpl<T> implements KvsEntity<T> {
6
6
  private readonly entityName;
@@ -8,8 +8,10 @@ export declare class EntityImpl<T> implements KvsEntity<T> {
8
8
  constructor(entityName: string, storageApi: StorageApi);
9
9
  get(key: string): Promise<T | undefined>;
10
10
  get(key: string, options: GetOptions): Promise<GetResult<T> | undefined>;
11
- set(key: string, value: T): Promise<void>;
11
+ set(key: string, value: T, options?: SetOptions): Promise<void>;
12
+ set(key: string, value: T, options: PolicySetOptions): Promise<void>;
13
+ set<U = T>(key: string, value: T, options: OverrideAndReturnSetOptions): Promise<SetResult<U> | undefined>;
12
14
  delete(key: string): Promise<void>;
13
- query(): IndexQueryBuilder<T>;
15
+ query(options?: Pick<EntityQueryOptions<T>, 'metadataFields'>): IndexQueryBuilder<T>;
14
16
  }
15
17
  //# sourceMappingURL=entity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,SAAS,CAAC,CAAC,CAAC;IAE9C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU;IAGzC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IASxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,KAAK,IAAI,iBAAiB,CAAC,CAAC,CAAC;CAG9B"}
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,UAAU,EACV,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,2BAA2B,EAC3B,SAAS,EAEV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,qBAAa,UAAU,CAAC,CAAC,CAAE,YAAW,SAAS,CAAC,CAAC,CAAC;IAE9C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU;IAGzC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IASxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAC/D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IACpE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAU1G,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC;CAGrF"}