@unito/integration-cli
Version:
Integration CLI
228 lines (225 loc) • 11.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const chalk_1 = tslib_1.__importDefault(require("chalk"));
const child_process_1 = tslib_1.__importDefault(require("child_process"));
const crawlerDriver_1 = require("@unito/integration-debugger/dist/src/services/crawlerDriver");
const baseCommand_1 = require("../baseCommand");
const errors_1 = require("../errors");
const GlobalConfiguration = tslib_1.__importStar(require("../resources/globalConfiguration"));
const integrations_1 = require("../resources/integrations");
const configuration_1 = require("../resources/configuration");
const decryption_1 = require("../resources/decryption");
const credentials_1 = require("../resources/credentials");
class Test extends baseCommand_1.BaseCommand {
static description = 'Test your integration';
static examples = ['<%= config.bin %> <%= command.id %>'];
static flags = {
environment: core_1.Flags.custom({
description: 'the environment of the platform',
options: Object.values(GlobalConfiguration.Environment),
default: GlobalConfiguration.Environment.Production,
})(),
verbose: core_1.Flags.boolean({
description: 'output more (debug) information',
default: false,
}),
'starting-path': core_1.Flags.string({
description: 'starting path of the crawler',
default: '/',
}),
'starting-operation': core_1.Flags.custom({
description: 'starting operation of the crawler',
options: Object.values(crawlerDriver_1.Operation),
default: crawlerDriver_1.Operation.GetItem,
})(),
'output-path': core_1.Flags.string({
description: 'output report in JSON format at the specified path',
}),
'credential-payload': core_1.Flags.string({
description: '(advanced) credential payload to use.',
exclusive: ['credential-id'],
}),
'credential-id': core_1.Flags.string({
description: '(advanced) credential to use.',
exclusive: ['credential-payload'],
}),
'test-account': core_1.Flags.string({
description: 'test account to use.',
options: Object.values(configuration_1.CredentialScope),
default: configuration_1.CredentialScope.DEVELOPMENT,
}),
'read-only': core_1.Flags.boolean({
description: 'whether or not to only perform read operations',
allowNo: true,
}),
timeout: core_1.Flags.integer({
description: `timeout in seconds passed as 'X-Unito-Operation-Deadline' header to the integration. Set to 0 for no timeout.`,
default: 20, // Test mode has a default timeout of 20 seconds.
}),
crawlMode: core_1.Flags.string({
description: 'mode to use while crawling the integration graph',
options: Object.values(configuration_1.CrawlMode),
default: configuration_1.CrawlMode.FULL,
}),
checks: core_1.Flags.string({
description: 'checks to perform while crawling the integration graph',
multiple: true,
default: [],
}),
debug: core_1.Flags.boolean({
description: 'log launch command to console before running it - including decrypted values - for debugging only',
hidden: true,
default: false,
}),
'config-path': core_1.Flags.string({
summary: 'relative path to a custom ".unito.json" file',
description: `use a custom configuration file instead of the default '.unito.json' or other environment specific
ones.
If you want to force the CLI to use a specific configuration file, you can use this flag to specify the relative
path from your integration's root folder (with a leading '/').
Usage: <%= config.bin %> <%= command.id %> --config-path=/myCustomConfig.json`,
}),
'skip-install': core_1.Flags.boolean({
description: 'whether or not to run npm install before starting the integration. Useful when npx linking modules to the integration.',
allowNo: true,
default: false,
}),
};
async catch(error) {
/* istanbul ignore if */
if ((0, errors_1.handleError)(this, error)) {
this.exit(-1);
}
throw error;
}
async run() {
(0, integrations_1.validateIsIntegrationDirectory)();
const { flags } = await this.parse(Test);
if (!flags['skip-install']) {
// Install NPM dependencies.
core_1.ux.action.start('Installing NPM dependencies', undefined, { stdout: true });
child_process_1.default.execSync('npm install', {
env: { ...process.env, NODE_ENV: 'development' },
stdio: ['ignore', 'ignore', 'inherit'],
});
core_1.ux.action.stop();
}
// Resolve the configuration.
const environment = flags.environment ?? GlobalConfiguration.Environment.Production;
const configuration = await (0, configuration_1.getConfiguration)(environment, flags['config-path']);
let credentialPayload = '{}';
let readOnly = flags['read-only'] ?? false;
if (flags['credential-id']) {
const credential = await (0, credentials_1.fetchCredential)(environment, this.config.configDir, flags['credential-id']);
credentialPayload = JSON.stringify({
...credential.payload,
unitoCredentialId: credential.id,
unitoUserId: credential.unitoUserId,
});
// When "credential-id" is provided, the default value of "read-only" is false.
readOnly = flags['read-only'] ?? true;
}
else if (flags['credential-payload']) {
credentialPayload = flags['credential-payload'];
}
else {
const credentials = configuration.testAccounts?.[flags['test-account']];
if (credentials) {
const decryptedEntries = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, credentials);
if (decryptedEntries.failed.length) {
throw new errors_1.EntryDecryptionError(decryptedEntries.failed.at(0), environment);
}
credentialPayload = JSON.stringify({
...decryptedEntries.successful,
unitoCredentialId: flags['test-account'],
unitoUserId: flags['test-account'],
});
}
}
// Load secrets.
const { successful: secrets, failed: failedSecrets } = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.secrets ?? {});
if (failedSecrets.length) {
throw new errors_1.EntryDecryptionError(failedSecrets.at(0), environment);
}
// Load environment variables.
const { successful: environmentVariables, failed: failedEnvironmentVariables } = await (0, decryption_1.decryptEntries)(configuration.name, environment, this.config.configDir, configuration.environmentVariables ?? {});
for (const environmentVariable of failedEnvironmentVariables) {
if (!process.env[environmentVariable]) {
core_1.ux.log();
core_1.ux.log(`Could not decrypt the environment variable ${chalk_1.default.yellowBright(environmentVariable)}.`);
core_1.ux.log('It may be a normal behavior locally as some environment variables are sensitive secrets.');
core_1.ux.log(`You can still pass ${chalk_1.default.yellowBright(`${environmentVariable}=<value>`)} to the CLI command to provide a value for this environment variable.`);
core_1.ux.log(`Example: ${chalk_1.default.yellowBright(`${environmentVariable}=foo ${this.config.bin} ${this.id}`)}`);
}
}
// Launch the tests.
const commandArguments = [
`${process.env.NODE_MODULES_FOLDER}/@unito/integration-debugger/dist/src/index.js`,
'--integration-url=http://localhost:9200',
'--spawn-process=npm run dev',
`--credential-payload=${credentialPayload}`,
`--secrets-payload=${JSON.stringify(secrets)}`,
'--non-interactive',
`--starting-path=${flags['starting-path']}`,
`--starting-operation=${flags['starting-operation']}`,
];
if (readOnly) {
commandArguments.push('--read-only');
}
if (flags.timeout) {
commandArguments.push(`--timeout=${flags.timeout}`);
}
if (configuration.graphRelativeUrl) {
commandArguments.push(`--graph-relative-url=${configuration.graphRelativeUrl}`);
}
if (configuration.credentialAccountRelativeUrl) {
commandArguments.push(`--credential-account-relative-url=${configuration.credentialAccountRelativeUrl}`);
}
if (configuration.webhookParsingRelativeUrl) {
commandArguments.push(`--webhook-parsing-relative-url=${configuration.webhookParsingRelativeUrl}`);
}
if (configuration.webhookSubscriptionsRelativeUrl) {
commandArguments.push(`--webhook-subscriptions-relative-url=${configuration.webhookSubscriptionsRelativeUrl}`);
}
if (configuration.webhookAcknowledgeRelativeUrl) {
commandArguments.push(`--webhook-acknowledge-relative-url=${configuration.webhookAcknowledgeRelativeUrl}`);
}
if (flags.checks?.length) {
commandArguments.push(`--checks=${flags.checks.join(',')}`);
}
if (flags.verbose) {
commandArguments.push('--verbose');
}
if (flags['output-path']) {
commandArguments.push(`--output-path=${flags['output-path']}`);
}
if (flags['crawlMode'] === configuration_1.CrawlMode.SAMPLE) {
commandArguments.push('--operation-collection-items-per-page=5');
commandArguments.push('--operation-collection-follow-next-pages=false');
}
if (flags['crawlMode'] === configuration_1.CrawlMode.SINGLE) {
commandArguments.push('--operation-collection-items-per-page=1');
commandArguments.push('--operation-collection-follow-next-pages=false');
}
if (flags.debug) {
core_1.ux.log(`Launching... node ${commandArguments.join(' ')}`);
}
const child = child_process_1.default.spawn('node', commandArguments, {
detached: true,
stdio: 'inherit',
env: { ...process.env, ...environmentVariables, NODE_ENV: 'development', PORT: '9200' },
});
// istanbul ignore next
child.on('exit', (code) => {
process.exit(code);
});
// Window change (ex: resize) signal.
// istanbul ignore next
process.on('SIGWINCH', () => {
child.kill('SIGWINCH');
});
}
}
exports.default = Test;