aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app.js19
-rw-r--r--routes/index.js173
-rw-r--r--views/department.jade2
-rw-r--r--views/employeesInDepartment.jade (renamed from views/employees_in_department.jade)4
-rw-r--r--views/index.jade6
5 files changed, 150 insertions, 54 deletions
diff --git a/app.js b/app.js
index c9e8a1b..036fe61 100644
--- a/app.js
+++ b/app.js
@@ -14,6 +14,11 @@ app.configure(function(){
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
+ // Unfuck the stuff that express injects
+ app.use(function (req, res, next) {
+ res.removeHeader("X-Powered-By");
+ next();
+ });
app.use(urlgenerator);
app.use(accept);
app.use(app.router);
@@ -42,7 +47,7 @@ function urlgenerator(req, res, next) {
department: function(dept_no) {
return 'http://' + host + '/department/' + dept_no;
},
- employees_in_department: function(dept_no, query) {
+ employeesInDepartment: function(dept_no, query) {
return url.format({
protocol: 'http',
host: host,
@@ -66,12 +71,12 @@ function urlgenerator(req, res, next) {
next();
}
-app.get('/', routes.index);
-app.get('/department', routes.departments);
-app.get('/department/:dept_no', routes.department);
-app.get('/department/:dept_no/employees', routes.employees_in_department);
-app.get('/employee', routes.employees);
-app.get('/employee/:emp_no', routes.employee);
+app.all('/', routes.index);
+app.all('/department', routes.departments);
+app.all('/department/:dept_no', routes.department);
+app.all('/department/:dept_no/employees', routes.employeesInDepartment);
+app.all('/employee', routes.employees);
+app.all('/employee/:emp_no', routes.employee);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
diff --git a/routes/index.js b/routes/index.js
index 23af53d..ba2ca23 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -45,23 +45,41 @@ function pager(req, count) {
function after(res, callback) {
return function(err, queryResult) {
if(err) {
- res.writeHead(500, {"Content-Type" : "text/plain"});
- return res.end("Error! " + util.inspect(err))
+ res.writeHead(500, {'Content-Type' : 'text/plain'});
+ return res.end('Error! ' + util.inspect(err))
}
callback(queryResult)
}
}
-exports.index = function(req, res) {
- switch(req.accept.types.getBestMatch(["text/html", "application/vnd.collection+json"])) {
- case "text/html":
+function method(handlers) {
+ return function(req, res) {
+ var handler = handlers[req.method];
+ if(handler) {
+ return handler(req, res)
+ }
+ // I'm just too lazy to include two rows
+ if(req.method == 'HEAD') {
+ var handler = handlers[req.method];
+ if(handler) {
+ return handler(req, res)
+ }
+ }
+ res.header('Allow', _.keys(handlers));
+ return res.send(405);
+ }
+}
+
+function getIndex(req, res) {
+ switch(req.accept.types.getBestMatch(['text/html', 'application/vnd.collection+json'])) {
+ case 'text/html':
res.render('index', {
title: 'Employee DB',
urlgenerator: res.urlgenerator
});
break;
- case "application/vnd.collection+json":
- default:
+ case 'application/vnd.collection+json':
+ case '*/*':
var c = {collection: {
links: [ {
rel: 'departments',
@@ -76,10 +94,16 @@ exports.index = function(req, res) {
res.contentType('application/vnd.collection+json');
res.send(JSON.stringify(collection_json.fromObject(c)), 200);
break;
+ default:
+ res.send(406);
}
};
-exports.departments = function(req, res) {
+exports.index = method({
+ GET: getIndex
+});
+
+function getDepartments(req, res) {
pg.connect(process.env.DATABASE_URL, function(err, client) {
if(err) throw err;
var sql = 'SELECT n_live_tup FROM pg_stat_user_tables WHERE relname=\'departments\'';
@@ -87,45 +111,53 @@ exports.departments = function(req, res) {
client.query(sql, after(res, function(rs) {
var p = pager(req, parseInt(rs.rows[0].n_live_tup));
client.query(sql2, [ p.offset, p.limit ], after(res, function(rs2) {
- switch(req.accept.types.getBestMatch(["text/html", "application/vnd.collection+json"])) {
- case "text/html":
+ switch(req.accept.types.getBestMatch(['text/html', 'application/vnd.collection+json'])) {
+ case 'text/html':
res.render('departments', {
title: 'Department List',
urlgenerator: res.urlgenerator, pager: p, query: req.query,
departments: rs2.rows
});
break;
- case "application/vnd.collection+json":
- default:
+ case 'application/vnd.collection+json':
+ case '*/*':
var c = {collection: {
href: res.urlgenerator.departments(),
items: mapDepartments(res, rs2.rows)
}};
res.contentType('application/vnd.collection+json');
res.send(JSON.stringify(collection_json.fromObject(c)), 200);
+ break;
+ default:
+ res.send(406);
}
}));
}));
});
};
-exports.department = function(req, res) {
+exports.departments = method({
+ GET: getDepartments
+});
+
+function getDepartment(req, res) {
var dept_no = req.params.dept_no;
- switch(req.accept.types.getBestMatch(["text/html", "application/vnd.collection+json"])) {
- case "text/html":
+ switch(req.accept.types.getBestMatch(['text/html', 'application/vnd.collection+json'])) {
+ case 'text/html':
res.render('department', {
title: 'Department ' + dept_no,
urlgenerator: res.urlgenerator,
dept_no: dept_no
});
- case "application/vnd.collection+json":
- default:
+ break;
+ case 'application/vnd.collection+json':
+ case '*/*':
var c = {collection: {
href: res.urlgenerator.department(dept_no),
links: [ {
rel: 'employees',
prompt: 'Employees in department ' + dept_no,
- href: res.urlgenerator.employees_in_department(dept_no)
+ href: res.urlgenerator.employeesInDepartment(dept_no)
},{
rel: 'departments',
prompt: 'All departments',
@@ -134,10 +166,17 @@ exports.department = function(req, res) {
}};
res.contentType('application/vnd.collection+json');
res.send(JSON.stringify(collection_json.fromObject(c)), 200);
+ break;
+ default:
+ res.send(406);
}
}
-exports.employees_in_department = function(req, res) {
+exports.department = method({
+ GET: getDepartment
+});
+
+function getEmployeesInDepartment(req, res) {
pg.connect(process.env.DATABASE_URL, function(err, client) {
if(err) throw err;
var dept_no = req.params.dept_no;
@@ -149,18 +188,19 @@ exports.employees_in_department = function(req, res) {
client.query(sql1, [ dept_no ], after(res, function(rs1) {
var p = pager(req, parseInt(rs1.rows[0].count));
client.query(sql2, [ dept_no, p.offset, p.limit ], after(res, function(rs2) {
- switch(req.accept.types.getBestMatch(["text/html", "application/vnd.collection+json"])) {
- case "text/html":
- res.render('employees_in_department', {
+ switch(req.accept.types.getBestMatch(['text/html', 'application/vnd.collection+json'])) {
+ case 'text/html':
+ res.render('employeesInDepartment', {
title: 'Department: #' + dept_no,
urlgenerator: res.urlgenerator, pager: p, query: req.query,
dept_no: dept_no,
employees: rs2.rows
});
- case "application/vnd.collection+json":
- default:
+ break;
+ case 'application/vnd.collection+json':
+ case '*/*':
var c = {collection: {
- href: res.urlgenerator.employees_in_department(dept_no),
+ href: res.urlgenerator.employeesInDepartment(dept_no),
links: [ {
rel: 'department',
prompt: 'Department: #' + dept_no,
@@ -170,13 +210,22 @@ exports.employees_in_department = function(req, res) {
}};
res.contentType('application/vnd.collection+json');
res.send(JSON.stringify(collection_json.fromObject(c)), 200);
+ break;
+ default:
+ res.send(406);
}
}));
}));
});
};
-exports.employees = function(req, res) {
+exports.employeesInDepartment = method({
+ GET: getEmployeesInDepartment
+});
+
+function getEmployees(req, res) {
+ var head = req.method == 'HEAD';
+
pg.connect(process.env.DATABASE_URL, function(err, client) {
if(err) throw err;
var emp_no = req.params.emp_no;
@@ -206,16 +255,27 @@ exports.employees = function(req, res) {
sql2Params.push(p.offset);
sql2Params.push(p.limit);
client.query(sql2, sql2Params, after(res, function(rs2) {
- switch(req.accept.types.getBestMatch(["text/html", "application/vnd.collection+json"])) {
- case "text/html":
- res.render('employees', {
- title: 'Employee List',
- urlgenerator: res.urlgenerator, pager: p, query: req.query,
- employees: rs2.rows,
- query: req.query
- });
- case "application/vnd.collection+json":
- default:
+ switch(req.accept.types.getBestMatch(['text/html', 'application/vnd.collection+json'])) {
+ case 'text/html':
+ if(head) {
+ // Can't be bothered to calculate Content-Length even if I
+ // should..
+ res.writeHead(200, {
+ 'Content-Type': 'text/html',
+ });
+ res.end();
+ }
+ else {
+ res.render('employees', {
+ title: 'Employee List',
+ urlgenerator: res.urlgenerator, pager: p, query: req.query,
+ employees: rs2.rows,
+ query: req.query
+ });
+ }
+ break;
+ case 'application/vnd.collection+json':
+ case '*/*':
var links = [];
if(_.isNumber(p.prevOffset)) {
links.push({
@@ -240,41 +300,66 @@ exports.employees = function(req, res) {
name: 'employee-search',
prompt: 'Employee search',
data: [
- { name: "name" }
+ { name: 'name' }
]
}],
items: mapEmployees(res, rs2.rows)
}};
- res.contentType('application/vnd.collection+json');
- res.send(JSON.stringify(collection_json.fromObject(c)), 200);
+ var text = JSON.stringify(collection_json.fromObject(c));
+ var headers = {
+ 'Content-Type': 'application/vnd.collection+json',
+ 'Content-Length': text.length
+ };
+ if(head) {
+ res.writeHead(200, headers);
+ res.end();
+ }
+ else {
+ res.send(text, headers, 200);
+ }
+ break;
+ default:
+ res.send(406);
}
}));
}));
});
};
-exports.employee = function(req, res) {
+exports.employees = method({
+ GET: getEmployees
+});
+
+function getEmployee(req, res) {
pg.connect(process.env.DATABASE_URL, function(err, client) {
if(err) throw err;
var emp_no = req.params.emp_no;
var sql = 'SELECT * FROM employees WHERE employees.emp_no=$1';
client.query(sql, [ emp_no ], after(res, function(rs) {
- switch(req.accept.types.getBestMatch(["text/html", "application/vnd.collection+json"])) {
- case "text/html":
+ switch(req.accept.types.getBestMatch(['text/html', 'application/vnd.collection+json', '*/*'])) {
+ case 'text/html':
res.render('employee', {
title: 'Employee: #' + emp_no,
urlgenerator: res.urlgenerator,
employee: rs.rows[0]
});
- case "application/vnd.collection+json":
- default:
+ break;
+ case 'application/vnd.collection+json':
+ case '*/*':
var c = {collection: {
href: res.urlgenerator.employee(emp_no),
items: [ mapRow(rs.rows[0]) ],
}};
res.contentType('application/vnd.collection+json');
res.send(JSON.stringify(collection_json.fromObject(c)), 200);
+ break;
+ default:
+ res.send(406);
}
}))
});
}
+
+exports.employee = method({
+ GET: getEmployee
+});
diff --git a/views/department.jade b/views/department.jade
index 32952a9..a29ef3a 100644
--- a/views/department.jade
+++ b/views/department.jade
@@ -10,6 +10,6 @@ block content
| >>
a(href=urlgenerator.department(dept_no)) Department ##{dept_no}
- p: a(href=urlgenerator.employees_in_department(dept_no)) Employees in this department
+ p: a(href=urlgenerator.employeesInDepartment(dept_no)) Employees in this department
p TODO: add links to manager and department name.
diff --git a/views/employees_in_department.jade b/views/employeesInDepartment.jade
index 277c750..f889c0c 100644
--- a/views/employees_in_department.jade
+++ b/views/employeesInDepartment.jade
@@ -11,12 +11,12 @@ block content
| >>
a(href=urlgenerator.department(dept_no)) Department ##{dept_no}
| >>
- a(href=urlgenerator.employees_in_department(dept_no)) Employees in Department ##{dept_no}
+ a(href=urlgenerator.employeesInDepartment(dept_no)) Employees in Department ##{dept_no}
table
each employee in employees
tr
td: a(href=urlgenerator.employee(employee.emp_no)) #{employee.first_name} #{employee.last_name}
- - var f = function(offset) { return urlgenerator.employees_in_department(dept_no, offset) }
+ - var f = function(offset) { return urlgenerator.employeesInDepartment(dept_no, offset) }
mixin pager(pager, f, query)
diff --git a/views/index.jade b/views/index.jade
index 7ff1bb1..363355c 100644
--- a/views/index.jade
+++ b/views/index.jade
@@ -25,6 +25,12 @@ block content
code text/html
| you will be served
code application/vnd.collection+json
+ | if you include
+ code */*
+ | in your
+ code Accept
+ | header. If you request only unsupported types, you'll get a
+ a(href='http://httpstatus.es/406') 406
| .
h4 Exploring with curl