UNPKG

@commercelayer/cli

Version:
220 lines (219 loc) 10.7 kB
"use strict"; 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;