Socket
Socket
Sign inDemoInstall

rest

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rest - npm Package Compare versions

Comparing version 1.2.0 to 1.3.0

interceptor/template.js

2

bower.json
{
"name": "rest",
"version": "1.2.0",
"version": "1.3.0",
"main": "./browser.js",

@@ -5,0 +5,0 @@ "moduleType": ["amd", "node"],

@@ -82,3 +82,3 @@ /*

return client(function jsonp(request) {
return new responsePromise.ResponsePromise(function (resolve, reject) {
return responsePromise.promise(function (resolve, reject) {

@@ -85,0 +85,0 @@ var callbackName, callbackParams, script, firstScript, response;

@@ -62,3 +62,3 @@ /*

/*jshint maxcomplexity:20 */
return new responsePromise.ResponsePromise(function (resolve, reject) {
return responsePromise.promise(function (resolve, reject) {

@@ -65,0 +65,0 @@ var options, clientRequest, client, url, headers, entity, response;

@@ -21,3 +21,3 @@ /*

return client(function xdr(request) {
return new responsePromise.ResponsePromise(function (resolve, reject) {
return responsePromise.promise(function (resolve, reject) {

@@ -24,0 +24,0 @@ var client, method, url, entity, response;

@@ -54,4 +54,21 @@ /*

function safeMixin(target, source) {
Object.keys(source || {}).forEach(function (prop) {
// make sure the property already exists as
// IE 6 will blow up if we add a new prop
if (source.hasOwnProperty(prop) && prop in target) {
try {
target[prop] = source[prop];
}
catch (e) {
// ignore, expected for some properties at some points in the request lifecycle
}
}
});
return target;
}
return client(function xhr(request) {
return new responsePromise.ResponsePromise(function (resolve, reject) {
return responsePromise.promise(function (resolve, reject) {
/*jshint maxcomplexity:20 */

@@ -83,14 +100,8 @@

client = response.raw = new XMLHttpRequest();
// mixin extra request properties before and after opening the request as some properties require being set at different phases of the request
safeMixin(client, request.mixin);
client.open(method, url, true);
safeMixin(client, request.mixin);
if (request.mixin) {
Object.keys(request.mixin).forEach(function (prop) {
// make sure the property already exists as
// IE 6 will blow up if we add a new prop
if (request.mixin.hasOwnProperty(prop) && prop in client) {
client[prop] = request.mixin[prop];
}
});
}
headers = request.headers;

@@ -97,0 +108,0 @@ for (headerName in headers) {

@@ -75,3 +75,3 @@ _Thanks for your interest in rest.js. Have something you'd like to contribute?

/*
* Copyright 2013 the original author or authors
* Copyright 2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -214,4 +214,4 @@ *

The Spring team takes a very conservative approach to accepting contributions to
the framework. This is to keep code quality and stability as high as possible,
The cujoJS team takes a very conservative approach to accepting contributions to
the library. This is to keep code quality and stability as high as possible,
and to keep complexity at a minimum. Your changes, if accepted, may be heavily

@@ -218,0 +218,0 @@ modified prior to merging. You will retain "Author:" attribution for your Git

# Interceptors
- [Incerceptor Principals](#interceptor-principals)
- [Interceptor Principals](#interceptor-principals)
- [Provided Interceptors](#interceptor-provided)

@@ -11,2 +11,3 @@ - [Common Interceptors](#interceptor-provided-common)

- [Path Prefix Interceptor](#module-rest/interceptor/pathPrefix)
- [Template Interceptor](#module-rest/interceptor/template)
- [Authentication Interceptors](#interceptor-provided-auth)

@@ -34,3 +35,3 @@ - [Basic Auth Interceptor](#module-rest/interceptor/basicAuth)

- [Cancellation](#interceptor-custom-concepts-cancellation)
- [Sharred Request/Response Context](#interceptor-custom-concepts-context)
- [Shared Request/Response Context](#interceptor-custom-concepts-context)
- [Async Request/Response](#interceptor-custom-concepts-async)

@@ -42,3 +43,3 @@ - [Override Parent Client (ComplexRequest)](#interceptor-custom-concepts-parent)

<a name="interceptor-principals"></a>
## Incerceptor Principals
## Interceptor Principals

@@ -362,2 +363,8 @@ rest.js distinguishes itself from other HTTP client libraries by providing a minimal core that can be wrapped by more advanced behavior. These configured clients can then be consumed by our application. If a portion of our application needs more advanced behavior, it can continue to wrap the client without impacting other portions of the application. Functional programming FTW.

</tr>
<tr>
<td>permissive</td>
<td>optional</td>
td><em>false</em></td>
<td>allow an unknown mime type for a request</td>
</tr>
</table>

@@ -376,3 +383,3 @@

```javascript
client = rest.wrap(mime, { mime: 'application/json' );
client = rest.wrap(mime, { mime: 'application/json' });
client({ method: 'POST', entity: { key: 'value' } }).then(function (response) {

@@ -423,2 +430,49 @@ assert.same('{ "key": "value" }', response.request.entity);

<a name="module-rest/interceptor/template"></a>
#### Template Interceptor
`rest/interceptor/template` ([src](../interceptor/template.js))
The template interceptor fully defines the request URI by expending the path as a URI Template with the request params. Params defined by the tempalte that are missing as well as additional params are ignored. After the template interceptor, the request.params are removed from the request object, as the URI is fully defined.
Note: primitive templating is provided by `rest/UrlBuilder`, however, its behavior is non-standard and less powerful.
**Phases**
- request
**Configuration**
<table>
<tr>
<th>Property</th>
<th>Required?</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>template</td>
<td>optional</td>
<td><em>empty string</em></td>
<td>default template if request.path is undefined</td>
</tr>
<tr>
<td>params</td>
<td>optional</td>
<td><em>empty object</em></td>
<td>default params to be combined with request.params</td>
</tr>
</table>
**Example**
```javascript
client = rest.wrap(template, { params: { lang: 'en-us' } });
client({ path: '/dictionary{/term:1,term}{?lang}', params: { term: 'hypermedia' } }).then(function (response) {
assert.same('/dictionary/h/hypermedia?lang=en-us', response.request.path);
assert.same(undefined, response.request.params);
});
```
<a name="interceptor-provided-auth"></a>

@@ -743,3 +797,3 @@ ### Authentication Interceptors

Rejects a request that takes longer than the timeout. If a request is in-flight, it is canceled. The timeout value may be specified in the request or the interceptor config.
Rejects a request that takes longer than the timeout. If a request is in-flight, it is canceled by default. The timeout value may be specified in the request or the interceptor config.

@@ -766,2 +820,8 @@ **Phases**

</tr>
<tr>
<td>transient</td>
<td>optional</td>
<td>false</td>
<td>disables the cancellation of timed out requests, allowing additional interceptors to gracefully handle the timeout.</td>
</tr>
</table>

@@ -768,0 +828,0 @@

@@ -175,4 +175,10 @@ # Common Interfaces

<td>Promise&lt;Header&gt;</td>
<td>Promise for a specific HTTP response headers. A Header may be a string or an array of strings depending on the number of the header name occurances in the response</td>
<td>Promise for a specific HTTP response headers. A Header may be a string or an array of strings depending on the number of the header name occurrences in the response</td>
</tr>
<tr>
<td>follow</td>
<td>relationship</td>
<td>ResponsePromise&lt;Response&gt;</td>
<td>Traverse to the resource identified by the relationship. An array of relationships will traverse deep into the API following each relationship in turn. Params for templated URIs can be express in the form `{ rel: relationship, params: { ... } }`</td>
</tr>
</table>

@@ -179,0 +185,0 @@

@@ -31,3 +31,3 @@ # Content Negotiation

Serveral common MIME types are supported out of the box including:
Several common MIME types are supported out of the box including:

@@ -42,3 +42,3 @@ - text/plain

* Multipart support is only available for requests from browsers that has implemented XMLHttpRequest 2. This includes most modern browsers with the exception of IE <=9.
* Multipart support is only available for requests from browsers that have implemented XMLHttpRequest 2. This includes most modern browsers with the exception of IE <=9.

@@ -50,3 +50,3 @@ <a name="mime-converters-custom"></a>

The `read` and `write` methods may additionaly accept an `opts` argument. Common opts include the `request` or `response`, and a `client` to make further requests if needed. Either a raw value or a promise may be returned.
The `read` and `write` methods may additionally accept an `opts` argument. Common opts include the `request` or `response`, parsed `mime` type, and a `client` to make further requests if needed. Either a raw value or a promise may be returned.

@@ -88,2 +88,19 @@ ```javascript

<a name="custom-json-converters"></a>
## Custom JSON Converters
Advanced JSON serialization often leverages custom revivers and replacers. The JSON converter can be extended to use custom revivers and replacers.
```javascript
var json = require('rest/mime/type/application/json');
var customJson = json.extend(reviver, replacer);
```
In order to use the extended converter, it must be [registered in a MIME registry](#mime-converters-custom). It's generally recommended to use a custom vendored MIME type when overriding a common converter. If that is not possible, using a child registry is highly recommended to avoid conflicts.
```javascript
registry.register('application/json', customJson);
```
<a name="mime-interceptor"></a>

@@ -94,2 +111,2 @@ ## MIME Interceptor

The [MIME interceptor](interceptors.md#module-rest/interceptor/mime) utilizes the mime registry to convert request and response entities between objects and text strings.
The [MIME interceptor](interceptors.md#module-rest/interceptor/mime) utilizes the mime registry to convert request and response entities between objects and text strings. A custom registry

@@ -80,1 +80,13 @@ # wire.js

```
The 'config' object for an interceptor may also use any wire.js facility. If a literal config object is desired, but is being wired in an undesirable way, use the 'literal' wire.js factory to provide the literal config.
```javascript
client: {
rest: [
{ $ref: 'myInterceptor', config: { literal: { module: 'not/a/wire/module/factory' } } },
]
},
myInterceptor: { ... },
$plugins: [{ module: 'rest/wire' }]
```
/*
* Copyright 2012-2014 the original author or authors
* Copyright 2012-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -112,3 +112,3 @@ *

config = initHandler(Object.create(config || {}));
config = initHandler(config || {});

@@ -121,3 +121,3 @@ function interceptedClient(request) {

request.originator = request.originator || interceptedClient;
return responsePromise(when(
return responsePromise(
requestHandler.call(context, request, config, meta),

@@ -151,3 +151,3 @@ function (request) {

}
));
);
}

@@ -154,0 +154,0 @@

@@ -13,9 +13,13 @@ /*

var interceptor, registry, plainText, when;
var interceptor, mime, registry, noopConverter, when;
interceptor = require('../interceptor');
mime = require('../mime');
registry = require('../mime/registry');
when = require('when');
plainText = registry.lookup('text/plain');
noopConverter = {
read: function (obj) { return obj; },
write: function (obj) { return obj; }
};

@@ -36,5 +40,6 @@ /**

* @param {Client} [config.client=<request.originator>] client passed to the
* serializer, defaults to the client originating the request
* converter, defaults to the client originating the request
* @param {Registry} [config.registry] MIME registry, defaults to the root
* registry
* @param {boolean} [config.permissive] Allow an unkown request MIME type
*

@@ -49,7 +54,7 @@ * @returns {Client}

request: function (request, config) {
var mime, headers;
var type, headers;
headers = request.headers || (request.headers = {});
mime = headers['Content-Type'] = headers['Content-Type'] || config.mime || 'text/plain';
headers.Accept = headers.Accept || config.accept || mime + ', application/json;q=0.8, text/plain;q=0.5, */*;q=0.2';
type = mime.parse(headers['Content-Type'] = headers['Content-Type'] || config.mime || 'text/plain');
headers.Accept = headers.Accept || config.accept || type.raw + ', application/json;q=0.8, text/plain;q=0.5, */*;q=0.2';

@@ -60,6 +65,12 @@ if (!('entity' in request)) {

return config.registry.lookup(mime).then(function (serializer) {
return config.registry.lookup(type).otherwise(function () {
// failed to resolve converter
if (config.permissive) {
return noopConverter;
}
throw 'mime-unknown';
}).then(function (converter) {
var client = config.client || request.originator;
return when.attempt(serializer.write, request.entity, { client: client, request: request })
return when.attempt(converter.write, request.entity, { client: client, request: request, mime: type, registry: config.registry })
.otherwise(function() {

@@ -72,4 +83,2 @@ throw 'mime-serialization';

});
}, function () {
throw 'mime-unknown';
});

@@ -82,8 +91,8 @@ },

var mime = response.headers['Content-Type'];
var type = mime.parse(response.headers['Content-Type']);
return config.registry.lookup(mime).otherwise(function () { return plainText; }).then(function (serializer) {
return config.registry.lookup(type).otherwise(function () { return noopConverter; }).then(function (converter) {
var client = config.client || response.request && response.request.originator;
return when.attempt(serializer.read, response.entity, { client: client, response: response })
return when.attempt(converter.read, response.entity, { client: client, response: response, mime: type, registry: config.registry })
.otherwise(function (e) {

@@ -90,0 +99,0 @@ response.error = 'mime-deserialization';

/*
* Copyright 2012-2013 the original author or authors
* Copyright 2012-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -24,2 +24,3 @@ *

* @param {number} [config.timeout=0] duration in milliseconds before canceling the request. Non-positive values disable the timeout
* @param {boolean} [config.transient=false] if true, timed out requests will not be marked as canceled so that it may be retried
*

@@ -31,7 +32,9 @@ * @returns {Client}

config.timeout = config.timeout || 0;
config.transient = !!config.transient;
return config;
},
request: function (request, config) {
var timeout, abortTrigger;
var timeout, abortTrigger, transient;
timeout = 'timeout' in request ? request.timeout : config.timeout;
transient = 'transient' in request ? request.transient : config.transient;
if (timeout <= 0) {

@@ -45,4 +48,8 @@ return request;

request.cancel();
if (transient) {
// unmark request as canceled for future requests
request.canceled = false;
}
}
else {
else if (!transient) {
request.canceled = true;

@@ -49,0 +56,0 @@ }

@@ -1,8 +0,8 @@

Copyright (c) 2012-2014 the original author or authors
Copyright (c) 2012-2015 the original author or authors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, 
including without limitation the rights to
the Software without restriction,
including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished 
to do
of the Software, and to permit persons to whom the Software is furnished
to do
so, subject to the following conditions:

@@ -9,0 +9,0 @@

/*
* Copyright 2012-2013 the original author or authors
* Copyright 2012-2014 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -13,55 +13,74 @@ *

var when, registry;
var mime, when, registry;
mime = require('../mime');
when = require('when');
function normalizeMime(mime) {
// TODO we're dropping info that may be important
return mime.split(/[;\+]/)[0].trim();
}
function Registry(mimes) {
function Registry(parent) {
var mimes = {};
if (typeof parent === 'function') {
// coerce a lookup function into the registry API
parent = (function (lookup) {
return {
lookup: function (mime) {
// cache to avoid duplicate lookups
mimes[mime] = lookup(mime);
return mimes[mime];
}
};
}(parent));
}
/**
* Lookup the converter for a MIME type
*
* @param {string} mime the MIME type
* @param {string} type the MIME type
* @return a promise for the converter
*/
this.lookup = function lookup(mime) {
mime = normalizeMime(mime);
return mime in mimes ? mimes[mime] : parent.lookup(mime);
this.lookup = function lookup(type) {
var parsed;
parsed = typeof type === 'string' ? mime.parse(type) : type;
if (mimes[parsed.raw]) {
return mimes[parsed.raw];
}
if (mimes[parsed.type + parsed.suffix]) {
return mimes[parsed.type + parsed.suffix];
}
if (mimes[parsed.type]) {
return mimes[parsed.type];
}
if (mimes[parsed.suffix]) {
return mimes[parsed.suffix];
}
return when.reject(new Error('Unable to locate converter for mime "' + parsed.raw + '"'));
};
/**
* Create a late dispatched proxy to the target converter.
*
* Common when a converter is registered under multiple names and
* should be kept in sync if updated.
*
* @param {string} type mime converter to dispatch to
* @returns converter whose read/write methods target the desired mime converter
*/
this.delegate = function delegate(type) {
return {
read: function () {
var args = arguments;
return this.lookup(type).then(function (converter) {
return converter.read.apply(this, args);
}.bind(this));
}.bind(this),
write: function () {
var args = arguments;
return this.lookup(type).then(function (converter) {
return converter.write.apply(this, args);
}.bind(this));
}.bind(this)
};
};
/**
* Register a custom converter for a MIME type
*
* @param {string} mime the MIME type
* @param {string} type the MIME type
* @param converter the converter for the MIME type
* @return a promise for the converter
*/
this.register = function register(mime, converter) {
mime = normalizeMime(mime);
mimes[mime] = when.resolve(converter);
return mimes[mime];
this.register = function register(type, converter) {
mimes[type] = when(converter);
return mimes[type];
};
}
Registry.prototype = {
/**

@@ -73,25 +92,19 @@ * Create a child registry whoes registered converters remain local, while

*/
child: function child() {
return new Registry(this);
}
this.child = function child() {
return new Registry(Object.create(mimes));
};
};
function loadAMD(mime) {
return when.promise(function (resolve, reject) {
// HOPE reject on a local require would be nice
require(['./type/' + mime], resolve, reject);
}).timeout(1000);
}
function loadNode(mime) {
return when.attempt(require, './type/' + mime);
}
registry = new Registry({});
registry = new Registry(typeof define === 'function' && define.amd ? loadAMD : loadNode);
// include text/plain and application/json by default
// include provided serializers
registry.register('application/hal', require('./type/application/hal'));
registry.register('application/json', require('./type/application/json'));
registry.register('application/x-www-form-urlencoded', require('./type/application/x-www-form-urlencoded'));
registry.register('multipart/form-data', require('./type/multipart/form-data'));
registry.register('text/plain', require('./type/text/plain'));
registry.register('application/json', require('./type/application/json'));
registry.register('+json', registry.delegate('application/json'));
return registry;

@@ -98,0 +111,0 @@

/*
* Copyright 2013 the original author or authors
* Copyright 2013-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -13,8 +13,9 @@ *

var json, pathPrefix, find, lazyPromise, when;
var pathPrefix, template, find, lazyPromise, responsePromise, when;
json = require('./json');
pathPrefix = require('../../../interceptor/pathPrefix');
template = require('../../../interceptor/template');
find = require('../../../util/find');
lazyPromise = require('../../../util/lazyPromise');
responsePromise = require('../../../util/responsePromise');
when = require('when');

@@ -32,5 +33,5 @@

/**
* JSON Hypertext Application Language serializer
* Hypertext Application Language serializer
*
* Implemented to http://tools.ietf.org/html/draft-kelly-json-hal-05
* Implemented to https://tools.ietf.org/html/draft-kelly-json-hal-06
*

@@ -49,2 +50,5 @@ * As the spec is still a draft, this implementation will be updated as the

*
* A `requestFor` method is added to the entity to make a request for the
* relationship.
*
* A `clientFor` method is added to the entity to get a full Client for a

@@ -59,3 +63,3 @@ * relationship.

read: function (str, opts) {
var root, client, console;
var client, console;

@@ -65,3 +69,2 @@ opts = opts || {};

console = opts.console || console;
root = json.read.apply(json, arguments);

@@ -74,34 +77,61 @@ function deprecationWarning(relationship, deprecation) {

find.findProperties(root, '_embedded', function (embedded, resource, name) {
Object.keys(embedded).forEach(function (relationship) {
if (relationship in resource) { return; }
defineProperty(resource, relationship, when(embedded[relationship]));
return opts.registry.lookup(opts.mime.suffix).then(function (converter) {
return when(converter.read(str, opts)).then(function (root) {
find.findProperties(root, '_embedded', function (embedded, resource, name) {
Object.keys(embedded).forEach(function (relationship) {
if (relationship in resource) { return; }
var related = responsePromise({
entity: embedded[relationship]
});
defineProperty(resource, relationship, related);
});
defineProperty(resource, name, embedded);
});
find.findProperties(root, '_links', function (links, resource, name) {
Object.keys(links).forEach(function (relationship) {
var link = links[relationship];
if (relationship in resource) { return; }
defineProperty(resource, relationship, responsePromise.make(lazyPromise(function () {
if (link.deprecation) { deprecationWarning(relationship, link.deprecation); }
if (link.templated === true) {
return template(client)({ path: link.href });
}
return client({ path: link.href });
})));
});
defineProperty(resource, name, links);
defineProperty(resource, 'clientFor', function (relationship, clientOverride) {
var link = links[relationship];
if (!link) {
throw new Error('Unknown relationship: ' + relationship);
}
if (link.deprecation) { deprecationWarning(relationship, link.deprecation); }
if (link.templated === true) {
return template(
clientOverride || client,
{ template: link.href }
);
}
return pathPrefix(
clientOverride || client,
{ prefix: link.href }
);
});
defineProperty(resource, 'requestFor', function (relationship, request, clientOverride) {
var client = this.clientFor(relationship, clientOverride);
return client(request);
});
});
return root;
});
defineProperty(resource, name, embedded);
});
find.findProperties(root, '_links', function (links, resource, name) {
Object.keys(links).forEach(function (relationship) {
var link = links[relationship];
if (relationship in resource || link.templated === true) { return; }
defineProperty(resource, relationship, lazyPromise(function () {
if (link.deprecation) { deprecationWarning(relationship, link.deprecation); }
return client({ path: link.href });
}));
});
defineProperty(resource, name, links);
defineProperty(resource, 'clientFor', function (relationship, clientOverride) {
var link = links[relationship];
if (link.deprecation) { deprecationWarning(relationship, link.deprecation); }
return pathPrefix(
clientOverride || client,
{ prefix: link.href }
);
});
});
return root;
},
write: function () {
return json.write.apply(json, arguments);
write: function (obj, opts) {
return opts.registry.lookup(opts.mime.suffix).then(function (converter) {
return converter.write(obj, opts);
});
}

@@ -108,0 +138,0 @@

/*
* Copyright 2012 the original author or authors
* Copyright 2012-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -13,13 +13,31 @@ *

return {
/**
* Create a new JSON converter with custom reviver/replacer.
*
* The extended converter must be published to a MIME registry in order
* to be used. The existing converter will not be modified.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
*
* @param {function} [reviver=undefined] custom JSON.parse reviver
* @param {function|Array} [replacer=undefined] custom JSON.stringify replacer
*/
function createConverter(reviver, replacer) {
return {
read: function (str) {
return JSON.parse(str);
},
read: function (str) {
return JSON.parse(str, reviver);
},
write: function (obj) {
return JSON.stringify(obj);
}
write: function (obj) {
return JSON.stringify(obj, replacer);
},
};
extend: createConverter
};
}
return createConverter();
});

@@ -26,0 +44,0 @@

{
"name": "rest",
"version": "1.2.0",
"version": "1.3.0",
"description": "RESTful HTTP client library",

@@ -49,2 +49,6 @@ "keywords": ["rest", "http", "client", "rest-template", "spring", "cujojs"],

"browser": "./browser",
"jspm": {
"registry": "npm",
"main": "./browser"
},
"scripts": {

@@ -51,0 +55,0 @@ "test": "npm run-script lint && npm run-script buster",

@@ -23,3 +23,3 @@ rest.js

The core client behavior can be augmented with [interceptors](docs/interceptors.md#interceptor-principals). An interceptor wraps the client and transforms the request and response. For example: an interceptor may authenticate a request, or reject the promise if an error is encountered. Interceptors may be combined to create a client with the desired behavior. A configured interceptor acts just like a client. The core clients are basic, they only know the low level mechanics of making a request and parsing the response. All other behavior is applied and configurated with interceptors.
The core client behavior can be augmented with [interceptors](docs/interceptors.md#interceptor-principals). An interceptor wraps the client and transforms the request and response. For example: an interceptor may authenticate a request, or reject the promise if an error is encountered. Interceptors may be combined to create a client with the desired behavior. A configured interceptor acts just like a client. The core clients are basic, they only know the low level mechanics of making a request and parsing the response. All other behavior is applied and configured with interceptors.

@@ -149,3 +149,4 @@ Interceptors are applied to a client by wrapping. To wrap a client with an interceptor, call the `wrap` method on the client providing the interceptor and optionally a configuration object. A new client is returned containing the interceptor's behavior applied to the parent client. It's important to note that the behavior of the original client is not modified, in order to use the new behavior, you must use the returned client.

- IE (6-11)
- Safari (5-7, iOS 4-7.1, should work in earlier versions)
- Safari (5-8, iOS 4-8.1, should work in earlier versions)
- Android (4.0-5.0, should work in earlier versions)
- Opera (11, 12, should work in earlier versions)

@@ -159,12 +160,16 @@

rest.js can be installed via [npm](https://npmjs.org/), [Bower](http://twitter.github.com/bower/), or from source.
rest.js can be installed via [npm](https://npmjs.org/), [Bower](http://bower.io/), [jspm](http://jspm.io/), or from source.
To install without source:
$ npm install rest
$ npm install --save rest
or
$ bower install rest
$ bower install --save rest
or
$ jspm install rest
From source:

@@ -174,5 +179,5 @@

rest.js is designed to run in a browser environment, utilizing [AMD modules](https://github.com/amdjs/amdjs-api/wiki/AMD), or within [Node.js](http://nodejs.org/). [curl.js](https://github.com/cujojs/curl) is highly recommended as an AMD loader, although any loader should work.
rest.js is designed to run in a browser environment, utilizing [AMD modules](https://github.com/amdjs/amdjs-api/wiki/AMD), or within [Node.js](http://nodejs.org/) as CommonJS modules. Any module loader capable of loading either AMD or CommonJS modules should be able to load rest.js. cujoJS [curl.js](https://github.com/cujojs/curl) is actively tested.
An ECMAScript 5 compatible environment is assumed. Older browsers, ::cough:: IE, that do not support ES5 natively can be shimmed. Any shim should work, although we've tested against cujo's [poly.js](https://github.com/cujojs/poly)
An ECMAScript 5 compatible environment is assumed. Older browsers, ::cough:: IE, that do not support ES5 natively can be shimmed. Any shim should work, although we test with cujoJS [poly.js](https://github.com/cujojs/poly)

@@ -229,3 +234,3 @@

Copyright 2012-2014 the original author or authors
Copyright 2012-2015 the original author or authors

@@ -238,5 +243,21 @@ rest.js is made available under the MIT license. See LICENSE.txt for details.

1.3.0
- response.follow() for easy traversal of hypermedia APIs
- application/hal mime converter supports templated URLs, and resource.requestFor('relationship', request)
- full URI Template (rfc6570) support in rest/util/uriTemplate
- section aware URI encoding in rest/util/uriEncoder
- best fit MIME type resolution, including by suffix (i.e. '+json'). Parsed MIME type provided to converters
- installation via jspm and loader support for System.js
- support for Android 4.0-5.0 (no code changes required, now actively testing)
- support for Safari 8, iOS 8.0 and 8.1 (no code changes required, now actively testing)
- raw configuration objects are retained by interceptors, config objects are no longer begotten
- transient timeouts via config.transient on rest/interceptor/timeout, allows retry interceptor to wrap timeout
- extend JSON converter for custom reviver and replacers
- request.mixin properties attempt setting before before and after opening the request. Some browsers (IE) are sensitive to when the properties are set.
- wire.js rest factory interceptors now wire configuration objects
- normalize responses for linked and embedded resources from application/hal mime converter to always be a ResponsePromise
1.2.0
- deprecated rest/interceptor/entity: instead use response.entity()
- deprecated and removed wire.js reference resolves 'client!' and 'resource!', instead use the 'rest/wire' factory
- deprecated and removed wire.js reference resolves 'client!' and 'resource!', instead use the 'rest/wire' factory
- deprecated and removed Dojo store support

@@ -243,0 +264,0 @@ - separate browser and node main modules, browser consumers should switch their main module from 'rest/rest' to 'rest/browser'. This allows tools such as browerify and webpack to more intelligently reason about the module structure.

@@ -5,2 +5,3 @@ [

{ browserName: 'firefox', platform: 'Windows 2008' },
{ browserName: 'firefox', version: '31', platform: 'Windows 2003' },
{ browserName: 'firefox', version: '24', platform: 'Windows 2003' },

@@ -16,2 +17,3 @@ { browserName: 'firefox', version: '17', platform: 'Windows 2003' },

{ browserName: 'internet explorer', version: '6', platform: 'Windows 2003' },
{ browserName: 'safari', version: '8', platform: 'Mac 10.9' },
{ browserName: 'safari', version: '7', platform: 'Mac 10.9' },

@@ -22,2 +24,4 @@ { browserName: 'safari', version: '6', platform: 'Mac 10.8' },

{ browserName: 'opera', version: '11', platform: 'Windows 2008' },
{ browserName: 'ipad', version: '8.1', platform: 'Mac 10.9' },
{ browserName: 'ipad', version: '8', platform: 'Mac 10.9' },
{ browserName: 'ipad', version: '7.1', platform: 'Mac 10.9' },

@@ -28,3 +32,9 @@ { browserName: 'ipad', version: '7', platform: 'Mac 10.9' },

{ browserName: 'ipad', version: '5', platform: 'Mac 10.6' },
{ browserName: 'ipad', version: '4.3', platform: 'Mac 10.6' }
{ browserName: 'ipad', version: '4.3', platform: 'Mac 10.6' },
{ browserName: 'android', version: '5.0', platform: 'Linux' },
{ browserName: 'android', version: '4.4', platform: 'Linux' },
{ browserName: 'android', version: '4.3', platform: 'Linux' },
{ browserName: 'android', version: '4.2', platform: 'Linux' },
{ browserName: 'android', version: '4.1', platform: 'Linux' },
{ browserName: 'android', version: '4.0', platform: 'Linux' }
]

@@ -20,3 +20,3 @@ /*

var client, jsonpInterceptor, rest, responsePromise;
var client, jsonpInterceptor, rest;

@@ -26,9 +26,8 @@ client = require('rest/client/jsonp');

rest = require('rest');
responsePromise = require('rest/util/responsePromise');
buster.testCase('rest/client/jsonp', {
'should make a cross origin request': function () {
var request = { path: 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0', params: { q: 'javascript' } };
var request = { path: 'http://ip.jsontest.com/' };
return client(request).then(function (response) {
assert(response.entity.responseData);
assert(response.entity.ip);
assert.same(request, response.request);

@@ -102,3 +101,3 @@ refute(request.canceled);

'should normalize a string to a request object': function () {
var request = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=javascript';
var request = 'http://ip.jsontest.com/';
return client(request).then(function (response) {

@@ -118,3 +117,3 @@ assert.same(request, response.request.path);

response.otherwise(function () {});
assert(response instanceof responsePromise.ResponsePromise);
assert.isFunction(response.entity);
}

@@ -121,0 +120,0 @@ });

@@ -21,7 +21,6 @@ /*

var rest, client, responsePromise, http, https, fs, serverHttp, serverHttps;
var rest, client, http, https, fs, serverHttp, serverHttps;
rest = require('rest');
client = require('rest/client/node');
responsePromise = require('rest/util/responsePromise');
http = require('http');

@@ -180,3 +179,3 @@ https = require('https');

response.otherwise(function () {});
assert(response instanceof responsePromise.ResponsePromise);
assert.isFunction(response.entity);
}

@@ -183,0 +182,0 @@ });

@@ -20,7 +20,6 @@ /*

var client, rest, responsePromise, flickrUrl;
var client, rest, flickrUrl;
client = require('rest/client/xdr');
rest = require('rest');
responsePromise = require('rest/util/responsePromise');

@@ -113,3 +112,3 @@ flickrUrl = 'http://api.flickr.com/services/rest/?method=flickr.test.echo&api_key=95f41bfa4faa0f43bf7c24795eabbed4&format=rest';

'should return a ResponsePromise': function () {
assert(client() instanceof responsePromise.ResponsePromise);
assert.isFunction(client().entity);
}

@@ -116,0 +115,0 @@ },

@@ -20,3 +20,3 @@ /*

var xhr, rest, xhrFallback, responsePromise, when, client;
var xhr, rest, xhrFallback, when, client;

@@ -26,3 +26,2 @@ xhr = require('rest/client/xhr');

xhrFallback = require('rest/interceptor/ie/xhr');
responsePromise = require('rest/util/responsePromise');
when = require('when');

@@ -110,3 +109,5 @@

refute.equals(xhr.foo, 'bar');
}).otherwise(fail);
}).otherwise(function (err) {
fail(JSON.stringify(err));
});
}

@@ -191,3 +192,3 @@ },

'should return a ResponsePromise': function () {
assert(client() instanceof responsePromise.ResponsePromise);
assert.isFunction(client().entity);
},

@@ -194,0 +195,0 @@ 'should ignore a "Content-Type: multipart/form-data" header': {

/*
* Copyright 2013-2014 the original author or authors
* Copyright 2013-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -20,3 +20,3 @@ *

var interceptor, rest, when, responsePromise;
var interceptor, rest, when;

@@ -26,3 +26,2 @@ interceptor = require('rest/interceptor');

when = require('when');
responsePromise = require('rest/util/responsePromise');

@@ -316,4 +315,3 @@ function defaultClient(request) {

request.phase = 'request';
refute.same(theConfig, config);
assert.same(theConfig.foo, config.foo);
assert.same(theConfig, config);
return request;

@@ -323,4 +321,3 @@ },

response.phase = 'response';
refute.same(theConfig, config);
assert.same(theConfig.foo, config.foo);
assert.same(theConfig, config);
return response;

@@ -452,3 +449,3 @@ }

},
'should initialize the config object, without modifying the provided object': function () {
'should initialize the config object, modifying the provided object': function () {
var theConfig, theInterceptor, client;

@@ -458,4 +455,3 @@ theConfig = { foo: 'bar' };

init: function (config) {
refute.same(theConfig, config);
assert.same('bar', config.foo);
assert.same(theConfig, config);
config.bleep = 'bloop';

@@ -465,6 +461,3 @@ return config;

request: function (request, config) {
refute.same(theConfig, config);
assert.same('bar', config.foo);
config.foo = 'not-bar';
assert.same('bar', theConfig.foo);
assert.same(theConfig, config);
request.phase = 'request';

@@ -474,4 +467,3 @@ return request;

response: function (response, config) {
assert.same('not-bar', config.foo);
assert.same('bar', theConfig.foo);
assert.same(theConfig, config);
response.phase = 'response';

@@ -481,3 +473,5 @@ return response;

});
refute('bleep' in theConfig);
client = theInterceptor(defaultClient, theConfig);
assert.same('bloop', theConfig.bleep);
return client().then(function (response) {

@@ -509,3 +503,3 @@ assert.same('request', response.request.phase);

client = theInterceptor(defaultClient);
assert(client() instanceof responsePromise.ResponsePromise);
assert.isFunction(client().entity);
}

@@ -512,0 +506,0 @@ });

@@ -99,2 +99,19 @@ /*

},
'should error the request if unable to find a converter for the desired mime, unless in permissive mode': function () {
var client, entity, request;
client = mime(
function (request) {
return { request: request, headers: {} };
},
{ permissive: true }
);
entity = {};
request = { headers: { 'Content-Type': 'application/vnd.com.example' }, entity: entity };
return client(request).then(function (response) {
assert.same(entity, response.request.entity);
assert.equals('application/vnd.com.example', response.request.headers['Content-Type']);
}).otherwise(fail);
},
'should use text/plain converter for a response if unable to find a converter for the desired mime': function () {

@@ -139,4 +156,14 @@ var client;

assert.calledWith(converter.read, 'response entity', { client: client, response: response });
assert.calledWith(converter.write, 'request entity', { client: client, request: response.request });
assert.calledWith(converter.read, 'response entity', {
client: client,
response: response,
mime: { raw: 'application/vnd.com.example', type: 'application/vnd.com.example', suffix: '', params: {} },
registry: customRegistry
});
assert.calledWith(converter.write, 'request entity', {
client: client,
request: response.request,
mime: { raw: 'application/vnd.com.example', type: 'application/vnd.com.example', suffix: '', params: {} },
registry: customRegistry
});
}).otherwise(fail);

@@ -143,0 +170,0 @@ },

/*
* Copyright 2012-2014 the original author or authors
* Copyright 2012-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -40,8 +40,9 @@ *

function cancelableClient(request) {
/*jshint validthis:true */
var d = when.defer();
request.canceled = false;
request.cancel = function () {
request.cancel = this.spy(function () {
request.canceled = true;
d.resolver.reject({ request: request });
};
});
return d.promise;

@@ -122,3 +123,3 @@ }

var client, request, response;
client = timeout(cancelableClient, { timeout: 11 });
client = timeout(cancelableClient.bind(this), { timeout: 11 });
request = {};

@@ -130,2 +131,3 @@ response = client(request).then(

assert.equals('timeout', response.error);
assert.called(request.cancel);
assert(request.canceled);

@@ -137,2 +139,34 @@ })

},
'should not cancel request if transient config enabled': function () {
var client, request, response;
client = timeout(cancelableClient.bind(this), { timeout: 11, transient: true });
request = {};
response = client(request).then(
fail,
failOnThrow(function (response) {
assert.same(request, response.request);
assert.equals('timeout', response.error);
assert.called(request.cancel);
refute(request.canceled);
})
);
refute(request.canceled);
return response;
},
'should use request transient value rather then interceptor': function () {
var client, request, response;
client = timeout(cancelableClient.bind(this), { timeout: 11, transient: false });
request = { transient: true };
response = client(request).then(
fail,
failOnThrow(function (response) {
assert.same(request, response.request);
assert.equals('timeout', response.error);
assert.called(request.cancel);
refute(request.canceled);
})
);
refute(request.canceled);
return response;
},
'should have the default client as the parent by default': function () {

@@ -139,0 +173,0 @@ assert.same(rest, timeout().skip());

/*
* Copyright 2012-2013 the original author or authors
* Copyright 2012-2014 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -79,2 +79,39 @@ *

}).otherwise(fail);
},
'should ignore charset in mime resolution': function () {
var converter = {};
registry.register('application/vnd.com.example', converter);
return registry.lookup('application/vnd.com.example;charset=utf-8').then(function (c) {
assert.same(converter, c);
}).otherwise(fail);
},
'should ignore suffix in mime resolution': function () {
var converter = {};
registry.register('application/vnd.com.example', converter);
return registry.lookup('application/vnd.com.example+foo').then(function (c) {
assert.same(converter, c);
}).otherwise(fail);
},
'should fallback to suffix if mime type is not resolved': function () {
var converter = {};
registry.register('+foo', converter);
return registry.lookup('application/vnd.com.example+foo').then(function (c) {
assert.same(converter, c);
}).otherwise(fail);
},
'should invoke the delegate mime converter': function () {
var converter = {
read: function (obj) {
return 'read ' + obj;
},
write: function (obj) {
return 'write ' + obj;
}
};
registry.register('+bar', registry.delegate('+foo'));
registry.register('+foo', converter);
return registry.lookup('application/vnd.com.example+foo').then(function (converter) {
assert.same('read hello', converter.read('hello'));
assert.same('write world', converter.write('world'));
});
}

@@ -81,0 +118,0 @@ });

/*
* Copyright 2013 the original author or authors
* Copyright 2013-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -19,7 +19,10 @@ *

var hal, mime, supports;
var hal, mime, registry, halMime, supports;
hal = require('rest/mime/type/application/hal');
mime = require('rest/interceptor/mime');
registry = require('rest/mime/registry');
halMime = require('rest/mime').parse('application/hal+json');
function client(request) {

@@ -45,24 +48,32 @@ return { request: request };

'should stringify json': function () {
assert.equals('{"foo":"bar"}', hal.write({ foo: 'bar' }));
return hal.write({ foo: 'bar' }, { mime: halMime, registry: registry }).then(function (resource) {
assert.equals('{"foo":"bar"}', resource);
}).otherwise(fail);
},
'should read json': function () {
assert.equals({ foo: 'bar' }, hal.read('{"foo":"bar"}'));
return hal.read('{"foo":"bar"}', { mime: halMime, registry: registry }).then(function (resource) {
assert.equals({ foo: 'bar' }, resource);
}).otherwise(fail);
},
'should place embedded relationships on the host object': function () {
var resource = hal.read(JSON.stringify({ _embedded: { prop: 'embed' } }));
return resource.prop.then(function (prop) {
assert.same(prop, 'embed');
});
return hal.read(JSON.stringify({ _embedded: { prop: 'embed' } }), { mime: halMime, registry: registry }).then(function (resource) {
return resource.prop.entity().then(function (prop) {
assert.same(prop, 'embed');
});
}).otherwise(fail);
},
'should not overwrite a property on the host oject with an embedded relationship': function () {
var resource = hal.read(JSON.stringify({ prop: 'host', _embedded: { prop: 'embed' } }));
assert.same(resource.prop, 'host');
return hal.read(JSON.stringify({ prop: 'host', _embedded: { prop: 'embed' } }), { mime: halMime, registry: registry }).then(function (resource) {
assert.same(resource.prop, 'host');
}).otherwise(fail);
},
'should place linked relationships on the host object': function () {
var resource = hal.read(JSON.stringify({ _links: { prop: { href: '/' } } }));
assert.isFunction(resource.prop.then);
return hal.read(JSON.stringify({ _links: { prop: { href: '/' } } }), { mime: halMime, registry: registry }).then(function (resource) {
assert.isFunction(resource.prop.entity);
}).otherwise(fail);
},
'should not overwrite a property on the host oject with a linked relationship': function () {
var resource = hal.read(JSON.stringify({ prop: 'host', _links: { prop: { href: '/' } } }));
assert.same(resource.prop, 'host');
return hal.read(JSON.stringify({ prop: 'host', _links: { prop: { href: '/' } } }), { mime: halMime, registry: registry }).then(function (resource) {
assert.same(resource.prop, 'host');
}).otherwise(fail);
},

@@ -83,11 +94,42 @@ 'should fetch a linked resource': function () {

},
'should get a client for an relationship': function () {
var resource = hal.read(JSON.stringify({ _links: { prop: { href: '/' } } }), { client: client });
return resource.clientFor('prop')().then(function (response) {
'should fetch a templated linked resource': function () {
var client = mime(function client(request) {
return request.path === '/' ?
{ request: request, entity: JSON.stringify({ _links: { self: { href: '/' }, child: { templated: true, href: '/resource{?lang}' } } }), headers: { 'Content-Type': 'application/hal+json' } } :
{ request: request, entity: JSON.stringify({ _links: { self: { href: '/resource' }, parent: { href: '/' } } }), headers: { 'Content-Type': 'application/hal+json' } };
});
return client({ path: '/' }).then(function (response) {
assert.same('/', response.request.path);
return response.entity.child.then(function (response) {
assert.same('/resource', response.request.path);
});
}).otherwise(fail);
},
'should make a request for a relationship': function () {
return hal.read(JSON.stringify({ _links: { prop: { href: '/' } } }), { mime: halMime, registry: registry, client: client }).then(function (resource) {
return resource.requestFor('prop', { method: 'delete' }).then(function (response) {
assert.same('/', response.request.path);
assert.same('delete', response.request.method);
});
}).otherwise(fail);
},
'should get a client for a relationship': function () {
return hal.read(JSON.stringify({ _links: { prop: { href: '/' } } }), { mime: halMime, registry: registry, client: client }).then(function (resource) {
return resource.clientFor('prop')().then(function (response) {
assert.same('/', response.request.path);
});
}).otherwise(fail);
},
'should get a client for a templated relationship': function () {
return hal.read(JSON.stringify({ _links: { prop: { templated: true, href: '/{?lang}' } } }), { mime: halMime, registry: registry, client: client }).then(function (resource) {
return resource.clientFor('prop')({ params: { lang: 'en-us' } }).then(function (response) {
assert.same('/?lang=en-us', response.request.path);
refute('params' in response.request);
});
}).otherwise(fail);
},
'should safely warn when accessing a deprecated relationship': {
'': function () {
var console, resource;
var console;

@@ -98,12 +140,14 @@ console = {

};
resource = hal.read(JSON.stringify({ _links: { prop: { href: '/', deprecation: 'http://example.com/deprecation' } } }), { client: client, console: console });
return resource.clientFor('prop')().then(function (response) {
assert.same('/', response.request.path);
assert.calledWith(console.warn, 'Relationship \'prop\' is deprecated, see http://example.com/deprecation');
refute.called(console.log);
return hal.read(JSON.stringify({ _links: { prop: { href: '/', deprecation: 'http://example.com/deprecation' } } }), { mime: halMime, registry: registry, client: client, console: console }).then(function (resource) {
return resource.clientFor('prop')().then(function (response) {
assert.same('/', response.request.path);
assert.calledWith(console.warn, 'Relationship \'prop\' is deprecated, see http://example.com/deprecation');
refute.called(console.log);
});
}).otherwise(fail);
},
'falling back to log if warn is not availble': function () {
var console, resource;
var console;

@@ -113,43 +157,39 @@ console = {

};
resource = hal.read(JSON.stringify({ _links: { prop: { href: '/', deprecation: 'http://example.com/deprecation' } } }), { client: client, console: console });
return resource.clientFor('prop')().then(function (response) {
assert.same('/', response.request.path);
assert.calledWith(console.log, 'Relationship \'prop\' is deprecated, see http://example.com/deprecation');
return hal.read(JSON.stringify({ _links: { prop: { href: '/', deprecation: 'http://example.com/deprecation' } } }), { mime: halMime, registry: registry, client: client, console: console }).then(function (resource) {
return resource.clientFor('prop')().then(function (response) {
assert.same('/', response.request.path);
assert.calledWith(console.log, 'Relationship \'prop\' is deprecated, see http://example.com/deprecation');
});
}).otherwise(fail);
},
'doing nothing if the console is unavailable': function () {
var console, resource;
var console;
console = {};
resource = hal.read(JSON.stringify({ _links: { prop: { href: '/', deprecation: 'http://example.com/deprecation' } } }), { client: client, console: console });
return resource.clientFor('prop')().then(function (response) {
assert.same('/', response.request.path);
return hal.read(JSON.stringify({ _links: { prop: { href: '/', deprecation: 'http://example.com/deprecation' } } }), { mime: halMime, registry: registry, client: client, console: console }).then(function (resource) {
return resource.clientFor('prop')().then(function (response) {
assert.same('/', response.request.path);
});
}).otherwise(fail);
}
},
'should not index templated links': function () {
var resource = hal.read(JSON.stringify({ _links: {
prop: { href: '/', templated: 'true' }, // not-templated, must be boolean true
query: { href: '/{?query}', templated: true } // templated
} }));
assert.isFunction(resource.prop.then);
refute(resource.query);
},
'should be able to write read entities': function () {
var raw, read, writen;
var raw;
raw = { _embedded: { prop: 'embed' }, _links: { prop: { href: '/' } }, foo: 'bar' };
read = hal.read(JSON.stringify(raw));
writen = hal.write(read);
assert.match(writen, '"foo":"bar"');
return hal.read(JSON.stringify(raw), { mime: halMime, registry: registry }).then(function (read) {
return hal.write(read, { mime: halMime, registry: registry });
}).then(function (written) {
assert.match(written, '"foo":"bar"');
if (!supports['Object.defineProperty']) {
refute.match(writen, '_embedded');
refute.match(writen, '_links');
refute.match(writen, 'clientFor');
refute.match(writen, 'prop');
}
if (!supports['Object.defineProperty']) {
refute.match(written, '_embedded');
refute.match(written, '_links');
refute.match(written, 'clientFor');
refute.match(written, 'prop');
}
});
}

@@ -156,0 +196,0 @@ });

@@ -11,3 +11,3 @@ /*

var assert, refute;
var assert, refute, undef;

@@ -27,2 +27,16 @@ assert = buster.assertions.assert;

assert.equals('{"foo":"bar"}', json.write({ foo: 'bar' }));
},
'should use provided reviver and replacer': function () {
var reviver, replacer, customJson;
reviver = function reviver() {};
replacer = [];
customJson = json.extend(reviver, replacer);
assert.equals(undef, customJson.read('{"foo":"bar"}'));
assert.equals('{}', customJson.write({ foo: 'bar' }));
// old json convert is unmodified
assert.equals({ foo: 'bar' }, json.read('{"foo":"bar"}'));
assert.equals('{"foo":"bar"}', json.write({ foo: 'bar' }));
}

@@ -29,0 +43,0 @@ });

/*
* Copyright 2014 the original author or authors
* Copyright 2014-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -11,3 +11,3 @@ *

var assert, refute, fail;
var assert, refute, fail, failOnThrow;

@@ -17,15 +17,37 @@ assert = buster.assertions.assert;

fail = buster.assertions.fail;
failOnThrow = buster.assertions.failOnThrow;
define('rest/util/responsePromise-test', function (require) {
var responsePromise = require('rest/util/responsePromise'),
when = require('when');
var responsePromise, mime, when, client;
responsePromise = require('rest/util/responsePromise');
mime = require('rest/interceptor/mime');
when = require('when');
client = mime(function (request) {
var page = request.params && request.params.page || 0;
return {
request: request,
headers: {
'Content-Type': 'application/hal+json'
},
entity: JSON.stringify({
page: page,
_links: {
self: { href: request.path },
next: { href: request.path + '/next' },
search: { href: request.path + '/{?q}', templated: true }
}
})
};
});
buster.testCase('rest/util/responsePromise', {
'should be an instance of Promise': function () {
assert(responsePromise(when()) instanceof when.Promise);
assert(responsePromise() instanceof when.Promise);
},
'should resolve the response entity': function () {
var response = responsePromise(when({ entity: 43 }));
var response = responsePromise({ entity: 43 });

@@ -40,9 +62,9 @@ return response.entity().then(

'should resolve the response entity for a rejected promise': function () {
var response = responsePromise(when.reject({ entity: 43 }));
var response = responsePromise.reject({ entity: 43 });
return response.entity().then(
fail,
function (entity) {
failOnThrow(function (entity) {
assert.equals(43, entity);
}
})
);

@@ -52,3 +74,3 @@ },

'should resolve the response status code': function () {
var response = responsePromise(when({ status: { code: 200 } }));
var response = responsePromise({ status: { code: 200 } });

@@ -63,9 +85,9 @@ return response.status().then(

'should resolve the response status code for a rejected promise': function () {
var response = responsePromise(when.reject({ status: { code: 200 } }));
var response = responsePromise.reject({ status: { code: 200 } });
return response.status().then(
fail,
function (status) {
failOnThrow(function (status) {
assert.equals(200, status);
}
})
);

@@ -76,3 +98,3 @@ },

var headers = { 'Content-Type': 'text/plain' };
var response = responsePromise(when({ headers: headers }));
var response = responsePromise({ headers: headers });

@@ -88,9 +110,9 @@ return response.headers().then(

var headers = { 'Content-Type': 'text/plain' };
var response = responsePromise(when.reject({ headers: headers }));
var response = responsePromise.reject({ headers: headers });
return response.headers().then(
fail,
function (_headers) {
failOnThrow(function (_headers) {
assert.same(headers, _headers);
}
})
);

@@ -101,3 +123,3 @@ },

var headers = { 'Content-Type': 'text/plain' };
var response = responsePromise(when({ headers: headers }));
var response = responsePromise({ headers: headers });

@@ -113,9 +135,9 @@ return response.header('Content-Type').then(

var headers = { 'Content-Type': 'text/plain' };
var response = responsePromise(when.reject({ headers: headers }));
var response = responsePromise.reject({ headers: headers });
return response.header('Content-Type').then(
fail,
function (_header) {
failOnThrow(function (_header) {
assert.same(headers['Content-Type'], _header);
}
})
);

@@ -125,3 +147,3 @@ },

var headers = { 'Content-Type': 'text/plain' };
var response = responsePromise(when({ headers: headers }));
var response = responsePromise({ headers: headers });

@@ -134,2 +156,62 @@ return response.header('content-type').then(

);
},
'should follow hypermedia reltionships': {
'': function () {
return client('http://example.com').follow('next').entity().then(
function (response) {
assert.same('http://example.com/next', response._links.self.href);
},
fail
);
},
'passing params': function () {
return client('http://example.com').follow({ rel: 'next', params: { projection: 'limited' } }).then(
function (response) {
assert.same('limited', response.request.params.projection);
assert.same('http://example.com/next', response.entity._links.self.href);
},
fail
);
},
'applying params to templates': function () {
return client('http://example.com').follow({ rel: 'search', params: { q: 'hypermedia client' } }).then(
function (response) {
assert.same('http://example.com/?q=hypermedia%20client', response.request.path);
refute('params' in response.request);
},
fail
);
},
'by chaining': function () {
return client('http://example.com').follow('next').follow('next').entity().then(
function (response) {
assert.same('http://example.com/next/next', response._links.self.href);
},
fail
);
},
'by inline chaining': function () {
return client('http://example.com').follow(['next', 'next']).entity().then(
function (response) {
assert.same('http://example.com/next/next', response._links.self.href);
},
fail
);
},
'with errors for non hypermedia responses': function () {
return responsePromise({ entity: {} }).follow('next').then(
fail,
failOnThrow(function (err) {
assert.same('Hypermedia response expected', err.message);
})
);
},
'with errors for unknown relationships': function () {
return client('http://example.com').follow('prev').then(
fail,
failOnThrow(function (err) {
assert.same('Unknown relationship: prev', err.message);
})
);
}
}

@@ -136,0 +218,0 @@

/*
* Copyright 2012-2013 the original author or authors
* Copyright 2012-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -167,3 +167,3 @@ *

},
'without wiring interceptor configurations': function () {
'wiring interceptor configurations': function () {
var spec, client;

@@ -178,3 +178,3 @@ client = function (request) {

interceptors: [
{ module: 'rest/interceptor/pathPrefix', config: { $ref: 'basePath', prefix: 'useThisOne' } }
{ module: 'rest/interceptor/pathPrefix', config: { $ref: 'basePath', prefix: 'dontUseThisOne' } }
]

@@ -184,3 +184,3 @@ }

basePath: {
literal: { prefix: 'dontUseThis' }
literal: { prefix: 'useThisOne' }
},

@@ -187,0 +187,0 @@ $plugins: [{ module: 'rest/wire' }]

/*
* Copyright 2014 the original author or authors
* Copyright 2014-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -13,14 +13,5 @@ *

var Promise = require('when/lib/Promise'),
when = require('when'),
var when = require('when'),
normalizeHeaderName = require('./normalizeHeaderName');
// extend ResponsePromise from Promise
function ResponsePromise() {
return Promise.apply(this, arguments);
}
ResponsePromise.prototype = Object.create(Promise.prototype);
// augment ResponsePromise with HTTP Response specific methods
function property(promise, name) {

@@ -42,5 +33,6 @@ return promise.then(

*/
ResponsePromise.prototype.entity = function entity() {
function entity() {
/*jshint validthis:true */
return property(this, 'entity');
};
}

@@ -52,5 +44,6 @@ /**

*/
ResponsePromise.prototype.status = function status() {
function status() {
/*jshint validthis:true */
return property(property(this, 'status'), 'code');
};
}

@@ -62,5 +55,6 @@ /**

*/
ResponsePromise.prototype.headers = function headers() {
function headers() {
/*jshint validthis:true */
return property(this, 'headers');
};
}

@@ -73,8 +67,46 @@ /**

*/
ResponsePromise.prototype.header = function header(headerName) {
function header(headerName) {
/*jshint validthis:true */
headerName = normalizeHeaderName(headerName);
return property(this.headers(), headerName);
};
}
/**
* Follow a related resource
*
* The relationship to follow may be define as a plain string, an object
* with the rel and params, or an array containing one or more entries
* with the previous forms.
*
* Examples:
* response.follow('next')
*
* response.follow({ rel: 'next', params: { pageSize: 100 } })
*
* response.follow([
* { rel: 'items', params: { projection: 'noImages' } },
* 'search',
* { rel: 'findByGalleryIsNull', params: { projection: 'noImages' } },
* 'items'
* ])
*
* @param {String|Object|Array} rels one, or more, relationships to follow
* @returns ResponsePromise<Response> related resource
*/
function follow(rels) {
/*jshint validthis:true */
rels = [].concat(rels);
return make(when.reduce(rels, function (response, rel) {
if (typeof rel === 'string') {
rel = { rel: rel };
}
if (typeof response.entity.clientFor !== 'function') {
throw new Error('Hypermedia response expected');
}
var client = response.entity.clientFor(rel.rel);
return client({ params: rel.params });
}, this));
}
/**
* Wrap a Promise as an ResponsePromise

@@ -85,10 +117,25 @@ *

*/
function makeResponsePromise(promise) {
return new ResponsePromise(promise.then.bind(promise));
function make(promise) {
promise.status = status;
promise.headers = headers;
promise.header = header;
promise.entity = entity;
promise.follow = follow;
return promise;
}
makeResponsePromise.ResponsePromise = ResponsePromise;
function responsePromise() {
return make(when.apply(when, arguments));
}
return makeResponsePromise;
responsePromise.make = make;
responsePromise.reject = function (val) {
return make(when.reject(val));
};
responsePromise.promise = function (func) {
return make(when.promise(func));
};
return responsePromise;
});

@@ -95,0 +142,0 @@

/*
* Copyright 2012-2014 the original author or authors
* Copyright 2012-2015 the original author or authors
* @license MIT, see LICENSE.txt for details

@@ -26,4 +26,7 @@ *

delete interceptorDef.config;
return wire(typeof interceptorDef === 'string' ? { module: interceptorDef } : interceptorDef).then(function (interceptor) {
return { interceptor: interceptor, config: interceptorConfig };
return when.all([
wire(typeof interceptorDef === 'string' ? { module: interceptorDef } : interceptorDef),
wire(interceptorConfig)
]).spread(function (interceptor, config) {
return { interceptor: interceptor, config: config };
});

@@ -30,0 +33,0 @@ }));

Sorry, the diff of this file is not supported yet

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