UNPKG

apiveritas

Version:

Lightweight CLI tool for consumer-driven API contract testing via JSON schema and payload comparisons.

108 lines (107 loc) 5.16 kB
"use strict"; /** * @file ApiCaller.ts * @author Mario Galea * @description * Responsible for executing a list of API requests defined in a Test Suite. * This class handles HTTP request logic using Axios, saves the responses to disk, and logs execution details. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApiCaller = void 0; const axios_1 = __importDefault(require("axios")); const ResponseSaver_1 = require("../utils/ResponseSaver"); const Logger_1 = require("../utils/Logger"); const chalk_1 = __importDefault(require("chalk")); /** * Executes a list of API requests, logs outcomes, and saves payloads for contract testing. */ class ApiCaller { /** * Constructs a new ApiCaller instance. * * @param requests - An array of API requests to be executed. * @param logger - A logger instance for structured logging. Defaults to a new Logger instance. * @param baseUrl - The base URL to prepend to all request paths unless overridden per request. */ constructor(requests, logger = new Logger_1.Logger(), baseUrl = '') { this.baseUrl = baseUrl; this.requests = requests; this.logger = logger; this.baseUrl = baseUrl; } /** * Iterates over the provided list of API requests, executes them using Axios, and saves each response to disk. * * - Logs all actions using the provided Logger. * - Automatically validates HTTP status codes if `expectedStatus` is defined in the request. * - Responses are saved to the payloads directory using the ResponseSaver utility. * - Supports per-request `baseUrl` override (useful for mock server mode). * * @returns A Promise that resolves when all API calls have completed (successfully or with handled failure). * * @throws If a request is missing its required `testSuite` field, an error is thrown immediately. * If a request is missing its `url`, an error is logged and the request is skipped. */ async callAll() { this.logger.info(`Base URL: ${this.baseUrl}`); const saver = new ResponseSaver_1.ResponseSaver(); this.logger.info(chalk_1.default.white.bold.underline('Test Run:\n')); for (const req of this.requests) { const method = (req.method ?? 'GET').toUpperCase(); const safeName = req.name.replace(/\s+/g, '_'); const testSuite = req.testSuite; if (!testSuite) { throw new Error(`Missing testSuite name in request: ${req.name}`); } if (!req.url) { this.logger.error(`Missing URL in request: ${req.name}`); continue; } // Allow per-request base URL override const effectiveBaseUrl = req.baseUrl ?? this.baseUrl; const fullUrl = effectiveBaseUrl + req.url; console.log(fullUrl); this.logger.info(`Calling URL: ${fullUrl} (method: ${method})`); try { const axiosConfig = { url: fullUrl, method, auth: req.auth, data: req.body ?? undefined, validateStatus: () => true, // allow all HTTP responses }; this.logger.debug(`Calling [${req.name}] with config:`); this.logger.debug(JSON.stringify(axiosConfig, null, 2)); const response = await (0, axios_1.default)(axiosConfig); if (req.expectedStatus && response.status !== req.expectedStatus) { this.logger.error(`[${req.name}] returned status ${response.status}, expected ${req.expectedStatus}`); } else { this.logger.info(`[${req.name}] returned expected status ${response.status}`); } this.logger.debug(`Response from [${req.name}]`); saver.saveResponse(testSuite, safeName, response.data); this.logger.info(`Saved response: payloads/${saver.getTimestampFolderName()}/${testSuite}/${safeName}.json`); } catch (error) { if (error instanceof Error) { this.logger.error(chalk_1.default.red(`❌ Failed to call [${req.name}]: ${error.message}`)); // Cast error to any to access response (Axios error) const anyErr = error; if (anyErr.response) { this.logger.error(chalk_1.default.red(`-> HTTP Status: ${anyErr.response.status}`)); this.logger.error(chalk_1.default.red(`-> Response body: ${JSON.stringify(anyErr.response.data)}`)); } } else { this.logger.error(chalk_1.default.red(`❌ Failed to call [${req.name}]: Unknown error`)); } } console.log(''); } } } exports.ApiCaller = ApiCaller;