@travetto/model 2.0.0 → 2.1.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 +4 -4
- package/package.json +6 -6
- package/src/internal/service/crud.ts +14 -4
- package/src/provider/file.ts +2 -7
- package/src/registry/decorator.ts +0 -29
- package/src/registry/model.ts +10 -3
- package/src/registry/types.ts +2 -2
- package/test-support/polymorphism.ts +2 -3
package/README.md
CHANGED
|
@@ -196,7 +196,7 @@ export interface ModelBulkSupport extends ModelCrudSupport {
|
|
|
196
196
|
```
|
|
197
197
|
|
|
198
198
|
## Declaration
|
|
199
|
-
Models are declared via the [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#
|
|
199
|
+
Models are declared via the [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L12) 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#L4)
|
|
200
200
|
|
|
201
201
|
**Code: ModelType**
|
|
202
202
|
```typescript
|
|
@@ -236,7 +236,7 @@ All fields are optional, but the `id` and `type` are important as those field ty
|
|
|
236
236
|
|[S3 Model Support](https://github.com/travetto/travetto/tree/main/module/model-s3#readme "S3 backing for the travetto model module.")|X|X| |X|X| |
|
|
237
237
|
|[SQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-sql#readme "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.")|X|X|X|X| |X|
|
|
238
238
|
|[MemoryModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/memory.ts#L50)|X|X|X|X|X|X|
|
|
239
|
-
|[FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#
|
|
239
|
+
|[FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L47)|X|X| |X|X|X|
|
|
240
240
|
|
|
241
241
|
## Custom Model Service
|
|
242
242
|
In addition to the provided contracts, the module also provides common utilities and shared test suites. The common utilities are useful for
|
|
@@ -375,7 +375,7 @@ export class MemoryPolymorphicSuite extends ModelPolymorphismSuite {
|
|
|
375
375
|
|
|
376
376
|
## CLI - model:export
|
|
377
377
|
|
|
378
|
-
The module provides the ability to generate an export of the model structure from all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#
|
|
378
|
+
The module provides the ability to generate an export of the model structure from all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L12)s within the application. This is useful for being able to generate the appropriate files to manually create the data schemas in production.
|
|
379
379
|
|
|
380
380
|
**Terminal: Running model export**
|
|
381
381
|
```bash
|
|
@@ -390,7 +390,7 @@ Options:
|
|
|
390
390
|
|
|
391
391
|
## CLI - model:install
|
|
392
392
|
|
|
393
|
-
The module provides the ability to install all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#
|
|
393
|
+
The module provides the ability to install all the various [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L12)s within the application given the current configuration being targetted. This is useful for being able to prepare the datastore manually.
|
|
394
394
|
|
|
395
395
|
**Terminal: Running model install**
|
|
396
396
|
```bash
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model",
|
|
3
3
|
"displayName": "Data Modeling Support",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "Datastore abstraction for core operations.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"datastore",
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
"directory": "module/model"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@travetto/di": "^2.
|
|
32
|
-
"@travetto/config": "^2.
|
|
33
|
-
"@travetto/registry": "^2.
|
|
34
|
-
"@travetto/schema": "^2.
|
|
31
|
+
"@travetto/di": "^2.1.0",
|
|
32
|
+
"@travetto/config": "^2.1.0",
|
|
33
|
+
"@travetto/registry": "^2.1.0",
|
|
34
|
+
"@travetto/schema": "^2.1.0"
|
|
35
35
|
},
|
|
36
36
|
"optionalPeerDependencies": {
|
|
37
|
-
"@travetto/cli": "^2.
|
|
37
|
+
"@travetto/cli": "^2.1.0"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as crypto from 'crypto';
|
|
2
2
|
|
|
3
3
|
import { Class, Util } from '@travetto/base';
|
|
4
|
-
import { SchemaValidator } from '@travetto/schema';
|
|
4
|
+
import { SchemaRegistry, SchemaValidator } from '@travetto/schema';
|
|
5
5
|
|
|
6
6
|
import { ModelRegistry } from '../../registry/model';
|
|
7
7
|
import { ModelType, OptionalId } from '../../types/model';
|
|
8
8
|
import { NotFoundError } from '../../error/not-found';
|
|
9
9
|
import { ExistsError } from '../../error/exists';
|
|
10
|
+
import { SubTypeNotSupportedError } from '../../error/invalid-sub-type';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Crud utilities
|
|
@@ -39,7 +40,7 @@ export class ModelCrudUtil {
|
|
|
39
40
|
|
|
40
41
|
const result = ModelRegistry.getBaseModel(cls).from(input as object) as T;
|
|
41
42
|
|
|
42
|
-
if (!(result instanceof cls)) {
|
|
43
|
+
if (!(result instanceof cls || result.constructor.ᚕid === cls.ᚕid)) {
|
|
43
44
|
if (onTypeMismatch === 'notfound') {
|
|
44
45
|
throw new NotFoundError(cls, result.id);
|
|
45
46
|
} else {
|
|
@@ -70,7 +71,7 @@ export class ModelCrudUtil {
|
|
|
70
71
|
|
|
71
72
|
const config = ModelRegistry.get(item.constructor as Class<T>);
|
|
72
73
|
if (config.subType) { // Subtyping, assign type
|
|
73
|
-
|
|
74
|
+
SchemaRegistry.ensureInstanceTypeField(cls, item);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
await SchemaValidator.validate(cls, item);
|
|
@@ -96,7 +97,7 @@ export class ModelCrudUtil {
|
|
|
96
97
|
|
|
97
98
|
const config = ModelRegistry.get(item.constructor as Class<T>);
|
|
98
99
|
if (config.subType) { // Subtyping, assign type
|
|
99
|
-
|
|
100
|
+
SchemaRegistry.ensureInstanceTypeField(cls, item);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
if (view) {
|
|
@@ -113,4 +114,13 @@ export class ModelCrudUtil {
|
|
|
113
114
|
|
|
114
115
|
return item as T;
|
|
115
116
|
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Ensure subtype is not supported
|
|
120
|
+
*/
|
|
121
|
+
static ensureNotSubType(cls: Class) {
|
|
122
|
+
if (ModelRegistry.get(cls).subType) {
|
|
123
|
+
throw new SubTypeNotSupportedError(cls);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
116
126
|
}
|
package/src/provider/file.ts
CHANGED
|
@@ -18,7 +18,6 @@ import { ModelCrudUtil } from '../internal/service/crud';
|
|
|
18
18
|
import { ModelExpiryUtil } from '../internal/service/expiry';
|
|
19
19
|
import { NotFoundError } from '../error/not-found';
|
|
20
20
|
import { ExistsError } from '../error/exists';
|
|
21
|
-
import { SubTypeNotSupportedError } from '../error/invalid-sub-type';
|
|
22
21
|
import { StreamModel, STREAMS } from '../internal/service/stream';
|
|
23
22
|
|
|
24
23
|
type Suffix = '.bin' | '.meta' | '.json' | '.expires';
|
|
@@ -142,9 +141,7 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
|
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>) {
|
|
145
|
-
|
|
146
|
-
throw new SubTypeNotSupportedError(cls);
|
|
147
|
-
}
|
|
144
|
+
ModelCrudUtil.ensureNotSubType(cls);
|
|
148
145
|
const prepped = await ModelCrudUtil.preStore(cls, item, this);
|
|
149
146
|
|
|
150
147
|
const file = await this.#resolveName(cls, '.json', item.id);
|
|
@@ -154,9 +151,7 @@ export class FileModelService implements ModelCrudSupport, ModelStreamSupport, M
|
|
|
154
151
|
}
|
|
155
152
|
|
|
156
153
|
async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string) {
|
|
157
|
-
|
|
158
|
-
throw new SubTypeNotSupportedError(cls);
|
|
159
|
-
}
|
|
154
|
+
ModelCrudUtil.ensureNotSubType(cls);
|
|
160
155
|
const id = item.id;
|
|
161
156
|
item = await ModelCrudUtil.naivePartialUpdate(cls, item, view, () => this.get(cls, id));
|
|
162
157
|
const file = await this.#resolveName(cls, '.json', item.id);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Class } from '@travetto/base';
|
|
2
|
-
import { SchemaRegistry } from '@travetto/schema';
|
|
3
2
|
|
|
4
3
|
import { ModelType } from '../types/model';
|
|
5
4
|
import { ModelRegistry } from './model';
|
|
@@ -15,39 +14,11 @@ export function Model(conf: Partial<ModelOptions<ModelType>> | string = {}) {
|
|
|
15
14
|
if (typeof conf === 'string') {
|
|
16
15
|
conf = { store: conf };
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
// Force registry first, and update with extra information after computing
|
|
20
|
-
ModelRegistry.register(target, conf);
|
|
21
|
-
|
|
22
|
-
const baseModel = ModelRegistry.getBaseModel(target);
|
|
23
|
-
if (baseModel !== target) { // Subtyping if base isn't self
|
|
24
|
-
if (conf.subType) {
|
|
25
|
-
SchemaRegistry.registerSubTypes(target, conf.subType);
|
|
26
|
-
} else {
|
|
27
|
-
conf.subType = SchemaRegistry.getSubTypeName(target);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
17
|
ModelRegistry.register(target, conf);
|
|
31
18
|
return target;
|
|
32
19
|
};
|
|
33
20
|
}
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Base Model decorator, extends `@Schema`
|
|
38
|
-
*
|
|
39
|
-
* @augments `@trv:schema/Schema`
|
|
40
|
-
*/
|
|
41
|
-
export function BaseModel(conf: Partial<ModelOptions<ModelType>> | string = {}) {
|
|
42
|
-
return function <T extends ModelType & { type: string }, U extends Class<T>>(target: U): U {
|
|
43
|
-
ModelRegistry.register(target, {
|
|
44
|
-
baseType: true,
|
|
45
|
-
...(typeof conf === 'string' ? { store: conf } : conf)
|
|
46
|
-
});
|
|
47
|
-
return target;
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
22
|
/**
|
|
52
23
|
* Defines an index on a model
|
|
53
24
|
*/
|
package/src/registry/model.ts
CHANGED
|
@@ -51,12 +51,20 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
createPending(cls: Class): Partial<ModelOptions<ModelType>> {
|
|
54
|
-
return { class: cls, indices: [], autoCreate: true };
|
|
54
|
+
return { class: cls, indices: [], autoCreate: true, baseType: cls.ᚕabstract };
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
onInstallFinalize(cls: Class) {
|
|
58
58
|
const config = this.pending.get(cls.ᚕid)! as ModelOptions<ModelType>;
|
|
59
|
-
|
|
59
|
+
|
|
60
|
+
const schema = SchemaRegistry.get(cls);
|
|
61
|
+
const view = schema.views[AllViewⲐ].schema;
|
|
62
|
+
delete view.id.required; // Allow ids to be optional
|
|
63
|
+
|
|
64
|
+
if ('type' in view && this.getBaseModel(cls) !== cls) {
|
|
65
|
+
config.subType = schema.subType; // Copy from schema
|
|
66
|
+
delete view.type.required; // Allow type to be optional
|
|
67
|
+
}
|
|
60
68
|
return config;
|
|
61
69
|
}
|
|
62
70
|
|
|
@@ -128,7 +136,6 @@ class $ModelRegistry extends MetadataRegistry<ModelOptions<ModelType>> {
|
|
|
128
136
|
return this.getStore(this.getBaseModel(cls));
|
|
129
137
|
}
|
|
130
138
|
|
|
131
|
-
|
|
132
139
|
const name = config.store ?? cls.name.toLowerCase();
|
|
133
140
|
|
|
134
141
|
const candidates = this.getInitialNameMapping().get(name) || [];
|
package/src/registry/types.ts
CHANGED
|
@@ -10,9 +10,8 @@ import {
|
|
|
10
10
|
} from '..';
|
|
11
11
|
import { isIndexedSupported } from '../src/internal/service/common';
|
|
12
12
|
import { ExistsError } from '../src/error/exists';
|
|
13
|
-
import { BaseModel } from '../src/registry/decorator';
|
|
14
13
|
|
|
15
|
-
@
|
|
14
|
+
@Model({ baseType: true })
|
|
16
15
|
export class Worker {
|
|
17
16
|
id: string;
|
|
18
17
|
type: string;
|
|
@@ -41,7 +40,7 @@ export class Engineer extends Worker {
|
|
|
41
40
|
major: string;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
@
|
|
43
|
+
@Model({ baseType: true })
|
|
45
44
|
@Index({
|
|
46
45
|
name: 'worker-name',
|
|
47
46
|
type: 'sorted',
|