UNPKG

neverbounce

Version:

An API wrapper for the NeverBounce API

104 lines (103 loc) 5.03 kB
import _Error from './Errors.js'; /** * HTTP Client for making API requests using fetch */ class HttpsClient { /** * Constructor * @param _nb NeverBounce client instance */ constructor(_nb) { this._nb = _nb; // Get version from package.json this._version = '5.0.0'; // This will be replaced with dynamic import when we implement the build process } /** * Performs API requests using fetch * @param params Request parameters * @param data Request data * @returns Promise with API response */ async request(params, data = {}) { const config = this._nb.getConfig(); // Set key data.key = config.apiKey; // Get request options const opts = this._nb.getRequestOpts(params); const path = opts.path ? `/${config.apiVersion}/${opts.path}` : ''; // Build URL const url = new URL(`https://${opts.host}:${opts.port}${path}`); // Set up fetch options const fetchOptions = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': opts.acceptedType, 'User-Agent': `NeverBounceApi-NodeJS/${this._version}` }, body: JSON.stringify(data) }; // Set up AbortController for timeout const controller = new AbortController(); if (config.timeout) { fetchOptions.signal = controller.signal; } try { const response = await fetch(url.toString(), fetchOptions); // Handle HTTP error codes if (response.status >= 400 && response.status < 500) { throw new _Error(_Error.GeneralError, `We were unable to complete your request. The following information was supplied: \n\n(Request error [status ${response.status}])`); } if (response.status >= 500) { throw new _Error(_Error.GeneralError, `We were unable to complete your request. The following information was supplied: \n\n(Internal error [status ${response.status}])`); } // Check content type const contentType = response.headers.get('content-type'); if (contentType?.includes('application/json')) { const decoded = await response.json(); // Check for missing status and error messages if (decoded.status === undefined || (decoded.status !== 'success' && decoded.message === undefined)) { throw new _Error(_Error.GeneralError, 'The response from server is incomplete. Either a status code was not included or ' + 'an error was returned without an error message. Try the request again, if ' + 'this error persists let us know at support@neverbounce.com.' + `\n\n(Internal error [status ${response.status}])`); } // Handle error statuses if (decoded.status !== 'success') { const errorType = _Error._lut[decoded.status] || _Error.GeneralError; if (errorType === _Error.AuthError) { throw new _Error(_Error.AuthError, `We were unable to authenticate your request. The following information was supplied: ${decoded.message}\n\n(auth_failure)`); } else { throw new _Error(errorType, `We were unable to complete your request. The following information was supplied: ${decoded.message}\n\n(${decoded.status})`); } } return decoded; } else if (contentType && contentType !== opts.acceptedType) { throw new _Error(_Error.GeneralError, `The response from NeverBounce was returned with the type "${contentType}" but a response ` + `type of "${opts.acceptedType}" was expected. Try the request again, if this error persists ` + 'let us know at support@neverbounce.com.\n\n(Internal error)'); } // Return text response for non-JSON content types const textResponse = await response.text(); return textResponse; } catch (error) { // Handle fetch errors or our custom errors if (error instanceof _Error) { throw error; } // Handle timeout or network errors if (error instanceof Error) { if (error.name === 'AbortError') { throw new _Error(_Error.GeneralError, 'The request timed out. Please try again later.'); } throw new _Error(_Error.GeneralError, `Network error: ${error.message}`); } // Fallback for unknown errors throw new _Error(_Error.GeneralError, 'An unknown error occurred during the request.'); } } } export default HttpsClient;