@e22m4u/js-repository-mongodb-adapter 0.0.15 → 0.0.17
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 +1 -1
- package/src/mongodb-adapter.js +15 -128
- package/src/mongodb-adapter.spec.js +1 -10
- package/src/utils/create-mongodb-url.js +1 -1
- package/src/utils/create-mongodb-url.spec.js +256 -2
- package/src/utils/index.js +1 -0
- package/src/utils/is-iso-date.spec.js +31 -0
- package/src/utils/is-object-id.spec.js +6 -4
- package/src/utils/transform-values-deep.js +1 -1
- package/src/utils/transform-values-deep.spec.js +26 -3
package/package.json
CHANGED
package/src/mongodb-adapter.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint no-unused-vars: 0 */
|
|
2
2
|
import {ObjectId} from 'mongodb';
|
|
3
3
|
import {MongoClient} from 'mongodb';
|
|
4
|
+
import {isIsoDate} from './utils/index.js';
|
|
4
5
|
import {isObjectId} from './utils/index.js';
|
|
5
6
|
import {Adapter} from '@e22m4u/js-repository';
|
|
6
7
|
import {DataType} from '@e22m4u/js-repository';
|
|
7
|
-
import {isIsoDate} from './utils/is-iso-date.js';
|
|
8
8
|
import {capitalize} from '@e22m4u/js-repository';
|
|
9
9
|
import {createMongodbUrl} from './utils/index.js';
|
|
10
10
|
import {ServiceContainer} from '@e22m4u/js-service';
|
|
@@ -71,12 +71,11 @@ const MONGODB_OPTION_NAMES = [
|
|
|
71
71
|
/**
|
|
72
72
|
* Default settings.
|
|
73
73
|
*
|
|
74
|
-
* @type {
|
|
74
|
+
* @type {object}
|
|
75
75
|
*/
|
|
76
76
|
const DEFAULT_SETTINGS = {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
serverSelectionTimeoutMS: 2000,
|
|
77
|
+
// connectTimeoutMS: 2500,
|
|
78
|
+
// serverSelectionTimeoutMS: 2500,
|
|
80
79
|
};
|
|
81
80
|
|
|
82
81
|
/**
|
|
@@ -86,51 +85,27 @@ export class MongodbAdapter extends Adapter {
|
|
|
86
85
|
/**
|
|
87
86
|
* Mongodb instance.
|
|
88
87
|
*
|
|
88
|
+
* @type {MongoClient}
|
|
89
89
|
* @private
|
|
90
90
|
*/
|
|
91
91
|
_client;
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* @type {Map<any, any>}
|
|
97
|
-
* @private
|
|
98
|
-
*/
|
|
99
|
-
_collections = new Map();
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Connected.
|
|
94
|
+
* Client.
|
|
103
95
|
*
|
|
104
|
-
* @
|
|
105
|
-
* @private
|
|
96
|
+
* @returns {MongoClient}
|
|
106
97
|
*/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Connected.
|
|
111
|
-
*
|
|
112
|
-
* @return {boolean}
|
|
113
|
-
*/
|
|
114
|
-
get connected() {
|
|
115
|
-
return this._connected;
|
|
98
|
+
get client() {
|
|
99
|
+
return this._client;
|
|
116
100
|
}
|
|
117
101
|
|
|
118
102
|
/**
|
|
119
|
-
*
|
|
103
|
+
* Collections.
|
|
120
104
|
*
|
|
121
|
-
* @type {
|
|
105
|
+
* @type {Map<any, any>}
|
|
122
106
|
* @private
|
|
123
107
|
*/
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Connecting.
|
|
128
|
-
*
|
|
129
|
-
* @return {boolean}
|
|
130
|
-
*/
|
|
131
|
-
get connecting() {
|
|
132
|
-
return this._connecting;
|
|
133
|
-
}
|
|
108
|
+
_collections = new Map();
|
|
134
109
|
|
|
135
110
|
/**
|
|
136
111
|
* Constructor.
|
|
@@ -140,80 +115,14 @@ export class MongodbAdapter extends Adapter {
|
|
|
140
115
|
*/
|
|
141
116
|
constructor(container, settings) {
|
|
142
117
|
settings = Object.assign({}, DEFAULT_SETTINGS, settings || {});
|
|
143
|
-
super(container, settings);
|
|
144
118
|
settings.protocol = settings.protocol || 'mongodb';
|
|
145
119
|
settings.hostname = settings.hostname || settings.host || '127.0.0.1';
|
|
146
120
|
settings.port = settings.port || 27017;
|
|
147
|
-
settings.database = settings.database || settings.db || '
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Connect.
|
|
152
|
-
*
|
|
153
|
-
* @return {Promise<*|undefined>}
|
|
154
|
-
* @private
|
|
155
|
-
*/
|
|
156
|
-
async connect() {
|
|
157
|
-
if (this._connecting) {
|
|
158
|
-
const tryAgainAfter = 500;
|
|
159
|
-
await new Promise(r => setTimeout(() => r(), tryAgainAfter));
|
|
160
|
-
return this.connect();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (this._connected) return;
|
|
164
|
-
this._connecting = true;
|
|
165
|
-
|
|
121
|
+
settings.database = settings.database || settings.db || 'database';
|
|
122
|
+
super(container, settings);
|
|
166
123
|
const options = selectObjectKeys(this.settings, MONGODB_OPTION_NAMES);
|
|
167
124
|
const url = createMongodbUrl(this.settings);
|
|
168
|
-
|
|
169
|
-
// console.log(`Connecting to ${url}`);
|
|
170
125
|
this._client = new MongoClient(url, options);
|
|
171
|
-
|
|
172
|
-
const {reconnectInterval} = this.settings;
|
|
173
|
-
const connectFn = async () => {
|
|
174
|
-
try {
|
|
175
|
-
await this._client.connect();
|
|
176
|
-
} catch (e) {
|
|
177
|
-
console.error(e);
|
|
178
|
-
// console.log('MongoDB connection failed!');
|
|
179
|
-
// console.log(`Reconnecting after ${reconnectInterval} ms.`);
|
|
180
|
-
await new Promise(r => setTimeout(() => r(), reconnectInterval));
|
|
181
|
-
return connectFn();
|
|
182
|
-
}
|
|
183
|
-
// console.log('MongoDB is connected.');
|
|
184
|
-
this._connected = true;
|
|
185
|
-
this._connecting = false;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
await connectFn();
|
|
189
|
-
|
|
190
|
-
this._client.once('serverClosed', event => {
|
|
191
|
-
if (this._connected) {
|
|
192
|
-
this._connected = false;
|
|
193
|
-
// console.log('MongoDB lost connection!');
|
|
194
|
-
console.log(event);
|
|
195
|
-
// console.log(`Reconnecting after ${reconnectInterval} ms.`);
|
|
196
|
-
setTimeout(() => connectFn(), reconnectInterval);
|
|
197
|
-
} else {
|
|
198
|
-
// console.log('MongoDB connection closed.');
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Disconnect.
|
|
205
|
-
*
|
|
206
|
-
* @return {Promise<*|undefined>}
|
|
207
|
-
*/
|
|
208
|
-
async disconnect() {
|
|
209
|
-
if (this._connecting) {
|
|
210
|
-
const tryAgainAfter = 500;
|
|
211
|
-
await new Promise(r => setTimeout(() => r(), tryAgainAfter));
|
|
212
|
-
return this.disconnect();
|
|
213
|
-
}
|
|
214
|
-
if (!this._connected) return;
|
|
215
|
-
this._connected = false;
|
|
216
|
-
if (this._client) await this._client.close();
|
|
217
126
|
}
|
|
218
127
|
|
|
219
128
|
/**
|
|
@@ -251,19 +160,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
251
160
|
return value;
|
|
252
161
|
}
|
|
253
162
|
|
|
254
|
-
/**
|
|
255
|
-
* Coerce iso date.
|
|
256
|
-
*
|
|
257
|
-
* @param value
|
|
258
|
-
* @return {*|Date}
|
|
259
|
-
* @private
|
|
260
|
-
*/
|
|
261
|
-
_coerceIsoDate(value) {
|
|
262
|
-
if (value === null) return value;
|
|
263
|
-
if (isIsoDate(value)) return new Date(value);
|
|
264
|
-
return value;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
163
|
/**
|
|
268
164
|
* To database.
|
|
269
165
|
*
|
|
@@ -344,7 +240,7 @@ export class MongodbAdapter extends Adapter {
|
|
|
344
240
|
if (collection) return collection;
|
|
345
241
|
const tableName =
|
|
346
242
|
this.getService(ModelDefinitionUtils).getTableNameByModelName(modelName);
|
|
347
|
-
collection = this.
|
|
243
|
+
collection = this.client.db(this.settings.database).collection(tableName);
|
|
348
244
|
this._collections.set(modelName, collection);
|
|
349
245
|
return collection;
|
|
350
246
|
}
|
|
@@ -641,7 +537,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
641
537
|
* @return {Promise<object>}
|
|
642
538
|
*/
|
|
643
539
|
async create(modelName, modelData, filter = undefined) {
|
|
644
|
-
await this.connect();
|
|
645
540
|
const idPropName = this._getIdPropName(modelName);
|
|
646
541
|
const idValue = modelData[idPropName];
|
|
647
542
|
if (idValue == null) {
|
|
@@ -677,7 +572,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
677
572
|
* @return {Promise<object>}
|
|
678
573
|
*/
|
|
679
574
|
async replaceById(modelName, id, modelData, filter = undefined) {
|
|
680
|
-
await this.connect();
|
|
681
575
|
id = this._coerceId(id);
|
|
682
576
|
const idPropName = this._getIdPropName(modelName);
|
|
683
577
|
modelData[idPropName] = id;
|
|
@@ -704,7 +598,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
704
598
|
* @return {Promise<object>}
|
|
705
599
|
*/
|
|
706
600
|
async patchById(modelName, id, modelData, filter = undefined) {
|
|
707
|
-
await this.connect();
|
|
708
601
|
id = this._coerceId(id);
|
|
709
602
|
const idPropName = this._getIdPropName(modelName);
|
|
710
603
|
delete modelData[idPropName];
|
|
@@ -729,7 +622,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
729
622
|
* @return {Promise<object[]>}
|
|
730
623
|
*/
|
|
731
624
|
async find(modelName, filter = undefined) {
|
|
732
|
-
await this.connect();
|
|
733
625
|
filter = filter || {};
|
|
734
626
|
const query = this._buildQuery(modelName, filter.where);
|
|
735
627
|
const sort = this._buildSort(modelName, filter.order);
|
|
@@ -751,7 +643,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
751
643
|
* @return {Promise<object>}
|
|
752
644
|
*/
|
|
753
645
|
async findById(modelName, id, filter = undefined) {
|
|
754
|
-
await this.connect();
|
|
755
646
|
id = this._coerceId(id);
|
|
756
647
|
const table = this._getCollection(modelName);
|
|
757
648
|
const projection = this._buildProjection(
|
|
@@ -772,7 +663,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
772
663
|
* @return {Promise<number>}
|
|
773
664
|
*/
|
|
774
665
|
async delete(modelName, where = undefined) {
|
|
775
|
-
await this.connect();
|
|
776
666
|
const table = this._getCollection(modelName);
|
|
777
667
|
const query = this._buildQuery(modelName, where);
|
|
778
668
|
const {deletedCount} = await table.deleteMany(query);
|
|
@@ -787,7 +677,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
787
677
|
* @return {Promise<boolean>}
|
|
788
678
|
*/
|
|
789
679
|
async deleteById(modelName, id) {
|
|
790
|
-
await this.connect();
|
|
791
680
|
id = this._coerceId(id);
|
|
792
681
|
const table = this._getCollection(modelName);
|
|
793
682
|
const {deletedCount} = await table.deleteOne({_id: id});
|
|
@@ -802,7 +691,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
802
691
|
* @return {Promise<boolean>}
|
|
803
692
|
*/
|
|
804
693
|
async exists(modelName, id) {
|
|
805
|
-
await this.connect();
|
|
806
694
|
id = this._coerceId(id);
|
|
807
695
|
const table = this._getCollection(modelName);
|
|
808
696
|
const result = await table.findOne({_id: id}, {});
|
|
@@ -817,7 +705,6 @@ export class MongodbAdapter extends Adapter {
|
|
|
817
705
|
* @return {Promise<number>}
|
|
818
706
|
*/
|
|
819
707
|
async count(modelName, where = undefined) {
|
|
820
|
-
await this.connect();
|
|
821
708
|
const query = this._buildQuery(modelName, where);
|
|
822
709
|
const table = this._getCollection(modelName);
|
|
823
710
|
return await table.count(query);
|
|
@@ -2,7 +2,6 @@ import {expect} from 'chai';
|
|
|
2
2
|
import {ObjectId} from 'mongodb';
|
|
3
3
|
import {MongoClient} from 'mongodb';
|
|
4
4
|
import {format} from '@e22m4u/js-format';
|
|
5
|
-
import {Service} from '@e22m4u/js-service';
|
|
6
5
|
import {Schema} from '@e22m4u/js-repository';
|
|
7
6
|
import {DataType} from '@e22m4u/js-repository';
|
|
8
7
|
import {createMongodbUrl} from './utils/index.js';
|
|
@@ -37,19 +36,11 @@ describe('MongodbAdapter', function () {
|
|
|
37
36
|
|
|
38
37
|
after(async function () {
|
|
39
38
|
for await (const adapter of ADAPTERS_STACK) {
|
|
40
|
-
await adapter.
|
|
39
|
+
await adapter.client.close(true);
|
|
41
40
|
}
|
|
42
41
|
await MDB_CLIENT.close(true);
|
|
43
42
|
});
|
|
44
43
|
|
|
45
|
-
it('able to connect and disconnect', async function () {
|
|
46
|
-
const S = new Service();
|
|
47
|
-
const adapter = new MongodbAdapter(S.container, CONFIG);
|
|
48
|
-
await adapter.connect();
|
|
49
|
-
expect(adapter.connected).to.be.true;
|
|
50
|
-
await adapter.disconnect();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
44
|
describe('create', function () {
|
|
54
45
|
it('generates a new identifier when a value of a primary key is not provided', async function () {
|
|
55
46
|
const schema = createSchema();
|
|
@@ -11,7 +11,7 @@ export function createMongodbUrl(options = {}) {
|
|
|
11
11
|
);
|
|
12
12
|
if (options.protocol && typeof options.protocol !== 'string')
|
|
13
13
|
throw new InvalidArgumentError(
|
|
14
|
-
'MongoDB option "protocol" must be a
|
|
14
|
+
'MongoDB option "protocol" must be a String, but %v given.',
|
|
15
15
|
options.protocol,
|
|
16
16
|
);
|
|
17
17
|
if (options.hostname && typeof options.hostname !== 'string')
|
|
@@ -8,11 +8,12 @@ describe('createMongodbUrl', function () {
|
|
|
8
8
|
expect(value).to.be.eq('mongodb://127.0.0.1:27017/database');
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
it('
|
|
11
|
+
it('requires the first argument to be an object', function () {
|
|
12
12
|
const throwable = v => () => createMongodbUrl(v);
|
|
13
13
|
const error = v =>
|
|
14
14
|
format(
|
|
15
|
-
'The first argument of "createMongodbUrl" must be
|
|
15
|
+
'The first argument of "createMongodbUrl" must be ' +
|
|
16
|
+
'an Object, but %s given.',
|
|
16
17
|
v,
|
|
17
18
|
);
|
|
18
19
|
expect(throwable('')).to.throw(error('""'));
|
|
@@ -26,4 +27,257 @@ describe('createMongodbUrl', function () {
|
|
|
26
27
|
throwable(undefined)();
|
|
27
28
|
throwable({})();
|
|
28
29
|
});
|
|
30
|
+
|
|
31
|
+
it('requires the "protocol" option to be a string', function () {
|
|
32
|
+
const throwable = v => () => createMongodbUrl({protocol: v});
|
|
33
|
+
const error = v =>
|
|
34
|
+
format(
|
|
35
|
+
'MongoDB option "protocol" must be ' + 'a String, but %s given.',
|
|
36
|
+
v,
|
|
37
|
+
);
|
|
38
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
39
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
40
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
41
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
42
|
+
throwable('mongodb')();
|
|
43
|
+
throwable('')();
|
|
44
|
+
throwable(0)();
|
|
45
|
+
throwable(false)();
|
|
46
|
+
throwable(undefined)();
|
|
47
|
+
throwable(null)();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('requires the "hostname" option to be a string', function () {
|
|
51
|
+
const throwable = v => () => createMongodbUrl({hostname: v});
|
|
52
|
+
const error = v =>
|
|
53
|
+
format(
|
|
54
|
+
'MongoDB option "hostname" must be ' + 'a String, but %s given.',
|
|
55
|
+
v,
|
|
56
|
+
);
|
|
57
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
58
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
59
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
60
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
61
|
+
throwable('127.0.0.1')();
|
|
62
|
+
throwable('')();
|
|
63
|
+
throwable(0)();
|
|
64
|
+
throwable(false)();
|
|
65
|
+
throwable(undefined)();
|
|
66
|
+
throwable(null)();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('requires the "host" option to be a string', function () {
|
|
70
|
+
const throwable = v => () => createMongodbUrl({host: v});
|
|
71
|
+
const error = v =>
|
|
72
|
+
format('MongoDB option "host" must be ' + 'a String, but %s given.', v);
|
|
73
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
74
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
75
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
76
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
77
|
+
throwable('127.0.0.1')();
|
|
78
|
+
throwable('')();
|
|
79
|
+
throwable(0)();
|
|
80
|
+
throwable(false)();
|
|
81
|
+
throwable(undefined)();
|
|
82
|
+
throwable(null)();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('requires the "port" option to be a number or a string', function () {
|
|
86
|
+
const throwable = v => () => createMongodbUrl({port: v});
|
|
87
|
+
const error = v =>
|
|
88
|
+
format(
|
|
89
|
+
'MongoDB option "port" must be a Number ' +
|
|
90
|
+
'or a String, but %s given.',
|
|
91
|
+
v,
|
|
92
|
+
);
|
|
93
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
94
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
95
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
96
|
+
throwable('127.0.0.1')();
|
|
97
|
+
throwable('')();
|
|
98
|
+
throwable(10)();
|
|
99
|
+
throwable(0)();
|
|
100
|
+
throwable(false)();
|
|
101
|
+
throwable(undefined)();
|
|
102
|
+
throwable(null)();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('requires the "database" option to be a string', function () {
|
|
106
|
+
const throwable = v => () => createMongodbUrl({database: v});
|
|
107
|
+
const error = v =>
|
|
108
|
+
format(
|
|
109
|
+
'MongoDB option "database" must be ' + 'a String, but %s given.',
|
|
110
|
+
v,
|
|
111
|
+
);
|
|
112
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
113
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
114
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
115
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
116
|
+
throwable('database')();
|
|
117
|
+
throwable('')();
|
|
118
|
+
throwable(0)();
|
|
119
|
+
throwable(false)();
|
|
120
|
+
throwable(undefined)();
|
|
121
|
+
throwable(null)();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('requires the "db" option to be a string', function () {
|
|
125
|
+
const throwable = v => () => createMongodbUrl({db: v});
|
|
126
|
+
const error = v =>
|
|
127
|
+
format('MongoDB option "db" must be ' + 'a String, but %s given.', v);
|
|
128
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
129
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
130
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
131
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
132
|
+
throwable('database')();
|
|
133
|
+
throwable('')();
|
|
134
|
+
throwable(0)();
|
|
135
|
+
throwable(false)();
|
|
136
|
+
throwable(undefined)();
|
|
137
|
+
throwable(null)();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('requires the "username" option to be a string', function () {
|
|
141
|
+
const throwable = v => () => createMongodbUrl({username: v});
|
|
142
|
+
const error = v =>
|
|
143
|
+
format(
|
|
144
|
+
'MongoDB option "username" must be ' + 'a String, but %s given.',
|
|
145
|
+
v,
|
|
146
|
+
);
|
|
147
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
148
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
149
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
150
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
151
|
+
throwable('username')();
|
|
152
|
+
throwable('')();
|
|
153
|
+
throwable(0)();
|
|
154
|
+
throwable(false)();
|
|
155
|
+
throwable(undefined)();
|
|
156
|
+
throwable(null)();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('requires the "password" option to be a string or a number', function () {
|
|
160
|
+
const throwable = v => () => createMongodbUrl({password: v});
|
|
161
|
+
const error = v =>
|
|
162
|
+
format(
|
|
163
|
+
'MongoDB option "password" must be a String ' +
|
|
164
|
+
'or a Number, but %s given.',
|
|
165
|
+
v,
|
|
166
|
+
);
|
|
167
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
168
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
169
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
170
|
+
throwable('password')();
|
|
171
|
+
throwable('')();
|
|
172
|
+
throwable(10)();
|
|
173
|
+
throwable(0)();
|
|
174
|
+
throwable(false)();
|
|
175
|
+
throwable(undefined)();
|
|
176
|
+
throwable(null)();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('requires the "pass" option to be a string or a number', function () {
|
|
180
|
+
const throwable = v => () => createMongodbUrl({pass: v});
|
|
181
|
+
const error = v =>
|
|
182
|
+
format(
|
|
183
|
+
'MongoDB option "pass" must be a String ' +
|
|
184
|
+
'or a Number, but %s given.',
|
|
185
|
+
v,
|
|
186
|
+
);
|
|
187
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
188
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
189
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
190
|
+
throwable('pass')();
|
|
191
|
+
throwable('')();
|
|
192
|
+
throwable(10)();
|
|
193
|
+
throwable(0)();
|
|
194
|
+
throwable(false)();
|
|
195
|
+
throwable(undefined)();
|
|
196
|
+
throwable(null)();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('sets the given "protocol" option', function () {
|
|
200
|
+
const res = createMongodbUrl({protocol: 'value'});
|
|
201
|
+
expect(res).to.be.eq('value://127.0.0.1:27017/database');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('sets the given "hostname" option', function () {
|
|
205
|
+
const res = createMongodbUrl({hostname: 'value'});
|
|
206
|
+
expect(res).to.be.eq('mongodb://value:27017/database');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('sets the given "host" option', function () {
|
|
210
|
+
const res = createMongodbUrl({host: 'value'});
|
|
211
|
+
expect(res).to.be.eq('mongodb://value:27017/database');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('sets the given "port" option as a number', function () {
|
|
215
|
+
const res = createMongodbUrl({port: 8080});
|
|
216
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:8080/database');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('sets the given "port" option as a string', function () {
|
|
220
|
+
const res = createMongodbUrl({port: '8080'});
|
|
221
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:8080/database');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('sets the given "database" option', function () {
|
|
225
|
+
const res = createMongodbUrl({database: 'value'});
|
|
226
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:27017/value');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('sets the given "db" option', function () {
|
|
230
|
+
const res = createMongodbUrl({db: 'value'});
|
|
231
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:27017/value');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('does not use the provided "username" option without a password', function () {
|
|
235
|
+
const res = createMongodbUrl({username: 'value'});
|
|
236
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('does not use the provided "user" option without a password', function () {
|
|
240
|
+
const res = createMongodbUrl({username: 'value'});
|
|
241
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('does not use the provided "password" option without a username', function () {
|
|
245
|
+
const res = createMongodbUrl({password: 'value'});
|
|
246
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('does not use the provided "pass" option without a username', function () {
|
|
250
|
+
const res = createMongodbUrl({pass: 'value'});
|
|
251
|
+
expect(res).to.be.eq('mongodb://127.0.0.1:27017/database');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('sets the given "username" and "password" option', function () {
|
|
255
|
+
const res = createMongodbUrl({username: 'usr', password: 'pwd'});
|
|
256
|
+
expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('sets the given "username" and "pass" option', function () {
|
|
260
|
+
const res = createMongodbUrl({username: 'usr', pass: 'pwd'});
|
|
261
|
+
expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('sets the given "user" and "password" option', function () {
|
|
265
|
+
const res = createMongodbUrl({user: 'usr', password: 'pwd'});
|
|
266
|
+
expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('sets the given "user" and "pass" option', function () {
|
|
270
|
+
const res = createMongodbUrl({user: 'usr', pass: 'pwd'});
|
|
271
|
+
expect(res).to.be.eq('mongodb://usr:pwd@127.0.0.1:27017/database');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('does not use the default "port" option for "mongodb+srv" protocol', function () {
|
|
275
|
+
const res = createMongodbUrl({protocol: 'mongodb+srv'});
|
|
276
|
+
expect(res).to.be.eq('mongodb+srv://127.0.0.1/database');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('does not use the provided "port" option for "mongodb+srv" protocol', function () {
|
|
280
|
+
const res = createMongodbUrl({protocol: 'mongodb+srv', port: 8080});
|
|
281
|
+
expect(res).to.be.eq('mongodb+srv://127.0.0.1/database');
|
|
282
|
+
});
|
|
29
283
|
});
|
package/src/utils/index.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {isIsoDate} from './is-iso-date.js';
|
|
3
|
+
|
|
4
|
+
describe('isIsoDate', function () {
|
|
5
|
+
it('returns false for an empty value', function () {
|
|
6
|
+
expect(isIsoDate('')).to.be.false;
|
|
7
|
+
expect(isIsoDate(0)).to.be.false;
|
|
8
|
+
expect(isIsoDate(false)).to.be.false;
|
|
9
|
+
expect(isIsoDate(undefined)).to.be.false;
|
|
10
|
+
expect(isIsoDate(null)).to.be.false;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns false for invalid values', function () {
|
|
14
|
+
expect(isIsoDate(10)).to.be.false;
|
|
15
|
+
expect(isIsoDate([])).to.be.false;
|
|
16
|
+
expect(isIsoDate({})).to.be.false;
|
|
17
|
+
expect(isIsoDate(new Map())).to.be.false;
|
|
18
|
+
expect(isIsoDate(NaN)).to.be.false;
|
|
19
|
+
expect(isIsoDate(Infinity)).to.be.false;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('returns true for the Date instance', function () {
|
|
23
|
+
expect(isIsoDate(new Date())).to.be.true;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('validates ISO string', function () {
|
|
27
|
+
expect(isIsoDate('2011-10-05T14:48:00.000Z')).to.be.true;
|
|
28
|
+
expect(isIsoDate('2018-11-10T11:22:33+00:00')).to.be.false;
|
|
29
|
+
expect(isIsoDate('2011-10-05T14:99:00.000Z')).to.be.false;
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {expect} from 'chai';
|
|
2
|
-
import {isObjectId} from './is-object-id.js';
|
|
3
2
|
import {ObjectId} from 'mongodb';
|
|
3
|
+
import {isObjectId} from './is-object-id.js';
|
|
4
4
|
|
|
5
5
|
describe('isObjectId', function () {
|
|
6
6
|
it('returns true for a valid ObjectId string or an instance', function () {
|
|
7
|
+
expect(isObjectId(new ObjectId())).to.be.true;
|
|
8
|
+
expect(isObjectId(String(new ObjectId()))).to.be.true;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('returns false for invalid values', function () {
|
|
7
12
|
expect(isObjectId('')).to.be.false;
|
|
8
13
|
expect(isObjectId('123')).to.be.false;
|
|
9
14
|
expect(isObjectId(0)).to.be.false;
|
|
@@ -17,8 +22,5 @@ describe('isObjectId', function () {
|
|
|
17
22
|
expect(isObjectId(new Date())).to.be.false;
|
|
18
23
|
expect(isObjectId(null)).to.be.false;
|
|
19
24
|
expect(isObjectId(undefined)).to.be.false;
|
|
20
|
-
//
|
|
21
|
-
expect(isObjectId(new ObjectId())).to.be.true;
|
|
22
|
-
expect(isObjectId(String(new ObjectId()))).to.be.true;
|
|
23
25
|
});
|
|
24
26
|
});
|
|
@@ -11,7 +11,7 @@ export function transformValuesDeep(value, transformer) {
|
|
|
11
11
|
if (!transformer || typeof transformer !== 'function')
|
|
12
12
|
throw new InvalidArgumentError(
|
|
13
13
|
'The second argument of "transformValuesDeep" ' +
|
|
14
|
-
'must be a Function, but %
|
|
14
|
+
'must be a Function, but %v given.',
|
|
15
15
|
transformer,
|
|
16
16
|
);
|
|
17
17
|
if (Array.isArray(value)) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {expect} from 'chai';
|
|
2
|
+
import {format} from '@e22m4u/js-format';
|
|
2
3
|
import {transformValuesDeep} from './transform-values-deep.js';
|
|
3
4
|
|
|
4
5
|
describe('transformValuesDeep', function () {
|
|
5
|
-
it('transforms property values of
|
|
6
|
+
it('transforms property values of the given object', function () {
|
|
6
7
|
const object = {
|
|
7
8
|
foo: 1,
|
|
8
9
|
bar: {
|
|
@@ -24,13 +25,13 @@ describe('transformValuesDeep', function () {
|
|
|
24
25
|
});
|
|
25
26
|
});
|
|
26
27
|
|
|
27
|
-
it('transforms elements of
|
|
28
|
+
it('transforms elements of the given array', function () {
|
|
28
29
|
const object = [1, 2, 3, [4, 5, 6, [7, 8, 9]]];
|
|
29
30
|
const result = transformValuesDeep(object, String);
|
|
30
31
|
expect(result).to.be.eql(['1', '2', '3', ['4', '5', '6', ['7', '8', '9']]]);
|
|
31
32
|
});
|
|
32
33
|
|
|
33
|
-
it('transforms
|
|
34
|
+
it('transforms the Date instance', function () {
|
|
34
35
|
const date = new Date();
|
|
35
36
|
const str = String(date);
|
|
36
37
|
const result1 = transformValuesDeep(date, String);
|
|
@@ -44,4 +45,26 @@ describe('transformValuesDeep', function () {
|
|
|
44
45
|
expect(result4).to.be.eql({date: str});
|
|
45
46
|
expect(result5).to.be.eql({foo: {date: str}});
|
|
46
47
|
});
|
|
48
|
+
|
|
49
|
+
it('requires the second argument to be a function', function () {
|
|
50
|
+
const throwable = v => () => transformValuesDeep('val', v);
|
|
51
|
+
const error = v =>
|
|
52
|
+
format(
|
|
53
|
+
'The second argument of "transformValuesDeep" ' +
|
|
54
|
+
'must be a Function, but %s given.',
|
|
55
|
+
v,
|
|
56
|
+
);
|
|
57
|
+
expect(throwable('str')).to.throw(error('"str"'));
|
|
58
|
+
expect(throwable('')).to.throw(error('""'));
|
|
59
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
60
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
61
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
62
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
63
|
+
expect(throwable([])).to.throw(error('Array'));
|
|
64
|
+
expect(throwable({})).to.throw(error('Object'));
|
|
65
|
+
expect(throwable(undefined)).to.throw(error('undefined'));
|
|
66
|
+
expect(throwable(null)).to.throw(error('null'));
|
|
67
|
+
throwable(() => undefined)();
|
|
68
|
+
throwable(function () {})();
|
|
69
|
+
});
|
|
47
70
|
});
|