UNPKG

@prismatic-io/spectral

Version:

Utility library for building Prismatic connectors and code-native integrations

220 lines (219 loc) 10.9 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.inputs = exports.buildRawRequestAction = exports.sendRawRequest = exports.handleErrors = exports.createClient = void 0; const isEmpty_1 = __importDefault(require("lodash/isEmpty")); const axios_1 = __importDefault(require("axios")); const axios_retry_1 = __importStar(require("axios-retry")); const form_data_1 = __importDefault(require("form-data")); const object_sizeof_1 = __importDefault(require("object-sizeof")); const __1 = require("../.."); const util_1 = __importDefault(require("../../util")); const inputs_1 = require("./inputs"); Object.defineProperty(exports, "inputs", { enumerable: true, get: function () { return inputs_1.inputs; } }); const toAuthorizationHeaders = (connection) => { var _a, _b, _c, _d; const accessToken = util_1.default.types.toString((_a = connection.token) === null || _a === void 0 ? void 0 : _a.access_token); if (accessToken) { return { Authorization: `Bearer ${accessToken}` }; } const apiKey = util_1.default.types.toString((_b = connection.fields) === null || _b === void 0 ? void 0 : _b.apiKey); if (apiKey) { return { Authorization: `Bearer ${apiKey}` }; } const username = util_1.default.types.toString((_c = connection.fields) === null || _c === void 0 ? void 0 : _c.username); const password = util_1.default.types.toString((_d = connection.fields) === null || _d === void 0 ? void 0 : _d.password); if (username && password) { const encoded = Buffer.from(`${username}:${password}`).toString("base64"); return { Authorization: `Basic ${encoded}` }; } throw new Error(`Failed to guess at authorization parameters for Connection: ${connection.key}`); }; const toFormData = (formData, fileData, fileDataFileNames = {}) => { const form = new form_data_1.default(); (formData || []).map(({ key, value }) => form.append(key, value)); (fileData || []).map(({ key, value }) => form.append(key, util_1.default.types.toBufferDataPayload(value).data, { filename: (fileDataFileNames === null || fileDataFileNames === void 0 ? void 0 : fileDataFileNames[key]) || key, })); return form; }; const computeRetryDelay = (retryDelay, useExponentialBackoff) => { if (useExponentialBackoff) { return axios_retry_1.exponentialDelay; } return typeof retryDelay === "number" ? () => retryDelay : retryDelay; }; const toAxiosRetryConfig = (_a) => { var { retryDelay, retryAllErrors, retryCondition, useExponentialBackoff } = _a, rest = __rest(_a, ["retryDelay", "retryAllErrors", "retryCondition", "useExponentialBackoff"]); return (Object.assign(Object.assign({}, rest), { retryDelay: computeRetryDelay(retryDelay, useExponentialBackoff), retryCondition: retryAllErrors ? () => true : typeof retryCondition === "function" ? retryCondition : axios_retry_1.isNetworkOrIdempotentRequestError })); }; /** * Creates a reusable Axios HTTP client. See * https://prismatic.io/docs/custom-connectors/connections/#using-the-built-in-createclient-http-client */ const createClient = ({ baseUrl, responseType, headers, timeout, params, debug = false, retryConfig, }) => { const client = axios_1.default.create({ baseURL: baseUrl, responseType, headers, timeout, params, maxContentLength: Number.POSITIVE_INFINITY, maxBodyLength: Number.POSITIVE_INFINITY, }); if (debug) { client.interceptors.request.use((request) => { const { baseURL, headers, method, timeout, url, data, params } = request; const dataSize = (0, object_sizeof_1.default)(data); console.log(util_1.default.types.toJSON({ type: "request", baseURL, params, url, headers, method, timeout, data: dataSize > 1024 * 10 || Buffer.isBuffer(data) ? `<data (${dataSize} bytes)>` : data, }, true, true)); return request; }); client.interceptors.response.use((response) => { const { headers, status, statusText, data } = response; const dataSize = (0, object_sizeof_1.default)(data); console.log(util_1.default.types.toJSON({ type: "response", headers, status, statusText, data: dataSize > 1024 * 10 || Buffer.isBuffer(data) ? `<data (${dataSize} bytes)>` : data, }, true, true)); return response; }); } if (retryConfig) { // eslint-disable-next-line @typescript-eslint/no-explicit-any (0, axios_retry_1.default)(client, toAxiosRetryConfig(retryConfig)); } return client; }; exports.createClient = createClient; /** * A global error handler that examines a thrown error and yields additional * information if the error was produced by Spectral's HTTP client. See * https://prismatic.io/docs/custom-connectors/error-handling/ * and * https://prismatic.io/docs/custom-connectors/connections/#using-the-built-in-createclient-http-client * * @param error A JavaScript error to handle * @returns An error with data, status and headers if it was an Axios error, or the error otherwise */ const handleErrors = (error) => { if (axios_1.default.isAxiosError(error)) { const { message, response } = error; return { message, data: response === null || response === void 0 ? void 0 : response.data, status: response === null || response === void 0 ? void 0 : response.status, headers: response === null || response === void 0 ? void 0 : response.headers, }; } return error; }; exports.handleErrors = handleErrors; /** * This function allows you to build a generic "Raw Request" action * for a custom connector. See * https://prismatic.io/docs/integrations/low-code-integration-designer/raw-request-actions/#building-an-http-raw-request-action-in-your-custom-component * * @param baseUrl The base URL of the API you're integrating with * @param values An object comprising the HTTP request you'd like to make * @param authorizationHeaders Auth headers to apply to the request * @returns The response to the request */ const sendRawRequest = (baseUrl_1, values_1, ...args_1) => __awaiter(void 0, [baseUrl_1, values_1, ...args_1], void 0, function* (baseUrl, values, authorizationHeaders = {}) { if (values.data && (!(0, isEmpty_1.default)(values.formData) || !(0, isEmpty_1.default)(values.fileData))) { throw new Error("Cannot specify both Data and File/Form Data."); } const payload = !(0, isEmpty_1.default)(values.formData) || !(0, isEmpty_1.default)(values.fileData) ? toFormData(values.formData, values.fileData, values.fileDataFileNames) : values.data; const client = (0, exports.createClient)({ baseUrl, debug: values.debugRequest, responseType: values.responseType, timeout: values.timeout, retryConfig: { retries: values.maxRetries, retryDelay: values.retryDelayMS, retryAllErrors: values.retryAllErrors, useExponentialBackoff: values.useExponentialBackoff, }, }); return yield client.request({ method: values.method, url: values.url, headers: Object.assign(Object.assign(Object.assign({}, util_1.default.types.keyValPairListToObject(values.headers)), (payload instanceof form_data_1.default ? payload.getHeaders() : {})), authorizationHeaders), params: util_1.default.types.keyValPairListToObject(values.queryParams), data: payload || undefined, }); }); exports.sendRawRequest = sendRawRequest; const buildRawRequestAction = (baseUrl, label = "Raw Request", description = "Issue a raw HTTP request") => (0, __1.action)({ display: { label, description }, inputs: Object.assign({ connection: { label: "Connection", type: "connection", required: true } }, inputs_1.inputs), perform: (context, _a) => __awaiter(void 0, void 0, void 0, function* () { var { connection } = _a, httpInputValues = __rest(_a, ["connection"]); const { data } = yield (0, exports.sendRawRequest)(baseUrl, httpInputValues, toAuthorizationHeaders(connection)); return { data }; }), }); exports.buildRawRequestAction = buildRawRequestAction;