Skip to content

Commit

Permalink
first commit, combined swagger and node-neo4j template at a basic level
Browse files Browse the repository at this point in the history
  • Loading branch information
flipside committed Nov 14, 2013
1 parent 934f48b commit f8525bf
Show file tree
Hide file tree
Showing 35 changed files with 11,559 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so

# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip

# Logs and databases #
######################
*.log
*.sql
*.sqlite

# OS generated files #
######################
.DS_Store*
ehthumbs.db
Icon?
Thumbs.db
*.swp
node_modules
.env
.env_local
.env_hosted
.env_staging
.env_development
.env_production

#directories#
node_modules/

public/app/scripts/vendor/*/
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@ node-neo4j-api-server
=====================

This is an open source node neo4j api server based on node-neo4j-template and swagger-node-express



node-neo4j-template
https://github.com/aseemk/node-neo4j-template/


Swagger-Node-Express
https://github.com/wordnik/swagger-node-express


146 changes: 146 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* Module dependencies.
*/

var express = require('express')
, url = require("url")
, swagger = require("swagger-node-express")
, http = require('http')
, path = require('path')

, routes = require('./routes')

, PORT = process.env.PORT || 3000
, API_STRING = '/api'
, BASE_URL = process.env.BASE_CALLBACK_URL || "http://localhost:"+PORT

, app = express()
, subpath = express();


app.use(API_STRING, subpath);

var petResources = require("./swagger/model_resources");

// configure api subpath
subpath.configure(function () {
// subpath.use(express.bodyParser());
subpath.use(express.json());
subpath.use(express.methodOverride());
subpath.use(app.router);

});

// all environments
app.configure(function () {
app.set('port', PORT);
// app.set('views', __dirname + '/views');
// app.set('view engine', 'jade');
// app.use(express.favicon());
// app.use(express.static(path.join(__dirname, 'public')));
app.use(express.logger('dev'));

// app.use(express.bodyParser()); //
// app.use(express.urlencoded());
app.use(express.json());

app.use(express.methodOverride());
app.use(app.router);


// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}

// app.locals({
// title: 'Node-Neo4j-API' // default title
// });


});



// Set the main handler in swagger to the express app
swagger.setAppHandler(subpath);

swagger.configureSwaggerPaths("", "/api-docs", "");

// default document middleware for swagger/index.html
// app.use('/swagger', function (req, res, next) {
// if (req.url === '/swagger') {
// res.redirect('/swagger/');
// }
// next();
// });


// This is a sample validator. It simply says that for _all_ POST, DELETE, PUT
// methods, the header `api_key` OR query param `api_key` must be equal
// to the string literal `special-key`. All other HTTP ops are A-OK
swagger.addValidator(
function validate(req, path, httpMethod) {
// example, only allow POST for api_key="special-key"
if ("POST" == httpMethod || "DELETE" == httpMethod || "PUT" == httpMethod) {
var apiKey = req.headers["api_key"];
if (!apiKey) {
apiKey = url.parse(req.url,true).query["api_key"]; }
if ("special-key" == apiKey) {
return true;
}
return false;
}
return true;
}
);


var models = require("./swagger/models");

// Add models and methods to swagger
swagger.addModels(models)
.addGet(petResources.findByTags)
.addGet(petResources.findByStatus)
.addGet(petResources.findById)
.addPost(petResources.addPet)
.addPut(petResources.updatePet)
.addDelete(petResources.deletePet);


// Configures the app's base path and api version.
console.log(BASE_URL+API_STRING);
swagger.configure(BASE_URL+API_STRING, "0.1");


// Routes

// Serve up swagger ui at /docs via static route
var docs_handler = express.static(__dirname + '/views/docs/');
app.get(/^\/docs(\/.*)?$/, function(req, res, next) {
if (req.url === '/docs') { // express static barfs on root url w/o trailing slash
res.writeHead(302, { 'Location' : req.url + '/' });
res.end();
return;
}
// take off leading /docs so that connect locates file correctly
req.url = req.url.substr('/docs'.length);
return docs_handler(req, res, next);
});

// app.get('/', routes.site.index);

// app.get('/users', routes.users.list);
// app.post('/users', routes.users.create);
// app.get('/users/:id', routes.users.show);
// app.post('/users/:id', routes.users.edit);
// app.del('/users/:id', routes.users.del);

// app.post('/users/:id/follow', routes.users.follow);
// app.post('/users/:id/unfollow', routes.users.unfollow);



app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
172 changes: 172 additions & 0 deletions models/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// user.js
// User model logic.

var neo4j = require('neo4j');
var db = new neo4j.GraphDatabase(process.env.NEO4J_URL || 'http://localhost:7474');

// constants:

var INDEX_NAME = 'nodes';
var INDEX_KEY = 'type';
var INDEX_VAL = 'user';

var FOLLOWS_REL = 'follows';

// private constructor:

var User = module.exports = function User(_node) {
// all we'll really store is the node; the rest of our properties will be
// derivable or just pass-through properties (see below).
this._node = _node;
};

// public instance properties:

Object.defineProperty(User.prototype, 'id', {
get: function () { return this._node.id; }
});

Object.defineProperty(User.prototype, 'exists', {
get: function () { return this._node.exists; }
});

Object.defineProperty(User.prototype, 'name', {
get: function () {
return this._node.data['name'];
},
set: function (name) {
this._node.data['name'] = name;
}
});

// private instance methods:

User.prototype._getFollowingRel = function (other, callback) {
var query = [
'START user=node({userId}), other=node({otherId})',
'MATCH (user) -[rel?:FOLLOWS_REL]-> (other)',
'RETURN rel'
].join('\n')
.replace('FOLLOWS_REL', FOLLOWS_REL);

var params = {
userId: this.id,
otherId: other.id,
};

db.query(query, params, function (err, results) {
if (err) return callback(err);
var rel = results[0] && results[0]['rel'];
callback(null, rel);
});
};

// public instance methods:

User.prototype.save = function (callback) {
this._node.save(function (err) {
callback(err);
});
};

User.prototype.del = function (callback) {
this._node.del(function (err) {
callback(err);
}, true); // true = yes, force it (delete all relationships)
};

User.prototype.follow = function (other, callback) {
this._node.createRelationshipTo(other._node, 'follows', {}, function (err, rel) {
callback(err);
});
};

User.prototype.unfollow = function (other, callback) {
this._getFollowingRel(other, function (err, rel) {
if (err) return callback(err);
if (!rel) return callback(null);
rel.del(function (err) {
callback(err);
});
});
};

// calls callback w/ (err, following, others) where following is an array of
// users this user follows, and others is all other users minus him/herself.
User.prototype.getFollowingAndOthers = function (callback) {
// query all users and whether we follow each one or not:
var query = [
'START user=node({userId}), other=node:INDEX_NAME(INDEX_KEY="INDEX_VAL")',
'MATCH (user) -[rel?:FOLLOWS_REL]-> (other)',
'RETURN other, COUNT(rel)' // COUNT(rel) is a hack for 1 or 0
].join('\n')
.replace('INDEX_NAME', INDEX_NAME)
.replace('INDEX_KEY', INDEX_KEY)
.replace('INDEX_VAL', INDEX_VAL)
.replace('FOLLOWS_REL', FOLLOWS_REL);

var params = {
userId: this.id,
};

var user = this;
db.query(query, params, function (err, results) {
if (err) return callback(err);

var following = [];
var others = [];

for (var i = 0; i < results.length; i++) {
var other = new User(results[i]['other']);
var follows = results[i]['COUNT(rel)'];

if (user.id === other.id) {
continue;
} else if (follows) {
following.push(other);
} else {
others.push(other);
}
}

callback(null, following, others);
});
};

// static methods:

User.get = function (id, callback) {
db.getNodeById(id, function (err, node) {
if (err) return callback(err);
callback(null, new User(node));
});
};

User.getAll = function (callback) {
db.getIndexedNodes(INDEX_NAME, INDEX_KEY, INDEX_VAL, function (err, nodes) {
// if (err) return callback(err);
// FIXME the index might not exist in the beginning, so special-case
// this error detection. warning: this is super brittle!
// the better and correct thing is to either ensure the index exists
// somewhere by creating it, or just use Neo4j's auto-indexing.
// (the Heroku Neo4j add-on doesn't support auto-indexing currently.)
if (err) return callback(null, []);
var users = nodes.map(function (node) {
return new User(node);
});
callback(null, users);
});
};

// creates the user and persists (saves) it to the db, incl. indexing it:
User.create = function (data, callback) {
var node = db.createNode(data);
var user = new User(node);
node.save(function (err) {
if (err) return callback(err);
node.index(INDEX_NAME, INDEX_KEY, INDEX_VAL, function (err) {
if (err) return callback(err);
callback(null, user);
});
});
};
Loading

0 comments on commit f8525bf

Please sign in to comment.