From c38f215db5ab2ce46020c5399ddc03f2f9092ed6 Mon Sep 17 00:00:00 2001 From: Daniel Saewitz Date: Tue, 26 Feb 2013 16:44:26 -0500 Subject: [PATCH] Add type parameter to /user/tasks --- .gitignore | 1 - package.json | 3 +- server.js | 10 +---- src/server/api.coffee | 38 +++++++++------- test/api.mocha.coffee | 100 ++++++++++-------------------------------- test/mocha.opts | 2 +- 6 files changed, 50 insertions(+), 104 deletions(-) diff --git a/.gitignore b/.gitignore index 77291d9d0f..029b274b27 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,3 @@ node_modules *.swp .idea* config.json -test/config.json \ No newline at end of file diff --git a/package.json b/package.json index 08d33fc7ab..a5ab0f7bbe 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "./server.js", "dependencies": { "derby": "git://github.com/lefnire/derby#habitrpg", - "racer": "git://github.com/switz/racer#master", + "racer": "git://github.com/lefnire/racer#habitrpg", "racer-db-mongo": "git://github.com/lefnire/racer-db-mongo#habitrpg", "derby-ui-boot": "git://github.com/codeparty/derby-ui-boot#master", "derby-auth": "git://github.com/lefnire/derby-auth#master", @@ -24,7 +24,6 @@ "nconf": "*", "icalendar": "git://github.com/lefnire/node-icalendar#master", "nodetime": "*", - "querystring": "~0.1.0", "superagent": "~0.12.4", "resolve": "~0.2.3", "browserify": "1.17.3", diff --git a/server.js b/server.js index a95b925c12..f9d64274a7 100644 --- a/server.js +++ b/server.js @@ -20,8 +20,6 @@ process.env.SMTP_SERVICE = conf.get("SMTP_SERVICE"); process.env.STRIPE_API_KEY = conf.get("STRIPE_API_KEY"); process.env.STRIPE_PUB_KEY = conf.get("STRIPE_PUB_KEY"); -/* - var agent; if (process.env.NODE_ENV === 'development') { // Follow these instructions for profiling / debugging leaks @@ -68,13 +66,9 @@ process.on('uncaughtException', function (error) { }); console.log(error.stack); }); -*/ + require('coffee-script') // remove intermediate compilation requirement -module.exports = server = require('./src/server') - -Error.stackTraceLimit = Infinity; - -server.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0'); +require('./src/server').listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0'); // Note: removed "up" module, which is default for development (but interferes with and production + PaaS) // Restore to 5310bb0 if I want it back (see https://github.com/codeparty/derby/issues/165#issuecomment-10405693) diff --git a/src/server/api.coffee b/src/server/api.coffee index 65c48a7d21..fba15fc09f 100644 --- a/src/server/api.coffee +++ b/src/server/api.coffee @@ -5,7 +5,7 @@ scoring = require '../app/scoring' _ = require 'underscore' validator = require 'derby-auth/node_modules/validator' check = validator.check -snaitize = validator.sanitize +sanitize = validator.sanitize icalendar = require 'icalendar' NO_TOKEN_OR_UID = err: "You must include a token and uid (user id) in your request" @@ -23,7 +23,7 @@ NO_USER_FOUND = err: "No user found." auth = (req, res, next) -> uid = req.headers['x-api-user'] token = req.headers['x-api-key'] - return res.json 500, NO_TOKEN_OR_UID unless uid || token + return res.json 401, NO_TOKEN_OR_UID unless uid || token model = req.getModel() query = model.query('users').withIdAndToken(uid, token) @@ -33,7 +33,7 @@ auth = (req, res, next) -> user = user.at(0) req.user = user req.userObj = user.get() - return res.json 500, NO_USER_FOUND if !req.userObj || _.isEmpty(req.userObj) + return res.json 401, NO_USER_FOUND if !req.userObj || _.isEmpty(req.userObj) next() router.get '/status', (req, res) -> @@ -48,27 +48,30 @@ router.get '/user', auth, (req, res) -> router.get '/task/:id', auth, (req, res) -> task = req.userObj.tasks[req.params.id] - return res.json 500, err: "No task found." if !task || _.isEmpty(task) + return res.json 400, err: "No task found." if !task || _.isEmpty(task) res.json 200, task router.put '/task/:id', auth, (req, res) -> task = req.userObj.tasks[req.params.id] - return res.json 500, err: "No task found." if !task || _.isEmpty(task) + return res.json 400, err: "No task found." if !task || _.isEmpty(task) - task.title = req.body.title if req.body.title - task.text = req.body.text if req.body.text - task.type = req.body.type if req.body.type + title = sanitize(req.body.title).xss() + text = sanitize(req.body.text).xss() + + task.title = title if title + task.text = text if text + #task.type = req.body.type if /^(habit|todo|daily|reward)$/.test req.body.type req.user.set "tasks.#{task.id}", task - res.send task + res.json 200, task router.post '/user/task', auth, (req, res) -> task = { title, text, type, value, note } = req.body - return res.json 500, err: "type must be habit, todo, daily, or reward" unless /habit|todo|daily|reward/.test type - return res.json 500, err: "must have a title" unless check(title).notEmpty() - return res.json 500, err: "must have text" unless check(text).notEmpty() + return res.json 400, err: "type must be habit, todo, daily, or reward" unless /^habit|todo|daily|reward$/.test type + return res.json 400, err: "must have a title" unless check(title).notEmpty() + return res.json 400, err: "must have text" unless check(text).notEmpty() self = req.userObj @@ -83,12 +86,15 @@ router.post '/user/task', auth, (req, res) -> router.get '/user/tasks', auth, (req, res) -> self = req.userObj - return res.json 500, NO_USER_FOUND if !self || _.isEmpty(self) + return res.json 400, NO_USER_FOUND if !self || _.isEmpty(self) model = req.getModel() model.ref '_user', req.user tasks = [] - for type in ['habit','todo','daily','reward'] + types = ['habit','todo','daily','reward'] + if /^habit|todo|daily|reward$/.test req.query.type + types = [req.query.type] + for type in types model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids" tasks = tasks.concat model.get("_#{type}List") @@ -102,11 +108,11 @@ router.get '/users/:uid/calendar.ics', (req, res) -> model = req.getModel() query = model.query('users').withIdAndToken(uid, apiToken) query.fetch (err, result) -> - return res.send(500, err) if err + return res.send(400, err) if err tasks = result.at(0).get('tasks') # tasks = result[0].tasks tasksWithDates = _.filter tasks, (task) -> !!task.date - return res.send(500, "No events found") if _.isEmpty(tasksWithDates) + return res.send(400, "No events found") if _.isEmpty(tasksWithDates) ical = new icalendar.iCalendar() ical.addProperty('NAME', 'HabitRPG') diff --git a/test/api.mocha.coffee b/test/api.mocha.coffee index e520688d24..1195eb1f2e 100644 --- a/test/api.mocha.coffee +++ b/test/api.mocha.coffee @@ -1,11 +1,6 @@ -expect = require 'expect.js' -{BrowserModel: Model} = require 'racer/test/util/model' -derby = require 'derby' -racer = require 'racer' _ = require 'underscore' -moment = require 'moment' request = require 'superagent' -qs = require 'querystring' +expect = require 'expect.js' require 'coffee-script' ## monkey-patch expect.js for better diffs on mocha @@ -16,24 +11,6 @@ expect.Assertion::be = expect.Assertion::equal = (obj) -> @_expected = obj origBe.call this, obj -expect.Assertion::assert = (truth, msg, error) -> - msg = (if @flags.not then error else msg) - ok = (if @flags.not then not truth else truth) - unless ok - err = new Error(msg.call(this)) - if "_expected" of this - err.expected = @_expected - err.actual = @obj - throw err - @and = new expect.Assertion(@obj) -### -racer.use require 'racer-db-mongo' - -store = racer.createStore - db: - type: 'Mongo' - uri: process.env.NODE_DB_URI -### # Custom modules scoring = require '../src/app/scoring' character = require '../src/app/character' @@ -46,56 +23,6 @@ uuid = null taskPath = null baseURL = 'http://localhost:1337/api/v1' -## Helper which clones the content at a path so tests can compare before/after values -# Otherwise, using model.get(path) will give the same object before as after -pathSnapshots = (paths) -> - if _.isString(paths) - return clone(model.get(paths)) - _.map paths, (path) -> clone(model.get(path)) -statsTask = -> pathSnapshots(['_user.stats', taskPath]) # quick snapshot of user.stats & task - -cleanUserObj = -> - userObj = character.newUserObject() - userObj.tasks = {} - userObj.habitIds = [] - userObj.dailyIds = [] - userObj.todoIds = [] - userObj.rewardIds = [] - return userObj -resetUser = -> model.set '_user', cleanUserObj() - -freshTask = (taskObj) -> - resetUser() - # create a test task - uuid = derby.uuid() - taskPath = "_user.tasks.#{uuid}" - {type} = taskObj - model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids" - [taskObj.id, taskObj.value] = [uuid, 0] - model.at("_#{type}List").push taskObj - -### -Helper function to determine if stats updates are numerically correct based on scoring -@direction: 'up' or 'down' -@options: The user stats modifiers and times to run, defaults to {times:1, modifiers:{lvl:1, weapon:0, armor:0}} -### -modificationsLookup = (direction, options = {}) -> - merged = _.defaults options, {times:1, lvl:1, weapon:0, armor:0} - {times, lvl, armor, weapon} = merged - userObj = cleanUserObj() - value = 0 - _.times times, (n) -> - delta = scoring.taskDeltaFormula(value, direction) - value += delta - if direction=='up' - gain = scoring.expModifier(delta, options) - userObj.stats.exp += gain - userObj.stats.money += gain - else - loss = scoring.hpModifier(delta, options) - userObj.stats.hp += loss - return {user:userObj, value:value} - ###### Specs ###### describe 'API', -> @@ -114,7 +41,7 @@ describe 'API', -> model = store.createModel() model.set '_userId', uid = model.id() user = character.newUserObject() - user.apiToken = derby.uuid() + user.apiToken = model.id() model.set "users.#{uid}", user # Crappy hack to let server start before tests run setTimeout done, 2000 @@ -133,7 +60,7 @@ describe 'API', -> request.get("#{baseURL}/user") .set('Accept', 'application/json') .end (res) -> - expect(res.statusCode).to.be 500 + expect(res.statusCode).to.be 401 expect(res.body.err).to.be 'You must include a token and uid (user id) in your request' done() @@ -230,3 +157,24 @@ describe 'API', -> # Ensure that the two sets are equal expect(_.difference(_.pluck(res.body,'id'), _.pluck(tasks,'id')).length).to.equal 0 done() + + it 'GET /api/v1/user/tasks (todos)', (done) -> + request.get("#{baseURL}/user/tasks") + .set('Accept', 'application/json') + .set('X-API-User', currentUser.id) + .set('X-API-Key', currentUser.apiToken) + .query(type:'todo') + .end (res) -> + query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken) + query.fetch (err, user) -> + expect(res.body.err).to.be undefined + expect(res.statusCode).to.be 200 + currentUser = user.at(0).get() + model.ref '_user', user.at(0) + model.refList "_todoList", "_user.tasks", "_user.todoIds" + tasks = model.get("_todoList") + # Ensure that user owns the tasks + expect(res.body.length).to.equal tasks.length + # Ensure that the two sets are equal + expect(_.difference(_.pluck(res.body,'id'), _.pluck(tasks,'id')).length).to.equal 0 + done() diff --git a/test/mocha.opts b/test/mocha.opts index 74e33e85ea..72889931b5 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,6 +1,6 @@ --colors --reporter spec ---timeout 2500 +--timeout 2800 --ignore-leaks --growl --debug