mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
fix(api): handle string challengeType (#60491)
This commit is contained in:
committed by
GitHub
parent
74aa61a7b4
commit
8138f086aa
@@ -19,7 +19,7 @@ type File {
|
||||
}
|
||||
|
||||
type CompletedChallenge {
|
||||
challengeType Int? @db.Int // Null | Undefined
|
||||
challengeType Json? // Null | Undefined | String | Int
|
||||
completedDate Json // DateTime | Float, but not, as far as we know, Null
|
||||
files File[]
|
||||
githubLink String? // Undefined
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
verifyTrophyWithMicrosoft
|
||||
} from '../helpers/challenge-helpers';
|
||||
import { UpdateReqType } from '../../utils';
|
||||
import { normalizeDate } from '../../utils/normalize';
|
||||
import { normalizeChallengeType, normalizeDate } from '../../utils/normalize';
|
||||
|
||||
interface JwtPayload {
|
||||
userToken: string;
|
||||
@@ -670,8 +670,13 @@ export const challengeRoutes: FastifyPluginCallbackTypebox = (
|
||||
|
||||
const newCompletedChallenges: CompletedChallenge[] =
|
||||
completedChallenges.map(c => {
|
||||
const { completedDate, ...rest } = c;
|
||||
return { completedDate: normalizeDate(completedDate), ...rest };
|
||||
const { completedDate, challengeType, ...rest } = c;
|
||||
|
||||
return {
|
||||
completedDate: normalizeDate(completedDate),
|
||||
challengeType: normalizeChallengeType(challengeType),
|
||||
...rest
|
||||
};
|
||||
});
|
||||
const newCompletedExams: CompletedExam[] = completedExams;
|
||||
const newProgressTimeStamps = progressTimestamps as ProgressTimestamp[];
|
||||
|
||||
@@ -3,7 +3,8 @@ import {
|
||||
normalizeProfileUI,
|
||||
normalizeChallenges,
|
||||
normalizeFlags,
|
||||
normalizeDate
|
||||
normalizeDate,
|
||||
normalizeChallengeType
|
||||
} from './normalize';
|
||||
|
||||
describe('normalize', () => {
|
||||
@@ -158,7 +159,7 @@ describe('normalize', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateDate', () => {
|
||||
describe('normalizeDate', () => {
|
||||
it('should return the date as a number', () => {
|
||||
expect(normalizeDate(1)).toEqual(1);
|
||||
expect(normalizeDate({ $date: '2023-10-01T00:00:00Z' })).toEqual(
|
||||
@@ -175,4 +176,21 @@ describe('normalize', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeChallengeType', () => {
|
||||
it('should return the challenge type as a number or null', () => {
|
||||
expect(normalizeChallengeType(10)).toEqual(10);
|
||||
expect(normalizeChallengeType('10')).toEqual(10);
|
||||
expect(normalizeChallengeType(null)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should throw an error if the challenge type is not in the expected shape', () => {
|
||||
expect(() => normalizeChallengeType('invalid')).toThrow(
|
||||
'Unexpected challengeType value: "invalid"'
|
||||
);
|
||||
expect(() => normalizeChallengeType({ type: '123' })).toThrow(
|
||||
'Unexpected challengeType value: {"type":"123"}'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+39
-12
@@ -61,6 +61,34 @@ export const normalizeDate = (date?: Prisma.JsonValue): number => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalizes a challenge type value to a number.
|
||||
*
|
||||
* @param challengeType A JSON value that can be a number, string, or null.
|
||||
* @returns The challenge type as a number or null.
|
||||
*/
|
||||
export const normalizeChallengeType = (
|
||||
challengeType?: Prisma.JsonValue
|
||||
): number | null => {
|
||||
if (typeof challengeType === 'number') {
|
||||
return challengeType;
|
||||
} else if (typeof challengeType === 'string') {
|
||||
const parsed = parseInt(challengeType, 10);
|
||||
if (isNaN(parsed)) {
|
||||
throw Error(
|
||||
'Unexpected challengeType value: ' + JSON.stringify(challengeType)
|
||||
);
|
||||
}
|
||||
return parsed;
|
||||
} else if (challengeType === null) {
|
||||
return null;
|
||||
} else {
|
||||
throw Error(
|
||||
'Unexpected challengeType value: ' + JSON.stringify(challengeType)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that the user's profile UI settings are valid.
|
||||
*
|
||||
@@ -125,9 +153,16 @@ export type NormalizedChallenge = {
|
||||
export const normalizeChallenges = (
|
||||
completedChallenges: CompletedChallenge[]
|
||||
): NormalizedChallenge[] => {
|
||||
const noNullProps = completedChallenges.map(challenge =>
|
||||
removeNulls(challenge)
|
||||
);
|
||||
const fixedDateAndType = completedChallenges.map(challenge => {
|
||||
const { completedDate, challengeType, ...rest } = challenge;
|
||||
return {
|
||||
...rest,
|
||||
completedDate: normalizeDate(completedDate),
|
||||
challengeType: normalizeChallengeType(challengeType)
|
||||
};
|
||||
});
|
||||
|
||||
const noNullProps = fixedDateAndType.map(challenge => removeNulls(challenge));
|
||||
// files.path is optional
|
||||
const noNullPath = noNullProps.map(challenge => {
|
||||
const { files, ...rest } = challenge;
|
||||
@@ -136,15 +171,7 @@ export const normalizeChallenges = (
|
||||
return { ...rest, files: noNullFiles };
|
||||
});
|
||||
|
||||
const withNumberDates = noNullPath.map(challenge => {
|
||||
const { completedDate, ...rest } = challenge;
|
||||
return {
|
||||
...rest,
|
||||
completedDate: normalizeDate(completedDate)
|
||||
};
|
||||
});
|
||||
|
||||
return withNumberDates;
|
||||
return noNullPath;
|
||||
};
|
||||
|
||||
type NormalizedSurvey = {
|
||||
|
||||
Reference in New Issue
Block a user