@constructor-io/constructorio-connect-cli
Version:
CLI tool to enable users to interface with the Constructor Connect Ecosystem
139 lines (138 loc) • 5.22 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 () {
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.getHttpClient = getHttpClient;
exports.checkRetryCondition = checkRetryCondition;
exports.calculateRetryDelay = calculateRetryDelay;
const errors_1 = require("@oclif/core/errors");
const axios_1 = __importStar(require("axios"));
const axios_retry_1 = __importDefault(require("axios-retry"));
const get_connect_token_1 = require("../customer/get-connect-token");
const render_tip_1 = require("../rendering/render-tip");
const version_1 = __importDefault(require("../version"));
const RETRY_IGNORED_HTTP_STATUS_CODES = [
/**
* HTTP 400: Bad Request
* This error is thrown whenever data validation fails.
*/
400,
/**
* HTTP 401: Unauthorized
* This error is thrown whenever a user has invalid credentials.
*/
401,
/**
* HTTP 403: Forbidden
* This error is thrown whenever a user has no permissions to access a resource.
*/
403,
];
const MAX_RETRIES = 3;
/**
* @returns An axios instance with the correct base URL and auth token.
*/
async function getHttpClient({ requiresAuth } = {
requiresAuth: true,
}) {
const headers = {
"X-CLI-Version": version_1.default,
};
if (requiresAuth) {
const token = await (0, get_connect_token_1.getConnectToken)();
headers.Authorization = `Bearer ${token}`;
}
const baseURL = process.env.HOST ?? "https://connect.cnstrc.com";
const instance = axios_1.default.create({
baseURL,
headers,
});
(0, axios_retry_1.default)(instance, {
retries: MAX_RETRIES,
/**
* Avoid retrying requests that failed because of an authentication error.
*/
retryCondition: checkRetryCondition,
/**
* If we get a 429 response, we want to wait some more time before
* retrying the request to allow the rate limit to reset.
*/
retryDelay: calculateRetryDelay,
/**
* We want to always retry with the same timeout value.
* Otherwise axios-retry will only retry the request if
* the sum of all requests durations are less than the timeout value.
*/
shouldResetTimeout: true,
});
instance.interceptors.response.use((response) => response, async (error) => {
if ((0, axios_1.isAxiosError)(error) && error.response) {
if (error.response.status === 403) {
throw new errors_1.CLIError("💥 Error while authenticating with the Constructor Connect API. Please check your CONNECT_AUTH_TOKEN environment variable.", {
suggestions: [
"Check the Connect Auth token you provided and try again",
"If you don't have one yet, please reach out over at partners@constructor.io",
],
});
}
}
// eslint-disable-next-line promise/no-promise-in-callback
return await Promise.reject(error);
});
return instance;
}
/**
* Checks if a request should be retried.
*/
function checkRetryCondition(error) {
const status = error?.response?.status;
const isIgnoredError = RETRY_IGNORED_HTTP_STATUS_CODES.includes(status);
return !isIgnoredError;
}
/**
* Calculates the time in ms to wait before retrying the request.
*/
function calculateRetryDelay(retryCount, error) {
const retryAfterMs = error?.response?.status === 429 ? 2000 : 500;
(0, render_tip_1.renderTip)([
`Retrying request after ${retryAfterMs}ms, currently on retry number ${retryCount}/${MAX_RETRIES}.`,
], {
breakFirstLine: true,
emoji: "🤔",
});
return retryAfterMs;
}