@travetto/model 6.0.0-rc.0 → 6.0.0-rc.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/README.md +23 -21
- package/__index__.ts +23 -15
- package/package.json +7 -7
- package/src/error/invalid-index.ts +2 -2
- package/src/registry/decorator.ts +3 -3
- package/src/registry/model.ts +5 -6
- package/src/registry/types.ts +1 -1
- package/src/{service → types}/basic.ts +8 -3
- package/src/{service → types}/blob.ts +1 -1
- package/src/{service → types}/bulk.ts +3 -3
- package/src/{service → types}/crud.ts +3 -8
- package/src/{service → types}/expiry.ts +3 -4
- package/src/{service → types}/indexed.ts +3 -4
- package/src/types/model.ts +1 -0
- package/src/{service → types}/storage.ts +6 -2
- package/src/{internal/service → util}/blob.ts +8 -6
- package/src/{internal/service → util}/bulk.ts +10 -4
- package/src/{internal/service → util}/crud.ts +13 -7
- package/src/{internal/service → util}/expiry.ts +9 -4
- package/src/{internal/service → util}/indexed.ts +13 -8
- package/src/{internal/service → util}/storage.ts +10 -4
- package/support/base-command.ts +2 -2
- package/support/bin/candidate.ts +7 -8
- package/support/bin/export.ts +1 -2
- package/support/bin/install.ts +1 -2
- package/support/cli.model_export.ts +3 -3
- package/support/cli.model_install.ts +3 -3
- package/support/doc.support.tsx +23 -11
- package/support/test/base.ts +9 -8
- package/support/test/basic.ts +1 -1
- package/support/test/blob.ts +7 -6
- package/support/test/bulk.ts +15 -15
- package/support/test/crud.ts +9 -9
- package/support/test/expiry.ts +19 -19
- package/support/test/indexed.ts +5 -5
- package/support/test/polymorphism.ts +9 -9
- package/support/test/suite.ts +9 -8
- package/src/internal/service/common.ts +0 -51
package/README.md
CHANGED
|
@@ -16,14 +16,20 @@ yarn add @travetto/model
|
|
|
16
16
|
This module provides a set of contracts/interfaces to data model persistence, modification and retrieval. This module builds heavily upon the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding."), which is used for data model validation.
|
|
17
17
|
|
|
18
18
|
## Contracts
|
|
19
|
-
The module is mainly composed of contracts. The contracts define the expected interface for various model patterns. The primary contracts are [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/
|
|
19
|
+
The module is mainly composed of contracts. The contracts define the expected interface for various model patterns. The primary contracts are [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/types/basic.ts#L8), [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L11), [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/types/indexed.ts#L11), [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10), [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/types/blob.ts#L8) and [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/types/bulk.ts#L64).
|
|
20
20
|
|
|
21
21
|
### Basic
|
|
22
|
-
All [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations, must honor the [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/
|
|
22
|
+
All [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations, must honor the [Basic](https://github.com/travetto/travetto/tree/main/module/model/src/types/basic.ts#L8) contract to be able to participate in the model ecosystem. This contract represents the bare minimum for a model service.
|
|
23
23
|
|
|
24
24
|
**Code: Basic Contract**
|
|
25
25
|
```typescript
|
|
26
26
|
export interface ModelBasicSupport<C = unknown> {
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Id Source
|
|
30
|
+
*/
|
|
31
|
+
idSource: ModelIdSource;
|
|
32
|
+
|
|
27
33
|
/**
|
|
28
34
|
* Get underlying client
|
|
29
35
|
*/
|
|
@@ -53,17 +59,12 @@ export interface ModelBasicSupport<C = unknown> {
|
|
|
53
59
|
```
|
|
54
60
|
|
|
55
61
|
### CRUD
|
|
56
|
-
The [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/
|
|
62
|
+
The [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L11) contract, builds upon the basic contract, and is built around the idea of simple data retrieval and storage, to create a foundation for other services that need only basic support. The model extension in [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework"), is an example of a module that only needs create, read and delete, and so any implementation of [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") that honors this contract, can be used with the [Authentication](https://github.com/travetto/travetto/tree/main/module/auth#readme "Authentication scaffolding for the Travetto framework") model extension.
|
|
57
63
|
|
|
58
64
|
**Code: Crud Contract**
|
|
59
65
|
```typescript
|
|
60
66
|
export interface ModelCrudSupport extends ModelBasicSupport {
|
|
61
67
|
|
|
62
|
-
/**
|
|
63
|
-
* Id Source
|
|
64
|
-
*/
|
|
65
|
-
idSource: ModelIdSource;
|
|
66
|
-
|
|
67
68
|
/**
|
|
68
69
|
* Update an item
|
|
69
70
|
* @param item The document to update.
|
|
@@ -99,7 +100,7 @@ export interface ModelCrudSupport extends ModelBasicSupport {
|
|
|
99
100
|
```
|
|
100
101
|
|
|
101
102
|
### Indexed
|
|
102
|
-
Additionally, an implementation may support the ability for basic [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/
|
|
103
|
+
Additionally, an implementation may support the ability for basic [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/types/indexed.ts#L11) queries. This is not the full featured query support of [Data Model Querying](https://github.com/travetto/travetto/tree/main/module/model-query#readme "Datastore abstraction for advanced query support."), but allowing for indexed lookups. This does not support listing by index, but may be added at a later date.
|
|
103
104
|
|
|
104
105
|
**Code: Indexed Contract**
|
|
105
106
|
```typescript
|
|
@@ -139,7 +140,7 @@ export interface ModelIndexedSupport extends ModelBasicSupport {
|
|
|
139
140
|
```
|
|
140
141
|
|
|
141
142
|
### Expiry
|
|
142
|
-
Certain implementations will also provide support for automatic [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/
|
|
143
|
+
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.
|
|
143
144
|
|
|
144
145
|
**Code: Expiry Contract**
|
|
145
146
|
```typescript
|
|
@@ -154,7 +155,7 @@ export interface ModelExpirySupport extends ModelCrudSupport {
|
|
|
154
155
|
```
|
|
155
156
|
|
|
156
157
|
### Blob
|
|
157
|
-
Some implementations also allow for the ability to read/write binary data as [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/
|
|
158
|
+
Some implementations also allow for the ability to read/write binary data as [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/types/blob.ts#L8). Given that all implementations can store [Base64](https://en.wikipedia.org/wiki/Base64) encoded data, the key differentiator here, is native support for streaming data, as well as being able to store binary data of significant sizes.
|
|
158
159
|
|
|
159
160
|
**Code: Blob Contract**
|
|
160
161
|
```typescript
|
|
@@ -213,7 +214,7 @@ export interface ModelBlobSupport {
|
|
|
213
214
|
```
|
|
214
215
|
|
|
215
216
|
### Bulk
|
|
216
|
-
Finally, there is support for [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/
|
|
217
|
+
Finally, there is support for [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/types/bulk.ts#L64) operations. This is not to simply imply issuing many commands at in parallel, but implementation support for an atomic/bulk operation. This should allow for higher throughput on data ingest, and potentially for atomic support on transactions.
|
|
217
218
|
|
|
218
219
|
**Code: Bulk Contract**
|
|
219
220
|
```typescript
|
|
@@ -223,7 +224,7 @@ export interface ModelBulkSupport extends ModelCrudSupport {
|
|
|
223
224
|
```
|
|
224
225
|
|
|
225
226
|
## Declaration
|
|
226
|
-
Models are declared via the [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L13) decorator, which allows the system to know that this is a class that is compatible with the module. The only requirement for a model is the [ModelType](https://github.com/travetto/travetto/tree/main/module/model/src/types/model.ts#
|
|
227
|
+
Models are declared via the [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L13) decorator, which allows the system to know that this is a class that is compatible with the module. The only requirement for a model is the [ModelType](https://github.com/travetto/travetto/tree/main/module/model/src/types/model.ts#L10)
|
|
227
228
|
|
|
228
229
|
**Code: ModelType**
|
|
229
230
|
```typescript
|
|
@@ -262,9 +263,10 @@ To enforce that these contracts are honored, the module provides shared test sui
|
|
|
262
263
|
import { DependencyRegistry } from '@travetto/di';
|
|
263
264
|
import { AppError, castTo, Class, classConstruct } from '@travetto/runtime';
|
|
264
265
|
|
|
265
|
-
import {
|
|
266
|
-
import {
|
|
267
|
-
import {
|
|
266
|
+
import { ModelBulkUtil } from '../../src/util/bulk.ts';
|
|
267
|
+
import { ModelCrudUtil } from '../../src/util/crud.ts';
|
|
268
|
+
import { ModelType } from '../../src/types/model.ts';
|
|
269
|
+
import { ModelSuite } from './suite.ts';
|
|
268
270
|
|
|
269
271
|
type ServiceClass = { serviceClass: { new(): unknown } };
|
|
270
272
|
|
|
@@ -280,7 +282,7 @@ export abstract class BaseModelSuite<T> {
|
|
|
280
282
|
|
|
281
283
|
async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
|
|
282
284
|
const svc = (await this.service);
|
|
283
|
-
if (
|
|
285
|
+
if (ModelCrudUtil.isSupported(svc)) {
|
|
284
286
|
let i = 0;
|
|
285
287
|
for await (const __el of svc.list(cls)) {
|
|
286
288
|
i += 1;
|
|
@@ -293,10 +295,10 @@ export abstract class BaseModelSuite<T> {
|
|
|
293
295
|
|
|
294
296
|
async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
|
|
295
297
|
const svc = await this.service;
|
|
296
|
-
if (
|
|
297
|
-
const
|
|
298
|
-
return
|
|
299
|
-
} else if (
|
|
298
|
+
if (ModelBulkUtil.isSupported(svc)) {
|
|
299
|
+
const result = await svc.processBulk(cls, items.map(x => ({ insert: x })));
|
|
300
|
+
return result.counts.insert;
|
|
301
|
+
} else if (ModelCrudUtil.isSupported(svc)) {
|
|
300
302
|
const out: Promise<M>[] = [];
|
|
301
303
|
for (const el of items) {
|
|
302
304
|
out.push(svc.create(cls, el));
|
package/__index__.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
export * from './src/registry/decorator';
|
|
2
|
-
export * from './src/registry/model';
|
|
3
|
-
export * from './src/registry/types';
|
|
4
|
-
export * from './src/types/model';
|
|
5
|
-
export * from './src/service/basic';
|
|
6
|
-
export * from './src/service/blob';
|
|
7
|
-
export * from './src/service/bulk';
|
|
8
|
-
export * from './src/service/crud';
|
|
9
|
-
export * from './src/service/indexed';
|
|
10
|
-
export * from './src/service/expiry';
|
|
11
|
-
export * from './src/service/storage';
|
|
1
|
+
export * from './src/registry/decorator.ts';
|
|
2
|
+
export * from './src/registry/model.ts';
|
|
3
|
+
export * from './src/registry/types.ts';
|
|
4
|
+
export * from './src/types/model.ts';
|
|
12
5
|
|
|
13
|
-
export * from './src/
|
|
14
|
-
export * from './src/
|
|
15
|
-
export * from './src/
|
|
16
|
-
export * from './src/
|
|
6
|
+
export * from './src/types/basic.ts';
|
|
7
|
+
export * from './src/types/blob.ts';
|
|
8
|
+
export * from './src/types/bulk.ts';
|
|
9
|
+
export * from './src/types/crud.ts';
|
|
10
|
+
export * from './src/types/indexed.ts';
|
|
11
|
+
export * from './src/types/expiry.ts';
|
|
12
|
+
export * from './src/types/storage.ts';
|
|
13
|
+
|
|
14
|
+
export * from './src/util/blob.ts';
|
|
15
|
+
export * from './src/util/bulk.ts';
|
|
16
|
+
export * from './src/util/crud.ts';
|
|
17
|
+
export * from './src/util/expiry.ts';
|
|
18
|
+
export * from './src/util/indexed.ts';
|
|
19
|
+
export * from './src/util/storage.ts';
|
|
20
|
+
|
|
21
|
+
export * from './src/error/exists.ts';
|
|
22
|
+
export * from './src/error/not-found.ts';
|
|
23
|
+
export * from './src/error/invalid-index.ts';
|
|
24
|
+
export * from './src/error/invalid-sub-type.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.2",
|
|
4
4
|
"description": "Datastore abstraction for core operations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"datastore",
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
"directory": "module/model"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/config": "^6.0.0-rc.
|
|
30
|
-
"@travetto/di": "^6.0.0-rc.
|
|
31
|
-
"@travetto/registry": "^6.0.0-rc.
|
|
32
|
-
"@travetto/schema": "^6.0.0-rc.
|
|
29
|
+
"@travetto/config": "^6.0.0-rc.2",
|
|
30
|
+
"@travetto/di": "^6.0.0-rc.2",
|
|
31
|
+
"@travetto/registry": "^6.0.0-rc.2",
|
|
32
|
+
"@travetto/schema": "^6.0.0-rc.2"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@travetto/cli": "^6.0.0-rc.
|
|
36
|
-
"@travetto/test": "^6.0.0-rc.
|
|
35
|
+
"@travetto/cli": "^6.0.0-rc.2",
|
|
36
|
+
"@travetto/test": "^6.0.0-rc.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependenciesMeta": {
|
|
39
39
|
"@travetto/cli": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Class, AppError } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { IndexConfig } from '../registry/types';
|
|
4
|
-
import { ModelType } from '../types/model';
|
|
3
|
+
import { IndexConfig } from '../registry/types.ts';
|
|
4
|
+
import { ModelType } from '../types/model.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Represents when an index is invalid
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { AppError, asConstructable, castTo, Class } from '@travetto/runtime';
|
|
2
2
|
import { SchemaRegistry } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { ModelType } from '../types/model';
|
|
5
|
-
import { ModelRegistry } from './model';
|
|
6
|
-
import { DataHandler, IndexConfig, ModelOptions, PrePersistScope } from './types';
|
|
4
|
+
import { ModelType } from '../types/model.ts';
|
|
5
|
+
import { ModelRegistry } from './model.ts';
|
|
6
|
+
import { DataHandler, IndexConfig, ModelOptions, PrePersistScope } from './types.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Model decorator, extends `@Schema`
|
package/src/registry/model.ts
CHANGED
|
@@ -2,12 +2,11 @@ import { SchemaRegistry } from '@travetto/schema';
|
|
|
2
2
|
import { MetadataRegistry } from '@travetto/registry';
|
|
3
3
|
import { DependencyRegistry } from '@travetto/di';
|
|
4
4
|
import { AppError, castTo, Class, describeFunction, asFull } from '@travetto/runtime';
|
|
5
|
-
import { AllViewSymbol } from '@travetto/schema/src/internal/types';
|
|
6
5
|
|
|
7
|
-
import { IndexConfig, IndexType, ModelOptions } from './types';
|
|
8
|
-
import { NotFoundError } from '../error/not-found';
|
|
9
|
-
import { ModelType } from '../types/model';
|
|
10
|
-
import { IndexNotSupported } from '../error/invalid-index';
|
|
6
|
+
import { IndexConfig, IndexType, ModelOptions } from './types.ts';
|
|
7
|
+
import { NotFoundError } from '../error/not-found.ts';
|
|
8
|
+
import { ModelType } from '../types/model.ts';
|
|
9
|
+
import { IndexNotSupported } from '../error/invalid-index.ts';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Registry for all models, built on the Metadata registry
|
|
@@ -74,7 +73,7 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
|
|
|
74
73
|
const config = asFull(this.pending.get(cls.Ⲑid)!);
|
|
75
74
|
|
|
76
75
|
const schema = SchemaRegistry.get(cls);
|
|
77
|
-
const view = schema.
|
|
76
|
+
const view = schema.totalView.schema;
|
|
78
77
|
delete view.id.required; // Allow ids to be optional
|
|
79
78
|
|
|
80
79
|
if (schema.subTypeField in view && this.getBaseModel(cls) !== cls) {
|
package/src/registry/types.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { Class } from '@travetto/runtime';
|
|
2
|
-
import { ModelType, OptionalId } from '../types/model';
|
|
2
|
+
import { ModelIdSource, ModelType, OptionalId } from '../types/model.ts';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Interface for basic data interface
|
|
6
|
-
*
|
|
7
|
-
* @concrete ../internal/service/common#ModelBasicSupportTarget
|
|
6
|
+
* @concrete
|
|
8
7
|
*/
|
|
9
8
|
export interface ModelBasicSupport<C = unknown> {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Id Source
|
|
12
|
+
*/
|
|
13
|
+
idSource: ModelIdSource;
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
* Get underlying client
|
|
12
17
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Class, AppError } from '@travetto/runtime';
|
|
2
2
|
import { ValidationError, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { ModelCrudSupport } from './crud';
|
|
5
|
-
import { ModelType, OptionalId } from '../types/model';
|
|
4
|
+
import { ModelCrudSupport } from './crud.ts';
|
|
5
|
+
import { ModelType, OptionalId } from '../types/model.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Bulk operation. Each operation has a single action and payload
|
|
@@ -59,7 +59,7 @@ export class BulkProcessError extends AppError<{ errors: BulkErrorItem[] }> {
|
|
|
59
59
|
/**
|
|
60
60
|
* Determines if model allows for bulk operations
|
|
61
61
|
*
|
|
62
|
-
* @concrete
|
|
62
|
+
* @concrete
|
|
63
63
|
*/
|
|
64
64
|
export interface ModelBulkSupport extends ModelCrudSupport {
|
|
65
65
|
processBulk<T extends ModelType>(cls: Class<T>, operations: BulkOp<T>[]): Promise<BulkResponse>;
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { ModelType, OptionalId
|
|
3
|
+
import { ModelType, OptionalId } from '../types/model.ts';
|
|
4
4
|
|
|
5
|
-
import { ModelBasicSupport } from './basic';
|
|
5
|
+
import { ModelBasicSupport } from './basic.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Interface for simple CRUD
|
|
9
|
-
* @concrete
|
|
9
|
+
* @concrete
|
|
10
10
|
*/
|
|
11
11
|
export interface ModelCrudSupport extends ModelBasicSupport {
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Id Source
|
|
15
|
-
*/
|
|
16
|
-
idSource: ModelIdSource;
|
|
17
|
-
|
|
18
13
|
/**
|
|
19
14
|
* Update an item
|
|
20
15
|
* @param item The document to update.
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { ModelType } from '../types/model';
|
|
4
|
-
import { ModelCrudSupport } from './crud';
|
|
3
|
+
import { ModelType } from '../types/model.ts';
|
|
4
|
+
import { ModelCrudSupport } from './crud.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Support for managing expiration of data
|
|
8
|
-
*
|
|
9
|
-
* @concrete ../internal/service/common#ModelExpirySupportTarget
|
|
8
|
+
* @concrete
|
|
10
9
|
*/
|
|
11
10
|
export interface ModelExpirySupport extends ModelCrudSupport {
|
|
12
11
|
/**
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Class, DeepPartial } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { ModelType, OptionalId } from '../types/model';
|
|
4
|
-
import { ModelBasicSupport } from './basic';
|
|
3
|
+
import { ModelType, OptionalId } from '../types/model.ts';
|
|
4
|
+
import { ModelBasicSupport } from './basic.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Support for simple indexed activity
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* @concrete ../internal/service/common#ModelIndexedSupportTarget
|
|
9
|
+
* @concrete
|
|
11
10
|
*/
|
|
12
11
|
export interface ModelIndexedSupport extends ModelBasicSupport {
|
|
13
12
|
/**
|
package/src/types/model.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Class } from '@travetto/runtime';
|
|
2
2
|
import { SchemaChange } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { ModelType } from '../types/model';
|
|
4
|
+
import { ModelType } from '../types/model.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* This interface defines the behavior for dealing with the
|
|
@@ -10,7 +10,7 @@ import { ModelType } from '../types/model';
|
|
|
10
10
|
*
|
|
11
11
|
* This is intended to be used during development only for rapid prototyping.
|
|
12
12
|
*
|
|
13
|
-
* @concrete
|
|
13
|
+
* @concrete
|
|
14
14
|
*/
|
|
15
15
|
export interface ModelStorageSupport {
|
|
16
16
|
|
|
@@ -53,4 +53,8 @@ export interface ModelStorageSupport {
|
|
|
53
53
|
* An event listener for whenever a model schema is changed
|
|
54
54
|
*/
|
|
55
55
|
changeSchema?(cls: Class, changes: SchemaChange): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Truncate blob storage data
|
|
58
|
+
*/
|
|
59
|
+
truncateBlob?(): Promise<void>;
|
|
56
60
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export const ModelBlobNamespace = '__blobs';
|
|
6
|
-
export const MODEL_BLOB: Class<ModelType> = class { id: string; };
|
|
2
|
+
import { AppError, BinaryInput, BinaryUtil, BlobMeta, ByteRange, hasFunction } from '@travetto/runtime';
|
|
3
|
+
import { ModelBlobSupport } from '../types/blob.ts';
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
|
-
* Utilities for processing
|
|
6
|
+
* Utilities for processing blobs
|
|
10
7
|
*/
|
|
11
8
|
export class ModelBlobUtil {
|
|
12
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Type guard for determining if service supports blob operations
|
|
12
|
+
*/
|
|
13
|
+
static isSupported = hasFunction<ModelBlobSupport>('getBlob');
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* Convert input to a Readable, and get what metadata is available
|
|
15
17
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Class } from '@travetto/runtime';
|
|
1
|
+
import { Class, hasFunction } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { BulkOp } from '
|
|
4
|
-
import { ModelType } from '
|
|
5
|
-
import { ModelCrudProvider, ModelCrudUtil } from './crud';
|
|
3
|
+
import { BulkOp, ModelBulkSupport } from '../types/bulk.ts';
|
|
4
|
+
import { ModelType } from '../types/model.ts';
|
|
5
|
+
import { ModelCrudProvider, ModelCrudUtil } from './crud.ts';
|
|
6
6
|
|
|
7
7
|
export type BulkPreStore<T extends ModelType> = {
|
|
8
8
|
insertedIds: Map<number, string>;
|
|
@@ -13,6 +13,12 @@ export type BulkPreStore<T extends ModelType> = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export class ModelBulkUtil {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Type guard for determining if service supports bulk operation
|
|
19
|
+
*/
|
|
20
|
+
static isSupported = hasFunction<ModelBulkSupport>('processBulk');
|
|
21
|
+
|
|
16
22
|
/**
|
|
17
23
|
* Prepares bulk ops for storage
|
|
18
24
|
* @param cls
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { castTo, Class, Util, asConstructable, AppError } from '@travetto/runtime';
|
|
1
|
+
import { castTo, Class, Util, asConstructable, AppError, hasFunction } from '@travetto/runtime';
|
|
2
2
|
import { DataUtil, SchemaRegistry, SchemaValidator, ValidationError, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { ModelRegistry } from '
|
|
5
|
-
import { ModelIdSource, ModelType, OptionalId } from '
|
|
6
|
-
import { NotFoundError } from '
|
|
7
|
-
import { ExistsError } from '
|
|
8
|
-
import { SubTypeNotSupportedError } from '
|
|
9
|
-
import { DataHandler, PrePersistScope } from '
|
|
4
|
+
import { ModelRegistry } from '../registry/model.ts';
|
|
5
|
+
import { ModelIdSource, ModelType, OptionalId } from '../types/model.ts';
|
|
6
|
+
import { NotFoundError } from '../error/not-found.ts';
|
|
7
|
+
import { ExistsError } from '../error/exists.ts';
|
|
8
|
+
import { SubTypeNotSupportedError } from '../error/invalid-sub-type.ts';
|
|
9
|
+
import { DataHandler, PrePersistScope } from '../registry/types.ts';
|
|
10
|
+
import { ModelCrudSupport } from '../types/crud.ts';
|
|
10
11
|
|
|
11
12
|
export type ModelCrudProvider = {
|
|
12
13
|
idSource: ModelIdSource;
|
|
@@ -17,6 +18,11 @@ export type ModelCrudProvider = {
|
|
|
17
18
|
*/
|
|
18
19
|
export class ModelCrudUtil {
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Type guard for determining if service supports crud operations
|
|
23
|
+
*/
|
|
24
|
+
static isSupported = hasFunction<ModelCrudSupport>('upsert');
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* Build a uuid generator
|
|
22
28
|
*/
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { ShutdownManager, Class, TimeSpan, TimeUtil, Util, castTo } from '@travetto/runtime';
|
|
1
|
+
import { ShutdownManager, Class, TimeSpan, TimeUtil, Util, castTo, hasFunction } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { ModelRegistry } from '
|
|
4
|
-
import { ModelExpirySupport } from '
|
|
5
|
-
import { ModelType } from '
|
|
3
|
+
import { ModelRegistry } from '../registry/model.ts';
|
|
4
|
+
import { ModelExpirySupport } from '../types/expiry.ts';
|
|
5
|
+
import { ModelType } from '../types/model.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Utils for model expiry
|
|
9
9
|
*/
|
|
10
10
|
export class ModelExpiryUtil {
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Type guard for determining if model supports expiry
|
|
14
|
+
*/
|
|
15
|
+
static isSupported = hasFunction<ModelExpirySupport>('deleteExpired');
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* Get expiry info for a given item
|
|
14
19
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { castTo, Class, DeepPartial, TypedObject } from '@travetto/runtime';
|
|
1
|
+
import { castTo, Class, DeepPartial, hasFunction, TypedObject } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { IndexNotSupported } from '
|
|
4
|
-
import { NotFoundError } from '
|
|
5
|
-
import { ModelRegistry } from '
|
|
6
|
-
import { IndexConfig } from '
|
|
7
|
-
import { ModelCrudSupport } from '
|
|
8
|
-
import { ModelIndexedSupport } from '
|
|
9
|
-
import { ModelType, OptionalId } from '
|
|
3
|
+
import { IndexNotSupported } from '../error/invalid-index.ts';
|
|
4
|
+
import { NotFoundError } from '../error/not-found.ts';
|
|
5
|
+
import { ModelRegistry } from '../registry/model.ts';
|
|
6
|
+
import type { IndexConfig } from '../registry/types.ts';
|
|
7
|
+
import type { ModelCrudSupport } from '../types/crud.ts';
|
|
8
|
+
import type { ModelIndexedSupport } from '../types/indexed.ts';
|
|
9
|
+
import type { ModelType, OptionalId } from '../types/model.ts';
|
|
10
10
|
|
|
11
11
|
type ComputeConfig = {
|
|
12
12
|
includeSortInFields?: boolean;
|
|
@@ -24,6 +24,11 @@ const DEFAULT_SEP = '\u8203';
|
|
|
24
24
|
*/
|
|
25
25
|
export class ModelIndexedUtil {
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Type guard for determining if service supports indexed operation
|
|
29
|
+
*/
|
|
30
|
+
static isSupported = hasFunction<ModelIndexedSupport>('getByIndex');
|
|
31
|
+
|
|
27
32
|
/**
|
|
28
33
|
* Compute flattened field to value mappings
|
|
29
34
|
* @param cls Class to get info for
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { Class, Runtime } from '@travetto/runtime';
|
|
1
|
+
import { Class, hasFunction, Runtime } from '@travetto/runtime';
|
|
2
2
|
import { SchemaChangeListener } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { ModelRegistry } from '
|
|
5
|
-
import { ModelStorageSupport } from '
|
|
6
|
-
import { ModelType } from '
|
|
4
|
+
import { ModelRegistry } from '../registry/model.ts';
|
|
5
|
+
import { ModelStorageSupport } from '../types/storage.ts';
|
|
6
|
+
import { ModelType } from '../types/model.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Model storage util
|
|
10
10
|
*/
|
|
11
11
|
export class ModelStorageUtil {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Type guard for determining if service supports storage operation
|
|
15
|
+
*/
|
|
16
|
+
static isSupported = hasFunction<ModelStorageSupport>('createStorage');
|
|
17
|
+
|
|
12
18
|
/**
|
|
13
19
|
* Register change listener on startup
|
|
14
20
|
*/
|
package/support/base-command.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { Env } from '@travetto/runtime';
|
|
|
2
2
|
import { CliValidationError, CliCommandShape, cliTpl } from '@travetto/cli';
|
|
3
3
|
import { RootRegistry } from '@travetto/registry';
|
|
4
4
|
|
|
5
|
-
import type { ModelStorageSupport } from '../src/service/storage';
|
|
5
|
+
import type { ModelStorageSupport } from '../src/service/storage.ts';
|
|
6
6
|
|
|
7
|
-
import { ModelCandidateUtil } from './bin/candidate';
|
|
7
|
+
import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* CLI Entry point for exporting model schemas
|
package/support/bin/candidate.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ModelRegistry } from '@travetto/model/src/registry/model';
|
|
1
|
+
import { toConcrete, Class } from '@travetto/runtime';
|
|
3
2
|
import { InjectableConfig, DependencyRegistry } from '@travetto/di';
|
|
4
|
-
import { ModelStorageSupportTarget } from '@travetto/model/src/internal/service/common';
|
|
5
3
|
|
|
6
|
-
import
|
|
7
|
-
import type {
|
|
4
|
+
import { ModelRegistry } from '../../src/registry/model.ts';
|
|
5
|
+
import type { ModelStorageSupport } from '../../src/types/storage.ts';
|
|
6
|
+
import type { ModelType } from '../../src/types/model.ts';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Utilities for finding candidates for model operations
|
|
@@ -33,14 +32,14 @@ export class ModelCandidateUtil {
|
|
|
33
32
|
* Get model names
|
|
34
33
|
*/
|
|
35
34
|
static async getModelNames(): Promise<string[]> {
|
|
36
|
-
return (await this.#getModels()).map(x => ModelRegistry.getStore(x)).
|
|
35
|
+
return (await this.#getModels()).map(x => ModelRegistry.getStore(x)).toSorted();
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
39
|
* Get all providers that are viable candidates
|
|
41
40
|
*/
|
|
42
41
|
static async getProviders(op?: keyof ModelStorageSupport): Promise<InjectableConfig[]> {
|
|
43
|
-
const types = DependencyRegistry.getCandidateTypes<ModelStorageSupport>(
|
|
42
|
+
const types = DependencyRegistry.getCandidateTypes(toConcrete<ModelStorageSupport>());
|
|
44
43
|
return types.filter(x => !op || x.class.prototype?.[op]);
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -50,7 +49,7 @@ export class ModelCandidateUtil {
|
|
|
50
49
|
static async getProviderNames(op?: keyof ModelStorageSupport): Promise<string[]> {
|
|
51
50
|
return (await this.getProviders(op))
|
|
52
51
|
.map(x => x.class.name.replace(/ModelService/, ''))
|
|
53
|
-
.
|
|
52
|
+
.toSorted();
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
/**
|
package/support/bin/export.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
|
-
import type { ModelStorageSupport } from '@travetto/model
|
|
3
|
-
import type { ModelType } from '@travetto/model/src/types/model';
|
|
2
|
+
import type { ModelStorageSupport, ModelType } from '@travetto/model';
|
|
4
3
|
|
|
5
4
|
export class ModelExportUtil {
|
|
6
5
|
static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
|
package/support/bin/install.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
|
-
import type { ModelStorageSupport } from '@travetto/model
|
|
3
|
-
import type { ModelType } from '@travetto/model/src/types/model';
|
|
2
|
+
import type { ModelStorageSupport, ModelType } from '@travetto/model';
|
|
4
3
|
|
|
5
4
|
export class ModelInstallUtil {
|
|
6
5
|
static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CliCommand } from '@travetto/cli';
|
|
2
2
|
|
|
3
|
-
import { BaseModelCommand } from './base-command';
|
|
4
|
-
import { ModelExportUtil } from './bin/export';
|
|
5
|
-
import { ModelCandidateUtil } from './bin/candidate';
|
|
3
|
+
import { BaseModelCommand } from './base-command.ts';
|
|
4
|
+
import { ModelExportUtil } from './bin/export.ts';
|
|
5
|
+
import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Exports model schemas
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CliCommand, cliTpl } from '@travetto/cli';
|
|
2
2
|
|
|
3
|
-
import { BaseModelCommand } from './base-command';
|
|
4
|
-
import { ModelInstallUtil } from './bin/install';
|
|
5
|
-
import { ModelCandidateUtil } from './bin/candidate';
|
|
3
|
+
import { BaseModelCommand } from './base-command.ts';
|
|
4
|
+
import { ModelInstallUtil } from './bin/install.ts';
|
|
5
|
+
import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Installing models
|
package/support/doc.support.tsx
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
/** @jsxImportSource @travetto/doc */
|
|
2
2
|
import { d, c, DocJSXElementByFn, DocJSXElement, DocFileUtil } from '@travetto/doc';
|
|
3
3
|
import { Config } from '@travetto/config';
|
|
4
|
+
import { Runtime, toConcrete } from '@travetto/runtime';
|
|
5
|
+
|
|
6
|
+
import { ModelBasicSupport } from '../src/types/basic.ts';
|
|
7
|
+
import { ModelBlobSupport } from '../src/types/blob.ts';
|
|
8
|
+
import { ModelBulkSupport } from '../src/types/bulk.ts';
|
|
9
|
+
import { ModelCrudSupport } from '../src/types/crud.ts';
|
|
10
|
+
import { ModelExpirySupport } from '../src/types/expiry.ts';
|
|
11
|
+
import { ModelIndexedSupport } from '../src/types/indexed.ts';
|
|
12
|
+
|
|
13
|
+
const toLink = (title: string, target: Function): DocJSXElementByFn<'CodeLink'> =>
|
|
14
|
+
d.codeLink(title, Runtime.getSourceFile(target), new RegExp(`\\binterface\\s+${target.name}`));
|
|
4
15
|
|
|
5
16
|
export const Links = {
|
|
6
|
-
Basic:
|
|
7
|
-
Crud:
|
|
8
|
-
Expiry:
|
|
9
|
-
Indexed:
|
|
10
|
-
Bulk:
|
|
11
|
-
Blob:
|
|
17
|
+
Basic: toLink('Basic', toConcrete<ModelBasicSupport>()),
|
|
18
|
+
Crud: toLink('CRUD', toConcrete<ModelCrudSupport>()),
|
|
19
|
+
Expiry: toLink('Expiry', toConcrete<ModelExpirySupport>()),
|
|
20
|
+
Indexed: toLink('Indexed', toConcrete<ModelIndexedSupport>()),
|
|
21
|
+
Bulk: toLink('Bulk', toConcrete<ModelBulkSupport>()),
|
|
22
|
+
Blob: toLink('Blob', toConcrete<ModelBlobSupport>()),
|
|
12
23
|
};
|
|
13
24
|
|
|
14
25
|
export const ModelTypes = (fn: | Function): DocJSXElement[] => {
|
|
15
26
|
const { content } = DocFileUtil.readSource(fn);
|
|
16
27
|
const found: DocJSXElementByFn<'CodeLink'>[] = [];
|
|
17
|
-
const seen = new Set();
|
|
18
|
-
for (const [, key] of content.matchAll(/Model(
|
|
19
|
-
if (!seen.has(key)) {
|
|
28
|
+
const seen = new Set<string>();
|
|
29
|
+
for (const [, key] of content.matchAll(/Model([A-Za-z]+)Support/g)) {
|
|
30
|
+
if (!seen.has(key) && key in Links) {
|
|
20
31
|
seen.add(key);
|
|
21
32
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
22
|
-
|
|
33
|
+
const link = Links[key as keyof typeof Links];
|
|
34
|
+
found.push(link);
|
|
23
35
|
}
|
|
24
36
|
}
|
|
25
37
|
return found.map(v => <li>{v}</li>);
|
|
@@ -33,7 +45,7 @@ export const ModelCustomConfig = ({ cfg }: { cfg: Function }): DocJSXElement =>
|
|
|
33
45
|
|
|
34
46
|
where the {cfg} is defined by:
|
|
35
47
|
|
|
36
|
-
<c.Code title={`Structure of ${cfg.name}`} src={cfg} />
|
|
48
|
+
<c.Code title={`Structure of ${cfg.name}`} src={cfg} startRe={/@Config/} />
|
|
37
49
|
|
|
38
50
|
Additionally, you can see that the class is registered with the {Config} annotation, and so these values can be overridden using the
|
|
39
51
|
standard {d.mod('Config')}resolution paths.
|
package/support/test/base.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { DependencyRegistry } from '@travetto/di';
|
|
2
2
|
import { AppError, castTo, Class, classConstruct } from '@travetto/runtime';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { ModelBulkUtil } from '../../src/util/bulk.ts';
|
|
5
|
+
import { ModelCrudUtil } from '../../src/util/crud.ts';
|
|
6
|
+
import { ModelType } from '../../src/types/model.ts';
|
|
7
|
+
import { ModelSuite } from './suite.ts';
|
|
7
8
|
|
|
8
9
|
type ServiceClass = { serviceClass: { new(): unknown } };
|
|
9
10
|
|
|
@@ -19,7 +20,7 @@ export abstract class BaseModelSuite<T> {
|
|
|
19
20
|
|
|
20
21
|
async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
|
|
21
22
|
const svc = (await this.service);
|
|
22
|
-
if (
|
|
23
|
+
if (ModelCrudUtil.isSupported(svc)) {
|
|
23
24
|
let i = 0;
|
|
24
25
|
for await (const __el of svc.list(cls)) {
|
|
25
26
|
i += 1;
|
|
@@ -32,10 +33,10 @@ export abstract class BaseModelSuite<T> {
|
|
|
32
33
|
|
|
33
34
|
async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
|
|
34
35
|
const svc = await this.service;
|
|
35
|
-
if (
|
|
36
|
-
const
|
|
37
|
-
return
|
|
38
|
-
} else if (
|
|
36
|
+
if (ModelBulkUtil.isSupported(svc)) {
|
|
37
|
+
const result = await svc.processBulk(cls, items.map(x => ({ insert: x })));
|
|
38
|
+
return result.counts.insert;
|
|
39
|
+
} else if (ModelCrudUtil.isSupported(svc)) {
|
|
39
40
|
const out: Promise<M>[] = [];
|
|
40
41
|
for (const el of items) {
|
|
41
42
|
out.push(svc.create(cls, el));
|
package/support/test/basic.ts
CHANGED
|
@@ -3,7 +3,7 @@ import assert from 'node:assert';
|
|
|
3
3
|
import { Suite, Test } from '@travetto/test';
|
|
4
4
|
import { ModelCrudSupport, Model, NotFoundError } from '@travetto/model';
|
|
5
5
|
|
|
6
|
-
import { BaseModelSuite } from './base';
|
|
6
|
+
import { BaseModelSuite } from './base.ts';
|
|
7
7
|
|
|
8
8
|
@Model('basic_person')
|
|
9
9
|
class Person {
|
package/support/test/blob.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
3
|
import { Suite, Test, TestFixtures } from '@travetto/test';
|
|
4
|
-
import { BaseModelSuite } from '@travetto/model/support/test/base';
|
|
5
4
|
import { BinaryUtil, Util } from '@travetto/runtime';
|
|
6
5
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
6
|
+
import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
|
|
7
|
+
|
|
8
|
+
import { ModelBlobSupport } from '../../src/types/blob.ts';
|
|
9
|
+
import { ModelBlobUtil } from '../../src/util/blob.ts';
|
|
9
10
|
|
|
10
11
|
const meta = BinaryUtil.getBlobMeta;
|
|
11
12
|
|
|
@@ -168,14 +169,14 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
168
169
|
console.log(writable);
|
|
169
170
|
assert(writable);
|
|
170
171
|
|
|
171
|
-
const
|
|
172
|
+
const response = await fetch(writable, {
|
|
172
173
|
method: 'PUT',
|
|
173
174
|
body: new File([buffer], 'gary', { type: 'image/jpeg' }),
|
|
174
175
|
});
|
|
175
176
|
|
|
176
|
-
console.error(await
|
|
177
|
+
console.error(await response.text());
|
|
177
178
|
|
|
178
|
-
assert(
|
|
179
|
+
assert(response.ok);
|
|
179
180
|
|
|
180
181
|
await service.updateBlobMeta('largeFile/one', {
|
|
181
182
|
contentType: 'image/jpeg',
|
package/support/test/bulk.ts
CHANGED
|
@@ -2,9 +2,9 @@ import assert from 'node:assert';
|
|
|
2
2
|
|
|
3
3
|
import { Suite, Test } from '@travetto/test';
|
|
4
4
|
|
|
5
|
-
import { Model } from '../../src/registry/decorator';
|
|
6
|
-
import { ModelBulkSupport } from '../../src/
|
|
7
|
-
import { BaseModelSuite } from './base';
|
|
5
|
+
import { Model } from '../../src/registry/decorator.ts';
|
|
6
|
+
import { ModelBulkSupport } from '../../src/types/bulk.ts';
|
|
7
|
+
import { BaseModelSuite } from './base.ts';
|
|
8
8
|
|
|
9
9
|
@Model('bulk-user')
|
|
10
10
|
class User {
|
|
@@ -18,29 +18,29 @@ export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
|
|
|
18
18
|
@Test()
|
|
19
19
|
async bulkInsert() {
|
|
20
20
|
const service = await this.service;
|
|
21
|
-
const
|
|
21
|
+
const result = await service.processBulk(User, [
|
|
22
22
|
{ insert: User.from({}) },
|
|
23
23
|
{ insert: User.from({}) },
|
|
24
24
|
{ insert: User.from({}) },
|
|
25
25
|
{ insert: User.from({}) }
|
|
26
26
|
]);
|
|
27
27
|
|
|
28
|
-
assert(
|
|
29
|
-
assert(
|
|
28
|
+
assert(result.counts.insert === 4);
|
|
29
|
+
assert(result.insertedIds.size === 4);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
@Test()
|
|
33
33
|
async bulkUpsert() {
|
|
34
34
|
const service = await this.service;
|
|
35
|
-
const
|
|
35
|
+
const result = await service.processBulk(User, [
|
|
36
36
|
{ upsert: User.from({}) },
|
|
37
37
|
{ upsert: User.from({}) },
|
|
38
38
|
{ upsert: User.from({}) },
|
|
39
39
|
{ upsert: User.from({}) }
|
|
40
40
|
]);
|
|
41
41
|
|
|
42
|
-
assert(
|
|
43
|
-
assert(
|
|
42
|
+
assert(result.counts.upsert === 4);
|
|
43
|
+
assert(result.insertedIds.size === 4);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
@Test()
|
|
@@ -48,9 +48,9 @@ export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
|
|
|
48
48
|
const service = await this.service;
|
|
49
49
|
const users = [0, 1, 2, 4].map(x => User.from({ name: `name-${x}`, id: service.idSource.create() }));
|
|
50
50
|
|
|
51
|
-
const
|
|
52
|
-
assert(
|
|
53
|
-
assert(
|
|
51
|
+
const result = await service.processBulk(User, users.map(u => ({ insert: u })));
|
|
52
|
+
assert(result.counts.insert === 4);
|
|
53
|
+
assert(result.insertedIds.size === 4);
|
|
54
54
|
|
|
55
55
|
const res2 = await service.processBulk(User, users.map(u => ({ update: u })));
|
|
56
56
|
assert(res2.counts.update === 4);
|
|
@@ -62,9 +62,9 @@ export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
|
|
|
62
62
|
const service = await this.service;
|
|
63
63
|
const users = [0, 1, 2, 4].map(x => User.from({ name: `name-${x}`, id: service.idSource.create() }));
|
|
64
64
|
|
|
65
|
-
const
|
|
66
|
-
assert(
|
|
67
|
-
assert(
|
|
65
|
+
const result = await service.processBulk(User, users.map(u => ({ insert: u })));
|
|
66
|
+
assert(result.counts.insert === 4);
|
|
67
|
+
assert(result.insertedIds.size === 4);
|
|
68
68
|
|
|
69
69
|
const res2 = await service.processBulk(User, users.map(u => ({ delete: u })));
|
|
70
70
|
assert(res2.counts.delete === 4);
|
package/support/test/crud.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Suite, Test } from '@travetto/test';
|
|
|
5
5
|
import { Schema, Text, Precision, Required, } from '@travetto/schema';
|
|
6
6
|
import { ModelCrudSupport, Model, NotFoundError, PersistValue } from '@travetto/model';
|
|
7
7
|
|
|
8
|
-
import { BaseModelSuite } from './base';
|
|
8
|
+
import { BaseModelSuite } from './base.ts';
|
|
9
9
|
|
|
10
10
|
@Schema()
|
|
11
11
|
class Address {
|
|
@@ -213,22 +213,22 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
213
213
|
@Test('verify dates')
|
|
214
214
|
async testDates() {
|
|
215
215
|
const service = await this.service;
|
|
216
|
-
const
|
|
216
|
+
const result = await service.create(Dated, Dated.from({ createdDate: new Date() }));
|
|
217
217
|
|
|
218
|
-
assert(
|
|
218
|
+
assert(result.createdDate instanceof Date);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
@Test('verify pre-persist on create/update')
|
|
222
222
|
async testPrePersist() {
|
|
223
223
|
const service = await this.service;
|
|
224
|
-
const
|
|
225
|
-
const created =
|
|
226
|
-
assert(
|
|
227
|
-
assert(
|
|
224
|
+
const result = await service.create(Dated, Dated.from({}));
|
|
225
|
+
const created = result.createdDate;
|
|
226
|
+
assert(result.createdDate instanceof Date);
|
|
227
|
+
assert(result.updatedDate instanceof Date);
|
|
228
228
|
|
|
229
229
|
await timers.setTimeout(100);
|
|
230
230
|
|
|
231
|
-
const final = await service.updatePartial(Dated, { id:
|
|
231
|
+
const final = await service.updatePartial(Dated, { id: result.id, value: 'random' });
|
|
232
232
|
assert(final.createdDate instanceof Date);
|
|
233
233
|
assert(final.createdDate.getTime() === created?.getTime());
|
|
234
234
|
assert(final.updatedDate instanceof Date);
|
|
@@ -254,7 +254,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
254
254
|
people.map(el => service.upsert(Person, el))
|
|
255
255
|
);
|
|
256
256
|
|
|
257
|
-
const found = (await this.toArray(service.list(Person))).
|
|
257
|
+
const found = (await this.toArray(service.list(Person))).toSorted((a, b) => a.age - b.age);
|
|
258
258
|
|
|
259
259
|
assert(found[0].age === people[0].age);
|
|
260
260
|
assert(found[1].age === people[1].age);
|
package/support/test/expiry.ts
CHANGED
|
@@ -4,11 +4,11 @@ import timers from 'node:timers/promises';
|
|
|
4
4
|
import { Suite, Test } from '@travetto/test';
|
|
5
5
|
import { TimeSpan, TimeUnit, TimeUtil } from '@travetto/runtime';
|
|
6
6
|
|
|
7
|
-
import { ExpiresAt, Model } from '../../src/registry/decorator';
|
|
8
|
-
import { ModelExpirySupport } from '../../src/
|
|
9
|
-
import { ModelExpiryUtil } from '../../src/
|
|
10
|
-
import { NotFoundError } from '../../src/error/not-found';
|
|
11
|
-
import { BaseModelSuite } from './base';
|
|
7
|
+
import { ExpiresAt, Model } from '../../src/registry/decorator.ts';
|
|
8
|
+
import { ModelExpirySupport } from '../../src/types/expiry.ts';
|
|
9
|
+
import { ModelExpiryUtil } from '../../src/util/expiry.ts';
|
|
10
|
+
import { NotFoundError } from '../../src/error/not-found.ts';
|
|
11
|
+
import { BaseModelSuite } from './base.ts';
|
|
12
12
|
|
|
13
13
|
@Model('expiry-user')
|
|
14
14
|
export class ExpiryUser {
|
|
@@ -34,63 +34,63 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
34
34
|
@Test()
|
|
35
35
|
async basic() {
|
|
36
36
|
const service = await this.service;
|
|
37
|
-
const
|
|
37
|
+
const result = await service.upsert(ExpiryUser, ExpiryUser.from({
|
|
38
38
|
expiresAt: this.timeFromNow('2s')
|
|
39
39
|
}));
|
|
40
|
-
assert(
|
|
40
|
+
assert(result instanceof ExpiryUser);
|
|
41
41
|
|
|
42
|
-
const expiry = ModelExpiryUtil.getExpiryState(ExpiryUser, await service.get(ExpiryUser,
|
|
42
|
+
const expiry = ModelExpiryUtil.getExpiryState(ExpiryUser, await service.get(ExpiryUser, result.id));
|
|
43
43
|
assert(!expiry.expired);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
@Test()
|
|
47
47
|
async aging() {
|
|
48
48
|
const service = await this.service;
|
|
49
|
-
const
|
|
49
|
+
const result = await service.upsert(ExpiryUser, ExpiryUser.from({
|
|
50
50
|
expiresAt: this.timeFromNow(100)
|
|
51
51
|
}));
|
|
52
52
|
|
|
53
|
-
assert(
|
|
53
|
+
assert(result instanceof ExpiryUser);
|
|
54
54
|
|
|
55
55
|
await this.wait(200);
|
|
56
56
|
|
|
57
|
-
await assert.rejects(() => service.get(ExpiryUser,
|
|
57
|
+
await assert.rejects(() => service.get(ExpiryUser, result.id), NotFoundError);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
@Test()
|
|
61
61
|
async updateExpired() {
|
|
62
62
|
const service = await this.service;
|
|
63
|
-
const
|
|
63
|
+
const result = await service.upsert(ExpiryUser, ExpiryUser.from({
|
|
64
64
|
expiresAt: this.timeFromNow(100)
|
|
65
65
|
}));
|
|
66
66
|
|
|
67
|
-
assert(
|
|
67
|
+
assert(result instanceof ExpiryUser);
|
|
68
68
|
|
|
69
69
|
await this.wait(200);
|
|
70
70
|
|
|
71
|
-
await assert.rejects(() => service.update(ExpiryUser, ExpiryUser.from({ id:
|
|
71
|
+
await assert.rejects(() => service.update(ExpiryUser, ExpiryUser.from({ id: result.id })), NotFoundError);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
@Test()
|
|
75
75
|
async ageWithExtension() {
|
|
76
76
|
const service = await this.service;
|
|
77
|
-
const
|
|
77
|
+
const result = await service.upsert(ExpiryUser, ExpiryUser.from({
|
|
78
78
|
expiresAt: this.timeFromNow('2s')
|
|
79
79
|
}));
|
|
80
|
-
assert(
|
|
80
|
+
assert(result instanceof ExpiryUser);
|
|
81
81
|
|
|
82
82
|
await this.wait(50);
|
|
83
83
|
|
|
84
|
-
assert(!ModelExpiryUtil.getExpiryState(ExpiryUser, (await service.get(ExpiryUser,
|
|
84
|
+
assert(!ModelExpiryUtil.getExpiryState(ExpiryUser, (await service.get(ExpiryUser, result.id))).expired);
|
|
85
85
|
|
|
86
86
|
await service.updatePartial(ExpiryUser, {
|
|
87
|
-
id:
|
|
87
|
+
id: result.id,
|
|
88
88
|
expiresAt: this.timeFromNow(100)
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
await this.wait(200);
|
|
92
92
|
|
|
93
|
-
await assert.rejects(() => service.get(ExpiryUser,
|
|
93
|
+
await assert.rejects(() => service.get(ExpiryUser, result.id), NotFoundError);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
@Test()
|
package/support/test/indexed.ts
CHANGED
|
@@ -4,12 +4,12 @@ import { Suite, Test } from '@travetto/test';
|
|
|
4
4
|
import { Schema } from '@travetto/schema';
|
|
5
5
|
import { TimeUtil } from '@travetto/runtime';
|
|
6
6
|
|
|
7
|
-
import { Index, Model } from '../../src/registry/decorator';
|
|
8
|
-
import { ModelIndexedSupport } from '../../src/
|
|
9
|
-
import { NotFoundError } from '../../src/error/not-found';
|
|
10
|
-
import { IndexNotSupported } from '../../src/error/invalid-index';
|
|
7
|
+
import { Index, Model } from '../../src/registry/decorator.ts';
|
|
8
|
+
import { ModelIndexedSupport } from '../../src/types/indexed.ts';
|
|
9
|
+
import { NotFoundError } from '../../src/error/not-found.ts';
|
|
10
|
+
import { IndexNotSupported } from '../../src/error/invalid-index.ts';
|
|
11
11
|
|
|
12
|
-
import { BaseModelSuite } from './base';
|
|
12
|
+
import { BaseModelSuite } from './base.ts';
|
|
13
13
|
|
|
14
14
|
@Model('index_user')
|
|
15
15
|
@Index({
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
NotFoundError, SubTypeNotSupportedError, PersistValue
|
|
10
10
|
} from '@travetto/model';
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
import { ExistsError } from '../../src/error/exists';
|
|
12
|
+
import { ModelIndexedUtil } from '../../src/util/indexed.ts';
|
|
13
|
+
import { ExistsError } from '../../src/error/exists.ts';
|
|
14
14
|
|
|
15
|
-
import { BaseModelSuite } from './base';
|
|
15
|
+
import { BaseModelSuite } from './base.ts';
|
|
16
16
|
|
|
17
17
|
@Model({ baseType: true })
|
|
18
18
|
export class Worker {
|
|
@@ -171,11 +171,11 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
171
171
|
await timers.setTimeout(15);
|
|
172
172
|
|
|
173
173
|
try {
|
|
174
|
-
const
|
|
174
|
+
const result = await service.upsert(Doctor, Doctor.from({
|
|
175
175
|
id: doc.id, name: 'gob', specialty: 'eyes'
|
|
176
176
|
}));
|
|
177
177
|
|
|
178
|
-
assert(
|
|
178
|
+
assert(result.updatedDate!.getTime() > update.getTime());
|
|
179
179
|
} catch (err) {
|
|
180
180
|
assert(err instanceof SubTypeNotSupportedError);
|
|
181
181
|
}
|
|
@@ -207,7 +207,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
207
207
|
);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
@Test('Polymorphic index', { skip: BaseModelSuite.ifNot(
|
|
210
|
+
@Test('Polymorphic index', { skip: BaseModelSuite.ifNot(ModelIndexedUtil.isSupported) })
|
|
211
211
|
async polymorphicIndexGet() {
|
|
212
212
|
const service: ModelIndexedSupport = castTo(await this.service);
|
|
213
213
|
const now = 30;
|
|
@@ -219,12 +219,12 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
219
219
|
|
|
220
220
|
await this.saveAll(IndexedWorker, [doc, fire, eng]);
|
|
221
221
|
|
|
222
|
-
const
|
|
222
|
+
const result = await service.getByIndex(IndexedWorker, 'worker-name', {
|
|
223
223
|
age: now,
|
|
224
224
|
name: 'rob'
|
|
225
225
|
});
|
|
226
226
|
|
|
227
|
-
assert(
|
|
227
|
+
assert(result instanceof IndexedFirefighter);
|
|
228
228
|
|
|
229
229
|
try {
|
|
230
230
|
const res2 = await service.getByIndex(IndexedFirefighter, 'worker-name', {
|
|
@@ -237,7 +237,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
@Test('Polymorphic index', { skip: BaseModelSuite.ifNot(
|
|
240
|
+
@Test('Polymorphic index', { skip: BaseModelSuite.ifNot(ModelIndexedUtil.isSupported) })
|
|
241
241
|
async polymorphicIndexDelete() {
|
|
242
242
|
const service: ModelIndexedSupport = castTo(await this.service);
|
|
243
243
|
const now = 30;
|
package/support/test/suite.ts
CHANGED
|
@@ -3,9 +3,9 @@ import { DependencyRegistry } from '@travetto/di';
|
|
|
3
3
|
import { RootRegistry } from '@travetto/registry';
|
|
4
4
|
import { SuiteRegistry, TestFixtures } from '@travetto/test';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { ModelRegistry } from '../../src/registry/model';
|
|
6
|
+
import { ModelBlobUtil } from '../../src/util/blob.ts';
|
|
7
|
+
import { ModelStorageUtil } from '../../src/util/storage.ts';
|
|
8
|
+
import { ModelRegistry } from '../../src/registry/model.ts';
|
|
9
9
|
|
|
10
10
|
const Loaded = Symbol();
|
|
11
11
|
|
|
@@ -35,7 +35,7 @@ export function ModelSuite<T extends { configClass: Class<{ autoCreate?: boolean
|
|
|
35
35
|
target,
|
|
36
36
|
async function (this: T) {
|
|
37
37
|
const service = await DependencyRegistry.getInstance(this.serviceClass, qualifier);
|
|
38
|
-
if (
|
|
38
|
+
if (ModelStorageUtil.isSupported(service)) {
|
|
39
39
|
await service.createStorage();
|
|
40
40
|
if (service.createModel) {
|
|
41
41
|
await Promise.all(ModelRegistry.getClasses()
|
|
@@ -50,10 +50,11 @@ export function ModelSuite<T extends { configClass: Class<{ autoCreate?: boolean
|
|
|
50
50
|
target,
|
|
51
51
|
async function (this: T) {
|
|
52
52
|
const service = await DependencyRegistry.getInstance(this.serviceClass, qualifier);
|
|
53
|
-
if (
|
|
53
|
+
if (ModelStorageUtil.isSupported(service)) {
|
|
54
54
|
const models = ModelRegistry.getClasses().filter(m => m === ModelRegistry.getBaseModel(m));
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
|
|
56
|
+
if (ModelBlobUtil.isSupported(service) && service.truncateBlob) {
|
|
57
|
+
await service.truncateBlob();
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
if (service.truncateModel) {
|
|
@@ -71,7 +72,7 @@ export function ModelSuite<T extends { configClass: Class<{ autoCreate?: boolean
|
|
|
71
72
|
target,
|
|
72
73
|
async function (this: T) {
|
|
73
74
|
const service = await DependencyRegistry.getInstance(this.serviceClass, qualifier);
|
|
74
|
-
if (
|
|
75
|
+
if (ModelStorageUtil.isSupported(service)) {
|
|
75
76
|
if (service.deleteModel) {
|
|
76
77
|
for (const m of ModelRegistry.getClasses()) {
|
|
77
78
|
if (m === ModelRegistry.getBaseModel(m)) {
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { hasFunction } from '@travetto/runtime';
|
|
2
|
-
|
|
3
|
-
import type { ModelBulkSupport } from '../../service/bulk';
|
|
4
|
-
import type { ModelCrudSupport } from '../../service/crud';
|
|
5
|
-
import type { ModelExpirySupport } from '../../service/expiry';
|
|
6
|
-
import type { ModelIndexedSupport } from '../../service/indexed';
|
|
7
|
-
import type { ModelStorageSupport } from '../../service/storage';
|
|
8
|
-
import type { ModelBlobSupport } from '../../service/blob';
|
|
9
|
-
|
|
10
|
-
export class ModelBasicSupportTarget { }
|
|
11
|
-
export class ModelCrudSupportTarget { }
|
|
12
|
-
export class ModelBulkSupportTarget { }
|
|
13
|
-
export class ModelStorageSupportTarget { }
|
|
14
|
-
export class ModelBlobSupportTarget { }
|
|
15
|
-
export class ModelExpirySupportTarget { }
|
|
16
|
-
export class ModelIndexedSupportTarget { }
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Type guard for determining if service supports basic operations
|
|
20
|
-
*/
|
|
21
|
-
export const isBasicSupported = hasFunction<ModelBulkSupport>('create');
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Type guard for determining if service supports crud operations
|
|
25
|
-
*/
|
|
26
|
-
export const isCrudSupported = hasFunction<ModelCrudSupport>('upsert');
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Type guard for determining if model supports expiry
|
|
30
|
-
*/
|
|
31
|
-
export const isExpirySupported = hasFunction<ModelExpirySupport>('deleteExpired');
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Type guard for determining if service supports blob operations
|
|
35
|
-
*/
|
|
36
|
-
export const isBlobSupported = hasFunction<ModelBlobSupport>('getBlob');
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Type guard for determining if service supports storage operation
|
|
40
|
-
*/
|
|
41
|
-
export const isStorageSupported = hasFunction<ModelStorageSupport>('createStorage');
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Type guard for determining if service supports bulk operation
|
|
45
|
-
*/
|
|
46
|
-
export const isBulkSupported = hasFunction<ModelBulkSupport>('processBulk');
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Type guard for determining if service supports indexed operation
|
|
50
|
-
*/
|
|
51
|
-
export const isIndexedSupported = hasFunction<ModelIndexedSupport>('getByIndex');
|