From 8ee9b5761a46973cddfbaadccde0b3423764f0ab Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Thu, 10 Mar 2022 21:41:33 +0100 Subject: [PATCH] fix: use keys in curriculum.json (#45407) * feat: add function to map from path to superblock * fix: use superblocks as keys in curriculum.json * fix: handle new superblock names * fix: keep deleting relational-database --- client/utils/build-challenges.js | 2 +- curriculum/getChallenges.js | 24 +++++---- curriculum/utils.js | 25 +++++++++ curriculum/{utils.test.js => utils.test.ts} | 53 ++++++++++++++++++- .../scripts/build/build-mobile-curriculum.js | 9 ++-- tsconfig.json | 1 + 6 files changed, 97 insertions(+), 17 deletions(-) rename curriculum/{utils.test.js => utils.test.ts} (65%) diff --git a/client/utils/build-challenges.js b/client/utils/build-challenges.js index a15a521aa4..b3d5f2dc6e 100644 --- a/client/utils/build-challenges.js +++ b/client/utils/build-challenges.js @@ -41,7 +41,7 @@ exports.buildChallenges = async function buildChallenges() { // temp removal of rdbms from production if (deploymentEnv !== 'staging') { - delete curriculum['13-relational-databases']; + delete curriculum['relational-database']; } const superBlocks = Object.keys(curriculum); diff --git a/curriculum/getChallenges.js b/curriculum/getChallenges.js index d91b202b3b..7c8ce4d675 100644 --- a/curriculum/getChallenges.js +++ b/curriculum/getChallenges.js @@ -18,7 +18,7 @@ const { const { isAuditedCert } = require('../utils/is-audited'); const { createPoly } = require('../utils/polyvinyl'); const { dasherize } = require('../utils/slugs'); -const { getSuperOrder } = require('./utils'); +const { getSuperOrder, getSuperBlockFromDir } = require('./utils'); const access = util.promisify(fs.access); @@ -172,7 +172,7 @@ exports.getChallengesForLang = async function getChallengesForLang(lang) { ); }; -async function buildBlocks({ basename: blockName }, curriculum, baseDir) { +async function buildBlocks({ basename: blockName }, curriculum, superBlock) { const metaPath = path.resolve( __dirname, `./challenges/_meta/${blockName}/meta.json` @@ -190,37 +190,41 @@ async function buildBlocks({ basename: blockName }, curriculum, baseDir) { if (!isUpcomingChange || showUpcomingChanges) { // add the block to the superBlock const blockInfo = { meta: blockMeta, challenges: [] }; - curriculum[baseDir].blocks[blockName] = blockInfo; + curriculum[superBlock].blocks[blockName] = blockInfo; } } catch (e) { - curriculum['00-certifications'].blocks[blockName] = { challenges: [] }; + if (e.code !== 'MODULE_NOT_FOUND') { + throw e; + } + curriculum['certifications'].blocks[blockName] = { challenges: [] }; } } async function buildSuperBlocks({ path, fullPath }, curriculum) { - const baseDir = getBaseDir(path); - curriculum[baseDir] = { blocks: {} }; + const superBlock = getSuperBlockFromDir(getBaseDir(path)); + curriculum[superBlock] = { blocks: {} }; - const cb = (file, curriculum) => buildBlocks(file, curriculum, baseDir); + const cb = (file, curriculum) => buildBlocks(file, curriculum, superBlock); return walk(fullPath, curriculum, { depth: 1, type: 'directories' }, cb); } async function buildChallenges({ path: filePath }, curriculum, lang) { // path is relative to getChallengesDirForLang(lang) const block = getBlockNameFromPath(filePath); - const baseDir = getBaseDir(filePath); + const superBlockDir = getBaseDir(filePath); + const superBlock = getSuperBlockFromDir(superBlockDir); let challengeBlock; // TODO: this try block and process exit can all go once errors terminate the // tests correctly. try { - challengeBlock = curriculum[baseDir].blocks[block]; + challengeBlock = curriculum[superBlock].blocks[block]; if (!challengeBlock) { // this should only happen when a isUpcomingChange block is skipped return; } } catch (e) { - console.log(`failed to create superBlock from ${baseDir}`); + console.log(`failed to create superBlock from ${superBlockDir}`); // eslint-disable-next-line no-process-exit process.exit(1); } diff --git a/curriculum/utils.js b/curriculum/utils.js index d68233a8bb..c1029d0232 100644 --- a/curriculum/utils.js +++ b/curriculum/utils.js @@ -53,4 +53,29 @@ function getSuperOrder( return order; } +const directoryToSuperblock = { + '00-certifications': 'certifications', // treating certifications as a superblock for simplicity + '01-responsive-web-design': 'responsive-web-design', + '02-javascript-algorithms-and-data-structures': + 'javascript-algorithms-and-data-structures', + '03-front-end-development-libraries': 'front-end-development-libraries', + '04-data-visualization': 'data-visualization', + '05-back-end-development-and-apis': 'back-end-development-and-apis', + '06-quality-assurance': 'quality-assurance', + '07-scientific-computing-with-python': 'scientific-computing-with-python', + '08-data-analysis-with-python': 'data-analysis-with-python', + '09-information-security': 'information-security', + '10-coding-interview-prep': 'coding-interview-prep', + '11-machine-learning-with-python': 'machine-learning-with-python', + '13-relational-databases': 'relational-database', + '14-responsive-web-design-22': '2022/responsive-web-design' +}; + +function getSuperBlockFromDir(dir) { + const superBlock = directoryToSuperblock[dir]; + if (!superBlock) throw Error(`${dir} does not map to a superblock`); + return directoryToSuperblock[dir]; +} + exports.getSuperOrder = getSuperOrder; +exports.getSuperBlockFromDir = getSuperBlockFromDir; diff --git a/curriculum/utils.test.js b/curriculum/utils.test.ts similarity index 65% rename from curriculum/utils.test.js rename to curriculum/utils.test.ts index 2f71941dfe..5f3cd2657c 100644 --- a/curriculum/utils.test.js +++ b/curriculum/utils.test.ts @@ -1,4 +1,10 @@ -import { getSuperOrder } from './utils'; +// utils are not typed (yet), so we have to disable some checks +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +import fs from 'fs'; +import path from 'path'; +import { SuperBlocks } from '../config/certification-settings'; +import { getSuperOrder, getSuperBlockFromDir } from './utils'; describe('getSuperOrder', () => { it('returns a number for valid superblocks', () => { @@ -86,3 +92,48 @@ describe('getSuperOrder', () => { ).toBe(12); }); }); + +describe('getSuperBlockFromPath', () => { + const directories = fs.readdirSync( + path.join(__dirname, './challenges/english') + ); + + it('handles all the directories in ./challenges/english', () => { + expect.assertions(14); + + for (const directory of directories) { + expect(() => getSuperBlockFromDir(directory)).not.toThrow(); + } + }); + + it("returns valid superblocks (or 'certifications') for all valid arguments", () => { + expect.assertions(14); + + const superBlockPaths = directories.filter(x => x !== '00-certifications'); + + for (const directory of superBlockPaths) { + expect(Object.values(SuperBlocks)).toContain( + getSuperBlockFromDir(directory) + ); + } + expect(getSuperBlockFromDir('00-certifications')).toBe('certifications'); + }); + + it("returns all valid superblocks (and 'certifications')", () => { + expect.assertions(1); + + const superBlocks = new Set(); + for (const directory of directories) { + superBlocks.add(getSuperBlockFromDir(directory)); + } + + // + 1 for 'certifications' + expect(superBlocks.size).toBe(Object.values(SuperBlocks).length + 1); + }); + + it('throws if a directory is unknown', () => { + expect.assertions(1); + + expect(() => getSuperBlockFromDir('unknown')).toThrow(); + }); +}); diff --git a/tools/scripts/build/build-mobile-curriculum.js b/tools/scripts/build/build-mobile-curriculum.js index 1e1d0d2e0c..970a5ffa7d 100644 --- a/tools/scripts/build/build-mobile-curriculum.js +++ b/tools/scripts/build/build-mobile-curriculum.js @@ -9,7 +9,7 @@ exports.buildMobileCurriculum = function buildMobileCurriculum(json) { function writeAndParseCurriculumJson(curriculum) { const superBlockKeys = Object.keys(curriculum).filter( - key => key !== '00-certifications' + key => key !== 'certifications' ); writeToFile('availableSuperblocks', { superblocks: superBlockKeys }); @@ -34,9 +34,8 @@ exports.buildMobileCurriculum = function buildMobileCurriculum(json) { } function writeToFile(filename, json) { - fs.writeFileSync( - `${mobileStaticPath}/mobile/${filename}.json`, - JSON.stringify(json, null, 2) - ); + const fullPath = `${mobileStaticPath}/mobile/${filename}.json`; + fs.mkdirSync(path.dirname(fullPath), { recursive: true }); + fs.writeFileSync(fullPath, JSON.stringify(json, null, 2)); } }; diff --git a/tsconfig.json b/tsconfig.json index 06605ad8d2..64d6d9ceb2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "client/plugins/**/*", "client/src/**/*", "client/utils/**/*", + "curriculum/*.test.ts", "tools/challenge-helper-scripts/**/*.ts", "tools/scripts/**/*.ts" ],