@commercelayer/cli
Version:
220 lines (219 loc) • 10.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkAlias = exports.checkScope = exports.getApplicationInfo = void 0;
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const sdk_1 = tslib_1.__importStar(require("@commercelayer/sdk"));
const provisioning_sdk_1 = tslib_1.__importDefault(require("@commercelayer/provisioning-sdk"));
const cli_core_1 = require("@commercelayer/cli-core");
const config_1 = require("../../config");
const node_util_1 = require("node:util");
const current_1 = require("./current");
const errors_1 = require("@oclif/core/lib/errors");
class ApplicationsLogin extends core_1.Command {
static description = `execute login to a Commerce Layer application (application must be of kind 'integration' or 'sales_channel')`;
static aliases = ['app:login', 'login'];
static examples = [
'$ commercelayer applications:login -o <organizationSlug> -i <clientId> -s <clientSecret> -a <applicationAlias>',
'$ cl app:login -i <clientId> -s <clientSecret> -a <applicationAlias>'
];
static flags = {
organization: core_1.Flags.string({
char: 'o',
description: 'organization slug',
required: false
}),
domain: core_1.Flags.string({
char: 'd',
description: 'api domain',
required: false,
hidden: true
}),
clientId: core_1.Flags.string({
name: 'clientId',
char: 'i',
description: 'application client_id',
required: true
}),
clientSecret: core_1.Flags.string({
char: 's',
description: 'application client_secret',
required: false,
dependsOn: ['clientId'],
exclusive: ['email', 'password']
}),
scope: core_1.Flags.string({
char: 'S',
description: 'access token scope (market, stock location)',
required: false,
multiple: true,
dependsOn: ['clientId']
}),
email: core_1.Flags.string({
char: 'e',
description: 'customer email',
dependsOn: ['password']
}),
password: core_1.Flags.string({
char: 'p',
description: 'customer secret password',
dependsOn: ['email']
}),
alias: core_1.Flags.string({
char: 'a',
description: 'the alias you want to associate to the application',
multiple: false,
required: true,
}),
debug: core_1.Flags.boolean({
description: 'show more verbose error messages',
hidden: true
})
};
async catch(error) {
this.error(error.message);
}
async parse(c) {
cli_core_1.clCommand.fixDashedFlagValue(this.argv, c.flags.clientId);
const parsed = await super.parse(c);
cli_core_1.clCommand.fixDashedFlagValue(this.argv, c.flags.clientId, 'i', parsed);
return parsed;
}
async run() {
const { flags } = await this.parse(ApplicationsLogin);
if (!flags.clientSecret && !flags.scope)
this.error(`You must provide one of the arguments ${cli_core_1.clColor.cli.flag('clientSecret')} and ${cli_core_1.clColor.cli.flag('scope')}`);
const scope = checkScope(flags.scope);
const alias = checkAlias(flags.alias, this.config, flags.organization);
const config = {
clientId: flags.clientId,
clientSecret: flags.clientSecret,
slug: flags.organization,
domain: flags.domain,
scope,
email: flags.email,
password: flags.password
};
if (config.domain === (0, config_1.configParam)(config_1.ConfigParams.defaultDomain))
config.domain = undefined;
try {
const token = await cli_core_1.clToken.getAccessToken(config);
if (!token?.accessToken)
this.error('Unable to get access token');
const app = await getApplicationInfo(config, token?.accessToken || '');
const typeCheck = (0, config_1.configParam)(config_1.ConfigParams.applicationTypeCheck);
if (typeCheck) {
if (!typeCheck.includes(app.kind))
this.error(`The credentials provided are associated to an application of type ${cli_core_1.clColor.msg.error(app.kind)} while the only allowed types are: ${cli_core_1.clColor.api.kind(typeCheck.join(','))}`
// , { suggestions: [`Double check your credentials or access the online dashboard of ${clColor.api.organization(app.organization)} and create a new valid application `] }
);
}
app.alias = alias;
(0, config_1.appsDirCreate)(this.config);
(0, config_1.writeConfigFile)(this.config, app);
(0, config_1.writeTokenFile)(this.config, app, token);
(0, config_1.currentApplication)(app);
const current = (0, config_1.currentApplication)();
this.log(`\nCurrent application: ${(0, current_1.printCurrent)(current)}`);
const interactMsg = cli_core_1.clApplication.isProvisioningApp(app) ? `the ${cli_core_1.clColor.api.application('Provisioning API')}` : `${cli_core_1.clColor.api.organization(app.organization)} organization`;
this.log(`\n${cli_core_1.clColor.msg.success.bold('Login successful!')} Your configuration has been stored locally. You can now interact with ${interactMsg}\n`);
}
catch (error) {
this.log(cli_core_1.clColor.msg.error.bold('Login failed!'));
if (flags.debug)
this.error((0, node_util_1.inspect)(error, false, null, true));
else if (sdk_1.CommerceLayerStatic.isApiError(error))
this.error((0, node_util_1.inspect)(error.errors, false, null, true));
else {
const connectMsg = cli_core_1.clApplication.isProvisioningApp(config) ? cli_core_1.clColor.msg.error('Provisioning API') : `organization ${cli_core_1.clColor.msg.error(config.slug)}`;
this.error(`Unable to connect to ${connectMsg}: ${cli_core_1.clColor.italic(error.message)}`);
}
}
}
}
exports.default = ApplicationsLogin;
const getApplicationInfo = async (auth, accessToken) => {
function error(type) {
throw new Error(`This application cannot access the ${cli_core_1.clColor.italic(cli_core_1.clApi.humanizeResource(type))} resource`);
}
const tokenInfo = cli_core_1.clToken.decodeAccessToken(accessToken);
auth.scope = cli_core_1.clApplication.arrayScope(tokenInfo.scope);
// Remove metrics-api scope if exists
if (auth.scope.includes(cli_core_1.clConfig.provisioning.scope))
auth.scope = cli_core_1.clConfig.provisioning.scope;
const provisioning = cli_core_1.clApplication.isProvisioningApp(auth);
const api = auth.api || (provisioning ? 'provisioning' : 'core');
let org, app, user;
if (provisioning) {
const clp = (0, provisioning_sdk_1.default)({ domain: auth.domain, accessToken });
// User info
const usr = await clp.user.retrieve().catch(() => { error(clp.user.type()); });
if (usr)
user = { name: `${usr.first_name}${(usr.first_name && usr.last_name) ? ' ' : ''}${usr.last_name}`, email: usr.email };
org = { slug: 'provisioning', name: user?.name || 'Provisioning API' };
app = { name: 'Provisioning App' };
}
else { // core
const cl = (0, sdk_1.default)({ organization: auth.slug || '', domain: auth.domain, accessToken });
// Organization info
org = await cl.organization.retrieve().catch(() => { error(cl.organization.type()); });
// Application info
app = await cl.application.retrieve().catch(() => { error(cl.application.type()); });
}
const mode = tokenInfo.test ? 'test' : 'live';
const appInfo = { ...auth,
organization: org.name ?? undefined,
key: cli_core_1.clApplication.appKey(),
slug: org.slug ?? undefined,
mode,
kind: tokenInfo.application.kind,
name: app.name || '',
baseUrl: cli_core_1.clApi.baseURL(api, auth.slug, auth.domain),
id: tokenInfo.application.id,
alias: '',
api,
user: user?.name
};
// if (Array.isArray(appInfo.scope) && (appInfo.scope.length === 0)) appInfo.scope = undefined
return appInfo;
};
exports.getApplicationInfo = getApplicationInfo;
const checkScope = (scopeFlags) => {
const scope = [];
if (scopeFlags) {
for (const s of scopeFlags) {
if (['provisioning-api', 'metrics-api'].includes(s) && (scopeFlags.length > 1))
throw new Error(`Scope ${cli_core_1.clColor.msg.error(s)} cannot be used together with other scopes`);
const colonIdx = s.indexOf(':');
const scopePrefix = s.substring(0, colonIdx);
if ((colonIdx < 1) || (colonIdx === s.length - 1))
throw new Error(`Invalid scope: ${cli_core_1.clColor.msg.error(s)}`);
if (scope.includes(s))
throw new Error(`Duplicate login scope: ${cli_core_1.clColor.msg.error(s)}`);
const scopeCheck = (0, config_1.configParam)(config_1.ConfigParams.scopeCheck);
if (scopeCheck && !scopeCheck.includes(scopePrefix))
throw new errors_1.CLIError(`Invalid scope prefix: ${cli_core_1.clColor.msg.error(scopePrefix)}`);
scope.push(s);
}
}
const _scope = (scope.length === 1) ? scope[0] : scope;
return _scope;
};
exports.checkScope = checkScope;
const checkAlias = (alias, config, organization) => {
const match = alias.match(/^[a-z0-9_-]*$/);
if ((match === null) || (match.length > 1))
throw new Error(`Invalid alias: ${cli_core_1.clColor.msg.error(alias)}. Accepted characters are ${cli_core_1.clColor.cli.value('[a-z0-9_-]')}`);
const ml = 15;
const al = match[0];
if (al.length > ml)
throw new Error(`Application alias must have a max length of ${cli_core_1.clColor.type.number(String(ml))} characters`);
if (config) {
const flags = { alias, organization };
const apps = (0, config_1.filterApplications)(config, flags);
if (apps.length > 0)
throw new Error(`Alias ${cli_core_1.clColor.msg.error(alias)} has already been used${organization ? ` for organization ${cli_core_1.clColor.api.organization(apps[0].organization)}` : ''}`);
}
return al;
};
exports.checkAlias = checkAlias;