fix: make superblock data paths match normal paths (#46406)

This commit is contained in:
Oliver Eyton-Williams
2022-06-24 08:12:16 -05:00
committed by GitHub
parent 4f6a006076
commit 232b6d232a
6 changed files with 153 additions and 122 deletions
+8 -3
View File
@@ -44287,7 +44287,8 @@
},
"node_modules/readdirp": {
"version": "3.6.0",
"license": "MIT",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -53744,7 +53745,8 @@
"license": "BSD-3-Clause",
"devDependencies": {
"debug": "4.3.2",
"dotenv": "10.0.0"
"dotenv": "10.0.0",
"readdirp": "^3.6.0"
},
"engines": {
"node": ">=16",
@@ -56128,7 +56130,8 @@
"version": "file:tools/scripts/build",
"requires": {
"debug": "4.3.2",
"dotenv": "10.0.0"
"dotenv": "10.0.0",
"readdirp": "*"
},
"dependencies": {
"debug": {
@@ -83946,6 +83949,8 @@
},
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"requires": {
"picomatch": "^2.2.1"
}
@@ -0,0 +1,97 @@
import path from 'path';
import fs from 'fs';
import readdirp from 'readdirp';
import { AssertionError } from 'chai';
import envData from '../../../config/env.json';
import { SuperBlocks } from '../../../config/certification-settings';
import {
superblockSchemaValidator,
availableSuperBlocksValidator
} from './external-data-schema';
import { orderedSuperBlockInfo } from './build-external-curricula-data';
if (envData.clientLocale == 'english' && !envData.showUpcomingChanges) {
const VERSION = 'v1';
describe('external curriculum data build', () => {
const clientStaticPath = path.resolve(__dirname, '../../../client/static');
const validateSuperBlock = superblockSchemaValidator();
test("the external curriculum data should be in the client's static directory", () => {
expect(
fs.existsSync(`${clientStaticPath}/curriculum-data/${VERSION}`)
).toBe(true);
expect(
fs.readdirSync(`${clientStaticPath}/curriculum-data/${VERSION}`).length
).toBeGreaterThan(0);
});
test('the available-superblocks file should have the correct structure', async () => {
const validateAvailableSuperBlocks = availableSuperBlocksValidator();
const availableSuperblocks: unknown = JSON.parse(
await fs.promises.readFile(
`${clientStaticPath}/curriculum-data/${VERSION}/available-superblocks.json`,
'utf-8'
)
);
const result = validateAvailableSuperBlocks(availableSuperblocks);
if (result.error) {
throw new AssertionError(
result.error.toString(),
`file: available-superblocks.json`
);
}
});
test('the files generated should have the correct schema', async () => {
const fileArray = (
await readdirp.promise(`${clientStaticPath}/curriculum-data/${VERSION}`)
).map(file => file.path);
fileArray
.filter(fileInArray => fileInArray !== 'available-superblocks.json')
.forEach(fileInArray => {
const fileContent = fs.readFileSync(
`${clientStaticPath}/curriculum-data/${VERSION}/${fileInArray}`,
'utf-8'
);
const result = validateSuperBlock(JSON.parse(fileContent));
if (result.error) {
throw new AssertionError(
result.error.toString(),
`file: ${fileInArray}`
);
}
});
});
test('All public SuperBlocks should be present in the SuperBlock object', () => {
const dashedNames = orderedSuperBlockInfo.map(
({ dashedName }) => dashedName
);
// TODO: this is a hack, we should have a single source of truth for the
// list of superblocks that are available.
const publicSuperBlockNames = Object.values(SuperBlocks).filter(
x => x !== '2022/javascript-algorithms-and-data-structures'
);
expect(dashedNames).toEqual(
expect.arrayContaining(publicSuperBlockNames)
);
expect(Object.keys(orderedSuperBlockInfo)).toHaveLength(
publicSuperBlockNames.length
);
});
});
} else {
describe.skip('External curriculum data is localized', () => {
test.todo('localized tests');
});
}
@@ -1,11 +1,9 @@
import { mkdirSync, writeFileSync, readFileSync } from 'fs';
import { resolve } from 'path';
import { resolve, dirname } from 'path';
import { SuperBlocks } from '../../../config/certification-settings';
type Intro = { [keyValue in SuperBlocks]: IntroProps };
export type Curriculum = { [keyValue in SuperBlocks]: CurriculumProps };
type SuperBlockKeys = keyof typeof SuperBlocks;
type SuperBlockValues = typeof SuperBlocks[SuperBlockKeys];
interface IntroProps extends CurriculumProps {
title: string;
@@ -23,22 +21,24 @@ interface Block {
meta: Record<string, unknown>;
}
export const superBlockMobileAppOrder = [
{ dashedName: '2022/responsive-web-design', public: false },
{ dashedName: 'responsive-web-design', public: true },
{ dashedName: 'javascript-algorithms-and-data-structures', public: true },
{ dashedName: 'front-end-development-libraries', public: false },
{ dashedName: 'data-visualization', public: false },
{ dashedName: 'back-end-development-and-apis', public: false },
{ dashedName: 'quality-assurance', public: false },
{ dashedName: 'scientific-computing-with-python', public: false },
{ dashedName: 'data-analysis-with-python', public: false },
{ dashedName: 'information-security', public: false },
{ dashedName: 'machine-learning-with-python', public: false },
{ dashedName: 'coding-interview-prep', public: false },
{ dashedName: 'relational-database', public: false }
export const orderedSuperBlockInfo = [
{ dashedName: SuperBlocks.RespWebDesignNew, public: false },
{ dashedName: SuperBlocks.RespWebDesign, public: true },
{ dashedName: SuperBlocks.JsAlgoDataStruct, public: true },
{ dashedName: SuperBlocks.FrontEndDevLibs, public: false },
{ dashedName: SuperBlocks.DataVis, public: false },
{ dashedName: SuperBlocks.BackEndDevApis, public: false },
{ dashedName: SuperBlocks.QualityAssurance, public: false },
{ dashedName: SuperBlocks.SciCompPy, public: false },
{ dashedName: SuperBlocks.DataAnalysisPy, public: false },
{ dashedName: SuperBlocks.InfoSec, public: false },
{ dashedName: SuperBlocks.MachineLearningPy, public: false },
{ dashedName: SuperBlocks.CodingInterviewPrep, public: false },
{ dashedName: SuperBlocks.RelationalDb, public: false }
];
const dashedNames = orderedSuperBlockInfo.map(({ dashedName }) => dashedName);
export function buildExtCurriculumData(
ver: string,
curriculum: Curriculum
@@ -55,21 +55,20 @@ export function buildExtCurriculumData(
parseCurriculumData();
function parseCurriculumData() {
const superBlockKeys = Object.values(SuperBlocks);
const superBlockKeys = Object.values(SuperBlocks).filter(x =>
dashedNames.includes(x)
);
writeToFile('available-superblocks', {
superblocks: [
superBlockMobileAppOrder,
Object.values(SuperBlocks).map(superblock =>
getSuperBlockName(superblock)
)
]
superblocks: orderedSuperBlockInfo.map(x => ({
...x,
title: getSuperBlockTitle(x.dashedName)
}))
});
for (let i = 0; i < superBlockKeys.length; i++) {
for (const superBlockKey of superBlockKeys) {
const superBlock = <Curriculum>{};
const superBlockKey = Object.values(SuperBlocks)[i];
const blockNames = Object.keys(curriculum[superBlockKeys[i]].blocks);
const blockNames = Object.keys(curriculum[superBlockKey].blocks);
if (blockNames.length === 0) continue;
@@ -86,20 +85,18 @@ export function buildExtCurriculumData(
curriculum[superBlockKey]['blocks'][blockNames[j]]['meta'];
}
writeToFile(superBlockKeys[i].replace(/\//, '-'), superBlock);
writeToFile(superBlockKey, superBlock);
}
}
function writeToFile(fileName: string, data: Record<string, unknown>): void {
mkdirSync(versionPath, { recursive: true });
const filePath = `${versionPath}/${fileName}.json`;
mkdirSync(dirname(filePath), { recursive: true });
writeFileSync(filePath, JSON.stringify(data, null, 2));
}
function getBlockDescription(
superBlockKeys: SuperBlockValues,
superBlockKeys: SuperBlocks,
blockKey: string
): string[] {
const intros = JSON.parse(readFileSync(blockIntroPath, 'utf-8')) as Intro;
@@ -107,11 +104,11 @@ export function buildExtCurriculumData(
return intros[superBlockKeys]['blocks'][blockKey]['intro'];
}
function getSuperBlockName(superBlockKeys: SuperBlockValues): string {
function getSuperBlockTitle(superBlock: SuperBlocks): string {
const superBlocks = JSON.parse(
readFileSync(blockIntroPath, 'utf-8')
) as Intro;
return superBlocks[superBlockKeys].title;
return superBlocks[superBlock].title;
}
}
@@ -27,6 +27,18 @@ const schema = Joi.object({}).pattern(
Joi.object().concat(subSchema)
);
exports.mobileSchemaValidator = () => {
return superblock => schema.validate(superblock);
};
const availableSuperBlocksSchema = Joi.object({
superblocks: Joi.array().items(
Joi.object({
dashedName: Joi.string().required(),
title: Joi.string().required(),
public: Joi.bool().required()
})
)
});
exports.superblockSchemaValidator = () => superblock =>
schema.validate(superblock);
exports.availableSuperBlocksValidator = () => data =>
availableSuperBlocksSchema.validate(data);
@@ -1,81 +0,0 @@
import path from 'path';
import fs from 'fs';
import { AssertionError } from 'chai';
import envData from '../../../config/env.json';
import { SuperBlocks } from '../../../config/certification-settings';
import { mobileSchemaValidator } from './mobileSchema';
import { superBlockMobileAppOrder } from './build-external-curricula-data';
if (envData.clientLocale == 'english' && !envData.showUpcomingChanges) {
const VERSION = 'v1';
describe('mobile curriculum build', () => {
const mobileStaticPath = path.resolve(__dirname, '../../../client/static');
const blockIntroPath = path.resolve(
__dirname,
'../../../client/i18n/locales/english/intro.json'
);
const validateMobileSuperBlock = mobileSchemaValidator();
test('the mobile curriculum should have a static folder with multiple files', () => {
expect(
fs.existsSync(`${mobileStaticPath}/curriculum-data/${VERSION}`)
).toBe(true);
expect(
fs.readdirSync(`${mobileStaticPath}/curriculum-data/${VERSION}`).length
).toBeGreaterThan(0);
});
test('the mobile curriculum should have access to the intro.json file', () => {
expect(fs.existsSync(blockIntroPath)).toBe(true);
});
test('the files generated should have the correct schema', () => {
const fileArray = fs.readdirSync(
`${mobileStaticPath}/curriculum-data/${VERSION}`
);
fileArray
.filter(fileInArray => fileInArray !== 'available-superblocks.json')
.forEach(fileInArray => {
const fileContent = fs.readFileSync(
`${mobileStaticPath}/curriculum-data/${VERSION}/${fileInArray}`,
'utf-8'
);
const result = validateMobileSuperBlock(JSON.parse(fileContent));
if (result.error) {
throw new AssertionError(
result.error.toString(),
`file: ${fileInArray}`
);
}
});
});
test('All SuperBlocks should be present in the mobile SuperBlock object', () => {
const dashedNames = superBlockMobileAppOrder.map(
({ dashedName }) => dashedName
);
// TODO: this is a hack, we should have a single source of truth for the
// list of superblocks that are available.
const publicSuperBlockNames = Object.values(SuperBlocks).filter(
x => x !== '2022/javascript-algorithms-and-data-structures'
);
expect(dashedNames).toEqual(
expect.arrayContaining(publicSuperBlockNames)
);
expect(Object.keys(superBlockMobileAppOrder)).toHaveLength(
publicSuperBlockNames.length
);
});
});
} else {
describe.skip('Mobile curriculum is not localized', () => {
test.todo('localized tests');
});
}
+2 -1
View File
@@ -20,6 +20,7 @@
"main": "none",
"devDependencies": {
"debug": "4.3.2",
"dotenv": "10.0.0"
"dotenv": "10.0.0",
"readdirp": "^3.6.0"
}
}