UNPKG

mir-client

Version:
264 lines (240 loc) 8.7 kB
import * as R from 'ramda' /** * @function CreateGetterFactory - Creates GetterFactory for a certain Mir API. * @param {Object} axios - Initialized Axios instance. * @param {string} resource - Resource name. * @returns {GetterFactory} - A factory function that accepts a Mir API resource name */ function CreateGetterFactory(axios, resource) { /** * @typedef GetterFactory - A Query Builder object * @type {function} * @returns {QueryBuilder} - Object that acts as a query builder for Mir requests */ const GetterFactory = () => { /** * A Query Builder object * @typedef QueryBuilder * @type {Object} * @property {Object} params - Stores parameters for the request * @property {Object} headers - Stores headers for the request * @property {Object} meta - Stores _meta from the preceding request, will be empty if no initial request has been made * @property {function} projection - Updates params object with projection configuration * @property {function} sort - Updates params object with sorting configuration * @property {function} filter - Updates params object with filter configuration * @property {function} page - Updates params object with page configuration * @property {function} limit - Updates params object with maximum result count * @property {function} next - Send a request for the next page, requires an initial request to have been made * @property {function} previous - Send a request for the previous page, requires an inital request to have been made * @property {function} first - Send a request for the first page, requires an initial to have been made * @property {function} last - Send a request for the last page, requires an initial request to have been made * @property {function} send - Send a request with the current stored headers and params configurations, resets meta object */ const QueryBuilder = { params: {}, headers: {}, meta: {}, /** * @function projection * @param {string} attribute - Name of the resource attribute to apply projection * @param {boolean} bool - Indicates whether the attribute should appear in the results * @this QueryBuilder */ projection(attribute, bool) { if(typeof bool != 'boolean') { var err = new Error() err.message = 'Bool param only accepts values of true or false.' err.code = 400 throw err } const value = bool ? 1 : 0 this.params.projection = R.merge(this.params, R.objOf(attribute, value)) return this }, /** * @function sort * @param {string} attribute - Name of the resource attribute to sort by * @param {string} value - Either '+' or '-' for ascending or descending * @this QueryBuilder */ sort(attribute, value) { if(!((value == '+') || (value == '-'))) { var err = new Error() err.message = 'Value param only accepts values of \'+\' or \'-\'.' err.code = 400 throw err } this.params.sort = this.params.sort ? `${this.params.sort},${value}${attribute}` : `${value}${attribute}` return this }, /** * @function filter * @param {Object} filter_obj - A mongodb query object. * @see {@link http://python-eve.org/features.html#filtering|Eve Filtering} * @see {@link https://docs.mongodb.com/v3.2/reference/operator/query/|MongoDB Queries} * @this QueryBuilder */ filter(filter_obj) { // Query object examples: // // _id where greater than 'a' // ---------------------------------- // { // _id: { // $gt: 'a' // } // } // // where quantity less than 20 or price is 10 // ---------------------------------- // { // $or: [ // { // quantity: { $lt: 20 } // }, // { // price: 10 // } // ] // } // ---------------------------------- if(typeof filter_obj != 'object') { var err = new Error() err.message = 'Parameter filter_obj must be a JSON object.' err.code = 400 throw err } this.params.where = filter_obj return this }, /** * * @function page * @param {number} value - A page number * @this QueryBuilder */ page(value) { this.params.page = value return this }, /** * Set the current limit value * @function limit * @param {number} value - Maximum number of results to return in the request * @this QueryBuilder */ limit(value) { this.params.max_results = value return this }, /** * Send a GET request for the next page of results * @function next * @this QueryBuilder * @returns {Promise} - Axios promise */ next() { if(!this.meta.page) { var err = new Error() err.message = 'Next requires an initial request to have been made.' err.code = 400 throw err } // Ensure page is not the last var lastPage = this.meta.total != 0 ? this.meta.max_results * this.meta.page >= this.meta.total : true if(lastPage) { var err = new Error() err.message = 'Next cannot be called on last page of results.' err.code = 400 throw err } // Increment this.params.page and this.send() again, returning promise this.params.page += 1 return this.send() }, /** * @function previous - Send GET for the previous page of results * @this QueryBuilder * @returns {Promise} - Axios promise */ previous() { if(!this.meta.page) { var err = new Error() err.message = 'Previous requires an initial request to have been made.' err.code = 400 throw err } // Ensure page is not the first if(this.params.page == 1) { var err = new Error() err.message = 'Current page of 1 has no previous page.' err.code = 400 throw err } // Decrement this.params.page and this.send() again, returning promise this.params.page -= 1 return this.send() }, /** * @function first - Send GET for the first page of results * @this QueryBuilder * @returns {Promise} - Axios promise */ first() { if(!this.meta.page) { var err = new Error() err.message = 'First requires an initial request to have been made.' err.code = 400 throw err } // Reset this.params.page to 1 and this.send() again, returning promise this.params.page = 1 return this.send() }, /** * @function last - Send GET for the last page of results * @this QueryBuilder * @returns {Promise} - Axios promise */ last() { if(!this.meta.page) { var err = new Error() err.message = 'Last requires an initial request to have been made.' err.code = 400 throw err } // Ensure page is not the last var lastPage = this.meta.total != 0 ? this.meta.max_results * this.meta.page >= this.meta.total : true // Increment this.params.page and this.send() again, returning promise var totalPages = this.meta.total == 0 ? 1 : Math.ceil(this.meta.total / this.meta.max_results) this.params.page = totalPages return this.send() }, /** * @function send - Send GET with currently defined params and headers * @param {function} hook - TODO: Not implemented, will just contain a function to alter returned results * @this QueryBuilder * @returns {Promise} - Axios promise */ send(hook) { const params = this.params const headers = this.headers return axios.get(`/${resource}`, { params, headers, }).then((result) => { this.meta = result.data._meta if(!this.params.page) { this.params.page = this.meta.page // set this.params.page with default page size if not specified in request } return result }) } } return QueryBuilder } return GetterFactory } export default CreateGetterFactory