Comparing version 0.1.2 to 0.2.0
ChangeLog | ||
========= | ||
0.2.0 (2017-04-21) | ||
------------------ | ||
* #17: Now using the Fetch API instead of the requests library. The requests | ||
library is kept around for BC purposes, but this will eventually be removed. | ||
* #25: the resourceCache was accidentally shared between Client instances. | ||
0.1.2 (2017-04-19) | ||
@@ -5,0 +13,0 @@ ------------------ |
@@ -5,27 +5,40 @@ var url = require('url'); | ||
var package = require('../package.json'); | ||
var fetch = require('node-fetch'); | ||
var Client = function(bookMark, requestOptions) { | ||
var Client = function(bookMark, options) { | ||
if (typeof requestOptions === 'undefined') { | ||
requestOptions = {}; | ||
if (typeof options === 'undefined') { | ||
options = {}; | ||
} | ||
this.resourceCache = {}; | ||
if (options.accept) { | ||
this.accept = options.accept; | ||
} else { | ||
this.accept = 'application/hal+json, application/json'; | ||
} | ||
/* Note: all the following settings exists solely to provide backwards | ||
* compatibility for the Requests library, and will eventually be removed. | ||
*/ | ||
// json-promise-any setting, makes sure that we're getting whole responses | ||
// and not just the response body. | ||
requestOptions.resolveWithFullResponse = true; | ||
options.resolveWithFullResponse = true; | ||
if (!requestOptions.headers) { | ||
requestOptions.headers = {}; | ||
if (!options.headers) { | ||
options.headers = {}; | ||
} | ||
if (!requestOptions.headers['User-Agent']) { | ||
requestOptions.headers['User-Agent'] = 'Restl/' + package.version; | ||
if (!options.headers['User-Agent']) { | ||
options.headers['User-Agent'] = 'Restl/' + package.version; | ||
} | ||
if (!requestOptions.headers.Accept) { | ||
requestOptions.headers.Accept = 'application/hal+json, application/json'; | ||
if (!options.headers.Accept) { | ||
options.headers.Accept = 'application/hal+json, application/json'; | ||
} | ||
// Parsing json by default. | ||
requestOptions.json = true; | ||
this.request = request.defaults(requestOptions); | ||
options.json = true; | ||
this.request = request.defaults(options); | ||
this.bookMark = bookMark; | ||
}; | ||
@@ -40,3 +53,3 @@ | ||
*/ | ||
resourceCache : {}, | ||
resourceCache : null, | ||
@@ -72,2 +85,22 @@ /** | ||
}, | ||
/** | ||
* This function does an arbitrary request using the fetch API. | ||
* | ||
* Every request in restl is routed through here so it can be initialized | ||
* with some useful defaults. | ||
*/ | ||
fetch : function(input, init) { | ||
var request = new fetch.Request(input, init); | ||
if (!request.headers.has('User-Agent')) { | ||
request.headers.set('User-Agent', 'Restl/' + require('../package.json').version); | ||
} | ||
if (!request.headers.has('Accept')) { | ||
request.headers.set('Accept', this.accept); | ||
} | ||
return fetch(request); | ||
} | ||
@@ -74,0 +107,0 @@ |
@@ -7,2 +7,3 @@ 'use strict'; | ||
var Promise = require('bluebird'); | ||
var fetch = require('node-fetch'); | ||
@@ -36,8 +37,14 @@ var Resource = function(client, uri) { | ||
return this.request({ | ||
method: 'PUT', | ||
uri: this.uri, | ||
body: body | ||
}).then(function() { | ||
return this.fetch( | ||
this.uri, | ||
{ | ||
method: 'PUT', | ||
body: JSON.stringify(body) | ||
} | ||
).then(function(response) { | ||
if (!response.ok) { | ||
throw new Error('HTTP error: ' + response.statusCode); | ||
} | ||
// Wipe out the local cache | ||
@@ -54,10 +61,15 @@ this.repr = null; | ||
*/ | ||
delete: function(body) { | ||
delete: function() { | ||
return this.request({ | ||
method: 'DELETE', | ||
uri: this.uri, | ||
body: body | ||
}).then(function() { | ||
return this.fetch( | ||
this.uri, | ||
{ | ||
method: 'DELETE', | ||
} | ||
).then(function(response) { | ||
if (!response.ok) { | ||
throw new Error('HTTP error: ' + response.statusCode); | ||
} | ||
// Wipe out the local cache | ||
@@ -86,16 +98,26 @@ this.repr = null; | ||
return this.request({ | ||
method: 'POST', | ||
uri: this.uri, | ||
body: body | ||
}).then(function(response) { | ||
if (response.headers.location) { | ||
return this.fetch( | ||
this.uri, | ||
{ | ||
method: 'POST', | ||
body: JSON.stringify(body) | ||
} | ||
).then(function(response) { | ||
if (!response.ok) { | ||
throw new Error('HTTP error: ' + response.statusCode); | ||
} | ||
if (response.headers.has('location')) { | ||
return this.client.getResource( | ||
url.resolve( | ||
this.uri, | ||
response.headers.location | ||
response.headers.get('location') | ||
) | ||
); | ||
} | ||
return null; | ||
}.bind(this)); | ||
}, | ||
@@ -109,10 +131,16 @@ | ||
return this.request({ | ||
method: 'GET', | ||
uri: this.uri | ||
}).then(function(response) { | ||
var response; | ||
return this.fetch(this.uri, {method: 'GET' }) | ||
.then(function(r) { | ||
response = r; | ||
if (!response.ok) { | ||
throw new Error('HTTP error: ' + response.statusCode); | ||
} else { | ||
return response.json(); | ||
} | ||
}).then(function(jsonBody) { | ||
this.repr = new Representation( | ||
this.uri, | ||
response.headers['content-type'], | ||
response.body | ||
jsonBody | ||
); | ||
@@ -215,2 +243,18 @@ // Parsing and storing embedded uris | ||
/** | ||
* Does an arbitrary HTTP request on the resource using the Fetch API. | ||
*/ | ||
fetch: function(input, init) { | ||
if (typeof input === 'string') { | ||
input = url.resolve(this.uri, input); | ||
} else { | ||
input.url = url.resolve(this.uri, input.url); | ||
} | ||
var request = new fetch.Request(input, init); | ||
return this.client.fetch(request); | ||
}, | ||
/** | ||
* Does an arbitrary HTTP request on the resource, and returns the HTTP | ||
@@ -217,0 +261,0 @@ * response object from the Request library, wrapped in a Promise. |
{ | ||
"name": "restl", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "Opiniated HAL client.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -22,39 +22,26 @@ Restl - A hypermedia client for nodejs | ||
npm install --save restl | ||
npm install --save restl | ||
Goals | ||
----- | ||
Features overview | ||
----------------- | ||
### For 1.0: | ||
Restl is a library that sits on top of a HTTP client (currently Request, but | ||
soon the Fetch API). | ||
* Expand CURIES automatically. | ||
* Support HTTP `Link` header. | ||
* Support non-JSON resources, including things like images. | ||
* Parse [HTML5 links][1]. | ||
* Parse [Atom][5]. | ||
* Built-in OAuth2. | ||
It provides some useful abstractions that make it easier to work with true | ||
hypermedia / HATEAOS servers. It currently parses [HAL][2] and has a deep | ||
understanding of links and embedded resources. | ||
Using this library it becomes very easy to follow links from a single bookmark, | ||
and discover resources and features on the server. Embedded resources are | ||
completely hidden. Embedded resources just show up as links, but when you're | ||
asking for the representation, the response to the `GET` request will be | ||
served from a cache. | ||
### Post 1.0 | ||
This feature allows HAL servers to upgrade links to embedded resources, and | ||
allows any client to transparently take advantage of this change and issue | ||
less HTTP requests. | ||
* Support for [HAL Forms][4]. | ||
* Parse and respect HTTP Cache headers. | ||
* Support [`Prefer: return=representation`][6]. | ||
* Browser support (nodejs only at the moment, but only because of the Request | ||
library.) | ||
* Unittests | ||
### Already done: | ||
* Following links. | ||
* Basic HAL parsing. | ||
* Normalizing `_links` and `_embedded`. | ||
* `PUT` request. | ||
* `DELETE` request. | ||
* `POST` request | ||
* Global resource cache. | ||
* Resolve every URI to an absolute URI. | ||
* Figuring caching resources from `_embedded`. | ||
Usage | ||
@@ -150,4 +137,2 @@ ----- | ||
#### Constructor | ||
```js | ||
@@ -337,4 +322,2 @@ var client = new Client(bookMark, options); | ||
[3]: https://www.npmjs.com/package/request | ||
[4]: https://rwcbook.github.io/hal-forms/ "HAL Forms" | ||
[5]: https://bitworking.org/projects/atom/rfc5023.html "AtomPub" | ||
[6]: https://tools.ietf.org/html/rfc7240 "Prefer Header for HTTP" |
@@ -10,4 +10,8 @@ { | ||
"_links" : { | ||
"self": { "href" : "/hal1.json" }, | ||
"next" : { "href" : "/hal2.json" } | ||
"self" : { "href" : "/hal1.json" }, | ||
"next" : { "href" : "/hal2.json" }, | ||
"collection" : { "href" : "/hal-collection.json" }, | ||
"headerTest": { "href" : "/headers" }, | ||
"error400" : { "href" : "/error/400" }, | ||
"redirect" : { "href" : "/redirect" } | ||
}, | ||
@@ -14,0 +18,0 @@ "title" : "Hal 1", |
@@ -5,3 +5,7 @@ { | ||
"next" : { "href" : "/hal2.json" }, | ||
"collection" : { "href" : "/hal-collection.json" } | ||
"collection" : { "href" : "/hal-collection.json" }, | ||
"headerTest": { "href" : "/headers" }, | ||
"error400" : { "href" : "/error/400" }, | ||
"redirect" : { "href" : "/redirect" }, | ||
"echo": { "href" : "/echo" } | ||
}, | ||
@@ -8,0 +12,0 @@ "title" : "Hal 1", |
@@ -36,2 +36,8 @@ const Client = require('../../lib/client'); | ||
after( async() => { | ||
await client.getResource('/reset').post({}); | ||
}); | ||
}); |
@@ -12,2 +12,12 @@ const Koa = require('koa'); | ||
app.use( | ||
route('/headers') | ||
.get(ctx => { | ||
ctx.response.status = 200; | ||
ctx.response.body = ctx.request.headers; | ||
}) | ||
); | ||
// Reset the server to the beginning state | ||
@@ -23,3 +33,30 @@ app.use( | ||
// HTTP errors as a service | ||
app.use( | ||
route('/error/:code') | ||
.get(ctx => { | ||
ctx.response.status = parseInt(ctx.params.code); | ||
ctx.response.body = ''; | ||
}) | ||
); | ||
// Redirect testing | ||
app.use( | ||
route('/redirect') | ||
.get(ctx => { | ||
ctx.response.redirect('/hal2.json'); | ||
}) | ||
); | ||
// Return request body as we received it | ||
app.use( | ||
route('/echo') | ||
.post(ctx => { | ||
ctx.response.statusCode = 200; | ||
ctx.response.type = ctx.request.headers['content-type']; | ||
ctx.response.body = ctx.req; | ||
}) | ||
); | ||
// Rest stuff! | ||
@@ -26,0 +63,0 @@ app.use( |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35981
5
26
830
321
3