Permalink
Browse files

seraph#readLink, seraph#link

  • Loading branch information...
1 parent d116ada commit 9ff6a46d9aea03702c8880747983a09455e0b850 @jonpacker jonpacker committed Jun 11, 2012
Showing with 122 additions and 7 deletions.
  1. +3 −0 .gitignore
  2. +115 −4 lib/seraph.js
  3. +4 −3 test/seraph.js
View
@@ -2,4 +2,7 @@ node_modules/
db/
.DS_Store
*.swp
+*.swo
+*.swm
+*.swn
npm-debug.log
View
@@ -1,4 +1,5 @@
var naan = require('naan');
+var async = require('async');
var request = require('request');
var _ = require('underscore');
var util = require('util');
@@ -86,6 +87,7 @@ var seraph = {
};
if (data) requestOpts.json = data;
+ callback = _.bind(callback, this);
// Allows mocking
(this._request || request)(requestOpts, function(err, response, body) {
@@ -122,12 +124,35 @@ var seraph = {
}
},
+ /**
+ * Take the end off a url and parse it as an int
+ */
+ _extractId: function(location) {
+ var matches = location.match(/\/(\d+)$/);
+ if (!matches) {
+ return null;
+ }
+
+ return parseInt(matches[1], 10);
+ },
+
+ /**
+ * Returns the url to an entity given an id
+ */
+ _location: function(options, type, id) {
+ return util.format('%s/data/db/%s/%d', options.endpoint, type, id);
+ },
+
/**
* Save or update an object. If a new object is passed, the callback will
* return a copy of that object with the <options.id> key set to the id of the
* created object.
*/
save: function(options, obj, callback) {
+ if (Array.isArray(obj)) {
+ return async.map(obj, naan.b.curry(this, this.save, options), callback);
+ }
+
var id = this._getId(options, obj, true);
if (id == null) {
@@ -150,11 +175,8 @@ var seraph = {
return callback(err);
}
- var location = response.headers.location;
- var locationComponents = location.match(/\/node\/(\d+)$/);
-
var result = _.extend({}, obj);
- result[options.id] = parseInt(locationComponents[1]);
+ result[options.id] = this._extractId(response.headers.location);
callback(null, result);
});
@@ -184,6 +206,10 @@ var seraph = {
* Read an object's properties. Maps to GET node/{id}/properties.
*/
read: function(options, id, callback) {
+ if (Array.isArray(id)) {
+ return async.map(id, naan.b.curry(this, this.read, options), callback);
+ }
+
id = this._getId(options, id);
if (!id) {
return callback(new Error("Invalid ID"));
@@ -205,6 +231,10 @@ var seraph = {
* TODO: delete all relationships as well
*/
delete: function(options, id, callback) {
+ if (Array.isArray(id)) {
+ return async.map(id, naan.b.curry(this, this.delete, options), callback);
+ }
+
id = this._getId(options, id);
if (!id) {
return callback(new Error("Invalid ID"));
@@ -214,6 +244,87 @@ var seraph = {
this.call(options, endpoint, 'DELETE', function(err) {
callback(err);
});
+ },
+
+ /**
+ * Create a relationship link object from the given link data returned from
+ * the neo4j server
+ */
+ _createLinkObject: function(linkData) {
+ return {
+ start: this._extractId(linkData.start),
+ end: this._extractId(linkData.end),
+ type: linkData.type,
+ properties: linkData.data,
+ id: this._extractId(linkData.self)
+ };
+ },
+
+ readLink: function(options, id, callback) {
+ if (Array.isArray(id)) {
+ var boundReadLink = naan.b.curry(this, this.readLink, options);
+ return async.map(id, boundReadLink, callback);
+ }
+
+ id = this._getId(options, id);
+
+ if (!id) {
+ return callback(new Error("Invalid ID"));
+ }
+
+ var endpoint = util.format('relationship/%d', id);
+ this.call(options, endpoint, function(err, linkData) {
+ if (err) {
+ callback(err);
+ } else {
+ callback(null, this._createLinkObject(linkData));
+ }
+ });
+ },
+
+ /**
+ * Link two objects together. maps to POST node/{first}/relationships
+ */
+ link: function(options, first, relationshipName, second, props, callback) {
+ if (Array.isArray(first)) {
+ var args = [ options, relationshipName, second, props ];
+ var linker = naan.b.ecurry(this, this.link, args, [0, 2, 3, 4]);
+ return async.map(first, linker, callback);
+ } else if (Array.isArray(second)) {
+ var args = [ options, first, relationshipName, props ];
+ var linker = naan.b.ecurry(this, this.link, args, [0, 1, 2, 4]);
+ return async.map(second, linker, callback);
+ }
+
+ var firstId = this._getId(options, first),
+ secondId = this._getId(options, second);
+
+ if (typeof props === 'function') {
+ callback = props;
+ props = null;
+ }
+
+ if (!firstId || !secondId) {
+ return callback(new Error("Invalid ID"));
+ }
+
+ var request = {
+ to: this._location(options, 'node', secondId),
+ type: relationshipName,
+ };
+
+ if (props) {
+ request['data'] = props;
+ }
+
+ var endpoint = util.format('node/%d/relationships', firstId);
+ this.call(options, endpoint, 'POST', request, function(err, linkData) {
+ if (err) {
+ callback(err);
+ } else {
+ callback(null, this._createLinkObject(linkData));
+ }
+ });
}
};
View
@@ -174,6 +174,7 @@ describe('CRUD Operations', function() {
assert.ok(!err);
db.read([user1.id, user2.id], function(err) {
assert.ok(!!err);
+ done();
});
});
}
@@ -198,12 +199,12 @@ describe('CRUD Operations', function() {
assert.equal(link.end, user2.id);
assert.equal(link.type, 'coworker');
assert.deepEqual(link.properties, {});
- assert.ok(link.id);
- done(null, link);
+ assert.ok(link.id != null);
+ done(null, link, user1, user2);
});
}
- function readLink(link, done) {
+ function readLink(link, user1, user2, done) {
var linkId = link.id;
db.readLink(link.id, function(err, link) {
assert.ok(!err);

0 comments on commit 9ff6a46

Please sign in to comment.