Skip to content

Commit 4fc72ef

Browse files
committed
Added support for arbitrary APIs. Fixes #4.
1 parent b891677 commit 4fc72ef

File tree

7 files changed

+337
-102
lines changed

7 files changed

+337
-102
lines changed

README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,19 @@ Performs a database query using the given *query* and *bindVars*, then passes a
148148
* *query*: an AQL query string or a [query builder](https://npmjs.org/package/aqb) instance.
149149
* *bindVars* (optional): an object with the variables to bind the query to.
150150

151+
### Arbitrary HTTP endpoints
152+
153+
#### database.endpoint([path[, headers]])
154+
155+
Returns a new *Endpoint* instance for the given path (relative to the database) that can be used to perform arbitrary HTTP requests.
156+
157+
*Parameter*
158+
159+
* *path* (optional): relative URL of the endpoint.
160+
* *headers* (optional): default headers that should be send with each request to the endpoint.
161+
162+
If *path* is missing, the endpoint will refer to the base URL of the database.
163+
151164
## Cursor API
152165

153166
*Cursor* instances provide an abstraction over the HTTP API's limitations. Unless a method explicitly exhausts the cursor, the driver will only fetch as many batches from the server as necessary. Unlike the server-side cursors, *Cursor* instances can also be rewinded.
@@ -202,6 +215,117 @@ Equivalent to *Array.prototype.reduce*.
202215

203216
Rewinds the cursor.
204217

218+
## Endpoint API
219+
220+
*Endpoint* instances provide access for arbitrary HTTP requests. This allows easy access to Foxx apps and other HTTP APIs not covered by the driver itself.
221+
222+
### endpoint.get([path,] [qs,] callback)
223+
224+
Performs a GET request to the given URL and passes the server response to the given callback.
225+
226+
*Parameter*
227+
228+
* *path* (optional): the endpoint-relative URL for the request.
229+
* *qs* (optional): the query string for the request.
230+
231+
If *path* is missing, the request will be made to the base URL of the endpoint.
232+
233+
If *qs* is an object, it will be translated to a query string.
234+
235+
### endpoint.post([path,] [body, [qs,]] callback)
236+
237+
Performs a POST request to the given URL and passes the server response to the given callback.
238+
239+
*Parameter*
240+
241+
* *path* (optional): the endpoint-relative URL for the request.
242+
* *body* (optional): the request body for the request.
243+
* *qs* (optional): the query string for the request.
244+
245+
If *path* is missing, the request will be made to the base URL of the endpoint.
246+
247+
If *body* is an object, it will be converted to JSON.
248+
249+
If *qs* is an object, it will be translated to a query string.
250+
251+
### endpoint.put([path,] [body, [qs,]] callback)
252+
253+
Performs a PUT request to the given URL and passes the server response to the given callback.
254+
255+
*Parameter*
256+
257+
* *path* (optional): the endpoint-relative URL for the request.
258+
* *body* (optional): the request body for the request.
259+
* *qs* (optional): the query string for the request.
260+
261+
If *path* is missing, the request will be made to the base URL of the endpoint.
262+
263+
If *body* is an object, it will be converted to JSON.
264+
265+
If *qs* is an object, it will be translated to a query string.
266+
267+
### endpoint.patch([path,] [body, [qs,]] callback)
268+
269+
Performs a PATCH request to the given URL and passes the server response to the given callback.
270+
271+
*Parameter*
272+
273+
* *path* (optional): the endpoint-relative URL for the request.
274+
* *body* (optional): the request body for the request.
275+
* *qs* (optional): the query string for the request.
276+
277+
If *path* is missing, the request will be made to the base URL of the endpoint.
278+
279+
If *body* is an object, it will be converted to JSON.
280+
281+
If *qs* is an object, it will be translated to a query string.
282+
283+
### endpoint.delete([path,] [qs,] callback)
284+
285+
Performs a DELETE request to the given URL and passes the server response to the given callback.
286+
287+
*Parameter*
288+
289+
* *path* (optional): the endpoint-relative URL for the request.
290+
* *qs* (optional): the query string for the request.
291+
292+
If *path* is missing, the request will be made to the base URL of the endpoint.
293+
294+
If *qs* is an object, it will be translated to a query string.
295+
296+
### endpoint.head([path,] [qs,] callback)
297+
298+
Performs a HEAD request to the given URL and passes the server response to the given callback.
299+
300+
*Parameter*
301+
302+
* *path* (optional): the endpoint-relative URL for the request.
303+
* *qs* (optional): the query string for the request.
304+
305+
If *path* is missing, the request will be made to the base URL of the endpoint.
306+
307+
If *qs* is an object, it will be translated to a query string.
308+
309+
### endpoint.request(opts, callback)
310+
311+
Performs an arbitrary request to the given URL and passes the server response to the given callback.
312+
313+
*Parameter*
314+
315+
* *opts*: an object with the following properties:
316+
* *path*: the endpoint-relative URL for the request.
317+
* *absolutePath* (optional): whether the *path* is relative to the connection's base URL instead of the database. Default: `false`.
318+
* *body* (optional): the request body.
319+
* *qs* (optional): the query string.
320+
* *headers* (optional): an object with additional HTTP headers to send with the request.
321+
* *method* (optional): HTTP method to use. Default: `"GET"`.
322+
323+
If *opts.path* is missing, the request will be made to the base URL of the endpoint.
324+
325+
If *opts.body* is an object, it will be converted to JSON.
326+
327+
If *opts.qs* is an object, it will be translated to a query string.
328+
205329
## Collection API
206330

207331
### Getting information about the collection

lib/collection.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = extend(
1818

1919
function BaseCollection(connection, body) {
2020
this._connection = connection;
21+
this._api = this._connection.endpoint('_api');
2122
extend(this, body);
2223
delete this.code;
2324
delete this.error;
@@ -37,7 +38,7 @@ extend(BaseCollection.prototype, {
3738
}
3839
if (!callback) callback = noop;
3940
var self = this;
40-
self._connection.get('collection/' + self.name + '/' + path, function (err, body) {
41+
self._api.get('collection/' + self.name + '/' + path, function (err, body) {
4142
if (err) callback(err);
4243
else {
4344
if (update) {
@@ -52,7 +53,7 @@ extend(BaseCollection.prototype, {
5253
_put: function (path, data, update, callback) {
5354
if (!callback) callback = noop;
5455
var self = this;
55-
self._connection.put('collection/' + self.name + '/' + path, data, function (err, body) {
56+
self._api.put('collection/' + self.name + '/' + path, data, function (err, body) {
5657
if (err) callback(err);
5758
else {
5859
if (update) extend(self, body);
@@ -102,7 +103,7 @@ extend(BaseCollection.prototype, {
102103
drop: function (callback) {
103104
if (!callback) callback = noop;
104105
var self = this;
105-
self._connection.delete('collection/' + self.name, function (err, body) {
106+
self._api.delete('collection/' + self.name, function (err, body) {
106107
if (err) callback(err);
107108
else callback(null);
108109
});
@@ -114,7 +115,7 @@ extend(BaseCollection.prototype, {
114115
}
115116
if (!callback) callback = noop;
116117
opts = extend({}, opts, {collection: this.name});
117-
this._connection.put(this._documentPath(documentHandle), data, opts, function (err, body) {
118+
this._api.put(this._documentPath(documentHandle), data, opts, function (err, body) {
118119
if (err) callback(err);
119120
else callback(null, body);
120121
});
@@ -126,7 +127,7 @@ extend(BaseCollection.prototype, {
126127
}
127128
if (!callback) callback = noop;
128129
opts = extend({}, opts, {collection: this.name});
129-
this._connection.patch(this._documentPath(documentHandle), data, opts, function (err, body) {
130+
this._api.patch(this._documentPath(documentHandle), data, opts, function (err, body) {
130131
if (err) callback(err);
131132
else callback(null, body);
132133
});
@@ -138,7 +139,7 @@ extend(BaseCollection.prototype, {
138139
}
139140
if (!callback) callback = noop;
140141
opts = extend({}, opts, {collection: this.name});
141-
this._connection.delete(this._documentPath(documentHandle), opts, function (err, body) {
142+
this._api.delete(this._documentPath(documentHandle), opts, function (err, body) {
142143
if (err) callback(err);
143144
else callback(null);
144145
});
@@ -149,7 +150,7 @@ extend(BaseCollection.prototype, {
149150
type = undefined;
150151
}
151152
if (!callback) callback = noop;
152-
this._connection.get('document', {
153+
this._api.get('document', {
153154
type: type || 'id',
154155
collection: this.name
155156
}, function (err, body) {
@@ -171,14 +172,14 @@ extend(DocumentCollection.prototype, {
171172
if (documentHandle.indexOf('/') === -1) {
172173
documentHandle = this.name + '/' + documentHandle;
173174
}
174-
this._connection.get('document/' + documentHandle, function (err, body) {
175+
this._api.get('document/' + documentHandle, function (err, body) {
175176
if (err) callback(err);
176177
else callback(null, body);
177178
});
178179
},
179180
save: function (data, callback) {
180181
if (!callback) callback = noop;
181-
this._connection.post('document/', data, {
182+
this._api.post('document/', data, {
182183
collection: this.name
183184
}, function (err, body) {
184185
if (err) callback(err);
@@ -199,14 +200,14 @@ extend(EdgeCollection.prototype, {
199200
if (documentHandle.indexOf('/') === -1) {
200201
documentHandle = this.name + '/' + documentHandle;
201202
}
202-
this._connection.get('edge/' + documentHandle, function (err, body) {
203+
this._api.get('edge/' + documentHandle, function (err, body) {
203204
if (err) callback(err);
204205
else callback(null, body);
205206
});
206207
},
207208
save: function (data, fromId, toId, callback) {
208209
if (!callback) callback = noop;
209-
this._connection.post('edge/', data, {
210+
this._api.post('edge/', data, {
210211
collection: this.name,
211212
from: fromId,
212213
to: toId
@@ -217,7 +218,7 @@ extend(EdgeCollection.prototype, {
217218
},
218219
_edges: function (vertex, direction, callback) {
219220
if (!callback) callback = noop;
220-
this._connection.get('edges/' + this.name, {
221+
this._api.get('edges/' + this.name, {
221222
vertex: vertex,
222223
direction: direction
223224
}, function (err, body) {

lib/connection.js

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var noop = require('./util/noop'),
66
querystring = require('querystring'),
77
request = require('request'),
88
ArangoError = require('./error'),
9+
Endpoint = require('./endpoint'),
910
jsonMime = /\/(json|javascript)(\W|$)/;
1011

1112
module.exports = Connection;
@@ -28,6 +29,9 @@ Connection.defaults = {
2829
};
2930

3031
extend(Connection.prototype, {
32+
endpoint: function (path) {
33+
return new Endpoint(this, path);
34+
},
3135
request: function (opts, callback) {
3236
if (!callback) callback = noop;
3337
if (!opts) opts = {};
@@ -39,20 +43,16 @@ extend(Connection.prototype, {
3943
headers['content-type'] = 'application/json';
4044
}
4145

42-
var url = this.config.url + '/_db/' + this.config.databaseName + '/_api/' + opts.path;
43-
while (true) {
44-
var oldUrl = url;
45-
url = url.replace(/\/[^\/]+\/..\//, '/');
46-
if (oldUrl === url) break;
47-
}
46+
var url = this.config.url;
47+
if (!opts.absolutePath) url += '/_db/' + this.config.databaseName;
48+
url += opts.path ? (opts.path.charAt(0) === '/' ? '' : '/') + opts.path : '';
4849
if (opts.qs) url += '?' + (typeof opts.qs === 'string' ? opts.qs : querystring.stringify(opts.qs));
4950

5051
request({
5152
url: url,
5253
headers: extend(headers, this.config.headers, opts.headers),
5354
method: (opts.method || 'get').toUpperCase(),
54-
body: body,
55-
encoding: 'utf-8'
55+
body: body
5656
}, function (err, response, rawBody) {
5757
if (err) callback(err);
5858
else if (!response.headers['content-type'].match(jsonMime)) callback(null, rawBody);
@@ -65,59 +65,5 @@ extend(Connection.prototype, {
6565
catch(e) {callback(e);}
6666
}
6767
});
68-
},
69-
get: function (path, data, callback) {
70-
if (typeof data === 'function') {
71-
callback = data;
72-
data = undefined;
73-
}
74-
this.request({path: path, qs: data}, callback);
75-
},
76-
post: function (path, data, qs, callback) {
77-
if (typeof qs === 'function') {
78-
callback = qs;
79-
qs = undefined;
80-
}
81-
if (typeof data === 'function') {
82-
callback = data;
83-
data = undefined;
84-
}
85-
this.request({path: path, body: data, qs: qs, method: 'post'}, callback);
86-
},
87-
put: function (path, data, qs, callback) {
88-
if (typeof qs === 'function') {
89-
callback = qs;
90-
qs = undefined;
91-
}
92-
if (typeof data === 'function') {
93-
callback = data;
94-
data = undefined;
95-
}
96-
this.request({path: path, body: data, qs: qs, method: 'put'}, callback);
97-
},
98-
patch: function (path, data, qs, callback) {
99-
if (typeof qs === 'function') {
100-
callback = qs;
101-
qs = undefined;
102-
}
103-
if (typeof data === 'function') {
104-
callback = data;
105-
data = undefined;
106-
}
107-
this.request({path: path, body: data, qs: qs, method: 'patch'}, callback);
108-
},
109-
delete: function (path, data, callback) {
110-
if (typeof data === 'function') {
111-
callback = data;
112-
data = undefined;
113-
}
114-
this.request({path: path, qs: data, method: 'delete'}, callback);
115-
},
116-
head: function (path, data, callback) {
117-
if (typeof data === 'function') {
118-
callback = data;
119-
data = undefined;
120-
}
121-
this.request({path: path, qs: data, method: 'head'}, callback);
12268
}
12369
});

lib/cursor.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = ArrayCursor;
99
function ArrayCursor(connection, body) {
1010
this.extra = body.extra;
1111
this._connection = connection;
12+
this._api = this._connection.endpoint('_api');
1213
this._result = body.result;
1314
this._hasMore = Boolean(body.hasMore);
1415
this._id = body.id;
@@ -30,7 +31,7 @@ extend(ArrayCursor.prototype, {
3031
var self = this;
3132
if (!self._hasMore) callback(null, self);
3233
else {
33-
self._connection.put('cursor/' + this._id, function (err, body) {
34+
self._api.put('cursor/' + this._id, function (err, body) {
3435
if (err) callback(err);
3536
else {
3637
self._result.push.apply(self._result, body.result);

0 commit comments

Comments
 (0)