@confluentinc/schemaregistry
Version:
Node.js client for Confluent Schema Registry
145 lines (144 loc) • 6.99 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RestService = void 0;
const axios_1 = __importDefault(require("axios"));
const rest_error_1 = require("./rest-error");
const axios_retry_1 = __importDefault(require("axios-retry"));
const retry_helper_1 = require("./retry-helper");
const package_json_1 = require("./package.json");
const static_token_provider_1 = require("./oauth/static-token-provider");
const oauth_client_1 = require("./oauth/oauth-client");
const oauth_client_azure_imds_1 = require("./oauth/oauth-client-azure-imds");
const toBase64 = (str) => Buffer.from(str).toString('base64');
class RestService {
constructor(baseURLs, isForward, axiosDefaults, basicAuthCredentials, bearerAuthCredentials, maxRetries, retriesWaitMs, retriesMaxWaitMs) {
this.client = axios_1.default.create(axiosDefaults);
(0, axios_retry_1.default)(this.client, {
retries: maxRetries ?? 3,
retryDelay: (retryCount) => {
return (0, retry_helper_1.fullJitter)(retriesWaitMs ?? 1000, retriesMaxWaitMs ?? 20000, retryCount - 1);
},
retryCondition: (error) => {
return (0, retry_helper_1.isRetriable)(error.response?.status ?? 0);
}
});
this.baseURLs = baseURLs;
if (isForward) {
this.setHeaders({ 'X-Forward': 'true' });
}
this.setHeaders({ 'Content-Type': 'application/vnd.schemaregistry.v1+json' });
this.setHeaders({ 'Confluent-Accept-Unknown-Properties': 'true' });
this.setHeaders({ 'Confluent-Client-Version': `javascript/${package_json_1.version}` });
this.handleBasicAuth(basicAuthCredentials);
this.handleBearerAuth(maxRetries ?? 2, retriesWaitMs ?? 1000, retriesMaxWaitMs ?? 20000, bearerAuthCredentials);
}
handleBasicAuth(basicAuthCredentials) {
if (basicAuthCredentials) {
switch (basicAuthCredentials.credentialsSource) {
case 'USER_INFO':
if (!basicAuthCredentials.userInfo) {
throw new Error('User info not provided');
}
this.setAuth(toBase64(basicAuthCredentials.userInfo));
break;
case 'SASL_INHERIT':
if (!basicAuthCredentials.sasl) {
throw new Error('Sasl info not provided');
}
if (basicAuthCredentials.sasl.mechanism?.toUpperCase() === 'GSSAPI') {
throw new Error('SASL_INHERIT support PLAIN and SCRAM SASL mechanisms only');
}
this.setAuth(toBase64(`${basicAuthCredentials.sasl.username}:${basicAuthCredentials.sasl.password}`));
break;
case 'URL':
if (!basicAuthCredentials.userInfo) {
throw new Error('User info not provided');
}
const basicAuthUrl = new URL(basicAuthCredentials.userInfo);
this.setAuth(toBase64(`${basicAuthUrl.username}:${basicAuthUrl.password}`));
break;
default:
throw new Error('Invalid basic auth credentials source');
}
}
}
handleBearerAuth(maxRetries, retriesWaitMs, retriesMaxWaitMs, bearerAuthCredentials) {
if (bearerAuthCredentials) {
delete this.client.defaults.auth;
const headers = ['logicalCluster', 'identityPoolId'];
const missingHeader = headers.find(header => !(header in bearerAuthCredentials));
if (missingHeader) {
throw new Error(`Bearer auth header '${missingHeader}' not provided`);
}
if (!(bearerAuthCredentials.credentialsSource in
RestService.oauthBearerTokenProviderBuilders)) {
throw new Error('Invalid bearer auth credentials source');
}
this.bearerTokenProvider = RestService.oauthBearerTokenProviderBuilders[bearerAuthCredentials.credentialsSource](bearerAuthCredentials).build(maxRetries, retriesWaitMs, retriesMaxWaitMs);
this.setHeaders(this.bearerTokenProvider.getAdditionalHeaders());
}
}
async handleRequest(url, method, data, // eslint-disable-line @typescript-eslint/no-explicit-any
config) {
if (this.bearerTokenProvider && this.bearerTokenProvider.tokenExpired()) {
await this.setOAuthBearerToken();
}
for (let i = 0; i < this.baseURLs.length; i++) {
try {
this.setBaseURL(this.baseURLs[i]);
const response = await this.client.request({
url,
method,
data,
...config,
});
return response;
}
catch (error) {
if (axios_1.default.isAxiosError(error) && error.response && !(0, retry_helper_1.isSuccess)(error.response.status)) {
const data = error.response.data;
if (data.error_code && data.message) {
error = new rest_error_1.RestError(data.message, error.response.status, data.error_code);
}
else {
error = new Error(`Unknown error: ${error.message}`);
}
}
if (i === this.baseURLs.length - 1) {
throw error;
}
}
}
throw new Error('Internal HTTP retry error'); // Should never reach here
}
setHeaders(headers) {
this.client.defaults.headers.common = { ...this.client.defaults.headers.common, ...headers };
}
setAuth(basicAuth, bearerToken) {
if (basicAuth) {
this.client.defaults.headers.common['Authorization'] = `Basic ${basicAuth}`;
}
if (bearerToken) {
this.client.defaults.headers.common['Authorization'] = `Bearer ${bearerToken}`;
}
}
async setOAuthBearerToken() {
const bearerToken = await this.bearerTokenProvider.getAccessToken();
this.setAuth(undefined, bearerToken);
}
setTimeout(timeout) {
this.client.defaults.timeout = timeout;
}
setBaseURL(baseUrl) {
this.client.defaults.baseURL = baseUrl;
}
}
exports.RestService = RestService;
RestService.oauthBearerTokenProviderBuilders = {
'STATIC_TOKEN': (credentials) => new static_token_provider_1._StaticTokenProviderBuilder(credentials),
'OAUTHBEARER': (credentials) => new oauth_client_1._OAuthClientBuilder(credentials),
'OAUTHBEARER_AZURE_IMDS': (credentials) => new oauth_client_azure_imds_1._AzureIMDSOAuthClientBuilder(credentials)
};