UNPKG

@algodex/algodex-sdk

Version:

API calls for interacting with the Algorand blockchain

153 lines (146 loc) 4.47 kB
/* * Copyright (C) 2021-2022 Algodex VASP (BVI) Corp. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ const axios = require('axios'); /** * @typedef import('axios').AxiosResponse AxiosResponse */ /** * # 🕸 Basic HTTP API Client * * HTTP interfaces * * Interfaces are based on Axios. Provides useful features like etag caching * * @example * // importing a client * const HTTPClient = require('@algodex/algodex-sdk/lib/http/HTTPClient') * const client = new HTTPClient('https://www.google.com', false) * client.get('/somepath').then(console.log) * * @example * // Creating a custom client * const HTTPClient = require('../HTTPClient'); * // Declare any defaults * const BASE_URL = 'https://httpbin.org/get'; * * // Static Function * function mapResponse({status, ...rest}){ * return Object.create({status: `mapped-${status}`, ...rest}) * } * * // Custom Client Constructor * function CustomClient(baseUrl=BASE_URL, etags=false) { * // Apply client * const client = new HTTPClient(baseUrl, etags); * Object.keys(client).forEach((key)=>{ * this[key] = client[key]; * }); * } * * // Statics * CustomClient.mapResponse = mapResponse; * * // Extend from HTTPClient * CustomClient.prototype = Object.create(HTTPClient.prototype); * // Override/Assign prototypes * Object.assign(ExplorerClient.prototype, { * async fetchHTTPBinGet() { * return await this.get(`${this.baseUrl}`); * }, * }); * * module.exports = CustomClient; * * @TODO: Generate from OAS/Swagger * @param {string} baseUrl Base URL to use for API requests * @param {boolean} [etags] Flag to enable Etag on GET Requests * @param {Config} [config] Optional configuration for services * @see https://axios-http.com/docs/intro * @ignore */ function HTTPClient(baseUrl, etags = false, config) { // TODO: Check for valid URL if (typeof baseUrl === 'undefined') throw new TypeError('Must have a valid URL'); if (typeof etags !== 'boolean') throw new TypeError('etags must be a boolean!'); /** * Algodex Config * @type {Config} */ this.config = config; /** * Base URL * @type {string} */ this.baseUrl = baseUrl; /** * Etag State * @type {boolean} */ this.etags = etags; if (etags) { /** * Cache for ETags * @type {{res: {}, etag: {}}} */ this.cache = { etag: {}, res: {}, }; } } // Extend from axios HTTPClient.prototype = Object.create(axios); // Override Axios with instance properties Object.assign(HTTPClient.prototype, { /** * * Optionally check for etag changes in the API and update the local cache * when it is stale. Used to reduce request load from react-query * to the backend. * * @param {string} url Request URL * @param {object} options HTTPClient/Axios Options * @return {Promise<AxiosResponse>} Response or Cached Result * @memberOf http.HTTPClient.prototype */ async get(url, options={}) { if (typeof url !== 'string') throw new TypeError('Must be a valid URL'); if (typeof options !== 'object') throw new TypeError('Options must be an Object!'); if (this.etags && typeof this.cache.etag[url] !== 'undefined') { if (typeof options.headers === 'undefined') { options.headers = {}; } options.headers['if-none-match'] = this.cache.etag[url]; } return !this.etags ? await axios.get(url, options) : await axios.get(url, options) .then((res) => { if (res && res.status === 200) { const etag = res.headers.etag; res.data.etag = etag; this.cache.res[url] = res; this.cache.etag[url] = etag; // console.log('NOT returning cache for ' + url); return res; } else { throw new Error('Unexpected Status'); } }) .catch((error) => { const errorResp = error.response; if (errorResp && errorResp.status === 304) { // console.log('returning cache for ' + url); return this.cache.res[url]; } else { throw new Error(`Invalid response for ${url} : ${error.message}`); } }); }, }); module.exports = HTTPClient;