forked from cujojs/rest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hateoas.js
138 lines (125 loc) · 3.73 KB
/
hateoas.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
* Copyright 2012-2016 the original author or authors
* @license MIT, see LICENSE.txt for details
*
* @author Scott Andrews
*/
'use strict';
var interceptor, pathPrefix, rfc5988LinkParser, find;
interceptor = require('../interceptor');
pathPrefix = require('./pathPrefix');
rfc5988LinkParser = require('../parsers/rfc5988');
find = require('../util/find');
/**
* [Experimental]
*
* Supports 'Hypertext As The Engine Of Application State' style
* services by indexing the 'links' property from the entity to make
* accessing links via the 'rel' attribute easier.
*
* Links are index in two ways:
* 1. as link's 'rel' which when accessed issues a request for the
* linked resource. A promise for the related resourse is expected
* to be returned.
* 2. as link's 'rel' with 'Link' appended, as a reference to the link
* object
*
* The 'Link' response header is also parsed for related resources
* following rfc5988. The values parsed from the headers are indexed
* into the response.links object.
*
* Also defines a 'clientFor' factory function that creates a new
* client configured to communicate with a related resource.
*
* The client for the resoruce reference and the 'clientFor' function
* can be provided by the 'client' config property.
*
* Index links are exposed by default on the entity. A child object may be
* configed by the 'target' config property.
*
* @param {Client} [client] client to wrap
* @param {string} [config.target=''] property to create on the entity and
* parse links into. If empty, the response entity is used directly.
* @param {Client} [config.client=request.originator] the parent client to
* use when creating clients for a linked resources. Defaults to the
* request's originator if available, otherwise the current interceptor's
* client
*
* @returns {Client}
*/
module.exports = interceptor({
init: function (config) {
config.target = config.target || '';
return config;
},
response: function (response, config, meta) {
var client;
client = config.client || (response.request && response.request.originator) || meta.client;
function apply(target, links) {
links.forEach(function (link) {
Object.defineProperty(target, link.rel + 'Link', {
enumerable: false,
configurable: true,
value: link
});
Object.defineProperty(target, link.rel, {
enumerable: false,
configurable: true,
get: function () {
var response = client({ path: link.href });
Object.defineProperty(target, link.rel, {
enumerable: false,
configurable: true,
value: response
});
return response;
}
});
});
// if only Proxy was well supported...
Object.defineProperty(target, 'clientFor', {
enumerable: false,
value: function clientFor(rel, parentClient) {
return pathPrefix(
parentClient || client,
{ prefix: target[rel + 'Link'].href }
);
}
});
}
function parseLinkHeaders(headers) {
var links = [];
[].concat(headers).forEach(function (header) {
try {
links = links.concat(rfc5988LinkParser.parse(header));
}
catch (e) {
// ignore
// TODO consider a debug mode that logs
}
});
return links;
}
if (response.headers && response.headers.Link) {
response.links = response.links || {};
apply(response.links, parseLinkHeaders(response.headers.Link));
}
find.findProperties(response.entity, 'links', function (obj, host) {
var target;
if (Array.isArray(host.links)) {
if (config.target === '') {
target = host;
}
else {
target = {};
Object.defineProperty(host, config.target, {
enumerable: false,
value: target
});
}
apply(target, host.links);
}
});
return response;
}
});