@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,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
+ }