@e22m4u/js-repository-mongodb-adapter
Version:
MongoDB адаптер для @e22m4u/js-repository
1,328 lines (1,246 loc) • 200 kB
JavaScript
import {expect} from 'chai';
import {ObjectId} from 'mongodb';
import {MongoClient} from 'mongodb';
import {format} from '@e22m4u/js-format';
import {DataType} from '@e22m4u/js-repository';
import {createMongodbUrl} from './utils/index.js';
import {MongodbAdapter} from './mongodb-adapter.js';
import {DatabaseSchema} from '@e22m4u/js-repository';
import {AdapterRegistry} from '@e22m4u/js-repository';
import {InvalidOperatorValueError} from '@e22m4u/js-repository';
import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '@e22m4u/js-repository';
const CONFIG = {
host: process.env.MONGODB_HOST || 'localhost',
port: process.env.MONGODB_PORT || 27017,
database: process.env.MONGODB_DATABASE,
};
const MDB_CLIENT = new MongoClient(createMongodbUrl(CONFIG));
const ADAPTERS_STACK = [];
function createSchema() {
const schema = new DatabaseSchema();
const adapter = new MongodbAdapter(schema.container, CONFIG);
ADAPTERS_STACK.push(adapter);
schema.defineDatasource({name: 'mongodb', adapter: 'mongodb'});
schema.getService(AdapterRegistry)._adapters['mongodb'] = adapter;
return schema;
}
describe('MongodbAdapter', function () {
this.timeout(15000);
afterEach(async function () {
await MDB_CLIENT.db(CONFIG.database).dropDatabase();
});
after(async function () {
for await (const adapter of ADAPTERS_STACK) {
await adapter.client.close(true);
}
await MDB_CLIENT.close(true);
});
describe('_buildProjection', function () {
describe('single field', function () {
it('returns undefined if the second argument is undefined', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', undefined);
expect(res).to.be.undefined;
});
it('returns undefined if the second argument is null', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', null);
expect(res).to.be.undefined;
});
it('requires the second argument to be a non-empty string', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const throwable = v => () => A._buildProjection('model', v);
const error = v =>
format(
'The provided option "fields" should be a non-empty String ' +
'or an Array of non-empty String, but %s given.',
v,
);
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable({})).to.throw(error('Object'));
expect(throwable('bar')()).to.be.eql({_id: 1, bar: 1});
expect(throwable(undefined)()).to.be.undefined;
expect(throwable(null)()).to.be.undefined;
});
it('converts the given property name to the column name', async function () {
const schema = createSchema();
schema.defineModel({
name: 'model',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.STRING,
columnName: 'bar',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', 'foo');
expect(res).to.be.eql({_id: 1, bar: 1});
});
it('converts property names chain to column names chain', async function () {
const schema = createSchema();
schema.defineModel({
name: 'modelA',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.OBJECT,
columnName: 'fooCol',
model: 'modelB',
},
},
});
schema.defineModel({
name: 'modelB',
properties: {
bar: {
type: DataType.OBJECT,
model: 'modelC',
},
},
});
schema.defineModel({
name: 'modelC',
properties: {
baz: {
type: DataType.OBJECT,
columnName: 'bazCol',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('modelA', 'foo.bar.baz.qux');
expect(res).to.be.eql({_id: 1, 'fooCol.bar.bazCol.qux': 1});
});
it('includes "_id" field to the projection', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', 'foo');
expect(res).to.be.eql({_id: 1, foo: 1});
});
it('includes "_id" as a column name of the given property', async function () {
const schema = createSchema();
schema.defineModel({
name: 'model',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.STRING,
primaryKey: true,
columnName: '_id',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', 'foo');
expect(res).to.be.eql({_id: 1});
});
});
describe('multiple fields', function () {
it('returns undefined if the second argument is an empty array', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', []);
expect(res).to.be.undefined;
});
it('requires the second argument to be an array of non-empty strings', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const throwable = v => () => A._buildProjection('model', v);
const error = v =>
format(
'The provided option "fields" should be a non-empty String ' +
'or an Array of non-empty String, but %s given.',
v,
);
expect(throwable([''])).to.throw(error('""'));
expect(throwable([10])).to.throw(error('10'));
expect(throwable([0])).to.throw(error('0'));
expect(throwable([true])).to.throw(error('true'));
expect(throwable([false])).to.throw(error('false'));
expect(throwable([{}])).to.throw(error('Object'));
expect(throwable([undefined])).to.throw(error('undefined'));
expect(throwable([null])).to.throw(error('null'));
expect(throwable([])()).to.be.undefined;
expect(throwable(['bar'])()).to.be.eql({_id: 1, bar: 1});
});
it('converts the given property names to column names', async function () {
const schema = createSchema();
schema.defineModel({
name: 'model',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.STRING,
columnName: 'bar',
},
baz: {
type: DataType.STRING,
columnName: 'qux',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', ['foo', 'baz']);
expect(res).to.be.eql({_id: 1, bar: 1, qux: 1});
});
it('converts property names chain to column names chain', async function () {
const schema = createSchema();
schema.defineModel({
name: 'modelA',
datasource: 'mongodb',
properties: {
foo1: {
type: DataType.OBJECT,
columnName: 'foo1Col',
model: 'modelB',
},
foo2: {
type: DataType.OBJECT,
columnName: 'foo2Col',
model: 'modelB',
},
},
});
schema.defineModel({
name: 'modelB',
properties: {
bar1: {
type: DataType.OBJECT,
model: 'modelC',
},
bar2: {
type: DataType.OBJECT,
model: 'modelC',
},
},
});
schema.defineModel({
name: 'modelC',
properties: {
baz1: {
type: DataType.OBJECT,
columnName: 'baz1Col',
},
baz2: {
type: DataType.OBJECT,
columnName: 'baz2Col',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('modelA', [
'foo1.bar1.baz1.qux1',
'foo2.bar2.baz2.qux2',
]);
expect(res).to.be.eql({
_id: 1,
'foo1Col.bar1.baz1Col.qux1': 1,
'foo2Col.bar2.baz2Col.qux2': 1,
});
});
it('includes "_id" field to the projection', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', ['foo', 'bar']);
expect(res).to.be.eql({_id: 1, foo: 1, bar: 1});
});
it('includes "_id" as a column name of the given property', async function () {
const schema = createSchema();
schema.defineModel({
name: 'model',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.STRING,
primaryKey: true,
columnName: '_id',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildProjection('model', ['foo', 'bar']);
expect(res).to.be.eql({_id: 1, bar: 1});
});
});
});
describe('_buildSort', function () {
describe('single field', function () {
it('returns undefined if the second argument is undefined', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', undefined);
expect(res).to.be.undefined;
});
it('returns undefined if the second argument is null', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', null);
expect(res).to.be.undefined;
});
it('requires the second argument to be a non-empty string', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const throwable = v => () => A._buildSort('model', v);
const error = v =>
format(
'The provided option "order" should be a non-empty String ' +
'or an Array of non-empty String, but %s given.',
v,
);
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable({})).to.throw(error('Object'));
expect(throwable('bar')()).to.be.eql({bar: 1});
expect(throwable(undefined)()).to.be.undefined;
expect(throwable(null)()).to.be.undefined;
});
it('uses ascending direction by default', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', 'foo');
expect(res).to.be.eql({foo: 1});
});
it('uses descending direction by the "DESC" flag', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', 'foo DESC');
expect(res).to.be.eql({foo: -1});
});
it('uses ascending direction by the "ASC" flag', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', 'foo ASC');
expect(res).to.be.eql({foo: 1});
});
it('converts the given property name to the column name', async function () {
const schema = createSchema();
schema.defineModel({
name: 'model',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.STRING,
columnName: 'bar',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res1 = A._buildSort('model', 'foo');
const res2 = A._buildSort('model', 'foo DESC');
const res3 = A._buildSort('model', 'foo ASC');
expect(res1).to.be.eql({bar: 1});
expect(res2).to.be.eql({bar: -1});
expect(res3).to.be.eql({bar: 1});
});
it('converts property names chain to column names chain', async function () {
const schema = createSchema();
schema.defineModel({
name: 'modelA',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.OBJECT,
columnName: 'fooCol',
model: 'modelB',
},
},
});
schema.defineModel({
name: 'modelB',
properties: {
bar: {
type: DataType.OBJECT,
model: 'modelC',
},
},
});
schema.defineModel({
name: 'modelC',
properties: {
baz: {
type: DataType.OBJECT,
columnName: 'bazCol',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res1 = A._buildSort('modelA', 'foo.bar.baz.qux');
const res2 = A._buildSort('modelA', 'foo.bar.baz.qux DESC');
const res3 = A._buildSort('modelA', 'foo.bar.baz.qux ASC');
expect(res1).to.be.eql({'fooCol.bar.bazCol.qux': 1});
expect(res2).to.be.eql({'fooCol.bar.bazCol.qux': -1});
expect(res3).to.be.eql({'fooCol.bar.bazCol.qux': 1});
});
});
describe('multiple fields', function () {
it('returns undefined if the second argument is an empty array', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', []);
expect(res).to.be.undefined;
});
it('requires the second argument to be an array of non-empty strings', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const throwable = v => () => A._buildSort('model', v);
const error = v =>
format(
'The provided option "order" should be a non-empty String ' +
'or an Array of non-empty String, but %s given.',
v,
);
expect(throwable([''])).to.throw(error('""'));
expect(throwable([10])).to.throw(error('10'));
expect(throwable([0])).to.throw(error('0'));
expect(throwable([true])).to.throw(error('true'));
expect(throwable([false])).to.throw(error('false'));
expect(throwable([{}])).to.throw(error('Object'));
expect(throwable([undefined])).to.throw(error('undefined'));
expect(throwable([null])).to.throw(error('null'));
expect(throwable([])()).to.be.undefined;
expect(throwable(['bar', 'baz'])()).to.be.eql({bar: 1, baz: 1});
});
it('uses ascending direction by default', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', ['foo', 'bar']);
expect(res).to.be.eql({foo: 1, bar: 1});
});
it('uses descending direction by the "DESC" flag', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', ['foo DESC', 'bar DESC']);
expect(res).to.be.eql({foo: -1, bar: -1});
});
it('uses ascending direction by the "ASC" flag', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', ['foo ASC', 'bar ASC']);
expect(res).to.be.eql({foo: 1, bar: 1});
});
it('uses multiple directions by the multiple fields', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res = A._buildSort('model', ['foo', 'bar DESC', 'baz ASC']);
expect(res).to.be.eql({foo: 1, bar: -1, baz: 1});
});
it('converts the given property names to column names', async function () {
const schema = createSchema();
schema.defineModel({
name: 'model',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.STRING,
columnName: 'bar',
},
baz: {
type: DataType.STRING,
columnName: 'qux',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res1 = A._buildSort('model', ['foo', 'baz']);
const res2 = A._buildSort('model', ['foo DESC', 'baz ASC']);
const res3 = A._buildSort('model', ['foo ASC', 'baz DESC']);
expect(res1).to.be.eql({bar: 1, qux: 1});
expect(res2).to.be.eql({bar: -1, qux: 1});
expect(res3).to.be.eql({bar: 1, qux: -1});
});
it('converts property names chain to column names chain', async function () {
const schema = createSchema();
schema.defineModel({
name: 'modelA',
datasource: 'mongodb',
properties: {
foo1: {
type: DataType.OBJECT,
columnName: 'foo1Col',
model: 'modelB',
},
foo2: {
type: DataType.OBJECT,
columnName: 'foo2Col',
model: 'modelB',
},
},
});
schema.defineModel({
name: 'modelB',
properties: {
bar1: {
type: DataType.OBJECT,
model: 'modelC',
},
bar2: {
type: DataType.OBJECT,
model: 'modelC',
},
},
});
schema.defineModel({
name: 'modelC',
properties: {
baz1: {
type: DataType.OBJECT,
columnName: 'baz1Col',
},
baz2: {
type: DataType.OBJECT,
columnName: 'baz2Col',
},
},
});
const A = await schema
.getService(AdapterRegistry)
.getAdapter('mongodb');
const res1 = A._buildSort('modelA', [
'foo1.bar1.baz1.qux1',
'foo2.bar2.baz2.qux2',
]);
const res2 = A._buildSort('modelA', [
'foo1.bar1.baz1.qux1 DESC',
'foo2.bar2.baz2.qux2 DESC',
]);
const res3 = A._buildSort('modelA', [
'foo1.bar1.baz1.qux1 ASC',
'foo2.bar2.baz2.qux2 ASC',
]);
expect(res1).to.be.eql({
'foo1Col.bar1.baz1Col.qux1': 1,
'foo2Col.bar2.baz2Col.qux2': 1,
});
expect(res2).to.be.eql({
'foo1Col.bar1.baz1Col.qux1': -1,
'foo2Col.bar2.baz2Col.qux2': -1,
});
expect(res3).to.be.eql({
'foo1Col.bar1.baz1Col.qux1': 1,
'foo2Col.bar2.baz2Col.qux2': 1,
});
});
});
});
describe('_buildQuery', function () {
it('requires the second argument to be an object', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const throwable = v => () => A._buildQuery('model', v);
const error = v =>
format(
'The provided option "where" should be an Object, but %s given.',
v,
);
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable({foo: 'bar'})()).to.be.eql({foo: 'bar'});
expect(throwable({})()).to.be.undefined;
expect(throwable(undefined)()).to.be.undefined;
expect(throwable(null)()).to.be.undefined;
});
it('converts the property names to column names', async function () {
const schema = createSchema();
schema.defineModel({
name: 'model',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.STRING,
columnName: 'bar',
},
baz: {
type: DataType.STRING,
columnName: 'qux',
},
},
});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', {foo: 'a1', baz: null});
expect(res).to.be.eql({bar: 'a1', qux: null});
});
it('converts property names chain to column names chain', async function () {
const schema = createSchema();
schema.defineModel({
name: 'modelA',
datasource: 'mongodb',
properties: {
foo: {
type: DataType.OBJECT,
columnName: 'fooCol',
model: 'modelB',
},
},
});
schema.defineModel({
name: 'modelB',
properties: {
bar: {
type: DataType.OBJECT,
model: 'modelC',
},
},
});
schema.defineModel({
name: 'modelC',
properties: {
baz: {
type: DataType.OBJECT,
columnName: 'bazCol',
},
},
});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('modelA', {'foo.bar.baz.qux': 10});
expect(res).to.be.eql({'fooCol.bar.bazCol.qux': 10});
});
it('throws an error when using "$" character', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const throwable = () => A._buildQuery('model', {$and: []});
expect(throwable).to.throw(
'The symbol "$" is not supported, but "$and" given.',
);
});
it('the "and" operator requires an array of objects', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const throwable = v => () => A._buildQuery('model', {and: v});
const error = v => {
v = v.replace(/"/g, '$');
const e = new InvalidOperatorValueError('and', 'an Array', v);
return e.message.replace(/"/g, '').replace(/\$/g, '"');
};
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
});
it('the "or" operator requires an array of objects', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const throwable = v => () => A._buildQuery('model', {or: v});
const error = v => {
v = v.replace(/"/g, '$');
const e = new InvalidOperatorValueError('or', 'an Array', v);
return e.message.replace(/"/g, '').replace(/\$/g, '"');
};
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
});
it('the "nor" operator requires an array of objects', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const throwable = v => () => A._buildQuery('model', {nor: v});
const error = v => {
v = v.replace(/"/g, '$');
const e = new InvalidOperatorValueError('nor', 'an Array', v);
return e.message.replace(/"/g, '').replace(/\$/g, '"');
};
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
});
it('does not include an empty value of "and" operator', async function () {
const input1 = {foo: 'a1', and: []};
const input2 = {foo: 'a2', and: undefined};
const input3 = {foo: 'a3', and: null};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res1 = A._buildQuery('model', input1);
const res2 = A._buildQuery('model', input2);
const res3 = A._buildQuery('model', input3);
expect(res1).to.be.eql({foo: 'a1'});
expect(res2).to.be.eql({foo: 'a2'});
expect(res3).to.be.eql({foo: 'a3'});
});
it('does not include an empty value of "or" operator', async function () {
const input1 = {foo: 'a1', or: []};
const input2 = {foo: 'a2', or: undefined};
const input3 = {foo: 'a3', or: null};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res1 = A._buildQuery('model', input1);
const res2 = A._buildQuery('model', input2);
const res3 = A._buildQuery('model', input3);
expect(res1).to.be.eql({foo: 'a1'});
expect(res2).to.be.eql({foo: 'a2'});
expect(res3).to.be.eql({foo: 'a3'});
});
it('does not include an empty value of "nor" operator', async function () {
const input1 = {foo: 'a1', nor: []};
const input2 = {foo: 'a2', nor: undefined};
const input3 = {foo: 'a3', nor: null};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res1 = A._buildQuery('model', input1);
const res2 = A._buildQuery('model', input2);
const res3 = A._buildQuery('model', input3);
expect(res1).to.be.eql({foo: 'a1'});
expect(res2).to.be.eql({foo: 'a2'});
expect(res3).to.be.eql({foo: 'a3'});
});
it('converts the "and" operator to "$and"', async function () {
const input = {and: [{foo: 'bar'}, {baz: 'qux'}]};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({$and: [{foo: 'bar'}, {baz: 'qux'}]});
});
it('converts the "or" operator to "$or"', async function () {
const input = {or: [{foo: 'bar'}, {baz: 'qux'}]};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({$or: [{foo: 'bar'}, {baz: 'qux'}]});
});
it('converts the "nor" operator to "$nor"', async function () {
const input = {nor: [{foo: 'bar'}, {baz: 'qux'}]};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({$nor: [{foo: 'bar'}, {baz: 'qux'}]});
});
it('converts the "eq" operator to "$eq"', async function () {
const input = {foo: {eq: 'bar'}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$eq: 'bar'}});
});
it('converts the "neq" operator to "$ne"', async function () {
const input = {foo: {neq: 'bar'}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$ne: 'bar'}});
});
it('converts the "gt" operator to "$gt"', async function () {
const input = {foo: {gt: 5}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$gt: 5}});
});
it('converts the "lt" operator to "$lt"', async function () {
const input = {foo: {lt: 5}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$lt: 5}});
});
it('converts the "gte" operator to "$gte"', async function () {
const input = {foo: {gte: 5}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$gte: 5}});
});
it('converts the "lte" operator to "$lte"', async function () {
const input = {foo: {lte: 5}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$lte: 5}});
});
it('converts the "inq" operator to "$in"', async function () {
const input = {foo: {inq: [1, 2, 3]}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$in: [1, 2, 3]}});
});
it('converts the "nin" operator to "$nin"', async function () {
const input = {foo: {nin: ['a', 'b', 'c']}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$nin: ['a', 'b', 'c']}});
});
it('converts the "between" operator to "$gte" and "$lte"', async function () {
const input = {foo: {between: [1, 10]}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
const expected = {foo: {$gte: 1, $lte: 10}};
expect(res).to.be.eql(expected);
});
it('converts the "exists" operator to "$exists"', async function () {
const input1 = {foo: {exists: true}};
const input2 = {foo: {exists: false}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res1 = A._buildQuery('model', input1);
const res2 = A._buildQuery('model', input2);
expect(res1).to.be.eql({foo: {$exists: true}});
expect(res2).to.be.eql({foo: {$exists: false}});
});
it('converts the "like" operator to "$regex"', async function () {
const input = {foo: {like: 'test'}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$regex: /test/}});
});
it('converts the "nlike" operator to "$not"', async function () {
const input = {foo: {nlike: 'test'}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$not: /test/}});
});
it('converts the "ilike" operator to "$regex" with "i" flag', async function () {
const input = {foo: {ilike: 'test'}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$regex: /test/i}});
});
it('converts the "nilike" operator to "$not" with "i" flag', async function () {
const input = {foo: {nilike: 'test'}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql({foo: {$not: /test/i}});
});
it('converts property value to an instance of ObjectId', async function () {
const oid = new ObjectId();
const id = String(oid);
const input = {foo: id};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res.foo).to.be.instanceof(ObjectId);
expect(res.foo).to.be.eql(oid);
});
it('the "eq" operator converts ObjectId string to an instance', async function () {
const oid = new ObjectId();
const id = oid.toString();
const input = {foo: {eq: id}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$eq: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(ObjectId);
expect(res).to.be.eql(oid);
});
it('the "neq" operator converts ObjectId string to an instance', async function () {
const oid = new ObjectId();
const id = oid.toString();
const input = {foo: {neq: id}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$ne: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(ObjectId);
expect(res).to.be.eql(oid);
});
it('the "inq" operator converts ObjectId string to an instance', async function () {
const oid = new ObjectId();
const id = oid.toString();
const input = {foo: {inq: [id]}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$in: res},
} = A._buildQuery('model', input);
expect(res[0]).to.be.instanceOf(ObjectId);
expect(res[0]).to.be.eql(oid);
});
it('the "nin" operator converts ObjectId string to an instance', async function () {
const oid = new ObjectId();
const id = oid.toString();
const input = {foo: {nin: [id]}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$nin: res},
} = A._buildQuery('model', input);
expect(res[0]).to.be.instanceOf(ObjectId);
expect(res[0]).to.be.eql(oid);
});
it('converts property value to an instance of Date', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: isoDate};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res.foo).to.be.instanceof(Date);
expect(res.foo).to.be.eql(date);
});
it('the "eq" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {eq: isoDate}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$eq: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(Date);
expect(res).to.be.eql(date);
});
it('the "neq" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {neq: isoDate}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$ne: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(Date);
expect(res).to.be.eql(date);
});
it('the "gt" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {gt: isoDate}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$gt: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(Date);
expect(res).to.be.eql(date);
});
it('the "lt" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {lt: isoDate}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$lt: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(Date);
expect(res).to.be.eql(date);
});
it('the "gte" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {gte: isoDate}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$gte: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(Date);
expect(res).to.be.eql(date);
});
it('the "lte" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {lte: isoDate}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$lte: res},
} = A._buildQuery('model', input);
expect(res).to.be.instanceOf(Date);
expect(res).to.be.eql(date);
});
it('the "inq" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {inq: [isoDate]}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$in: res},
} = A._buildQuery('model', input);
expect(res[0]).to.be.instanceOf(Date);
expect(res[0]).to.be.eql(date);
});
it('the "nin" operator converts Date string to an instance', async function () {
const date = new Date();
const isoDate = date.toISOString();
const input = {foo: {nin: [isoDate]}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$nin: res},
} = A._buildQuery('model', input);
expect(res[0]).to.be.instanceOf(Date);
expect(res[0]).to.be.eql(date);
});
it('the "between" operator converts Date string to an instance', async function () {
const date1 = new Date();
const date2 = new Date();
const isoDate1 = date1.toISOString();
const isoDate2 = date2.toISOString();
const input = {foo: {between: [isoDate1, isoDate2]}};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const {
foo: {$gte: res1, $lte: res2},
} = A._buildQuery('model', input);
expect(res1).to.be.instanceOf(Date);
expect(res1).to.be.eql(date1);
expect(res2).to.be.instanceOf(Date);
expect(res2).to.be.eql(date2);
});
it('combines the given operators by the "and" clause', async function () {
const input = {
foo: {
eq: 'bar',
neq: 'baz',
gt: 5,
lt: 10,
gte: 6,
lte: 9,
inq: ['qux'],
nin: ['qwe'],
between: [100, 200],
exists: true,
like: 'asd',
nlike: 'zxc',
ilike: 'rty',
nilike: 'fgh',
regexp: 'vbn',
flags: 'i',
},
};
const expected = {
$and: [
{foo: {$eq: 'bar'}},
{foo: {$ne: 'baz'}},
{foo: {$gt: 5}},
{foo: {$lt: 10}},
{foo: {$gte: 6}},
{foo: {$lte: 9}},
{foo: {$in: ['qux']}},
{foo: {$nin: ['qwe']}},
{foo: {$gte: 100, $lte: 200}},
{foo: {$exists: true}},
{foo: {$regex: /asd/}},
{foo: {$not: /zxc/}},
{foo: {$regex: /rty/i}},
{foo: {$not: /fgh/i}},
{foo: {$regex: /vbn/i}},
],
};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql(expected);
});
it('combines the given operators by the existing "and" clause', async function () {
const input = {
and: [{featured: true}, {removed: false}],
foo: {
eq: 'bar',
neq: 'baz',
gt: 5,
lt: 10,
gte: 6,
lte: 9,
inq: ['qux'],
nin: ['qwe'],
between: [100, 200],
exists: true,
like: 'asd',
nlike: 'zxc',
ilike: 'rty',
nilike: 'fgh',
regexp: 'vbn',
flags: 'i',
},
};
const expected = {
$and: [
{featured: true},
{removed: false},
{foo: {$eq: 'bar'}},
{foo: {$ne: 'baz'}},
{foo: {$gt: 5}},
{foo: {$lt: 10}},
{foo: {$gte: 6}},
{foo: {$lte: 9}},
{foo: {$in: ['qux']}},
{foo: {$nin: ['qwe']}},
{foo: {$gte: 100, $lte: 200}},
{foo: {$exists: true}},
{foo: {$regex: /asd/}},
{foo: {$not: /zxc/}},
{foo: {$regex: /rty/i}},
{foo: {$not: /fgh/i}},
{foo: {$regex: /vbn/i}},
],
};
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
const res = A._buildQuery('model', input);
expect(res).to.be.eql(expected);
});
});
describe('create', function () {
it('generates a new identifier when a value of a primary key is not provided', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const rep = schema.getRepository('model');
const result = await rep.create({foo: 'bar'});
expect(result).to.be.eql({[DEF_PK]: result[DEF_PK], foo: 'bar'});
expect(typeof result[DEF_PK]).to.be.eq('string');
expect(result[DEF_PK]).to.have.lengthOf(24);
});
it('generates a new identifier when a value of a primary key is undefined', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const rep = schema.getRepository('model');
const result = await rep.create({[DEF_PK]: undefined, foo: 'bar'});
expect(result).to.be.eql({[DEF_PK]: result[DEF_PK], foo: 'bar'});
expect(typeof result[DEF_PK]).to.be.eq('string');
expect(result[DEF_PK]).to.have.lengthOf(24);
});
it('generates a new identifier when a value of a primary key is null', async function () {
const schema = createSchema();
schema.defineModel({name: 'model', datasource: 'mongodb'});
const rep = schema.getRepository('model');
const result = await rep.create({[DEF_PK]: null, foo: 'bar'});
expect(result).to.be.eql({