Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fetchr

Package Overview
Dependencies
Maintainers
5
Versions
95
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fetchr - npm Package Compare versions

Comparing version 0.5.17 to 0.5.18

5

docs/fetchr.md

@@ -74,4 +74,9 @@ # Fetchr API

### getServiceMeta()
Returns metadata for all service calls in an array format.
The 0 index will be the first service call.
### updateOptions(options)
Update the options of the fetchr instance.

116

libs/fetcher.client.js

@@ -27,2 +27,3 @@ /**

var defaultConstructGetUri = require('./util/defaultConstructGetUri');
var Promise = global.Promise || require('es6-promise').Promise;

@@ -59,2 +60,5 @@ function parseResponse(response) {

* @param {Object} options configuration options for Request
* @param {Array} [options._serviceMeta] Array to hold per-request/session metadata from all service calls.
* Data will be pushed on to this array while the Fetchr instance maintains the reference for this session.
*
* @constructor

@@ -74,3 +78,4 @@ */

context: options.context || {},
contextPicker: options.contextPicker || {}
contextPicker: options.contextPicker || {},
_serviceMeta: options._serviceMeta || []
};

@@ -127,4 +132,35 @@ this._params = {};

Request.prototype.end = function (callback) {
var clientConfig = this._clientConfig;
var callback = callback || lodash.noop;
var self = this;
var promise = new Promise(function (resolve, reject) {
debug('Executing request %s.%s with params %o and body %o', self.resource, self.operation, self._params, self._body);
setImmediate(executeRequest, self, resolve, reject);
});
promise.then(function (result) {
if (result.meta) {
self.options._serviceMeta.push(result.meta)
};
return result;
});
if (callback) {
promise.then(function (result) {
setImmediate(callback, null, result.data, result.meta);
}, function (err) {
setImmediate(callback, err);
});
} else {
return promise;
}
};
/**
* Execute and resolve/reject this fetcher request
* @method executeRequest
* @param {Object} request Request instance object
* @param {Function} resolve function to call when request fulfilled
* @param {Function} reject function to call when request rejected
*/
function executeRequest (request, resolve, reject) {
var clientConfig = request._clientConfig;
var use_post;

@@ -138,6 +174,6 @@ var allow_retry_post;

if (!uri) {
uri = clientConfig.cors ? this.options.corsPath : this.options.xhrPath;
uri = clientConfig.cors ? request.options.corsPath : request.options.xhrPath;
}
use_post = this.operation !== OP_READ || clientConfig.post_for_read;
use_post = request.operation !== OP_READ || clientConfig.post_for_read;
// We use GET request by default for READ operation, but you can override that behavior

@@ -147,3 +183,3 @@ // by specifying {post_for_read: true} in your request's clientConfig

var getUriFn = lodash.isFunction(clientConfig.constructGetUri) ? clientConfig.constructGetUri : defaultConstructGetUri;
var get_uri = getUriFn.call(this, uri, this.resource, this._params, clientConfig, pickContext(this.options.context, this.options.contextPicker, 'GET'));
var get_uri = getUriFn.call(request, uri, request.resource, request._params, clientConfig, pickContext(request.options.context, request.options.contextPicker, 'GET'));
/* istanbul ignore next */

@@ -153,4 +189,17 @@ if (!get_uri) {

// TODO: Add test for this fallback
get_uri = defaultConstructGetUri.call(this, uri, this.resource, this._params, clientConfig, this.options.context);
get_uri = defaultConstructGetUri.call(request, uri, request.resource, request._params, clientConfig, request.options.context);
}
// TODO: Remove `returnMeta` feature flag after next release
// This feature flag will enable the new return format for GET api requests
// Whereas before any data from services was returned as is. We now return
// an object with a data key containing the service response, and a meta key
// containing the service's metadata response (i.e headers and statusCode).
// We need this feature flag to be truly backwards compatible because it is
// concievable that some active browser sessions could have the old version of
// client fetcher while the server upgrades to the new version. This could be
// easily fixed by refreshing the browser, but the feature flag will ensure
// old fetcher clients will receive the old format and the new client will
// receive the new format
get_uri += (get_uri.indexOf('?') !== -1) ? '&' : '?';
get_uri += 'returnMeta=true';
if (get_uri.length <= MAX_URI_LEN) {

@@ -164,8 +213,8 @@ uri = get_uri;

if (!use_post) {
return REST.get(uri, {}, lodash.merge({xhrTimeout: this.options.xhrTimeout}, clientConfig), function getDone(err, response) {
return REST.get(uri, {}, lodash.merge({xhrTimeout: request.options.xhrTimeout}, clientConfig), function getDone(err, response) {
if (err) {
debug('Syncing ' + this.resource + ' failed: statusCode=' + err.statusCode, 'info');
return callback(err);
debug('Syncing ' + request.resource + ' failed: statusCode=' + err.statusCode, 'info');
return reject(err);
}
callback(null, parseResponse(response));
resolve(parseResponse(response));
});

@@ -177,19 +226,19 @@ }

requests[DEFAULT_GUID] = {
resource: this.resource,
operation: this.operation,
params: this._params
resource: request.resource,
operation: request.operation,
params: request._params
};
if (this._body) {
requests[DEFAULT_GUID].body = this._body;
if (request._body) {
requests[DEFAULT_GUID].body = request._body;
}
data = {
requests: requests,
context: this.options.context
context: request.options.context
}; // TODO: remove. leave here for now for backward compatibility
uri = this._constructGroupUri(uri);
allow_retry_post = (this.operation === OP_READ);
REST.post(uri, {}, data, lodash.merge({unsafeAllowRetry: allow_retry_post, xhrTimeout: this.options.xhrTimeout}, clientConfig), function postDone(err, response) {
uri = request._constructGroupUri(uri);
allow_retry_post = (request.operation === OP_READ);
REST.post(uri, {}, data, lodash.merge({unsafeAllowRetry: allow_retry_post, xhrTimeout: request.options.xhrTimeout}, clientConfig), function postDone(err, response) {
if (err) {
debug('Syncing ' + this.resource + ' failed: statusCode=' + err.statusCode, 'info');
return callback(err);
debug('Syncing ' + request.resource + ' failed: statusCode=' + err.statusCode, 'info');
return reject(err);
}

@@ -202,3 +251,3 @@ var result = parseResponse(response);

}
callback(null, result.data);
resolve(result);
});

@@ -242,3 +291,11 @@ };

function Fetcher (options) {
this.options = options || {};
this._serviceMeta = [];
this.options = {
xhrPath: options.xhrPath,
xhrTimeout: options.xhrTimeout,
corsPath: options.corsPath,
context: options.context,
contextPicker: options.contextPicker,
_serviceMeta: this._serviceMeta
};
}

@@ -365,2 +422,13 @@

this.options = lodash.merge(this.options, options);
},
/**
* get the serviceMeta array.
* The array contains all xhr meta returned in this session
* with the 0 index being the first call.
* @method getServiceMeta
* @return {Array} array of metadata returned by each service call
*/
getServiceMeta: function () {
return this._serviceMeta;
}

@@ -367,0 +435,0 @@ };

@@ -14,2 +14,3 @@ /**

var objectAssign = require('object-assign');
var Promise = global.Promise || require('es6-promise').Promise;

@@ -75,2 +76,3 @@ function parseValue(value) {

* @param {Object} [options.req] The request object from express/connect. It can contain per-request/context data.
* @param {Array} [options.serviceMeta] Array to hold per-request/session metadata from all service calls.
* @constructor

@@ -87,2 +89,3 @@ */

this.req = options.req || {};
this.serviceMeta = options.serviceMeta || [];
this._params = {};

@@ -127,2 +130,3 @@ this._body = null;

};
/**

@@ -135,11 +139,51 @@ * Execute this fetcher request and call callback.

Request.prototype.end = function (callback) {
var args = [this.req, this.resource, this._params, this._clientConfig, callback];
var op = this.operation;
var self = this;
var promise = new Promise(function (resolve, reject) {
executeRequest(self, resolve, reject);
setImmediate(executeRequest, self, resolve, reject);
});
promise.then(function (result) {
if (result.meta) {
self.serviceMeta.push(result.meta)
};
return result;
});
if (callback) {
promise.then(function (result) {
setImmediate(callback, null, result.data, result.meta);
}, function (err) {
setImmediate(callback, err);
});
} else {
return promise;
}
};
/**
* Execute and resolve/reject this fetcher request
* @method executeRequest
* @param {Object} request Request instance object
* @param {Function} resolve function to call when request fulfilled
* @param {Function} reject function to call when request rejected
*/
function executeRequest (request, resolve, reject) {
var args = [request.req, request.resource, request._params, request._clientConfig, function executeRequestCallback(err, data, meta) {
if (err) {
reject(err);
} else {
resolve({
data: data,
meta: meta
});
}
}];
var op = request.operation;
if ((op === OP_CREATE) || (op === OP_UPDATE)) {
args.splice(3, 0, this._body);
args.splice(3, 0, request._body);
}
var service = Fetcher.getService(this.resource);
var service = Fetcher.getService(request.resource);
service[op].apply(service, args);
};
}

@@ -159,2 +203,4 @@ /**

this.req = this.options.req || {};
this.serviceMeta = [];
}

@@ -264,7 +310,11 @@

}
request = new Request(OP_READ, resource, {req: req});
var serviceMeta = [];
request = new Request(OP_READ, resource, {
req: req,
serviceMeta: serviceMeta
});
request
.params(parseParamValues(qs.parse(path.join('&'))))
.end(function (err, data, meta) {
meta = meta || {};
.end(function (err, data) {
var meta = serviceMeta[0] || {};
if (meta.headers) {

@@ -278,3 +328,11 @@ res.set(meta.headers);

}
res.status(meta.statusCode || 200).json(data);
if (req.query.returnMeta) {
res.status(meta.statusCode || 200).json({
data: data,
meta: meta
});
} else {
// TODO: Remove `returnMeta` feature flag after next release
res.status(meta.statusCode || 200).json(data);
}
});

@@ -302,9 +360,12 @@ } else {

}
request = new Request(singleRequest.operation, singleRequest.resource, {req: req});
var serviceMeta = [];
request = new Request(singleRequest.operation, singleRequest.resource, {
req: req,
serviceMeta: serviceMeta
});
request
.params(singleRequest.params)
.body(singleRequest.body || {})
.end(function(err, data, meta) {
meta = meta || {};
.end(function(err, data) {
var meta = serviceMeta[0] || {};
if (meta.headers) {

@@ -319,3 +380,6 @@ res.set(meta.headers);

var responseObj = {};
responseObj[DEFAULT_GUID] = {data: data};
responseObj[DEFAULT_GUID] = {
data: data,
meta: meta
};
res.status(meta.statusCode || 200).json(responseObj);

@@ -345,3 +409,6 @@ });

Fetcher.prototype.read = function (resource, params, config, callback) {
var request = new Request('read', resource, {req: this.req});
var request = new Request('read', resource, {
req: this.req,
serviceMeta: this.serviceMeta
});
if (1 === arguments.length) {

@@ -378,3 +445,6 @@ return request;

Fetcher.prototype.create = function (resource, params, body, config, callback) {
var request = new Request('create', resource, {req: this.req});
var request = new Request('create', resource, {
req: this.req,
serviceMeta: this.serviceMeta
});
if (1 === arguments.length) {

@@ -412,3 +482,6 @@ return request;

Fetcher.prototype.update = function (resource, params, body, config, callback) {
var request = new Request('update', resource, {req: this.req});
var request = new Request('update', resource, {
req: this.req,
serviceMeta: this.serviceMeta
});
if (1 === arguments.length) {

@@ -445,3 +518,6 @@ return request;

Fetcher.prototype['delete'] = function (resource, params, config, callback) {
var request = new Request('delete', resource, {req: this.req});
var request = new Request('delete', resource, {
req: this.req,
serviceMeta: this.serviceMeta
});
if (1 === arguments.length) {

@@ -480,2 +556,9 @@ return request;

/**
* Get all the aggregated metadata sent data services in this request
*/
Fetcher.prototype.getServiceMeta = function () {
return this.serviceMeta;
}
module.exports = Fetcher;

@@ -482,0 +565,0 @@

{
"name": "fetchr",
"version": "0.5.17",
"version": "0.5.18",
"description": "Fetchr augments Flux applications by allowing Flux stores to be used on server and client to fetch data",

@@ -25,2 +25,3 @@ "main": "index.js",

"debug": "^2.0.0",
"es6-promise": "^3.0.2",
"fumble": "^0.1.0",

@@ -41,4 +42,4 @@ "lodash": "^3.3.0",

"pre-commit": "^1.0.0",
"qs": "^3.0.0",
"request": "^2.55.0",
"qs": "^4.0.0",
"request": "^2.61.0",
"supertest": "^1.0.1"

@@ -45,0 +46,0 @@ },

@@ -152,2 +152,48 @@ # Fetchr

## Service Metadata
Service calls on the client transparently become xhr requests.
It is a good idea to set cache headers on common xhr calls.
You can do so by providing a third parameter in your service's callback.
If you want to look at what headers were set by the service you just called,
simply inspect the third parameter in the callback.
Note: If you're using promises, the metadata will be available on the `meta`
property of the resolved value.
```js
// dataService.js
module.exports = {
name: 'data_service',
read: function(req, resource, params, config, callback) {
// business logic
var data = 'response';
var meta = {
headers: {
'cache-control': 'public, max-age=3600'
},
statusCode: 200 // You can even provide a custom statusCode for the xhr response
};
callback(null, data, meta);
}
}
```
```js
fetcher
.read('data_service')
.params({id: ###})
.end(function (err, data, meta) {
// data will be 'response'
// meta will have the header and statusCode from above
});
```
There is a convenience method called `fetcher.getServiceMeta` on the fetchr instance.
This method will return the metadata for all the calls that have happened so far
in an array format.
In the server, this will include all service calls for the current request.
In the client, this will include all service calls for the current session.
## Updating Configuration

@@ -312,2 +358,3 @@

```js
var fetcher = new Fetcher({

@@ -339,2 +386,3 @@ context: { // These context values are persisted with XHR calls as query params

});
```

@@ -341,0 +389,0 @@ ## API

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc