http.js

/* eslint-disable no-console */
import axios from 'axios';
import { version } from '../package.json';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import RequestError from './exceptions';

/**
 * Perform a request and raise error when status is not 2XX
 * @async
 * @param {function} fn - Function that will perform a request
 * @returns {object} Response
 * @throws {RequestError|Error}
 */
async function raiseForStatus(fn) {
  try {
    const { data } = await fn;
    return data;
  } catch (error) {
    if (error.response) {
      throw new RequestError(error.response.status, error.response.data);
    } else {
      throw error;
    }
  }
}

/** Class representing an active Belvo API session */
class APISession {
  /**
   * Create a session.
   * @param {string} url - Belvo API host URL.
   */
  constructor(url) {
    this.session = axios.create({
      baseURL: url,
      headers: {
        'Content-Type': 'application/json',
        'User-Agent': `belvo-js (${version})`,
      },
    });
  }

  /**
   * Login to Belvo API using secret key credentials.
   * @async
   * @param {string} secretKeyId - The Id of the key.
   * @param {string} secretKeyPassword - The password of the key.
   * @returns {boolean} true when login is sucessful else false.
   */
  async login(secretKeyId, secretKeyPassword) {
    const auth = {
      username: secretKeyId,
      password: secretKeyPassword,
    };

    try {
      await raiseForStatus(this.session.get('/api/', { auth }));
    } catch (error) {
      console.log(error);
      return false;
    }
    this.session.defaults.auth = auth;
    this.keyId = secretKeyId;
    this.keyPassword = secretKeyPassword;
    return true;
  }

  /**
   * Get all results from a paginated response
   * @async
   * @param {string} url - API endpoint
   * @param {object} params - Params to filter results in get.
   * @yields {object} The next result in the response.
   */
  async* getAll(url, params = {}) {
    const { data: { results, next } } = await this.session.get(url, { params });

    // eslint-disable-next-line no-restricted-syntax
    for (const item of results) {
      yield item;
    }

    if (next) {
      yield* this.getAll(next);
    }
  }

  /**
   * Get a list of resources.
   * @async
   * @param {string} url - API endpoint
   * @param {number} limit - Maximum number of results to get.
   * @param {object} params - Params to filter results in get.
   * @returns {array} List of resources.
   */
  async list(url, limit = 100, params = {}) {
    const results = [];
    const pageSizeFilter = (limit !== 100) ? { page_size: Math.max(limit, ('page_size' in params) ? params.page_size : 0) } : {};
    const generator = await this.getAll(url, { ...params, ...pageSizeFilter });

    for (let index = 0; index < limit; index += 1) {
      // eslint-disable-next-line no-await-in-loop
      const next = await generator.next();
      if (next.done) { break; }
      results.push(next.value);
    }
    return results;
  }

  /**
   * Get details of a specific resource.
   * @async
   * @param {str} url - API endpoint
   * @param {string} id - UUID4 representing the resource id.
   * @returns {object}
   */
  async get(url, id) {
    const response = await raiseForStatus(this.session.get(`${url}${id}/`));
    return response;
  }

  /**
   * Do a POST request to the API.
   * @async
   * @param {string} url - API endpoint.
   * @param {object} payload - JSON request payload.
   * @returns {object} Response
   * @throws {RequestError}
   */
  async post(url, payload) {
    const response = await raiseForStatus(this.session.post(url, payload));
    return response;
  }

  /**
   * Do a PATCH request to the API.
   * @async
   * @param {string} url - API endpoint.
   * @param {object} payload - Response
   * @returns {object} Response
   * @throws {RequestError}
   */
  async patch(url, payload) {
    const response = await raiseForStatus(this.session.patch(url, payload));
    return response;
  }

  /**
   * Do a PUT request to the API.
   * @async
   * @param {string} url - API endpoint.
   * @param {string} id - UUID4 representing the resource Id.
   * @param {object} payload - JSON request payload.
   * @throws {RequestError}
   */
  async put(url, id, payload) {
    const composedUrl = `${url}${id}/`;
    const response = await raiseForStatus(this.session.put(composedUrl, payload));
    return response;
  }

  /**
   * Do a DELETE request to the API.
   * @async
   * @param {stroing} url - API endpoint.
   * @param {string} id - UUID4 representing the resource Id.
   * @returns {boolean}
   * @throws {RequestError}
   */
  async delete(url, id) {
    const composedUrl = `${url}${id}/`;
    try {
      await raiseForStatus(this.session.delete(composedUrl));
    } catch (error) {
      return false;
    }
    return true;
  }
}

export default APISession;