@travetto/model-redis 2.1.3 → 2.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.
- package/README.md +2 -2
- package/package.json +4 -4
- package/src/config.ts +1 -1
- package/src/service.ts +35 -34
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm install @travetto/model-redis
|
|
|
10
10
|
|
|
11
11
|
This module provides an [redis](https://redis.io)-based implementation for the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."). This source allows the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module to read, write and query against [redis](https://redis.io).
|
|
12
12
|
|
|
13
|
-
Supported
|
|
13
|
+
Supported features:
|
|
14
14
|
|
|
15
15
|
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts#L11)
|
|
16
16
|
* [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts#L11)
|
|
@@ -53,7 +53,7 @@ export class RedisModelConfig {
|
|
|
53
53
|
namespace?: string;
|
|
54
54
|
autoCreate?: boolean;
|
|
55
55
|
|
|
56
|
-
postConstruct() {
|
|
56
|
+
postConstruct(): void {
|
|
57
57
|
|
|
58
58
|
}
|
|
59
59
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-redis",
|
|
3
3
|
"displayName": "Redis Model Support",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"description": "Redis backing for the travetto model module.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"typescript",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"directory": "module/model-redis"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/config": "^2.
|
|
30
|
-
"@travetto/model": "^2.
|
|
31
|
-
"redis": "^4.
|
|
29
|
+
"@travetto/config": "^2.2.0",
|
|
30
|
+
"@travetto/model": "^2.2.0",
|
|
31
|
+
"redis": "^4.2.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
package/src/config.ts
CHANGED
package/src/service.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Class, ShutdownManager, Util } from '@travetto/base';
|
|
|
4
4
|
import { DeepPartial } from '@travetto/schema';
|
|
5
5
|
import {
|
|
6
6
|
ModelCrudSupport, ModelExpirySupport, ModelRegistry, ModelType, ModelStorageSupport,
|
|
7
|
-
NotFoundError, ExistsError, ModelIndexedSupport,
|
|
7
|
+
NotFoundError, ExistsError, ModelIndexedSupport, OptionalId
|
|
8
8
|
} from '@travetto/model';
|
|
9
9
|
import { Injectable } from '@travetto/di';
|
|
10
10
|
|
|
@@ -29,7 +29,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
29
29
|
|
|
30
30
|
constructor(public readonly config: RedisModelConfig) { }
|
|
31
31
|
|
|
32
|
-
#resolveKey(cls: Class | string, id?: string, extra?: string) {
|
|
32
|
+
#resolveKey(cls: Class | string, id?: string, extra?: string): string {
|
|
33
33
|
let key = typeof cls === 'string' ? cls : ModelRegistry.getStore(cls);
|
|
34
34
|
if (id) {
|
|
35
35
|
key = `${key}:${id}`;
|
|
@@ -73,7 +73,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
73
73
|
return this.#streamValues('scan', { match: `${this.#resolveKey(prefix)}*` });
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
#removeIndices<T extends ModelType>(cls: Class, item: T, multi: RedisMulti) {
|
|
76
|
+
#removeIndices<T extends ModelType>(cls: Class, item: T, multi: RedisMulti): void {
|
|
77
77
|
for (const idx of ModelRegistry.getIndices(cls, ['sorted', 'unsorted'])) {
|
|
78
78
|
const { key } = ModelIndexedUtil.computeIndexKey(cls, idx, item);
|
|
79
79
|
const fullKey = this.#resolveKey(cls, idx.name, key);
|
|
@@ -84,7 +84,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
#addIndices<T extends ModelType>(cls: Class, item: T, multi: RedisMulti) {
|
|
87
|
+
#addIndices<T extends ModelType>(cls: Class, item: T, multi: RedisMulti): void {
|
|
88
88
|
for (const idx of ModelRegistry.getIndices(cls, ['sorted', 'unsorted'])) {
|
|
89
89
|
const { key, sort } = ModelIndexedUtil.computeIndexKey(cls, idx, item);
|
|
90
90
|
const fullKey = this.#resolveKey(cls, idx.name, key);
|
|
@@ -96,7 +96,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
async #store<T extends ModelType>(cls: Class<T>, item: T, action: 'write' | 'delete') {
|
|
99
|
+
async #store<T extends ModelType>(cls: Class<T>, item: T, action: 'write' | 'delete'): Promise<void> {
|
|
100
100
|
const key = this.#resolveKey(cls, item.id);
|
|
101
101
|
const config = ModelRegistry.get(cls);
|
|
102
102
|
const existing = await this.get(cls, item.id).catch(() => undefined);
|
|
@@ -150,7 +150,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
async #getIdByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) {
|
|
153
|
+
async #getIdByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<string> {
|
|
154
154
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
155
155
|
|
|
156
156
|
const idxCfg = ModelRegistry.getIndex(cls, idx, ['sorted', 'unsorted']);
|
|
@@ -171,7 +171,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
171
171
|
throw new NotFoundError(`${cls.name}: ${idx}`, key);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
async postConstruct() {
|
|
174
|
+
async postConstruct(): Promise<void> {
|
|
175
175
|
this.client = redis.createClient(this.config.client);
|
|
176
176
|
await this.client.connect();
|
|
177
177
|
await ModelStorageUtil.registerModelChangeListener(this);
|
|
@@ -188,11 +188,11 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
uuid() {
|
|
191
|
+
uuid(): string {
|
|
192
192
|
return Util.uuid(32);
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
async has<T extends ModelType>(cls: Class<T>, id: string, error?: 'notfound' | 'data') {
|
|
195
|
+
async has<T extends ModelType>(cls: Class<T>, id: string, error?: 'notfound' | 'data'): Promise<void> {
|
|
196
196
|
const res = await this.client.exists(this.#resolveKey(cls, id));
|
|
197
197
|
if (res === 0 && error === 'notfound') {
|
|
198
198
|
throw new NotFoundError(cls, id);
|
|
@@ -201,7 +201,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
async get<T extends ModelType>(cls: Class<T>, id: string) {
|
|
204
|
+
async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
|
|
205
205
|
const payload = await this.client.get(this.#resolveKey(cls, id));
|
|
206
206
|
if (payload) {
|
|
207
207
|
const item = await ModelCrudUtil.load(cls, payload);
|
|
@@ -212,7 +212,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
212
212
|
throw new NotFoundError(cls, id);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>) {
|
|
215
|
+
async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> {
|
|
216
216
|
if (item.id) {
|
|
217
217
|
await this.has(cls, item.id, 'data');
|
|
218
218
|
}
|
|
@@ -221,30 +221,31 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
221
221
|
return prepped;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
async update<T extends ModelType>(cls: Class<T>, item: T) {
|
|
224
|
+
async update<T extends ModelType>(cls: Class<T>, item: T): Promise<T> {
|
|
225
225
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
226
226
|
await this.has(cls, item.id, 'notfound');
|
|
227
227
|
return this.upsert(cls, item);
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>) {
|
|
230
|
+
async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> {
|
|
231
231
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
232
232
|
const prepped = await ModelCrudUtil.preStore(cls, item, this);
|
|
233
233
|
await this.#store(cls, prepped, 'write');
|
|
234
234
|
return prepped;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string) {
|
|
237
|
+
async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T> {
|
|
238
238
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
239
239
|
const id = item.id;
|
|
240
|
-
|
|
241
|
-
await this.#store(cls,
|
|
242
|
-
return
|
|
240
|
+
const updated = await ModelCrudUtil.naivePartialUpdate(cls, item, view, (): Promise<T> => this.get(cls, id));
|
|
241
|
+
await this.#store(cls, updated, 'write');
|
|
242
|
+
return updated;
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
async delete<T extends ModelType>(cls: Class<T>, id: string) {
|
|
245
|
+
async delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void> {
|
|
246
246
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
247
|
-
|
|
247
|
+
const where: ModelType = { id };
|
|
248
|
+
await this.#store(cls, where, 'delete');
|
|
248
249
|
}
|
|
249
250
|
|
|
250
251
|
async * list<T extends ModelType>(cls: Class<T>): AsyncIterable<T> {
|
|
@@ -255,14 +256,14 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
const bodies = (await this.client.mGet(ids))
|
|
258
|
-
.filter(x => !!x)
|
|
259
|
+
.filter((x): x is string => !!x);
|
|
259
260
|
|
|
260
261
|
for (const body of bodies) {
|
|
261
262
|
try {
|
|
262
263
|
yield await ModelCrudUtil.load(cls, body);
|
|
263
|
-
} catch (
|
|
264
|
-
if (!(
|
|
265
|
-
throw
|
|
264
|
+
} catch (err) {
|
|
265
|
+
if (!(err instanceof NotFoundError)) {
|
|
266
|
+
throw err;
|
|
266
267
|
}
|
|
267
268
|
}
|
|
268
269
|
}
|
|
@@ -270,17 +271,17 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
270
271
|
}
|
|
271
272
|
|
|
272
273
|
// Expiry
|
|
273
|
-
async deleteExpired<T extends ModelType>(cls: Class<T>) {
|
|
274
|
+
async deleteExpired<T extends ModelType>(cls: Class<T>): Promise<number> {
|
|
274
275
|
// Automatic
|
|
275
276
|
return -1;
|
|
276
277
|
}
|
|
277
278
|
|
|
278
279
|
// Storage
|
|
279
|
-
async createStorage() {
|
|
280
|
+
async createStorage(): Promise<void> {
|
|
280
281
|
// Do nothing
|
|
281
282
|
}
|
|
282
283
|
|
|
283
|
-
async deleteStorage() {
|
|
284
|
+
async deleteStorage(): Promise<void> {
|
|
284
285
|
if (!this.config.namespace) {
|
|
285
286
|
await this.client.flushDb();
|
|
286
287
|
} else {
|
|
@@ -292,7 +293,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
292
293
|
}
|
|
293
294
|
}
|
|
294
295
|
|
|
295
|
-
async truncateModel<T extends ModelType>(model: Class<T>) {
|
|
296
|
+
async truncateModel<T extends ModelType>(model: Class<T>): Promise<void> {
|
|
296
297
|
for await (const ids of this.#iterate(model)) {
|
|
297
298
|
if (ids.length) {
|
|
298
299
|
await this.client.del(ids);
|
|
@@ -301,11 +302,11 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
301
302
|
}
|
|
302
303
|
|
|
303
304
|
// Indexed
|
|
304
|
-
async getByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) {
|
|
305
|
+
async getByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<T> {
|
|
305
306
|
return this.get(cls, await this.#getIdByIndex(cls, idx, body));
|
|
306
307
|
}
|
|
307
308
|
|
|
308
|
-
async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) {
|
|
309
|
+
async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void> {
|
|
309
310
|
return this.delete(cls, await this.#getIdByIndex(cls, idx, body));
|
|
310
311
|
}
|
|
311
312
|
|
|
@@ -320,7 +321,7 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
320
321
|
|
|
321
322
|
let stream: AsyncIterable<string[]>;
|
|
322
323
|
|
|
323
|
-
const { key } = ModelIndexedUtil.computeIndexKey(cls, idxCfg
|
|
324
|
+
const { key } = ModelIndexedUtil.computeIndexKey(cls, idxCfg, body, { emptySortValue: null });
|
|
324
325
|
const fullKey = this.#resolveKey(cls, idx, key);
|
|
325
326
|
|
|
326
327
|
if (idxCfg.type === 'unsorted') {
|
|
@@ -337,14 +338,14 @@ export class RedisModelService implements ModelCrudSupport, ModelExpirySupport,
|
|
|
337
338
|
const bodies = (await this.client.mGet(
|
|
338
339
|
ids.map(x => this.#resolveKey(cls, x))
|
|
339
340
|
))
|
|
340
|
-
.filter(x => !!x)
|
|
341
|
+
.filter((x): x is string => !!x);
|
|
341
342
|
|
|
342
343
|
for (const full of bodies) {
|
|
343
344
|
try {
|
|
344
345
|
yield await ModelCrudUtil.load(cls, full);
|
|
345
|
-
} catch (
|
|
346
|
-
if (!(
|
|
347
|
-
throw
|
|
346
|
+
} catch (err) {
|
|
347
|
+
if (!(err instanceof NotFoundError)) {
|
|
348
|
+
throw err;
|
|
348
349
|
}
|
|
349
350
|
}
|
|
350
351
|
}
|