@travetto/model-mongo 5.0.0-rc.10 → 5.0.0-rc.12
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 +1 -1
- package/package.json +5 -5
- package/src/service.ts +41 -42
package/README.md
CHANGED
|
@@ -17,10 +17,10 @@ This module provides an [mongodb](https://mongodb.com)-based implementation for
|
|
|
17
17
|
|
|
18
18
|
Supported features:
|
|
19
19
|
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts#L11)
|
|
20
|
-
* [Streaming](https://github.com/travetto/travetto/tree/main/module/model/src/service/stream.ts#L3)
|
|
21
20
|
* [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts#L11)
|
|
22
21
|
* [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/service/bulk.ts#L19)
|
|
23
22
|
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/service/indexed.ts#L12)
|
|
23
|
+
* [Blob](https://github.com/travetto/travetto/tree/main/module/model/src/service/blob.ts#L8)
|
|
24
24
|
* [Query Crud](https://github.com/travetto/travetto/tree/main/module/model-query/src/service/crud.ts#L11)
|
|
25
25
|
* [Facet](https://github.com/travetto/travetto/tree/main/module/model-query/src/service/facet.ts#L12)
|
|
26
26
|
* [Query](https://github.com/travetto/travetto/tree/main/module/model-query/src/service/query.ts#L10)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-mongo",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.12",
|
|
4
4
|
"description": "Mongo backing for the travetto model module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mongo",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"directory": "module/model-mongo"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@travetto/config": "^5.0.0-rc.
|
|
29
|
-
"@travetto/model": "^5.0.0-rc.
|
|
30
|
-
"@travetto/model-query": "^5.0.0-rc.
|
|
28
|
+
"@travetto/config": "^5.0.0-rc.12",
|
|
29
|
+
"@travetto/model": "^5.0.0-rc.12",
|
|
30
|
+
"@travetto/model-query": "^5.0.0-rc.12",
|
|
31
31
|
"mongodb": "^6.8.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@travetto/command": "^5.0.0-rc.
|
|
34
|
+
"@travetto/command": "^5.0.0-rc.11"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@travetto/command": {
|
package/src/service.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
// Wildcard import needed here due to packaging issues
|
|
2
1
|
import {
|
|
3
2
|
type Db, GridFSBucket, MongoClient, type Sort, type CreateIndexesOptions,
|
|
4
3
|
type GridFSFile, type IndexSpecification, type Collection, ObjectId,
|
|
5
4
|
Binary
|
|
6
5
|
} from 'mongodb';
|
|
7
|
-
import { Readable } from 'node:stream';
|
|
8
6
|
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
import { Readable } from 'node:stream';
|
|
9
8
|
|
|
10
9
|
import {
|
|
11
|
-
ModelRegistry, ModelType, OptionalId,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
StreamMeta, BulkOp, BulkResponse,
|
|
15
|
-
NotFoundError, ExistsError, IndexConfig,
|
|
16
|
-
StreamRange
|
|
10
|
+
ModelRegistry, ModelType, OptionalId, ModelCrudSupport, ModelStorageSupport,
|
|
11
|
+
ModelExpirySupport, ModelBulkSupport, ModelIndexedSupport, BulkOp, BulkResponse,
|
|
12
|
+
NotFoundError, ExistsError, IndexConfig, ModelBlobSupport, ModelBlobUtil
|
|
17
13
|
} from '@travetto/model';
|
|
18
14
|
import {
|
|
19
15
|
ModelQuery, ModelQueryCrudSupport, ModelQueryFacetSupport, ModelQuerySupport,
|
|
@@ -21,7 +17,10 @@ import {
|
|
|
21
17
|
QueryVerifier
|
|
22
18
|
} from '@travetto/model-query';
|
|
23
19
|
|
|
24
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
ShutdownManager, type Class, type DeepPartial, AppError, TypedObject,
|
|
22
|
+
castTo, asFull, BlobMeta, ByteRange, BinaryInput, BinaryUtil
|
|
23
|
+
} from '@travetto/runtime';
|
|
25
24
|
import { Injectable } from '@travetto/di';
|
|
26
25
|
import { FieldConfig, SchemaRegistry, SchemaValidator } from '@travetto/schema';
|
|
27
26
|
|
|
@@ -33,9 +32,9 @@ import { ModelQuerySuggestUtil } from '@travetto/model-query/src/internal/servic
|
|
|
33
32
|
import { PointImpl } from '@travetto/model-query/src/internal/model/point';
|
|
34
33
|
import { ModelQueryExpiryUtil } from '@travetto/model-query/src/internal/service/expiry';
|
|
35
34
|
import { ModelExpiryUtil } from '@travetto/model/src/internal/service/expiry';
|
|
36
|
-
import { enforceRange, StreamModel, STREAMS } from '@travetto/model/src/internal/service/stream';
|
|
37
35
|
import { AllViewⲐ } from '@travetto/schema/src/internal/types';
|
|
38
36
|
import { ModelBulkUtil } from '@travetto/model/src/internal/service/bulk';
|
|
37
|
+
import { MODEL_BLOB, ModelBlobNamespace } from '@travetto/model/src/internal/service/blob';
|
|
39
38
|
|
|
40
39
|
import { MongoUtil, WithId } from './internal/util';
|
|
41
40
|
import { MongoModelConfig } from './config';
|
|
@@ -46,7 +45,7 @@ const asFielded = (cfg: IndexConfig<ModelType>): { [IdxFieldsⲐ]: Sort } => cas
|
|
|
46
45
|
|
|
47
46
|
type IdxCfg = CreateIndexesOptions;
|
|
48
47
|
|
|
49
|
-
type
|
|
48
|
+
type BlobRaw = GridFSFile & { metadata?: BlobMeta };
|
|
50
49
|
|
|
51
50
|
/**
|
|
52
51
|
* Mongo-based model source
|
|
@@ -54,7 +53,7 @@ type StreamRaw = GridFSFile & { metadata: StreamMeta };
|
|
|
54
53
|
@Injectable()
|
|
55
54
|
export class MongoModelService implements
|
|
56
55
|
ModelCrudSupport, ModelStorageSupport,
|
|
57
|
-
ModelBulkSupport,
|
|
56
|
+
ModelBulkSupport, ModelBlobSupport,
|
|
58
57
|
ModelIndexedSupport, ModelQuerySupport,
|
|
59
58
|
ModelQueryCrudSupport, ModelQueryFacetSupport,
|
|
60
59
|
ModelQuerySuggestSupport, ModelExpirySupport {
|
|
@@ -66,11 +65,11 @@ export class MongoModelService implements
|
|
|
66
65
|
|
|
67
66
|
constructor(public readonly config: MongoModelConfig) { }
|
|
68
67
|
|
|
69
|
-
async #
|
|
70
|
-
const files:
|
|
68
|
+
async #describeBlobRaw(location: string): Promise<BlobRaw> {
|
|
69
|
+
const files: BlobRaw[] = await this.#bucket.find({ filename: location }, { limit: 1 }).toArray();
|
|
71
70
|
|
|
72
71
|
if (!files?.length) {
|
|
73
|
-
throw new NotFoundError(
|
|
72
|
+
throw new NotFoundError(ModelBlobNamespace, location);
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
return files[0];
|
|
@@ -80,7 +79,7 @@ export class MongoModelService implements
|
|
|
80
79
|
this.client = await MongoClient.connect(this.config.url, this.config.options);
|
|
81
80
|
this.#db = this.client.db(this.config.namespace);
|
|
82
81
|
this.#bucket = new GridFSBucket(this.#db, {
|
|
83
|
-
bucketName:
|
|
82
|
+
bucketName: ModelBlobNamespace,
|
|
84
83
|
writeConcern: { w: 1 }
|
|
85
84
|
});
|
|
86
85
|
await ModelStorageUtil.registerModelChangeListener(this);
|
|
@@ -151,10 +150,8 @@ export class MongoModelService implements
|
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
async truncateModel<T extends ModelType>(cls: Class<T>): Promise<void> {
|
|
154
|
-
if (cls ===
|
|
155
|
-
|
|
156
|
-
await this.#bucket.drop();
|
|
157
|
-
} catch { }
|
|
153
|
+
if (cls === MODEL_BLOB) {
|
|
154
|
+
await this.#bucket.drop().catch(() => { });
|
|
158
155
|
} else {
|
|
159
156
|
const col = await this.getStore(cls);
|
|
160
157
|
await col.deleteMany({});
|
|
@@ -283,37 +280,39 @@ export class MongoModelService implements
|
|
|
283
280
|
}
|
|
284
281
|
}
|
|
285
282
|
|
|
286
|
-
//
|
|
287
|
-
async
|
|
283
|
+
// Blob
|
|
284
|
+
async insertBlob(location: string, input: BinaryInput, meta?: BlobMeta, errorIfExisting = false): Promise<void> {
|
|
285
|
+
await this.describeBlob(location);
|
|
286
|
+
if (errorIfExisting) {
|
|
287
|
+
throw new ExistsError(ModelBlobNamespace, location);
|
|
288
|
+
}
|
|
289
|
+
return this.upsertBlob(location, input, meta);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async upsertBlob(location: string, input: BinaryInput, meta?: BlobMeta): Promise<void> {
|
|
293
|
+
const [stream, blobMeta] = await ModelBlobUtil.getInput(input, meta);
|
|
288
294
|
const writeStream = this.#bucket.openUploadStream(location, {
|
|
289
|
-
contentType:
|
|
290
|
-
metadata:
|
|
295
|
+
contentType: blobMeta.contentType,
|
|
296
|
+
metadata: blobMeta
|
|
291
297
|
});
|
|
292
298
|
|
|
293
|
-
await pipeline(
|
|
299
|
+
await pipeline(stream, writeStream);
|
|
294
300
|
}
|
|
295
301
|
|
|
296
|
-
async
|
|
297
|
-
const meta = await this.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const res = await this.#bucket.openDownloadStreamByName(location, range);
|
|
305
|
-
if (!res) {
|
|
306
|
-
throw new NotFoundError(STREAMS, location);
|
|
307
|
-
}
|
|
308
|
-
return res;
|
|
302
|
+
async getBlob(location: string, range?: ByteRange): Promise<Blob> {
|
|
303
|
+
const meta = await this.describeBlob(location);
|
|
304
|
+
const final = range ? ModelBlobUtil.enforceRange(range, meta.size!) : undefined;
|
|
305
|
+
const mongoRange = final ? { start: final.start, end: final.end + 1 } : undefined;
|
|
306
|
+
const res = (): Readable => this.#bucket.openDownloadStreamByName(location, mongoRange);
|
|
307
|
+
return BinaryUtil.readableBlob(res, { ...meta, range: final });
|
|
309
308
|
}
|
|
310
309
|
|
|
311
|
-
async
|
|
312
|
-
return (await this.#
|
|
310
|
+
async describeBlob(location: string): Promise<BlobMeta> {
|
|
311
|
+
return (await this.#describeBlobRaw(location)).metadata ?? {};
|
|
313
312
|
}
|
|
314
313
|
|
|
315
|
-
async
|
|
316
|
-
const fileId = (await this.#
|
|
314
|
+
async deleteBlob(location: string): Promise<void> {
|
|
315
|
+
const fileId = (await this.#describeBlobRaw(location))._id;
|
|
317
316
|
await this.#bucket.delete(fileId);
|
|
318
317
|
}
|
|
319
318
|
|