| 
									
										
										
										
											2015-08-09 22:14:31 -07:00
										 |  |  | import _ from 'lodash'; | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  | import debug from 'debug'; | 
					
						
							| 
									
										
										
										
											2016-01-09 20:08:01 -08:00
										 |  |  | import accepts from 'accepts'; | 
					
						
							| 
									
										
										
										
											2016-08-05 14:49:23 -07:00
										 |  |  | import dedent from 'dedent'; | 
					
						
							| 
									
										
										
										
											2015-11-09 17:27:56 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  | import { ifNoUserSend } from '../utils/middleware'; | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  | import { cachedMap } from '../utils/map'; | 
					
						
							|  |  |  | import createNameIdMap from '../../common/utils/create-name-id-map'; | 
					
						
							| 
									
										
										
										
											2016-08-05 14:49:23 -07:00
										 |  |  | import { | 
					
						
							|  |  |  |   checkMapData, | 
					
						
							|  |  |  |   getFirstChallenge | 
					
						
							|  |  |  | } from '../../common/utils/get-first-challenge'; | 
					
						
							| 
									
										
										
										
											2015-08-09 22:14:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  | const log = debug('fcc:boot:challenges'); | 
					
						
							| 
									
										
										
										
											2015-08-09 22:14:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08: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 12:01:00 -08:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |     updateData.$push = { | 
					
						
							| 
									
										
										
										
											2016-02-10 12:01:00 -08:00
										 |  |  |       progressTimestamps: { | 
					
						
							|  |  |  |         timestamp: Date.now(), | 
					
						
							|  |  |  |         completedChallenge: challengeId | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |     }; | 
					
						
							|  |  |  |     finalChallenge = completedChallenge; | 
					
						
							| 
									
										
										
										
											2015-06-20 19:52:37 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-10-01 21:44:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |   updateData.$set = { | 
					
						
							|  |  |  |     [`challengeMap.${challengeId}`]: finalChallenge | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( | 
					
						
							| 
									
										
										
										
											2016-02-10 10:05:51 -08:00
										 |  |  |     timezone && | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |     timezone !== 'UTC' && | 
					
						
							|  |  |  |     (!userTimezone || userTimezone === 'UTC') | 
					
						
							|  |  |  |   ) { | 
					
						
							|  |  |  |     updateData.$set = { | 
					
						
							|  |  |  |       ...updateData.$set, | 
					
						
							|  |  |  |       timezone: userTimezone | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-09 20:08:01 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |   log('user update data', updateData); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |   return { | 
					
						
							|  |  |  |     alreadyCompleted, | 
					
						
							|  |  |  |     updateData, | 
					
						
							|  |  |  |     completedDate: finalChallenge.completedDate, | 
					
						
							|  |  |  |     lastUpdated: finalChallenge.lastUpdated | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2015-06-20 19:52:37 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  | export default function(app) { | 
					
						
							| 
									
										
										
										
											2015-08-09 22:14:31 -07:00
										 |  |  |   const send200toNonUser = ifNoUserSend(true); | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |   const api = app.loopback.Router(); | 
					
						
							|  |  |  |   const router = app.loopback.Router(); | 
					
						
							|  |  |  |   const Block = app.models.Block; | 
					
						
							|  |  |  |   const map$ = cachedMap(Block); | 
					
						
							| 
									
										
										
										
											2015-06-22 16:43:31 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |   api.post( | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |     '/modern-challenge-completed', | 
					
						
							|  |  |  |     send200toNonUser, | 
					
						
							|  |  |  |     modernChallengeCompleted | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 11:26:33 -07:00
										 |  |  |   // deprecate endpoint
 | 
					
						
							|  |  |  |   // remove once new endpoint is live
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |   api.post( | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |     '/completed-challenge', | 
					
						
							| 
									
										
										
										
											2015-06-22 16:43:31 -07:00
										 |  |  |     send200toNonUser, | 
					
						
							|  |  |  |     completedChallenge | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |   api.post( | 
					
						
							| 
									
										
										
										
											2016-06-08 11:26:33 -07:00
										 |  |  |     '/challenge-completed', | 
					
						
							|  |  |  |     send200toNonUser, | 
					
						
							|  |  |  |     completedChallenge | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |   // deprecate endpoint
 | 
					
						
							|  |  |  |   // remove once new endpoint is live
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |   api.post( | 
					
						
							| 
									
										
										
										
											2015-06-22 16:43:31 -07:00
										 |  |  |     '/completed-zipline-or-basejump', | 
					
						
							|  |  |  |     send200toNonUser, | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |     projectCompleted | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |   api.post( | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |     '/project-completed', | 
					
						
							|  |  |  |     send200toNonUser, | 
					
						
							|  |  |  |     projectCompleted | 
					
						
							| 
									
										
										
										
											2015-06-22 16:43:31 -07:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2015-06-02 19:02:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 21:07:22 -08:00
										 |  |  |   api.post( | 
					
						
							|  |  |  |     '/backend-challenge-completed', | 
					
						
							|  |  |  |     send200toNonUser, | 
					
						
							|  |  |  |     backendChallengeCompleted | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |   router.get( | 
					
						
							|  |  |  |     '/challenges/current-challenge', | 
					
						
							|  |  |  |     redirectToCurrentChallenge | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   app.use(api); | 
					
						
							|  |  |  |   app.use('/:lang', router); | 
					
						
							| 
									
										
										
										
											2015-06-03 16:31:42 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07: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-02-14 17:10:26 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |     const errors = req.validationErrors(true); | 
					
						
							|  |  |  |     if (errors) { | 
					
						
							|  |  |  |       if (type === 'json') { | 
					
						
							|  |  |  |         return res.status(403).send({ errors }); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-02-14 17:10:26 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |       log('errors', errors); | 
					
						
							|  |  |  |       return res.sendStatus(403); | 
					
						
							| 
									
										
										
										
											2016-01-15 01:44:18 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-02-14 17:10:26 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |     const user = req.user; | 
					
						
							|  |  |  |     return user.getChallengeMap$() | 
					
						
							|  |  |  |       .flatMap(() => { | 
					
						
							|  |  |  |         const completedDate = Date.now(); | 
					
						
							|  |  |  |         const { | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           files | 
					
						
							|  |  |  |         } = req.body; | 
					
						
							| 
									
										
										
										
											2016-02-14 17:10:26 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |         const { | 
					
						
							|  |  |  |           alreadyCompleted, | 
					
						
							|  |  |  |           updateData, | 
					
						
							|  |  |  |           lastUpdated | 
					
						
							|  |  |  |         } = buildUserUpdate( | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |           user, | 
					
						
							|  |  |  |           id, | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |           { id, files, completedDate } | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2016-02-14 17:10:26 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |         const points = alreadyCompleted ? user.points : user.points + 1; | 
					
						
							| 
									
										
										
										
											2016-01-14 15:15:44 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |         return user.update$(updateData) | 
					
						
							|  |  |  |           .doOnNext(({ count }) => log('%s documents updated', count)) | 
					
						
							|  |  |  |           .map(() => { | 
					
						
							|  |  |  |             if (type === 'json') { | 
					
						
							|  |  |  |               return res.json({ | 
					
						
							|  |  |  |                 points, | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |                 alreadyCompleted, | 
					
						
							|  |  |  |                 completedDate, | 
					
						
							|  |  |  |                 lastUpdated | 
					
						
							| 
									
										
										
										
											2016-01-14 15:15:44 -08:00
										 |  |  |               }); | 
					
						
							| 
									
										
										
										
											2016-06-01 15:52:08 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |             return res.sendStatus(200); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2016-01-14 15:15:44 -08:00
										 |  |  |       }) | 
					
						
							|  |  |  |       .subscribe(() => {}, next); | 
					
						
							| 
									
										
										
										
											2015-05-16 00:39:43 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-02 19:02:54 -07:00
										 |  |  |   function completedChallenge(req, res, next) { | 
					
						
							| 
									
										
										
										
											2016-03-30 23:58:38 -04:00
										 |  |  |     req.checkBody('id', 'id must be an ObjectId').isMongoId(); | 
					
						
							| 
									
										
										
										
											2016-01-09 20:08:01 -08:00
										 |  |  |     const type = accepts(req).type('html', 'json', 'text'); | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |     const errors = req.validationErrors(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (errors) { | 
					
						
							|  |  |  |       if (type === 'json') { | 
					
						
							|  |  |  |         return res.status(403).send({ errors }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       log('errors', errors); | 
					
						
							|  |  |  |       return res.sendStatus(403); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |     return req.user.getChallengeMap$() | 
					
						
							|  |  |  |       .flatMap(() => { | 
					
						
							|  |  |  |         const completedDate = Date.now(); | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |         const { id, solution, timezone } = req.body; | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |         const { | 
					
						
							|  |  |  |           alreadyCompleted, | 
					
						
							|  |  |  |           updateData, | 
					
						
							|  |  |  |           lastUpdated | 
					
						
							|  |  |  |         } = buildUserUpdate( | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |           req.user, | 
					
						
							|  |  |  |           id, | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |           { id, solution, completedDate }, | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |           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, | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |                 alreadyCompleted, | 
					
						
							|  |  |  |                 completedDate, | 
					
						
							|  |  |  |                 lastUpdated | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |               }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return res.sendStatus(200); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .subscribe(() => {}, next); | 
					
						
							| 
									
										
										
										
											2015-05-20 21:50:31 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-05-19 22:31:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |   function projectCompleted(req, res, next) { | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |     const type = accepts(req).type('html', 'json', 'text'); | 
					
						
							|  |  |  |     req.checkBody('id', 'id must be an ObjectId').isMongoId(); | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |     req.checkBody('challengeType', 'must be a number').isNumber(); | 
					
						
							|  |  |  |     req.checkBody('solution', 'solution must be a URL').isURL(); | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const errors = req.validationErrors(true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (errors) { | 
					
						
							|  |  |  |       if (type === 'json') { | 
					
						
							|  |  |  |         return res.status(403).send({ errors }); | 
					
						
							| 
									
										
										
										
											2016-01-19 22:30:01 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |       log('errors', errors); | 
					
						
							|  |  |  |       return res.sendStatus(403); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-06-02 19:02:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |     const { user, body = {} } = req; | 
					
						
							| 
									
										
										
										
											2015-06-20 19:52:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |     const completedChallenge = _.pick( | 
					
						
							|  |  |  |       body, | 
					
						
							| 
									
										
										
										
											2016-06-08 11:11:13 -07:00
										 |  |  |       [ 'id', 'solution', 'githubLink', 'challengeType' ] | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |     ); | 
					
						
							|  |  |  |     completedChallenge.completedDate = Date.now(); | 
					
						
							| 
									
										
										
										
											2015-06-20 19:52:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-19 22:30:01 -08:00
										 |  |  |     if ( | 
					
						
							|  |  |  |       !completedChallenge.solution || | 
					
						
							|  |  |  |       // only basejumps require github links
 | 
					
						
							|  |  |  |       ( | 
					
						
							|  |  |  |         completedChallenge.challengeType === 4 && | 
					
						
							|  |  |  |         !completedChallenge.githubLink | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2015-06-02 19:02:54 -07:00
										 |  |  |       req.flash('errors', { | 
					
						
							|  |  |  |         msg: 'You haven\'t supplied the necessary URLs for us to inspect ' + | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |           'your work.' | 
					
						
							| 
									
										
										
										
											2015-06-02 19:02:54 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |       return res.sendStatus(403); | 
					
						
							| 
									
										
										
										
											2015-05-19 22:31:01 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-05-21 00:17:44 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-19 22:30:01 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-26 21:07:22 -08:00
										 |  |  |     return user.getChallengeMap$() | 
					
						
							|  |  |  |       .flatMap(() => { | 
					
						
							|  |  |  |         const { | 
					
						
							|  |  |  |           alreadyCompleted, | 
					
						
							|  |  |  |           updateData, | 
					
						
							|  |  |  |           lastUpdated | 
					
						
							|  |  |  |         } = 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, | 
					
						
							|  |  |  |                 completedDate: completedChallenge.completedDate, | 
					
						
							|  |  |  |                 lastUpdated | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return res.status(200).send(true); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .subscribe(() => {}, next); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function backendChallengeCompleted(req, res, next) { | 
					
						
							|  |  |  |     const type = accepts(req).type('html', 'json', 'text'); | 
					
						
							|  |  |  |     req.checkBody('id', 'id must be an ObjectId').isMongoId(); | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const { user, body = {} } = req; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const completedChallenge = _.pick( | 
					
						
							|  |  |  |       body, | 
					
						
							|  |  |  |       [ 'id', 'solution' ] | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     completedChallenge.completedDate = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |     return user.getChallengeMap$() | 
					
						
							|  |  |  |       .flatMap(() => { | 
					
						
							|  |  |  |         const { | 
					
						
							|  |  |  |           alreadyCompleted, | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |           updateData, | 
					
						
							|  |  |  |           lastUpdated | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |         } = buildUserUpdate(user, completedChallenge.id, completedChallenge); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return user.update$(updateData) | 
					
						
							|  |  |  |           .doOnNext(({ count }) => log('%s documents updated', count)) | 
					
						
							|  |  |  |           .doOnNext(() => { | 
					
						
							|  |  |  |             if (type === 'json') { | 
					
						
							|  |  |  |               return res.send({ | 
					
						
							|  |  |  |                 alreadyCompleted, | 
					
						
							| 
									
										
										
										
											2016-08-11 16:41:03 -07:00
										 |  |  |                 points: alreadyCompleted ? user.points : user.points + 1, | 
					
						
							|  |  |  |                 completedDate: completedChallenge.completedDate, | 
					
						
							|  |  |  |                 lastUpdated | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |               }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return res.status(200).send(true); | 
					
						
							| 
									
										
										
										
											2016-02-10 22:10:06 -08:00
										 |  |  |           }); | 
					
						
							|  |  |  |       }) | 
					
						
							| 
									
										
										
										
											2016-01-19 22:30:01 -08:00
										 |  |  |       .subscribe(() => {}, next); | 
					
						
							| 
									
										
										
										
											2015-05-19 22:31:01 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   function redirectToCurrentChallenge(req, res, next) { | 
					
						
							|  |  |  |     const { user } = req; | 
					
						
							|  |  |  |     return map$ | 
					
						
							|  |  |  |       .map(({ entities, result }) => ({ | 
					
						
							|  |  |  |         result, | 
					
						
							|  |  |  |         entities: createNameIdMap(entities) | 
					
						
							|  |  |  |       })) | 
					
						
							| 
									
										
										
										
											2016-08-05 14:49:23 -07:00
										 |  |  |       .map(map => { | 
					
						
							|  |  |  |         checkMapData(map); | 
					
						
							|  |  |  |         const { | 
					
						
							|  |  |  |           entities: { challenge: challengeMap, challengeIdToName } | 
					
						
							|  |  |  |         } = map; | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |         let finalChallenge; | 
					
						
							|  |  |  |         const dashedName = challengeIdToName[user && user.currentChallengeId]; | 
					
						
							|  |  |  |         finalChallenge = challengeMap[dashedName]; | 
					
						
							|  |  |  |         // redirect to first challenge
 | 
					
						
							|  |  |  |         if (!finalChallenge) { | 
					
						
							| 
									
										
										
										
											2016-08-05 14:49:23 -07:00
										 |  |  |           finalChallenge = getFirstChallenge(map); | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |         const { block, dashedName: finalDashedName } = finalChallenge || {}; | 
					
						
							| 
									
										
										
										
											2016-08-05 14:49:23 -07:00
										 |  |  |         if (!finalDashedName || !block) { | 
					
						
							|  |  |  |           // this should normally not be hit if database is properly seeded
 | 
					
						
							|  |  |  |           console.error(new Error(dedent`
 | 
					
						
							|  |  |  |             Attemped to find '${dashedName}' | 
					
						
							|  |  |  |             from '${user && user.currentChallengeId || 'no challenge id found'}' | 
					
						
							|  |  |  |             but came up empty. | 
					
						
							|  |  |  |             db may not be properly seeded. | 
					
						
							|  |  |  |           `));
 | 
					
						
							|  |  |  |           if (dashedName) { | 
					
						
							|  |  |  |             // attempt to find according to dashedName
 | 
					
						
							|  |  |  |             return `/challenges/${dashedName}`; | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-04 10:49:37 -07:00
										 |  |  |         return `/challenges/${block}/${finalDashedName}`; | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .subscribe( | 
					
						
							|  |  |  |         redirect => res.redirect(redirect || '/map'), | 
					
						
							|  |  |  |         next | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |