Merge pull request #173 from andrewhayward/api

[WIP] Pull data from remote data source
This commit is contained in:
Mike Larsson 2013-05-09 11:15:19 -07:00
Родитель ca80a354a3 e9aa537530
Коммит 743a7d3169
5 изменённых файлов: 260 добавлений и 27 удалений

195
api.js Normal file
Просмотреть файл

@ -0,0 +1,195 @@
var request = require('request');
var _ = require('underscore');
var DEFAULT_ERROR = 'There was a problem accessing this data.';
var DEFAULT_QUERY = {
page: 1,
pageSize: 12
};
// Core API function
// Loads data into `request.remote`, and intercepts XHR requests
function api (method, default_query) {
return function (req, res, next) {
if (!_.isFunction(method))
method = api[method];
if (!_.isFunction(method)) {
console.error('Method supplied to API not a function');
return next('Supplied method not valid');
}
// Build query from various inputs
var query = _.extend(
DEFAULT_QUERY,
default_query || {},
req.query || {},
req.body || {},
req.params || {}
);
method(query, function(err, data) {
if (!_.isObject(data))
data = {};
if (err)
data.error = err;
if (req.xhr)
return res.json(data);
req.remote = {
err: err,
data: data
}
next();
});
}
}
// Wrapper for API methods
// Normalises input and output
function apiMethod (method) {
return function (query, callback) {
if (_.isFunction(query) && !callback) {
callback = query;
query = {};
}
// Assume any non-object query is being passed in as an ID
if (!_.isObject(query))
query = {id: query};
query = _.defaults(query, DEFAULT_QUERY);
method(query, function(err, data) {
if (!err)
return callback(null, data);
if (!data || _.isString(data))
data = {message: data || DEFAULT_ERROR};
callback(err, data);
});
}
}
// Load data from remote endpoint
var remote = (function() {
function remote (method, path, callback) {
// TODO - put this in settings somewhere
var origin = 'http://openbadger-csol.mofostaging.net';
if (!request[method])
return callback(500, 'Unknown method');
// TODO - need to add ability to pass data through
// TODO - might want to cache this at some point
request[method](origin + path, function(err, response, body) {
if (err)
return callback(500, err);
if (response.statusCode !== 200)
return callback(500, 'Upstream error');
try {
var data = JSON.parse(body);
} catch (e) {
return callback(500, e.message);
}
if (data.status !== 'ok')
return callback(500, data.reason);
callback(null, data);
});
}
_.each(['get', 'post', 'put', 'patch', 'head', 'del'], function(method) {
Object.defineProperty(remote, method, {
enumerable: true,
value: function(path, callback) {
remote(method, path, callback);
}
});
});
return remote;
})();
// Make sure badges returned from remote API
// contain all the information we need
function normalizeBadge (badge, id) {
if (!id)
id = badge.shortname;
if (!badge.id)
badge.id = id;
if (!badge.url)
badge.url = '/badges/' + badge.id;
return badge;
}
api.getBadges = apiMethod(function getBadges (query, callback) {
var pageSize = parseInt(query.pageSize, 10),
page = parseInt(query.page, 10);
if (isNaN(pageSize) || pageSize < 1)
return callback(400, 'Invalid pageSize number');
if (isNaN(page) || page < 1)
return callback(400, 'Invalid page number');
var start = (page - 1) * pageSize,
end = start + pageSize;
remote.get('/v1/badges', function(err, data) {
if (err)
return callback(err, data);
var badges = _.map(data.badges, normalizeBadge);
var pages = Math.ceil(badges.length / pageSize);
if (page > pages)
return callback(404, {
message: 'Page not found',
page: page,
pages: pages
});
callback(null, {
page: page,
pages: pages,
items: badges.slice(start, end)
});
});
});
api.getBadge = apiMethod(function getBadge (query, callback) {
var id = query.id;
if (!id)
return callback(400, 'Invalid badge key');
remote('get', '/v1/badges', function(err, data) {
if (err)
return callback(err, data);
var badge = data.badges[id];
if (!badge)
return callback(404, 'Badge not found');
normalizeBadge(badge, id);
callback(null, {
badge: badge
});
});
});
module.exports = api;

Просмотреть файл

@ -1,3 +1,5 @@
var api = require('../api');
module.exports = function (app) {
function getFilters() {
@ -150,11 +152,30 @@ module.exports = function (app) {
});
app.param('badgeName', function (req, res, next, badgeName) {
// yep, get stuff from the db.
next();
api.getBadge(badgeName, function(err, data) {
if (err)
return next(data.message);
req.params.badge = data.badge;
next();
});
});
app.get('/badges', function (req, res, next) {
app.get('/badges', api('getBadges'), function (req, res, next) {
var err = req.remote.error;
var data = req.remote.data;
if (err)
return next({status: err, message: data.message});
res.render('badges/list.html', {
filters: getFilters(),
items: data.items,
page: data.page,
pages: data.pages
});
/*
var badges = [];
for (var i = 0; i < 12; ++i) {
@ -169,10 +190,13 @@ module.exports = function (app) {
filters: getFilters(),
items: badges
});
*/
});
app.get('/badges/:badgeName', function (req, res, next) {
res.render('badges/single.html');
res.render('badges/single.html', {
badge: req.params.badge
});
});
app.get('/badges/:badgeName/claim', function (req, res, next) {

Просмотреть файл

@ -4,13 +4,14 @@
"description": "The website for the Chicago Summer of Learning, developed by Ocupop and Mozilla.",
"main": "app.js",
"dependencies": {
"express": "~3.1.0",
"nunjucks": "~0.1.8a",
"mysql": "~2.0.0-alpha7",
"sequelize": "~1.6.0",
"tap": "~0.4.1",
"async": "~0.2.6",
"bcrypt": "~0.7.5",
"express": "~3.1.0",
"mysql": "~2.0.0-alpha7",
"nunjucks": "~0.1.8a",
"request": "~2.21.0",
"sequelize": "~1.6.0",
"tap": "~0.4.1",
"underscore": "~1.4.4"
},
"devDependencies": {},

Просмотреть файл

@ -1,22 +1,21 @@
{% extends 'badges/layout.html' %}
{% set pageTitle = 'Some Badge' %}
{% set user = {} %}
{% set pageTitle = badge.name %}
{% block content %}
<div class="row">
<div class="span4">
<img src="/media/images/badge-large.png">
<img src="{{ badge.image }}">
</div>
<div class="span8">
<p><strong>Part of <a href="/programs/ae784f">Some Program</a>, from <a href="/orgs/some-organisation">Some Organisation</a>.</strong></p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>{{ badge.description }}</p>
<p class="text-right">
<a href="/badges/ae784f/claim" class="btn">Claim this badge</a>
<a href="/badges/ae784f/favorite" class="btn"><i class="icon-heart"></i> Add to your favorites</a>
<a href="{{ badge.url }}/claim" class="btn">Claim this badge</a>
<a href="{{ badge.url }}/favorite" class="btn"><i class="icon-heart"></i> Add to your favorites</a>
</p>
</div>
</div>
{#
<h3><a href="/badges">Related Badges</a></h3>
<ul class="thumbnails">
<li class="span3">
@ -68,4 +67,5 @@
</figure>
</li>
</ul>
#}
{% endblock %}

Просмотреть файл

@ -48,7 +48,7 @@
{% include item.template %}
{% else %}
<figure class="thumbnail">
<a href="{{ item.url }}"><img src="{{ item.thumbnail }}"></a>
<a href="{{ item.url }}"><img src="{{ item.image }}"></a>
<figcaption class="caption">
<p>{{ item.description }}</p>
{% block item_actions_wrapper %}
@ -68,15 +68,28 @@
</ul>
{% endblock %}
{% block pagination %}
<nav class="pagination pagination-centered">
<ul>
<li class="disabled"><span>&laquo;</span></li>
<li class="disabled"><span>1</span></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">&raquo;</a></li>
</ul>
</nav>
{% if pages > 1 %}
<nav class="pagination pagination-centered">
<ul>
{% if page == 1 %}
<li class="disabled"><span>&laquo;</span></li>
{% else %}
<li><a href="?page={{ page - 1 }}">&laquo;</a></li>
{% endif %}
{% for pageNum in range(1, (pages + 1)) %}
{% if page == pageNum %}
<li class="disabled"><span>{{ pageNum }}</span></li>
{% else %}
<li><a href="?page={{ pageNum }}">{{ pageNum }}</a></li>
{% endif %}
{% endfor %}
{% if page == pages %}
<li class="disabled"><span>&raquo;</span></li>
{% else %}
<li><a href="?page={{ page + 1 }}">&raquo;</a></li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endblock %}
{% endblock %}