UNPKG

loopback-connector-rest

Version:
210 lines (191 loc) 4.96 kB
// Copyright IBM Corp. 2013,2019. All Rights Reserved. // Node module: loopback-connector-rest // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT 'use strict'; const debug = require('debug')('loopback:connector:rest'); const request = require('postman-request'); const g = require('strong-globalize')(); /** * @class RestConnector */ module.exports = RestResource; /** * @constructor * Build a REST resource client for CRUD operations * @param {string} pluralModelName The model name * @param {string} baseUrl The base URL * @param {function} [requestFunc] Custom request with defaults * @returns {RestResource} */ function RestResource(pluralModelName, baseUrl, requestFunc) { if (!this instanceof RestResource) { return new RestResource(pluralModelName, baseUrl, requestFunc); } if (baseUrl.charAt(baseUrl.length - 1) === '/') { this._url = baseUrl + pluralModelName; } else { this._url = baseUrl + '/' + pluralModelName; } this.request = requestFunc || request; } RestResource.prototype._req = function(obj) { if (debug.enabled) { debug('Request: %j', obj); } return obj; }; /** * @private * Wrap the callback so that it takes (err, result, response) * @param {function} cb The callback function * @returns {*} */ function wrap(cb) { let callback = cb; if (cb) { callback = function(err, response, body) { // FIXME: [rfeng] We need to have a better error mapping if (!err && response && response.statusCode >= 400) { const errObj = new Error(); errObj.message = 'HTTP code: ' + response.statusCode; errObj.statusCode = response.statusCode; if (body) { errObj.body = body; } if (response.headers) { errObj.headers = response.headers; } return cb(errObj, null, response); } else { return cb(err, body, response); } }; } return callback; } /** * Map the create operation to HTTP POST /{model} * @param {object} obj The HTTP body * @param {function} [cb] The callback function */ RestResource.prototype.create = function(obj, cb) { this._request( this._req({ method: 'POST', uri: this._url, json: true, body: obj, }), wrap(cb), ); }; /** * Map the update operation to POST /{model}/{id} * @param {*} id The id value * @param {object} obj The HTTP body * @param {function} [cb] The callback function */ RestResource.prototype.update = function(id, obj, cb) { this._request( this._req({ method: 'PUT', uri: this._url + '/' + id, json: true, body: obj, }), wrap(cb), ); }; /** * Map the delete operation to POST /{model}/{id} * @param {*} id The id value * @param {function} [cb] The callback function */ RestResource.prototype.delete = function(id, cb) { this._request( this._req({ method: 'DELETE', uri: this._url + '/' + id, json: true, }), wrap(cb), ); }; /** * Map the delete operation to POST /{model} * @param {*} id The id value * @param {function} [cb] The callback function */ RestResource.prototype.deleteAll = function(cb) { this._request( this._req({ method: 'DELETE', uri: this._url, json: true, }), wrap(cb), ); }; /** * Map the find operation to GET /{model}/{id} * @param {*} id The id value * @param {function} [cb] The callback function */ RestResource.prototype.find = function(id, cb) { this._request( this._req({ method: 'GET', uri: this._url + '/' + id, json: true, }), wrap(cb), ); }; /** * Map the all/query operation to GET /{model} * @param {object} q query string * @param {function} [cb] callback with (err, results) */ RestResource.prototype.all = RestResource.prototype.query = function(q, cb) { q = q || {}; if (!cb && typeof q === 'function') { cb = q; q = {}; } this._request( this._req({ method: 'GET', uri: this._url, json: true, qs: q, }), wrap(cb), ); }; const RequestBuilder = require('./rest-builder'); const requestFunc = this.request; RestResource.prototype._request = RequestBuilder.prototype._request; function defineFunctions() { const spec = require('./rest-crud.json'); const functions = {}; spec.operations.forEach(function(op) { if (!op.template) { throw new Error(g.f('The operation template is missing: %j', op)); } const builder = RequestBuilder.compile(op.template, requestFunc); builder.debug(spec.debug); // Bind all the functions to the template const functions = op.functions; if (functions) { for (const f in functions) { if (spec.debug) { g.log('Mixing in method: %s %s', f, functions[f]); } const fn = builder.operation(functions[f]); functions[f] = fn; } } }); return functions; }