@travetto/model 8.0.0-alpha.22 → 8.0.0-alpha.24
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 +72 -67
- package/__index__.ts +7 -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/error/unique.ts +13 -0
- 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 +8 -9
- 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/types/bulk.ts
CHANGED
|
@@ -7,11 +7,7 @@ import type { ModelType, OptionalId } from './model.ts';
|
|
|
7
7
|
/**
|
|
8
8
|
* Bulk operation. Each operation has a single action and payload
|
|
9
9
|
*/
|
|
10
|
-
export type BulkOperation<T extends ModelType> =
|
|
11
|
-
{ delete?: T } &
|
|
12
|
-
{ insert?: OptionalId<T> } &
|
|
13
|
-
{ update?: T } &
|
|
14
|
-
{ upsert?: OptionalId<T> };
|
|
10
|
+
export type BulkOperation<T extends ModelType> = { delete?: T } & { insert?: OptionalId<T> } & { update?: T } & { upsert?: OptionalId<T> };
|
|
15
11
|
|
|
16
12
|
/**
|
|
17
13
|
* Bulk response provides a summary of all the operations
|
|
@@ -37,13 +33,13 @@ export interface BulkResponse<E = unknown> {
|
|
|
37
33
|
};
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
type BulkErrorItem = { message: string
|
|
36
|
+
type BulkErrorItem = { message: string; type: string; errors?: ValidationError[]; idx: number };
|
|
41
37
|
|
|
42
38
|
/**
|
|
43
39
|
* Bulk processing error
|
|
44
40
|
*/
|
|
45
41
|
export class BulkProcessError extends RuntimeError<{ errors: BulkErrorItem[] }> {
|
|
46
|
-
constructor(errors: { idx: number
|
|
42
|
+
constructor(errors: { idx: number; error: ValidationResultError }[]) {
|
|
47
43
|
super('Bulk processing errors have occurred', {
|
|
48
44
|
category: 'data',
|
|
49
45
|
details: {
|
|
@@ -63,4 +59,4 @@ export class BulkProcessError extends RuntimeError<{ errors: BulkErrorItem[] }>
|
|
|
63
59
|
*/
|
|
64
60
|
export interface ModelBulkSupport extends ModelCrudSupport {
|
|
65
61
|
processBulk<T extends ModelType>(cls: Class<T>, operations: BulkOperation<T>[]): Promise<BulkResponse>;
|
|
66
|
-
}
|
|
62
|
+
}
|
package/src/types/crud.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import type { ModelListOptions, ModelType, OptionalId } from './model.ts';
|
|
4
|
-
|
|
5
3
|
import type { ModelBasicSupport } from './basic.ts';
|
|
4
|
+
import type { ModelListOptions, ModelType, OptionalId } from './model.ts';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Interface for simple CRUD
|
|
9
8
|
* @concrete
|
|
10
9
|
*/
|
|
11
10
|
export interface ModelCrudSupport extends ModelBasicSupport {
|
|
12
|
-
|
|
13
11
|
/**
|
|
14
12
|
* Update an item
|
|
15
13
|
* @param item The document to update.
|
|
@@ -46,4 +44,4 @@ export interface ModelCrudSupport extends ModelBasicSupport {
|
|
|
46
44
|
* @param options Options for listing
|
|
47
45
|
*/
|
|
48
46
|
list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]>;
|
|
49
|
-
}
|
|
47
|
+
}
|
package/src/types/expiry.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import type { ModelType } from './model.ts';
|
|
4
3
|
import type { ModelCrudSupport } from './crud.ts';
|
|
4
|
+
import type { ModelType } from './model.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Support for managing expiration of data
|
|
@@ -14,4 +14,4 @@ export interface ModelExpirySupport extends ModelCrudSupport {
|
|
|
14
14
|
* @returns Returns the number of documents expired
|
|
15
15
|
*/
|
|
16
16
|
deleteExpired<T extends ModelType>(cls: Class<T>): Promise<number>;
|
|
17
|
-
}
|
|
17
|
+
}
|
package/src/types/model.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model Id Source
|
|
3
3
|
*/
|
|
4
|
-
export type ModelIdSource = { create: () => string
|
|
4
|
+
export type ModelIdSource = { create: () => string; valid: (id: string) => boolean };
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Simple model interface
|
|
@@ -25,4 +25,4 @@ export interface ModelListOptions {
|
|
|
25
25
|
abort?: AbortSignal;
|
|
26
26
|
limit?: number;
|
|
27
27
|
batchSizeHint?: number;
|
|
28
|
-
}
|
|
28
|
+
}
|
package/src/types/storage.ts
CHANGED
|
@@ -12,7 +12,6 @@ import type { ModelType } from './model.ts';
|
|
|
12
12
|
* @concrete
|
|
13
13
|
*/
|
|
14
14
|
export interface ModelStorageSupport {
|
|
15
|
-
|
|
16
15
|
/**
|
|
17
16
|
* Should storage modification be allowed
|
|
18
17
|
*/
|
|
@@ -48,4 +47,4 @@ export interface ModelStorageSupport {
|
|
|
48
47
|
* Truncate blob storage data
|
|
49
48
|
*/
|
|
50
49
|
truncateBlob?(): Promise<void>;
|
|
51
|
-
}
|
|
50
|
+
}
|
package/src/util/blob.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { hasFunction } from '@travetto/runtime';
|
|
2
|
+
|
|
2
3
|
import type { ModelBlobSupport } from '../types/blob.ts';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Utilities for processing blobs
|
|
6
7
|
*/
|
|
7
8
|
export class ModelBlobUtil {
|
|
8
|
-
|
|
9
9
|
/**
|
|
10
10
|
* Type guard for determining if service supports blob operations
|
|
11
11
|
*/
|
|
@@ -15,4 +15,4 @@ export class ModelBlobUtil {
|
|
|
15
15
|
* Type guard for determining if service supports blob write urls
|
|
16
16
|
*/
|
|
17
17
|
static isWriteUrlSupported = hasFunction<ModelBlobSupport>('getBlobWriteUrl');
|
|
18
|
-
}
|
|
18
|
+
}
|
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
|
+
}
|