@adobe/aio-lib-db 0.1.0-alpha.1

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.
@@ -0,0 +1,114 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+ const { dbStatsApi, listCollectionsApi, createCollectionApi } = require('./api/client')
13
+ const DbCollection = require('./DbCollection')
14
+
15
+ class DbClient {
16
+ /**
17
+ * @param {DbBase} db
18
+ * @hideconstructor
19
+ */
20
+ constructor(db) {
21
+ this.db = db
22
+ this._activeCursors = new Set()
23
+ }
24
+
25
+ /**
26
+ * Instantiates and returns a new DbClient object scoped to the configured namespace
27
+ *
28
+ * @static
29
+ * @constructs
30
+ * @param {DbBase} db
31
+ * @returns {Promise<DbClient>}
32
+ */
33
+ static async init(db) {
34
+ return new DbClient(db)
35
+ }
36
+
37
+ /**
38
+ * Get the statistics for the scoped database
39
+ *
40
+ * @returns {Promise<Object>}
41
+ * @throws {DbError}
42
+ */
43
+ async dbStats() {
44
+ return dbStatsApi(this.db)
45
+ }
46
+
47
+ /**
48
+ * List the collections in the scoped database according to the filter if provided
49
+ *
50
+ * @param {Object=} filter
51
+ * @param {Object=} options
52
+ * @returns {Promise<Object[]>}
53
+ * @throws {DbError}
54
+ */
55
+ async listCollections(filter = {}, options = {}) {
56
+ return listCollectionsApi(this.db, filter, options)
57
+ }
58
+
59
+ /**
60
+ * Close all active cursors
61
+ *
62
+ * @returns {Promise<void>}
63
+ * @throws {DbError}
64
+ */
65
+ async close() {
66
+ for (const cursor of this._activeCursors) {
67
+ await cursor.close()
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Gets the collection with the given name from the scoped database
73
+ *
74
+ * @param {string} name
75
+ * @returns {DbCollection}
76
+ */
77
+ collection(name) {
78
+ return DbCollection.init(name, this.db, this)
79
+ }
80
+
81
+ /**
82
+ * Creates a new collection in the scoped database
83
+ *
84
+ * @param {string} name
85
+ * @param {Object=} options
86
+ * @returns {Promise<DbCollection>}
87
+ * @throws {DbError}
88
+ */
89
+ async createCollection(name, options = {}) {
90
+ await createCollectionApi(this.db, name, options)
91
+ return DbCollection.init(name, this.db, this)
92
+ }
93
+
94
+ /**
95
+ * Register an active cursor to be closed by client.close() later
96
+ *
97
+ * {AbstractDbCursor} cursor
98
+ */
99
+ registerCursor(cursor) {
100
+ this._activeCursors.add(cursor)
101
+ }
102
+
103
+ /**
104
+ * Remove a cursor from the active cursors set.
105
+ * This is called by cursor.close() after it has closed its API session.
106
+ *
107
+ * @param {AbstractDbCursor} cursor
108
+ */
109
+ unregisterCursor(cursor) {
110
+ this._activeCursors.delete(cursor)
111
+ }
112
+ }
113
+
114
+ module.exports = DbClient
@@ -0,0 +1,352 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+ const {
13
+ insertOneApi, findOneApi, updateOneApi, replaceOneApi, deleteOneApi, bulkWriteApi, deleteManyApi, updateManyApi,
14
+ insertManyApi, findOneAndUpdateApi, findOneAndReplaceApi, findOneAndDeleteApi, createIndexApi, dropIndexApi,
15
+ getIndexesApi, distinctApi, countDocumentsApi, estimatedDocumentCountApi, dropApi, renameCollectionApi, statsApi,
16
+ findArrayApi
17
+ } = require('./api/collection')
18
+ const FindCursor = require("./FindCursor")
19
+ const AggregateCursor = require("./AggregateCursor")
20
+
21
+ class DbCollection {
22
+ /**
23
+ * @param {string} name
24
+ * @param {DbBase} db
25
+ * @param {DbClient} client
26
+ * @hideconstructor
27
+ */
28
+ constructor(name, db, client) {
29
+ this.name = name
30
+ this.db = db
31
+ this.client = client
32
+ }
33
+
34
+ /**
35
+ * Instantiates and returns a new DbCollection object
36
+ *
37
+ * @static
38
+ * @constructs
39
+ * @param {string} name
40
+ * @param {DbBase} db
41
+ * @param {DbClient} client
42
+ * @returns {DbCollection}
43
+ */
44
+ static init(name, db, client) {
45
+ return new DbCollection(name, db, client)
46
+ }
47
+
48
+ /**
49
+ * Inserts a single document into the collection
50
+ *
51
+ * @param {Object} document
52
+ * @param {Object=} options
53
+ * @returns {Promise<Object>}
54
+ * @throws {DbError}
55
+ */
56
+ async insertOne(document, options = {}) {
57
+ return insertOneApi(this.db, this.name, document, options)
58
+ }
59
+
60
+ /**
61
+ * Insert multiple documents into the collection
62
+ *
63
+ * @param {Object[]} documents
64
+ * @param {Object=} options
65
+ * @returns {Promise<Object>}
66
+ * @throws {DbError}
67
+ */
68
+ async insertMany(documents, options = {}) {
69
+ return insertManyApi(this.db, this.name, documents, options)
70
+ }
71
+
72
+ /**
73
+ * Finds the first document matching the filter
74
+ *
75
+ * @param {Object} filter
76
+ * @param {Object=} options
77
+ * @returns {Promise<Object>}
78
+ * @throws {DbError}
79
+ */
80
+ async findOne(filter, options = {}) {
81
+ return findOneApi(this.db, this.name, filter, options)
82
+ }
83
+
84
+ /**
85
+ * Find all documents that match the filter
86
+ *
87
+ * @param {Object=} filter
88
+ * @param {Object=} options
89
+ * @returns {FindCursor}
90
+ * @throws {DbError}
91
+ */
92
+ find(filter = {}, options = {}) {
93
+ return new FindCursor(this.db, this.client, this.name, filter, options)
94
+ }
95
+
96
+ /**
97
+ * Update and return the first document that matches the filter
98
+ *
99
+ * @param {Object} filter
100
+ * @param {Object} update
101
+ * @param {Object=} options
102
+ * @returns {Promise<Object>}
103
+ * @throws {DbError}
104
+ */
105
+ async findOneAndUpdate(filter, update, options = {}) {
106
+ return findOneAndUpdateApi(this.db, this.name, filter, update, options)
107
+ }
108
+
109
+ /**
110
+ * Replace and return the first document that matches the filter
111
+ *
112
+ * @param {Object} filter
113
+ * @param {Object} replacement
114
+ * @param {Object=} options
115
+ * @returns {Promise<Object>}
116
+ * @throws {DbError}
117
+ */
118
+ async findOneAndReplace(filter, replacement, options = {}) {
119
+ return findOneAndReplaceApi(this.db, this.name, filter, replacement, options)
120
+ }
121
+
122
+ /**
123
+ * Delete and return the first document that matches the filter
124
+ *
125
+ * @param {Object} filter
126
+ * @param {Object=} options
127
+ * @returns {Promise<Object>}
128
+ * @throws {DbError}
129
+ */
130
+ async findOneAndDelete(filter, options = {}) {
131
+ return findOneAndDeleteApi(this.db, this.name, filter, options)
132
+ }
133
+
134
+ /**
135
+ * Finds the first batch of documents that match the filter and return them as an array.
136
+ * Does not allow retrieving more than one batch of results.
137
+ *
138
+ * @param {Object=} filter
139
+ * @param {Object=} options
140
+ * @returns {Promise<Object[]>}
141
+ */
142
+ async findArray(filter = {}, options = {}) {
143
+ return findArrayApi(this.db, this.name, filter, options)
144
+ }
145
+
146
+ /**
147
+ * Updates the first document that matches the filter
148
+ *
149
+ * @param {Object} filter
150
+ * @param {Object} update
151
+ * @param {Object=} options
152
+ * @returns {Promise<Object>}
153
+ * @throws {DbError}
154
+ */
155
+ async updateOne(filter, update, options = {}) {
156
+ return updateOneApi(this.db, this.name, filter, update, options)
157
+ }
158
+
159
+ /**
160
+ * Update all documents that match a filter
161
+ *
162
+ * @param {Object} filter
163
+ * @param {Object} update
164
+ * @param {Object=} options
165
+ * @returns {Promise<Object>}
166
+ * @throws {DbError}
167
+ */
168
+ async updateMany(filter, update, options = {}) {
169
+ return updateManyApi(this.db, this.name, filter, update, options)
170
+ }
171
+
172
+ /**
173
+ * Replaces the first document that matches the filter
174
+ *
175
+ * @param {Object} filter
176
+ * @param {Object} replacement
177
+ * @param {Object=} options
178
+ * @returns {Promise<Object>}
179
+ * @throws {DbError}
180
+ */
181
+ async replaceOne(filter, replacement, options = {}) {
182
+ return replaceOneApi(this.db, this.name, filter, replacement, options)
183
+ }
184
+
185
+ /**
186
+ * Deletes the first document that matches the filter
187
+ *
188
+ * @param {Object} filter
189
+ * @param {Object=} options
190
+ * @returns {Promise<Object>}
191
+ * @throws {DbError}
192
+ */
193
+ async deleteOne(filter, options = {}) {
194
+ return deleteOneApi(this.db, this.name, filter, options)
195
+ }
196
+
197
+ /**
198
+ * Delete all documents that match a filter
199
+ *
200
+ * @param {Object} filter
201
+ * @param {Object=} options
202
+ * @returns {Promise<Object>}
203
+ * @throws {DbError}
204
+ */
205
+ async deleteMany(filter, options = {}) {
206
+ return deleteManyApi(this.db, this.name, filter, options)
207
+ }
208
+
209
+ /**
210
+ * Executes an aggregation pipeline over the documents in a collection
211
+ *
212
+ * @param {Object[]=} pipeline
213
+ * @param {Object=} options
214
+ * @returns {AggregateCursor}
215
+ * @throws {DbError}
216
+ */
217
+ aggregate(pipeline = [], options = {}) {
218
+ return new AggregateCursor(this.db, this.client, this.name, pipeline, options)
219
+ }
220
+
221
+ /**
222
+ * Execute multiple insert/update/delete commands in one operation
223
+ *
224
+ * @param {Object[]} operations
225
+ * @param {Object=} options
226
+ * @returns {Promise<Object>}
227
+ * @throws {DbError}
228
+ */
229
+ async bulkWrite(operations, options = {}) {
230
+ return bulkWriteApi(this.db, this.name, operations, options)
231
+ }
232
+
233
+ /**
234
+ * Gets the list of indexes in the collection
235
+ *
236
+ * @returns {Promise<Object[]>}
237
+ * @throws {DbError}
238
+ */
239
+ async getIndexes() {
240
+ return getIndexesApi(this.db, this.name)
241
+ }
242
+
243
+ /**
244
+ * Create an index with the provided specification
245
+ *
246
+ * @param {Object} specification
247
+ * @param {Object=} options
248
+ * @returns {Promise<String>} The name of the created index
249
+ * @throws {DbError}
250
+ */
251
+ async createIndex(specification, options = {}) {
252
+ return createIndexApi(this.db, this.name, specification, options)
253
+ }
254
+
255
+ /**
256
+ * Drops the index with the given name
257
+ *
258
+ * @param {string} index
259
+ * @param {Object=} options
260
+ * @returns {Promise<Object>}
261
+ * @throws {DbError}
262
+ */
263
+ async dropIndex(index, options = {}) {
264
+ return dropIndexApi(this.db, this.name, index, options)
265
+ }
266
+
267
+ /**
268
+ * Find all distinct values for a field, optionally applying a filter
269
+ *
270
+ * @param {(Object|string)} field
271
+ * @param {Object=} filter
272
+ * @param {Object=} options
273
+ * @returns {Promise<*[]>}
274
+ * @throws {DbError}
275
+ */
276
+ async distinct(field, filter = {}, options = {}) {
277
+ return distinctApi(this.db, this.name, field, filter, options)
278
+ }
279
+
280
+ /**
281
+ * Count the number of documents, optionally applying a filter
282
+ *
283
+ * @param {Object=} filter
284
+ * @param {Object=} options
285
+ * @returns {Promise<number>}
286
+ * @throws {DbError}
287
+ */
288
+ async countDocuments(filter = {}, options = {}) {
289
+ return countDocumentsApi(this.db, this.name, filter, options)
290
+ }
291
+
292
+ /**
293
+ * @deprecated
294
+ * @see countDocuments
295
+ *
296
+ * @param {Object=} filter
297
+ * @param {Object=} options
298
+ * @returns {Promise<number>}
299
+ * @throws {DbError}
300
+ */
301
+ async count(filter = {}, options = {}) {
302
+ return this.countDocuments(filter, options)
303
+ }
304
+
305
+ /**
306
+ * Estimates the number of documents based on collection metadata
307
+ *
308
+ * @param {Object=} options
309
+ * @returns {Promise<number>}
310
+ * @throws {DbError}
311
+ */
312
+ async estimatedDocumentCount(options = {}) {
313
+ return estimatedDocumentCountApi(this.db, this.name, options)
314
+ }
315
+
316
+ /**
317
+ * Drop this collection from the database
318
+ *
319
+ * @param {Object=} options
320
+ * @returns {Promise<void>}
321
+ * @throws {DbError}
322
+ */
323
+ async drop(options = {}) {
324
+ return dropApi(this.db, this.name, options)
325
+ }
326
+
327
+ /**
328
+ * Rename this collection
329
+ *
330
+ * @param {string} newCollectionName
331
+ * @param {Object=} options
332
+ * @returns {Promise<void>}
333
+ * @throws {DbError}
334
+ */
335
+ async renameCollection(newCollectionName, options = {}) {
336
+ await renameCollectionApi(this.db, this.name, newCollectionName, options)
337
+ this.name = newCollectionName
338
+ }
339
+
340
+ /**
341
+ * Get collection statistics
342
+ *
343
+ * @param {Object=} options
344
+ * @returns {Promise<Object>}
345
+ * @throws {DbError}
346
+ */
347
+ async stats(options = {}) {
348
+ return statsApi(this.db, this.name, options)
349
+ }
350
+ }
351
+
352
+ module.exports = DbCollection
package/lib/DbError.js ADDED
@@ -0,0 +1,21 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+ class DbError extends Error {
13
+ constructor(message, requestId = null, httpStatusCode = undefined, options = {}) {
14
+ super(message, options)
15
+ this.name = 'DbError'
16
+ this.requestId = requestId
17
+ this.httpStatusCode = httpStatusCode
18
+ }
19
+ }
20
+
21
+ module.exports = DbError
@@ -0,0 +1,107 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+ const AbstractDbCursor = require("./AbstractDbCursor")
13
+ const { findApi } = require("./api/collection")
14
+
15
+ class FindCursor extends AbstractDbCursor {
16
+ #filter
17
+
18
+ /**
19
+ * @param {DbBase} db
20
+ * @param {DbClient} client
21
+ * @param {string} collectionName
22
+ * @param {Object} filter
23
+ * @param {Object} options
24
+ */
25
+ constructor(db, client, collectionName, filter, options) {
26
+ super(db, client, collectionName, options)
27
+ this.#filter = filter
28
+ }
29
+
30
+ /**
31
+ * Get the first batch of results from the database
32
+ *
33
+ * @returns {Promise<Object>}
34
+ * @throws {DbError}
35
+ * @protected
36
+ * @internal
37
+ */
38
+ async _firstRequest() {
39
+ return await findApi(this._db, this._axiosClient, this._collection, this.#filter, this._options)
40
+ }
41
+
42
+ /**
43
+ * Set the query filter
44
+ *
45
+ * @param {Object} filter
46
+ * @returns {this|Readable}
47
+ */
48
+ filter(filter) {
49
+ this._throwIfInitialized()
50
+ this.#filter = filter
51
+ return this
52
+ }
53
+
54
+ /**
55
+ * Set the sort order for the query
56
+ *
57
+ * @param {(string|string[]|Object|number)} sort
58
+ * @param {(string|Object|number)=} direction
59
+ * @returns {this}
60
+ */
61
+ sort(sort, direction = undefined) {
62
+ this._throwIfInitialized()
63
+ // Defer formatting to the service to avoid depending on the mongodb library here
64
+ this._options.sort = { sort: sort, direction: direction }
65
+ return this
66
+ }
67
+
68
+ /**
69
+ * Set the projection for the query
70
+ *
71
+ * @param {Object} projection
72
+ * @returns {this}
73
+ */
74
+ project(projection) {
75
+ // The actual option is 'projection', but project() is the name of the method that sets it
76
+ // on MongoDb's FindCursor class
77
+ this._throwIfInitialized()
78
+ this._options.projection = projection
79
+ return this
80
+ }
81
+
82
+ /**
83
+ * Set the limit for the query
84
+ *
85
+ * @param {number} limit
86
+ * @returns {this}
87
+ */
88
+ limit(limit) {
89
+ this._throwIfInitialized()
90
+ this._options.limit = limit
91
+ return this
92
+ }
93
+
94
+ /**
95
+ * Set the number of documents to skip before returning results
96
+ *
97
+ * @param {number} skip
98
+ * @returns {this}
99
+ */
100
+ skip(skip) {
101
+ this._throwIfInitialized()
102
+ this._options.skip = skip
103
+ return this
104
+ }
105
+ }
106
+
107
+ module.exports = FindCursor
@@ -0,0 +1,96 @@
1
+ /*
2
+ Copyright 2025 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+ const { apiPost, apiGet } = require("../../utils/apiRequest")
13
+
14
+ /**
15
+ * Make a post request to the App Builder Database Service API and return the result
16
+ *
17
+ * @param {DbBase} db
18
+ * @param {string} endpoint
19
+ * @param {Object=} params
20
+ * @param {Object=} options
21
+ * @param {AxiosInstance=} axiosClient
22
+ * @returns {Promise<*>}
23
+ * @throws {DbError}
24
+ */
25
+ async function postClientApi(db, endpoint, params = {}, options = {}, axiosClient = undefined) {
26
+ const client = axiosClient || db.axiosClient;
27
+ return await apiPost(db, client, `client/${endpoint}`, params, options)
28
+ }
29
+
30
+ /**
31
+ * Make a get request to the App Builder Database Service API and return the result
32
+ *
33
+ * @param {DbBase} db
34
+ * @param {string} endpoint
35
+ * @returns {Promise<*>}
36
+ * @throws {DbError}
37
+ */
38
+ async function getClientApi(db, endpoint) {
39
+ return await apiGet(db, db.axiosClient, `client/${endpoint}`)
40
+ }
41
+
42
+ /**
43
+ * Gets the statistics for the scoped database
44
+ *
45
+ * @param {DbBase} db
46
+ * @returns {Promise<Object>}
47
+ * @throws {DbError}
48
+ */
49
+ async function dbStats(db) {
50
+ return await getClientApi(db, 'dbStats')
51
+ }
52
+
53
+ /**
54
+ * Create a new collection in the scoped database
55
+ *
56
+ * @param {DbBase} db
57
+ * @param {string} name
58
+ * @param {Object=} options
59
+ * @returns {Promise<void>}
60
+ * @throws {DbError}
61
+ */
62
+ async function createCollection(db, name, options = {}) {
63
+ return await postClientApi(db, 'createCollection', { name: name }, options)
64
+ }
65
+
66
+ /**
67
+ * List the collections in the scoped database
68
+ *
69
+ * @param {DbBase} db
70
+ * @param {Object=} filter
71
+ * @param {Object=} options
72
+ * @returns {Promise<Object[]>}
73
+ * @throws {DbError}
74
+ */
75
+ async function listCollections(db, filter = {}, options = {}) {
76
+ return await postClientApi(db, 'listCollections', { filter: filter }, options)
77
+ }
78
+
79
+ /**
80
+ * Close a database session
81
+ *
82
+ * @param {DbBase} db
83
+ * @param {AxiosInstance} axiosClient
84
+ * @returns {Promise<void>}
85
+ * @throws {DbError}
86
+ */
87
+ async function close(db, axiosClient) {
88
+ return await postClientApi(db, 'close', undefined, undefined, axiosClient)
89
+ }
90
+
91
+ module.exports = {
92
+ dbStatsApi: dbStats,
93
+ closeApi: close,
94
+ createCollectionApi: createCollection,
95
+ listCollectionsApi: listCollections
96
+ }