@travetto/model 8.0.0-alpha.21 → 8.0.0-alpha.23
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 +22 -23
- package/__index__.ts +6 -9
- package/package.json +7 -7
- package/src/error/exists.ts +1 -1
- package/src/error/invalid-index.ts +5 -3
- package/src/error/invalid-sub-type.ts +1 -1
- package/src/error/not-found.ts +1 -1
- package/src/registry/decorator.ts +25 -19
- package/src/registry/registry-adapter.ts +7 -7
- package/src/registry/registry-index.ts +8 -7
- package/src/registry/types.ts +3 -3
- package/src/types/basic.ts +2 -2
- package/src/types/blob.ts +2 -3
- package/src/types/bulk.ts +4 -8
- package/src/types/crud.ts +2 -4
- package/src/types/expiry.ts +2 -2
- package/src/types/model.ts +2 -2
- package/src/types/storage.ts +1 -2
- package/src/util/blob.ts +2 -2
- package/src/util/bulk.ts +6 -3
- package/src/util/crud.ts +38 -28
- package/src/util/expiry.ts +4 -5
- package/src/util/storage.ts +3 -4
- package/support/base-command.ts +3 -5
- package/support/bin/candidate.ts +8 -11
- package/support/bin/export.ts +2 -2
- package/support/bin/install.ts +2 -2
- package/support/cli.model_export.ts +5 -4
- package/support/cli.model_install.ts +5 -4
- package/support/doc.support.tsx +16 -18
- package/support/test/base.ts +9 -7
- package/support/test/basic.ts +10 -8
- package/support/test/blob.ts +7 -8
- package/support/test/bulk.ts +16 -5
- package/support/test/crud.ts +108 -90
- package/support/test/expiry.ts +39 -21
- package/support/test/polymorphism.ts +39 -31
- package/support/test/suite.ts +15 -12
package/src/util/bulk.ts
CHANGED
|
@@ -13,7 +13,6 @@ export type BulkPreStore<T extends ModelType> = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export class ModelBulkUtil {
|
|
16
|
-
|
|
17
16
|
/**
|
|
18
17
|
* Type guard for determining if service supports bulk operation
|
|
19
18
|
*/
|
|
@@ -25,7 +24,11 @@ export class ModelBulkUtil {
|
|
|
25
24
|
* @param operations
|
|
26
25
|
* @param provider
|
|
27
26
|
*/
|
|
28
|
-
static async preStore<T extends ModelType>(
|
|
27
|
+
static async preStore<T extends ModelType>(
|
|
28
|
+
cls: Class<T>,
|
|
29
|
+
operations: BulkOperation<T>[],
|
|
30
|
+
provider: ModelCrudProvider
|
|
31
|
+
): Promise<BulkPreStore<T>> {
|
|
29
32
|
const insertedIds = new Map<number, string>();
|
|
30
33
|
const upsertedIds = new Map<number, string>();
|
|
31
34
|
const updatedIds = new Map<number, string>();
|
|
@@ -53,4 +56,4 @@ export class ModelBulkUtil {
|
|
|
53
56
|
}
|
|
54
57
|
return { insertedIds, upsertedIds, updatedIds, existingUpsertedIds, operations };
|
|
55
58
|
}
|
|
56
|
-
}
|
|
59
|
+
}
|
package/src/util/crud.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type BinaryArray, BinaryUtil, type Class, castKey, castTo, hasFunction, JSONUtil, RuntimeError, Util } from '@travetto/runtime';
|
|
2
2
|
import { DataUtil, SchemaRegistryIndex, SchemaValidator, type ValidationError, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import { ModelRegistryIndex } from '../registry/registry-index.ts';
|
|
5
|
-
import type { ModelIdSource, ModelType, OptionalId } from '../types/model.ts';
|
|
6
|
-
import { NotFoundError } from '../error/not-found.ts';
|
|
7
4
|
import { ExistsError } from '../error/exists.ts';
|
|
8
5
|
import { SubTypeNotSupportedError } from '../error/invalid-sub-type.ts';
|
|
6
|
+
import { NotFoundError } from '../error/not-found.ts';
|
|
7
|
+
import { ModelRegistryIndex } from '../registry/registry-index.ts';
|
|
9
8
|
import type { DataHandler, PrePersistScope } from '../registry/types.ts';
|
|
10
9
|
import type { ModelCrudSupport } from '../types/crud.ts';
|
|
10
|
+
import type { ModelIdSource, ModelType, OptionalId } from '../types/model.ts';
|
|
11
11
|
|
|
12
12
|
type ModelLoadInput = string | BinaryArray | object;
|
|
13
13
|
|
|
@@ -19,7 +19,6 @@ export type ModelCrudProvider = {
|
|
|
19
19
|
* Crud utilities
|
|
20
20
|
*/
|
|
21
21
|
export class ModelCrudUtil {
|
|
22
|
-
|
|
23
22
|
/**
|
|
24
23
|
* Type guard for determining if service supports crud operations
|
|
25
24
|
*/
|
|
@@ -38,15 +37,17 @@ export class ModelCrudUtil {
|
|
|
38
37
|
if (!actions) {
|
|
39
38
|
return [];
|
|
40
39
|
}
|
|
41
|
-
return (await Promise.allSettled(actions))
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
return (await Promise.allSettled(actions))
|
|
41
|
+
.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
|
+
})
|
|
50
|
+
.filter(item => !!item);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
/**
|
|
@@ -54,11 +55,13 @@ export class ModelCrudUtil {
|
|
|
54
55
|
* @param cls Class to load model for
|
|
55
56
|
* @param input Input as string or plain object
|
|
56
57
|
*/
|
|
57
|
-
static async load<T extends ModelType>(
|
|
58
|
+
static async load<T extends ModelType>(
|
|
59
|
+
cls: Class<T>,
|
|
60
|
+
input: ModelLoadInput,
|
|
61
|
+
onTypeMismatch: 'notfound' | 'exists' = 'notfound'
|
|
62
|
+
): Promise<T> {
|
|
58
63
|
const resolvedInput: object =
|
|
59
|
-
typeof input === 'string' ? JSONUtil.fromUTF8(input) :
|
|
60
|
-
BinaryUtil.isBinaryArray(input) ? JSONUtil.fromBinaryArray(input) :
|
|
61
|
-
input;
|
|
64
|
+
typeof input === 'string' ? JSONUtil.fromUTF8(input) : BinaryUtil.isBinaryArray(input) ? JSONUtil.fromBinaryArray(input) : input;
|
|
62
65
|
|
|
63
66
|
const result = SchemaRegistryIndex.getBaseClass(cls).from(resolvedInput);
|
|
64
67
|
|
|
@@ -79,7 +82,12 @@ export class ModelCrudUtil {
|
|
|
79
82
|
* @param cls Type to store for
|
|
80
83
|
* @param item Item to store
|
|
81
84
|
*/
|
|
82
|
-
static async preStore<T extends ModelType>(
|
|
85
|
+
static async preStore<T extends ModelType>(
|
|
86
|
+
cls: Class<T>,
|
|
87
|
+
item: Partial<OptionalId<T>>,
|
|
88
|
+
provider: ModelCrudProvider,
|
|
89
|
+
scope: PrePersistScope = 'all'
|
|
90
|
+
): Promise<T> {
|
|
83
91
|
if (!item.id) {
|
|
84
92
|
item.id = provider.idSource.create();
|
|
85
93
|
}
|
|
@@ -128,7 +136,7 @@ export class ModelCrudUtil {
|
|
|
128
136
|
};
|
|
129
137
|
|
|
130
138
|
const hasTargets = Object.keys(schema.fields).some(key => isCleanTarget(key));
|
|
131
|
-
|
|
139
|
+
|
|
132
140
|
let res = item;
|
|
133
141
|
if (hasTargets) {
|
|
134
142
|
res = { ...item };
|
|
@@ -143,9 +151,9 @@ export class ModelCrudUtil {
|
|
|
143
151
|
const fieldKey = castKey<typeof res>(key);
|
|
144
152
|
if (!isCleanTarget(key) && res[fieldKey] !== undefined && res[fieldKey] !== null) {
|
|
145
153
|
if (field.array && Array.isArray(res[fieldKey])) {
|
|
146
|
-
const arr = res[fieldKey]
|
|
154
|
+
const arr = res[fieldKey];
|
|
147
155
|
let changed = false;
|
|
148
|
-
const
|
|
156
|
+
const newArray = arr.map(subItem => {
|
|
149
157
|
const cleaned = this.cleanTransientFields(field.type, subItem);
|
|
150
158
|
if (cleaned !== subItem) {
|
|
151
159
|
changed = true;
|
|
@@ -156,7 +164,7 @@ export class ModelCrudUtil {
|
|
|
156
164
|
if (res === item) {
|
|
157
165
|
res = { ...item };
|
|
158
166
|
}
|
|
159
|
-
res[fieldKey] =
|
|
167
|
+
res[fieldKey] = castTo(newArray);
|
|
160
168
|
}
|
|
161
169
|
} else {
|
|
162
170
|
const val = res[fieldKey];
|
|
@@ -190,10 +198,10 @@ export class ModelCrudUtil {
|
|
|
190
198
|
*/
|
|
191
199
|
static async prePersist<T>(cls: Class<T>, item: T, scope: PrePersistScope): Promise<T> {
|
|
192
200
|
const config = ModelRegistryIndex.getConfig(cls);
|
|
193
|
-
for (const state of
|
|
201
|
+
for (const state of config.prePersist ?? []) {
|
|
194
202
|
if (state.scope === scope || scope === 'all' || state.scope === 'all') {
|
|
195
203
|
const handler: DataHandler<T> = castTo(state.handler);
|
|
196
|
-
item = await handler(item) ?? item;
|
|
204
|
+
item = (await handler(item)) ?? item;
|
|
197
205
|
}
|
|
198
206
|
}
|
|
199
207
|
return this.cleanTransientFields(cls, item);
|
|
@@ -205,7 +213,7 @@ export class ModelCrudUtil {
|
|
|
205
213
|
static async postLoad<T>(cls: Class<T>, item: T): Promise<T> {
|
|
206
214
|
const config = ModelRegistryIndex.getConfig(cls);
|
|
207
215
|
for (const handler of castTo<DataHandler<T>[]>(config.postLoad ?? [])) {
|
|
208
|
-
item = await handler(item) ?? item;
|
|
216
|
+
item = (await handler(item)) ?? item;
|
|
209
217
|
}
|
|
210
218
|
return item;
|
|
211
219
|
}
|
|
@@ -215,7 +223,9 @@ export class ModelCrudUtil {
|
|
|
215
223
|
*/
|
|
216
224
|
static async prePartialUpdate<T extends ModelType>(cls: Class<T>, item: Partial<T>, view?: string): Promise<Partial<T>> {
|
|
217
225
|
if (!DataUtil.isPlainObject(item)) {
|
|
218
|
-
throw new RuntimeError(`A partial update requires a plain object, not an instance of ${castTo<Function>(item).constructor.name}`, {
|
|
226
|
+
throw new RuntimeError(`A partial update requires a plain object, not an instance of ${castTo<Function>(item).constructor.name}`, {
|
|
227
|
+
category: 'data'
|
|
228
|
+
});
|
|
219
229
|
}
|
|
220
230
|
const keys = Object.keys(item);
|
|
221
231
|
if ((keys.length === 1 && item.id) || keys.length === 0) {
|
|
@@ -237,4 +247,4 @@ export class ModelCrudUtil {
|
|
|
237
247
|
const full = await get();
|
|
238
248
|
return cls.from(castTo({ ...full, ...prepared }));
|
|
239
249
|
}
|
|
240
|
-
}
|
|
250
|
+
}
|
package/src/util/expiry.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Class, castTo, hasFunction, ShutdownManager, type TimeSpan, TimeUtil, Util } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
+
import { ModelRegistryIndex } from '../registry/registry-index.ts';
|
|
3
4
|
import type { ModelExpirySupport } from '../types/expiry.ts';
|
|
4
5
|
import type { ModelType } from '../types/model.ts';
|
|
5
|
-
import { ModelRegistryIndex } from '../registry/registry-index.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Utils for model expiry
|
|
9
9
|
*/
|
|
10
10
|
export class ModelExpiryUtil {
|
|
11
|
-
|
|
12
11
|
/**
|
|
13
12
|
* Type guard for determining if model supports expiry
|
|
14
13
|
*/
|
|
@@ -17,7 +16,7 @@ export class ModelExpiryUtil {
|
|
|
17
16
|
/**
|
|
18
17
|
* Get expiry info for a given item
|
|
19
18
|
*/
|
|
20
|
-
static getExpiryState<T extends ModelType>(cls: Class<T>, item: T): { expiresAt?: Date
|
|
19
|
+
static getExpiryState<T extends ModelType>(cls: Class<T>, item: T): { expiresAt?: Date; expired?: boolean } {
|
|
21
20
|
const expKey = ModelRegistryIndex.getExpiryFieldName(cls);
|
|
22
21
|
const expiresAt: Date = castTo(item[expKey]);
|
|
23
22
|
|
|
@@ -45,4 +44,4 @@ export class ModelExpiryUtil {
|
|
|
45
44
|
})();
|
|
46
45
|
}
|
|
47
46
|
}
|
|
48
|
-
}
|
|
47
|
+
}
|
package/src/util/storage.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { type Class, hasFunction, Runtime } from '@travetto/runtime';
|
|
2
2
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
3
3
|
|
|
4
|
-
import type { ModelStorageSupport } from '../types/storage.ts';
|
|
5
4
|
import { ModelRegistryIndex } from '../registry/registry-index.ts';
|
|
5
|
+
import type { ModelStorageSupport } from '../types/storage.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Model storage util
|
|
9
9
|
*/
|
|
10
10
|
export class ModelStorageUtil {
|
|
11
|
-
|
|
12
11
|
/**
|
|
13
12
|
* Type guard for determining if service supports storage operation
|
|
14
13
|
*/
|
|
@@ -33,7 +32,7 @@ export class ModelStorageUtil {
|
|
|
33
32
|
return false;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
return
|
|
35
|
+
return autoCreate === 'production' || !Runtime.production;
|
|
37
36
|
};
|
|
38
37
|
|
|
39
38
|
// Initialize on startup (test manages)
|
|
@@ -47,4 +46,4 @@ export class ModelStorageUtil {
|
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
|
-
}
|
|
49
|
+
}
|
package/support/base-command.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type CliCommandShape, cliTpl, CliModuleFlag, CliProfilesFlag } from '@travetto/cli';
|
|
1
|
+
import { type CliCommandShape, CliModuleFlag, CliProfilesFlag, cliTpl } from '@travetto/cli';
|
|
3
2
|
import { Registry } from '@travetto/registry';
|
|
3
|
+
import { Env } from '@travetto/runtime';
|
|
4
4
|
import { Schema, type ValidationError } from '@travetto/schema';
|
|
5
5
|
|
|
6
6
|
import type { ModelStorageSupport } from '../src/types/storage.ts';
|
|
7
|
-
|
|
8
7
|
import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -12,7 +11,6 @@ import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
|
12
11
|
*/
|
|
13
12
|
@Schema()
|
|
14
13
|
export abstract class BaseModelCommand implements CliCommandShape {
|
|
15
|
-
|
|
16
14
|
static async validate(operation: keyof ModelStorageSupport, provider: string, models: string[]): Promise<ValidationError | undefined> {
|
|
17
15
|
const candidates = await ModelCandidateUtil.export(operation);
|
|
18
16
|
if (provider && !candidates.providers.includes(provider)) {
|
|
@@ -52,4 +50,4 @@ export abstract class BaseModelCommand implements CliCommandShape {
|
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
abstract main(...args: unknown[]): ReturnType<CliCommandShape['main']>;
|
|
55
|
-
}
|
|
53
|
+
}
|
package/support/bin/candidate.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type
|
|
1
|
+
import { DependencyRegistryIndex, type InjectableCandidate } from '@travetto/di';
|
|
2
|
+
import { type Class, toConcrete } from '@travetto/runtime';
|
|
3
3
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import type { ModelStorageSupport } from '../../src/types/storage.ts';
|
|
6
|
-
import type { ModelType } from '../../src/types/model.ts';
|
|
7
5
|
import { ModelRegistryIndex } from '../../src/registry/registry-index.ts';
|
|
6
|
+
import type { ModelType } from '../../src/types/model.ts';
|
|
7
|
+
import type { ModelStorageSupport } from '../../src/types/storage.ts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Utilities for finding candidates for model operations
|
|
11
11
|
*/
|
|
12
12
|
export class ModelCandidateUtil {
|
|
13
|
-
|
|
14
|
-
static async export(operation: keyof ModelStorageSupport): Promise<{ models: string[], providers: string[] }> {
|
|
13
|
+
static async export(operation: keyof ModelStorageSupport): Promise<{ models: string[]; providers: string[] }> {
|
|
15
14
|
return {
|
|
16
15
|
models: await this.getModelNames(),
|
|
17
16
|
providers: await this.getProviderNames(operation)
|
|
@@ -48,9 +47,7 @@ export class ModelCandidateUtil {
|
|
|
48
47
|
* Get list of names of all viable providers
|
|
49
48
|
*/
|
|
50
49
|
static async getProviderNames(operation?: keyof ModelStorageSupport): Promise<string[]> {
|
|
51
|
-
return (await this.getProviders(operation))
|
|
52
|
-
.map(x => x.class.name.replace(/ModelService/, ''))
|
|
53
|
-
.toSorted();
|
|
50
|
+
return (await this.getProviders(operation)).map(x => x.class.name.replace(/ModelService/, '')).toSorted();
|
|
54
51
|
}
|
|
55
52
|
|
|
56
53
|
/**
|
|
@@ -67,10 +64,10 @@ export class ModelCandidateUtil {
|
|
|
67
64
|
* @param models
|
|
68
65
|
* @returns
|
|
69
66
|
*/
|
|
70
|
-
static async resolve(provider: string, models: string[]): Promise<{ provider: ModelStorageSupport
|
|
67
|
+
static async resolve(provider: string, models: string[]): Promise<{ provider: ModelStorageSupport; models: Class<ModelType>[] }> {
|
|
71
68
|
return {
|
|
72
69
|
provider: await this.getProvider(provider),
|
|
73
70
|
models: await this.#getModels(models)
|
|
74
71
|
};
|
|
75
72
|
}
|
|
76
|
-
}
|
|
73
|
+
}
|
package/support/bin/export.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Class } from '@travetto/runtime';
|
|
2
1
|
import type { ModelStorageSupport, ModelType } from '@travetto/model';
|
|
2
|
+
import type { Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
export class ModelExportUtil {
|
|
5
5
|
static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
|
|
@@ -7,4 +7,4 @@ export class ModelExportUtil {
|
|
|
7
7
|
console.log(await provider.exportModel!(model));
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
-
}
|
|
10
|
+
}
|
package/support/bin/install.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Class } from '@travetto/runtime';
|
|
2
1
|
import type { ModelStorageSupport, ModelType } from '@travetto/model';
|
|
2
|
+
import type { Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
export class ModelInstallUtil {
|
|
5
5
|
static async run(provider: ModelStorageSupport, models: Class<ModelType>[]): Promise<void> {
|
|
@@ -11,4 +11,4 @@ export class ModelInstallUtil {
|
|
|
11
11
|
await provider.upsertModel(cls);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
}
|
|
14
|
+
}
|
|
@@ -2,8 +2,8 @@ import { CliCommand } from '@travetto/cli';
|
|
|
2
2
|
import { MethodValidator } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import { BaseModelCommand } from './base-command.ts';
|
|
5
|
-
import { ModelExportUtil } from './bin/export.ts';
|
|
6
5
|
import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
6
|
+
import { ModelExportUtil } from './bin/export.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Export model definitions for a selected provider and model set.
|
|
@@ -13,12 +13,13 @@ import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
|
13
13
|
*/
|
|
14
14
|
@CliCommand()
|
|
15
15
|
export class ModelExportCommand extends BaseModelCommand {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
getOperation(): 'exportModel' {
|
|
17
|
+
return 'exportModel';
|
|
18
|
+
}
|
|
18
19
|
|
|
19
20
|
@MethodValidator(BaseModelCommand.validate.bind(null, 'exportModel'))
|
|
20
21
|
async main(provider: string, models: string[]): Promise<void> {
|
|
21
22
|
const resolved = await ModelCandidateUtil.resolve(provider, models);
|
|
22
23
|
await ModelExportUtil.run(resolved.provider, resolved.models);
|
|
23
24
|
}
|
|
24
|
-
}
|
|
25
|
+
}
|
|
@@ -2,8 +2,8 @@ import { CliCommand, cliTpl } from '@travetto/cli';
|
|
|
2
2
|
import { MethodValidator } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import { BaseModelCommand } from './base-command.ts';
|
|
5
|
-
import { ModelInstallUtil } from './bin/install.ts';
|
|
6
5
|
import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
6
|
+
import { ModelInstallUtil } from './bin/install.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Install or update model definitions for a selected provider.
|
|
@@ -13,8 +13,9 @@ import { ModelCandidateUtil } from './bin/candidate.ts';
|
|
|
13
13
|
*/
|
|
14
14
|
@CliCommand()
|
|
15
15
|
export class ModelInstallCommand extends BaseModelCommand {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
getOperation(): 'upsertModel' {
|
|
17
|
+
return 'upsertModel';
|
|
18
|
+
}
|
|
18
19
|
|
|
19
20
|
@MethodValidator(BaseModelCommand.validate.bind(null, 'upsertModel'))
|
|
20
21
|
async main(provider: string, models: string[]): Promise<void> {
|
|
@@ -22,4 +23,4 @@ export class ModelInstallCommand extends BaseModelCommand {
|
|
|
22
23
|
await ModelInstallUtil.run(resolved.provider, resolved.models);
|
|
23
24
|
console.log(cliTpl`${{ success: 'Successfully' }} installed ${{ param: models.length.toString() }} model(s)`);
|
|
24
25
|
}
|
|
25
|
-
}
|
|
26
|
+
}
|
package/support/doc.support.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** @jsxImportSource @travetto/doc/support */
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import { Config } from '@travetto/config';
|
|
4
|
-
import {
|
|
4
|
+
import { c, DocFileUtil, type DocJSXElement, type DocJSXElementByFn, d } from '@travetto/doc';
|
|
5
|
+
import { castKey, Runtime, toConcrete } from '@travetto/runtime';
|
|
5
6
|
|
|
6
7
|
import type { ModelBasicSupport } from '../src/types/basic.ts';
|
|
7
8
|
import type { ModelBlobSupport } from '../src/types/blob.ts';
|
|
@@ -17,7 +18,7 @@ export const Links = {
|
|
|
17
18
|
Crud: toLink('CRUD', toConcrete<ModelCrudSupport>()),
|
|
18
19
|
Expiry: toLink('Expiry', toConcrete<ModelExpirySupport>()),
|
|
19
20
|
Bulk: toLink('Bulk', toConcrete<ModelBulkSupport>()),
|
|
20
|
-
Blob: toLink('Blob', toConcrete<ModelBlobSupport>())
|
|
21
|
+
Blob: toLink('Blob', toConcrete<ModelBlobSupport>())
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
export const ModelTypes = (fn: Function): DocJSXElement[] => {
|
|
@@ -27,24 +28,21 @@ export const ModelTypes = (fn: Function): DocJSXElement[] => {
|
|
|
27
28
|
for (const [, key] of content.matchAll(/Model([A-Za-z]+)Support/g)) {
|
|
28
29
|
if (!seen.has(key) && key in Links) {
|
|
29
30
|
seen.add(key);
|
|
30
|
-
|
|
31
|
-
const link = Links[key as keyof typeof Links];
|
|
31
|
+
const link = Links[castKey(key)];
|
|
32
32
|
found.push(link);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
return found.map(type => <li>{type}</li>);
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export const ModelCustomConfig = ({ config }: { config: Function }): DocJSXElement =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
standard {d.module('Config')}resolution paths.
|
|
50
|
-
</>;
|
|
38
|
+
export const ModelCustomConfig = ({ config }: { config: Function }): DocJSXElement => (
|
|
39
|
+
<>
|
|
40
|
+
Out of the box, by installing the module, everything should be wired up by default.If you need to customize any aspect of the source or
|
|
41
|
+
config, you can override and register it with the {d.module('Di')} module.
|
|
42
|
+
<c.Code title="Wiring up a custom Model Source" src="doc/custom-service.ts" />
|
|
43
|
+
where the {config} is defined by:
|
|
44
|
+
<c.Code title={`Structure of ${config.name}`} src={config} startRe={/@Config/} />
|
|
45
|
+
Additionally, you can see that the class is registered with the {Config} annotation, and so these values can be overridden using the
|
|
46
|
+
standard {d.module('Config')}resolution paths.
|
|
47
|
+
</>
|
|
48
|
+
);
|
package/support/test/base.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { DependencyRegistryIndex } from '@travetto/di';
|
|
2
|
-
import {
|
|
2
|
+
import { type Class, castTo, classConstruct, RuntimeError } from '@travetto/runtime';
|
|
3
3
|
|
|
4
|
+
import type { ModelType } from '../../src/types/model.ts';
|
|
4
5
|
import { ModelBulkUtil } from '../../src/util/bulk.ts';
|
|
5
6
|
import { ModelCrudUtil } from '../../src/util/crud.ts';
|
|
6
|
-
import type { ModelType } from '../../src/types/model.ts';
|
|
7
7
|
import { ModelSuite } from './suite.ts';
|
|
8
8
|
|
|
9
|
-
type ServiceClass = { serviceClass: { new(): unknown } };
|
|
9
|
+
type ServiceClass = { serviceClass: { new (): unknown } };
|
|
10
10
|
|
|
11
11
|
@ModelSuite()
|
|
12
12
|
export abstract class BaseModelSuite<T> {
|
|
13
|
-
|
|
14
13
|
static ifNot(pred: (svc: unknown) => boolean): (x: unknown) => Promise<boolean> {
|
|
15
14
|
return async (x: unknown) => !pred(classConstruct(castTo<ServiceClass>(x).serviceClass));
|
|
16
15
|
}
|
|
@@ -19,7 +18,7 @@ export abstract class BaseModelSuite<T> {
|
|
|
19
18
|
configClass: Class;
|
|
20
19
|
|
|
21
20
|
async getSize<U extends ModelType>(cls: Class<U>): Promise<number> {
|
|
22
|
-
const svc =
|
|
21
|
+
const svc = await this.service;
|
|
23
22
|
if (ModelCrudUtil.isSupported(svc)) {
|
|
24
23
|
let i = 0;
|
|
25
24
|
for await (const batch of svc.list(cls)) {
|
|
@@ -34,7 +33,10 @@ export abstract class BaseModelSuite<T> {
|
|
|
34
33
|
async saveAll<M extends ModelType>(cls: Class<M>, items: M[]): Promise<number> {
|
|
35
34
|
const svc = await this.service;
|
|
36
35
|
if (ModelBulkUtil.isSupported(svc)) {
|
|
37
|
-
const result = await svc.processBulk(
|
|
36
|
+
const result = await svc.processBulk(
|
|
37
|
+
cls,
|
|
38
|
+
items.map(x => ({ insert: x }))
|
|
39
|
+
);
|
|
38
40
|
return result.counts.insert;
|
|
39
41
|
} else if (ModelCrudUtil.isSupported(svc)) {
|
|
40
42
|
const out: Promise<M>[] = [];
|
|
@@ -59,4 +61,4 @@ export abstract class BaseModelSuite<T> {
|
|
|
59
61
|
}
|
|
60
62
|
return castTo(out.flat());
|
|
61
63
|
}
|
|
62
|
-
}
|
|
64
|
+
}
|
package/support/test/basic.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
|
+
import { Model, type ModelCrudSupport, ModelCrudUtil, NotFoundError, TransientField } from '@travetto/model';
|
|
3
4
|
import { Suite, Test } from '@travetto/test';
|
|
4
|
-
import { type ModelCrudSupport, Model, NotFoundError, ModelCrudUtil, TransientField } from '@travetto/model';
|
|
5
5
|
|
|
6
6
|
import { BaseModelSuite } from './base.ts';
|
|
7
7
|
|
|
@@ -27,7 +27,6 @@ class Person {
|
|
|
27
27
|
|
|
28
28
|
@Suite()
|
|
29
29
|
export abstract class ModelBasicSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
30
|
-
|
|
31
30
|
@Test('create, read, delete')
|
|
32
31
|
async create() {
|
|
33
32
|
const service = await this.service;
|
|
@@ -76,11 +75,14 @@ export abstract class ModelBasicSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
76
75
|
async testComputed() {
|
|
77
76
|
const service = await this.service;
|
|
78
77
|
const id = service.idSource.create();
|
|
79
|
-
await service.create(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
await service.create(
|
|
79
|
+
ComputedPerson,
|
|
80
|
+
ComputedPerson.from({
|
|
81
|
+
id,
|
|
82
|
+
name: 'Bob',
|
|
83
|
+
ignoredField: 'secret'
|
|
84
|
+
})
|
|
85
|
+
);
|
|
84
86
|
|
|
85
87
|
const retrieved = await service.get(ComputedPerson, id);
|
|
86
88
|
assert(retrieved.nameUpper === 'BOB');
|
|
@@ -98,4 +100,4 @@ export abstract class ModelBasicSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
98
100
|
assert(prepared.nameUpper === undefined);
|
|
99
101
|
assert(prepared.ignoredField === undefined);
|
|
100
102
|
}
|
|
101
|
-
}
|
|
103
|
+
}
|
package/support/test/blob.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
|
-
import { Suite, Test, TestFixtures } from '@travetto/test';
|
|
4
3
|
import { BinaryMetadataUtil, BinaryUtil, Util } from '@travetto/runtime';
|
|
4
|
+
import { Suite, Test, TestFixtures } from '@travetto/test';
|
|
5
5
|
|
|
6
6
|
import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
|
|
7
7
|
|
|
@@ -10,7 +10,6 @@ import { ModelBlobUtil } from '../../src/util/blob.ts';
|
|
|
10
10
|
|
|
11
11
|
@Suite()
|
|
12
12
|
export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
13
|
-
|
|
14
13
|
fixture = new TestFixtures(['@travetto/model']);
|
|
15
14
|
|
|
16
15
|
@Test()
|
|
@@ -90,7 +89,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
90
89
|
const partialMeta = BinaryMetadataUtil.read(partial)!;
|
|
91
90
|
const subContent = await partial.text();
|
|
92
91
|
const range = BinaryMetadataUtil.enforceRange({ start: 10, end: 20 }, partialMeta);
|
|
93
|
-
assert(subContent.length ===
|
|
92
|
+
assert(subContent.length === range.end - range.start + 1);
|
|
94
93
|
|
|
95
94
|
const og = await this.fixture.readUTF8('/text.txt');
|
|
96
95
|
|
|
@@ -100,7 +99,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
100
99
|
const partialUnboundedMeta = BinaryMetadataUtil.read(partialUnbounded)!;
|
|
101
100
|
const subContent2 = await partialUnbounded.text();
|
|
102
101
|
const range2 = BinaryMetadataUtil.enforceRange({ start: 10 }, partialUnboundedMeta);
|
|
103
|
-
assert(subContent2.length ===
|
|
102
|
+
assert(subContent2.length === range2.end - range2.start + 1);
|
|
104
103
|
assert(subContent2.startsWith('klm'));
|
|
105
104
|
assert(subContent2.endsWith('xyz'));
|
|
106
105
|
|
|
@@ -161,14 +160,14 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
const writable = await service.getBlobWriteUrl!('largeFile/one', {
|
|
164
|
-
contentType: 'image/jpeg'
|
|
163
|
+
contentType: 'image/jpeg'
|
|
165
164
|
});
|
|
166
165
|
|
|
167
166
|
assert(writable);
|
|
168
167
|
|
|
169
168
|
const response = await fetch(writable, {
|
|
170
169
|
method: 'PUT',
|
|
171
|
-
body: new File([bytes], 'gary', { type: 'image/jpeg' })
|
|
170
|
+
body: new File([bytes], 'gary', { type: 'image/jpeg' })
|
|
172
171
|
});
|
|
173
172
|
|
|
174
173
|
console.error(await response.text());
|
|
@@ -179,7 +178,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
179
178
|
contentType: 'image/jpeg',
|
|
180
179
|
title: 'orange',
|
|
181
180
|
filename: 'gary',
|
|
182
|
-
size: bytes.byteLength
|
|
181
|
+
size: bytes.byteLength
|
|
183
182
|
});
|
|
184
183
|
|
|
185
184
|
const found = await service.getBlob('largeFile/one');
|
|
@@ -189,4 +188,4 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
189
188
|
assert(foundMeta.title === 'orange');
|
|
190
189
|
assert(foundMeta.filename === 'gary');
|
|
191
190
|
}
|
|
192
|
-
}
|
|
191
|
+
}
|
package/support/test/bulk.ts
CHANGED
|
@@ -14,7 +14,6 @@ class User {
|
|
|
14
14
|
|
|
15
15
|
@Suite()
|
|
16
16
|
export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
|
|
17
|
-
|
|
18
17
|
@Test()
|
|
19
18
|
async bulkInsert() {
|
|
20
19
|
const service = await this.service;
|
|
@@ -48,11 +47,17 @@ export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
|
|
|
48
47
|
const service = await this.service;
|
|
49
48
|
const users = [0, 1, 2, 4].map(x => User.from({ name: `name-${x}`, id: service.idSource.create() }));
|
|
50
49
|
|
|
51
|
-
const result = await service.processBulk(
|
|
50
|
+
const result = await service.processBulk(
|
|
51
|
+
User,
|
|
52
|
+
users.map(u => ({ insert: u }))
|
|
53
|
+
);
|
|
52
54
|
assert(result.counts.insert === 4);
|
|
53
55
|
assert(result.insertedIds.size === 4);
|
|
54
56
|
|
|
55
|
-
const res2 = await service.processBulk(
|
|
57
|
+
const res2 = await service.processBulk(
|
|
58
|
+
User,
|
|
59
|
+
users.map(u => ({ update: u }))
|
|
60
|
+
);
|
|
56
61
|
assert(res2.counts.update === 4);
|
|
57
62
|
assert(res2.insertedIds.size === 0);
|
|
58
63
|
}
|
|
@@ -62,11 +67,17 @@ export abstract class ModelBulkSuite extends BaseModelSuite<ModelBulkSupport> {
|
|
|
62
67
|
const service = await this.service;
|
|
63
68
|
const users = [0, 1, 2, 4].map(x => User.from({ name: `name-${x}`, id: service.idSource.create() }));
|
|
64
69
|
|
|
65
|
-
const result = await service.processBulk(
|
|
70
|
+
const result = await service.processBulk(
|
|
71
|
+
User,
|
|
72
|
+
users.map(u => ({ insert: u }))
|
|
73
|
+
);
|
|
66
74
|
assert(result.counts.insert === 4);
|
|
67
75
|
assert(result.insertedIds.size === 4);
|
|
68
76
|
|
|
69
|
-
const res2 = await service.processBulk(
|
|
77
|
+
const res2 = await service.processBulk(
|
|
78
|
+
User,
|
|
79
|
+
users.map(u => ({ delete: u }))
|
|
80
|
+
);
|
|
70
81
|
assert(res2.counts.delete === 4);
|
|
71
82
|
}
|
|
72
83
|
}
|