UNPKG

@confluentinc/schemaregistry

Version:
163 lines (162 loc) 7.53 kB
"use strict"; 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 oauth_client_1 = require("./oauth/oauth-client"); const rest_error_1 = require("./rest-error"); const axios_retry_1 = __importDefault(require("axios-retry")); const retry_helper_1 = require("./retry-helper"); const toBase64 = (str) => Buffer.from(str).toString('base64'); class RestService { constructor(baseURLs, isForward, axiosDefaults, basicAuthCredentials, bearerAuthCredentials, maxRetries, retriesWaitMs, retriesMaxWaitMs) { this.oauthBearer = false; 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.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`); } this.setHeaders({ 'Confluent-Identity-Pool-Id': bearerAuthCredentials.identityPoolId, 'target-sr-cluster': bearerAuthCredentials.logicalCluster }); switch (bearerAuthCredentials.credentialsSource) { case 'STATIC_TOKEN': if (!bearerAuthCredentials.token) { throw new Error('Bearer token not provided'); } this.setAuth(undefined, bearerAuthCredentials.token); break; case 'OAUTHBEARER': this.oauthBearer = true; const requiredFields = [ 'clientId', 'clientSecret', 'issuerEndpointUrl', 'scope' ]; const missingField = requiredFields.find(field => !(field in bearerAuthCredentials)); if (missingField) { throw new Error(`OAuth credential '${missingField}' not provided`); } const issuerEndPointUrl = new URL(bearerAuthCredentials.issuerEndpointUrl); this.oauthClient = new oauth_client_1.OAuthClient(bearerAuthCredentials.clientId, bearerAuthCredentials.clientSecret, issuerEndPointUrl.origin, issuerEndPointUrl.pathname, bearerAuthCredentials.scope, maxRetries, retriesWaitMs, retriesMaxWaitMs); break; default: throw new Error('Invalid bearer auth credentials source'); } } } async handleRequest(url, method, data, // eslint-disable-line @typescript-eslint/no-explicit-any config) { if (this.oauthBearer) { 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() { if (!this.oauthClient) { throw new Error('OAuthClient not initialized'); } const bearerToken = await this.oauthClient.getAccessToken(); this.setAuth(undefined, bearerToken); } setTimeout(timeout) { this.client.defaults.timeout = timeout; } setBaseURL(baseUrl) { this.client.defaults.baseURL = baseUrl; } } exports.RestService = RestService;