@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.
- package/COPYING.txt +7 -0
- package/COPYRIGHT +5 -0
- package/LICENSE +201 -0
- package/README.md +557 -0
- package/index.js +12 -0
- package/lib/AbstractDbCursor.js +350 -0
- package/lib/AggregateCursor.js +187 -0
- package/lib/DbBase.js +149 -0
- package/lib/DbClient.js +114 -0
- package/lib/DbCollection.js +352 -0
- package/lib/DbError.js +21 -0
- package/lib/FindCursor.js +107 -0
- package/lib/api/client.js +96 -0
- package/lib/api/collection.js +425 -0
- package/lib/api/db.js +90 -0
- package/lib/constants.js +44 -0
- package/lib/init.js +35 -0
- package/package.json +56 -0
- package/utils/apiRequest.js +103 -0
- package/utils/axiosUtils.js +57 -0
- package/utils/ejsonHandler.js +61 -0
|
@@ -0,0 +1,103 @@
|
|
|
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 { RUNTIME_HEADER, REQUEST_ID_HEADER } = require('../lib/constants')
|
|
13
|
+
const DbError = require('../lib/DbError')
|
|
14
|
+
const { EJSON } = require("bson")
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Execute a POST request to the App Builder Database Service API and return the data field from the result
|
|
18
|
+
*
|
|
19
|
+
* @param {DbBase} db
|
|
20
|
+
* @param {AxiosInstance} axiosClient
|
|
21
|
+
* @param {string} apiPath db api url past <ENDPOINT>/v1/
|
|
22
|
+
* @param {Object=} params
|
|
23
|
+
* @param {Object=} options
|
|
24
|
+
* @returns {Promise<*>}
|
|
25
|
+
* @throws {DbError}
|
|
26
|
+
*/
|
|
27
|
+
async function apiPost(db, axiosClient, apiPath, params = {}, options = {}) {
|
|
28
|
+
const body = params
|
|
29
|
+
if (Object.keys(options).length > 0) {
|
|
30
|
+
body.options = options
|
|
31
|
+
}
|
|
32
|
+
return await apiRequest(db, axiosClient, `v1/${apiPath}`, 'POST', body)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Execute a GET request to the App Builder Database Service API and return the data field from the result
|
|
37
|
+
*
|
|
38
|
+
* @param {DbBase} db
|
|
39
|
+
* @param {AxiosInstance} axiosClient
|
|
40
|
+
* @param {string} apiPath db api url past <ENDPOINT>/v1/
|
|
41
|
+
* @returns {Promise<*>}
|
|
42
|
+
* @throws {DbError}
|
|
43
|
+
*/
|
|
44
|
+
async function apiGet(db, axiosClient, apiPath) {
|
|
45
|
+
return await apiRequest(db, axiosClient, `v1/${apiPath}`, 'GET')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Internal helper method to construct and execute a request to the App Builder Database Service API
|
|
50
|
+
*
|
|
51
|
+
* @param {DbBase} db
|
|
52
|
+
* @param {AxiosInstance} axiosClient
|
|
53
|
+
* @param {string} apiPath
|
|
54
|
+
* @param {string} method
|
|
55
|
+
* @param {Object=} body
|
|
56
|
+
* @returns {Promise<*>}
|
|
57
|
+
* @throws {DbError}
|
|
58
|
+
*/
|
|
59
|
+
async function apiRequest(db, axiosClient, apiPath, method, body = {}) {
|
|
60
|
+
const fullUrl = `${db.serviceUrl}/${apiPath}`
|
|
61
|
+
let res
|
|
62
|
+
try {
|
|
63
|
+
const creds = db.runtimeAuth.split(/:(.*)/,2)
|
|
64
|
+
/** @type {Object}
|
|
65
|
+
* @mixes AxiosRequestConfig */
|
|
66
|
+
const reqConfig = {
|
|
67
|
+
headers: { [RUNTIME_HEADER]: db.runtimeNamespace },
|
|
68
|
+
auth: {
|
|
69
|
+
username: creds[0],
|
|
70
|
+
password: creds[1]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (method === 'GET') {
|
|
74
|
+
res = await axiosClient.get(fullUrl, reqConfig)
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
res = await axiosClient.post(fullUrl, EJSON.stringify(body, { relaxed: false }), reqConfig)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err.response?.data) {
|
|
82
|
+
const reqId = err.response.data.requestId || err.response.headers[REQUEST_ID_HEADER]
|
|
83
|
+
throw new DbError(
|
|
84
|
+
`Request ${reqId} to ${apiPath} failed with code ${err.response.status}: ${err.response.data.message}`,
|
|
85
|
+
reqId,
|
|
86
|
+
err.response.status,
|
|
87
|
+
{ cause: err }
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
throw err
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!res.data.success) {
|
|
94
|
+
const reqId = res.data.requestId || res.headers[REQUEST_ID_HEADER]
|
|
95
|
+
throw new DbError(`Request ${reqId} to ${apiPath} failed: ${res.data.message}`, reqId, res.status)
|
|
96
|
+
}
|
|
97
|
+
return res.data.data
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
apiPost,
|
|
102
|
+
apiGet
|
|
103
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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 { default: axios } = require("axios")
|
|
13
|
+
const { CookieJar } = require("tough-cookie")
|
|
14
|
+
const { HttpCookieAgent, HttpsCookieAgent } = require("http-cookie-agent/http")
|
|
15
|
+
const { cleanParse } = require("./ejsonHandler")
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Use EJSON instead of JSON and clean primitive values
|
|
19
|
+
*
|
|
20
|
+
* @param {AxiosResponse} response
|
|
21
|
+
* @returns {AxiosResponse}
|
|
22
|
+
*/
|
|
23
|
+
function jsonToEjson(response) {
|
|
24
|
+
if (/^application\/json/.test(response.headers["content-type"])) {
|
|
25
|
+
response.data = cleanParse(response.data)
|
|
26
|
+
}
|
|
27
|
+
return response
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Instantiate an Axios client with optional session support
|
|
32
|
+
*
|
|
33
|
+
* @param withSession
|
|
34
|
+
* @return {AxiosInstance}
|
|
35
|
+
*/
|
|
36
|
+
function getAxiosClient(withSession = false) {
|
|
37
|
+
let client
|
|
38
|
+
|
|
39
|
+
if (withSession) {
|
|
40
|
+
const jar = new CookieJar()
|
|
41
|
+
client = axios.create({
|
|
42
|
+
httpAgent: new HttpCookieAgent({ cookies: { jar } }),
|
|
43
|
+
httpsAgent: new HttpsCookieAgent({ cookies: { jar } })
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
client = axios.create()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Parse 20x results using EJSON instead of JSON
|
|
51
|
+
client.interceptors.response.use(jsonToEjson)
|
|
52
|
+
return client
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
getAxiosClient
|
|
57
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 { BSONValue, EJSON } = require("bson")
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Transform any of the object's properties that are BSONValues representing primitives back into their js types
|
|
16
|
+
* Does a recursive deep mapping
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} obj
|
|
19
|
+
* @returns {BSONValue|Object|*}
|
|
20
|
+
*/
|
|
21
|
+
function transformBsonPrimitives(obj) {
|
|
22
|
+
// A long-standing bug in js makes typeof null = 'object', so filter that out first
|
|
23
|
+
if (obj === null) {
|
|
24
|
+
return obj
|
|
25
|
+
}
|
|
26
|
+
else if (obj instanceof BSONValue) {
|
|
27
|
+
if (obj.valueOf !== undefined && typeof obj.valueOf() !== 'object') {
|
|
28
|
+
return obj.valueOf()
|
|
29
|
+
}
|
|
30
|
+
return obj
|
|
31
|
+
}
|
|
32
|
+
else if (Array.isArray(obj)) {
|
|
33
|
+
return obj.map((val) => {
|
|
34
|
+
return transformBsonPrimitives(val)
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
else if (typeof obj === 'object') {
|
|
38
|
+
const mapped = {}
|
|
39
|
+
Object.keys(obj).forEach((key) => {
|
|
40
|
+
mapped[key] = transformBsonPrimitives(obj[key])
|
|
41
|
+
})
|
|
42
|
+
return mapped
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return obj
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse a response into BSON with {relaxed: false} except for primitives
|
|
51
|
+
*
|
|
52
|
+
* @param {Object} res The body from an Axios response
|
|
53
|
+
* @returns {Object}
|
|
54
|
+
*/
|
|
55
|
+
function cleanParse(res) {
|
|
56
|
+
return transformBsonPrimitives(EJSON.parse(JSON.stringify(res), { relaxed: false }))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
cleanParse
|
|
61
|
+
}
|