@knapsack-pro/core
Version:
Knapsack Pro Core library splits tests across CI nodes and makes sure that tests will run in optimal time on each CI node. This library gives core features like communication with KnapsackPro.com API. This library is a dependency for other projects specif
179 lines (178 loc) • 7.71 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.KnapsackProAPI = exports.getHeaders = void 0;
const axios = __importStar(require("axios"));
const axios_retry_1 = __importDefault(require("axios-retry"));
const config_1 = require("./config");
const knapsack_pro_logger_1 = require("./knapsack-pro-logger");
const getHeaders = ({ clientName, clientVersion, }) => {
const ci = (0, config_1.ciProvider)();
return {
'KNAPSACK-PRO-CLIENT-NAME': clientName,
'KNAPSACK-PRO-CLIENT-VERSION': clientVersion,
...(ci !== null ? { 'KNAPSACK-PRO-CI-PROVIDER': ci } : {}),
};
};
exports.getHeaders = getHeaders;
class KnapsackProAPI {
api;
knapsackProLogger;
constructor(clientName, clientVersion) {
this.retryCondition = this.retryCondition.bind(this);
this.retryDelay = this.retryDelay.bind(this);
this.knapsackProLogger = new knapsack_pro_logger_1.KnapsackProLogger();
this.api = this.setUpApiClient(clientName, clientVersion);
}
fetchTestsFromQueue(allTestFiles, initializeQueue, attemptConnectToQueue) {
const url = '/v1/queues/queue';
const data = {
test_suite_token: config_1.KnapsackProEnvConfig.testSuiteToken,
can_initialize_queue: initializeQueue,
attempt_connect_to_queue: attemptConnectToQueue,
fixed_queue_split: config_1.KnapsackProEnvConfig.fixedQueueSplit,
commit_hash: config_1.KnapsackProEnvConfig.commitHash,
branch: config_1.KnapsackProEnvConfig.branch,
node_total: config_1.KnapsackProEnvConfig.ciNodeTotal,
node_index: config_1.KnapsackProEnvConfig.ciNodeIndex,
node_build_id: config_1.KnapsackProEnvConfig.ciNodeBuildId,
user_seat: config_1.KnapsackProEnvConfig.maskedUserSeat,
...(initializeQueue &&
!attemptConnectToQueue && {
test_files: allTestFiles,
build_author: (0, config_1.buildAuthor)(),
commit_authors: (0, config_1.commitAuthors)(),
}),
};
return this.api.post(url, data);
}
createBuildSubset(recordedTestFiles) {
const url = '/v1/build_subsets';
const data = {
test_suite_token: config_1.KnapsackProEnvConfig.testSuiteToken,
commit_hash: config_1.KnapsackProEnvConfig.commitHash,
branch: config_1.KnapsackProEnvConfig.branch,
node_total: config_1.KnapsackProEnvConfig.ciNodeTotal,
node_index: config_1.KnapsackProEnvConfig.ciNodeIndex,
test_files: recordedTestFiles,
};
return this.api.post(url, data);
}
isExpectedErrorStatus(error) {
const { response } = error;
if (!response) {
return false;
}
const { status } = response;
return (status === 400 ||
status === 422 ||
status === 403);
}
setUpApiClient(clientName, clientVersion) {
const apiClient = axios.create({
baseURL: config_1.KnapsackProEnvConfig.endpoint,
timeout: 15000,
headers: (0, exports.getHeaders)({ clientName, clientVersion }),
});
(0, axios_retry_1.default)(apiClient, {
retries: 2,
shouldResetTimeout: true,
retryDelay: this.retryDelay,
retryCondition: this.retryCondition,
});
apiClient.interceptors.request.use((config) => {
const { method, baseURL, url, headers, data } = config;
const apiUrl = baseURL + url.replace(baseURL ?? '', '');
const requestHeaders = knapsack_pro_logger_1.KnapsackProLogger.objectInspect(headers);
const requestBody = knapsack_pro_logger_1.KnapsackProLogger.objectInspect(data);
this.knapsackProLogger.info(`${method?.toUpperCase()} ${apiUrl}`);
this.knapsackProLogger.debug(`${method?.toUpperCase()} ${apiUrl}\n\n` +
'Request headers:\n' +
`${requestHeaders}\n\n` +
'Request body:\n' +
`${requestBody}`);
return config;
});
apiClient.interceptors.response.use((response) => {
const { status, statusText, data, headers: { 'x-request-id': requestId }, } = response;
const responeseBody = knapsack_pro_logger_1.KnapsackProLogger.objectInspect(data);
this.knapsackProLogger.info(`${status} ${statusText}\n\n` +
'Request ID:\n' +
`${requestId}\n\n` +
'Response body:\n' +
`${responeseBody}`);
return response;
}, (error) => {
const { response } = error;
if (response) {
const { status, statusText, data, headers: { 'x-request-id': requestId }, } = response;
const responeseBody = knapsack_pro_logger_1.KnapsackProLogger.objectInspect(data);
this.knapsackProLogger.error(`${status} ${statusText}\n\n` +
'Request ID:\n' +
`${requestId}\n\n` +
'Response error body:\n' +
`${responeseBody}`);
}
else {
this.knapsackProLogger.error(error);
}
return Promise.reject(error);
});
return apiClient;
}
retryCondition(error) {
return (axios_retry_1.default.isNetworkError(error) ||
this.isRetriableRequestError(error) ||
!this.isExpectedErrorStatus(error));
}
isRetriableRequestError(error) {
if (!error.config) {
return false;
}
return axios_retry_1.default.isRetryableError(error);
}
retryDelay(retryCount) {
const requestRetryTimebox = 8000;
const delay = retryCount * requestRetryTimebox;
const randomSum = delay * 0.2 * Math.random();
const finalDelay = delay + randomSum;
this.knapsackProLogger.info(`(${retryCount}) Wait ${finalDelay} ms and retry request to Knapsack Pro API.`);
return finalDelay;
}
}
exports.KnapsackProAPI = KnapsackProAPI;