diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index 18f63e67f..467824789 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -319,6 +319,9 @@ _.extend UserSchema.properties, courseID: c.objectId({}) courseInstanceID: c.objectId({}) } + coursePrepaidID: c.objectId({ + description: 'Prepaid which has paid for this user\'s course access' + }) c.extendBasicProperties UserSchema, 'user' diff --git a/server/prepaids/prepaid_handler.coffee b/server/prepaids/prepaid_handler.coffee index e2d6815d3..3fc43b85f 100644 --- a/server/prepaids/prepaid_handler.coffee +++ b/server/prepaids/prepaid_handler.coffee @@ -78,14 +78,26 @@ PrepaidHandler = class PrepaidHandler extends Handler Prepaid.count {'redeemers.userID': userID}, (err, count) => return @sendDatabaseError(res, err) if err return @sendSuccess(res, @formatEntity(req, prepaid)) if count - redeemers = _.clone(prepaid.get('redeemers') or []) - redeemers.push({ date: new Date(), userID: userID }) - prepaid.set('redeemers', redeemers) - # Not worrying about race conditions. Worst case: overwrite one user with another. - # You can't end up with more than maxRedeemers in the list of redeemers. - prepaid.save (err, prepaid) => + + query = + _id: prepaid.get('_id') + 'redeemers.userID': { $ne: req.user.get('_id') } + $where: "this.redeemers.length < #{prepaid.get('maxRedeemers')}" + update = { $push: { redeemers : { date: new Date(), userID: userID } }} + Prepaid.update query, update, (err, nMatched) => return @sendDatabaseError(res, err) if err - @sendSuccess(res, @formatEntity(req, prepaid)) + if nMatched is 0 + @logError(req.user, "POST prepaid redeemer lost race on maxRedeemers") + return @sendForbiddenError(res) + + user.set('coursePrepaidID', prepaid.get('_id')) + user.save (err, user) => + return @sendDatabaseError(res, err) if err + # return prepaid with new redeemer added locally + redeemers = _.clone(prepaid.get('redeemers') or []) + redeemers.push({ date: new Date(), userID: userID }) + prepaid.set('redeemers', redeemers) + @sendSuccess(res, @formatEntity(req, prepaid)) createPrepaid: (user, type, maxRedeemers, properties, done) -> Prepaid.generateNewCode (code) => diff --git a/test/server/functional/prepaid.spec.coffee b/test/server/functional/prepaid.spec.coffee index 87af0fb43..9a0f39e25 100644 --- a/test/server/functional/prepaid.spec.coffee +++ b/test/server/functional/prepaid.spec.coffee @@ -53,7 +53,12 @@ describe '/db/prepaid', -> request.post {uri: url, json: redeemer }, (err, res, body) -> expect(body.redeemers.length).toBe(1) expect(res.statusCode).toBe(200) - done() + prepaid = Prepaid.findById body._id, (err, prepaid) -> + expect(err).toBeNull() + expect(prepaid.get('redeemers').length).toBe(1) + User.findById otherUser.id, (err, user) -> + expect(user.get('coursePrepaidID').equals(prepaid.get('_id'))).toBe(true) + done() it 'does not allow more redeemers than maxRedeemers', (done) -> loginNewUser (user1) ->