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
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # configuration-management
2
+
3
+ Schema-driven system configuration for **Adobe Commerce** and **Adobe App Builder** sync applications.
4
+
5
+ Mirrors Magento's `core_config_data` model: scoped configuration (`default` / `websites` / `stores`) stored in **Adobe App Builder Database (ABDB)** with AES-256-GCM encryption for sensitive fields.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install configuration-management
11
+ ```
12
+
13
+ Your App Builder project must also have Adobe I/O runtime dependencies installed (peer dependencies):
14
+
15
+ ```bash
16
+ npm install @adobe/aio-lib-core-auth @adobe/aio-lib-db @adobe/aio-lib-ims @adobe/aio-sdk dotenv
17
+ ```
18
+
19
+ For the React Admin UI, also install Spectrum and React peers:
20
+
21
+ ```bash
22
+ npm install react react-dom @adobe/react-spectrum @adobe/uix-guest @adobe/exc-app react-router-dom react-error-boundary @spectrum-icons/workflow
23
+ ```
24
+
25
+ ## Quick start
26
+
27
+ Read a config value from ABDB inside an App Builder action:
28
+
29
+ ```js
30
+ const { getConfig } = require('configuration-management')
31
+
32
+ async function main (params) {
33
+ const apiUrl = await getConfig('sync_general/api/url', params, {
34
+ scope: 'websites',
35
+ scopeCode: 'base'
36
+ })
37
+ // ...
38
+ }
39
+ ```
40
+
41
+ ## API
42
+
43
+ ### Config resolution
44
+
45
+ | Export | Description |
46
+ |--------|-------------|
47
+ | `getConfig(path, params, options)` | Read a value with Magento-style scope inheritance |
48
+ | `clearAbdbConfigCache()` | Clear the in-process lookup cache |
49
+
50
+ ### ABDB helpers (`configuration-management/abdb`)
51
+
52
+ | Export | Description |
53
+ |--------|-------------|
54
+ | `getClient(params, options)` | Connect to ABDB using IMS credentials from action params |
55
+ | `withDbClient(params, fn, options)` | Run work with auto-close |
56
+ | `findOne`, `insertOne`, `updateOne`, … | Mongo-style collection helpers |
57
+
58
+ ### Scope / path model (`configuration-management/shared`)
59
+
60
+ | Export | Description |
61
+ |--------|-------------|
62
+ | `toStateKey(scope, scopeId, path)` | Encode `section/group/field` as ABDB document `_id` |
63
+ | `buildInheritanceChain(scope, scopeId, parentWebsiteId)` | Magento-style fallback chain |
64
+ | `isValidPath`, `normalizeScope`, `normalizeScopeId` | Validation helpers |
65
+
66
+ ### Encryption (`configuration-management/crypto`)
67
+
68
+ | Export | Description |
69
+ |--------|-------------|
70
+ | `encrypt(plaintext, params)` | AES-256-GCM encrypt for at-rest storage |
71
+ | `decrypt(ciphertext, params)` | Decrypt stored values |
72
+ | `isEncrypted(value)` | Detect `enc:v1:` wire format |
73
+
74
+ ### Commerce REST (`configuration-management/oauth1a`)
75
+
76
+ | Export | Description |
77
+ |--------|-------------|
78
+ | `getCommerceOauthClient(options, logger)` | OAuth 1.0a client for Adobe Commerce REST API |
79
+
80
+ ### React Admin UI (`configuration-management/web`)
81
+
82
+ Spectrum-based Commerce Admin extension UI for schema-driven system configuration.
83
+
84
+ ```js
85
+ import { createRoot } from 'react-dom/client'
86
+ import {
87
+ ConfigurationManagementApp,
88
+ configureWeb,
89
+ SystemConfig
90
+ } from 'configuration-management/web'
91
+ import actionUrls from './config.json' // deploy-time URLs from aio app deploy
92
+ import 'configuration-management/web/styles.css'
93
+
94
+ configureWeb({ actionUrls })
95
+
96
+ createRoot(document.getElementById('root')).render(
97
+ <ConfigurationManagementApp runtime={runtime} ims={ims} />
98
+ )
99
+ ```
100
+
101
+ | Export | Description |
102
+ |--------|-------------|
103
+ | `ConfigurationManagementApp` | Full app shell (router + Spectrum provider + UIX registration) |
104
+ | `SystemConfig` | Dynamic config form UI |
105
+ | `SystemConfigSchemaEditor` | Schema designer |
106
+ | `useSystemConfig`, `useSystemConfigSchema` | Data hooks |
107
+ | `configureWeb({ actionUrls, extensionId, actionKeys })` | Wire deploy-time action URLs before render |
108
+
109
+ Styles: `import 'configuration-management/web/styles.css'`
110
+
111
+ ### App Builder actions (`configuration-management/actions`)
112
+
113
+ OpenWhisk runtime actions and the Commerce Admin extension manifest ship with the package.
114
+
115
+ #### Automatic wiring on `npm install`
116
+
117
+ The package runs a **postinstall** script that patches your project's `app.config.yaml`
118
+ (if it exists) with:
119
+
120
+ ```yaml
121
+ extensions:
122
+ commerce/backend-ui/1:
123
+ $include: node_modules/configuration-management/actions/configurations/ext.config.yaml
124
+ ```
125
+
126
+ Requirements:
127
+
128
+ - Run `npm install` from your App Builder project root (where `app.config.yaml` lives).
129
+ - Do not use `npm install --ignore-scripts` (that skips postinstall).
130
+
131
+ Opt out for a single install:
132
+
133
+ ```bash
134
+ CONFIGURATION_MANAGEMENT_SKIP_SETUP=1 npm install configuration-management
135
+ ```
136
+
137
+ Re-run manually anytime:
138
+
139
+ ```bash
140
+ npx configuration-management-setup
141
+ ```
142
+
143
+ #### Manual wiring
144
+
145
+ If you prefer to edit `app.config.yaml` yourself:
146
+
147
+ ```yaml
148
+ extensions:
149
+ commerce/backend-ui/1:
150
+ $include: node_modules/configuration-management/actions/configurations/ext.config.yaml
151
+ ```
152
+
153
+ The bundled `ext.config.yaml` declares all actions under the `ConfigurationManagement` package
154
+ (`system-config-list`, `system-config-save`, `system-config-schema`, `export-config`,
155
+ `import-config`, `commerce-rest-get`, `sync-store-mappings-from-commerce`) plus admin menu
156
+ `registration`. It expects a `web-src/` folder at your project root for the UI shell.
157
+
158
+ **Minimal host project layout:**
159
+
160
+ ```
161
+ my-app/
162
+ ├── app.config.yaml ← $include ext.config from node_modules (above)
163
+ ├── web-src/
164
+ │ ├── index.html
165
+ │ └── src/
166
+ │ ├── index.js ← import ConfigurationManagementApp + configureWeb
167
+ │ └── config.json ← generated by aio app deploy
168
+ ├── .env
169
+ └── package.json ← depends on configuration-management
170
+ ```
171
+
172
+ Action helper utilities are also exported for custom actions:
173
+
174
+ ```js
175
+ const { errorResponse, checkMissingRequestInputs } = require('configuration-management/actions/utils')
176
+ ```
177
+
178
+ ## Environment / action inputs
179
+
180
+ | Variable | Purpose |
181
+ |----------|---------|
182
+ | `AIO_DB_REGION` | ABDB region (`amer`, `emea`, …) |
183
+ | `OAUTH_CLIENT_ID`, `OAUTH_CLIENT_SECRET`, `OAUTH_ORG_ID`, `OAUTH_SCOPES` | IMS credentials for ABDB |
184
+ | `SYSTEM_CONFIG_CRYPT_KEY` | Preferred encryption key (fallback: `OAUTH_CLIENT_SECRET`) |
185
+ | `COMMERCE_BASE_URL`, `COMMERCE_CONSUMER_*`, `COMMERCE_ACCESS_TOKEN*` | Commerce REST (for scope code resolution) |
186
+
187
+ ## Storage model
188
+
189
+ Documents in the `system_config_data` collection:
190
+
191
+ ```
192
+ { _id, scope, scope_id, path, value, createdAt, updatedAt }
193
+ ```
194
+
195
+ Config paths use the format `section/group/field` (e.g. `sync_general/api/url`).
196
+
197
+ ## License
198
+
199
+ Apache-2.0
@@ -0,0 +1,55 @@
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 { Core } = require('@adobe/aio-sdk')
14
+ const { errorResponse, checkMissingRequestInputs } = require('../../utils')
15
+ const { getCommerceOauthClient } = require('configuration-management/oauth1a')
16
+
17
+ async function main(params) {
18
+ const logger = Core.Logger('main', { level: params.LOG_LEVEL || 'info' })
19
+
20
+ try {
21
+ const requiredParams = ['operation', 'COMMERCE_BASE_URL']
22
+ const requiredHeaders = ['Authorization']
23
+ const errorMessage = checkMissingRequestInputs(params, requiredParams, requiredHeaders)
24
+ if (errorMessage) {
25
+ // return and log client errors
26
+ return errorResponse(400, errorMessage, logger)
27
+ }
28
+
29
+ const { operation } = params
30
+ const oauth = getCommerceOauthClient(
31
+ {
32
+ url: params.COMMERCE_BASE_URL,
33
+ consumerKey: params.COMMERCE_CONSUMER_KEY,
34
+ consumerSecret: params.COMMERCE_CONSUMER_SECRET,
35
+ accessToken: params.COMMERCE_ACCESS_TOKEN,
36
+ accessTokenSecret: params.COMMERCE_ACCESS_TOKEN_SECRET
37
+ },
38
+ logger
39
+ )
40
+
41
+ const content = await oauth.get(operation)
42
+
43
+ return {
44
+ statusCode: 200,
45
+ body: content
46
+ }
47
+ } catch (error) {
48
+ // log any server errors
49
+ logger.error(error)
50
+ // return with 500
51
+ return errorResponse(500, error, logger)
52
+ }
53
+ }
54
+
55
+ exports.main = main
@@ -0,0 +1,259 @@
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
+ // Export the entire system_config (schema + values) to a portable JSON
9
+ // document. The result includes:
10
+ // - schema : the document stored under system_config_schema._id = 'v1'
11
+ // - values : every row in system_config_data, as { scope, scope_id, path, value }
12
+ // - meta : timestamp + counts so the operator can sanity-check the dump
13
+ //
14
+ // Sensitive fields are exported as their ENCRYPTED ciphertext so the dump is
15
+ // safe to share, but it can only be re-imported into a workspace whose
16
+ // SYSTEM_CONFIG_CRYPT_KEY matches the one used when these values were saved.
17
+ //
18
+ // Trigger from the UI's "Export → JSON" button, or invoke directly:
19
+ // POST .../system-config-export
20
+ // body: {} → full dump
21
+ // body: { schemaOnly: true } → omit values
22
+ // body: { valuesOnly: true } → omit schema
23
+ // body: { scopes: ['default','websites'] } → filter by scope tuple
24
+ //
25
+ // Response: { ok, dump }, where `dump` is the JSON the caller saves as a file.
26
+
27
+ const { Core } = require('@adobe/aio-sdk')
28
+ const { errorResponse } = require('../../utils')
29
+ const { getClient } = require('configuration-management/abdb')
30
+ const { isEncrypted, decrypt } = require('configuration-management/crypto')
31
+ const { getCommerceOauthClient } = require('configuration-management/oauth1a')
32
+
33
+ const SCHEMA_COLLECTION = 'system_config_schema'
34
+ const SCHEMA_DOC_ID = 'v1'
35
+ const DATA_COLLECTION = 'system_config_data'
36
+ // Bumped to v2 when we started decrypting sensitive values into plaintext on
37
+ // export. Importers ≥ v2 know to re-encrypt with the target env's key.
38
+ const EXPORT_VERSION = 2
39
+
40
+ /**
41
+ * Walk a schema doc and return the set of "section/group/field" paths whose
42
+ * field is marked `sensitive: true`. Used by both export (decide what to
43
+ * decrypt) and import (decide what to re-encrypt against the target's key).
44
+ */
45
+ function deriveLanguageCode (code) {
46
+ const m = String(code || '').toLowerCase().match(/^([a-z]{2})_/)
47
+ return m ? m[1] : 'en'
48
+ }
49
+
50
+ /**
51
+ * Build a Commerce-style store_mappings blob from live REST data:
52
+ * { storeId: { code, language_code, website_code, website_id } }
53
+ * Returns null if Commerce credentials aren't configured or the call fails.
54
+ * Embedding this in every export lets cross-env imports translate
55
+ * website_id / store_id by matching `website_code` / store `code` against the
56
+ * target env's own Commerce, regardless of whether sync-store-mappings was
57
+ * ever run on the source.
58
+ */
59
+ async function fetchSourceStoreMappingsFromCommerce (params, logger) {
60
+ if (!params.COMMERCE_BASE_URL || !params.COMMERCE_CONSUMER_KEY) return null
61
+ try {
62
+ const oauth = getCommerceOauthClient({
63
+ url: params.COMMERCE_BASE_URL,
64
+ consumerKey: params.COMMERCE_CONSUMER_KEY,
65
+ consumerSecret: params.COMMERCE_CONSUMER_SECRET,
66
+ accessToken: params.COMMERCE_ACCESS_TOKEN,
67
+ accessTokenSecret: params.COMMERCE_ACCESS_TOKEN_SECRET
68
+ }, logger)
69
+ const [storeViews, websites] = await Promise.all([
70
+ oauth.get('store/storeViews'),
71
+ oauth.get('store/websites')
72
+ ])
73
+ const websiteById = new Map()
74
+ for (const w of websites || []) {
75
+ if (w && w.id != null) websiteById.set(String(w.id), w)
76
+ }
77
+ const mapping = {}
78
+ for (const sv of storeViews || []) {
79
+ if (!sv || sv.id == null) continue
80
+ const storeId = String(sv.id)
81
+ if (storeId === '0' || sv.code === 'admin') continue
82
+ const websiteId = sv.website_id != null ? String(sv.website_id) : ''
83
+ const website = websiteById.get(websiteId)
84
+ mapping[storeId] = {
85
+ code: String(sv.code || ''),
86
+ language_code: deriveLanguageCode(sv.code),
87
+ website_code: website ? String(website.code || '') : '',
88
+ website_id: websiteId
89
+ }
90
+ }
91
+ return Object.keys(mapping).length ? mapping : null
92
+ } catch (err) {
93
+ if (logger) logger.warn(`Export: Commerce store_mappings lookup failed: ${err.message}`)
94
+ return null
95
+ }
96
+ }
97
+
98
+ function collectSensitivePaths (schema) {
99
+ const out = new Set()
100
+ if (!schema || !Array.isArray(schema.sections)) return out
101
+ for (const s of schema.sections) {
102
+ if (!s || !Array.isArray(s.groups)) continue
103
+ for (const g of s.groups) {
104
+ if (!g || !Array.isArray(g.fields)) continue
105
+ for (const f of g.fields) {
106
+ if (f && f.sensitive) out.add(`${s.id}/${g.id}/${f.id}`)
107
+ }
108
+ }
109
+ }
110
+ return out
111
+ }
112
+
113
+ async function tryFindOne (collection, query) {
114
+ try {
115
+ const arr = await collection.find(query).limit(1).toArray()
116
+ return arr && arr.length ? arr[0] : null
117
+ } catch (err) {
118
+ const msg = err && err.message ? String(err.message) : String(err)
119
+ if (/not found/i.test(msg)) return null
120
+ throw err
121
+ }
122
+ }
123
+
124
+ async function main (params) {
125
+ const logger = Core.Logger('export-config', { level: params.LOG_LEVEL || 'info' })
126
+
127
+ const schemaOnly = params.schemaOnly === true || params.schemaOnly === 'true'
128
+ const valuesOnly = params.valuesOnly === true || params.valuesOnly === 'true'
129
+ const scopeFilter = Array.isArray(params.scopes) && params.scopes.length
130
+ ? new Set(params.scopes.map(String))
131
+ : null
132
+
133
+ let dbHandle
134
+ try {
135
+ dbHandle = await getClient(params)
136
+ } catch (e) {
137
+ logger.error(`ABDB connect failed: ${e.message}`)
138
+ return errorResponse(500, `ABDB connect failed: ${e.message}`, logger)
139
+ }
140
+ const { client, close } = dbHandle
141
+
142
+ try {
143
+ let schema = null
144
+ if (!valuesOnly) {
145
+ const schemaCol = await client.collection(SCHEMA_COLLECTION)
146
+ const doc = await tryFindOne(schemaCol, { _id: SCHEMA_DOC_ID })
147
+ schema = doc && doc.schema ? doc.schema : { sections: [] }
148
+ }
149
+
150
+ // We need the schema to know which paths are sensitive, even when the
151
+ // caller asked for valuesOnly — load it locally without including it in
152
+ // the dump.
153
+ let schemaForFlags = schema
154
+ if (!schemaForFlags) {
155
+ try {
156
+ const schemaCol = await client.collection(SCHEMA_COLLECTION)
157
+ const doc = await tryFindOne(schemaCol, { _id: SCHEMA_DOC_ID })
158
+ schemaForFlags = doc && doc.schema ? doc.schema : null
159
+ } catch (_) { /* ok if missing */ }
160
+ }
161
+ const sensitivePaths = collectSensitivePaths(schemaForFlags)
162
+
163
+ // Always pull the SOURCE env's Commerce mapping so we can stamp every
164
+ // website/store-scoped row with its scope_code (website_code / store
165
+ // view code). The importer then needs only the target's Commerce — no
166
+ // separate storeMappings blob has to be carried between envs.
167
+ const storeMappingsFromCommerce = await fetchSourceStoreMappingsFromCommerce(params, logger)
168
+ // Build quick lookup tables: websiteId → website_code, storeId → store code.
169
+ const websiteCodeById = new Map()
170
+ const storeCodeById = new Map()
171
+ if (storeMappingsFromCommerce) {
172
+ for (const [storeId, m] of Object.entries(storeMappingsFromCommerce)) {
173
+ if (!m) continue
174
+ if (m.website_id != null && m.website_code) {
175
+ websiteCodeById.set(String(m.website_id), String(m.website_code))
176
+ }
177
+ if (m.code) storeCodeById.set(String(storeId), String(m.code))
178
+ }
179
+ }
180
+
181
+ let values = []
182
+ let decryptedCount = 0
183
+ let decryptFailedCount = 0
184
+ if (!schemaOnly) {
185
+ const dataCol = await client.collection(DATA_COLLECTION)
186
+ const docs = await dataCol.find({}).toArray().catch(() => [])
187
+ for (const d of docs) {
188
+ if (!d || typeof d.path !== 'string') continue
189
+ if (scopeFilter && !scopeFilter.has(d.scope)) continue
190
+ let value = d.value
191
+ // Decrypt sensitive ciphertext using THIS env's key so the dump is
192
+ // portable to any other workspace. The recipient will re-encrypt
193
+ // with its own key based on schema.sensitive flags. This means the
194
+ // exported JSON file contains plaintext secrets — treat it as
195
+ // sensitive.
196
+ if (isEncrypted(value)) {
197
+ try {
198
+ value = decrypt(value, params)
199
+ decryptedCount++
200
+ } catch (e) {
201
+ // Couldn't decrypt — keep the ciphertext envelope so a target
202
+ // workspace with the matching key can still pick it up via the
203
+ // legacy sourceCryptKey path.
204
+ decryptFailedCount++
205
+ logger.warn(`Export: failed to decrypt ${d.path} @ ${d.scope}:${d.scope_id}: ${e.message}`)
206
+ }
207
+ }
208
+ // Tag the row with the source env's code (website_code or store
209
+ // view code). At import time the recipient looks up its own Commerce
210
+ // and resolves scope_code → target scope_id directly, with no need
211
+ // to ship a separate storeMappings blob.
212
+ let scopeCode
213
+ if (d.scope === 'websites') scopeCode = websiteCodeById.get(String(d.scope_id))
214
+ else if (d.scope === 'stores') scopeCode = storeCodeById.get(String(d.scope_id))
215
+ values.push({
216
+ scope: d.scope,
217
+ scope_id: d.scope_id,
218
+ ...(scopeCode ? { scope_code: scopeCode } : {}),
219
+ path: d.path,
220
+ value
221
+ })
222
+ }
223
+ // Stable ordering for diffable dumps.
224
+ values.sort((a, b) => {
225
+ if (a.scope !== b.scope) return a.scope.localeCompare(b.scope)
226
+ if (a.scope_id !== b.scope_id) return String(a.scope_id).localeCompare(String(b.scope_id))
227
+ return a.path.localeCompare(b.path)
228
+ })
229
+ }
230
+
231
+ const dump = {
232
+ __format: 'adobe-commerce-app-builder/system-config-export',
233
+ __version: EXPORT_VERSION,
234
+ exportedAt: new Date().toISOString(),
235
+ // List of sensitive paths so the importer can re-encrypt them with the
236
+ // target env's key without needing to re-derive from the schema.
237
+ sensitivePaths: Array.from(sensitivePaths),
238
+ sensitiveDecrypted: decryptedCount,
239
+ sensitiveDecryptFailed: decryptFailedCount,
240
+ counts: {
241
+ sections: schema ? (schema.sections || []).length : 0,
242
+ values: values.length,
243
+ scopeCoded: values.filter(v => v.scope_code).length
244
+ },
245
+ ...(valuesOnly ? {} : { schema }),
246
+ ...(schemaOnly ? {} : { values })
247
+ }
248
+
249
+ logger.info(`Exported: ${dump.counts.sections} section(s), ${dump.counts.values} value(s)`)
250
+ return { statusCode: 200, body: { ok: true, dump } }
251
+ } catch (error) {
252
+ logger.error(error)
253
+ return errorResponse(500, error.message || 'Export failed', logger)
254
+ } finally {
255
+ try { await close() } catch (_) {}
256
+ }
257
+ }
258
+
259
+ exports.main = main
@@ -0,0 +1,151 @@
1
+ operations:
2
+ view:
3
+ - type: web
4
+ impl: index.html
5
+ # Action sources live alongside this manifest (same directory).
6
+ actions: .
7
+ # Host App Builder apps override `web` to point at their UI shell (see README).
8
+ web: ../../../../web-src
9
+ runtimeManifest:
10
+ packages:
11
+ admin-ui-sdk:
12
+ license: Apache-2.0
13
+ actions:
14
+ registration:
15
+ function: registration/index.js
16
+ web: 'yes'
17
+ runtime: 'nodejs:20'
18
+ inputs:
19
+ LOG_LEVEL: debug
20
+ annotations:
21
+ require-adobe-auth: true
22
+ final: true
23
+ ConfigurationManagement:
24
+ license: Apache-2.0
25
+ actions:
26
+ commerce-rest-get:
27
+ function: commerce/index.js
28
+ web: 'yes'
29
+ runtime: 'nodejs:20'
30
+ inputs:
31
+ LOG_LEVEL: debug
32
+ COMMERCE_BASE_URL: $COMMERCE_BASE_URL
33
+ COMMERCE_CONSUMER_KEY: $COMMERCE_CONSUMER_KEY
34
+ COMMERCE_CONSUMER_SECRET: $COMMERCE_CONSUMER_SECRET
35
+ COMMERCE_ACCESS_TOKEN: $COMMERCE_ACCESS_TOKEN
36
+ COMMERCE_ACCESS_TOKEN_SECRET: $COMMERCE_ACCESS_TOKEN_SECRET
37
+ annotations:
38
+ require-adobe-auth: false
39
+ final: true
40
+ system-config-list:
41
+ function: system-config-list/index.js
42
+ web: 'yes'
43
+ runtime: 'nodejs:20'
44
+ inputs:
45
+ LOG_LEVEL: debug
46
+ SYSTEM_CONFIG_CRYPT_KEY: $SYSTEM_CONFIG_CRYPT_KEY
47
+ OAUTH_CLIENT_ID: $OAUTH_CLIENT_ID
48
+ OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
49
+ OAUTH_ORG_ID: $OAUTH_ORG_ID
50
+ OAUTH_SCOPES: $OAUTH_SCOPES
51
+ AIO_DB_REGION: $AIO_DB_REGION
52
+ annotations:
53
+ require-adobe-auth: false
54
+ include-ims-credentials: true
55
+ final: true
56
+ system-config-save:
57
+ function: system-config-save/index.js
58
+ web: 'yes'
59
+ runtime: 'nodejs:20'
60
+ inputs:
61
+ LOG_LEVEL: debug
62
+ SYSTEM_CONFIG_CRYPT_KEY: $SYSTEM_CONFIG_CRYPT_KEY
63
+ OAUTH_CLIENT_ID: $OAUTH_CLIENT_ID
64
+ OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
65
+ OAUTH_ORG_ID: $OAUTH_ORG_ID
66
+ OAUTH_SCOPES: $OAUTH_SCOPES
67
+ AIO_DB_REGION: $AIO_DB_REGION
68
+ annotations:
69
+ require-adobe-auth: false
70
+ include-ims-credentials: true
71
+ final: true
72
+ system-config-schema:
73
+ function: system-config-schema/index.js
74
+ web: 'yes'
75
+ runtime: 'nodejs:20'
76
+ inputs:
77
+ LOG_LEVEL: debug
78
+ OAUTH_CLIENT_ID: $OAUTH_CLIENT_ID
79
+ OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
80
+ OAUTH_ORG_ID: $OAUTH_ORG_ID
81
+ OAUTH_SCOPES: $OAUTH_SCOPES
82
+ AIO_DB_REGION: $AIO_DB_REGION
83
+ annotations:
84
+ require-adobe-auth: false
85
+ include-ims-credentials: true
86
+ final: true
87
+ export-config:
88
+ function: export-config/index.js
89
+ web: 'yes'
90
+ runtime: 'nodejs:20'
91
+ inputs:
92
+ LOG_LEVEL: debug
93
+ SYSTEM_CONFIG_CRYPT_KEY: $SYSTEM_CONFIG_CRYPT_KEY
94
+ OAUTH_CLIENT_ID: $OAUTH_CLIENT_ID
95
+ OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
96
+ OAUTH_ORG_ID: $OAUTH_ORG_ID
97
+ OAUTH_SCOPES: $OAUTH_SCOPES
98
+ AIO_DB_REGION: $AIO_DB_REGION
99
+ COMMERCE_BASE_URL: $COMMERCE_BASE_URL
100
+ COMMERCE_CONSUMER_KEY: $COMMERCE_CONSUMER_KEY
101
+ COMMERCE_CONSUMER_SECRET: $COMMERCE_CONSUMER_SECRET
102
+ COMMERCE_ACCESS_TOKEN: $COMMERCE_ACCESS_TOKEN
103
+ COMMERCE_ACCESS_TOKEN_SECRET: $COMMERCE_ACCESS_TOKEN_SECRET
104
+ annotations:
105
+ require-adobe-auth: false
106
+ include-ims-credentials: true
107
+ final: true
108
+ import-config:
109
+ function: import-config/index.js
110
+ web: 'yes'
111
+ runtime: 'nodejs:20'
112
+ limits:
113
+ timeout: 300000
114
+ memory: 512
115
+ inputs:
116
+ LOG_LEVEL: debug
117
+ SYSTEM_CONFIG_CRYPT_KEY: $SYSTEM_CONFIG_CRYPT_KEY
118
+ OAUTH_CLIENT_ID: $OAUTH_CLIENT_ID
119
+ OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
120
+ OAUTH_ORG_ID: $OAUTH_ORG_ID
121
+ OAUTH_SCOPES: $OAUTH_SCOPES
122
+ AIO_DB_REGION: $AIO_DB_REGION
123
+ COMMERCE_BASE_URL: $COMMERCE_BASE_URL
124
+ COMMERCE_CONSUMER_KEY: $COMMERCE_CONSUMER_KEY
125
+ COMMERCE_CONSUMER_SECRET: $COMMERCE_CONSUMER_SECRET
126
+ COMMERCE_ACCESS_TOKEN: $COMMERCE_ACCESS_TOKEN
127
+ COMMERCE_ACCESS_TOKEN_SECRET: $COMMERCE_ACCESS_TOKEN_SECRET
128
+ annotations:
129
+ require-adobe-auth: false
130
+ include-ims-credentials: true
131
+ final: true
132
+ sync-store-mappings-from-commerce:
133
+ function: sync-store-mappings-from-commerce/index.js
134
+ web: 'yes'
135
+ runtime: 'nodejs:20'
136
+ inputs:
137
+ LOG_LEVEL: debug
138
+ OAUTH_CLIENT_ID: $OAUTH_CLIENT_ID
139
+ OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
140
+ OAUTH_ORG_ID: $OAUTH_ORG_ID
141
+ OAUTH_SCOPES: $OAUTH_SCOPES
142
+ AIO_DB_REGION: $AIO_DB_REGION
143
+ COMMERCE_BASE_URL: $COMMERCE_BASE_URL
144
+ COMMERCE_CONSUMER_KEY: $COMMERCE_CONSUMER_KEY
145
+ COMMERCE_CONSUMER_SECRET: $COMMERCE_CONSUMER_SECRET
146
+ COMMERCE_ACCESS_TOKEN: $COMMERCE_ACCESS_TOKEN
147
+ COMMERCE_ACCESS_TOKEN_SECRET: $COMMERCE_ACCESS_TOKEN_SECRET
148
+ annotations:
149
+ require-adobe-auth: false
150
+ include-ims-credentials: true
151
+ final: true