@travetto/model-mongo 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -92,8 +92,6 @@ export class MongoModelConfig {
92
92
  */
93
93
  @Field(Object)
94
94
  options: mongo.MongoClientOptions = {
95
- useNewUrlParser: true,
96
- useUnifiedTopology: true
97
95
  };
98
96
 
99
97
  /**
@@ -111,7 +109,10 @@ export class MongoModelConfig {
111
109
  */
112
110
  async fetch(val: string) {
113
111
  return ResourceManager.read(val)
114
- .catch(e => fs.readFile(val))
112
+ .then(res => typeof res === 'string' ? res : res.toString('utf8'))
113
+ .catch(e => fs.readFile(val)
114
+ .then(res => typeof res === 'string' ? res : res.toString('utf8'))
115
+ )
115
116
  .catch(() => val);
116
117
  }
117
118
 
@@ -122,16 +123,16 @@ export class MongoModelConfig {
122
123
  const opts = this.options;
123
124
  if (opts.ssl) {
124
125
  if (opts.sslCert) {
125
- opts.sslCert = await this.fetch(opts.sslCert as string);
126
+ opts.tlsCertificateFile = await this.fetch(opts.sslCert);
126
127
  }
127
128
  if (opts.sslKey) {
128
- opts.sslKey = await this.fetch(opts.sslKey as string);
129
+ opts.sslKey = await this.fetch(opts.sslKey);
129
130
  }
130
131
  if (opts.sslCA) {
131
- opts.sslCA = await Promise.all(opts.sslCA.map(k => this.fetch(k as string)));
132
+ opts.sslCA = await this.fetch(opts.sslCA);
132
133
  }
133
134
  if (opts.sslCRL) {
134
- opts.sslCRL = await Promise.all(opts.sslCRL.map(k => this.fetch(k as string)));
135
+ opts.sslCRL = await this.fetch(opts.sslCRL);
135
136
  }
136
137
  }
137
138
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@travetto/model-mongo",
3
3
  "displayName": "MongoDB Model Support",
4
- "version": "2.0.0",
4
+ "version": "2.1.0",
5
5
  "description": "Mongo backing for the travetto model module.",
6
6
  "keywords": [
7
7
  "mongo",
@@ -26,11 +26,10 @@
26
26
  "directory": "module/model-mongo"
27
27
  },
28
28
  "dependencies": {
29
- "@travetto/config": "^2.0.0",
30
- "@travetto/model": "^2.0.0",
31
- "@travetto/model-query": "2.0.0",
32
- "@types/mongodb": "^3.6.17",
33
- "mongodb": "^3.6.9"
29
+ "@travetto/config": "^2.1.0",
30
+ "@travetto/model": "^2.1.0",
31
+ "@travetto/model-query": "2.1.0",
32
+ "mongodb": "^4.3.1"
34
33
  },
35
34
  "publishConfig": {
36
35
  "access": "public"
package/src/config.ts CHANGED
@@ -44,8 +44,6 @@ export class MongoModelConfig {
44
44
  */
45
45
  @Field(Object)
46
46
  options: mongo.MongoClientOptions = {
47
- useNewUrlParser: true,
48
- useUnifiedTopology: true
49
47
  };
50
48
 
51
49
  /**
@@ -63,7 +61,10 @@ export class MongoModelConfig {
63
61
  */
64
62
  async fetch(val: string) {
65
63
  return ResourceManager.read(val)
66
- .catch(e => fs.readFile(val))
64
+ .then(res => typeof res === 'string' ? res : res.toString('utf8'))
65
+ .catch(e => fs.readFile(val)
66
+ .then(res => typeof res === 'string' ? res : res.toString('utf8'))
67
+ )
67
68
  .catch(() => val);
68
69
  }
69
70
 
@@ -74,16 +75,16 @@ export class MongoModelConfig {
74
75
  const opts = this.options;
75
76
  if (opts.ssl) {
76
77
  if (opts.sslCert) {
77
- opts.sslCert = await this.fetch(opts.sslCert as string);
78
+ opts.tlsCertificateFile = await this.fetch(opts.sslCert);
78
79
  }
79
80
  if (opts.sslKey) {
80
- opts.sslKey = await this.fetch(opts.sslKey as string);
81
+ opts.sslKey = await this.fetch(opts.sslKey);
81
82
  }
82
83
  if (opts.sslCA) {
83
- opts.sslCA = await Promise.all(opts.sslCA.map(k => this.fetch(k as string)));
84
+ opts.sslCA = await this.fetch(opts.sslCA);
84
85
  }
85
86
  if (opts.sslCRL) {
86
- opts.sslCRL = await Promise.all(opts.sslCRL.map(k => this.fetch(k as string)));
87
+ opts.sslCRL = await this.fetch(opts.sslCRL);
87
88
  }
88
89
  }
89
90
  }
@@ -31,17 +31,18 @@ export class MongoUtil {
31
31
  f = f[key as keyof typeof f] as IndexField<T>;
32
32
  keys.push(key);
33
33
  }
34
- return { [keys.join('.')]: (f as boolean) === true ? 1 : f as number } as Record<string, number>;
34
+ const rf = f as unknown as (number | boolean);
35
+ return { [keys.join('.')]: rf === true ? 1 : rf } as Record<string, number>;
35
36
  }
36
37
 
37
38
  static uuid(val: string) {
38
39
  return new mongo.Binary(Buffer.from(val.replace(/-/g, ''), 'hex'), mongo.Binary.SUBTYPE_UUID);
39
40
  }
40
41
 
41
- static idToString(id: string | mongo.ObjectID | mongo.Binary) {
42
+ static idToString(id: string | mongo.ObjectId | mongo.Binary) {
42
43
  if (typeof id === 'string') {
43
44
  return id;
44
- } else if (id instanceof mongo.ObjectID) {
45
+ } else if (id instanceof mongo.ObjectId) {
45
46
  return id.toHexString();
46
47
  } else {
47
48
  return id.buffer.toString('hex');
package/src/service.ts CHANGED
@@ -33,7 +33,9 @@ import { MongoModelConfig } from './config';
33
33
 
34
34
  const IdxFieldsⲐ = Symbol.for('@trv:model-mongo/idx');
35
35
 
36
- const asFielded = <T extends ModelType>(cfg: IndexConfig<T>) => (cfg as unknown as { [IdxFieldsⲐ]: mongo.SortOptionObject<T> });
36
+ const asFielded = <T extends ModelType>(cfg: IndexConfig<T>) => (cfg as unknown as { [IdxFieldsⲐ]: mongo.Sort });
37
+
38
+ type IdxCfg = mongo.CreateIndexesOptions;
37
39
 
38
40
  /**
39
41
  * Mongo-based model source
@@ -53,7 +55,7 @@ export class MongoModelService implements
53
55
  constructor(public readonly config: MongoModelConfig) { }
54
56
 
55
57
  async #describeStreamRaw(location: string) {
56
- const files = (await this.#bucket.find({ filename: location }, { limit: 1 }).toArray()) as [{ _id: mongo.ObjectID, metadata: StreamMeta }];
58
+ const files = (await this.#bucket.find({ filename: location }, { limit: 1 }).toArray()) as (mongo.GridFSFile & { metadata: StreamMeta })[];
57
59
 
58
60
  if (!files?.length) {
59
61
  throw new NotFoundError(STREAMS, location);
@@ -92,11 +94,11 @@ export class MongoModelService implements
92
94
  await this.#db.dropDatabase();
93
95
  }
94
96
 
95
- getGeoIndices<T extends ModelType>(cls: Class<T>, path: FieldConfig[] = [], root = cls): Record<string, '2d'>[] {
97
+ getGeoIndices<T extends ModelType>(cls: Class<T>, path: FieldConfig[] = [], root = cls): mongo.IndexSpecification[] {
96
98
  const fields = SchemaRegistry.has(cls) ?
97
99
  Object.values(SchemaRegistry.get(cls).views[AllViewⲐ].schema) :
98
100
  [];
99
- const out: Record<string, '2d'>[] = [];
101
+ const out: mongo.IndexSpecification[] = [];
100
102
  for (const field of fields) {
101
103
  if (SchemaRegistry.has(field.type)) {
102
104
  // Recurse
@@ -115,7 +117,10 @@ export class MongoModelService implements
115
117
  return [
116
118
  ...indices.map(idx => {
117
119
  const combined = asFielded(idx)[IdxFieldsⲐ] ??= Object.assign({}, ...idx.fields.map(x => MongoUtil.toIndex(x as IndexField<T>)));
118
- return [combined, (idx.type === 'unique' ? { unique: true } : {}) as mongo.IndexOptions] as const;
120
+ return [
121
+ combined as mongo.IndexSpecification,
122
+ (idx.type === 'unique' ? { unique: true } : {}) as IdxCfg
123
+ ] as const;
119
124
  }),
120
125
  ...this.getGeoIndices(cls).map(x => [x] as const)
121
126
  ];
@@ -127,7 +132,7 @@ export class MongoModelService implements
127
132
  if (creating.length) {
128
133
  console.debug('Creating indexes', { indices: creating });
129
134
  for (const el of creating) {
130
- await col.createIndex(el[0], el[1]);
135
+ await col.createIndex(el[0], el[1] ?? {});
131
136
  }
132
137
  }
133
138
  }
@@ -177,7 +182,7 @@ export class MongoModelService implements
177
182
 
178
183
  const store = await this.getStore(cls);
179
184
  const result = await store.insertOne(cleaned);
180
- if (result.insertedCount === 0) {
185
+ if (!result.insertedId) {
181
186
  throw new ExistsError(cls, cleaned.id);
182
187
  }
183
188
  delete (cleaned as { _id?: unknown })._id;
@@ -239,7 +244,11 @@ export class MongoModelService implements
239
244
  }, {} as Record<string, unknown>);
240
245
 
241
246
  const id = item.id;
242
- const res = await store.findOneAndUpdate(this.getWhere<ModelType>(cls, { id }), final, { returnOriginal: false });
247
+ const res = await store.findOneAndUpdate(
248
+ this.getWhere<ModelType>(cls, { id }),
249
+ final,
250
+ { returnDocument: 'after' }
251
+ );
243
252
 
244
253
  if (!res.value) {
245
254
  new NotFoundError(cls, id);
@@ -258,8 +267,7 @@ export class MongoModelService implements
258
267
 
259
268
  async * list<T extends ModelType>(cls: Class<T>) {
260
269
  const store = await this.getStore(cls);
261
- const cursor = store.find(this.getWhere(cls, {})).batchSize(100);
262
- cursor.timeout = true;
270
+ const cursor = store.find(this.getWhere(cls, {}), { timeout: true }).batchSize(100);
263
271
  for await (const el of cursor) {
264
272
  try {
265
273
  yield MongoUtil.postLoadId(await ModelCrudUtil.load(cls, el));
@@ -323,7 +331,7 @@ export class MongoModelService implements
323
331
  }
324
332
 
325
333
  const store = await this.getStore(cls);
326
- const bulk = store.initializeUnorderedBulkOp({ w: 1 });
334
+ const bulk = store.initializeUnorderedBulkOp({ writeConcern: { w: 1 } });
327
335
  const { upsertedIds, insertedIds } = await ModelBulkUtil.preStore(cls, operations, this);
328
336
 
329
337
  out.insertedIds = new Map([...upsertedIds.entries(), ...insertedIds.entries()]);
@@ -336,7 +344,7 @@ export class MongoModelService implements
336
344
  } else if (op.update) {
337
345
  bulk.find({ _id: MongoUtil.uuid(op.update.id) }).update({ $set: op.update });
338
346
  } else if (op.delete) {
339
- bulk.find({ _id: MongoUtil.uuid(op.delete.id) }).removeOne();
347
+ bulk.find({ _id: MongoUtil.uuid(op.delete.id) }).deleteOne();
340
348
  }
341
349
  }
342
350
 
@@ -347,7 +355,7 @@ export class MongoModelService implements
347
355
  MongoUtil.postLoadId(op.insert as T);
348
356
  }
349
357
  }
350
- for (const { index, _id } of res.getUpsertedIds() as { index: number, _id: mongo.ObjectID }[]) {
358
+ for (const { index, _id } of res.getUpsertedIds() as { index: number, _id: mongo.ObjectId }[]) {
351
359
  out.insertedIds.set(index, MongoUtil.idToString(_id));
352
360
  }
353
361
 
@@ -422,10 +430,9 @@ export class MongoModelService implements
422
430
  const where = this.getWhere(
423
431
  cls,
424
432
  ModelIndexedUtil.projectIndex(cls, idx, body, { emptySortValue: { $exists: true } }) as WhereClause<T>
425
- ) as mongo.FilterQuery<T>;
433
+ ) as mongo.Filter<T>;
426
434
 
427
- const cursor = store.find(where).batchSize(100).sort(asFielded(idxCfg)[IdxFieldsⲐ]);
428
- cursor.timeout = true;
435
+ const cursor = store.find(where, { timeout: true }).batchSize(100).sort(asFielded(idxCfg)[IdxFieldsⲐ]);
429
436
 
430
437
  for await (const el of cursor) {
431
438
  yield (await MongoUtil.postLoadId(await ModelCrudUtil.load(cls, el))) as T;
@@ -522,10 +529,10 @@ export class MongoModelService implements
522
529
 
523
530
  pipeline.unshift({ $match: q });
524
531
 
525
- const result = await col.aggregate(pipeline).toArray();
532
+ const result = (await col.aggregate(pipeline).toArray()) as { _id: mongo.ObjectId, count: number }[];
526
533
 
527
- return result.map((val: ({ _id: string, count: number })) => ({
528
- key: val._id,
534
+ return result.map(val => ({
535
+ key: MongoUtil.idToString(val._id),
529
536
  count: val.count
530
537
  })).sort((a, b) => b.count - a.count);
531
538
  }