@travetto/model 7.1.4 → 8.0.0-alpha.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 +12 -11
- package/package.json +7 -7
- package/src/error/exists.ts +2 -2
- package/src/error/invalid-index.ts +2 -2
- package/src/error/invalid-sub-type.ts +2 -2
- package/src/error/not-found.ts +2 -2
- package/src/registry/decorator.ts +2 -2
- package/src/registry/registry-index.ts +3 -3
- package/src/registry/types.ts +3 -1
- package/src/types/blob.ts +11 -10
- package/src/types/bulk.ts +2 -2
- package/src/util/blob.ts +1 -37
- package/src/util/crud.ts +10 -10
- package/src/util/expiry.ts +1 -1
- package/src/util/indexed.ts +0 -1
- package/support/doc.support.tsx +1 -1
- package/support/test/base.ts +2 -2
- package/support/test/blob.ts +36 -38
- package/support/test/crud.ts +42 -0
- package/support/test/expiry.ts +5 -5
- package/support/test/suite.ts +70 -66
package/README.md
CHANGED
|
@@ -182,10 +182,10 @@ export interface ModelBlobSupport {
|
|
|
182
182
|
* Upsert blob to storage
|
|
183
183
|
* @param location The location of the blob
|
|
184
184
|
* @param input The actual blob to write
|
|
185
|
-
* @param
|
|
185
|
+
* @param metadata Additional metadata to store with the blob
|
|
186
186
|
* @param overwrite Should we replace content if already found, defaults to true
|
|
187
187
|
*/
|
|
188
|
-
upsertBlob(location: string, input:
|
|
188
|
+
upsertBlob(location: string, input: BinaryType, metadata?: BinaryMetadata, overwrite?: boolean): Promise<void>;
|
|
189
189
|
|
|
190
190
|
/**
|
|
191
191
|
* Get blob from storage
|
|
@@ -197,7 +197,7 @@ export interface ModelBlobSupport {
|
|
|
197
197
|
* Get metadata for blob
|
|
198
198
|
* @param location The location of the blob
|
|
199
199
|
*/
|
|
200
|
-
|
|
200
|
+
getBlobMetadata(location: string): Promise<BinaryMetadata>;
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
203
|
* Delete blob by location
|
|
@@ -208,25 +208,26 @@ export interface ModelBlobSupport {
|
|
|
208
208
|
/**
|
|
209
209
|
* Update blob metadata
|
|
210
210
|
* @param location The location of the blob
|
|
211
|
+
* @param metadata The metadata to update
|
|
211
212
|
*/
|
|
212
|
-
|
|
213
|
+
updateBlobMetadata(location: string, metadata: BinaryMetadata): Promise<void>;
|
|
213
214
|
|
|
214
215
|
/**
|
|
215
216
|
* Produces an externally usable URL for sharing limited read access to a specific resource
|
|
216
217
|
*
|
|
217
218
|
* @param location The asset location to read from
|
|
218
|
-
* @param
|
|
219
|
+
* @param expiresIn Expiry
|
|
219
220
|
*/
|
|
220
|
-
getBlobReadUrl?(location: string,
|
|
221
|
+
getBlobReadUrl?(location: string, expiresIn?: TimeSpan): Promise<string>;
|
|
221
222
|
|
|
222
223
|
/**
|
|
223
224
|
* Produces an externally usable URL for sharing allowing direct write access
|
|
224
225
|
*
|
|
225
226
|
* @param location The asset location to write to
|
|
226
|
-
* @param
|
|
227
|
-
* @param
|
|
227
|
+
* @param metadata The metadata to associate with the final asset
|
|
228
|
+
* @param expiresIn Expiry
|
|
228
229
|
*/
|
|
229
|
-
getBlobWriteUrl?(location: string,
|
|
230
|
+
getBlobWriteUrl?(location: string, metadata: BinaryMetadata, expiresIn?: TimeSpan): Promise<string>;
|
|
230
231
|
}
|
|
231
232
|
```
|
|
232
233
|
|
|
@@ -278,7 +279,7 @@ To enforce that these contracts are honored, the module provides shared test sui
|
|
|
278
279
|
**Code: Memory Service Test Configuration**
|
|
279
280
|
```typescript
|
|
280
281
|
import { DependencyRegistryIndex } from '@travetto/di';
|
|
281
|
-
import {
|
|
282
|
+
import { RuntimeError, castTo, type Class, classConstruct } from '@travetto/runtime';
|
|
282
283
|
|
|
283
284
|
import { ModelBulkUtil } from '../../src/util/bulk.ts';
|
|
284
285
|
import { ModelCrudUtil } from '../../src/util/crud.ts';
|
|
@@ -306,7 +307,7 @@ export abstract class BaseModelSuite<T> {
|
|
|
306
307
|
}
|
|
307
308
|
return i;
|
|
308
309
|
} else {
|
|
309
|
-
throw new
|
|
310
|
+
throw new RuntimeError(`Size is not supported for this service: ${this.serviceClass.name}`);
|
|
310
311
|
}
|
|
311
312
|
}
|
|
312
313
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Datastore abstraction for core operations.",
|
|
6
6
|
"keywords": [
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"directory": "module/model"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/config": "^
|
|
31
|
-
"@travetto/di": "^
|
|
32
|
-
"@travetto/registry": "^
|
|
33
|
-
"@travetto/schema": "^
|
|
30
|
+
"@travetto/config": "^8.0.0-alpha.0",
|
|
31
|
+
"@travetto/di": "^8.0.0-alpha.0",
|
|
32
|
+
"@travetto/registry": "^8.0.0-alpha.0",
|
|
33
|
+
"@travetto/schema": "^8.0.0-alpha.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@travetto/cli": "^
|
|
37
|
-
"@travetto/test": "^
|
|
36
|
+
"@travetto/cli": "^8.0.0-alpha.0",
|
|
37
|
+
"@travetto/test": "^8.0.0-alpha.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
40
40
|
"@travetto/cli": {
|
package/src/error/exists.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type Class,
|
|
1
|
+
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Represents when a data item already exists
|
|
5
5
|
*/
|
|
6
|
-
export class ExistsError extends
|
|
6
|
+
export class ExistsError extends RuntimeError {
|
|
7
7
|
constructor(cls: Class | string, id: string) {
|
|
8
8
|
super(`${typeof cls === 'string' ? cls : cls.name} with id ${id} already exists`, {
|
|
9
9
|
category: 'data',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Class,
|
|
1
|
+
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import type { IndexConfig } from '../registry/types.ts';
|
|
4
4
|
import type { ModelType } from '../types/model.ts';
|
|
@@ -6,7 +6,7 @@ import type { ModelType } from '../types/model.ts';
|
|
|
6
6
|
/**
|
|
7
7
|
* Represents when an index is invalid
|
|
8
8
|
*/
|
|
9
|
-
export class IndexNotSupported<T extends ModelType> extends
|
|
9
|
+
export class IndexNotSupported<T extends ModelType> extends RuntimeError {
|
|
10
10
|
constructor(cls: Class<T>, idx: IndexConfig<T>, message: string = '') {
|
|
11
11
|
super(`${typeof cls === 'string' ? cls : cls.name} and index ${idx.name} of type ${idx.type} is not supported. ${message}`.trim(), { category: 'data' });
|
|
12
12
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type Class,
|
|
1
|
+
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Represents when a model subtype class is unable to be used directly
|
|
5
5
|
*/
|
|
6
|
-
export class SubTypeNotSupportedError extends
|
|
6
|
+
export class SubTypeNotSupportedError extends RuntimeError {
|
|
7
7
|
constructor(cls: Class | string) {
|
|
8
8
|
super(`${typeof cls === 'string' ? cls : cls.name} cannot be used for this operation`, { category: 'data' });
|
|
9
9
|
}
|
package/src/error/not-found.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type Class,
|
|
1
|
+
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Represents when a model of cls and id cannot be found
|
|
5
5
|
*/
|
|
6
|
-
export class NotFoundError extends
|
|
6
|
+
export class NotFoundError extends RuntimeError {
|
|
7
7
|
constructor(cls: Class | string, id: string, details: Record<string, unknown> = {}) {
|
|
8
8
|
super(`${typeof cls === 'string' ? cls : cls.name} with id ${id} not found`, { category: 'notfound', details });
|
|
9
9
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RuntimeError, castTo, type Class, getClass } from '@travetto/runtime';
|
|
2
2
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import type { ModelType } from '../types/model.ts';
|
|
@@ -30,7 +30,7 @@ export function Model(config: Partial<ModelConfig<ModelType>> | string = {}) {
|
|
|
30
30
|
*/
|
|
31
31
|
export function Index<T extends ModelType>(...indices: IndexConfig<T>[]) {
|
|
32
32
|
if (indices.some(config => config.fields.some(field => field === 'id'))) {
|
|
33
|
-
throw new
|
|
33
|
+
throw new RuntimeError('Cannot create an index with the id field');
|
|
34
34
|
}
|
|
35
35
|
return function (cls: Class<T>): void {
|
|
36
36
|
ModelRegistryIndex.getForRegister(cls).register({ indices });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
|
|
2
|
-
import {
|
|
2
|
+
import { RuntimeError, castTo, type Class } from '@travetto/runtime';
|
|
3
3
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
5
|
import type { IndexConfig, IndexType, ModelConfig } from './types.ts';
|
|
@@ -77,7 +77,7 @@ export class ModelRegistryIndex implements RegistryIndex {
|
|
|
77
77
|
|
|
78
78
|
// Don't allow two models with same class name, or same store name
|
|
79
79
|
if (classes.size > 1) {
|
|
80
|
-
throw new
|
|
80
|
+
throw new RuntimeError('Duplicate models with same store name', {
|
|
81
81
|
details: { classes: [...classes].toSorted() }
|
|
82
82
|
});
|
|
83
83
|
}
|
|
@@ -122,7 +122,7 @@ export class ModelRegistryIndex implements RegistryIndex {
|
|
|
122
122
|
getExpiryFieldName<T extends ModelType>(cls: Class<T>): keyof T {
|
|
123
123
|
const expiry = this.getConfig(cls).expiresAt;
|
|
124
124
|
if (!expiry) {
|
|
125
|
-
throw new
|
|
125
|
+
throw new RuntimeError(`${cls.name} is not configured with expiry support, please use @ExpiresAt to declare expiration behavior`);
|
|
126
126
|
}
|
|
127
127
|
return castTo(expiry);
|
|
128
128
|
}
|
package/src/registry/types.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import type { Class,
|
|
1
|
+
import type { Class, Primitive, ValidFields } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import type { ModelType } from '../types/model.ts';
|
|
4
4
|
|
|
5
|
+
type RetainPrimitiveFields<T> = Pick<T, ValidFields<T, Primitive | Date>>;
|
|
6
|
+
|
|
5
7
|
export type SortClauseRaw<T> = {
|
|
6
8
|
[P in keyof T]?:
|
|
7
9
|
T[P] extends object ? SortClauseRaw<RetainPrimitiveFields<T[P]>> : 1 | -1;
|
package/src/types/blob.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BinaryType, BinaryMetadata, ByteRange, TimeSpan } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Support for Blobs CRUD.
|
|
@@ -11,10 +11,10 @@ export interface ModelBlobSupport {
|
|
|
11
11
|
* Upsert blob to storage
|
|
12
12
|
* @param location The location of the blob
|
|
13
13
|
* @param input The actual blob to write
|
|
14
|
-
* @param
|
|
14
|
+
* @param metadata Additional metadata to store with the blob
|
|
15
15
|
* @param overwrite Should we replace content if already found, defaults to true
|
|
16
16
|
*/
|
|
17
|
-
upsertBlob(location: string, input:
|
|
17
|
+
upsertBlob(location: string, input: BinaryType, metadata?: BinaryMetadata, overwrite?: boolean): Promise<void>;
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Get blob from storage
|
|
@@ -26,7 +26,7 @@ export interface ModelBlobSupport {
|
|
|
26
26
|
* Get metadata for blob
|
|
27
27
|
* @param location The location of the blob
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
getBlobMetadata(location: string): Promise<BinaryMetadata>;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Delete blob by location
|
|
@@ -37,23 +37,24 @@ export interface ModelBlobSupport {
|
|
|
37
37
|
/**
|
|
38
38
|
* Update blob metadata
|
|
39
39
|
* @param location The location of the blob
|
|
40
|
+
* @param metadata The metadata to update
|
|
40
41
|
*/
|
|
41
|
-
|
|
42
|
+
updateBlobMetadata(location: string, metadata: BinaryMetadata): Promise<void>;
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* Produces an externally usable URL for sharing limited read access to a specific resource
|
|
45
46
|
*
|
|
46
47
|
* @param location The asset location to read from
|
|
47
|
-
* @param
|
|
48
|
+
* @param expiresIn Expiry
|
|
48
49
|
*/
|
|
49
|
-
getBlobReadUrl?(location: string,
|
|
50
|
+
getBlobReadUrl?(location: string, expiresIn?: TimeSpan): Promise<string>;
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
53
|
* Produces an externally usable URL for sharing allowing direct write access
|
|
53
54
|
*
|
|
54
55
|
* @param location The asset location to write to
|
|
55
|
-
* @param
|
|
56
|
-
* @param
|
|
56
|
+
* @param metadata The metadata to associate with the final asset
|
|
57
|
+
* @param expiresIn Expiry
|
|
57
58
|
*/
|
|
58
|
-
getBlobWriteUrl?(location: string,
|
|
59
|
+
getBlobWriteUrl?(location: string, metadata: BinaryMetadata, expiresIn?: TimeSpan): Promise<string>;
|
|
59
60
|
}
|
package/src/types/bulk.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Class,
|
|
1
|
+
import { type Class, RuntimeError } from '@travetto/runtime';
|
|
2
2
|
import type { ValidationError, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import type { ModelCrudSupport } from './crud.ts';
|
|
@@ -42,7 +42,7 @@ type BulkErrorItem = { message: string, type: string, errors?: ValidationError[]
|
|
|
42
42
|
/**
|
|
43
43
|
* Bulk processing error
|
|
44
44
|
*/
|
|
45
|
-
export class BulkProcessError extends
|
|
45
|
+
export class BulkProcessError extends RuntimeError<{ errors: BulkErrorItem[] }> {
|
|
46
46
|
constructor(errors: { idx: number, error: ValidationResultError }[]) {
|
|
47
47
|
super('Bulk processing errors have occurred', {
|
|
48
48
|
category: 'data',
|
package/src/util/blob.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AppError, type BinaryInput, BinaryUtil, type BlobMeta, type ByteRange, hasFunction } from '@travetto/runtime';
|
|
1
|
+
import { hasFunction } from '@travetto/runtime';
|
|
3
2
|
import type { ModelBlobSupport } from '../types/blob.ts';
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -11,39 +10,4 @@ export class ModelBlobUtil {
|
|
|
11
10
|
* Type guard for determining if service supports blob operations
|
|
12
11
|
*/
|
|
13
12
|
static isSupported = hasFunction<ModelBlobSupport>('getBlob');
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Convert input to a Readable, and get what metadata is available
|
|
17
|
-
*/
|
|
18
|
-
static async getInput(input: BinaryInput, metadata: BlobMeta = {}): Promise<[Readable, BlobMeta]> {
|
|
19
|
-
let result: Readable;
|
|
20
|
-
if (input instanceof Blob) {
|
|
21
|
-
metadata = { ...BinaryUtil.getBlobMeta(input), ...metadata };
|
|
22
|
-
metadata.size ??= input.size;
|
|
23
|
-
result = Readable.fromWeb(input.stream());
|
|
24
|
-
} else if (typeof input === 'object' && 'pipeThrough' in input) {
|
|
25
|
-
result = Readable.fromWeb(input);
|
|
26
|
-
} else if (typeof input === 'object' && 'pipe' in input) {
|
|
27
|
-
result = input;
|
|
28
|
-
} else {
|
|
29
|
-
metadata.size = input.length;
|
|
30
|
-
result = Readable.from(input);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return [result, metadata ?? {}];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Enforce byte range for stream stream/file of a certain size
|
|
38
|
-
*/
|
|
39
|
-
static enforceRange({ start, end }: ByteRange, size: number): Required<ByteRange> {
|
|
40
|
-
// End is inclusive
|
|
41
|
-
end = Math.min(end ?? (size - 1), size - 1);
|
|
42
|
-
|
|
43
|
-
if (Number.isNaN(start) || Number.isNaN(end) || !Number.isFinite(start) || start >= size || start < 0 || start > end) {
|
|
44
|
-
throw new AppError('Invalid position, out of range', { category: 'data' });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return { start, end };
|
|
48
|
-
}
|
|
49
13
|
}
|
package/src/util/crud.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { castTo, type Class, Util,
|
|
1
|
+
import { castTo, type Class, Util, RuntimeError, hasFunction, BinaryUtil, type BinaryArray, JSONUtil } from '@travetto/runtime';
|
|
2
2
|
import { DataUtil, SchemaRegistryIndex, SchemaValidator, type ValidationError, ValidationResultError } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
import { ModelRegistryIndex } from '../registry/registry-index.ts';
|
|
@@ -9,6 +9,8 @@ import { SubTypeNotSupportedError } from '../error/invalid-sub-type.ts';
|
|
|
9
9
|
import type { DataHandler, PrePersistScope } from '../registry/types.ts';
|
|
10
10
|
import type { ModelCrudSupport } from '../types/crud.ts';
|
|
11
11
|
|
|
12
|
+
type ModelLoadInput = string | BinaryArray | object;
|
|
13
|
+
|
|
12
14
|
export type ModelCrudProvider = {
|
|
13
15
|
idSource: ModelIdSource;
|
|
14
16
|
};
|
|
@@ -37,13 +39,11 @@ export class ModelCrudUtil {
|
|
|
37
39
|
* @param cls Class to load model for
|
|
38
40
|
* @param input Input as string or plain object
|
|
39
41
|
*/
|
|
40
|
-
static async load<T extends ModelType>(cls: Class<T>, input:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
resolvedInput = input;
|
|
46
|
-
}
|
|
42
|
+
static async load<T extends ModelType>(cls: Class<T>, input: ModelLoadInput, onTypeMismatch: 'notfound' | 'exists' = 'notfound'): Promise<T> {
|
|
43
|
+
const resolvedInput: object =
|
|
44
|
+
typeof input === 'string' ? JSONUtil.fromUTF8(input) :
|
|
45
|
+
BinaryUtil.isBinaryArray(input) ? JSONUtil.fromBinaryArray(input) :
|
|
46
|
+
input;
|
|
47
47
|
|
|
48
48
|
const result = SchemaRegistryIndex.getBaseClass(cls).from(resolvedInput);
|
|
49
49
|
|
|
@@ -142,11 +142,11 @@ export class ModelCrudUtil {
|
|
|
142
142
|
*/
|
|
143
143
|
static async prePartialUpdate<T extends ModelType>(cls: Class<T>, item: Partial<T>, view?: string): Promise<Partial<T>> {
|
|
144
144
|
if (!DataUtil.isPlainObject(item)) {
|
|
145
|
-
throw new
|
|
145
|
+
throw new RuntimeError(`A partial update requires a plain object, not an instance of ${castTo<Function>(item).constructor.name}`, { category: 'data' });
|
|
146
146
|
}
|
|
147
147
|
const keys = Object.keys(item);
|
|
148
148
|
if ((keys.length === 1 && item.id) || keys.length === 0) {
|
|
149
|
-
throw new
|
|
149
|
+
throw new RuntimeError('No fields to update');
|
|
150
150
|
} else {
|
|
151
151
|
item = { ...item };
|
|
152
152
|
delete item.id;
|
package/src/util/expiry.ts
CHANGED
|
@@ -34,7 +34,7 @@ export class ModelExpiryUtil {
|
|
|
34
34
|
static registerCull(service: ModelExpirySupport & { readonly config?: { cullRate?: number | TimeSpan } }): void {
|
|
35
35
|
const cullable = ModelRegistryIndex.getClasses().filter(cls => !!ModelRegistryIndex.getConfig(cls).expiresAt);
|
|
36
36
|
if (service.deleteExpired && cullable.length) {
|
|
37
|
-
const cullInterval = TimeUtil.
|
|
37
|
+
const cullInterval = TimeUtil.duration(service.config?.cullRate ?? '10m', 'ms');
|
|
38
38
|
|
|
39
39
|
(async (): Promise<void> => {
|
|
40
40
|
await Util.nonBlockingTimeout(1000);
|
package/src/util/indexed.ts
CHANGED
|
@@ -71,7 +71,6 @@ export class ModelIndexedUtil {
|
|
|
71
71
|
if (empty === undefined || empty === Error) {
|
|
72
72
|
throw new IndexNotSupported(cls, config, `Missing field value for ${parts.join('.')}`);
|
|
73
73
|
}
|
|
74
|
-
itemRef = castTo(empty!);
|
|
75
74
|
} else {
|
|
76
75
|
if (field !== sortField || (opts.includeSortInFields ?? true)) {
|
|
77
76
|
fields.push({ path: parts, value: castTo(itemRef) });
|
package/support/doc.support.tsx
CHANGED
|
@@ -22,7 +22,7 @@ export const Links = {
|
|
|
22
22
|
Blob: toLink('Blob', toConcrete<ModelBlobSupport>()),
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
export const ModelTypes = (fn:
|
|
25
|
+
export const ModelTypes = (fn: Function): DocJSXElement[] => {
|
|
26
26
|
const { content } = DocFileUtil.readSource(fn);
|
|
27
27
|
const found: DocJSXElementByFn<'CodeLink'>[] = [];
|
|
28
28
|
const seen = new Set<string>();
|
package/support/test/base.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DependencyRegistryIndex } from '@travetto/di';
|
|
2
|
-
import {
|
|
2
|
+
import { RuntimeError, castTo, type Class, classConstruct } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
import { ModelBulkUtil } from '../../src/util/bulk.ts';
|
|
5
5
|
import { ModelCrudUtil } from '../../src/util/crud.ts';
|
|
@@ -27,7 +27,7 @@ export abstract class BaseModelSuite<T> {
|
|
|
27
27
|
}
|
|
28
28
|
return i;
|
|
29
29
|
} else {
|
|
30
|
-
throw new
|
|
30
|
+
throw new RuntimeError(`Size is not supported for this service: ${this.serviceClass.name}`);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
package/support/test/blob.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
3
|
import { Suite, Test, TestFixtures } from '@travetto/test';
|
|
4
|
-
import { BinaryUtil, Util } from '@travetto/runtime';
|
|
4
|
+
import { BinaryMetadataUtil, BinaryUtil, Util } from '@travetto/runtime';
|
|
5
5
|
|
|
6
6
|
import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
|
|
7
7
|
|
|
8
8
|
import type { ModelBlobSupport } from '../../src/types/blob.ts';
|
|
9
|
-
import { ModelBlobUtil } from '../../src/util/blob.ts';
|
|
10
|
-
|
|
11
|
-
const meta = BinaryUtil.getBlobMeta;
|
|
12
9
|
|
|
13
10
|
@Suite()
|
|
14
11
|
export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
@@ -18,51 +15,51 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
18
15
|
@Test()
|
|
19
16
|
async writeBasic(): Promise<void> {
|
|
20
17
|
const service = await this.service;
|
|
21
|
-
const buffer = await this.fixture.
|
|
18
|
+
const buffer = await this.fixture.readBinaryArray('/asset.yml');
|
|
22
19
|
|
|
23
20
|
const id = Util.uuid();
|
|
24
21
|
|
|
25
22
|
await service.upsertBlob(id, buffer);
|
|
26
|
-
const m = await service.
|
|
27
|
-
const retrieved = await service.
|
|
23
|
+
const m = await service.getBlobMetadata(id);
|
|
24
|
+
const retrieved = await service.getBlobMetadata(id);
|
|
28
25
|
assert.deepStrictEqual(m, retrieved);
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
@Test()
|
|
32
29
|
async upsert(): Promise<void> {
|
|
33
30
|
const service = await this.service;
|
|
34
|
-
const buffer = await this.fixture.
|
|
31
|
+
const buffer = await this.fixture.readBinaryArray('/asset.yml');
|
|
35
32
|
|
|
36
33
|
const id = Util.uuid();
|
|
37
34
|
|
|
38
35
|
await service.upsertBlob(id, buffer, { hash: '10' });
|
|
39
|
-
assert((await service.
|
|
36
|
+
assert((await service.getBlobMetadata(id)).hash === '10');
|
|
40
37
|
|
|
41
38
|
await service.upsertBlob(id, buffer, { hash: '20' });
|
|
42
|
-
assert((await service.
|
|
39
|
+
assert((await service.getBlobMetadata(id)).hash === '20');
|
|
43
40
|
|
|
44
41
|
await service.upsertBlob(id, buffer, { hash: '30' }, false);
|
|
45
|
-
assert((await service.
|
|
42
|
+
assert((await service.getBlobMetadata(id)).hash === '20');
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
@Test()
|
|
49
46
|
async writeStream(): Promise<void> {
|
|
50
47
|
const service = await this.service;
|
|
51
|
-
const buffer = await this.fixture.
|
|
48
|
+
const buffer = await this.fixture.readBinaryArray('/asset.yml');
|
|
52
49
|
|
|
53
50
|
const id = Util.uuid();
|
|
54
51
|
await service.upsertBlob(id, buffer);
|
|
55
|
-
const { hash } = await service.
|
|
52
|
+
const { hash } = await service.getBlobMetadata(id);
|
|
56
53
|
|
|
57
54
|
const retrieved = await service.getBlob(id);
|
|
58
|
-
const { hash: received } =
|
|
55
|
+
const { hash: received } = BinaryMetadataUtil.read(retrieved)!;
|
|
59
56
|
assert(hash === received);
|
|
60
57
|
}
|
|
61
58
|
|
|
62
59
|
@Test()
|
|
63
60
|
async writeAndDelete(): Promise<void> {
|
|
64
61
|
const service = await this.service;
|
|
65
|
-
const buffer = await this.fixture.
|
|
62
|
+
const buffer = await this.fixture.readBinaryArray('/asset.yml');
|
|
66
63
|
|
|
67
64
|
const id = Util.uuid();
|
|
68
65
|
await service.upsertBlob(id, buffer);
|
|
@@ -77,7 +74,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
77
74
|
@Test()
|
|
78
75
|
async partialStream(): Promise<void> {
|
|
79
76
|
const service = await this.service;
|
|
80
|
-
const buffer = await this.fixture.
|
|
77
|
+
const buffer = await this.fixture.readBinaryArray('/text.txt');
|
|
81
78
|
|
|
82
79
|
const id = Util.uuid();
|
|
83
80
|
await service.upsertBlob(id, buffer);
|
|
@@ -89,19 +86,19 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
89
86
|
|
|
90
87
|
const partial = await service.getBlob(id, { start: 10, end: 20 });
|
|
91
88
|
assert(partial.size === 11);
|
|
92
|
-
const partialMeta =
|
|
89
|
+
const partialMeta = BinaryMetadataUtil.read(partial)!;
|
|
93
90
|
const subContent = await partial.text();
|
|
94
|
-
const range =
|
|
91
|
+
const range = BinaryMetadataUtil.enforceRange({ start: 10, end: 20 }, partialMeta);
|
|
95
92
|
assert(subContent.length === (range.end - range.start) + 1);
|
|
96
93
|
|
|
97
|
-
const og = await this.fixture.
|
|
94
|
+
const og = await this.fixture.readText('/text.txt');
|
|
98
95
|
|
|
99
96
|
assert(subContent === og.substring(10, 21));
|
|
100
97
|
|
|
101
98
|
const partialUnbounded = await service.getBlob(id, { start: 10 });
|
|
102
|
-
const partialUnboundedMeta =
|
|
99
|
+
const partialUnboundedMeta = BinaryMetadataUtil.read(partialUnbounded)!;
|
|
103
100
|
const subContent2 = await partialUnbounded.text();
|
|
104
|
-
const range2 =
|
|
101
|
+
const range2 = BinaryMetadataUtil.enforceRange({ start: 10 }, partialUnboundedMeta);
|
|
105
102
|
assert(subContent2.length === (range2.end - range2.start) + 1);
|
|
106
103
|
assert(subContent2.startsWith('klm'));
|
|
107
104
|
assert(subContent2.endsWith('xyz'));
|
|
@@ -123,15 +120,16 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
123
120
|
@Test()
|
|
124
121
|
async writeAndGet() {
|
|
125
122
|
const service = await this.service;
|
|
126
|
-
const buffer = await this.fixture.
|
|
123
|
+
const buffer = await this.fixture.readBinaryArray('/asset.yml');
|
|
127
124
|
await service.upsertBlob('orange', buffer, { contentType: 'text/yaml', filename: 'asset.yml' });
|
|
128
125
|
const saved = await service.getBlob('orange');
|
|
129
|
-
const savedMeta =
|
|
126
|
+
const savedMeta = BinaryMetadataUtil.read(saved)!;
|
|
127
|
+
console.error(savedMeta);
|
|
130
128
|
|
|
131
129
|
assert('text/yaml' === savedMeta.contentType);
|
|
132
|
-
assert(buffer.
|
|
130
|
+
assert(buffer.byteLength === savedMeta.size);
|
|
133
131
|
assert('asset.yml' === savedMeta.filename);
|
|
134
|
-
assert(
|
|
132
|
+
assert(!!savedMeta.hash);
|
|
135
133
|
}
|
|
136
134
|
|
|
137
135
|
@Test()
|
|
@@ -140,16 +138,16 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
140
138
|
|
|
141
139
|
await this.writeAndGet();
|
|
142
140
|
|
|
143
|
-
await service.
|
|
141
|
+
await service.updateBlobMetadata('orange', {
|
|
144
142
|
contentType: 'text/yml',
|
|
145
143
|
filename: 'orange.yml'
|
|
146
144
|
});
|
|
147
145
|
|
|
148
|
-
const savedMeta = await service.
|
|
146
|
+
const savedMeta = await service.getBlobMetadata('orange');
|
|
149
147
|
|
|
150
148
|
assert('text/yml' === savedMeta.contentType);
|
|
151
149
|
assert('orange.yml' === savedMeta.filename);
|
|
152
|
-
assert(
|
|
150
|
+
assert(savedMeta.hash === undefined);
|
|
153
151
|
}
|
|
154
152
|
|
|
155
153
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -157,38 +155,38 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
|
|
|
157
155
|
async signedUrl() {
|
|
158
156
|
const service = await this.service;
|
|
159
157
|
|
|
160
|
-
const
|
|
161
|
-
for (let i = 0; i <
|
|
162
|
-
|
|
158
|
+
const bytes = BinaryUtil.binaryArrayToBuffer(BinaryUtil.makeBinaryArray(1.5 * 10000));
|
|
159
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
160
|
+
bytes.writeUInt8(Math.trunc(Math.random() * 255), i);
|
|
163
161
|
}
|
|
164
162
|
|
|
165
163
|
const writable = await service.getBlobWriteUrl!('largeFile/one', {
|
|
166
164
|
contentType: 'image/jpeg',
|
|
167
165
|
});
|
|
168
166
|
|
|
169
|
-
console.log(writable);
|
|
170
167
|
assert(writable);
|
|
171
168
|
|
|
172
169
|
const response = await fetch(writable, {
|
|
173
170
|
method: 'PUT',
|
|
174
|
-
body: new File([
|
|
171
|
+
body: new File([bytes], 'gary', { type: 'image/jpeg' }),
|
|
175
172
|
});
|
|
176
173
|
|
|
177
174
|
console.error(await response.text());
|
|
178
175
|
|
|
179
176
|
assert(response.ok);
|
|
180
177
|
|
|
181
|
-
await service.
|
|
178
|
+
await service.updateBlobMetadata('largeFile/one', {
|
|
182
179
|
contentType: 'image/jpeg',
|
|
183
180
|
title: 'orange',
|
|
184
181
|
filename: 'gary',
|
|
185
|
-
size:
|
|
182
|
+
size: bytes.byteLength,
|
|
186
183
|
});
|
|
187
184
|
|
|
188
185
|
const found = await service.getBlob('largeFile/one');
|
|
189
|
-
|
|
186
|
+
const foundMeta = BinaryMetadataUtil.read(found);
|
|
187
|
+
assert(found.size === bytes.byteLength);
|
|
190
188
|
assert(found.type === 'image/jpeg');
|
|
191
|
-
assert(
|
|
192
|
-
assert(
|
|
189
|
+
assert(foundMeta.title === 'orange');
|
|
190
|
+
assert(foundMeta.filename === 'gary');
|
|
193
191
|
}
|
|
194
192
|
}
|
package/support/test/crud.ts
CHANGED
|
@@ -67,6 +67,13 @@ class Dated {
|
|
|
67
67
|
updatedDate: Date;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
@Model()
|
|
71
|
+
class BigIntModel {
|
|
72
|
+
id: string;
|
|
73
|
+
largeNumber: bigint;
|
|
74
|
+
optionalBigInt?: bigint;
|
|
75
|
+
}
|
|
76
|
+
|
|
70
77
|
@Suite()
|
|
71
78
|
export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
72
79
|
|
|
@@ -343,4 +350,39 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
343
350
|
assert(o2.simples);
|
|
344
351
|
assert(o2.simples.length === 1);
|
|
345
352
|
}
|
|
353
|
+
|
|
354
|
+
@Test('Verify bigint storage and retrieval')
|
|
355
|
+
async testBigIntReadWrite() {
|
|
356
|
+
const service = await this.service;
|
|
357
|
+
|
|
358
|
+
// Create with bigint values
|
|
359
|
+
const created = await service.create(BigIntModel, BigIntModel.from({
|
|
360
|
+
largeNumber: 9007199254740991n, // Number.MAX_SAFE_INTEGER as bigint
|
|
361
|
+
optionalBigInt: 1234567890123456789n
|
|
362
|
+
}));
|
|
363
|
+
|
|
364
|
+
assert(created.id);
|
|
365
|
+
assert.strictEqual(created.largeNumber, 9007199254740991n);
|
|
366
|
+
assert.strictEqual(created.optionalBigInt, 1234567890123456789n);
|
|
367
|
+
|
|
368
|
+
// Retrieve and verify
|
|
369
|
+
const retrieved = await service.get(BigIntModel, created.id);
|
|
370
|
+
assert.strictEqual(retrieved.largeNumber, 9007199254740991n);
|
|
371
|
+
assert.strictEqual(retrieved.optionalBigInt, 1234567890123456789n);
|
|
372
|
+
|
|
373
|
+
// Update with new bigint value
|
|
374
|
+
const updated = await service.update(BigIntModel, BigIntModel.from({
|
|
375
|
+
id: created.id,
|
|
376
|
+
largeNumber: 18014398509481982n,
|
|
377
|
+
optionalBigInt: undefined
|
|
378
|
+
}));
|
|
379
|
+
|
|
380
|
+
assert.strictEqual(updated.largeNumber, 18014398509481982n);
|
|
381
|
+
assert.strictEqual(updated.optionalBigInt, undefined);
|
|
382
|
+
|
|
383
|
+
// Verify update persisted
|
|
384
|
+
const final = await service.get(BigIntModel, created.id);
|
|
385
|
+
assert.strictEqual(final.largeNumber, 18014398509481982n);
|
|
386
|
+
assert(!final.optionalBigInt);
|
|
387
|
+
}
|
|
346
388
|
}
|
package/support/test/expiry.ts
CHANGED
|
@@ -2,7 +2,7 @@ import assert from 'node:assert';
|
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
3
|
|
|
4
4
|
import { Suite, Test } from '@travetto/test';
|
|
5
|
-
import { type TimeSpan,
|
|
5
|
+
import { type TimeSpan, TimeUtil } from '@travetto/runtime';
|
|
6
6
|
|
|
7
7
|
import { ExpiresAt, Model } from '../../src/registry/decorator.ts';
|
|
8
8
|
import type { ModelExpirySupport } from '../../src/types/expiry.ts';
|
|
@@ -23,12 +23,12 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
23
23
|
|
|
24
24
|
delayFactor: number = 1;
|
|
25
25
|
|
|
26
|
-
async wait(
|
|
27
|
-
await timers.setTimeout(TimeUtil.
|
|
26
|
+
async wait(input: TimeSpan | number | string) {
|
|
27
|
+
await timers.setTimeout(TimeUtil.duration(input, 'ms') * this.delayFactor);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
timeFromNow(
|
|
31
|
-
return TimeUtil.fromNow(TimeUtil.
|
|
30
|
+
timeFromNow(input: TimeSpan | number | string) {
|
|
31
|
+
return TimeUtil.fromNow(TimeUtil.duration(input, 'ms') * this.delayFactor);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
@Test()
|
package/support/test/suite.ts
CHANGED
|
@@ -1,14 +1,80 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Class } from '@travetto/runtime';
|
|
2
2
|
import { DependencyRegistryIndex } from '@travetto/di';
|
|
3
3
|
import { Registry } from '@travetto/registry';
|
|
4
|
-
import { SuiteRegistryIndex, TestFixtures } from '@travetto/test';
|
|
4
|
+
import { SuiteRegistryIndex, TestFixtures, type SuitePhaseHandler } from '@travetto/test';
|
|
5
5
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
6
6
|
|
|
7
7
|
import { ModelBlobUtil } from '../../src/util/blob.ts';
|
|
8
8
|
import { ModelStorageUtil } from '../../src/util/storage.ts';
|
|
9
9
|
import { ModelRegistryIndex } from '../../src/registry/registry-index.ts';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
type ConfigType = { autoCreate?: boolean, namespace?: string };
|
|
12
|
+
|
|
13
|
+
class ModelSuiteHandler<T extends { configClass: Class<ConfigType>, serviceClass: Class }> implements SuitePhaseHandler<T> {
|
|
14
|
+
qualifier?: symbol;
|
|
15
|
+
target: Class<T>;
|
|
16
|
+
constructor(target: Class<T>, qualifier?: symbol) {
|
|
17
|
+
this.qualifier = qualifier;
|
|
18
|
+
this.target = target;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async beforeAll(instance: T) {
|
|
22
|
+
await Registry.init();
|
|
23
|
+
|
|
24
|
+
const config = await DependencyRegistryIndex.getInstance<ConfigType>(instance.configClass);
|
|
25
|
+
if ('namespace' in config) {
|
|
26
|
+
config.namespace = `test_${Math.trunc(Math.random() * 10000)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// We manually create
|
|
30
|
+
config.autoCreate = false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async beforeEach(instance: T) {
|
|
34
|
+
const service = await DependencyRegistryIndex.getInstance<T>(instance.serviceClass, this.qualifier);
|
|
35
|
+
if (ModelStorageUtil.isSupported(service)) {
|
|
36
|
+
await service.createStorage();
|
|
37
|
+
if (service.upsertModel) {
|
|
38
|
+
await Promise.all(ModelRegistryIndex.getClasses()
|
|
39
|
+
.filter(cls => cls === SchemaRegistryIndex.getBaseClass(cls))
|
|
40
|
+
.map(modelCls => service.upsertModel!(modelCls)));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async afterEach(instance: T) {
|
|
46
|
+
const service = await DependencyRegistryIndex.getInstance<T>(instance.serviceClass, this.qualifier);
|
|
47
|
+
if (ModelStorageUtil.isSupported(service)) {
|
|
48
|
+
const models = ModelRegistryIndex.getClasses().filter(m => m === SchemaRegistryIndex.getBaseClass(m));
|
|
49
|
+
|
|
50
|
+
if (ModelBlobUtil.isSupported(service) && service.truncateBlob) {
|
|
51
|
+
await service.truncateBlob();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (service.truncateModel) {
|
|
55
|
+
await Promise.all(models.map(x => service.truncateModel!(x)));
|
|
56
|
+
} else if (service.deleteModel) {
|
|
57
|
+
await Promise.all(models.map(x => service.deleteModel!(x)));
|
|
58
|
+
} else {
|
|
59
|
+
await service.deleteStorage(); // Purge it all
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async afterAll(instance: T) {
|
|
65
|
+
const service = await DependencyRegistryIndex.getInstance<T>(instance.serviceClass, this.qualifier);
|
|
66
|
+
if (ModelStorageUtil.isSupported(service)) {
|
|
67
|
+
if (service.deleteModel) {
|
|
68
|
+
for (const model of ModelRegistryIndex.getClasses()) {
|
|
69
|
+
if (model === SchemaRegistryIndex.getBaseClass(model)) {
|
|
70
|
+
await service.deleteModel(model);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
await service.deleteStorage();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
12
78
|
|
|
13
79
|
/**
|
|
14
80
|
* Model test suite decorator
|
|
@@ -21,69 +87,7 @@ export function ModelSuite<T extends { configClass: Class<{ autoCreate?: boolean
|
|
|
21
87
|
return (target: Class<T>): void => {
|
|
22
88
|
target.prototype.fixtures = fixtures;
|
|
23
89
|
SuiteRegistryIndex.getForRegister(target).register({
|
|
24
|
-
|
|
25
|
-
async function (this: T & { [Loaded]?: boolean }) {
|
|
26
|
-
await Registry.init();
|
|
27
|
-
|
|
28
|
-
if (!this[Loaded]) {
|
|
29
|
-
const config = await DependencyRegistryIndex.getInstance(this.configClass);
|
|
30
|
-
if ('namespace' in config) {
|
|
31
|
-
config.namespace = `test_${Math.trunc(Math.random() * 10000)}`;
|
|
32
|
-
}
|
|
33
|
-
// We manually create
|
|
34
|
-
config.autoCreate = false;
|
|
35
|
-
this[Loaded] = true;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
],
|
|
39
|
-
beforeEach: [
|
|
40
|
-
async function (this: T) {
|
|
41
|
-
const service = await DependencyRegistryIndex.getInstance(this.serviceClass, qualifier);
|
|
42
|
-
if (ModelStorageUtil.isSupported(service)) {
|
|
43
|
-
await service.createStorage();
|
|
44
|
-
if (service.upsertModel) {
|
|
45
|
-
await Promise.all(ModelRegistryIndex.getClasses()
|
|
46
|
-
.filter(x => x === SchemaRegistryIndex.getBaseClass(x))
|
|
47
|
-
.map(m => service.upsertModel!(m)));
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
],
|
|
52
|
-
afterEach: [
|
|
53
|
-
async function (this: T) {
|
|
54
|
-
const service = await DependencyRegistryIndex.getInstance(this.serviceClass, qualifier);
|
|
55
|
-
if (ModelStorageUtil.isSupported(service)) {
|
|
56
|
-
const models = ModelRegistryIndex.getClasses().filter(m => m === SchemaRegistryIndex.getBaseClass(m));
|
|
57
|
-
|
|
58
|
-
if (ModelBlobUtil.isSupported(service) && service.truncateBlob) {
|
|
59
|
-
await service.truncateBlob();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (service.truncateModel) {
|
|
63
|
-
await Promise.all(models.map(x => service.truncateModel!(x)));
|
|
64
|
-
} else if (service.deleteModel) {
|
|
65
|
-
await Promise.all(models.map(x => service.deleteModel!(x)));
|
|
66
|
-
} else {
|
|
67
|
-
await service.deleteStorage(); // Purge it all
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
],
|
|
72
|
-
afterAll: [
|
|
73
|
-
async function (this: T) {
|
|
74
|
-
const service = await DependencyRegistryIndex.getInstance(this.serviceClass, qualifier);
|
|
75
|
-
if (ModelStorageUtil.isSupported(service)) {
|
|
76
|
-
if (service.deleteModel) {
|
|
77
|
-
for (const m of ModelRegistryIndex.getClasses()) {
|
|
78
|
-
if (m === SchemaRegistryIndex.getBaseClass(m)) {
|
|
79
|
-
await service.deleteModel(m);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
await service.deleteStorage();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
]
|
|
90
|
+
phaseHandlers: [new ModelSuiteHandler(target, qualifier)]
|
|
87
91
|
});
|
|
88
92
|
};
|
|
89
93
|
}
|