freeCodeCamp/server/boot/certificate.js

254 lines
5.8 KiB
JavaScript
Raw Normal View History

2015-10-02 18:47:36 +00:00
import _ from 'lodash';
import moment from 'moment';
import loopback from 'loopback';
import path from 'path';
2015-10-02 18:47:36 +00:00
import dedent from 'dedent';
import { Observable } from 'rx';
2016-02-10 04:54:49 +00:00
import debug from 'debug';
2015-10-02 18:47:36 +00:00
import {
ifNoUser401,
ifNoUserSend
} from '../utils/middleware';
2016-02-10 04:54:49 +00:00
import { observeQuery } from '../utils/rx';
2015-10-02 18:47:36 +00:00
import {
2015-12-09 22:34:33 +00:00
frontEndChallengeId,
2016-01-11 23:58:37 +00:00
dataVisChallengeId,
2015-12-09 22:34:33 +00:00
backEndChallengeId
} from '../utils/constantStrings.json';
import {
completeCommitment$
} from '../utils/commit';
2016-01-11 23:58:37 +00:00
import certTypes from '../utils/certTypes.json';
2016-01-27 19:34:44 +00:00
const log = debug('fcc:certification');
const renderCertifedEmail = loopback.template(path.join(
__dirname,
'..',
'views',
'emails',
'certified.ejs'
));
const renderNotifyEmail = loopback.template(path.join(
__dirname,
'..',
'views',
'emails',
'a-new-user.ejs'
));
2015-10-02 18:47:36 +00:00
const sendMessageToNonUser = ifNoUserSend(
'must be logged in to complete.'
);
2016-02-10 04:54:49 +00:00
function isCertified(ids, challengeMap = {}) {
return _.every(ids, ({ id }) => challengeMap[id]);
2015-10-02 18:47:36 +00:00
}
2016-01-11 23:58:37 +00:00
function getIdsForCert$(id, Challenge) {
return observeQuery(
2015-10-02 18:47:36 +00:00
Challenge,
'findById',
2016-01-11 23:58:37 +00:00
id,
2015-10-02 18:47:36 +00:00
{
id: true,
tests: true,
name: true,
challengeType: true
2015-10-02 18:47:36 +00:00
}
)
.shareReplay();
2016-01-11 23:58:37 +00:00
}
2015-10-02 18:47:36 +00:00
// getFormatedDate(challengeMap: Object, challengeId: String) => String, throws
function getFormatedDate(challengeMap, challengeId) {
return moment(challengeMap[challengeId].completedDate)
.format('MMMM D, YYYY');
}
// sendCertifiedEmail(
// {
// email: String,
// username: String,
// isFrontEndCert: Boolean,
// isBackEndCert: Boolean,
// isDataVisCert: Boolean
// },
// send$: Observable
// ) => Observable
function sendCertifiedEmail(
{
email,
name,
username,
isFrontEndCert,
isBackEndCert,
isDataVisCert,
challengeMap
},
send$
) {
if (
!isFrontEndCert ||
!isBackEndCert ||
!isDataVisCert
) {
return Observable.just(false);
}
let frontEndDate;
let backEndDate;
let dataVisDate;
try {
frontEndDate = getFormatedDate(challengeMap, frontEndChallengeId);
backEndDate = getFormatedDate(challengeMap, backEndChallengeId);
dataVisDate = getFormatedDate(challengeMap, dataVisChallengeId);
} catch (err) {
return Observable.throw(err);
}
const notifyTeam = {
type: 'email',
to: 'Michael@FreeCodeCamp.com',
from: 'Team@FreeCodeCamp.com',
subject: 'A new user has arrived!',
text: renderNotifyEmail({
username,
name,
frontEndDate,
dataVisDate,
backEndDate
})
};
const notifyUser = {
type: 'email',
to: email,
from: 'Michael@FreeCodeCamp.com',
subject: 'Congratulation on gaining your third certification!',
text: renderCertifedEmail({
username,
name
})
};
return Observable.combineLatest(
send$(notifyTeam),
send$(notifyUser),
() => true
);
}
2016-01-11 23:58:37 +00:00
export default function certificate(app) {
const router = app.loopback.Router();
const { Email, Challenge } = app.models;
2016-01-11 23:58:37 +00:00
const certTypeIds = {
[certTypes.frontEnd]: getIdsForCert$(frontEndChallengeId, Challenge),
2016-01-12 00:23:24 +00:00
[certTypes.dataVis]: getIdsForCert$(dataVisChallengeId, Challenge),
[certTypes.backEnd]: getIdsForCert$(backEndChallengeId, Challenge)
2016-01-11 23:58:37 +00:00
};
2015-10-02 18:47:36 +00:00
router.post(
'/certificate/verify/front-end',
ifNoUser401,
2016-01-11 23:58:37 +00:00
verifyCert.bind(null, certTypes.frontEnd)
2015-10-02 18:47:36 +00:00
);
router.post(
2015-12-09 22:34:33 +00:00
'/certificate/verify/back-end',
2015-10-02 18:47:36 +00:00
ifNoUser401,
2016-01-11 23:58:37 +00:00
verifyCert.bind(null, certTypes.backEnd)
);
router.post(
'/certificate/verify/data-visualization',
ifNoUser401,
verifyCert.bind(null, certTypes.dataVis)
2015-10-02 18:47:36 +00:00
);
router.post(
'/certificate/honest',
sendMessageToNonUser,
postHonest
);
app.use(router);
2016-01-11 23:58:37 +00:00
function verifyCert(certType, req, res, next) {
2016-02-10 04:54:49 +00:00
const { user } = req;
2016-04-08 21:24:21 +00:00
return user.getChallengeMap$()
.flatMap(() => certTypeIds[certType])
.flatMap(challenge => {
const {
id,
tests,
name,
challengeType
} = challenge;
2015-10-02 18:47:36 +00:00
if (
user[certType] ||
!isCertified(tests, user.challengeMap)
2015-10-02 18:47:36 +00:00
) {
return Observable.just(false);
2015-10-02 18:47:36 +00:00
}
const updateData = {
$set: {
[`challengeMap.${id}`]: {
id,
name,
completedDate: new Date(),
challengeType
},
[certType]: true
}
};
// set here so sendCertifiedEmail works properly
// not used otherwise
user[certType] = true;
user.challengeMap[id] = { completedDate: new Date() };
return Observable.combineLatest(
// update user data
user.update$(updateData),
// If user has committed to nonprofit,
// this will complete their pledge
completeCommitment$(user),
// sends notification email is user has all three certs
// if not it noop
sendCertifiedEmail(user, Email.send$),
({ count }, pledgeOrMessage) => ({ count, pledgeOrMessage })
)
.map(
({ count, pledgeOrMessage }) => {
if (typeof pledgeOrMessage === 'string') {
log(pledgeOrMessage);
}
log(`${count} documents updated`);
return true;
}
);
2015-10-02 18:47:36 +00:00
})
.subscribe(
2016-02-10 04:54:49 +00:00
(didCertify) => {
if (didCertify) {
2015-10-02 18:47:36 +00:00
return res.status(200).send(true);
}
return res.status(200).send(
dedent`
Looks like you have not completed the neccessary steps.
Please return to the challenge map.
2015-10-02 18:47:36 +00:00
`
);
},
next
);
}
function postHonest(req, res, next) {
2016-02-10 04:54:49 +00:00
return req.user.update$({ $set: { isHonest: true } }).subscribe(
() => res.status(200).send(true),
next
);
2015-10-02 18:47:36 +00:00
}
}