@prismatic-io/spectral
Version:
Utility library for building Prismatic connectors and code-native integrations
220 lines (219 loc) • 10.9 kB
JavaScript
;
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;