@unito/integration-debugger
Version:
The Unito Integration Debugger
313 lines (312 loc) • 13.8 kB
JavaScript
"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;