@e22m4u/js-repository-mongodb-adapter 0.0.20 → 0.0.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/js-repository-mongodb-adapter",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
4
4
  "description": "MongoDB адаптер для @e22m4u/js-repository",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -38,7 +38,7 @@
38
38
  "peerDependencies": {
39
39
  "@e22m4u/js-format": "*",
40
40
  "@e22m4u/js-service": "*",
41
- "@e22m4u/js-repository": "~0.0.34"
41
+ "@e22m4u/js-repository": "~0.0.36"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@commitlint/cli": "^17.7.1",
@@ -364,15 +364,24 @@ export class MongodbAdapter extends Adapter {
364
364
  * @private
365
365
  */
366
366
  _buildQuery(modelName, clause) {
367
+ if (clause == null) return;
368
+ if (typeof clause !== 'object' || Array.isArray(clause))
369
+ throw new InvalidArgumentError(
370
+ 'The provided option "where" should be an Object, but %v given.',
371
+ clause,
372
+ );
367
373
  const query = {};
368
- if (!clause || typeof clause !== 'object') return query;
369
374
  const idPropName = this._getIdPropName(modelName);
370
375
  Object.keys(clause).forEach(key => {
371
376
  let cond = clause[key];
372
377
  // and/or/nor clause
373
378
  if (key === 'and' || key === 'or' || key === 'nor') {
374
- if (Array.isArray(cond))
375
- cond = cond.map(c => this._buildQuery(modelName, c));
379
+ if (cond == null) return;
380
+ if (!Array.isArray(cond))
381
+ throw new InvalidOperatorValueError(key, 'an Array', cond);
382
+ if (cond.length === 0) return;
383
+ cond = cond.map(c => this._buildQuery(modelName, c));
384
+ cond = cond.filter(c => c != null);
376
385
  query['$' + key] = cond;
377
386
  return;
378
387
  }
@@ -527,7 +536,7 @@ export class MongodbAdapter extends Adapter {
527
536
  // unknown
528
537
  query[key] = cond;
529
538
  });
530
- return query;
539
+ return Object.keys(query).length ? query : undefined;
531
540
  }
532
541
 
533
542
  /**
@@ -7,6 +7,7 @@ import {DataType} from '@e22m4u/js-repository';
7
7
  import {createMongodbUrl} from './utils/index.js';
8
8
  import {MongodbAdapter} from './mongodb-adapter.js';
9
9
  import {AdapterRegistry} from '@e22m4u/js-repository';
10
+ import {InvalidOperatorValueError} from '@e22m4u/js-repository';
10
11
  import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '@e22m4u/js-repository';
11
12
 
12
13
  const CONFIG = {
@@ -274,18 +275,34 @@ describe('MongodbAdapter', function () {
274
275
  expect(throwable(null)()).to.be.undefined;
275
276
  });
276
277
 
277
- it('recognizes direction by the given direction flag', async function () {
278
+ it('uses ascending direction by default', async function () {
278
279
  const schema = createSchema();
279
280
  schema.defineModel({name: 'model', datasource: 'mongodb'});
280
281
  const A = await schema
281
282
  .getService(AdapterRegistry)
282
283
  .getAdapter('mongodb');
283
- const res1 = A._buildSort('model', 'foo');
284
- const res2 = A._buildSort('model', 'foo DESC');
285
- const res3 = A._buildSort('model', 'foo ASC');
286
- expect(res1).to.be.eql({foo: 1});
287
- expect(res2).to.be.eql({foo: -1});
288
- expect(res3).to.be.eql({foo: 1});
284
+ const res = A._buildSort('model', 'foo');
285
+ expect(res).to.be.eql({foo: 1});
286
+ });
287
+
288
+ it('uses descending direction by the "DESC" flag', async function () {
289
+ const schema = createSchema();
290
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
291
+ const A = await schema
292
+ .getService(AdapterRegistry)
293
+ .getAdapter('mongodb');
294
+ const res = A._buildSort('model', 'foo DESC');
295
+ expect(res).to.be.eql({foo: -1});
296
+ });
297
+
298
+ it('uses ascending direction by the "ASC" flag', async function () {
299
+ const schema = createSchema();
300
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
301
+ const A = await schema
302
+ .getService(AdapterRegistry)
303
+ .getAdapter('mongodb');
304
+ const res = A._buildSort('model', 'foo ASC');
305
+ expect(res).to.be.eql({foo: 1});
289
306
  });
290
307
 
291
308
  it('converts the given property name to the column name', async function () {
@@ -348,18 +365,44 @@ describe('MongodbAdapter', function () {
348
365
  expect(throwable(['bar', 'baz'])()).to.be.eql({bar: 1, baz: 1});
349
366
  });
350
367
 
351
- it('recognizes direction by the given direction flag', async function () {
368
+ it('uses ascending direction by default', async function () {
369
+ const schema = createSchema();
370
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
371
+ const A = await schema
372
+ .getService(AdapterRegistry)
373
+ .getAdapter('mongodb');
374
+ const res = A._buildSort('model', ['foo', 'bar']);
375
+ expect(res).to.be.eql({foo: 1, bar: 1});
376
+ });
377
+
378
+ it('uses descending direction by the "DESC" flag', async function () {
379
+ const schema = createSchema();
380
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
381
+ const A = await schema
382
+ .getService(AdapterRegistry)
383
+ .getAdapter('mongodb');
384
+ const res = A._buildSort('model', ['foo DESC', 'bar DESC']);
385
+ expect(res).to.be.eql({foo: -1, bar: -1});
386
+ });
387
+
388
+ it('uses ascending direction by the "ASC" flag', async function () {
389
+ const schema = createSchema();
390
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
391
+ const A = await schema
392
+ .getService(AdapterRegistry)
393
+ .getAdapter('mongodb');
394
+ const res = A._buildSort('model', ['foo ASC', 'bar ASC']);
395
+ expect(res).to.be.eql({foo: 1, bar: 1});
396
+ });
397
+
398
+ it('uses multiple directions by the multiple fields', async function () {
352
399
  const schema = createSchema();
353
400
  schema.defineModel({name: 'model', datasource: 'mongodb'});
354
401
  const A = await schema
355
402
  .getService(AdapterRegistry)
356
403
  .getAdapter('mongodb');
357
- const res1 = A._buildSort('model', ['foo', 'bar']);
358
- const res2 = A._buildSort('model', ['foo DESC', 'bar ASC']);
359
- const res3 = A._buildSort('model', ['foo ASC', 'bar DESC']);
360
- expect(res1).to.be.eql({foo: 1, bar: 1});
361
- expect(res2).to.be.eql({foo: -1, bar: 1});
362
- expect(res3).to.be.eql({foo: 1, bar: -1});
404
+ const res = A._buildSort('model', ['foo', 'bar DESC', 'baz ASC']);
405
+ expect(res).to.be.eql({foo: 1, bar: -1, baz: 1});
363
406
  });
364
407
 
365
408
  it('converts the given property names to column names', async function () {
@@ -391,6 +434,120 @@ describe('MongodbAdapter', function () {
391
434
  });
392
435
  });
393
436
 
437
+ describe('_buildQuery', function () {
438
+ it('requires the second argument to be an object', async function () {
439
+ const schema = createSchema();
440
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
441
+ const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
442
+ const throwable = v => () => A._buildQuery('model', v);
443
+ const error = v =>
444
+ format(
445
+ 'The provided option "where" should be an Object, but %s given.',
446
+ v,
447
+ );
448
+ expect(throwable('str')).to.throw(error('"str"'));
449
+ expect(throwable('')).to.throw(error('""'));
450
+ expect(throwable(10)).to.throw(error('10'));
451
+ expect(throwable(0)).to.throw(error('0'));
452
+ expect(throwable(true)).to.throw(error('true'));
453
+ expect(throwable(false)).to.throw(error('false'));
454
+ expect(throwable({foo: 'bar'})()).to.be.eql({foo: 'bar'});
455
+ expect(throwable({})()).to.be.undefined;
456
+ expect(throwable(undefined)()).to.be.undefined;
457
+ expect(throwable(null)()).to.be.undefined;
458
+ });
459
+
460
+ it('converts the property names to column names', async function () {
461
+ const schema = createSchema();
462
+ schema.defineModel({
463
+ name: 'model',
464
+ datasource: 'mongodb',
465
+ properties: {
466
+ foo: {
467
+ type: DataType.STRING,
468
+ columnName: 'bar',
469
+ },
470
+ baz: {
471
+ type: DataType.STRING,
472
+ columnName: 'qux',
473
+ },
474
+ },
475
+ });
476
+ const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
477
+ const res = A._buildQuery('model', {foo: 'a1', baz: null});
478
+ expect(res).to.be.eql({bar: 'a1', qux: null});
479
+ });
480
+
481
+ it('converts strings of the ObjectId to instances', async function () {
482
+ const schema = createSchema();
483
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
484
+ const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
485
+ const oid1 = new ObjectId();
486
+ const oid2 = new ObjectId();
487
+ const id1 = String(oid1);
488
+ const id2 = String(oid2);
489
+ const res = A._buildQuery('model', {foo: id1, bar: id2});
490
+ expect(res.foo).to.be.instanceof(ObjectId);
491
+ expect(res.bar).to.be.instanceof(ObjectId);
492
+ expect(res.foo).to.be.eql(oid1);
493
+ expect(res.bar).to.be.eql(oid2);
494
+ });
495
+
496
+ it('adds "$" prefix to the "and", "or" and "nor" operator keys', async function () {
497
+ const input = {
498
+ and: [{foo: 'a1'}],
499
+ or: [{foo: 'a2'}],
500
+ nor: [{foo: 'a3'}],
501
+ };
502
+ const schema = createSchema();
503
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
504
+ const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
505
+ const res = A._buildQuery('model', input);
506
+ expect(res).to.be.eql({
507
+ $and: [{foo: 'a1'}],
508
+ $or: [{foo: 'a2'}],
509
+ $nor: [{foo: 'a3'}],
510
+ });
511
+ });
512
+
513
+ it('does not include an empty array of "and", "or" and "nor" operators', async function () {
514
+ const input1 = {foo: 'a1', and: [], or: [], nor: []};
515
+ const input2 = {foo: 'a2', and: undefined, or: undefined, nor: undefined};
516
+ const input3 = {foo: 'a3', and: null, or: null, nor: null};
517
+ const schema = createSchema();
518
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
519
+ const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
520
+ const res1 = A._buildQuery('model', input1);
521
+ const res2 = A._buildQuery('model', input2);
522
+ const res3 = A._buildQuery('model', input3);
523
+ expect(res1).to.be.eql({foo: 'a1'});
524
+ expect(res2).to.be.eql({foo: 'a2'});
525
+ expect(res3).to.be.eql({foo: 'a3'});
526
+ });
527
+
528
+ it('operators "and", "or" and "nor" are require an array of objects', async function () {
529
+ const schema = createSchema();
530
+ schema.defineModel({name: 'model', datasource: 'mongodb'});
531
+ const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
532
+ const throwable = (k, v) => () => A._buildQuery('model', {[k]: v});
533
+ const error = (k, v) => {
534
+ const e = new InvalidOperatorValueError(k, 'an Array', v);
535
+ return e.message;
536
+ };
537
+ const testOf = v => {
538
+ expect(throwable('and', v)).to.throw(error('and', v));
539
+ expect(throwable('or', v)).to.throw(error('or', v));
540
+ expect(throwable('nor', v)).to.throw(error('nor', v));
541
+ };
542
+ testOf('str');
543
+ testOf('');
544
+ testOf(10);
545
+ testOf(0);
546
+ testOf(true);
547
+ testOf(false);
548
+ });
549
+ });
550
+
394
551
  describe('create', function () {
395
552
  it('generates a new identifier when a value of a primary key is not provided', async function () {
396
553
  const schema = createSchema();