UNPKG

@qrvey/fetch

Version:

![install size](https://packagephobia.now.sh/badge?p=%40qrvey%2Ffetch) ![coverage](https://img.shields.io/badge/unit_test_coverage-93%25-brightgreen)

340 lines (332 loc) 12.2 kB
'use strict'; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; // src/helpers/constants.ts var HTTPAction = { GET: "GET", POST: "POST", PATCH: "PATCH", DELETE: "DELETE", PUT: "PUT" }; // src/helpers/errors/customError.ts var CustomError = class extends Error { constructor(message, errorDetails, stack, context = process.env.SERVICE_NAME || "QrveyCustomError") { super(message); this.errorDetails = errorDetails; this.context = context; this.name = "ERROR in " + this.context; this.stack = stack; } toJSON() { return { name: this.name, message: this.message, stack: this.stack, details: this.errorDetails }; } }; // src/helpers/errors/restClientError.ts var RestClientError = class extends CustomError { // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(url, restError = {}, status) { const message = `REST Client Error at ${url}${(restError == null ? void 0 : restError.message) ? `: ${restError == null ? void 0 : restError.message}` : ""}`; super(message, restError, restError.stack); if (status !== void 0) this.status = status; } }; // src/helpers/errors/restBadHttpActionParams.ts var RestBadHttpActionParams = class extends CustomError { // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(url, message, error = {}) { const errorMessage = `Bad RestHttpAction params at ${url}: ${message}`; super(errorMessage, error.message || error, error.stack); } }; // src/services/fetchClient.service.ts var FetchClientService = class { static validateEndpoint(endpoint) { if (endpoint.startsWith("/")) return; const errorMessage = `Invalid endpoint "${endpoint}". Please replace with "/${endpoint}"`; throw new RestBadHttpActionParams(endpoint, errorMessage); } static buildUrl(endpoint, options) { var _a, _b; const baseDomain = (_a = process.env.DOMAIN) != null ? _a : ""; const privateDomain = (_b = process.env.PRIVATE_DOMAIN) != null ? _b : ""; const defaultDomain = options.privateDomain === false ? baseDomain : privateDomain; return `${options.baseDomain || defaultDomain}${endpoint}`; } static baseHeaders(options) { var _a, _b, _c, _d; const baseHeaders = { "Content-Type": "application/json" }; const customContentTypeHeader = ((_a = options.headers) == null ? void 0 : _a["content-type"]) || ((_b = options.headers) == null ? void 0 : _b["Content-Type"]); if (customContentTypeHeader) { baseHeaders["Content-Type"] = customContentTypeHeader; } if (options.useApiKey && process.env.API_KEY) baseHeaders["x-api-key"] = process.env.API_KEY; const serviceNameEnv = (_c = process.env.SERVICE_NAME) == null ? void 0 : _c.trim(); const hostnameEnv = (_d = process.env.HOSTNAME) == null ? void 0 : _d.trim(); if (!baseHeaders["q-service-name"] && (serviceNameEnv == null ? void 0 : serviceNameEnv.length)) { baseHeaders["q-service-name"] = serviceNameEnv; } if (!baseHeaders["q-hostname"] && (hostnameEnv == null ? void 0 : hostnameEnv.length)) { baseHeaders["q-hostname"] = hostnameEnv; } return baseHeaders; } static customHeaders(options) { const _a = options.headers || {}, { // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars "Content-Type": _ContentType, // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars "content-type": _contentType } = _a, customHeadersObject = __objRest(_a, [ "Content-Type", "content-type" ]); return customHeadersObject; } static internalHeaders() { var _a, _b; const internalHeaders = {}; const serviceNameEnv = (_a = process.env.SERVICE_NAME) == null ? void 0 : _a.trim(); const hostnameEnv = (_b = process.env.HOSTNAME) == null ? void 0 : _b.trim(); if (serviceNameEnv == null ? void 0 : serviceNameEnv.length) { internalHeaders["q-service-name"] = serviceNameEnv; } if (hostnameEnv == null ? void 0 : hostnameEnv.length) { internalHeaders["q-hostname"] = hostnameEnv; } return internalHeaders; } static buildFetchOptions(method, body, options) { const headers = __spreadValues(__spreadValues(__spreadValues({}, this.baseHeaders(options)), this.customHeaders(options)), this.internalHeaders()); const filteredHeaders = Object.fromEntries( Object.entries(headers).filter( ([, value]) => value !== null && value !== void 0 ) ); const requestOptions = { headers: filteredHeaders, method }; if (body) requestOptions["body"] = JSON.stringify(body); return requestOptions; } static isValidUrl(url) { return !!(url.protocol && url.hostname && url.pathname); } static hasValidProtocol(url) { const secureProtocols = ["http:", "https:"]; return secureProtocols.includes(url.protocol); } static verifyUrl(urlObj) { if (!this.isValidUrl(urlObj)) { throw new RestBadHttpActionParams( "Invalid URL", `URL details: Protocol: ${urlObj.protocol}, Hostname: ${urlObj.hostname}, Pathname: ${urlObj.pathname}` ); } if (!this.hasValidProtocol(urlObj)) { throw new RestBadHttpActionParams( "Invalid URL", `URL protocol [${urlObj.protocol}] is not secure` ); } } static toHttpUrl(url) { try { const urlObj = new URL(url); this.verifyUrl(urlObj); return urlObj.href; } catch (error) { throw new RestBadHttpActionParams( `Invalid URL [${url}]`, "URL is not valid.", error ); } } static isFileDownload(contentType, contentDisposition) { return (contentDisposition == null ? void 0 : contentDisposition.includes("attachment")) || (contentType == null ? void 0 : contentType.includes("application/octet-stream")) || (contentType == null ? void 0 : contentType.includes("text/csv")) || (contentType == null ? void 0 : contentType.includes("application/vnd.ms-excel")) || (contentType == null ? void 0 : contentType.includes("application/vnd.openxmlformats")) || (contentType == null ? void 0 : contentType.includes("application/zip")) || (contentType == null ? void 0 : contentType.includes("application/pdf")); } static async getResponseData(isJsonResponse, response, options) { try { if (options.returnRawResponse) { return response; } const contentType = response.headers.get("content-type"); const contentDisposition = response.headers.get( "content-disposition" ); const contentEncoding = response.headers.get("content-encoding"); const isFileDownload = this.isFileDownload( contentType, contentDisposition ); if (options.skipJsonParsing && contentEncoding) { return await response.arrayBuffer(); } if (options.skipJsonParsing && isFileDownload) { return await response.arrayBuffer(); } if (options.skipJsonParsing) { return await response.text(); } const text = await response.text(); if (isJsonResponse) { try { return JSON.parse(text); } catch (e) { return text; } } return text; } catch (error) { console.error("Error processing response data:", error); return null; } } static getResponseHeaders(response) { const headers = {}; response.headers.forEach((value, key) => { headers[key] = value; }); return headers; } static async handleResponse(response, httpActionOptions) { const responseContentType = response.headers.get("content-type"); const isJsonResponse = responseContentType && responseContentType.includes("application/json") && !httpActionOptions.skipJsonParsing; const responseData = await this.getResponseData( !!isJsonResponse, response, httpActionOptions ); if (response.ok) { if (httpActionOptions.includeHttpResponseDetails) { const headers = this.getResponseHeaders(response); return { status: response.status, data: responseData, headers }; } return responseData; } throw new RestClientError( response.url, responseData != null ? responseData : {}, response.status ); } static fetchData(url, { fetchOptions, httpActionOptions }) { return fetch(url, fetchOptions).then( (response) => this.handleResponse(response, httpActionOptions) ).catch((error) => { if (error instanceof RestClientError) throw error; throw new RestClientError(url, error); }); } static queryParamsToQueryString(queryParameters) { const queryParamsArray = Object.entries(queryParameters).map( ([queryName, queryValue]) => { const isArrayValue = Array.isArray(queryValue); if (isArrayValue) return queryValue.map((val) => `${queryName}=${val}`).join("&"); else return `${queryName}=${queryValue}`; } ); return queryParamsArray.join("&"); } static httpAction(method, endpoint, body, options = {}) { this.validateEndpoint(endpoint); const url = this.buildUrl(endpoint, options); const queryParamsString = options.queryParameters ? `?${this.queryParamsToQueryString(options.queryParameters)}` : ""; const httpUrl = this.toHttpUrl(`${url}${queryParamsString}`); const fetchDataOptions = { fetchOptions: this.buildFetchOptions( method.toUpperCase(), body, options ), httpActionOptions: options }; return this.fetchData(httpUrl, fetchDataOptions); } }; // src/services/fetch.service.ts var FetchService = class _FetchService { static sendRequest(endpoint, body, options) { return FetchClientService.httpAction( options.method, endpoint, body, options ); } static get(endpoint, options) { return _FetchService.sendRequest(endpoint, null, __spreadProps(__spreadValues({}, options), { method: HTTPAction.GET })); } static post(endpoint, body, options) { return _FetchService.sendRequest(endpoint, body, __spreadProps(__spreadValues({}, options), { method: HTTPAction.POST })); } static put(endpoint, body, options) { return _FetchService.sendRequest(endpoint, body, __spreadProps(__spreadValues({}, options), { method: HTTPAction.PUT })); } static patch(endpoint, body, options) { return _FetchService.sendRequest(endpoint, body, __spreadProps(__spreadValues({}, options), { method: HTTPAction.PATCH })); } static delete(endpoint, body, options) { return _FetchService.sendRequest(endpoint, body, __spreadProps(__spreadValues({}, options), { method: HTTPAction.DELETE })); } }; exports.FetchService = FetchService; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map