refactor: remove certIds and the associated map (#64299)

This commit is contained in:
Oliver Eyton-Williams
2025-12-04 14:01:27 +01:00
committed by GitHub
parent ff66ae89df
commit a38caeca39
8 changed files with 111 additions and 194 deletions
+8 -16
View File
@@ -1,26 +1,18 @@
import { Prisma } from '@prisma/client';
import {
certSlugTypeMap,
certIds
certToIdMap,
Certification
} from '../../../../shared/config/certification-settings.js';
import { normalizeDate } from '../../utils/normalize.js';
const {
legacyInfosecQaId,
respWebDesignId,
frontEndDevLibsId,
jsAlgoDataStructId,
dataVis2018Id,
apisMicroservicesId
} = certIds;
const fullStackCertificateIds = [
respWebDesignId,
jsAlgoDataStructId,
frontEndDevLibsId,
dataVis2018Id,
apisMicroservicesId,
legacyInfosecQaId
certToIdMap[Certification.RespWebDesign],
certToIdMap[Certification.JsAlgoDataStruct],
certToIdMap[Certification.FrontEndDevLibs],
certToIdMap[Certification.DataVis],
certToIdMap[Certification.BackEndDevApis],
certToIdMap[Certification.LegacyInfoSecQa]
];
/**
@@ -16,6 +16,8 @@ import {
setupServer,
superRequest
} from '../../../vitest.utils.js';
import { getChallenges } from '../../utils/get-challenges.js';
import { createCertLookup } from './certificate.js';
describe('certificate routes', () => {
setupServer();
@@ -461,3 +463,32 @@ describe('certificate routes', () => {
});
});
});
describe('createCertLookup', () => {
let challenges: ReturnType<typeof getChallenges>;
beforeAll(() => {
// TODO: create a mock challenges array specific to these tests.
challenges = getChallenges();
});
test('should create a lookup for all certifications', () => {
const certLookup = createCertLookup(challenges);
for (const cert of Object.values(Certification)) {
const certData = certLookup[cert];
expect(certData).toHaveProperty('id');
expect(certData).toHaveProperty('tests');
expect(certData).toHaveProperty('challengeType');
}
});
test('each certification should have a unique challenge id', () => {
const certLookup = createCertLookup(challenges);
const ids = Object.values(certLookup)
.map(({ id }) => id)
.sort();
const uniqueIds = Array.from(new Set(ids)).sort();
expect(uniqueIds).toEqual(ids);
});
});
+22 -105
View File
@@ -4,9 +4,9 @@ import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebo
import { getChallenges } from '../../utils/get-challenges.js';
import {
certIds,
Certification,
certSlugTypeMap,
certToIdMap,
certToTitleMap,
currentCertifications,
legacyCertifications,
@@ -83,10 +83,11 @@ function assertTestsExist(
}
}
function getCertById(
challengeId: string,
function getCertBySlug(
cert: Certification,
challenges: ReturnType<typeof getChallenges>
): { id: string; tests: { id: string }[]; challengeType: number } {
const challengeId = certToIdMap[cert];
const challengeById = challenges.filter(({ id }) => id === challengeId)[0];
if (!challengeById) {
throw new Error(`Challenge with id '${challengeId}' not found`);
@@ -96,112 +97,28 @@ function getCertById(
return { id, tests, challengeType };
}
function createCertLookup(
challenges: ReturnType<typeof getChallenges>
): Record<
type CertLookup = Record<
Certification,
{ id: string; tests: { id: string }[]; challengeType: number }
> {
return {
// legacy
[Certification.LegacyFrontEnd]: getCertById(
certIds.legacyFrontEndChallengeId,
challenges
),
[Certification.JsAlgoDataStruct]: getCertById(
certIds.jsAlgoDataStructId,
challenges
),
>;
[Certification.LegacyBackEnd]: getCertById(
certIds.legacyBackEndChallengeId,
challenges
),
[Certification.LegacyDataVis]: getCertById(
certIds.legacyDataVisId,
challenges
),
[Certification.LegacyInfoSecQa]: getCertById(
certIds.legacyInfosecQaId,
challenges
),
[Certification.LegacyFullStack]: getCertById(
certIds.legacyFullStackId,
challenges
),
/**
* Create a lookup from Certification enum values to their corresponding
* challenge metadata (id, tests and challengeType) using the provided
* challenges array.
*
* @param challenges - The array returned by getChallenges().
* @returns A record mapping each Certification to an object with id, tests and challengeType.
*/
export function createCertLookup(
challenges: ReturnType<typeof getChallenges>
): CertLookup {
const certLookup = {} as CertLookup;
// modern
[Certification.RespWebDesign]: getCertById(
certIds.respWebDesignId,
challenges
),
[Certification.FrontEndDevLibs]: getCertById(
certIds.frontEndDevLibsId,
challenges
),
[Certification.DataVis]: getCertById(certIds.dataVis2018Id, challenges),
[Certification.JsAlgoDataStructNew]: getCertById(
certIds.jsAlgoDataStructV8Id,
challenges
),
[Certification.BackEndDevApis]: getCertById(
certIds.apisMicroservicesId,
challenges
),
[Certification.QualityAssurance]: getCertById(certIds.qaV7Id, challenges),
[Certification.InfoSec]: getCertById(certIds.infosecV7Id, challenges),
[Certification.SciCompPy]: getCertById(certIds.sciCompPyV7Id, challenges),
[Certification.DataAnalysisPy]: getCertById(
certIds.dataAnalysisPyV7Id,
challenges
),
[Certification.MachineLearningPy]: getCertById(
certIds.machineLearningPyV7Id,
challenges
),
[Certification.RelationalDb]: getCertById(
certIds.relationalDatabaseV8Id,
challenges
),
[Certification.CollegeAlgebraPy]: getCertById(
certIds.collegeAlgebraPyV8Id,
challenges
),
[Certification.FoundationalCSharp]: getCertById(
certIds.foundationalCSharpV8Id,
challenges
),
[Certification.JsV9]: getCertById(certIds.javascriptV9Id, challenges),
[Certification.RespWebDesignV9]: getCertById(
certIds.respWebDesignV9Id,
challenges
),
[Certification.A2English]: getCertById(certIds.a2EnglishId, challenges),
// upcoming
[Certification.FrontEndDevLibsV9]: getCertById(
certIds.frontEndLibsV9Id,
challenges
),
[Certification.PythonV9]: getCertById(certIds.pythonV9Id, challenges),
[Certification.RelationalDbV9]: getCertById(
certIds.relationalDbV9Id,
challenges
),
[Certification.BackEndDevApisV9]: getCertById(
certIds.backEndDevApisV9Id,
challenges
),
[Certification.FullStackDeveloperV9]: getCertById(
certIds.fullStackDeveloperV9Id,
challenges
),
[Certification.B1English]: getCertById(certIds.b1EnglishId, challenges),
[Certification.A2Spanish]: getCertById(certIds.a2SpanishId, challenges),
[Certification.A1Chinese]: getCertById(certIds.a1ChineseId, challenges),
[Certification.A2Chinese]: getCertById(certIds.a2ChineseId, challenges)
};
for (const cert of Object.values(Certification)) {
certLookup[cert] = getCertBySlug(cert, challenges);
}
return certLookup;
}
interface CertI {
+2 -2
View File
@@ -5,7 +5,7 @@ import * as schemas from '../../schemas.js';
import {
certSlugTypeMap,
certToTitleMap,
certTypeIdMap,
certToIdMap,
completionHours,
oldDataVizId
} from '../../../../shared/config/certification-settings.js';
@@ -52,7 +52,7 @@ export const unprotectedCertificateRoutes: FastifyPluginCallbackTypebox = (
}
const certType = certSlugTypeMap[certSlug];
const certId = certTypeIdMap[certType];
const certId = certToIdMap[certSlug];
const certTitle = certToTitleMap[certSlug];
const completionTime = completionHours[certType] || 300;
const user = await fastify.prisma.user.findFirst({
+6 -6
View File
@@ -1,7 +1,7 @@
import { createSelector } from 'reselect';
import { liveCerts } from '../../config/cert-and-project-map';
import {
certTypeIdMap,
certSlugTypeMap,
certToTitleMap
} from '../../../shared-dist/config/certification-settings.js';
@@ -231,9 +231,9 @@ export const claimableCertsSelector = createSelector([userSelector], user => {
({ id }) => id
);
const isClaimedById = Object.entries(certTypeIdMap).reduce(
(acc, [userFlag, certId]) => {
acc[certId] = Boolean(user[userFlag]);
const isClaimedByCert = Object.entries(certSlugTypeMap).reduce(
(acc, [cert, userFlag]) => {
acc[cert] = Boolean(user[userFlag]);
return acc;
},
{}
@@ -241,9 +241,9 @@ export const claimableCertsSelector = createSelector([userSelector], user => {
const claimable = [];
for (const { id, projects, certSlug } of liveCerts) {
for (const { projects, certSlug } of liveCerts) {
if (!projects) continue;
if (isClaimedById[id]) continue;
if (isClaimedByCert[certSlug]) continue;
const projectIds = projects.map(p => p.id);
const allProjectsComplete = projectIds.every(id =>
+2 -5
View File
@@ -10,10 +10,7 @@ import {
import store from 'store';
import { navigate } from 'gatsby';
import {
certTypeIdMap,
certTypes
} from '../../../../shared-dist/config/certification-settings';
import { Certification } from '../../../../shared-dist/config/certification-settings';
import { createFlashMessage } from '../../components/Flash/redux';
import { liveCerts } from '../../../config/cert-and-project-map';
import {
@@ -191,7 +188,7 @@ function* verifyCertificationSaga({ payload }) {
// (20/06/2022) Full Stack client-side validation is already done here:
// https://github.com/freeCodeCamp/freeCodeCamp/blob/main/client/src/components/settings/certification.js#L309
if (currentCert?.id !== certTypeIdMap[certTypes.fullStack]) {
if (currentCert?.certSlug !== Certification.LegacyFullStack) {
const flash = {
type: 'info',
message: 'flash.incomplete-steps',
+3 -3
View File
@@ -3,7 +3,7 @@ import {
Certification,
linkedInCredentialIds,
certToTitleMap,
certIds
certToIdMap
} from './certification-settings';
describe('linkedInCredentialIds', () => {
@@ -24,9 +24,9 @@ describe('certToTitleMap', () => {
});
});
describe('certIds', () => {
describe('certToIdMap', () => {
it('should have no duplicate values', () => {
const ids = Object.values(certIds).sort();
const ids = Object.values(certToIdMap).sort();
const uniqueIds = Array.from(new Set(ids)).sort();
expect(uniqueIds).toEqual(ids);
+37 -57
View File
@@ -127,38 +127,43 @@ export const certTypes = {
a2English: 'isA2EnglishCert'
} as const;
export const certIds = {
legacyFrontEndChallengeId: '561add10cb82ac38a17513be',
legacyBackEndChallengeId: '660add10cb82ac38a17513be',
legacyDataVisId: '561add10cb82ac39a17513bc',
legacyInfosecQaId: '561add10cb82ac38a17213bc',
legacyFullStackId: '561add10cb82ac38a17213bd',
respWebDesignId: '561add10cb82ac38a17513bc',
frontEndDevLibsId: '561acd10cb82ac38a17513bc',
dataVis2018Id: '5a553ca864b52e1d8bceea14',
jsAlgoDataStructId: '561abd10cb81ac38a17513bc',
apisMicroservicesId: '561add10cb82ac38a17523bc',
qaV7Id: '5e611829481575a52dc59c0e',
infosecV7Id: '5e6021435ac9d0ecd8b94b00',
sciCompPyV7Id: '5e44431b903586ffb414c951',
dataAnalysisPyV7Id: '5e46fc95ac417301a38fb934',
machineLearningPyV7Id: '5e46fc95ac417301a38fb935',
relationalDatabaseV8Id: '606243f50267e718b1e755f4',
collegeAlgebraPyV8Id: '61531b20cc9dfa2741a5b800',
foundationalCSharpV8Id: '647f7da207d29547b3bee1ba',
jsAlgoDataStructV8Id: '658180220947283cdc0689ce',
respWebDesignV9Id: '68db314d3c11a8bff07c7535',
javascriptV9Id: '68c4069c1ef859270e17c495',
frontEndLibsV9Id: '68e008aa5f80c6099d47b3a2',
pythonV9Id: '68e6bd5020effa1586e79855',
relationalDbV9Id: '68e6bd5120effa1586e79856',
backEndDevApisV9Id: '68e6bd5120effa1586e79857',
fullStackDeveloperV9Id: '64514fda6c245de4d11eb7bb',
a2EnglishId: '651dd7e01d697d0aab7833b7',
b1EnglishId: '66607e53317411dd5e8aae21',
a2SpanishId: '681a6b22e5a782fe3459984a',
a1ChineseId: '68f1268149f045a650d4229e',
a2ChineseId: '682c3153086dd7cabe7f48bc'
export const certToIdMap: Record<Certification, string> = {
// Legacy certifications
[Certification.LegacyFrontEnd]: '561add10cb82ac38a17513be',
[Certification.JsAlgoDataStruct]: '561abd10cb81ac38a17513bc',
[Certification.LegacyBackEnd]: '660add10cb82ac38a17513be',
[Certification.LegacyDataVis]: '561add10cb82ac39a17513bc',
[Certification.LegacyInfoSecQa]: '561add10cb82ac38a17213bc',
[Certification.LegacyFullStack]: '561add10cb82ac38a17213bd',
// Current certifications
[Certification.RespWebDesign]: '561add10cb82ac38a17513bc',
[Certification.JsAlgoDataStructNew]: '658180220947283cdc0689ce',
[Certification.FrontEndDevLibs]: '561acd10cb82ac38a17513bc',
[Certification.DataVis]: '5a553ca864b52e1d8bceea14',
[Certification.BackEndDevApis]: '561add10cb82ac38a17523bc',
[Certification.QualityAssurance]: '5e611829481575a52dc59c0e',
[Certification.InfoSec]: '5e6021435ac9d0ecd8b94b00',
[Certification.SciCompPy]: '5e44431b903586ffb414c951',
[Certification.DataAnalysisPy]: '5e46fc95ac417301a38fb934',
[Certification.MachineLearningPy]: '5e46fc95ac417301a38fb935',
[Certification.RelationalDb]: '606243f50267e718b1e755f4',
[Certification.CollegeAlgebraPy]: '61531b20cc9dfa2741a5b800',
[Certification.FoundationalCSharp]: '647f7da207d29547b3bee1ba',
[Certification.A2English]: '651dd7e01d697d0aab7833b7',
// Upcoming certifications
[Certification.RespWebDesignV9]: '68db314d3c11a8bff07c7535',
[Certification.JsV9]: '68c4069c1ef859270e17c495',
[Certification.FrontEndDevLibsV9]: '68e008aa5f80c6099d47b3a2',
[Certification.PythonV9]: '68e6bd5020effa1586e79855',
[Certification.RelationalDbV9]: '68e6bd5120effa1586e79856',
[Certification.BackEndDevApisV9]: '68e6bd5120effa1586e79857',
[Certification.FullStackDeveloperV9]: '64514fda6c245de4d11eb7bb',
[Certification.B1English]: '66607e53317411dd5e8aae21',
[Certification.A2Spanish]: '681a6b22e5a782fe3459984a',
[Certification.A2Chinese]: '682c3153086dd7cabe7f48bc',
[Certification.A1Chinese]: '68f1268149f045a650d4229e'
};
export const completionHours = {
@@ -248,31 +253,6 @@ export const superBlockCertTypeMap = {
[SuperBlocks.A2English]: certTypes.a2English
};
export const certTypeIdMap = {
[certTypes.frontEnd]: certIds.legacyFrontEndChallengeId,
[certTypes.backEnd]: certIds.legacyBackEndChallengeId,
[certTypes.dataVis]: certIds.legacyDataVisId,
[certTypes.infosecQa]: certIds.legacyInfosecQaId,
[certTypes.fullStack]: certIds.legacyFullStackId,
[certTypes.respWebDesign]: certIds.respWebDesignId,
[certTypes.respWebDesignV9]: certIds.respWebDesignV9Id,
[certTypes.frontEndDevLibs]: certIds.frontEndDevLibsId,
[certTypes.jsAlgoDataStruct]: certIds.jsAlgoDataStructId,
[certTypes.dataVis2018]: certIds.dataVis2018Id,
[certTypes.apisMicroservices]: certIds.apisMicroservicesId,
[certTypes.qaV7]: certIds.qaV7Id,
[certTypes.infosecV7]: certIds.infosecV7Id,
[certTypes.sciCompPyV7]: certIds.sciCompPyV7Id,
[certTypes.dataAnalysisPyV7]: certIds.dataAnalysisPyV7Id,
[certTypes.machineLearningPyV7]: certIds.machineLearningPyV7Id,
[certTypes.relationalDatabaseV8]: certIds.relationalDatabaseV8Id,
[certTypes.collegeAlgebraPyV8]: certIds.collegeAlgebraPyV8Id,
[certTypes.foundationalCSharpV8]: certIds.foundationalCSharpV8Id,
[certTypes.jsAlgoDataStructV8]: certIds.jsAlgoDataStructV8Id,
[certTypes.javascriptV9]: certIds.javascriptV9Id,
[certTypes.a2English]: certIds.a2EnglishId
};
// TODO: use i18n keys instead of hardcoded titles
export const certToTitleMap: Record<Certification, string> = {
// Legacy certifications