rails-ranger.js

import Axios               from 'axios'
import { clone }           from 'lodash'
import RouteBuilder        from './route-builder'
import DataTransformations from './utils/data-transformations'

class RailsRanger {
  /**
  * RailsRanger object constructor
  * @constructor
  * @param {object} options - Configurations of the new RailsRanger instance
  * @param {boolean} options.transformData - Sets the response/request data transformations on/off
  * @param {object} options.axios - Configurations to be be handed down to Axios
  */
  constructor ({ transformData = true, axios = {} } = {}) {
    this.options = {
      transformData
    }

    const clientConfigs = {}

    if (this.options.transformData) {
      const dataTransformations = {
        transformRequest:  [DataTransformations.prepareRequest],
        transformResponse: [DataTransformations.prepareResponse]
      }
      Object.assign(clientConfigs, dataTransformations)
    }

    Object.assign(clientConfigs, axios)

    this.client       = Axios.create(clientConfigs)
    this.routeBuilder = new RouteBuilder()
  }

  /**
  * Defines a namespace to be used in the next request of the chain
  * @param {string} resource - the name of the resource to be used as namespace
  * @param {integer} id - the ID of the resource, can be left empty
  * @example
  * const api = new RailsRanger
  * api.resource('users', 1).list('blogPosts')
  * //=> GET request to '/users/1/blog_posts' path
  */
  resource (resource, id = null) {
    const newRouteBuilder = this.routeBuilder.resource(resource, id)

    return this._newInstanceWithNewRouteBuilder(newRouteBuilder)
  }

  /**
  * Defines a namespace to be used in the next request of the chain
  * @param {string} namespace - The path fragment to be used as the namespace
  * @param {object} params - The parameters to be interpolated into the path, can be left empty
  * @example
  * const api = new RailsRanger
  * api.namespace('admin/:type', { type: 'super' }).list('blogPosts')
  * //=> GET request to '/admin/super/blog_posts' path
  */
  namespace (namespace, params = {}) {
    const newRouteBuilder = this.routeBuilder.namespace(namespace, params)

    return this._newInstanceWithNewRouteBuilder(newRouteBuilder)
  }

  /**
  * Makes a GET request to the given path with the given parameters
  * @param {string} path - The base path of the request
  * @param {object} params - The parameters to be injected in the path (as query or replacing path segments)
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.get('/users/:id', { id: 1, flag: true })
  * //=> GET request to '/users/1?flag=true' path
  */
  get (path, params) {
    return this._performRawRequest({ method: 'get', path, params })
  }

  /**
  * Makes a POST request to the given path with the given parameters
  * @param {string} path - The base path of the request
  * @param {object} params - The parameters to be injected in the path or sent in the request payload
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.post('/users/:id', { id: 1, flag: true })
  * //=> POST request to '/users/1' path with { flag: true } parameters
  */
  post (path, params) {
    return this._performRawRequest({ method: 'post', path, params })
  }

  /**
  * Makes a PATCH request to the given path with the given parameters
  * @param {string} path - The base path of the request
  * @param {object} params - The parameters to be injected in the path or sent in the request payload
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.patch('/users/:id', { id: 1, flag: true })
  * //=> PATCH request to '/users/1' path with { flag: true } parameters
  */
  patch (path, params) {
    return this._performRawRequest({ method: 'patch', path, params })
  }

  /**
  * Makes a PUT request to the given path with the given parameters
  * @param {string} path - The base path of the request
  * @param {object} params - The parameters to be injected in the path or sent in the request payload
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.put('/users/:id', { id: 1, flag: true })
  * //=> PUT request to '/users/1' path with { flag: true } parameters
  */
  put (path, params) {
    return this._performRawRequest({ method: 'put', path, params })
  }

  /**
  * Makes a DELETE request to the given path with the given parameters
  * @param {string} path - The base path of the request
  * @param {object} params - The parameters to be injected in the path (as query or replacing path segments)
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.delete('/users/:id', { id: 1, flag: true })
  * //=> DELETE request to '/users/1?flag=true' path
  */
  delete (path, params) {
    return this._performRawRequest({ method: 'delete', path, params })
  }

  /**
  * Makes a GET request to the **index path** of the given resource
  * @param {string} resource - The base path of the request
  * @param {object} params - The parameters to be injected in the path as query
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.list('users', { flag: true })
  * //=> GET request to '/users?flag=true' path
  */
  list (resource, params) {
    return this._performActionRequest({ action: 'index', resource, params })
  }

  /**
  * Makes a GET request to the **show path** of the given resource
  * @param {string} resource - The base path of the request
  * @param {object} params - The parameters to be injected in the path as query
  * @param {number|string} params.id - The id of the resource
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.show('users', { id: 1, flag: true })
  * //=> GET request to '/users/1?flag=true' path
  */
  show (resource, params) {
    return this._performActionRequest({ action: 'show', resource, params })
  }

  /**
  * Makes a DELETE request to the **destroy path** of the given resource
  * @param {string} resource - The base path of the request
  * @param {object} params - The parameters to be injected in the path as query
  * @param {number|string} params.id - The id of the resource
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.destroy('users', { id: 1, flag: true })
  * //=> DELETE request to '/users/1?flag=true' path
  */
  destroy (resource, params) {
    return this._performActionRequest({ action: 'destroy', resource, params })
  }

  /**
  * Makes a POST request to the **create path** of the given resource
  * @param {string} resource - The base path of the request
  * @param {object} params - The parameters to be injected in the path as query
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.create('users', { email: 'john@doe.com', password: 123456 })
  * //=> POST request to '/users' path with the { email: 'john@doe.com', password: 123456 } parameters
  */
  create (resource, params) {
    return this._performActionRequest({ action: 'create', resource, params })
  }

  /**
  * Makes a PATCH request to the update path of the given resource
  * @param {string} resource - The base path of the request
  * @param {object} params - The parameters to be injected in the path as query
  * @param {number|string} params.id - The id of the resource
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.update('users', { id: 1, email: 'elton@doe.com' })
  * //=> PATCH request to '/users/1' path with the { email: 'elton@doe.com' } parameters
  */
  update (resource, params) {
    return this._performActionRequest({ action: 'update', resource, params })
  }

  /**
  * Makes a GET request to the new path of the given resource
  * @param {string} resource - The base path of the request
  * @param {object} params - The parameters to be injected in the path as query
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.new('users', { flag: true })
  * //=> GET request to '/users/new?flag=true' path
  */
  new (resource, params) {
    return this._performActionRequest({ action: 'new', resource, params })
  }

  /**
  * Makes a GET request to the edit path of the given resource
  * @param {string} resource - The base path of the request
  * @param {object} params - The parameters to be injected in the path as query
  * @param {number|string} params.id - The id of the resource
  * @returns {Promise}
  * @example
  * const api = new RailsRanger
  * api.edit('users', { id: 1, flag: true })
  * //=> GET request to '/users/1/edit?flag=true' path
  */
  edit (resource, params) {
    return this._performActionRequest({ action: 'edit', resource, params })
  }

  /**
   * Private functions
   */
  _performRawRequest ({ method, path, params }) {
    const request = this.routeBuilder[method](path, params)

    return this.client[method](request.path, request.params)
  }

  _performActionRequest ({ action, resource, params }) {
    const request = this.routeBuilder[action](resource, params)

    return this.client[request.method](request.path, request.params)
  }

  _newInstanceWithNewRouteBuilder (newRouteBuilder) {
    const newInstance        = clone(this)
    newInstance.routeBuilder = newRouteBuilder
    return newInstance
  }
}

export default RailsRanger