@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 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#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#L4)
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#L48)|X|X| |X|X|X|
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#L13)s within the application. This is useful for being able to generate the appropriate files to manually create the data schemas in production.
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#L13)s within the application given the current configuration being targetted. This is useful for being able to prepare the datastore manually.
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.0.0",
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.0.0",
32
- "@travetto/config": "^2.0.0",
33
- "@travetto/registry": "^2.0.0",
34
- "@travetto/schema": "^2.0.0"
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.0.0"
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
- item.type = config.subType;
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
- item.type = config.subType;
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
  }
@@ -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
- if (ModelRegistry.get(cls).subType) {
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
- if (ModelRegistry.get(cls).subType) {
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
  */
@@ -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
- delete SchemaRegistry.get(cls).views[AllViewⲐ].schema.id.required; // Allow ids to be optional
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) || [];
@@ -35,9 +35,9 @@ export class ModelOptions<T extends ModelType = ModelType> {
35
35
  */
36
36
  store?: string;
37
37
  /**
38
- * If a sub type, identifier type
38
+ * If a sub type
39
39
  */
40
- subType?: string;
40
+ subType?: boolean;
41
41
  /**
42
42
  * Is a base type?
43
43
  */
@@ -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
- @BaseModel()
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
- @BaseModel()
43
+ @Model({ baseType: true })
45
44
  @Index({
46
45
  name: 'worker-name',
47
46
  type: 'sorted',