diff --git a/common/app/routes/challenges/utils.js b/common/app/routes/challenges/utils.js index af21c8819f..8eda75aabc 100644 --- a/common/app/routes/challenges/utils.js +++ b/common/app/routes/challenges/utils.js @@ -1,5 +1,6 @@ import { compose } from 'redux'; import { bonfire, html, js } from '../../utils/challengeTypes'; +import protect from '../../utils/empty-protector'; export function encodeScriptTags(value) { return value @@ -78,19 +79,6 @@ export function loggerToStr(args) { .reduce((str, arg) => str + arg + '\n', ''); } -export function getFirstChallenge( - { superBlock, block, challenge }, - result -) { - return challenge[ - block[ - superBlock[ - result[0] - ].blocks[0] - ].challenges[0] - ]; -} - export function getNextChallenge( current, entities, @@ -285,20 +273,6 @@ export function getMouse(e, [dx, dy]) { return [pageX - dx, pageY - dy]; } -const emptyProtector = { - blocks: [], - challenges: [] -}; -// protect against malformed data -function protect(block) { - // if no block or block has no challenges or blocks - // use protector - if (!block || !(block.challenges || block.blocks)) { - return emptyProtector; - } - return block; -} - // interface Node { // isHidden: Boolean, // children: Void|[ ...Node ], diff --git a/common/app/utils/empty-protector.js b/common/app/utils/empty-protector.js new file mode 100644 index 0000000000..517ecba917 --- /dev/null +++ b/common/app/utils/empty-protector.js @@ -0,0 +1,13 @@ +const emptyProtector = { + blocks: [], + challenges: [] +}; +// protect against malformed map data +// protect(block: { challenges: [], block: [] }|Void) => block|emptyProtector +export default function protect(block) { + // if no block or block has no challenges or blocks + if (!block || !(block.challenges || block.blocks)) { + return emptyProtector; + } + return block; +} diff --git a/common/utils/get-first-challenge.js b/common/utils/get-first-challenge.js new file mode 100644 index 0000000000..0ce74e8a02 --- /dev/null +++ b/common/utils/get-first-challenge.js @@ -0,0 +1,44 @@ +import emptyProtector from '../app/utils/empty-protector'; + +export function checkMapData( + { + entities: { + challenge, + block, + superBlock, + challengeIdToName + }, + result + } +) { + if ( + !challenge || + !block || + !superBlock || + !challengeIdToName || + !result || + !result.length + ) { + throw new Error( + 'entities not found, db may not be properly seeded. Crashing hard' + ); + } +} +// getFirstChallenge( +// map: { +// entities: { challenge: Object, block: Object, superBlock: Object }, +// result: [...superBlockDashedName: String] +// } +// ) => Challenge|Void +export function getFirstChallenge({ + entities: { superBlock, block, challenge }, + result +}) { + return challenge[ + emptyProtector(block[ + emptyProtector(superBlock[ + result[0] + ]).blocks[0] + ]).challenges[0] + ]; +} diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 60312d019f..0f7600585d 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -1,11 +1,15 @@ import _ from 'lodash'; -// import { Observable, Scheduler } from 'rx'; import debug from 'debug'; import accepts from 'accepts'; +import dedent from 'dedent'; import { ifNoUserSend } from '../utils/middleware'; import { cachedMap } from '../utils/map'; import createNameIdMap from '../../common/utils/create-name-id-map'; +import { + checkMapData, + getFirstChallenge +} from '../../common/utils/get-first-challenge'; const log = debug('fcc:boot:challenges'); @@ -274,40 +278,34 @@ export default function(app) { result, entities: createNameIdMap(entities) })) - .map(({ - result, - entities: { - challenge: challengeMap, - block: blockMap, - superBlock: superBlockMap, - challengeIdToName - } - }) => { + .map(map => { + checkMapData(map); + const { + entities: { challenge: challengeMap, challengeIdToName } + } = map; let finalChallenge; const dashedName = challengeIdToName[user && user.currentChallengeId]; finalChallenge = challengeMap[dashedName]; - if ( - !challengeMap || - !blockMap || - !superBlockMap || - !result || - !result.length - ) { - throw new Error( - 'entities not found, db may not be properly seeded. Crashing hard' - ); - } // redirect to first challenge if (!finalChallenge) { - finalChallenge = challengeMap[ - block[ - superBlockMap[ - result[0] - ].blocks[0] - ].challenges[0] - ]; + finalChallenge = getFirstChallenge(map); } const { block, dashedName: finalDashedName } = finalChallenge || {}; + 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; + } + } return `/challenges/${block}/${finalDashedName}`; }) .subscribe(