@travetto/model 8.0.0-alpha.10 → 8.0.0-alpha.12
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 +14 -7
- package/package.json +7 -7
- package/src/types/crud.ts +8 -3
- package/src/types/model.ts +10 -1
- package/src/util/crud.ts +15 -6
- package/support/test/base.ts +5 -5
- package/support/test/crud.ts +39 -5
- package/support/test/polymorphism.ts +4 -4
package/README.md
CHANGED
|
@@ -110,12 +110,19 @@ export interface ModelCrudSupport extends ModelBasicSupport {
|
|
|
110
110
|
updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T>;
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
|
-
* List all items
|
|
113
|
+
* List all items of a collection, results returned in batches of items.
|
|
114
|
+
*
|
|
115
|
+
* Note: Batch size hint can be used to optimize batch size, but is not guaranteed.
|
|
116
|
+
*
|
|
117
|
+
* @param cls The class to list
|
|
118
|
+
* @param options Options for listing
|
|
114
119
|
*/
|
|
115
|
-
list<T extends ModelType>(cls: Class<T
|
|
120
|
+
list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]>;
|
|
116
121
|
}
|
|
117
122
|
```
|
|
118
123
|
|
|
124
|
+
The `list` operation returns batches of model records as an async stream. It also accepts listing options such as `limit` to cap how many records are produced, alongside other runtime controls such as abort signals and batch size hints.
|
|
125
|
+
|
|
119
126
|
### Expiry
|
|
120
127
|
Certain implementations will also provide support for automatic [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10) of data at runtime. This is extremely useful for temporary data as, and is used in the [Caching](https://github.com/travetto/travetto/tree/main/module/cache#readme "Caching functionality with decorators for declarative use.") module for expiring data accordingly.
|
|
121
128
|
|
|
@@ -262,8 +269,8 @@ export abstract class BaseModelSuite<T> {
|
|
|
262
269
|
const svc = (await this.service);
|
|
263
270
|
if (ModelCrudUtil.isSupported(svc)) {
|
|
264
271
|
let i = 0;
|
|
265
|
-
for await (const
|
|
266
|
-
i +=
|
|
272
|
+
for await (const batch of svc.list(cls)) {
|
|
273
|
+
i += batch.length;
|
|
267
274
|
}
|
|
268
275
|
return i;
|
|
269
276
|
} else {
|
|
@@ -292,12 +299,12 @@ export abstract class BaseModelSuite<T> {
|
|
|
292
299
|
return DependencyRegistryIndex.getInstance(this.serviceClass);
|
|
293
300
|
}
|
|
294
301
|
|
|
295
|
-
async toArray<U>(src: AsyncIterable<U> | AsyncGenerator<U>): Promise<U[]> {
|
|
296
|
-
const out: U[] = [];
|
|
302
|
+
async toArray<U>(src: AsyncIterable<U | U[]> | AsyncGenerator<U | U[]>): Promise<U[]> {
|
|
303
|
+
const out: (U | U[])[] = [];
|
|
297
304
|
for await (const el of src) {
|
|
298
305
|
out.push(el);
|
|
299
306
|
}
|
|
300
|
-
return out;
|
|
307
|
+
return castTo(out.flat());
|
|
301
308
|
}
|
|
302
309
|
}
|
|
303
310
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Datastore abstraction for core operations.",
|
|
6
6
|
"keywords": [
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"directory": "module/model"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/di": "^8.0.0-alpha.
|
|
32
|
-
"@travetto/registry": "^8.0.0-alpha.
|
|
33
|
-
"@travetto/schema": "^8.0.0-alpha.
|
|
30
|
+
"@travetto/config": "^8.0.0-alpha.12",
|
|
31
|
+
"@travetto/di": "^8.0.0-alpha.11",
|
|
32
|
+
"@travetto/registry": "^8.0.0-alpha.11",
|
|
33
|
+
"@travetto/schema": "^8.0.0-alpha.12"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
37
|
-
"@travetto/test": "^8.0.0-alpha.
|
|
36
|
+
"@travetto/cli": "^8.0.0-alpha.17",
|
|
37
|
+
"@travetto/test": "^8.0.0-alpha.11"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
40
40
|
"@travetto/cli": {
|
package/src/types/crud.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import type { ModelType, OptionalId } from '../types/model.ts';
|
|
3
|
+
import type { ModelListOptions, ModelType, OptionalId } from '../types/model.ts';
|
|
4
4
|
|
|
5
5
|
import type { ModelBasicSupport } from './basic.ts';
|
|
6
6
|
|
|
@@ -38,7 +38,12 @@ export interface ModelCrudSupport extends ModelBasicSupport {
|
|
|
38
38
|
updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T>;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* List all items
|
|
41
|
+
* List all items of a collection, results returned in batches of items.
|
|
42
|
+
*
|
|
43
|
+
* Note: Batch size hint can be used to optimize batch size, but is not guaranteed.
|
|
44
|
+
*
|
|
45
|
+
* @param cls The class to list
|
|
46
|
+
* @param options Options for listing
|
|
42
47
|
*/
|
|
43
|
-
list<T extends ModelType>(cls: Class<T
|
|
48
|
+
list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]>;
|
|
44
49
|
}
|
package/src/types/model.ts
CHANGED
|
@@ -16,4 +16,13 @@ export interface ModelType {
|
|
|
16
16
|
id: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export type OptionalId<T extends ModelType> = Omit<T, 'id'> & { id?: string };
|
|
19
|
+
export type OptionalId<T extends ModelType> = Omit<T, 'id'> & { id?: string };
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for listing items
|
|
23
|
+
*/
|
|
24
|
+
export interface ModelListOptions {
|
|
25
|
+
abort?: AbortSignal;
|
|
26
|
+
limit?: number;
|
|
27
|
+
batchSizeHint?: number;
|
|
28
|
+
}
|
package/src/util/crud.ts
CHANGED
|
@@ -34,6 +34,21 @@ export class ModelCrudUtil {
|
|
|
34
34
|
return { create, valid };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
static async filterOutNotFound<T extends ModelType>(actions: Promise<T>[] | undefined): Promise<T[]> {
|
|
38
|
+
if (!actions) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
return (await Promise.allSettled(actions)).map(p => {
|
|
42
|
+
if (p.status === 'fulfilled') {
|
|
43
|
+
return p.value;
|
|
44
|
+
} else if (p.reason instanceof NotFoundError) {
|
|
45
|
+
return undefined!;
|
|
46
|
+
} else {
|
|
47
|
+
throw p.reason;
|
|
48
|
+
}
|
|
49
|
+
}).filter(item => !!item);
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
/**
|
|
38
53
|
* Load model
|
|
39
54
|
* @param cls Class to load model for
|
|
@@ -117,9 +132,6 @@ export class ModelCrudUtil {
|
|
|
117
132
|
item = await handler(item) ?? item;
|
|
118
133
|
}
|
|
119
134
|
}
|
|
120
|
-
if (typeof item === 'object' && item && 'prePersist' in item && typeof item['prePersist'] === 'function') {
|
|
121
|
-
item = await item.prePersist() ?? item;
|
|
122
|
-
}
|
|
123
135
|
return item;
|
|
124
136
|
}
|
|
125
137
|
|
|
@@ -131,9 +143,6 @@ export class ModelCrudUtil {
|
|
|
131
143
|
for (const handler of castTo<DataHandler<T>[]>(config.postLoad ?? [])) {
|
|
132
144
|
item = await handler(item) ?? item;
|
|
133
145
|
}
|
|
134
|
-
if (typeof item === 'object' && item && 'postLoad' in item && typeof item['postLoad'] === 'function') {
|
|
135
|
-
item = await item.postLoad() ?? item;
|
|
136
|
-
}
|
|
137
146
|
return item;
|
|
138
147
|
}
|
|
139
148
|
|
package/support/test/base.ts
CHANGED
|
@@ -22,8 +22,8 @@ export abstract class BaseModelSuite<T> {
|
|
|
22
22
|
const svc = (await this.service);
|
|
23
23
|
if (ModelCrudUtil.isSupported(svc)) {
|
|
24
24
|
let i = 0;
|
|
25
|
-
for await (const
|
|
26
|
-
i +=
|
|
25
|
+
for await (const batch of svc.list(cls)) {
|
|
26
|
+
i += batch.length;
|
|
27
27
|
}
|
|
28
28
|
return i;
|
|
29
29
|
} else {
|
|
@@ -52,11 +52,11 @@ export abstract class BaseModelSuite<T> {
|
|
|
52
52
|
return DependencyRegistryIndex.getInstance(this.serviceClass);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
async toArray<U>(src: AsyncIterable<U> | AsyncGenerator<U>): Promise<U[]> {
|
|
56
|
-
const out: U[] = [];
|
|
55
|
+
async toArray<U>(src: AsyncIterable<U | U[]> | AsyncGenerator<U | U[]>): Promise<U[]> {
|
|
56
|
+
const out: (U | U[])[] = [];
|
|
57
57
|
for await (const el of src) {
|
|
58
58
|
out.push(el);
|
|
59
59
|
}
|
|
60
|
-
return out;
|
|
60
|
+
return castTo(out.flat());
|
|
61
61
|
}
|
|
62
62
|
}
|
package/support/test/crud.ts
CHANGED
|
@@ -3,7 +3,7 @@ import timers from 'node:timers/promises';
|
|
|
3
3
|
|
|
4
4
|
import { Suite, Test } from '@travetto/test';
|
|
5
5
|
import { Schema, Text, Precision, Required, } from '@travetto/schema';
|
|
6
|
-
import { type ModelCrudSupport, Model, NotFoundError, PersistValue } from '@travetto/model';
|
|
6
|
+
import { type ModelCrudSupport, Model, NotFoundError, PersistValue, PrePersist } from '@travetto/model';
|
|
7
7
|
|
|
8
8
|
import { BaseModelSuite } from './base.ts';
|
|
9
9
|
|
|
@@ -42,14 +42,13 @@ class SimpleList {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
@Model()
|
|
45
|
+
@PrePersist((item) => {
|
|
46
|
+
item.name = `${item.name}-suffix`;
|
|
47
|
+
})
|
|
45
48
|
class User2 {
|
|
46
49
|
id: string;
|
|
47
50
|
address?: Address;
|
|
48
51
|
name: string;
|
|
49
|
-
|
|
50
|
-
prePersist() {
|
|
51
|
-
this.name = `${this.name}-suffix`;
|
|
52
|
-
}
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
@Model()
|
|
@@ -77,6 +76,8 @@ class BigIntModel {
|
|
|
77
76
|
@Suite()
|
|
78
77
|
export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
79
78
|
|
|
79
|
+
indexLimitSkew = 0;
|
|
80
|
+
|
|
80
81
|
@Test('save it')
|
|
81
82
|
async save() {
|
|
82
83
|
const service = await this.service;
|
|
@@ -268,6 +269,39 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
268
269
|
assert(found[2].age === people[2].age);
|
|
269
270
|
}
|
|
270
271
|
|
|
272
|
+
@Test('verify list abort signal')
|
|
273
|
+
async listAbortSignal() {
|
|
274
|
+
const service = await this.service;
|
|
275
|
+
|
|
276
|
+
await Promise.all(
|
|
277
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x => service.upsert(Person, Person.from({
|
|
278
|
+
id: service.idSource.create(),
|
|
279
|
+
name: 'Bob',
|
|
280
|
+
age: 20 + x,
|
|
281
|
+
gender: 'm',
|
|
282
|
+
address: {
|
|
283
|
+
street1: 'a',
|
|
284
|
+
...(x === 1 ? { street2: 'b' } : {})
|
|
285
|
+
}
|
|
286
|
+
})))
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const controller = new AbortController();
|
|
290
|
+
const found: Person[] = [];
|
|
291
|
+
|
|
292
|
+
for await (const items of service.list(Person, { abort: controller.signal, batchSizeHint: 1 })) {
|
|
293
|
+
found.push(...items);
|
|
294
|
+
controller.abort();
|
|
295
|
+
await timers.setTimeout(10);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (this.indexLimitSkew) {
|
|
299
|
+
assert(found.length > 0 && found.length < this.indexLimitSkew);
|
|
300
|
+
} else {
|
|
301
|
+
assert(found.length === 1);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
271
305
|
@Test('save it')
|
|
272
306
|
async verifyRaw() {
|
|
273
307
|
const service = await this.service;
|
|
@@ -71,7 +71,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
71
71
|
const fire2 = await service.get(Worker, fire.id);
|
|
72
72
|
assert(fire2 instanceof Firefighter);
|
|
73
73
|
|
|
74
|
-
const all = await
|
|
74
|
+
const all = await this.toArray(service.list(Worker));
|
|
75
75
|
assert(all.length === 3);
|
|
76
76
|
|
|
77
77
|
const doc3 = all.find(x => x instanceof Doctor);
|
|
@@ -89,7 +89,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
89
89
|
assert(eng3.major === 'oranges');
|
|
90
90
|
assert(eng3.name === 'cob');
|
|
91
91
|
|
|
92
|
-
const engineers = await
|
|
92
|
+
const engineers = await this.toArray(service.list(Engineer));
|
|
93
93
|
assert(engineers.length === 1);
|
|
94
94
|
|
|
95
95
|
await service.create(Engineer, Engineer.from({
|
|
@@ -97,10 +97,10 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
97
97
|
name: 'bob2'
|
|
98
98
|
}));
|
|
99
99
|
|
|
100
|
-
const all2 = await
|
|
100
|
+
const all2 = await this.toArray(service.list(Worker));
|
|
101
101
|
assert(all2.length === 4);
|
|
102
102
|
|
|
103
|
-
const engineers2 = await
|
|
103
|
+
const engineers2 = await this.toArray(service.list(Engineer));
|
|
104
104
|
assert(engineers2.length === 2);
|
|
105
105
|
}
|
|
106
106
|
|