freeCodeCamp/server/boot/challenge.js

267 lines
6.5 KiB
JavaScript
Raw Normal View History

2015-08-10 05:14:31 +00:00
import _ from 'lodash';
2016-06-01 22:52:08 +00:00
// import { Observable, Scheduler } from 'rx';
import debug from 'debug';
import accepts from 'accepts';
2016-06-01 22:52:08 +00:00
import { ifNoUserSend } from '../utils/middleware';
2015-08-10 05:14:31 +00:00
2016-01-27 19:34:44 +00:00
const log = debug('fcc:challenges');
2015-08-10 05:14:31 +00:00
function buildUserUpdate(
user,
challengeId,
completedChallenge,
timezone
) {
const updateData = { $set: {} };
let finalChallenge;
const { timezone: userTimezone, challengeMap = {} } = user;
const oldChallenge = challengeMap[challengeId];
const alreadyCompleted = !!oldChallenge;
if (alreadyCompleted) {
// add data from old challenge
finalChallenge = {
...completedChallenge,
completedDate: oldChallenge.completedDate,
lastUpdated: completedChallenge.completedDate
};
2016-02-10 20:01:00 +00:00
} else {
updateData.$push = {
2016-02-10 20:01:00 +00:00
progressTimestamps: {
timestamp: Date.now(),
completedChallenge: challengeId
}
};
finalChallenge = completedChallenge;
}
updateData.$set = {
[`challengeMap.${challengeId}`]: finalChallenge
};
if (
2016-02-10 18:05:51 +00:00
timezone &&
timezone !== 'UTC' &&
(!userTimezone || userTimezone === 'UTC')
) {
updateData.$set = {
...updateData.$set,
timezone: userTimezone
};
}
log('user update data', updateData);
return { alreadyCompleted, updateData };
}
2015-06-03 02:02:54 +00:00
module.exports = function(app) {
2015-08-10 05:14:31 +00:00
const router = app.loopback.Router();
const send200toNonUser = ifNoUserSend(true);
2016-06-01 22:52:08 +00:00
router.post(
'/modern-challenge-completed',
send200toNonUser,
modernChallengeCompleted
);
router.post(
'/completed-challenge/',
send200toNonUser,
completedChallenge
);
2016-06-01 22:52:08 +00:00
router.post(
'/completed-zipline-or-basejump',
send200toNonUser,
completedZiplineOrBasejump
);
2015-06-03 02:02:54 +00:00
app.use(router);
2016-06-01 22:52:08 +00:00
function modernChallengeCompleted(req, res, next) {
const type = accepts(req).type('html', 'json', 'text');
req.checkBody('id', 'id must be an ObjectId').isMongoId();
req.checkBody('files', 'files must be an object with polyvinyls for keys')
.isFiles();
2016-06-01 22:52:08 +00:00
const errors = req.validationErrors(true);
if (errors) {
if (type === 'json') {
return res.status(403).send({ errors });
}
2016-06-01 22:52:08 +00:00
log('errors', errors);
return res.sendStatus(403);
2016-01-15 09:44:18 +00:00
}
2016-06-01 22:52:08 +00:00
const user = req.user;
return user.getChallengeMap$()
.flatMap(() => {
const completedDate = Date.now();
const {
id,
files
} = req.body;
2016-06-01 22:52:08 +00:00
const { alreadyCompleted, updateData } = buildUserUpdate(
user,
id,
{
id,
files,
completedDate
}
);
2016-06-01 22:52:08 +00:00
const points = alreadyCompleted ? user.points : user.points + 1;
2016-01-14 23:15:44 +00:00
2016-06-01 22:52:08 +00:00
return user.update$(updateData)
.doOnNext(({ count }) => log('%s documents updated', count))
.map(() => {
if (type === 'json') {
return res.json({
points,
alreadyCompleted
2016-01-14 23:15:44 +00:00
});
2016-06-01 22:52:08 +00:00
}
return res.sendStatus(200);
});
2016-01-14 23:15:44 +00:00
})
.subscribe(() => {}, next);
}
2015-06-03 02:02:54 +00:00
function completedChallenge(req, res, next) {
2016-03-31 03:58:38 +00:00
req.checkBody('id', 'id must be an ObjectId').isMongoId();
req.checkBody('name', 'name must be at least 3 characters')
.isString()
.isLength({ min: 3 });
req.checkBody('challengeType', 'challengeType must be an integer')
2016-03-03 05:53:42 +00:00
.isNumber();
const type = accepts(req).type('html', 'json', 'text');
2015-06-03 02:02:54 +00:00
const errors = req.validationErrors(true);
if (errors) {
if (type === 'json') {
return res.status(403).send({ errors });
}
log('errors', errors);
return res.sendStatus(403);
}
return req.user.getChallengeMap$()
.flatMap(() => {
const completedDate = Date.now();
const {
id,
name,
challengeType,
solution,
timezone
} = req.body;
const { alreadyCompleted, updateData } = buildUserUpdate(
req.user,
id,
{
id,
challengeType,
solution,
name,
completedDate
},
timezone
);
const user = req.user;
const points = alreadyCompleted ? user.points : user.points + 1;
return user.update$(updateData)
.doOnNext(({ count }) => log('%s documents updated', count))
.map(() => {
if (type === 'json') {
return res.json({
points,
alreadyCompleted
});
}
return res.sendStatus(200);
});
})
.subscribe(() => {}, next);
}
2015-06-03 02:02:54 +00:00
function completedZiplineOrBasejump(req, res, next) {
const type = accepts(req).type('html', 'json', 'text');
req.checkBody('id', 'id must be an ObjectId').isMongoId();
req.checkBody('name', 'Name must be at least 3 characters')
.isString()
.isLength({ min: 3 });
req.checkBody('challengeType', 'must be a number')
2016-03-03 05:53:42 +00:00
.isNumber();
req.checkBody('solution', 'solution must be a url').isURL();
const errors = req.validationErrors(true);
if (errors) {
if (type === 'json') {
return res.status(403).send({ errors });
}
log('errors', errors);
return res.sendStatus(403);
}
2015-06-03 02:02:54 +00:00
const { user, body = {} } = req;
const completedChallenge = _.pick(
body,
[ 'id', 'name', 'solution', 'githubLink', 'challengeType' ]
);
completedChallenge.challengeType = +completedChallenge.challengeType;
completedChallenge.completedDate = Date.now();
if (
!completedChallenge.solution ||
// only basejumps require github links
(
completedChallenge.challengeType === 4 &&
!completedChallenge.githubLink
)
) {
2015-06-03 02:02:54 +00:00
req.flash('errors', {
msg: 'You haven\'t supplied the necessary URLs for us to inspect ' +
'your work.'
2015-06-03 02:02:54 +00:00
});
return res.sendStatus(403);
}
2015-05-21 07:17:44 +00:00
return user.getChallengeMap$()
.flatMap(() => {
const {
alreadyCompleted,
updateData
} = buildUserUpdate(user, completedChallenge.id, completedChallenge);
return user.update$(updateData)
.doOnNext(({ count }) => log('%s documents updated', count))
.doOnNext(() => {
if (type === 'json') {
return res.send({
alreadyCompleted,
points: alreadyCompleted ? user.points : user.points + 1
});
}
return res.status(200).send(true);
});
})
.subscribe(() => {}, next);
}
2015-06-03 02:02:54 +00:00
};