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.
Files changed (35) hide show
  1. package/README.md +199 -0
  2. package/actions/configurations/commerce/index.js +55 -0
  3. package/actions/configurations/export-config/index.js +259 -0
  4. package/actions/configurations/ext.config.yaml +151 -0
  5. package/actions/configurations/import-config/index.js +544 -0
  6. package/actions/configurations/registration/index.js +37 -0
  7. package/actions/configurations/sync-store-mappings-from-commerce/index.js +199 -0
  8. package/actions/configurations/system-config-list/index.js +127 -0
  9. package/actions/configurations/system-config-save/index.js +160 -0
  10. package/actions/configurations/system-config-schema/index.js +327 -0
  11. package/actions/utils.js +73 -0
  12. package/package.json +74 -0
  13. package/scripts/setup-app-config.js +114 -0
  14. package/src/abdb-config.js +241 -0
  15. package/src/abdb-helper.js +476 -0
  16. package/src/index.js +20 -0
  17. package/src/oauth1a.js +135 -0
  18. package/src/system-config-crypto.js +113 -0
  19. package/src/system-config-shared.js +89 -0
  20. package/web/src/components/App.js +47 -0
  21. package/web/src/components/AppSectionNav.js +49 -0
  22. package/web/src/components/ExtensionRegistration.js +33 -0
  23. package/web/src/components/MainPage.js +46 -0
  24. package/web/src/components/SystemConfig.js +1464 -0
  25. package/web/src/components/SystemConfigSchemaEditor.js +459 -0
  26. package/web/src/hooks/useConfirm.js +355 -0
  27. package/web/src/hooks/useSystemConfig.js +238 -0
  28. package/web/src/hooks/useSystemConfigSchema.js +102 -0
  29. package/web/src/index.js +41 -0
  30. package/web/src/schema/systemConfigSchema.js +82 -0
  31. package/web/src/settings.js +57 -0
  32. package/web/src/styles/index.css +326 -0
  33. package/web/src/theme.js +104 -0
  34. package/web/src/utils/storeMappingsFromCommerceRest.js +73 -0
  35. 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
+ }