Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various SLAS adjustments, added callbackURIs #318

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.<br/>
![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.
Expand Down Expand Up @@ -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 => ...);
Expand Down Expand Up @@ -1346,4 +1362,4 @@ callback | (Function) | Callback function executed as a result. The error

**Returns:** (void) Function has no return value

***
***
9 changes: 6 additions & 3 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2106,6 +2106,7 @@ program
.option('--channels <channels>', 'comma separated list of site IDs this API client should support')
.option('--scopes <scopes>', 'comma separated list of auth z scopes this API client should support')
.option('--redirecturis <redirecturis>', 'comma separated list of redirect uris this API client should support')
.option('--callbackuris <callbackuris>', 'comma separated list of callback uris this API client should support')

.option('-j, --json', 'Formats the output in json')
.action(async function(options) {
Expand All @@ -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()) : [];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be set to an empty array if the client does not have redirectURLs (passwordless login)

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();
Expand Down Expand Up @@ -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();
Expand Down
Binary file added docs/images/client-api-user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 30 additions & 40 deletions lib/slas.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) : '')}`;
}


Expand All @@ -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);
}
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

order from cli expects the reverse:
await slas.cli.client.list(options.tenant, options.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 {
Expand All @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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), {
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -251,7 +246,8 @@ const slas = {
secret: secret,
channels: channels,
scopes: scopes,
redirectUri: redirecturis
redirectUri: redirecturis || [],
callbackUri: callbackuris || []
}
}
tenantId = secrets.getScapiTenantId(tenantId || params.ecomTenant);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down