@toa.io/storages.mongodb 1.0.0-alpha.31 → 1.0.0-alpha.310
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/CHANGELOG.md +203 -0
- package/package.json +10 -9
- package/readme.md +15 -0
- package/src/client.js +173 -39
- package/src/factory.js +6 -12
- package/src/inbox.js +85 -0
- package/src/index.js +1 -7
- package/src/indexes.js +84 -0
- package/src/measurements.js +39 -0
- package/src/migrations.js +343 -0
- package/src/outbox.js +131 -0
- package/src/record.js +88 -20
- package/src/storage.js +497 -130
- package/src/translate/criteria.js +25 -16
- package/src/translate/options.js +17 -11
- package/src/translate/rename.js +1 -5
- package/src/translate.js +14 -15
- package/test/migrations.test.js +388 -0
- package/test/query/criteria.fixtures.js +2 -7
- package/test/query/criteria.test.js +5 -4
- package/test/record.test.js +98 -25
- package/test/storage.test.js +217 -0
- package/test/translate.test.js +59 -7
- package/types/connection.d.ts +18 -12
- package/types/pointer.d.ts +4 -6
- package/types/record.d.ts +5 -7
- package/src/collection.js +0 -69
- package/src/deployment.js +0 -22
package/test/record.test.js
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
to,
|
|
5
|
-
from
|
|
6
|
-
} = require('../src/record')
|
|
4
|
+
import { to, from, codec } from '../src/record.js'
|
|
7
5
|
|
|
8
6
|
describe('to', () => {
|
|
9
7
|
it('should rename id to _id', () => {
|
|
10
|
-
/** @type {toa.core.storages.Record} */
|
|
8
|
+
/** @type {import('@toa.io/core/types').storages.Record} */
|
|
11
9
|
const entity = {
|
|
12
10
|
id: '1',
|
|
13
|
-
|
|
11
|
+
VERSION: 0
|
|
14
12
|
}
|
|
15
13
|
const record = to(entity)
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
assert.partialDeepStrictEqual(record, { _id: '1' })
|
|
18
16
|
})
|
|
19
17
|
|
|
20
18
|
it('should not modify argument', () => {
|
|
21
|
-
/** @type {toa.core.storages.Record} */
|
|
19
|
+
/** @type {import('@toa.io/core/types').storages.Record} */
|
|
22
20
|
const entity = {
|
|
23
21
|
id: '1',
|
|
24
|
-
|
|
22
|
+
VERSION: 0
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
to(entity)
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
assert.deepStrictEqual(entity, {
|
|
30
28
|
id: '1',
|
|
31
|
-
|
|
29
|
+
VERSION: 0
|
|
32
30
|
})
|
|
33
31
|
})
|
|
34
32
|
})
|
|
@@ -38,28 +36,103 @@ describe('from', () => {
|
|
|
38
36
|
/** @type {toa.mongodb.Record} */
|
|
39
37
|
const record = {
|
|
40
38
|
_id: '1',
|
|
41
|
-
|
|
39
|
+
VERSION: 0
|
|
42
40
|
}
|
|
43
41
|
const entity = from(record)
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
assert.deepStrictEqual(entity, {
|
|
46
44
|
id: '1',
|
|
47
|
-
|
|
45
|
+
VERSION: 0
|
|
48
46
|
})
|
|
49
47
|
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('codec', () => {
|
|
51
|
+
const properties = {
|
|
52
|
+
settled: { type: 'string', format: 'date-time' },
|
|
53
|
+
endpoint: { type: 'string' }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
it('should hold a plain record where the entity declares no date', () => {
|
|
57
|
+
const { to: write, from: read, dates } = codec({ endpoint: { type: 'string' } })
|
|
58
|
+
|
|
59
|
+
assert.deepStrictEqual(dates, [])
|
|
60
|
+
assert.strictEqual(write, to)
|
|
61
|
+
assert.strictEqual(read, from)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should write a date-time property as a date', () => {
|
|
65
|
+
const { to: write } = codec(properties)
|
|
66
|
+
const record = write({ id: '1', settled: '2026-09-05T10:00:00.000Z', endpoint: 'a.b.c' })
|
|
67
|
+
|
|
68
|
+
assert.ok(record.settled instanceof Date)
|
|
69
|
+
assert.strictEqual(record.settled.toISOString(), '2026-09-05T10:00:00.000Z')
|
|
70
|
+
assert.strictEqual(record.endpoint, 'a.b.c', 'and leaves every other property alone')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should read a date back as the string the entity carries', () => {
|
|
74
|
+
const { from: read } = codec(properties)
|
|
75
|
+
const entity = read({ _id: '1', settled: new Date('2026-09-05T10:00:00.000Z') })
|
|
76
|
+
|
|
77
|
+
assert.strictEqual(entity.settled, '2026-09-05T10:00:00.000Z')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should leave a property that has not been written to', () => {
|
|
81
|
+
const { to: write, from: read } = codec(properties)
|
|
82
|
+
|
|
83
|
+
assert.strictEqual(write({ id: '1', settled: null }).settled, null)
|
|
84
|
+
assert.strictEqual(read({ _id: '1' }).settled, undefined)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should read a record written before the property was declared a date', () => {
|
|
88
|
+
const { from: read } = codec(properties)
|
|
89
|
+
|
|
90
|
+
assert.strictEqual(read({ _id: '1', settled: 'not a date' }).settled, 'not a date')
|
|
91
|
+
})
|
|
50
92
|
|
|
51
93
|
it('should not modify argument', () => {
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
_id: '1',
|
|
55
|
-
_version: 0
|
|
56
|
-
}
|
|
94
|
+
const { to: write } = codec(properties)
|
|
95
|
+
const entity = { id: '1', settled: '2026-09-05T10:00:00.000Z' }
|
|
57
96
|
|
|
58
|
-
|
|
97
|
+
write(entity)
|
|
59
98
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
99
|
+
assert.strictEqual(entity.settled, '2026-09-05T10:00:00.000Z')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should ignore a format said of the wrong type', () => {
|
|
103
|
+
assert.deepStrictEqual(codec({ at: { type: 'string', format: 'epoch-millis' } }).dates, [])
|
|
104
|
+
assert.deepStrictEqual(codec({ at: { type: 'integer', format: 'date-time' } }).dates, [])
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('codec, epoch-millis', () => {
|
|
109
|
+
const properties = {
|
|
110
|
+
DELETED: { type: 'integer', format: 'epoch-millis' },
|
|
111
|
+
VERSION: { type: 'integer' }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const millis = 1788698804112
|
|
115
|
+
|
|
116
|
+
it('should write milliseconds as a date', () => {
|
|
117
|
+
const record = codec(properties).to({ id: '1', DELETED: millis, VERSION: 2 })
|
|
118
|
+
|
|
119
|
+
assert.ok(record.DELETED instanceof Date)
|
|
120
|
+
assert.strictEqual(record.DELETED.getTime(), millis)
|
|
121
|
+
assert.strictEqual(record.VERSION, 2, 'and leaves a plain integer alone')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should read a date back as milliseconds', () => {
|
|
125
|
+
const entity = codec(properties).from({ _id: '1', DELETED: new Date(millis) })
|
|
126
|
+
|
|
127
|
+
assert.strictEqual(entity.DELETED, millis)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should leave the null of a record that is not deleted', () => {
|
|
131
|
+
assert.strictEqual(codec(properties).to({ id: '1', DELETED: null }).DELETED, null)
|
|
132
|
+
assert.strictEqual(codec(properties).from({ _id: '1', DELETED: null }).DELETED, null)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should read a record written before the property was a date', () => {
|
|
136
|
+
assert.strictEqual(codec(properties).from({ _id: '1', DELETED: millis }).DELETED, millis)
|
|
64
137
|
})
|
|
65
138
|
})
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { describe, it, beforeEach, mock } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util'
|
|
4
|
+
|
|
5
|
+
import { Storage } from '../src/storage.js'
|
|
6
|
+
|
|
7
|
+
let collection
|
|
8
|
+
let db
|
|
9
|
+
let storage
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
collection = {
|
|
13
|
+
collectionName: 'test',
|
|
14
|
+
findOne: mock.fn(async () => null),
|
|
15
|
+
find: mock.fn(() => ({ stream: () => null })),
|
|
16
|
+
updateMany: mock.fn(async () => ({ modifiedCount: 0 })),
|
|
17
|
+
updateOne: mock.fn(async () => ({ upsertedCount: 0, modifiedCount: 0 }))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
db = { collection: mock.fn(() => null) }
|
|
21
|
+
|
|
22
|
+
const client = { collection, db, link: () => null }
|
|
23
|
+
|
|
24
|
+
storage = new Storage(client, { schema: { properties: {} } })
|
|
25
|
+
|
|
26
|
+
await storage.open()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('open', () => {
|
|
30
|
+
it('should not touch the migrations record where the entity declares none', () => {
|
|
31
|
+
assert.equal(db.collection.mock.callCount(), 0)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('get', () => {
|
|
36
|
+
it('should filter deleted', async () => {
|
|
37
|
+
await storage.get({})
|
|
38
|
+
|
|
39
|
+
assert.ok(
|
|
40
|
+
collection.findOne.mock.calls.some(
|
|
41
|
+
(call) =>
|
|
42
|
+
call.arguments.length === 2 &&
|
|
43
|
+
isDeepStrictEqual(call.arguments[0], { DELETED: null }) &&
|
|
44
|
+
isDeepStrictEqual(call.arguments[1], {})
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should filter deleted with sort', async () => {
|
|
50
|
+
await storage.get({ options: { sort: [['CREATED', 'desc']] } })
|
|
51
|
+
|
|
52
|
+
assert.ok(
|
|
53
|
+
collection.findOne.mock.calls.some(
|
|
54
|
+
(call) =>
|
|
55
|
+
call.arguments.length === 2 &&
|
|
56
|
+
isDeepStrictEqual(call.arguments[0], { DELETED: null }) &&
|
|
57
|
+
isDeepStrictEqual(call.arguments[1], { sort: [['CREATED', -1]] })
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should not filter deleted if queried by id', async () => {
|
|
63
|
+
const id = 'bcb6780f50e243348cad40ed6b5ef575'
|
|
64
|
+
|
|
65
|
+
await storage.get({ id })
|
|
66
|
+
|
|
67
|
+
assert.ok(
|
|
68
|
+
collection.findOne.mock.calls.some(
|
|
69
|
+
(call) =>
|
|
70
|
+
call.arguments.length === 2 &&
|
|
71
|
+
isDeepStrictEqual(call.arguments[0], { _id: id }) &&
|
|
72
|
+
isDeepStrictEqual(call.arguments[1], {})
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should not filter deleted if requested', async () => {
|
|
78
|
+
await storage.get({ options: { deleted: true } })
|
|
79
|
+
|
|
80
|
+
assert.ok(
|
|
81
|
+
collection.findOne.mock.calls.some(
|
|
82
|
+
(call) =>
|
|
83
|
+
call.arguments.length === 2 &&
|
|
84
|
+
isDeepStrictEqual(call.arguments[0], {}) &&
|
|
85
|
+
isDeepStrictEqual(call.arguments[1], {})
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe('stream', () => {
|
|
92
|
+
it('should filter deleted', async () => {
|
|
93
|
+
await storage.stream()
|
|
94
|
+
|
|
95
|
+
assert.ok(
|
|
96
|
+
collection.find.mock.calls.some(
|
|
97
|
+
(call) =>
|
|
98
|
+
call.arguments.length === 2 &&
|
|
99
|
+
isDeepStrictEqual(call.arguments[0], { DELETED: null }) &&
|
|
100
|
+
isDeepStrictEqual(call.arguments[1], {})
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should filter deleted with sort', async () => {
|
|
106
|
+
await storage.stream({ options: { sort: [['CREATED', 'desc']] } })
|
|
107
|
+
|
|
108
|
+
assert.ok(
|
|
109
|
+
collection.find.mock.calls.some(
|
|
110
|
+
(call) =>
|
|
111
|
+
call.arguments.length === 2 &&
|
|
112
|
+
isDeepStrictEqual(call.arguments[0], { DELETED: null }) &&
|
|
113
|
+
isDeepStrictEqual(call.arguments[1], { sort: [['CREATED', -1]] })
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should not filter deleted if requested', async () => {
|
|
119
|
+
await storage.stream({ options: { deleted: true } })
|
|
120
|
+
|
|
121
|
+
assert.ok(
|
|
122
|
+
collection.find.mock.calls.some(
|
|
123
|
+
(call) =>
|
|
124
|
+
call.arguments.length === 2 &&
|
|
125
|
+
isDeepStrictEqual(call.arguments[0], {}) &&
|
|
126
|
+
isDeepStrictEqual(call.arguments[1], {})
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('converge', () => {
|
|
133
|
+
const record = { id: 'a1', VERSION: 4, REGION: 1, status: 'paid' }
|
|
134
|
+
|
|
135
|
+
const call = () => collection.updateOne.mock.calls[0].arguments
|
|
136
|
+
|
|
137
|
+
it('should select by id alone, so that what is absent is upserted', async () => {
|
|
138
|
+
await storage.converge(record)
|
|
139
|
+
|
|
140
|
+
const [criteria, , options] = call()
|
|
141
|
+
|
|
142
|
+
assert.deepEqual(criteria, { _id: 'a1' })
|
|
143
|
+
assert.equal(options.upsert, true)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should supersede a lower version', async () => {
|
|
147
|
+
await storage.converge(record)
|
|
148
|
+
|
|
149
|
+
const [, pipeline] = call()
|
|
150
|
+
const [older] = pipeline[0].$replaceWith.$cond[0].$or
|
|
151
|
+
|
|
152
|
+
assert.deepEqual(older, { $lt: [{ $ifNull: ['$VERSION', 0] }, 4] })
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('should supersede an equal version written by an outranked region', async () => {
|
|
156
|
+
await storage.converge(record)
|
|
157
|
+
|
|
158
|
+
const [, pipeline] = call()
|
|
159
|
+
const [, tied] = pipeline[0].$replaceWith.$cond[0].$or
|
|
160
|
+
|
|
161
|
+
assert.deepEqual(tied.$and, [
|
|
162
|
+
{ $eq: ['$VERSION', 4] },
|
|
163
|
+
{ $gt: [{ $ifNull: ['$REGION', 0] }, 1] }
|
|
164
|
+
])
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('should leave what is stored where it does not', async () => {
|
|
168
|
+
await storage.converge(record)
|
|
169
|
+
|
|
170
|
+
const [, pipeline] = call()
|
|
171
|
+
|
|
172
|
+
assert.equal(pipeline[0].$replaceWith.$cond[2], '$$ROOT')
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('should write the record as a literal, so that a `$` value is not a field path', async () => {
|
|
176
|
+
await storage.converge({ ...record, status: '$paid' })
|
|
177
|
+
|
|
178
|
+
const [, pipeline] = call()
|
|
179
|
+
const written = pipeline[0].$replaceWith.$cond[1].$literal
|
|
180
|
+
|
|
181
|
+
assert.equal(written._id, 'a1')
|
|
182
|
+
assert.equal(written.status, '$paid')
|
|
183
|
+
assert.equal(written.id, undefined)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('should answer true where it inserted', async () => {
|
|
187
|
+
collection.updateOne.mock.mockImplementationOnce(async () => ({
|
|
188
|
+
upsertedCount: 1,
|
|
189
|
+
modifiedCount: 0
|
|
190
|
+
}))
|
|
191
|
+
|
|
192
|
+
assert.equal(await storage.converge(record), true)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it('should answer true where it superseded', async () => {
|
|
196
|
+
collection.updateOne.mock.mockImplementationOnce(async () => ({
|
|
197
|
+
upsertedCount: 0,
|
|
198
|
+
modifiedCount: 1
|
|
199
|
+
}))
|
|
200
|
+
|
|
201
|
+
assert.equal(await storage.converge(record), true)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('should read a record that lacks a region as the first one', async () => {
|
|
205
|
+
await storage.converge(record)
|
|
206
|
+
|
|
207
|
+
const [, pipeline] = call()
|
|
208
|
+
const [, tied] = pipeline[0].$replaceWith.$cond[0].$or
|
|
209
|
+
|
|
210
|
+
// the migration writes it, and one that reached here without it would win every tie
|
|
211
|
+
assert.deepEqual(tied.$and[1].$gt[0], { $ifNull: ['$REGION', 0] })
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('should answer false where it changed nothing, and not throw', async () => {
|
|
215
|
+
assert.equal(await storage.converge(record), false)
|
|
216
|
+
})
|
|
217
|
+
})
|
package/test/translate.test.js
CHANGED
|
@@ -1,33 +1,85 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import { translate } from '../src/translate.js'
|
|
4
5
|
|
|
5
6
|
describe('options', () => {
|
|
6
7
|
it('should translate omit', () => {
|
|
7
8
|
const options = { omit: 10 }
|
|
8
9
|
const query = translate({ options })
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
assert.deepStrictEqual(query.options.skip, options.omit)
|
|
11
12
|
})
|
|
12
13
|
|
|
13
14
|
it('should translate limit', () => {
|
|
14
15
|
const options = { limit: 10 }
|
|
15
16
|
const query = translate({ options })
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
assert.deepStrictEqual(query.options.limit, options.limit)
|
|
18
19
|
})
|
|
19
20
|
|
|
20
21
|
it('should translate sort', () => {
|
|
21
|
-
const options = {
|
|
22
|
+
const options = {
|
|
23
|
+
sort: [
|
|
24
|
+
['a', 'asc'],
|
|
25
|
+
['b', 'desc'],
|
|
26
|
+
['id', 'asc']
|
|
27
|
+
]
|
|
28
|
+
}
|
|
22
29
|
const query = translate({ options })
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
assert.deepStrictEqual(query.options.sort, [
|
|
32
|
+
['a', 1],
|
|
33
|
+
['b', -1],
|
|
34
|
+
['_id', 1]
|
|
35
|
+
])
|
|
25
36
|
})
|
|
26
37
|
|
|
27
38
|
it('should translate projection', () => {
|
|
28
39
|
const options = { projection: ['a', 'b', 'c', 'id'] }
|
|
29
40
|
const query = translate({ options })
|
|
30
41
|
|
|
31
|
-
|
|
42
|
+
assert.deepStrictEqual(query.options.projection, { a: 1, b: 1, c: 1, _id: 1 })
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe('dates', () => {
|
|
47
|
+
const comparison = (selector, operator, value) => ({
|
|
48
|
+
type: 'COMPARISON',
|
|
49
|
+
operator,
|
|
50
|
+
left: { type: 'SELECTOR', selector },
|
|
51
|
+
right: { type: 'VALUE', value }
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should compare a date property against a date', () => {
|
|
55
|
+
const criteria = comparison('settled', '<', '2026-09-05T10:00:00.000Z')
|
|
56
|
+
const query = translate({ criteria }, ['settled'])
|
|
57
|
+
|
|
58
|
+
assert.ok(query.criteria.settled.$lt instanceof Date)
|
|
59
|
+
assert.strictEqual(
|
|
60
|
+
query.criteria.settled.$lt.toISOString(),
|
|
61
|
+
'2026-09-05T10:00:00.000Z'
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should convert every value of a list', () => {
|
|
66
|
+
const criteria = comparison('settled', '=in=', ['2026-09-05T10:00:00.000Z'])
|
|
67
|
+
const query = translate({ criteria }, ['settled'])
|
|
68
|
+
|
|
69
|
+
assert.ok(query.criteria.settled.$in[0] instanceof Date)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should leave a property that is not a date', () => {
|
|
73
|
+
const criteria = comparison('endpoint', '==', 'a.b.c')
|
|
74
|
+
const query = translate({ criteria }, ['settled'])
|
|
75
|
+
|
|
76
|
+
assert.deepStrictEqual(query.criteria, { endpoint: { $eq: 'a.b.c' } })
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should leave every criterion where the entity declares no date', () => {
|
|
80
|
+
const criteria = comparison('settled', '<', '2026-09-05T10:00:00.000Z')
|
|
81
|
+
const query = translate({ criteria })
|
|
82
|
+
|
|
83
|
+
assert.strictEqual(query.criteria.settled.$lt, '2026-09-05T10:00:00.000Z')
|
|
32
84
|
})
|
|
33
85
|
})
|
package/types/connection.d.ts
CHANGED
|
@@ -6,26 +6,32 @@ import type {
|
|
|
6
6
|
FindOneAndReplaceOptions,
|
|
7
7
|
FindOneAndUpdateOptions,
|
|
8
8
|
FindOptions,
|
|
9
|
-
UpdateFilter
|
|
9
|
+
UpdateFilter
|
|
10
10
|
} from 'mongodb'
|
|
11
11
|
|
|
12
12
|
import type { Connector } from '@toa.io/core'
|
|
13
|
-
import type { Record } from './record'
|
|
13
|
+
import type { Record } from './record.d.ts'
|
|
14
14
|
|
|
15
|
-
declare namespace toa.mongodb{
|
|
15
|
+
declare namespace toa.mongodb {
|
|
16
|
+
interface Connection extends Connector {
|
|
17
|
+
get(query: Filter<Record>, options?: FindOptions<Record>): Promise<Record>
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
get (query: Filter<Record>, options?: FindOptions<Record>): Promise<Record>
|
|
19
|
+
find(query: Filter<Record>, options?: FindOptions<Record>): Promise<Record[]>
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
add(record: Record): Promise<boolean>
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
addMany(records: Record[]): Promise<boolean>
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
replace(
|
|
26
|
+
query: Filter<Record>,
|
|
27
|
+
record: UpdateFilter<Record>,
|
|
28
|
+
options?: FindOneAndReplaceOptions
|
|
29
|
+
): Promise<any>
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
update(
|
|
32
|
+
query: Filter<Record>,
|
|
33
|
+
update: UpdateFilter<Record>,
|
|
34
|
+
options?: FindOneAndUpdateOptions
|
|
35
|
+
): Promise<any>
|
|
29
36
|
}
|
|
30
|
-
|
|
31
37
|
}
|
package/types/pointer.d.ts
CHANGED
package/types/record.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
declare namespace toa.mongodb {
|
|
2
|
+
interface Record {
|
|
3
|
+
_id: string
|
|
4
|
+
VERSION: number
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
_version: number
|
|
6
|
-
|
|
7
|
-
[key: string]: any
|
|
8
|
-
}
|
|
9
|
-
|
|
6
|
+
[key: string]: any
|
|
7
|
+
}
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
export type Record = toa.mongodb.Record
|
package/src/collection.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { Connector } = require('@toa.io/core')
|
|
4
|
-
|
|
5
|
-
class Collection extends Connector {
|
|
6
|
-
#client
|
|
7
|
-
#collection
|
|
8
|
-
|
|
9
|
-
constructor (client) {
|
|
10
|
-
super()
|
|
11
|
-
|
|
12
|
-
this.#client = client
|
|
13
|
-
|
|
14
|
-
this.depends(client)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async open () {
|
|
18
|
-
this.#collection = this.#client.collection
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** @hot */
|
|
22
|
-
async get (query, options) {
|
|
23
|
-
return /** @type {toa.mongodb.Record} */ this.#collection.findOne(query, options)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** @hot */
|
|
27
|
-
async find (query, options) {
|
|
28
|
-
const cursor = this.#collection.find(query, options)
|
|
29
|
-
|
|
30
|
-
return cursor.toArray()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** @hot */
|
|
34
|
-
async add (record) {
|
|
35
|
-
return await this.#collection.insertOne(record)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** @hot */
|
|
39
|
-
async replace (query, record, options) {
|
|
40
|
-
return await this.#collection.findOneAndReplace(query, record, options)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** @hot */
|
|
44
|
-
async update (query, update, options) {
|
|
45
|
-
return this.#collection.findOneAndUpdate(query, update, options)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async index (keys, options) {
|
|
49
|
-
return this.#collection.createIndex(keys, options)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async indexes () {
|
|
53
|
-
try {
|
|
54
|
-
const array = await this.#collection.listIndexes().toArray()
|
|
55
|
-
|
|
56
|
-
return array.map(({ name }) => name).filter((name) => name !== '_id_')
|
|
57
|
-
} catch {
|
|
58
|
-
return []
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async dropIndexes (names) {
|
|
63
|
-
const all = names.map((name) => this.#collection.dropIndex(name))
|
|
64
|
-
|
|
65
|
-
return Promise.all(all)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
exports.Collection = Collection
|
package/src/deployment.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { createVariables } = require('@toa.io/pointer')
|
|
4
|
-
|
|
5
|
-
const deployment = (instances, annotation) => {
|
|
6
|
-
const requests = instances.map((instance) => createRequest(instance))
|
|
7
|
-
const variables = createVariables(ID, annotation, requests)
|
|
8
|
-
|
|
9
|
-
return { variables }
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function createRequest (instance) {
|
|
13
|
-
return {
|
|
14
|
-
group: instance.locator.label,
|
|
15
|
-
selectors: [instance.locator.id]
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const ID = 'mongodb'
|
|
20
|
-
|
|
21
|
-
exports.ID = ID
|
|
22
|
-
exports.deployment = deployment
|