fix(api): handle string challengeType (#60491)

This commit is contained in:
Oliver Eyton-Williams
2025-05-23 14:56:18 +02:00
committed by GitHub
parent 74aa61a7b4
commit 8138f086aa
4 changed files with 68 additions and 18 deletions
+1 -1
View File
@@ -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
+8 -3
View File
@@ -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[];
+20 -2
View File
@@ -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
View File
@@ -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 = {