configuration-management 0.1.0
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/README.md +199 -0
- package/actions/configurations/commerce/index.js +55 -0
- package/actions/configurations/export-config/index.js +259 -0
- package/actions/configurations/ext.config.yaml +151 -0
- package/actions/configurations/import-config/index.js +544 -0
- package/actions/configurations/registration/index.js +37 -0
- package/actions/configurations/sync-store-mappings-from-commerce/index.js +199 -0
- package/actions/configurations/system-config-list/index.js +127 -0
- package/actions/configurations/system-config-save/index.js +160 -0
- package/actions/configurations/system-config-schema/index.js +327 -0
- package/actions/utils.js +73 -0
- package/package.json +74 -0
- package/scripts/setup-app-config.js +114 -0
- package/src/abdb-config.js +241 -0
- package/src/abdb-helper.js +476 -0
- package/src/index.js +20 -0
- package/src/oauth1a.js +135 -0
- package/src/system-config-crypto.js +113 -0
- package/src/system-config-shared.js +89 -0
- package/web/src/components/App.js +47 -0
- package/web/src/components/AppSectionNav.js +49 -0
- package/web/src/components/ExtensionRegistration.js +33 -0
- package/web/src/components/MainPage.js +46 -0
- package/web/src/components/SystemConfig.js +1464 -0
- package/web/src/components/SystemConfigSchemaEditor.js +459 -0
- package/web/src/hooks/useConfirm.js +355 -0
- package/web/src/hooks/useSystemConfig.js +238 -0
- package/web/src/hooks/useSystemConfigSchema.js +102 -0
- package/web/src/index.js +41 -0
- package/web/src/schema/systemConfigSchema.js +82 -0
- package/web/src/settings.js +57 -0
- package/web/src/styles/index.css +326 -0
- package/web/src/theme.js +104 -0
- package/web/src/utils/storeMappingsFromCommerceRest.js +73 -0
- package/web/src/utils.js +52 -0
|
@@ -0,0 +1,476 @@
|
|
|
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
|
+
|
|
13
|
+
const { generateAccessToken } = require('@adobe/aio-lib-core-auth')
|
|
14
|
+
const libDb = require('@adobe/aio-lib-db')
|
|
15
|
+
|
|
16
|
+
const COLLECTION_IMPORT_QUEUE = 'import_queue'
|
|
17
|
+
const IMPORT_PIPELINE_COLLECTIONS = [COLLECTION_IMPORT_QUEUE]
|
|
18
|
+
const ABDB_SCOPES = ['adobeio.abdata.read', 'adobeio.abdata.write', 'adobeio.abdata.manage']
|
|
19
|
+
|
|
20
|
+
function isUnsetOauthInput (value) {
|
|
21
|
+
if (value == null || value === '') return true
|
|
22
|
+
const s = String(value).trim()
|
|
23
|
+
return s === '' || s.startsWith('$')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function normalizeScopesToArray (scopes) {
|
|
27
|
+
if (!scopes) return []
|
|
28
|
+
if (Array.isArray(scopes)) return scopes.filter(Boolean).map(String)
|
|
29
|
+
const s = String(scopes).trim()
|
|
30
|
+
if (!s) return []
|
|
31
|
+
return s.split(/[\s,]+/).filter(Boolean)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get ABDB client. Requires action to have include-ims-credentials: true.
|
|
36
|
+
*
|
|
37
|
+
* Region resolution (first non-empty wins):
|
|
38
|
+
* 1. options.region — explicit override at the call site
|
|
39
|
+
* 2. params.AIO_DB_REGION — action input from ext.config.yaml
|
|
40
|
+
* 3. process.env.AIO_DB_REGION — local .env when running aio app run
|
|
41
|
+
*
|
|
42
|
+
* Throws if none is configured — we never silently pick a region for you.
|
|
43
|
+
*
|
|
44
|
+
* @param {object} params - Action params (must contain OAuth credentials for generateAccessToken)
|
|
45
|
+
* @param {object} [options]
|
|
46
|
+
* @param {string} [options.region] - explicit region override
|
|
47
|
+
* @returns {Promise<{client: object, close: function}>}
|
|
48
|
+
*/
|
|
49
|
+
async function getClient (params, options = {}) {
|
|
50
|
+
const tokenResponse = await generateAccessToken(params)
|
|
51
|
+
const token = tokenResponse.access_token || tokenResponse
|
|
52
|
+
const region = options.region || params?.AIO_DB_REGION || process.env.AIO_DB_REGION
|
|
53
|
+
if (!region || typeof region !== 'string' || !region.trim()) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
'ABDB region not configured: set AIO_DB_REGION in .env and pass it through ext.config.yaml inputs'
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const db = await libDb.init({ token, region: region.trim() })
|
|
60
|
+
const client = await db.connect()
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
client,
|
|
64
|
+
close: () => client.close()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get collection by name
|
|
70
|
+
* @param {object} client - ABDB client from getClient()
|
|
71
|
+
* @param {string} collectionName - Collection name
|
|
72
|
+
* @returns {Promise<object>} MongoDB-style collection
|
|
73
|
+
*/
|
|
74
|
+
async function getCollection (client, collectionName) {
|
|
75
|
+
return client.collection(collectionName)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get collection by name
|
|
80
|
+
* @param {object} client - ABDB client from getClient()
|
|
81
|
+
* @param {string} collectionName - Collection name
|
|
82
|
+
* @returns {Promise<object>} MongoDB-style collection
|
|
83
|
+
*/
|
|
84
|
+
function getCollectionByName (client,collectionName) {
|
|
85
|
+
return client.collection(collectionName)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Normalize listCollections API response to a Set of collection names.
|
|
90
|
+
* @param {*} raw
|
|
91
|
+
* @returns {Set<string>}
|
|
92
|
+
*/
|
|
93
|
+
function collectionNamesFromListResponse (raw) {
|
|
94
|
+
const out = new Set()
|
|
95
|
+
if (!raw) return out
|
|
96
|
+
const visit = (item) => {
|
|
97
|
+
if (typeof item === 'string') {
|
|
98
|
+
out.add(item)
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
if (item && typeof item === 'object') {
|
|
102
|
+
const n = item.name ?? item.collectionName
|
|
103
|
+
if (typeof n === 'string') out.add(n)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(raw)) {
|
|
107
|
+
raw.forEach(visit)
|
|
108
|
+
return out
|
|
109
|
+
}
|
|
110
|
+
if (typeof raw === 'object') {
|
|
111
|
+
const nested = raw.collections ?? raw.cursor?.firstBatch ?? raw.data
|
|
112
|
+
if (Array.isArray(nested)) {
|
|
113
|
+
nested.forEach(visit)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return out
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Ensure import pipeline collections exist in ABDB (recreate if dropped in console).
|
|
121
|
+
* Uses listCollections when possible; falls back to createCollection with duplicate tolerance.
|
|
122
|
+
*
|
|
123
|
+
* @param {object} client - DbClient from getClient().client
|
|
124
|
+
* @param {object} [options]
|
|
125
|
+
* @param {{ info?: function, warn?: function }} [options.logger] - optional aio logger
|
|
126
|
+
* @returns {Promise<void>}
|
|
127
|
+
*/
|
|
128
|
+
async function ensureImportCollectionsExist (client, options = {}) {
|
|
129
|
+
const log = options.logger || { info: () => {}, warn: () => {} }
|
|
130
|
+
let existing = new Set()
|
|
131
|
+
try {
|
|
132
|
+
const raw = await client.listCollections({})
|
|
133
|
+
existing = collectionNamesFromListResponse(raw)
|
|
134
|
+
} catch (e) {
|
|
135
|
+
log.warn(`ensureImportCollectionsExist: listCollections failed (${e.message}); attempting createCollection for each pipeline collection`)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const created = []
|
|
139
|
+
for (const name of IMPORT_PIPELINE_COLLECTIONS) {
|
|
140
|
+
if (existing.has(name)) continue
|
|
141
|
+
try {
|
|
142
|
+
await client.createCollection(name)
|
|
143
|
+
created.push(name)
|
|
144
|
+
} catch (err) {
|
|
145
|
+
const m = (err && err.message) ? String(err.message) : String(err)
|
|
146
|
+
if (/exist|already|duplicate/i.test(m)) {
|
|
147
|
+
continue
|
|
148
|
+
}
|
|
149
|
+
throw err
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (created.length) {
|
|
153
|
+
log.info(`ensureImportCollectionsExist: created ABDB collections: ${created.join(', ')}`)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* OAuth Server-to-Server (client_credentials) via @adobe/aio-lib-ims — same pattern as workspace .env OAUTH_*.
|
|
159
|
+
*
|
|
160
|
+
* @param {object} params - Must include OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_ORG_ID; OAUTH_SCOPES merged with ABDB scopes
|
|
161
|
+
* @returns {Promise<string|null>} token or null if required params are missing
|
|
162
|
+
*/
|
|
163
|
+
async function fetchImsTokenFromClientCredentials (params = {}) {
|
|
164
|
+
const clientId = params.OAUTH_CLIENT_ID
|
|
165
|
+
const clientSecret = params.OAUTH_CLIENT_SECRET
|
|
166
|
+
const orgId = params.OAUTH_ORG_ID
|
|
167
|
+
if (isUnsetOauthInput(clientId) || isUnsetOauthInput(clientSecret) || isUnsetOauthInput(orgId)) {
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
const extra = normalizeScopesToArray(params.OAUTH_SCOPES)
|
|
171
|
+
const scopes = [...new Set([...ABDB_SCOPES, ...extra])]
|
|
172
|
+
if (scopes.length === 0) {
|
|
173
|
+
throw new Error('No IMS scopes resolved for ABDB; set OAUTH_SCOPES or rely on default ABDB scopes')
|
|
174
|
+
}
|
|
175
|
+
const { Ims } = require('@adobe/aio-lib-ims')
|
|
176
|
+
const ims = new Ims()
|
|
177
|
+
const tokenResult = await ims.getAccessTokenByClientCredentials(clientId, clientSecret, orgId, scopes)
|
|
178
|
+
const token =
|
|
179
|
+
tokenResult?.access_token?.token ||
|
|
180
|
+
(typeof tokenResult?.payload?.access_token === 'string' ? tokenResult.payload.access_token : null)
|
|
181
|
+
if (!token) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`IMS client_credentials failed or returned no token: ${JSON.stringify(tokenResult?.payload || tokenResult)}`
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
return token
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Resolve IMS bearer token for ABDB.
|
|
191
|
+
* Order: options.token → params access_token → AIO_DB_IMS_TOKEN → OAUTH_* client_credentials (@adobe/aio-lib-ims) → @adobe/aio-lib-core-auth
|
|
192
|
+
*
|
|
193
|
+
* @param {object} params - Runtime action params
|
|
194
|
+
* @param {object} options - { token?: string }
|
|
195
|
+
* @returns {Promise<string>}
|
|
196
|
+
*/
|
|
197
|
+
async function resolveImsToken (params = {}, options = {}) {
|
|
198
|
+
if (options.token != null && String(options.token).trim() !== '') {
|
|
199
|
+
return String(options.token).trim()
|
|
200
|
+
}
|
|
201
|
+
const fromParams = params.access_token || params.ACCESS_TOKEN || params.__oauth?.access_token
|
|
202
|
+
if (fromParams != null && String(fromParams).trim() !== '') {
|
|
203
|
+
return String(fromParams).trim()
|
|
204
|
+
}
|
|
205
|
+
if (process.env.AIO_DB_IMS_TOKEN != null && String(process.env.AIO_DB_IMS_TOKEN).trim() !== '') {
|
|
206
|
+
return String(process.env.AIO_DB_IMS_TOKEN).trim()
|
|
207
|
+
}
|
|
208
|
+
const fromOAuth = await fetchImsTokenFromClientCredentials(params)
|
|
209
|
+
if (fromOAuth) {
|
|
210
|
+
return fromOAuth
|
|
211
|
+
}
|
|
212
|
+
let generateAccessTokenFn
|
|
213
|
+
try {
|
|
214
|
+
({ generateAccessToken: generateAccessTokenFn } = require('@adobe/aio-lib-core-auth'))
|
|
215
|
+
} catch {
|
|
216
|
+
throw new Error(
|
|
217
|
+
'ABDB IMS token missing: set OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_ORG_ID, OAUTH_SCOPES on the action; ' +
|
|
218
|
+
'or pass access_token / options.token; or set AIO_DB_IMS_TOKEN; ' +
|
|
219
|
+
'or install @adobe/aio-lib-core-auth.'
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
const tokenResponse = await generateAccessTokenFn(params)
|
|
223
|
+
return tokenResponse.access_token?.token || tokenResponse.access_token || tokenResponse
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get ABDB client. Requires a valid IMS access token (see resolveImsToken).
|
|
228
|
+
*
|
|
229
|
+
* @param {object} params - Action params (for generateAccessToken when package is used)
|
|
230
|
+
* @param {object} options - { token?: string, region?: string, ow?: { namespace?: string } }
|
|
231
|
+
* @returns {Promise<{ db: object, client: object, close: () => Promise<void> }>}
|
|
232
|
+
*/
|
|
233
|
+
async function getClientAbdb (params = {}, options = {}) {
|
|
234
|
+
const token = await resolveImsToken(params, options)
|
|
235
|
+
const region = options.region || process.env.AIO_DB_REGION || 'amer'
|
|
236
|
+
const ow = options.ow
|
|
237
|
+
|
|
238
|
+
const db = await libDb.init({ token, region, ...(ow ? { ow } : {}) })
|
|
239
|
+
const client = await db.connect()
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
db,
|
|
243
|
+
client,
|
|
244
|
+
close: async () => client.close()
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Run work with a connected client; always closes the client in finally.
|
|
250
|
+
*
|
|
251
|
+
* @param {object} params
|
|
252
|
+
* @param {(client: object) => Promise<*>} fn
|
|
253
|
+
* @param {object} [options]
|
|
254
|
+
* @returns {Promise<*>}
|
|
255
|
+
*/
|
|
256
|
+
async function withDbClient (params, fn, options = {}) {
|
|
257
|
+
const { client, close } = await getClientAbdb(params, options)
|
|
258
|
+
try {
|
|
259
|
+
return await fn(client)
|
|
260
|
+
} finally {
|
|
261
|
+
await close()
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// ——— DbClient (database-level) ———
|
|
265
|
+
|
|
266
|
+
async function dbStats (client, options = {}) {
|
|
267
|
+
return client.dbStats(options)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function orgStats (client, options = {}) {
|
|
271
|
+
return client.orgStats(options)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function listCollections (client, filter = {}, options = {}) {
|
|
275
|
+
return client.listCollections(filter, options)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function createCollection (client, name, options = {}) {
|
|
279
|
+
return client.createCollection(name, options)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ——— DbCollection: writes ———
|
|
283
|
+
|
|
284
|
+
async function insertOne (client, collectionName, document, options = {}) {
|
|
285
|
+
return getCollectionByName(client, collectionName).insertOne(document, options)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function insertMany (client, collectionName, documents, options = {}) {
|
|
289
|
+
return getCollectionByName(client, collectionName).insertMany(documents, options)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function updateOne (client, collectionName, filter, update, options = {}) {
|
|
293
|
+
return getCollectionByName(client, collectionName).updateOne(filter, update, options)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function updateMany (client, collectionName, filter, update, options = {}) {
|
|
297
|
+
return getCollectionByName(client, collectionName).updateMany(filter, update, options)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function replaceOne (client, collectionName, filter, replacement, options = {}) {
|
|
301
|
+
return getCollectionByName(client, collectionName).replaceOne(filter, replacement, options)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function deleteOne (client, collectionName, filter, options = {}) {
|
|
305
|
+
return getCollectionByName(client, collectionName).deleteOne(filter, options)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function deleteMany (client, collectionName, filter, options = {}) {
|
|
309
|
+
return getCollectionByName(client, collectionName).deleteMany(filter, options)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function bulkWrite (client, collectionName, operations, options = {}) {
|
|
313
|
+
return getCollectionByName(client, collectionName).bulkWrite(operations, options)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function findOneAndUpdate (client, collectionName, filter, update, options = {}) {
|
|
317
|
+
return getCollectionByName(client, collectionName).findOneAndUpdate(filter, update, options)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function findOneAndReplace (client, collectionName, filter, replacement, options = {}) {
|
|
321
|
+
return getCollectionByName(client, collectionName).findOneAndReplace(filter, replacement, options)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function findOneAndDelete (client, collectionName, filter, options = {}) {
|
|
325
|
+
return getCollectionByName(client, collectionName).findOneAndDelete(filter, options)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ——— DbCollection: reads ———
|
|
329
|
+
|
|
330
|
+
async function findOne (client, collectionName, filter, options = {}) {
|
|
331
|
+
return getCollectionByName(client, collectionName).findOne(filter, options)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** ABDB findOne throws DbError with message "Document not found" when no match — unlike MongoDB null. */
|
|
335
|
+
function isDocumentNotFoundDbError (err) {
|
|
336
|
+
const msg = err != null && typeof err.message === 'string' ? err.message : ''
|
|
337
|
+
return err?.name === 'DbError' && msg.includes('Document not found')
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Like findOne, but returns null when no document matches (ABDB error → null).
|
|
342
|
+
*
|
|
343
|
+
* @returns {Promise<object|null>}
|
|
344
|
+
*/
|
|
345
|
+
async function findOneOrNull (client, collectionName, filter, options = {}) {
|
|
346
|
+
try {
|
|
347
|
+
return await findOne(client, collectionName, filter, options)
|
|
348
|
+
} catch (err) {
|
|
349
|
+
if (isDocumentNotFoundDbError(err)) {
|
|
350
|
+
return null
|
|
351
|
+
}
|
|
352
|
+
throw err
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @returns {object} FindCursor — await cursor.close() when done (or call client.close()).
|
|
358
|
+
*/
|
|
359
|
+
function find (client, collectionName, filter = {}, options = {}) {
|
|
360
|
+
return getCollectionByName(client, collectionName).find(filter, options)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function findArray (client, collectionName, filter = {}, options = {}) {
|
|
364
|
+
return getCollectionByName(client, collectionName).findArray(filter, options)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** find() + toArray() with cursor closed after use. */
|
|
368
|
+
async function findToArray (client, collectionName, filter = {}, options = {}) {
|
|
369
|
+
const cursor = find(client, collectionName, filter, options)
|
|
370
|
+
try {
|
|
371
|
+
return await cursor.toArray()
|
|
372
|
+
} finally {
|
|
373
|
+
await cursor.close()
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function countDocuments (client, collectionName, filter = {}, options = {}) {
|
|
378
|
+
return getCollectionByName(client, collectionName).countDocuments(filter, options)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function estimatedDocumentCount (client, collectionName, options = {}) {
|
|
382
|
+
return getCollectionByName(client, collectionName).estimatedDocumentCount(options)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function distinct (client, collectionName, field, filter = {}, options = {}) {
|
|
386
|
+
return getCollectionByName(client, collectionName).distinct(field, filter, options)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ——— aggregate ———
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* @returns {object} AggregateCursor — close when done.
|
|
393
|
+
*/
|
|
394
|
+
function aggregate (client, collectionName, pipeline = [], options = {}) {
|
|
395
|
+
return getCollectionByName(client, collectionName).aggregate(pipeline, options)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function aggregateToArray (client, collectionName, pipeline = [], options = {}) {
|
|
399
|
+
const cursor = aggregate(client, collectionName, pipeline, options)
|
|
400
|
+
try {
|
|
401
|
+
return await cursor.toArray()
|
|
402
|
+
} finally {
|
|
403
|
+
await cursor.close()
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ——— indexes & collection admin ———
|
|
408
|
+
|
|
409
|
+
async function getIndexes (client, collectionName) {
|
|
410
|
+
return getCollectionByName(client, collectionName).getIndexes()
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async function createIndex (client, collectionName, specification, options = {}) {
|
|
414
|
+
return getCollectionByName(client, collectionName).createIndex(specification, options)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async function dropIndex (client, collectionName, index, options = {}) {
|
|
418
|
+
return getCollectionByName(client, collectionName).dropIndex(index, options)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async function collectionStats (client, collectionName, options = {}) {
|
|
422
|
+
return getCollectionByName(client, collectionName).stats(options)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function dropCollection (client, collectionName, options = {}) {
|
|
426
|
+
return getCollectionByName(client, collectionName).drop(options)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
async function renameCollection (client, collectionName, newCollectionName, options = {}) {
|
|
430
|
+
const col = getCollectionByName(client, collectionName)
|
|
431
|
+
await col.renameCollection(newCollectionName, options)
|
|
432
|
+
return col
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
module.exports = {
|
|
436
|
+
COLLECTION_IMPORT_QUEUE,
|
|
437
|
+
IMPORT_PIPELINE_COLLECTIONS,
|
|
438
|
+
getClient,
|
|
439
|
+
getCollection,
|
|
440
|
+
ensureImportCollectionsExist,
|
|
441
|
+
resolveImsToken,
|
|
442
|
+
getCollectionByName,
|
|
443
|
+
getClientAbdb,
|
|
444
|
+
withDbClient,
|
|
445
|
+
dbStats,
|
|
446
|
+
orgStats,
|
|
447
|
+
listCollections,
|
|
448
|
+
createCollection,
|
|
449
|
+
insertOne,
|
|
450
|
+
insertMany,
|
|
451
|
+
updateOne,
|
|
452
|
+
updateMany,
|
|
453
|
+
replaceOne,
|
|
454
|
+
deleteOne,
|
|
455
|
+
deleteMany,
|
|
456
|
+
bulkWrite,
|
|
457
|
+
findOneAndUpdate,
|
|
458
|
+
findOneAndReplace,
|
|
459
|
+
findOneAndDelete,
|
|
460
|
+
findOne,
|
|
461
|
+
findOneOrNull,
|
|
462
|
+
find,
|
|
463
|
+
findArray,
|
|
464
|
+
findToArray,
|
|
465
|
+
countDocuments,
|
|
466
|
+
estimatedDocumentCount,
|
|
467
|
+
distinct,
|
|
468
|
+
aggregate,
|
|
469
|
+
aggregateToArray,
|
|
470
|
+
getIndexes,
|
|
471
|
+
createIndex,
|
|
472
|
+
dropIndex,
|
|
473
|
+
collectionStats,
|
|
474
|
+
dropCollection,
|
|
475
|
+
renameCollection
|
|
476
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
|
|
8
|
+
const abdbHelper = require('./abdb-helper')
|
|
9
|
+
const abdbConfig = require('./abdb-config')
|
|
10
|
+
const systemConfigShared = require('./system-config-shared')
|
|
11
|
+
const systemConfigCrypto = require('./system-config-crypto')
|
|
12
|
+
const oauth1a = require('./oauth1a')
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
...abdbHelper,
|
|
16
|
+
...abdbConfig,
|
|
17
|
+
...systemConfigShared,
|
|
18
|
+
...systemConfigCrypto,
|
|
19
|
+
...oauth1a
|
|
20
|
+
}
|
package/src/oauth1a.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
|
|
13
|
+
const Oauth1a = require('oauth-1.0a')
|
|
14
|
+
const crypto = require('crypto')
|
|
15
|
+
const got = require('got')
|
|
16
|
+
|
|
17
|
+
function getOauthClient(options, logger) {
|
|
18
|
+
const instance = {}
|
|
19
|
+
|
|
20
|
+
// Remove trailing slash if any
|
|
21
|
+
const serverUrl = options.url
|
|
22
|
+
const apiVersion = options.version
|
|
23
|
+
const oauth = Oauth1a({
|
|
24
|
+
consumer: {
|
|
25
|
+
key: options.consumerKey,
|
|
26
|
+
secret: options.consumerSecret
|
|
27
|
+
},
|
|
28
|
+
signature_method: 'HMAC-SHA256',
|
|
29
|
+
hash_function: hashFunctionSha256
|
|
30
|
+
})
|
|
31
|
+
const token = {
|
|
32
|
+
key: options.accessToken,
|
|
33
|
+
secret: options.accessTokenSecret
|
|
34
|
+
}
|
|
35
|
+
const storeCode = options.storeCode || 'default'
|
|
36
|
+
|
|
37
|
+
function hashFunctionSha256(baseString, key) {
|
|
38
|
+
return crypto.createHmac('sha256', key).update(baseString).digest('base64')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function apiCall(requestData, requestToken = '', customHeaders = {}) {
|
|
42
|
+
try {
|
|
43
|
+
const headers = {
|
|
44
|
+
...(requestToken
|
|
45
|
+
? { Authorization: 'Bearer ' + requestToken }
|
|
46
|
+
: oauth.toHeader(oauth.authorize(requestData, token))),
|
|
47
|
+
...customHeaders
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Configure HTTPS options based on environment
|
|
51
|
+
const httpsOptions = {
|
|
52
|
+
method: requestData.method,
|
|
53
|
+
headers,
|
|
54
|
+
body: requestData.body,
|
|
55
|
+
responseType: 'json',
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return await got(requestData.url, httpsOptions).json()
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.error(error)
|
|
61
|
+
|
|
62
|
+
throw error
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
instance.consumerToken = async function (loginData) {
|
|
67
|
+
return apiCall({
|
|
68
|
+
url: createUrl('integration/customer/token', storeCode),
|
|
69
|
+
method: 'POST',
|
|
70
|
+
body: loginData
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createUrl (resourceUrl, store) {
|
|
75
|
+
const s = (store != null && String(store).trim()) ? String(store).trim() : storeCode
|
|
76
|
+
return serverUrl + s + '/' + apiVersion + '/' + resourceUrl
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
instance.get = async function (resourceUrl, requestToken = '', customHeaders = {}) {
|
|
80
|
+
const requestData = {
|
|
81
|
+
url: createUrl(resourceUrl, storeCode),
|
|
82
|
+
method: 'GET'
|
|
83
|
+
}
|
|
84
|
+
return apiCall(requestData, requestToken, customHeaders)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
instance.post = async function (resourceUrl, data, requestToken = '', customHeaders = {}) {
|
|
88
|
+
const requestData = {
|
|
89
|
+
url: createUrl(resourceUrl, storeCode),
|
|
90
|
+
method: 'POST',
|
|
91
|
+
body: data
|
|
92
|
+
}
|
|
93
|
+
return apiCall(requestData, requestToken, customHeaders)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
instance.put = async function (resourceUrl, data, requestToken = '', customHeaders = {}) {
|
|
97
|
+
const requestData = {
|
|
98
|
+
url: createUrl(resourceUrl, storeCode),
|
|
99
|
+
method: 'PUT',
|
|
100
|
+
body: data
|
|
101
|
+
}
|
|
102
|
+
return apiCall(requestData, requestToken, customHeaders)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
instance.delete = async function (resourceUrl, requestToken = '', customHeaders = {}) {
|
|
106
|
+
const requestData = {
|
|
107
|
+
url: createUrl(resourceUrl, storeCode),
|
|
108
|
+
method: 'DELETE'
|
|
109
|
+
}
|
|
110
|
+
return apiCall(requestData, requestToken, customHeaders)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
instance.deleteWithBody = async function (resourceUrl, data, requestToken = '', customHeaders = {}) {
|
|
114
|
+
const requestData = {
|
|
115
|
+
url: createUrl(resourceUrl, storeCode),
|
|
116
|
+
method: 'DELETE',
|
|
117
|
+
body: data
|
|
118
|
+
}
|
|
119
|
+
return apiCall(requestData, requestToken, customHeaders)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return instance
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getCommerceOauthClient(options, logger) {
|
|
126
|
+
options.version = 'V1'
|
|
127
|
+
const storePrefix = options.storeCode ? `${options.storeCode}/` : ''
|
|
128
|
+
options.url = options.url + 'rest/' + storePrefix
|
|
129
|
+
return getOauthClient(options, logger)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
getOauthClient,
|
|
134
|
+
getCommerceOauthClient
|
|
135
|
+
}
|