@forge/kvs 0.0.2-next.0 → 0.0.2-next.2
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/out/__test__/index.test.d.ts +2 -0
- package/out/__test__/index.test.d.ts.map +1 -0
- package/out/__test__/index.test.js +509 -0
- package/out/conditions.d.ts +38 -0
- package/out/conditions.d.ts.map +1 -0
- package/out/conditions.js +98 -0
- package/out/entity-query.d.ts +13 -0
- package/out/entity-query.d.ts.map +1 -0
- package/out/entity-query.js +100 -0
- package/out/entity.d.ts +13 -0
- package/out/entity.d.ts.map +1 -0
- package/out/entity.js +35 -0
- package/out/errors.d.ts +21 -0
- package/out/errors.d.ts.map +1 -0
- package/out/errors.js +26 -0
- package/out/index.d.ts +9 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +43 -0
- package/out/interfaces/entity-query.d.ts +41 -0
- package/out/interfaces/entity-query.d.ts.map +1 -0
- package/out/interfaces/entity-query.js +2 -0
- package/out/interfaces/kvs-api.d.ts +72 -0
- package/out/interfaces/kvs-api.d.ts.map +1 -0
- package/out/interfaces/kvs-api.js +2 -0
- package/out/interfaces/kvs.d.ts +19 -0
- package/out/interfaces/kvs.d.ts.map +1 -0
- package/out/interfaces/kvs.js +2 -0
- package/out/interfaces/query.d.ts +17 -0
- package/out/interfaces/query.d.ts.map +1 -0
- package/out/interfaces/query.js +2 -0
- package/out/interfaces/types.d.ts +64 -0
- package/out/interfaces/types.d.ts.map +1 -0
- package/out/interfaces/types.js +8 -0
- package/out/kvs.d.ts +16 -0
- package/out/kvs.d.ts.map +1 -0
- package/out/kvs.js +36 -0
- package/out/query.d.ts +14 -0
- package/out/query.d.ts.map +1 -0
- package/out/query.js +38 -0
- package/out/storage-api.d.ts +20 -0
- package/out/storage-api.d.ts.map +1 -0
- package/out/storage-api.js +77 -0
- package/out/utils/__test__/error-handling.test.d.ts +2 -0
- package/out/utils/__test__/error-handling.test.d.ts.map +1 -0
- package/out/utils/__test__/error-handling.test.js +121 -0
- package/out/utils/error-handling.d.ts +7 -0
- package/out/utils/error-handling.d.ts.map +1 -0
- package/out/utils/error-handling.js +42 -0
- package/package.json +4 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__test__/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const conditions_1 = require("../conditions");
|
|
4
|
+
const entity_query_1 = require("../entity-query");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const types_1 = require("../interfaces/types");
|
|
7
|
+
const kvs_1 = require("../kvs");
|
|
8
|
+
const storage_api_1 = require("../storage-api");
|
|
9
|
+
function prepare(response) {
|
|
10
|
+
const apiClient = jest.fn().mockResolvedValueOnce(response);
|
|
11
|
+
const storageApi = new storage_api_1.StorageApi(apiClient);
|
|
12
|
+
const sut = new kvs_1.KvsImpl(storageApi);
|
|
13
|
+
return {
|
|
14
|
+
apiClient,
|
|
15
|
+
storageApi,
|
|
16
|
+
sut
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
describe('KVS', () => {
|
|
20
|
+
const traceId = 'traceId';
|
|
21
|
+
it('should get correctly', async () => {
|
|
22
|
+
const response = new Response(JSON.stringify({ key: 'foo', value: 'bar' }), {
|
|
23
|
+
status: 200,
|
|
24
|
+
headers: { 'x-trace-id': traceId }
|
|
25
|
+
});
|
|
26
|
+
const { sut, apiClient } = prepare(response);
|
|
27
|
+
const rs = await sut.get('foo');
|
|
28
|
+
expect(rs).toEqual('bar');
|
|
29
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/get', expect.objectContaining({
|
|
30
|
+
body: JSON.stringify({ key: 'foo' })
|
|
31
|
+
}));
|
|
32
|
+
});
|
|
33
|
+
it('should handle not found error', async () => {
|
|
34
|
+
const response = new Response(JSON.stringify({ code: 'KEY_NOT_FOUND', message: 'Provided key does not exist' }), {
|
|
35
|
+
status: 404,
|
|
36
|
+
statusText: 'Not Found',
|
|
37
|
+
headers: { 'x-trace-id': traceId }
|
|
38
|
+
});
|
|
39
|
+
const { sut } = prepare(response);
|
|
40
|
+
await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 404, statusText: 'Not Found', traceId }, { code: 'KEY_NOT_FOUND', message: 'Provided key does not exist' }));
|
|
41
|
+
});
|
|
42
|
+
it('should handle unexpected response', async () => {
|
|
43
|
+
const response = new Response(JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }), {
|
|
44
|
+
status: 500,
|
|
45
|
+
statusText: 'Internal Server Error',
|
|
46
|
+
headers: { 'x-trace-id': traceId }
|
|
47
|
+
});
|
|
48
|
+
const { sut } = prepare(response);
|
|
49
|
+
await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 500, statusText: 'Internal Server Error', traceId }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
|
|
50
|
+
});
|
|
51
|
+
it('should handle non-json response even though status is ok', async () => {
|
|
52
|
+
const response = new Response('Some non JSON response that will fail to parse', {
|
|
53
|
+
status: 200,
|
|
54
|
+
headers: { 'x-trace-id': traceId }
|
|
55
|
+
});
|
|
56
|
+
const { sut } = prepare(response);
|
|
57
|
+
await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsError('Unexpected error. Response was not valid JSON: Some non JSON response that will fail to parse'));
|
|
58
|
+
});
|
|
59
|
+
it('should getSecret correctly', async () => {
|
|
60
|
+
const response = new Response(JSON.stringify({ key: 'foo', value: 'bar' }), {
|
|
61
|
+
status: 200,
|
|
62
|
+
headers: { 'x-trace-id': traceId }
|
|
63
|
+
});
|
|
64
|
+
const { sut, apiClient } = prepare(response);
|
|
65
|
+
const rs = await sut.getSecret('foo');
|
|
66
|
+
expect(rs).toEqual('bar');
|
|
67
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/get', expect.objectContaining({
|
|
68
|
+
body: JSON.stringify({ key: 'foo' })
|
|
69
|
+
}));
|
|
70
|
+
});
|
|
71
|
+
it('should getEntity correctly', async () => {
|
|
72
|
+
const response = new Response(JSON.stringify({ key: 'foo', value: { name: 'Jane Doe' } }), {
|
|
73
|
+
status: 200,
|
|
74
|
+
headers: { 'x-trace-id': traceId }
|
|
75
|
+
});
|
|
76
|
+
const { sut, apiClient } = prepare(response);
|
|
77
|
+
const rs = await sut.entity('employees').get('foo');
|
|
78
|
+
expect(rs).toEqual({ name: 'Jane Doe' });
|
|
79
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/get', expect.objectContaining({
|
|
80
|
+
body: JSON.stringify({ entityName: 'employees', key: 'foo' })
|
|
81
|
+
}));
|
|
82
|
+
});
|
|
83
|
+
it('should set correctly', async () => {
|
|
84
|
+
const response = new Response(undefined, {
|
|
85
|
+
status: 204,
|
|
86
|
+
headers: { 'x-trace-id': traceId }
|
|
87
|
+
});
|
|
88
|
+
const { sut, apiClient } = prepare(response);
|
|
89
|
+
const rs = await sut.set('foo', 'bar');
|
|
90
|
+
expect(rs).toBeUndefined();
|
|
91
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/set', expect.objectContaining({
|
|
92
|
+
body: JSON.stringify({ key: 'foo', value: 'bar' })
|
|
93
|
+
}));
|
|
94
|
+
});
|
|
95
|
+
it('should setSecret correctly', async () => {
|
|
96
|
+
const response = new Response(undefined, {
|
|
97
|
+
status: 204,
|
|
98
|
+
headers: { 'x-trace-id': traceId }
|
|
99
|
+
});
|
|
100
|
+
const { sut, apiClient } = prepare(response);
|
|
101
|
+
const rs = await sut.setSecret('foo', 'bar');
|
|
102
|
+
expect(rs).toBeUndefined();
|
|
103
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/set', expect.objectContaining({
|
|
104
|
+
body: JSON.stringify({ key: 'foo', value: 'bar' })
|
|
105
|
+
}));
|
|
106
|
+
});
|
|
107
|
+
it('should setEntity correctly', async () => {
|
|
108
|
+
const response = new Response(undefined, {
|
|
109
|
+
status: 204,
|
|
110
|
+
headers: { 'x-trace-id': traceId }
|
|
111
|
+
});
|
|
112
|
+
const { sut, apiClient } = prepare(response);
|
|
113
|
+
const rs = await sut.entity('employees').set('foo', { name: 'Jane Doe' });
|
|
114
|
+
expect(rs).toBeUndefined();
|
|
115
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/set', expect.objectContaining({
|
|
116
|
+
body: JSON.stringify({ entityName: 'employees', key: 'foo', value: { name: 'Jane Doe' } })
|
|
117
|
+
}));
|
|
118
|
+
});
|
|
119
|
+
it('should delete correctly', async () => {
|
|
120
|
+
const response = new Response(undefined, {
|
|
121
|
+
status: 204,
|
|
122
|
+
headers: { 'x-trace-id': traceId }
|
|
123
|
+
});
|
|
124
|
+
const { sut, apiClient } = prepare(response);
|
|
125
|
+
const rs = await sut.delete('foo');
|
|
126
|
+
expect(rs).toBeUndefined();
|
|
127
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/delete', expect.objectContaining({
|
|
128
|
+
body: JSON.stringify({ key: 'foo' })
|
|
129
|
+
}));
|
|
130
|
+
});
|
|
131
|
+
it('should deleteSecret correctly', async () => {
|
|
132
|
+
const response = new Response(undefined, {
|
|
133
|
+
status: 204,
|
|
134
|
+
headers: { 'x-trace-id': traceId }
|
|
135
|
+
});
|
|
136
|
+
const { sut, apiClient } = prepare(response);
|
|
137
|
+
const rs = await sut.deleteSecret('foo');
|
|
138
|
+
expect(rs).toBeUndefined();
|
|
139
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/secret/delete', expect.objectContaining({
|
|
140
|
+
body: JSON.stringify({ key: 'foo' })
|
|
141
|
+
}));
|
|
142
|
+
});
|
|
143
|
+
it('should deleteEntity correctly', async () => {
|
|
144
|
+
const response = new Response(undefined, {
|
|
145
|
+
status: 204,
|
|
146
|
+
headers: { 'x-trace-id': traceId }
|
|
147
|
+
});
|
|
148
|
+
const { sut, apiClient } = prepare(response);
|
|
149
|
+
const rs = await sut.entity('employees').delete('foo');
|
|
150
|
+
expect(rs).toBeUndefined();
|
|
151
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/delete', expect.objectContaining({
|
|
152
|
+
body: JSON.stringify({ entityName: 'employees', key: 'foo' })
|
|
153
|
+
}));
|
|
154
|
+
});
|
|
155
|
+
it('should query correctly', async () => {
|
|
156
|
+
const response = new Response(JSON.stringify({ cursor: 'third-page', data: [{ key: 'foo', value: 'bar' }] }), {
|
|
157
|
+
status: 200,
|
|
158
|
+
headers: { 'x-trace-id': traceId }
|
|
159
|
+
});
|
|
160
|
+
const { sut, apiClient } = prepare(response);
|
|
161
|
+
const rs = await sut
|
|
162
|
+
.query()
|
|
163
|
+
.cursor('second-page')
|
|
164
|
+
.limit(1)
|
|
165
|
+
.where('key', conditions_1.WhereConditions.beginsWith('fo'))
|
|
166
|
+
.getMany();
|
|
167
|
+
expect(rs).toEqual({
|
|
168
|
+
results: [{ key: 'foo', value: 'bar' }],
|
|
169
|
+
nextCursor: 'third-page'
|
|
170
|
+
});
|
|
171
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/query', expect.objectContaining({
|
|
172
|
+
body: JSON.stringify({
|
|
173
|
+
limit: 1,
|
|
174
|
+
after: 'second-page',
|
|
175
|
+
where: [{ property: 'key', condition: 'BEGINS_WITH', values: ['fo'] }]
|
|
176
|
+
})
|
|
177
|
+
}));
|
|
178
|
+
});
|
|
179
|
+
it('should getOne out of a list of results', async () => {
|
|
180
|
+
const response = new Response(JSON.stringify({
|
|
181
|
+
data: [
|
|
182
|
+
{ key: 'foo', value: 'bar' },
|
|
183
|
+
{ key: 'bar', value: 'foo' }
|
|
184
|
+
]
|
|
185
|
+
}), {
|
|
186
|
+
status: 200,
|
|
187
|
+
headers: { 'x-trace-id': traceId }
|
|
188
|
+
});
|
|
189
|
+
const { sut, apiClient } = prepare(response);
|
|
190
|
+
const rs = await sut.query().getOne();
|
|
191
|
+
expect(rs).toEqual({ key: 'foo', value: 'bar' });
|
|
192
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/query', expect.objectContaining({
|
|
193
|
+
body: JSON.stringify({ limit: 1 })
|
|
194
|
+
}));
|
|
195
|
+
});
|
|
196
|
+
it('should default to undefined when getOne receives an empty list', async () => {
|
|
197
|
+
const response = new Response(JSON.stringify({ data: [] }), {
|
|
198
|
+
status: 200,
|
|
199
|
+
headers: { 'x-trace-id': traceId }
|
|
200
|
+
});
|
|
201
|
+
const { sut, apiClient } = prepare(response);
|
|
202
|
+
const rs = await sut.query().getOne();
|
|
203
|
+
expect(rs).toBeUndefined();
|
|
204
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/query', expect.objectContaining({
|
|
205
|
+
body: JSON.stringify({ limit: 1 })
|
|
206
|
+
}));
|
|
207
|
+
});
|
|
208
|
+
it('should query entity correctly and pass partition with index', async () => {
|
|
209
|
+
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
|
|
210
|
+
status: 200,
|
|
211
|
+
headers: { 'x-trace-id': traceId }
|
|
212
|
+
});
|
|
213
|
+
const { sut, apiClient } = prepare(response);
|
|
214
|
+
const rs = await sut
|
|
215
|
+
.entity('employees')
|
|
216
|
+
.query()
|
|
217
|
+
.index('by-employmentyear', { partition: [2000] })
|
|
218
|
+
.getMany();
|
|
219
|
+
expect(rs).toEqual({
|
|
220
|
+
results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
|
|
221
|
+
});
|
|
222
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
223
|
+
body: JSON.stringify({
|
|
224
|
+
entityName: 'employees',
|
|
225
|
+
indexName: 'by-employmentyear',
|
|
226
|
+
partition: [2000]
|
|
227
|
+
})
|
|
228
|
+
}));
|
|
229
|
+
});
|
|
230
|
+
it('should query entity correctly (without any filters)', async () => {
|
|
231
|
+
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
|
|
232
|
+
status: 200,
|
|
233
|
+
headers: { 'x-trace-id': traceId }
|
|
234
|
+
});
|
|
235
|
+
const { sut, apiClient } = prepare(response);
|
|
236
|
+
const rs = await sut
|
|
237
|
+
.entity('employees')
|
|
238
|
+
.query()
|
|
239
|
+
.index('by-key')
|
|
240
|
+
.where(conditions_1.WhereConditions.beginsWith('f'))
|
|
241
|
+
.sort(types_1.Sort.ASC)
|
|
242
|
+
.limit(100)
|
|
243
|
+
.cursor('second-page')
|
|
244
|
+
.getMany();
|
|
245
|
+
expect(rs).toEqual({
|
|
246
|
+
results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
|
|
247
|
+
});
|
|
248
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
249
|
+
body: JSON.stringify({
|
|
250
|
+
entityName: 'employees',
|
|
251
|
+
indexName: 'by-key',
|
|
252
|
+
range: { condition: 'BEGINS_WITH', values: ['f'] },
|
|
253
|
+
sort: 'ASC',
|
|
254
|
+
limit: 100,
|
|
255
|
+
cursor: 'second-page'
|
|
256
|
+
})
|
|
257
|
+
}));
|
|
258
|
+
});
|
|
259
|
+
it('should query entity correctly (with "or" filters)', async () => {
|
|
260
|
+
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
|
|
261
|
+
status: 200,
|
|
262
|
+
headers: { 'x-trace-id': traceId }
|
|
263
|
+
});
|
|
264
|
+
const { sut, apiClient } = prepare(response);
|
|
265
|
+
const filter = new entity_query_1.FilterBuilder()
|
|
266
|
+
.or('age', conditions_1.FilterConditions.greaterThan(30))
|
|
267
|
+
.or('age', conditions_1.FilterConditions.lessThan(35));
|
|
268
|
+
const rs = await sut
|
|
269
|
+
.entity('employees')
|
|
270
|
+
.query()
|
|
271
|
+
.index('by-key')
|
|
272
|
+
.where(conditions_1.WhereConditions.beginsWith('f'))
|
|
273
|
+
.filters(filter)
|
|
274
|
+
.getMany();
|
|
275
|
+
expect(rs).toEqual({
|
|
276
|
+
results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
|
|
277
|
+
});
|
|
278
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
279
|
+
body: JSON.stringify({
|
|
280
|
+
entityName: 'employees',
|
|
281
|
+
indexName: 'by-key',
|
|
282
|
+
range: { condition: 'BEGINS_WITH', values: ['f'] },
|
|
283
|
+
filters: {
|
|
284
|
+
or: [
|
|
285
|
+
{ property: 'age', condition: 'GREATER_THAN', values: [30] },
|
|
286
|
+
{ property: 'age', condition: 'LESS_THAN', values: [35] }
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
}));
|
|
291
|
+
});
|
|
292
|
+
it('should query entity correctly (with "and" filters)', async () => {
|
|
293
|
+
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
|
|
294
|
+
status: 200,
|
|
295
|
+
headers: { 'x-trace-id': traceId }
|
|
296
|
+
});
|
|
297
|
+
const { sut, apiClient } = prepare(response);
|
|
298
|
+
const rs = await sut
|
|
299
|
+
.entity('employees')
|
|
300
|
+
.query()
|
|
301
|
+
.index('by-key')
|
|
302
|
+
.where(conditions_1.WhereConditions.beginsWith('f'))
|
|
303
|
+
.filters(new entity_query_1.FilterBuilder()
|
|
304
|
+
.and('age', conditions_1.FilterConditions.greaterThan(30))
|
|
305
|
+
.and('employmentyear', conditions_1.FilterConditions.lessThan(2000)))
|
|
306
|
+
.getMany();
|
|
307
|
+
expect(rs).toEqual({
|
|
308
|
+
results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
|
|
309
|
+
});
|
|
310
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
311
|
+
body: JSON.stringify({
|
|
312
|
+
entityName: 'employees',
|
|
313
|
+
indexName: 'by-key',
|
|
314
|
+
range: { condition: 'BEGINS_WITH', values: ['f'] },
|
|
315
|
+
filters: {
|
|
316
|
+
and: [
|
|
317
|
+
{ property: 'age', condition: 'GREATER_THAN', values: [30] },
|
|
318
|
+
{ property: 'employmentyear', condition: 'LESS_THAN', values: [2000] }
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
})
|
|
322
|
+
}));
|
|
323
|
+
});
|
|
324
|
+
it('should getOne entity out of a list', async () => {
|
|
325
|
+
const response = new Response(JSON.stringify({
|
|
326
|
+
data: [
|
|
327
|
+
{ key: 'foo', value: { name: 'Jane Foo' } },
|
|
328
|
+
{ key: 'bar', value: { name: 'Jane Bar' } }
|
|
329
|
+
]
|
|
330
|
+
}), {
|
|
331
|
+
status: 200,
|
|
332
|
+
headers: { 'x-trace-id': traceId }
|
|
333
|
+
});
|
|
334
|
+
const { sut, apiClient } = prepare(response);
|
|
335
|
+
const rs = await sut
|
|
336
|
+
.entity('employees')
|
|
337
|
+
.query()
|
|
338
|
+
.index('by-key')
|
|
339
|
+
.where(conditions_1.WhereConditions.beginsWith('f'))
|
|
340
|
+
.getOne();
|
|
341
|
+
expect(rs).toEqual({ key: 'foo', value: { name: 'Jane Foo' } });
|
|
342
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
343
|
+
body: JSON.stringify({
|
|
344
|
+
entityName: 'employees',
|
|
345
|
+
indexName: 'by-key',
|
|
346
|
+
range: { condition: 'BEGINS_WITH', values: ['f'] },
|
|
347
|
+
limit: 1
|
|
348
|
+
})
|
|
349
|
+
}));
|
|
350
|
+
});
|
|
351
|
+
it('should default to undefined when getOne entity receives an empty list', async () => {
|
|
352
|
+
const response = new Response(JSON.stringify({ data: [] }), {
|
|
353
|
+
status: 200,
|
|
354
|
+
headers: { 'x-trace-id': traceId }
|
|
355
|
+
});
|
|
356
|
+
const { sut, apiClient } = prepare(response);
|
|
357
|
+
const rs = await sut
|
|
358
|
+
.entity('employees')
|
|
359
|
+
.query()
|
|
360
|
+
.index('by-key')
|
|
361
|
+
.where(conditions_1.WhereConditions.beginsWith('f'))
|
|
362
|
+
.getOne();
|
|
363
|
+
expect(rs).toBeUndefined();
|
|
364
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
365
|
+
body: JSON.stringify({
|
|
366
|
+
entityName: 'employees',
|
|
367
|
+
indexName: 'by-key',
|
|
368
|
+
range: { condition: 'BEGINS_WITH', values: ['f'] },
|
|
369
|
+
limit: 1
|
|
370
|
+
})
|
|
371
|
+
}));
|
|
372
|
+
});
|
|
373
|
+
it.each([
|
|
374
|
+
{
|
|
375
|
+
title: 'between',
|
|
376
|
+
condition: conditions_1.WhereConditions.between(10, 20),
|
|
377
|
+
expectedPayload: { condition: 'BETWEEN', values: [10, 20] }
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
title: 'beginsWith',
|
|
381
|
+
condition: conditions_1.WhereConditions.beginsWith('foo'),
|
|
382
|
+
expectedPayload: { condition: 'BEGINS_WITH', values: ['foo'] }
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
title: 'equalTo',
|
|
386
|
+
condition: conditions_1.WhereConditions.equalTo('foo'),
|
|
387
|
+
expectedPayload: { condition: 'EQUAL_TO', values: ['foo'] }
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
title: 'greaterThan',
|
|
391
|
+
condition: conditions_1.WhereConditions.greaterThan(10),
|
|
392
|
+
expectedPayload: { condition: 'GREATER_THAN', values: [10] }
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
title: 'greaterThanEqualTo',
|
|
396
|
+
condition: conditions_1.WhereConditions.greaterThanEqualTo(10),
|
|
397
|
+
expectedPayload: { condition: 'GREATER_THAN_EQUAL_TO', values: [10] }
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
title: 'lessThan',
|
|
401
|
+
condition: conditions_1.WhereConditions.lessThan(10),
|
|
402
|
+
expectedPayload: { condition: 'LESS_THAN', values: [10] }
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
title: 'lessThanEqualTo',
|
|
406
|
+
condition: conditions_1.WhereConditions.lessThanEqualTo(10),
|
|
407
|
+
expectedPayload: { condition: 'LESS_THAN_EQUAL_TO', values: [10] }
|
|
408
|
+
}
|
|
409
|
+
])('should test $title all where conditions', async ({ condition, expectedPayload }) => {
|
|
410
|
+
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
|
|
411
|
+
status: 200,
|
|
412
|
+
headers: { 'x-trace-id': traceId }
|
|
413
|
+
});
|
|
414
|
+
const { sut, apiClient } = prepare(response);
|
|
415
|
+
const rs = await sut.entity('employees').query().index('by-key').where(condition).getMany();
|
|
416
|
+
expect(rs).toEqual({
|
|
417
|
+
results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
|
|
418
|
+
});
|
|
419
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
420
|
+
body: JSON.stringify({
|
|
421
|
+
entityName: 'employees',
|
|
422
|
+
indexName: 'by-key',
|
|
423
|
+
range: expectedPayload
|
|
424
|
+
})
|
|
425
|
+
}));
|
|
426
|
+
});
|
|
427
|
+
it.each([
|
|
428
|
+
{
|
|
429
|
+
title: 'between',
|
|
430
|
+
condition: conditions_1.FilterConditions.between(10, 20),
|
|
431
|
+
expectedPayload: { condition: 'BETWEEN', values: [10, 20] }
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
title: 'beginsWith',
|
|
435
|
+
condition: conditions_1.FilterConditions.beginsWith('foo'),
|
|
436
|
+
expectedPayload: { condition: 'BEGINS_WITH', values: ['foo'] }
|
|
437
|
+
},
|
|
438
|
+
{ title: 'exists', condition: conditions_1.FilterConditions.exists(), expectedPayload: { condition: 'EXISTS', values: [true] } },
|
|
439
|
+
{
|
|
440
|
+
title: 'notExists',
|
|
441
|
+
condition: conditions_1.FilterConditions.notExists(),
|
|
442
|
+
expectedPayload: { condition: 'NOT_EXISTS', values: [true] }
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
title: 'greaterThan',
|
|
446
|
+
condition: conditions_1.FilterConditions.greaterThan(10),
|
|
447
|
+
expectedPayload: { condition: 'GREATER_THAN', values: [10] }
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
title: 'greaterThanEqualTo',
|
|
451
|
+
condition: conditions_1.FilterConditions.greaterThanEqualTo(10),
|
|
452
|
+
expectedPayload: { condition: 'GREATER_THAN_EQUAL_TO', values: [10] }
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
title: 'lessThan',
|
|
456
|
+
condition: conditions_1.FilterConditions.lessThan(10),
|
|
457
|
+
expectedPayload: { condition: 'LESS_THAN', values: [10] }
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
title: 'lessThanEqualTo',
|
|
461
|
+
condition: conditions_1.FilterConditions.lessThanEqualTo(10),
|
|
462
|
+
expectedPayload: { condition: 'LESS_THAN_EQUAL_TO', values: [10] }
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
title: 'contains',
|
|
466
|
+
condition: conditions_1.FilterConditions.contains('foo'),
|
|
467
|
+
expectedPayload: { condition: 'CONTAINS', values: ['foo'] }
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
title: 'notContains',
|
|
471
|
+
condition: conditions_1.FilterConditions.notContains('foo'),
|
|
472
|
+
expectedPayload: { condition: 'NOT_CONTAINS', values: ['foo'] }
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
title: 'equalTo',
|
|
476
|
+
condition: conditions_1.FilterConditions.equalTo('foo'),
|
|
477
|
+
expectedPayload: { condition: 'EQUAL_TO', values: ['foo'] }
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
title: 'notEqualTo',
|
|
481
|
+
condition: conditions_1.FilterConditions.notEqualTo('foo'),
|
|
482
|
+
expectedPayload: { condition: 'NOT_EQUAL_TO', values: ['foo'] }
|
|
483
|
+
}
|
|
484
|
+
])('should test $title all filter conditions', async ({ condition, expectedPayload }) => {
|
|
485
|
+
const response = new Response(JSON.stringify({ data: [{ key: 'foo', value: { name: 'Jane Doe' } }] }), {
|
|
486
|
+
status: 200,
|
|
487
|
+
headers: { 'x-trace-id': traceId }
|
|
488
|
+
});
|
|
489
|
+
const { sut, apiClient } = prepare(response);
|
|
490
|
+
const rs = await sut
|
|
491
|
+
.entity('employees')
|
|
492
|
+
.query()
|
|
493
|
+
.index('by-key')
|
|
494
|
+
.filters(new entity_query_1.FilterBuilder().and('name', condition))
|
|
495
|
+
.getMany();
|
|
496
|
+
expect(rs).toEqual({
|
|
497
|
+
results: [{ key: 'foo', value: { name: 'Jane Doe' } }]
|
|
498
|
+
});
|
|
499
|
+
expect(apiClient).toHaveBeenCalledWith('/api/v1/entity/query', expect.objectContaining({
|
|
500
|
+
body: JSON.stringify({
|
|
501
|
+
entityName: 'employees',
|
|
502
|
+
indexName: 'by-key',
|
|
503
|
+
filters: {
|
|
504
|
+
and: [{ property: 'name', ...expectedPayload }]
|
|
505
|
+
}
|
|
506
|
+
})
|
|
507
|
+
}));
|
|
508
|
+
});
|
|
509
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BeginsWithClause, BetweenClause, ContainsClause, EqualToClause, ExistsClause, GreaterThanClause, GreaterThanEqualToClause, LessThanClause, LessThanEqualToClause, NotContainsClause, NotEqualToClause, NotExistsClause, StringOrNumber, StringOrNumberOrBoolean } from './interfaces/types';
|
|
2
|
+
declare function between<T extends StringOrNumber>(firstValue: T, secondValue: T): BetweenClause<T>;
|
|
3
|
+
declare function beginsWith(value: StringOrNumber): BeginsWithClause;
|
|
4
|
+
declare function exists(): ExistsClause;
|
|
5
|
+
declare function notExists(): NotExistsClause;
|
|
6
|
+
declare function greaterThan(value: StringOrNumber): GreaterThanClause;
|
|
7
|
+
declare function greaterThanEqualTo(value: StringOrNumber): GreaterThanEqualToClause;
|
|
8
|
+
declare function lessThan(value: StringOrNumber): LessThanClause;
|
|
9
|
+
declare function lessThanEqualTo(value: StringOrNumber): LessThanEqualToClause;
|
|
10
|
+
declare function contains(value: string): ContainsClause;
|
|
11
|
+
declare function notContains(value: string): NotContainsClause;
|
|
12
|
+
declare function equalTo(value: StringOrNumberOrBoolean): EqualToClause;
|
|
13
|
+
declare function notEqualTo(value: StringOrNumberOrBoolean): NotEqualToClause;
|
|
14
|
+
export declare const WhereConditions: {
|
|
15
|
+
beginsWith: typeof beginsWith;
|
|
16
|
+
between: typeof between;
|
|
17
|
+
equalTo: typeof equalTo;
|
|
18
|
+
greaterThan: typeof greaterThan;
|
|
19
|
+
greaterThanEqualTo: typeof greaterThanEqualTo;
|
|
20
|
+
lessThan: typeof lessThan;
|
|
21
|
+
lessThanEqualTo: typeof lessThanEqualTo;
|
|
22
|
+
};
|
|
23
|
+
export declare const FilterConditions: {
|
|
24
|
+
beginsWith: typeof beginsWith;
|
|
25
|
+
between: typeof between;
|
|
26
|
+
contains: typeof contains;
|
|
27
|
+
notContains: typeof notContains;
|
|
28
|
+
equalTo: typeof equalTo;
|
|
29
|
+
notEqualTo: typeof notEqualTo;
|
|
30
|
+
exists: typeof exists;
|
|
31
|
+
notExists: typeof notExists;
|
|
32
|
+
greaterThan: typeof greaterThan;
|
|
33
|
+
greaterThanEqualTo: typeof greaterThanEqualTo;
|
|
34
|
+
lessThan: typeof lessThan;
|
|
35
|
+
lessThanEqualTo: typeof lessThanEqualTo;
|
|
36
|
+
};
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=conditions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conditions.d.ts","sourceRoot":"","sources":["../src/conditions.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,uBAAuB,EACxB,MAAM,oBAAoB,CAAC;AAE5B,iBAAS,OAAO,CAAC,CAAC,SAAS,cAAc,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAK1F;AAED,iBAAS,UAAU,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAK3D;AAED,iBAAS,MAAM,IAAI,YAAY,CAK9B;AAED,iBAAS,SAAS,IAAI,eAAe,CAKpC;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAK7D;AAED,iBAAS,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,wBAAwB,CAK3E;AAED,iBAAS,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAKvD;AAED,iBAAS,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,qBAAqB,CAKrE;AAED,iBAAS,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAK/C;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAKrD;AAED,iBAAS,OAAO,CAAC,KAAK,EAAE,uBAAuB,GAAG,aAAa,CAK9D;AAED,iBAAS,UAAU,CAAC,KAAK,EAAE,uBAAuB,GAAG,gBAAgB,CAKpE;AAED,eAAO,MAAM,eAAe;;;;;;;;CAQ3B,CAAC;AAEF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;CAa5B,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FilterConditions = exports.WhereConditions = void 0;
|
|
4
|
+
function between(firstValue, secondValue) {
|
|
5
|
+
return {
|
|
6
|
+
condition: 'BETWEEN',
|
|
7
|
+
values: [firstValue, secondValue]
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function beginsWith(value) {
|
|
11
|
+
return {
|
|
12
|
+
condition: 'BEGINS_WITH',
|
|
13
|
+
values: [value]
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function exists() {
|
|
17
|
+
return {
|
|
18
|
+
condition: 'EXISTS',
|
|
19
|
+
values: [true]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function notExists() {
|
|
23
|
+
return {
|
|
24
|
+
condition: 'NOT_EXISTS',
|
|
25
|
+
values: [true]
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function greaterThan(value) {
|
|
29
|
+
return {
|
|
30
|
+
condition: 'GREATER_THAN',
|
|
31
|
+
values: [value]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function greaterThanEqualTo(value) {
|
|
35
|
+
return {
|
|
36
|
+
condition: 'GREATER_THAN_EQUAL_TO',
|
|
37
|
+
values: [value]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function lessThan(value) {
|
|
41
|
+
return {
|
|
42
|
+
condition: 'LESS_THAN',
|
|
43
|
+
values: [value]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function lessThanEqualTo(value) {
|
|
47
|
+
return {
|
|
48
|
+
condition: 'LESS_THAN_EQUAL_TO',
|
|
49
|
+
values: [value]
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function contains(value) {
|
|
53
|
+
return {
|
|
54
|
+
condition: 'CONTAINS',
|
|
55
|
+
values: [value]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function notContains(value) {
|
|
59
|
+
return {
|
|
60
|
+
condition: 'NOT_CONTAINS',
|
|
61
|
+
values: [value]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function equalTo(value) {
|
|
65
|
+
return {
|
|
66
|
+
condition: 'EQUAL_TO',
|
|
67
|
+
values: [value]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function notEqualTo(value) {
|
|
71
|
+
return {
|
|
72
|
+
condition: 'NOT_EQUAL_TO',
|
|
73
|
+
values: [value]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
exports.WhereConditions = {
|
|
77
|
+
beginsWith,
|
|
78
|
+
between,
|
|
79
|
+
equalTo,
|
|
80
|
+
greaterThan,
|
|
81
|
+
greaterThanEqualTo,
|
|
82
|
+
lessThan,
|
|
83
|
+
lessThanEqualTo
|
|
84
|
+
};
|
|
85
|
+
exports.FilterConditions = {
|
|
86
|
+
beginsWith,
|
|
87
|
+
between,
|
|
88
|
+
contains,
|
|
89
|
+
notContains,
|
|
90
|
+
equalTo,
|
|
91
|
+
notEqualTo,
|
|
92
|
+
exists,
|
|
93
|
+
notExists,
|
|
94
|
+
greaterThan,
|
|
95
|
+
greaterThanEqualTo,
|
|
96
|
+
lessThan,
|
|
97
|
+
lessThanEqualTo
|
|
98
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EntityFilterClauses, AndFilter, EntityQueryBuilder, IndexQueryBuilder, Filter, IndexOptions, OrFilter } from './interfaces/entity-query';
|
|
2
|
+
import { StorageApi } from './storage-api';
|
|
3
|
+
export declare class KvsIndexQueryBuilder<T> implements IndexQueryBuilder<T> {
|
|
4
|
+
private readonly entityName;
|
|
5
|
+
private readonly storageApi;
|
|
6
|
+
constructor(entityName: string, storageApi: StorageApi);
|
|
7
|
+
index(name: string, indexOptions?: IndexOptions<T>): EntityQueryBuilder<T>;
|
|
8
|
+
}
|
|
9
|
+
export declare class FilterBuilder<T> implements Filter<T> {
|
|
10
|
+
and(field: keyof T, condition: EntityFilterClauses): AndFilter<T>;
|
|
11
|
+
or(field: keyof T, condition: EntityFilterClauses): OrFilter<T>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=entity-query.d.ts.map
|
|
@@ -0,0 +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,EAEN,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;AAgED,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,MAAM,CAAC,CAAC,CAAC;IAChD,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,SAAS,CAAC,CAAC,CAAC;IAIjE,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,mBAAmB,GAAG,QAAQ,CAAC,CAAC,CAAC;CAGhE"}
|