UNPKG

@unito/integration-debugger

Version:

The Unito Integration Debugger

313 lines (312 loc) 13.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Runner = void 0; const fs_1 = __importDefault(require("fs")); const chalk_1 = __importDefault(require("chalk")); const readline_1 = __importDefault(require("readline")); const integrationsPlatform_1 = require("../services/integrationsPlatform"); const CrawlerDriver = __importStar(require("../services/crawlerDriver")); const styles_1 = require("../styles"); const configuration_1 = require("./configuration"); const process_1 = require("./process"); class Runner { configuration; stop; constructor(configuration) { this.configuration = configuration; this.stop = false; } async run() { this.log((0, styles_1.generateLogo)(Date.now())); this.setupInputs(); this.showConfiguration(); this.spawnIntegrationProcess(); (0, integrationsPlatform_1.setEnvironment)(this.configuration.environment); (0, integrationsPlatform_1.setApiKey)(this.configuration.apiKey); const integration = await this.loadIntegration(); const credential = await this.loadCredential(); await this.waitIntegrationReady(integration, credential); const crawlerDriver = await this.createCrawlerDriver(); const credentialAccount = await this.crawlCredentialAccount(crawlerDriver); const steps = await this.crawlGraph(crawlerDriver); if (credentialAccount) { steps.push(credentialAccount); } return this.generateReport(crawlerDriver, steps); } // istanbul ignore next setupInputs() { if (!this.configuration.inputs) { return; } this.log(`Press ${chalk_1.default.yellowBright('q')} to terminate gracefully. Press ${chalk_1.default.yellowBright('<ctrl+c>')} to terminate immediately.`); this.log(''); readline_1.default.emitKeypressEvents(process.stdin); if (process.stdin.setRawMode) { process.stdin.setRawMode(true); } process.stdin.resume().on('keypress', (_ch, key) => { if (key && key.ctrl && key.name == 'c') { process.exit(2); } if (key && key.name == 'q') { this.stop = true; } }); } showConfiguration() { this.log(chalk_1.default.blueBright('Using configuration:')); const sanitizedConfiguration = (0, configuration_1.sanitizeConfiguration)(this.configuration); for (const [key, value] of Object.entries(sanitizedConfiguration)) { if (typeof value === 'object') { this.log((0, styles_1.indent)(`${key}: ${JSON.stringify(value)}`)); } else { this.log((0, styles_1.indent)(`${key}: ${value}`)); } } } async createCrawlerDriver() { let crawlerDriver; if (this.configuration.credentialId) { crawlerDriver = await CrawlerDriver.createWithProxyCrawler(this.configuration.credentialId, { readOnly: this.configuration.readOnly ?? false, timeout: this.configuration.timeout, [CrawlerDriver.Operation.GetCollection]: { itemsPerPage: this.configuration.operationCollectionItemsPerPage, followNextPage: this.configuration.operationCollectionFollowNextPages, }, }); } else { crawlerDriver = await CrawlerDriver.createWithDirectCrawler(this.configuration.integrationUrl ?? '', this.configuration.graphRelativeUrl ?? '/', this.configuration.credentialAccountRelativeUrl ?? '/me', this.configuration.webhookParsingRelativeUrl, this.configuration.webhookSubscriptionsRelativeUrl, this.configuration.webhookAcknowledgeRelativeUrl, this.configuration.credentialPayload ?? {}, this.configuration.secretsPayload ?? {}, { readOnly: this.configuration.readOnly ?? false, timeout: this.configuration.timeout, [CrawlerDriver.Operation.GetCollection]: { itemsPerPage: this.configuration.operationCollectionItemsPerPage, followNextPage: this.configuration.operationCollectionFollowNextPages, }, }); } crawlerDriver.stepCheckKeys = this.configuration.stepCheckKeys; return crawlerDriver; } spawnIntegrationProcess() { // istanbul ignore next if (this.configuration.spawnProcessCommand) { this.log(chalk_1.default.blueBright('Spawning process...')); (0, process_1.spawnProcess)(this.configuration.spawnProcessCommand, data => { if (this.configuration.verbose) { this.log((0, styles_1.indent)(`${chalk_1.default.gray('■')} ${data.trimEnd()}`)); } }); this.log((0, styles_1.indent)(`${chalk_1.default.greenBright('■')} process spawned.`)); } } async crawlCredentialAccount(crawlerDriver) { if (this.stop) { return undefined; } this.log(chalk_1.default.blueBright('Crawling the credential account route...')); const path = this.configuration.credentialAccountRelativeUrl ?? '/me'; crawlerDriver.startFrom({ path, schemaPath: undefined, parentOperation: undefined, parentPath: undefined, requestSchema: undefined, operation: CrawlerDriver.Operation.GetCredentialAccount, warnings: [], errors: [], }); const step = await crawlerDriver.next(); this.log((0, styles_1.indent)([ step === undefined || step.errors.length > 0 ? chalk_1.default.redBright('■') : chalk_1.default.greenBright('■'), CrawlerDriver.Operation.GetCredentialAccount, path, ].join(' '))); if (this.configuration.verbose) { this.log(''); this.log((0, styles_1.indent)((0, styles_1.jsonOutput)(step), 2)); this.log(''); } return step; } async crawlGraph(crawlerDriver) { if (this.stop) { return []; } this.log(chalk_1.default.blueBright('Crawling the graph route...')); crawlerDriver.startFrom({ path: this.configuration.startingPath ?? '/', schemaPath: undefined, parentOperation: undefined, parentPath: undefined, requestSchema: undefined, operation: this.configuration.startingOperation ?? CrawlerDriver.Operation.GetItem, warnings: [], errors: [], }); let step = undefined; const stepsProcessed = []; do { step = await crawlerDriver.next(); if (step) { this.log((0, styles_1.indent)((0, styles_1.renderCallEntry)(step))); stepsProcessed.push(step); if (stepsProcessed.length % 20 === 0) { this.log((0, styles_1.indent)(chalk_1.default.cyanBright([ '■', 'crawled:', stepsProcessed.length, '/', stepsProcessed.length + crawlerDriver.remaining().length, ].join(' ')))); } if (this.configuration.verbose) { this.log(''); this.log((0, styles_1.indent)((0, styles_1.jsonOutput)(step), 2)); this.log(''); } } } while (!this.stop && step); this.log((0, styles_1.indent)(chalk_1.default.cyanBright(['■', 'crawled:', stepsProcessed.length, '/', stepsProcessed.length + crawlerDriver.remaining().length].join(' ')))); return stepsProcessed; } async loadIntegration() { let integration = null; // istanbul ignore next if (this.configuration.integrationId) { try { integration = await (0, integrationsPlatform_1.getIntegration)(this.configuration.integrationId); } catch (err) { this.log(`Could not load integration because: ${err.message}`); } } return integration; } async loadCredential() { let credential = null; // istanbul ignore next if (this.configuration.credentialId) { try { credential = await (0, integrationsPlatform_1.getCredential)(this.configuration.credentialId); } catch (err) { this.log(`Could not load credential because: ${err.message}`); } } return credential; } async waitIntegrationReady(integration, credential) { if (this.stop) { return false; } this.log(chalk_1.default.blueBright('Waiting for readiness of the integration...')); const maxAttempts = 10; let alive = false; let attempt = 1; do { if (this.configuration.environment === configuration_1.Environment.None) { alive = await (0, integrationsPlatform_1.getNoneIntegrationAlive)(this.configuration.integrationUrl); } else { alive = await (0, integrationsPlatform_1.getIntegrationAlive)(integration, credential); } // istanbul ignore next if (!alive) { this.log((0, styles_1.indent)(`${chalk_1.default.yellowBright('■')} not ready yet, waiting 2 sec... (${attempt}/${maxAttempts})`)); await new Promise(resolve => setTimeout(resolve, 2000)); } attempt += 1; } while (!this.stop && !alive && attempt <= maxAttempts); this.log((0, styles_1.indent)([ alive ? chalk_1.default.greenBright('■') : chalk_1.default.redBright('■'), alive ? 'integration ready' : 'integration not ready', ].join(' '))); return alive; } generateReport(crawlerDriver, steps) { this.log(chalk_1.default.blueBright('Report')); const report = { pathsCrawled: steps.length, pathsSkipped: crawlerDriver.remaining().length, steps, }; const stepsWithWarnings = steps.filter(step => !!step.warnings.length); const stepsInError = steps.filter(step => !!step.errors.length); this.log((0, styles_1.indent)(`${chalk_1.default.gray('■')} ${report.pathsCrawled} paths crawled`)); if (report.pathsSkipped > 0) { this.log((0, styles_1.indent)(chalk_1.default.yellowBright(`■ ${report.pathsSkipped} path(s) not crawled`))); } if (stepsWithWarnings.length > 0) { this.log((0, styles_1.indent)(chalk_1.default.yellowBright(`■ ${stepsWithWarnings.length} warning(s)`))); } else { this.log(chalk_1.default.greenBright((0, styles_1.indent)('■ 0 warning'))); } if (stepsInError.length > 0) { this.log((0, styles_1.indent)(chalk_1.default.redBright(`■ ${stepsInError.length} error(s)`))); for (const [index, error] of Object.entries(stepsInError)) { this.log((0, styles_1.indent)(chalk_1.default.redBright(`■ Error ${index}`))); this.log((0, styles_1.indent)((0, styles_1.jsonOutput)(error), 2)); } } else { this.log(chalk_1.default.greenBright((0, styles_1.indent)('■ 0 error'))); } if (this.configuration.outputFilePath) { fs_1.default.writeFileSync(this.configuration.outputFilePath, JSON.stringify(report, null, 2), { encoding: 'utf8', flag: 'w', }); this.log((0, styles_1.indent)(`${chalk_1.default.gray('■')} JSON report generated at ${this.configuration.outputFilePath}`)); } return report; } log(message) { // istanbul ignore next if (this.configuration.outputs) { process.stdout.write(message); process.stdout.write('\n'); } } } exports.Runner = Runner;