UNPKG

lazo-next

Version:

A client-server web framework built on Node.js that allows front-end developers to easily create a 100% SEO compliant, component MVC structured web application with an optimized first page load.

284 lines (244 loc) 10.9 kB
define(['base', 'request', 'underscore', 'resolver/model'], function (Base, request, _, helpers) { /** * A utility class that can be used to call service endpoints. * @class ServiceProxy */ var ServiceProxy = Base.extend({ /** * Constructor used internally. * @constructor * @private */ constructor: function (ctx) { this.ctx = ctx; }, /** * Use to send a GET request to a service * @method get * @param {String} svc The url for a given service endpoint * @param {Object} options * @param {Object} [options.params] A hash containing name-value pairs used in url substitution. * @param {Function} options.success Callback function to be called when fetch succeeds, passed <code>(response)</code> as argument. * @param {Function} options.error Callback function to be called when fetch fails, passed <code>(response)</code> as argument. * @param {Object} [options.headers] A hash containing name-value pairs of headers to be sent to the service. * @param {Boolean} [options.raw] A boolean that if set to true will return a unparsed response. * @async */ get: function (svc, options) { options = options || {}; options.raw = !!options.raw; if (!options.success) { throw new Error('Success callback undefined for service call svc: ' + svc); } var error = options.error; options.error = function (err) { if (error) { error(err); } }; // use the backbone verbs return this.sync( 'read', { name: svc, url: svc, params: options.params, ctx: this.ctx }, options); }, /** * Used to send a POST request to a service * @method post * @param {String} svc The url for a given service endpoint * @param {Object} attributes A hash containing name-value pairs used to be sent as the payload to the server * @param {Object} options * @param {Object} [options.params] A hash containing name-value pairs used in url substitution. * @param {Function} options.success Callback function to be called when fetch succeeds, passed <code>(response)</code> as argument. * @param {Function} options.error Callback function to be called when fetch fails, passed <code>(response)</code> as argument. * @async */ post: function (svc, attributes, options) { options = options || {}; if (!options.success) { throw new Error('Success callback undefined for service call svc: ' + svc); } var error = options.error; options.error = function (err) { if (error) { error(err); } }; // use the backbone verbs return this.sync( 'create', { name: svc, url: svc, params: options.params, ctx: this.ctx, toJSON: function () { return attributes; } }, options); }, /** * Used to send a PUT request to a service * @method put * @param {String} svc The url for a given service endpoint * @param {Object} attributes A hash containing name-value pairs used to be sent as the payload to the server * @param {Object} options * @param {Object} [options.params] A hash containing name-value pairs used in url substitution. * @param {Function} options.success Callback function to be called when fetch succeeds, passed <code>(response)</code> as argument. * @param {Function} options.error Callback function to be called when fetch fails, passed <code>(response)</code> as argument. * @async */ put: function (svc, attributes, options) { options = options || {}; if (!options.success) { throw new Error('Success callback undefined for service call svc: ' + svc); } var error = options.error; options.error = function (err) { if (error) { error(err); } }; // use the backbone verbs return this.sync( 'update', { name: svc, url: svc, params: options.params, ctx: this.ctx, toJSON: function () { return attributes; } }, options); }, /** * Used to send a DELETE request to a service * @method destroy * @param {String} svc The url for a given service endpoint * @param {Object} attributes A hash containing name-value pairs used to be sent as the payload to the server * @param {Object} options * @param {Object} [options.params] A hash containing name-value pairs used in url substitution. * @param {Function} options.success Callback function to be called when fetch succeeds, passed <code>(response)</code> as argument. * @param {Function} options.error Callback function to be called when fetch fails, passed <code>(response)</code> as argument. * @async */ destroy: function (svc, attributes, options) { options = options || {}; if (!options.success) { throw new Error('Success callback undefined for service call svc: ' + svc); } var error = options.error; options.error = function (err) { if (error) { error(err); } }; // use the backbone verbs return this.sync( 'delete', { name: svc, url: svc, params: options.params, ctx: this.ctx }, options); }, sync: function (method, model, options) { options = _(options).clone(); method = helpers.methodMap[method]; // convert backbone verbs to http LAZO.logger.debug('[server.serviceProxy.sync] Begin %s request', method, model.url); var errorWrapper = function (err, requestOptions, response, body) { var uri = requestOptions && requestOptions.uri ? requestOptions.uri : 'uri not defined'; LAZO.logger.debug('[server.serviceProxy.sync] %s request failed', method, uri, err); var resp = { statusCode: (response && response.statusCode ? response.statusCode : 500), headers: (response && response.headers ? response.headers : {}), body: body }; response = response || {}; if (err instanceof Error) { resp.error = err.message; } else { resp.error = err; } options.error(resp); }; try { if (model._default) { throw new Error('No model defined in repo for ' + model.name); } // throw error if no url if (!model.urlRoot && !model.url) { throw new Error('Error: ServiceProxy failed. No url or urlRoot for ' + model.name + ' type: ' + method); } // setup options for request call var requestOptions = { method: method, headers: ServiceProxy._mergeHeaders(options.headers), timeout: options.timeout || 30000 }; if (method === 'POST' || method === 'PUT' || method === 'PATCH') { requestOptions.body = model.toJSON(options); } requestOptions.uri = helpers.substitute(_.result(model, 'url'), model.params); // make the request return request(requestOptions, function (error, response, body) { if (error) { return errorWrapper(error, requestOptions, response, body); } else if (response && (response.statusCode && response.statusCode >= 200 && response.statusCode < 300 || response.statusCode === 304)) { LAZO.logger.debug('[server.serviceProxy.sync] %s request succeed', method, model.url); var data = body || ''; if (!options.raw) { data = JSON.parse(body); } if (response.headers) { model.responseHeaders = response.headers; } if (options.success) { return options.success(data); } } else { return errorWrapper(new Error(response.statusCode), requestOptions, response, body); } } ); } catch (e) { errorWrapper(e, requestOptions, {}, ''); } } }, { _includedHeaders: [], _mergeHeaders: function (headers) { var mapped = _.map(this._includedHeaders, function (header) { return typeof header === 'function' ? header() : header; }); var result = _.extend({}, headers); _.each(mapped, function (header) { _.defaults(result, header); }); return result; }, /** * Include custom headers for every outgoing request. * @method includeHeaders * @param headers {Object|Function} an object containing the custom headers or a function that returns them when evaluated. */ includeHeaders: function (headers) { this._includedHeaders.push(headers); } }); return ServiceProxy; });