mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-23 23:58:02 -05:00
Refactor POST /db/earned_achievement
This commit is contained in:
parent
5c8b8832b3
commit
f509c95a4b
5 changed files with 100 additions and 71 deletions
|
@ -15,10 +15,9 @@ class EarnedAchievementHandler extends Handler
|
|||
|
||||
editableProperties: ['notified']
|
||||
|
||||
# Don't allow POSTs or anything yet
|
||||
hasAccess: (req) ->
|
||||
return false unless req.user
|
||||
req.method in ['GET', 'POST', 'PUT'] # or req.user.isAdmin()
|
||||
req.method in ['GET', 'PUT'] # or req.user.isAdmin()
|
||||
|
||||
get: (req, res) ->
|
||||
return @getByAchievementIDs(req, res) if req.query.view is 'get-by-achievement-ids'
|
||||
|
@ -45,71 +44,6 @@ class EarnedAchievementHandler extends Handler
|
|||
documents = (@formatEntity(req, doc) for doc in documents)
|
||||
@sendSuccess(res, documents)
|
||||
|
||||
post: (req, res) ->
|
||||
achievementID = req.body.achievement
|
||||
triggeredBy = req.body.triggeredBy
|
||||
collection = req.body.collection
|
||||
if collection isnt 'level.sessions' and not testing # TODO: remove this restriction
|
||||
return @sendBadInputError(res, 'Only doing level session achievements for now.')
|
||||
|
||||
model = mongoose.modelNameByCollection(collection)
|
||||
|
||||
async.parallel({
|
||||
achievement: (callback) ->
|
||||
Achievement.findById achievementID, (err, achievement) -> callback(err, achievement)
|
||||
|
||||
trigger: (callback) ->
|
||||
model.findById triggeredBy, (err, trigger) -> callback(err, trigger)
|
||||
|
||||
earned: (callback) ->
|
||||
q = { achievement: achievementID, user: req.user._id+'' }
|
||||
EarnedAchievement.findOne q, (err, earned) -> callback(err, earned)
|
||||
}, (err, { achievement, trigger, earned } ) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
if not achievement
|
||||
return @sendNotFoundError(res, 'Could not find achievement.')
|
||||
else if not trigger
|
||||
return @sendNotFoundError(res, 'Could not find trigger.')
|
||||
else if achievement.get('proportionalTo') and earned
|
||||
EarnedAchievement.createForAchievement(achievement, trigger, {previouslyEarnedAchievement: earned}).then (earnedAchievementDoc) =>
|
||||
@sendCreated(res, (earnedAchievementDoc or earned)?.toObject())
|
||||
else if earned
|
||||
achievementEarned = achievement.get('rewards')
|
||||
actuallyEarned = earned.get('earnedRewards')
|
||||
if not _.isEqual(achievementEarned, actuallyEarned)
|
||||
earned.set('earnedRewards', achievementEarned)
|
||||
earned.save((err) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
@upsertNonNumericRewards(req.user, achievement, (err) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendSuccess(res, earned.toObject())
|
||||
)
|
||||
)
|
||||
else
|
||||
@upsertNonNumericRewards(req.user, achievement, (err) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendSuccess(res, earned.toObject())
|
||||
)
|
||||
else
|
||||
EarnedAchievement.createForAchievement(achievement, trigger).then (earnedAchievementDoc) =>
|
||||
if earnedAchievementDoc
|
||||
@sendCreated(res, earnedAchievementDoc.toObject())
|
||||
else
|
||||
console.error "Couldn't create achievement", achievement, trigger
|
||||
@sendNotFoundError res, "Couldn't create achievement"
|
||||
)
|
||||
|
||||
upsertNonNumericRewards: (user, achievement, done) ->
|
||||
update = {}
|
||||
for rewardType, rewards of achievement.get('rewards') ? {}
|
||||
continue if rewardType is 'gems'
|
||||
if rewards.length
|
||||
update.$addToSet ?= {}
|
||||
update.$addToSet["earned.#{rewardType}"] = $each: rewards
|
||||
User.update {_id: user._id}, update, {}, (err, result) ->
|
||||
log.error err if err?
|
||||
done?(err)
|
||||
|
||||
getByAchievementIDs: (req, res) ->
|
||||
query = { user: req.user._id+''}
|
||||
ids = req.query.achievementIDs
|
||||
|
|
57
server/middleware/earned-achievements.coffee
Normal file
57
server/middleware/earned-achievements.coffee
Normal file
|
@ -0,0 +1,57 @@
|
|||
log = require 'winston'
|
||||
mongoose = require 'mongoose'
|
||||
Achievement = require './../models/Achievement'
|
||||
EarnedAchievement = require './../models/EarnedAchievement'
|
||||
errors = require '../commons/errors'
|
||||
wrap = require 'co-express'
|
||||
|
||||
|
||||
exports.post = wrap (req, res) ->
|
||||
achievementID = req.body.achievement
|
||||
triggeredBy = req.body.triggeredBy
|
||||
collection = req.body.collection
|
||||
if collection isnt 'level.sessions' and not testing # TODO: remove this restriction
|
||||
throw new errors.UnprocessableEntity('Only doing level session achievements for now.')
|
||||
|
||||
model = mongoose.modelNameByCollection(collection)
|
||||
|
||||
[achievement, trigger, earned] = yield [
|
||||
Achievement.findById(achievementID),
|
||||
model.findById(triggeredBy)
|
||||
EarnedAchievement.findOne({ achievement: achievementID, user: req.user.id })
|
||||
]
|
||||
|
||||
if not achievement
|
||||
throw new errors.NotFound('Could not find achievement.')
|
||||
if not trigger
|
||||
throw new errors.NotFound('Could not find trigger.')
|
||||
|
||||
if achievement.get('proportionalTo') and earned
|
||||
earnedAchievementDoc = yield EarnedAchievement.createForAchievement(achievement, trigger, {previouslyEarnedAchievement: earned})
|
||||
res.status(201).send((earnedAchievementDoc or earned)?.toObject({req}))
|
||||
|
||||
else if earned
|
||||
achievementEarned = achievement.get('rewards')
|
||||
actuallyEarned = earned.get('earnedRewards')
|
||||
if not _.isEqual(achievementEarned, actuallyEarned)
|
||||
earned.set('earnedRewards', achievementEarned)
|
||||
yield earned.save()
|
||||
|
||||
# make sure user has all the levels and items they should have
|
||||
update = {}
|
||||
for rewardType, rewards of achievement.get('rewards') ? {}
|
||||
continue if rewardType is 'gems'
|
||||
if rewards.length
|
||||
update.$addToSet ?= {}
|
||||
update.$addToSet["earned.#{rewardType}"] = { $each: rewards }
|
||||
yield req.user.update(update)
|
||||
|
||||
return res.status(200).send(earned.toObject({req}))
|
||||
|
||||
else
|
||||
earnedAchievementDoc = yield EarnedAchievement.createForAchievement(achievement, trigger)
|
||||
if not earnedAchievementDoc
|
||||
console.error "Couldn't create achievement", achievement, trigger
|
||||
throw new errors.NotFound("Couldn't create achievement")
|
||||
res.status(201).send(earnedAchievementDoc.toObject({res}))
|
||||
|
|
@ -7,6 +7,7 @@ module.exports =
|
|||
contact: require './contact'
|
||||
courseInstances: require './course-instances'
|
||||
courses: require './courses'
|
||||
earnedAchievements: require './earned-achievements'
|
||||
files: require './files'
|
||||
healthcheck: require './healthcheck'
|
||||
levels: require './levels'
|
||||
|
|
|
@ -96,7 +96,10 @@ module.exports.setup = (app) ->
|
|||
app.post('/db/course_instance/:handle/members', mw.auth.checkLoggedIn(), mw.courseInstances.addMembers)
|
||||
app.get('/db/course_instance/:handle/classroom', mw.auth.checkLoggedIn(), mw.courseInstances.fetchClassroom)
|
||||
app.get('/db/course_instance/:handle/course', mw.auth.checkLoggedIn(), mw.courseInstances.fetchCourse)
|
||||
|
||||
|
||||
EarnedAchievement = require '../models/EarnedAchievement'
|
||||
app.post('/db/earned_achievement', mw.earnedAchievements.post)
|
||||
|
||||
Level = require '../models/Level'
|
||||
app.post('/db/level/:handle', mw.auth.checkLoggedIn(), mw.versions.postNewVersion(Level, { hasPermissionsOrTranslations: 'artisan' })) # TODO: add /new-version to route like Article has
|
||||
app.get('/db/level/:handle/session', mw.auth.checkHasUser(), mw.levels.upsertSession)
|
||||
|
|
|
@ -7,12 +7,15 @@ LevelSession = require '../../../server/models/LevelSession'
|
|||
User = require '../../../server/models/User'
|
||||
request = require '../request'
|
||||
EarnedAchievementHandler = require '../../../server/handlers/earned_achievement_handler'
|
||||
mongoose = require 'mongoose'
|
||||
|
||||
url = getURL('/db/achievement')
|
||||
|
||||
|
||||
# Fixtures
|
||||
|
||||
lockedLevelID = new mongoose.Types.ObjectId().toString()
|
||||
|
||||
unlockable =
|
||||
name: 'Dungeon Arena Started'
|
||||
description: 'Started playing Dungeon Arena.'
|
||||
|
@ -22,6 +25,9 @@ unlockable =
|
|||
userField: 'creator'
|
||||
recalculable: true
|
||||
related: 'a'
|
||||
rewards: {
|
||||
levels: [lockedLevelID]
|
||||
}
|
||||
|
||||
unlockable2 = _.clone unlockable
|
||||
unlockable2.name = 'This one is obsolete'
|
||||
|
@ -163,6 +169,7 @@ describe 'DELETE /db/achievement/:handle', ->
|
|||
|
||||
describe 'POST /db/earned_achievement', ->
|
||||
beforeEach addAllAchievements
|
||||
eaURL = getURL('/db/earned_achievement')
|
||||
|
||||
it 'manually creates earned achievements for level achievements, which do not happen automatically', utils.wrap (done) ->
|
||||
session = new LevelSession({
|
||||
|
@ -174,7 +181,7 @@ describe 'POST /db/earned_achievement', ->
|
|||
earnedAchievements = yield EarnedAchievement.find()
|
||||
expect(earnedAchievements.length).toBe(0)
|
||||
json = {achievement: @unlockable.id, triggeredBy: session._id, collection: 'level.sessions'}
|
||||
[res, body] = yield request.postAsync {uri: getURL('/db/earned_achievement'), json: json}
|
||||
[res, body] = yield request.postAsync { url: eaURL, json }
|
||||
expect(res.statusCode).toBe(201)
|
||||
expect(body.achievement).toBe @unlockable.id
|
||||
expect(body.user).toBe @admin.id
|
||||
|
@ -191,14 +198,41 @@ describe 'POST /db/earned_achievement', ->
|
|||
yield utils.loginUser(user)
|
||||
yield user.update({simulatedBy: 10})
|
||||
json = {achievement: @repeatable.id, triggeredBy: user.id, collection: 'users'}
|
||||
[res, body] = yield request.postAsync {uri: getURL('/db/earned_achievement'), json: json}
|
||||
[res, body] = yield request.postAsync { url: eaURL, json }
|
||||
expect(res.statusCode).toBe(201)
|
||||
expect(body.earnedPoints).toBe(10)
|
||||
yield user.update({simulatedBy: 30})
|
||||
[res, body] = yield request.postAsync {uri: getURL('/db/earned_achievement'), json: json}
|
||||
[res, body] = yield request.postAsync { url: eaURL, json }
|
||||
expect(res.statusCode).toBe(201)
|
||||
expect(body.earnedPoints).toBe(20) # this is kinda weird, TODO: just return total amounts
|
||||
done()
|
||||
|
||||
it 'ensures the user has the rewards they earned', utils.wrap (done) ->
|
||||
user = yield utils.initUser()
|
||||
yield utils.loginUser(user)
|
||||
|
||||
# get the User the unlockable achievement, check they got their reward
|
||||
session = new LevelSession({
|
||||
permissions: simplePermissions
|
||||
creator: user._id
|
||||
level: original: 'dungeon-arena'
|
||||
})
|
||||
yield session.save()
|
||||
json = {achievement: @unlockable.id, triggeredBy: session._id, collection: 'level.sessions'}
|
||||
[res, body] = yield request.postAsync { url: eaURL, json }
|
||||
user = yield User.findById(user.id)
|
||||
expect(user.get('earned').levels[0]).toBe(lockedLevelID)
|
||||
|
||||
# mess with the user's earned levels, make sure they don't have it anymore
|
||||
yield user.update({$unset: {earned:1}})
|
||||
user = yield User.findById(user.id)
|
||||
expect(user.get('earned')).toBeUndefined()
|
||||
|
||||
# hit the endpoint again, make sure the level was restored
|
||||
[res, body] = yield request.postAsync { url: eaURL, json }
|
||||
user = yield User.findById(user.id)
|
||||
expect(user.get('earned').levels[0]).toBe(lockedLevelID)
|
||||
done()
|
||||
|
||||
describe 'automatically achieving achievements', ->
|
||||
beforeEach addAllAchievements
|
||||
|
|
Loading…
Reference in a new issue