UNPKG

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