mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-05 03:52:14 +00:00
update apiv2 to use async.series instead of the previous count
method. much better flow control
This commit is contained in:
parent
e667b3a2b0
commit
e7d200f1b0
1 changed files with 70 additions and 71 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
express = require 'express'
|
express = require 'express'
|
||||||
router = new express.Router()
|
router = new express.Router()
|
||||||
util = require 'util'
|
util = require 'util'
|
||||||
|
async = require 'async'
|
||||||
|
|
||||||
_ = require 'lodash'
|
_ = require 'lodash'
|
||||||
algos = require 'habitrpg-shared/script/algos'
|
algos = require 'habitrpg-shared/script/algos'
|
||||||
|
|
@ -26,78 +27,76 @@ POST new actions
|
||||||
router.post '/', api.auth, (req, res, next) ->
|
router.post '/', api.auth, (req, res, next) ->
|
||||||
model = req.getModel()
|
model = req.getModel()
|
||||||
{user} = req
|
{user} = req
|
||||||
actions = req.body
|
|
||||||
|
|
||||||
doneCount = 1 + # cron
|
performAction = (action, cb) ->
|
||||||
_.size(actions) # standard operations
|
task = action.task ? {}
|
||||||
done = (err) ->
|
switch action.op
|
||||||
|
when "cron"
|
||||||
|
misc.batchTxn model, (uObj, paths) ->
|
||||||
|
# habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks
|
||||||
|
_.each ['habit','daily','todo','reward'], (type) -> uObj["#{type}s"] = _.where(uObj.tasks, {type}); true
|
||||||
|
algos.cron uObj, {paths}
|
||||||
|
, {user, cb, cron:true}
|
||||||
|
|
||||||
|
when "score"
|
||||||
|
return cb() unless user.get "tasks.#{task.id}"
|
||||||
|
sendScore = -> api.score(model, user, task.id, action.dir, cb)
|
||||||
|
if task.type in ["daily","todo"]
|
||||||
|
# switch completed state. Since checkbox is not binded to model unlike when you click through Derby website.
|
||||||
|
completed = if action.dir is "up" then true else false
|
||||||
|
user.set "tasks.#{task.id}.completed", completed, sendScore
|
||||||
|
else sendScore()
|
||||||
|
|
||||||
|
when "sortTask"
|
||||||
|
path = action.task.type + "Ids"
|
||||||
|
a = user.get(path)
|
||||||
|
a.splice(action.to, 0, a.splice(action.from, 1)[0])
|
||||||
|
user.set path, a, cb
|
||||||
|
|
||||||
|
when "addTask"
|
||||||
|
api.addTask user, task, cb
|
||||||
|
|
||||||
|
when "delTask"
|
||||||
|
api.deleteTask user, task, cb
|
||||||
|
|
||||||
|
# this API is only working with string or number variables. It should return error if object given or object is at the path.
|
||||||
|
when "set"
|
||||||
|
oldValue = user.get(action.path)
|
||||||
|
if _.isObject(action.value) or _.isObject(oldValue)
|
||||||
|
console.error "action.value was an object, which isn't currently supported. Tyler - double check this"
|
||||||
|
cb()
|
||||||
|
else
|
||||||
|
user.set action.path, action.value, cb
|
||||||
|
|
||||||
|
when "revive"
|
||||||
|
[uObj, paths] = [user.get(), {}]
|
||||||
|
algos.revive uObj, {paths}
|
||||||
|
setOps = _.map paths, (v,k) ->
|
||||||
|
(reviveCb) -> user.set k, helpers.dotGet(k,uObj), reviveCb
|
||||||
|
console.log setOps
|
||||||
|
async.parallel setOps, cb
|
||||||
|
|
||||||
|
else
|
||||||
|
cb()
|
||||||
|
|
||||||
|
# Setup the array of functions we're going to call in parallel with async
|
||||||
|
req.body = [] if _.isEmpty req.body
|
||||||
|
actions = _.transform (req.body or []), (result, action) ->
|
||||||
|
unless _.isEmpty(action)
|
||||||
|
result.push (cb) -> performAction(action, cb)
|
||||||
|
# always run cron check
|
||||||
|
req.body.unshift({op: 'cron'}) unless _.isEmpty actions
|
||||||
|
|
||||||
|
# call all the operations, then return the user object to the requester
|
||||||
|
async.series actions, (err) ->
|
||||||
return next(err) if err
|
return next(err) if err
|
||||||
if --doneCount is 0
|
uObj = misc.hydrate user.get()
|
||||||
uObj = misc.hydrate user.get()
|
#transform user structure FROM user.tasks{} + user.habitIds[] TO user.habits[] + user.todos[] etc.
|
||||||
#transform user structure FROM user.tasks{} + user.habitIds[] TO user.habits[] + user.todos[] etc.
|
_.each ['habit','daily','todo','reward'], (type) ->
|
||||||
_.each ['habit','daily','todo','reward'], (type) ->
|
uObj["#{type}s"] = _.transform uObj["#{type}Ids"], (result, tid) -> result.push(uObj.tasks[tid])
|
||||||
uObj["#{type}s"] = _.transform uObj["#{type}Ids"], (result, tid) -> result.push(uObj.tasks[tid])
|
delete uObj["#{type}Ids"]
|
||||||
delete uObj["#{type}Ids"]
|
delete uObj.tasks
|
||||||
delete uObj.tasks
|
res.json 200, uObj
|
||||||
res.json 200, uObj
|
console.log "Reply sent"
|
||||||
console.log "Reply sent"
|
|
||||||
|
|
||||||
notEmpty = _.find actions, ((action) -> !_.isEmpty(action))
|
|
||||||
unless notEmpty
|
|
||||||
# yan's strange User.log({}) thing
|
|
||||||
doneCount = 1
|
|
||||||
return done()
|
|
||||||
|
|
||||||
misc.batchTxn model, (uObj, paths) ->
|
|
||||||
# habitrpg-shared/algos requires uObj.habits, uObj.dailys etc instead of uObj.tasks
|
|
||||||
_.each ['habit','daily','todo','reward'], (type) -> uObj["#{type}s"] = _.where(uObj.tasks, {type}); true
|
|
||||||
algos.cron uObj, {paths}
|
|
||||||
, {user, done, cron:true}
|
|
||||||
|
|
||||||
if _.isArray actions
|
|
||||||
actions.forEach (action)->
|
|
||||||
|
|
||||||
task = action.task ? {}
|
|
||||||
|
|
||||||
return done() if _.isEmpty(action)
|
|
||||||
|
|
||||||
switch action.op
|
|
||||||
when "score"
|
|
||||||
return done() unless user.get "tasks.#{task.id}"
|
|
||||||
sendScore = -> api.score(model, user, task.id, action.dir, done)
|
|
||||||
if task.type in ["daily","todo"]
|
|
||||||
# switch completed state. Since checkbox is not binded to model unlike when you click through Derby website.
|
|
||||||
completed = if action.dir is "up" then true else false
|
|
||||||
user.set "tasks.#{task.id}.completed", completed, sendScore
|
|
||||||
else sendScore()
|
|
||||||
|
|
||||||
when "sortTask"
|
|
||||||
path = action.task.type + "Ids"
|
|
||||||
a = user.get(path)
|
|
||||||
a.splice(action.to, 0, a.splice(action.from, 1)[0])
|
|
||||||
user.set path, a, done
|
|
||||||
|
|
||||||
when "addTask"
|
|
||||||
api.addTask user, task, done
|
|
||||||
|
|
||||||
when "delTask"
|
|
||||||
api.deleteTask user, task, done
|
|
||||||
|
|
||||||
# this API is only working with string or number variables. It should return error if object given or object is at the path.
|
|
||||||
when "set"
|
|
||||||
oldValue = user.get(action.path)
|
|
||||||
if _.isObject(action.value) or _.isObject(oldValue)
|
|
||||||
console.error "action.value was an object, which isn't currently supported. Tyler - double check this"
|
|
||||||
else
|
|
||||||
user.set action.path, action.value, done
|
|
||||||
|
|
||||||
when "revive"
|
|
||||||
[uObj, paths] = [user.get(), {}]
|
|
||||||
algos.revive uObj, {paths}
|
|
||||||
doneCount += (_.size(paths) - 1) # once for each path, but +1 is already accounted for at top of this function (whole 'revive' counted as 1)
|
|
||||||
_.each paths, (v,k) ->
|
|
||||||
user.set k, helpers.dotGet(k,uObj), done
|
|
||||||
|
|
||||||
else done()
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue