diff --git a/README.md b/README.md index 0f7aff1..d43deea 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,22 @@ Ensure you have a valid Commerce Cloud API key (client ID) set up. If you don't For automation (e.g. a build server integration) you'll need the API key as well as the API secret for authentication. If you want to use authentication in interactive mode, you have to set _Redirect URIs_ to `http://localhost:8080`. If you want to manage sandboxes you have to set _Default Scopes_ to `roles tenantFilter profile`. +### SLAS Prerequisites ### +In order to use your API key with SLAS, please ensure the following are configured for your client: + +1. Has `Roles` > `Commerce Cloud Developer Experience` > `Sandbox API User` with "All Sandboxes" scope.
+ ![Account Manager Client ID](docs/images/client-api-user.png) +2. Set _Default Scopes_ to: +```txt +mail +roles +tenantFilter +profile +openId +``` +3. Set "Token Endpoint Auth Method" to `client_secret_post` +4. Set "Access Token Format" to `JWT` + ### Grant your API key access to your instances ### In order to perform CLI commands, you have to permit API calls to the Commerce Cloud instance(s) you wish to integrate with. You do that by modifying the Open Commerce API Settings as well as the WebDAV Client Permissions on the Commerce Cloud instance. @@ -653,14 +669,14 @@ The following APIs are available (assuming `sfcc` refers to `require('sfcc-ci')` sfcc.job.run(instance, job_id, job_params, token, callback); sfcc.job.status(instance, job_id, job_execution_id, token, callback); sfcc.manifest.generate(directories, ignorePatterns, targetDirectory, fileName); - sfcc.slas.tenant.add(tenantId, shortcode, description, merchantName, contact, emailAddress, fileName).then(result => ...).catch(err => ...); - sfcc.slas.tenant.get(tenantId, shortcode).then(result => ...).catch(err => ...); - sfcc.slas.tenant.list(shortcode).then(result => ...).catch(err => ...); - sfcc.slas.tenant.delete(tenantId, shortcode).then(result => ...).catch(err => ...); - sfcc.slas.client.add(tenantId, shortcode, file, clientid, clientname, privateclient, ecomtenant, ecomsite, secret, channels, scopes, redirecturis).then(result => ...).catch(err => ...); - sfcc.slas.client.get(tenantId, shortcode, clientId).then(result => ...).catch(err => ...); - sfcc.slas.client.list(shortcode, tenantId).then(result => ...).catch(err => ...); - sfcc.slas.client.delete(tenantId, shortcode, clientId).then(result => ...).catch(err => ...); + sfcc.slas.tenant.add(tenantId, shortcode, description, merchantName, contact, emailAddress, fileName, token).then(result => ...).catch(err => ...); + sfcc.slas.tenant.get(tenantId, shortcode, token).then(result => ...).catch(err => ...); + sfcc.slas.tenant.list(shortcode, token).then(result => ...).catch(err => ...); + sfcc.slas.tenant.delete(tenantId, shortcode, token).then(result => ...).catch(err => ...); + sfcc.slas.client.add(tenantId, shortcode, file, clientid, clientname, privateclient, ecomtenant, ecomsite, secret, channels, scopes, redirecturis, callbackuris, token).then(result => ...).catch(err => ...); + sfcc.slas.client.get(tenantId, shortcode, clientId, token).then(result => ...).catch(err => ...); + sfcc.slas.client.list(shortcode, tenantId, token).then(result => ...).catch(err => ...); + sfcc.slas.client.delete(tenantId, shortcode, clientId, token).then(result => ...).catch(err => ...); sfcc.user.create(org, user, mail, firstName, lastName, token).then(result => ...).catch(err => ...); sfcc.user.list(org, role, login, count, sortBy, token).then(result => ...).catch(err => ...); sfcc.user.update(login, changes, token).then(result => ...).catch(err => ...); @@ -1346,4 +1362,4 @@ callback | (Function) | Callback function executed as a result. The error **Returns:** (void) Function has no return value -*** \ No newline at end of file +*** diff --git a/cli.js b/cli.js index 44d32e0..bb022f3 100755 --- a/cli.js +++ b/cli.js @@ -2106,6 +2106,7 @@ program .option('--channels ', 'comma separated list of site IDs this API client should support') .option('--scopes ', 'comma separated list of auth z scopes this API client should support') .option('--redirecturis ', 'comma separated list of redirect uris this API client should support') + .option('--callbackuris ', 'comma separated list of callback uris this API client should support') .option('-j, --json', 'Formats the output in json') .action(async function(options) { @@ -2119,11 +2120,13 @@ program const secret = options.secret; const channels = !options.channels || options.channels.split(',').map(item => item.trim()); const scopes = !options.scopes || options.scopes.split(',').map(item => item.trim()); - const redirecturis = !options.redirecturis || options.redirecturis.split(',').map(item => item.trim()); + const redirecturis = options.redirecturis ? options.redirecturis.split(',').map(item => item.trim()) : []; + const callbackuris = options.callbackuris ? options.callbackuris.split(',').map(item => item.trim()) : []; const slas = require('./lib/slas'); await slas.cli.client.add(options.tenant, options.shortcode, options.file, - clientid, clientname, privateclient, ecomtenant, ecomsite, secret, channels, scopes, redirecturis, asJson); + clientid, clientname, privateclient, ecomtenant, ecomsite, secret, + channels, scopes, redirecturis, callbackuris, asJson); }).on('--help', function() { console.log(); @@ -2176,7 +2179,7 @@ program var asJson = ( options.json ? options.json : false ); const slas = require('./lib/slas'); - await slas.cli.client.get(options.tenant, options.shortcode, options.clientid, asJson); + await slas.cli.client.delete(options.tenant, options.shortcode, options.clientid, asJson); }).on('--help', function() { console.log(); diff --git a/docs/images/client-api-user.png b/docs/images/client-api-user.png new file mode 100644 index 0000000..ff66f4c Binary files /dev/null and b/docs/images/client-api-user.png differ diff --git a/lib/slas.js b/lib/slas.js index a83ef84..ce91ac6 100644 --- a/lib/slas.js +++ b/lib/slas.js @@ -8,11 +8,11 @@ const secrets = require('./secrets'); * Generates a SLAS Admin Url * @param {string} tenantId the tenant found in BM - e.g bbsv_stg * @param {string} shortcode the shortcode found in BM - e.g acdefg - * @param {string} [clientId] if provided a client URL is generated + * @param {string} [clientId] if provided a client URL is generated */ function getSlasUrl(tenantId, shortcode, clientId) { return `https://${shortcode}.api.commercecloud.salesforce.com/shopper/auth-admin/v1/tenants/${tenantId - + (clientId ? ('/clients/' + clientId) : '')}`; + + (clientId ? ('/clients/' + clientId) : '')}`; } @@ -26,6 +26,10 @@ async function handleResponse(response) { throw new Error(`HTTP Fault ${response.status} (${response.statusText})`) } + if (response.status === 204) { + return 'Success, 204: No Content'; + } + const resultText = await response.text(); return JSON.parse(resultText); } @@ -63,8 +67,8 @@ const slas = { let result try { result = await slas.api.tenant.add(tenantId, shortcode, - description, merchantName, contact, emailAddress, fileName) - console.info('sucessfully add tenant') + description, merchantName, contact, emailAddress, fileName, auth.getToken()) + console.info('sucessfully added tenant') handleCLIOutput(result, asJson) } catch (e) { handleCLIError('Could not add tenant: ', e.message, asJson) @@ -73,7 +77,7 @@ const slas = { get: async (tenantId, shortcode, asJson) => { let result try { - result = await slas.api.tenant.get(tenantId, shortcode) + result = await slas.api.tenant.get(tenantId, shortcode, auth.getToken()) handleCLIOutput(result, asJson) } catch (e) { handleCLIError('Could not get tenant: ', e.message, asJson) @@ -82,7 +86,7 @@ const slas = { list: async (shortcode, asJson) => { let result try { - result = await slas.api.tenant.list(shortcode) + result = await slas.api.tenant.list(shortcode, auth.getToken()) handleCLIOutput(result, asJson) } catch (e) { handleCLIError('Could not get tenants: ', e.message, asJson) @@ -91,7 +95,7 @@ const slas = { delete: async (tenantId, shortcode, asJson) => { let result try { - result = await slas.api.tenant.delete(tenantId, shortcode) + result = await slas.api.tenant.delete(tenantId, shortcode, auth.getToken()) handleCLIOutput(result, asJson) } catch (e) { handleCLIError('Could not delete tenant: ', e.message, asJson) @@ -100,12 +104,13 @@ const slas = { }, client : { add: async (tenantId, shortcode, fileName,clientid, clientname, privateclient, - ecomtenant, ecomsite, secret, channels, scopes, redirecturis, asJson) => { + ecomtenant, ecomsite, secret, channels, scopes, redirecturis, callbackuris, asJson) => { let result try { result = await slas.api.client.add(tenantId, shortcode, fileName, clientid, clientname, - privateclient, ecomtenant, ecomsite, secret, channels, scopes, redirecturis); - console.info('sucessfully add client ') + privateclient, ecomtenant, ecomsite, secret, channels, scopes, redirecturis, + callbackuris, auth.getToken()); + console.info('sucessfully added client ') handleCLIOutput(result, asJson) } catch (e) { handleCLIError('Could not add client: ', e.message, asJson) @@ -114,16 +119,16 @@ const slas = { get: async (tenantId, shortcode, clientId, asJson) => { let result try { - result = await slas.api.client.get(tenantId, shortcode, clientId) + result = await slas.api.client.get(tenantId, shortcode, clientId, auth.getToken()) handleCLIOutput(result, asJson) } catch (e) { handleCLIError('Could not get tenant: ', e.message, asJson) } }, - list: async (shortcode, tenantId, asJson) => { + list: async (tenantId, shortcode, asJson) => { let result try { - result = await slas.api.client.list(shortcode, tenantId); + result = await slas.api.client.list(shortcode, tenantId, auth.getToken()); if (asJson) { console.info(JSON.stringify(result, null, 4)); } else { @@ -136,7 +141,7 @@ const slas = { delete: async (tenantId, shortcode, clientId, asJson) => { let result try { - result = await slas.api.client.delete(tenantId, shortcode, clientId) + result = await slas.api.client.delete(tenantId, shortcode, clientId, auth.getToken()) handleCLIOutput(result, asJson) } catch (e) { handleCLIError('Could not delete tenant: ', e.message, asJson) @@ -146,8 +151,7 @@ const slas = { }, api: { tenant: { - add: async (tenantId, shortcode, description, merchantName, contact, emailAddress, fileName) => { - const token = auth.getToken(); + add: async (tenantId, shortcode, description, merchantName, contact, emailAddress, fileName, token) => { let params // set fallbacks shortcode = secrets.getScapiShortCode(shortcode); @@ -180,9 +184,7 @@ const slas = { return await handleResponse(response); }, - get: async (tenantId, shortcode) => { - const token = auth.getToken(); - + get: async (tenantId, shortcode, token) => { // set fallbacks tenantId = secrets.getScapiTenantId(tenantId); shortcode = secrets.getScapiShortCode(shortcode); @@ -197,11 +199,8 @@ const slas = { return await handleResponse(response); }, - list: async (shortcode) => { - const token = auth.getToken(); - + list: async (shortcode, token) => { // set fallbacks - tenantId = secrets.getScapiTenantId(tenantId); shortcode = secrets.getScapiShortCode(shortcode); const response = await fetch(getSlasUrl('', shortcode), { @@ -213,9 +212,7 @@ const slas = { }); return await handleResponse(response); }, - delete: async (tenantId, shortcode) => { - const token = auth.getToken(); - + delete: async (tenantId, shortcode, token) => { // set fallbacks tenantId = secrets.getScapiTenantId(tenantId); shortcode = secrets.getScapiShortCode(shortcode); @@ -233,9 +230,7 @@ const slas = { }, client: { add: async (tenantId, shortcode, file, clientid, clientname, privateclient, - ecomtenant, ecomsite, secret, channels, scopes, redirecturis,) => { - const token = auth.getToken(); - + ecomtenant, ecomsite, secret, channels, scopes, redirecturis, callbackuris, token) => { // set fallbacks shortcode = secrets.getScapiShortCode(shortcode); let params; @@ -251,7 +246,8 @@ const slas = { secret: secret, channels: channels, scopes: scopes, - redirectUri: redirecturis + redirectUri: redirecturis || [], + callbackUri: callbackuris || [] } } tenantId = secrets.getScapiTenantId(tenantId || params.ecomTenant); @@ -263,12 +259,9 @@ const slas = { }, body: JSON.stringify(params) }); - return await handleResponse(response); }, - get: async (tenantId, shortcode, clientId) => { - const token = auth.getToken(); - + get: async (tenantId, shortcode, clientId, token) => { // set fallbacks tenantId = secrets.getScapiTenantId(tenantId); shortcode = secrets.getScapiShortCode(shortcode); @@ -283,9 +276,7 @@ const slas = { return await handleResponse(response); }, - list: async (shortcode, tenantId) => { - const token = auth.getToken(); - + list: async (shortcode, tenantId, token) => { // set fallbacks tenantId = secrets.getScapiTenantId(tenantId); shortcode = secrets.getScapiShortCode(shortcode); @@ -297,11 +288,10 @@ const slas = { 'Content-Type': 'application/json' } }); + return await handleResponse(response); }, - delete: async (tenantId, shortcode, clientId) => { - const token = auth.getToken(); - + delete: async (tenantId, shortcode, clientId, token) => { // set fallbacks tenantId = secrets.getScapiTenantId(tenantId); shortcode = secrets.getScapiShortCode(shortcode);