fix(client/api): ms trophy validation (#51892)

pull/51924/head
Tom 2023-10-11 15:15:32 -05:00 committed by GitHub
parent 89f0310372
commit 9b50d54001
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 10 deletions

View File

@ -719,14 +719,42 @@ function createMsTrophyChallengeCompleted(app) {
} }
const { msTrophyId = '' } = challenge; const { msTrophyId = '' } = challenge;
const msTrophyApiUrl = `https://learn.microsoft.com/api/gamestatus/achievements/${msTrophyId}?username=${msUsername}&locale=en-us`;
const msApiRes = await fetch(msTrophyApiUrl);
const msTrophyJson = await msApiRes.json();
if (!msApiRes.ok || msTrophyJson.awardType !== 'Trophy') { const msProfileApi = `https://learn.microsoft.com/api/profiles/${msUsername}`;
const msProfileApiRes = await fetch(msProfileApi);
const msProfileJson = await msProfileApiRes.json();
if (!msProfileApiRes.ok || !msProfileJson.userId) {
return res.status(403).json({ return res.status(403).json({
type: 'error', type: 'error',
message: 'flash.ms.trophy.err-3', message: 'flash.ms.profile.err',
variables: {
msUsername
}
});
}
const { userId } = msProfileJson;
const msGameStatusApi = `https://learn.microsoft.com/api/gamestatus/${userId}`;
const msGameStatusApiRes = await fetch(msGameStatusApi);
const msGameStatusJson = await msGameStatusApiRes.json();
if (!msGameStatusApiRes.ok) {
return res.status(403).json({
type: 'error',
message: 'flash.ms.trophy.err-3'
});
}
const hasEarnedTrophy = msGameStatusJson.achievements?.some(
a => a.awardUid === msTrophyId
);
if (!hasEarnedTrophy) {
return res.status(403).json({
type: 'error',
message: 'flash.ms.trophy.err-4',
variables: { variables: {
msUsername msUsername
} }
@ -735,7 +763,7 @@ function createMsTrophyChallengeCompleted(app) {
const completedChallenge = pick(body, ['id']); const completedChallenge = pick(body, ['id']);
completedChallenge.solution = msTrophyApiUrl; completedChallenge.solution = msGameStatusApi;
completedChallenge.completedDate = Date.now(); completedChallenge.completedDate = Date.now();
try { try {
@ -765,7 +793,7 @@ function createMsTrophyChallengeCompleted(app) {
log(e); log(e);
return res.status(500).json({ return res.status(500).json({
type: 'error', type: 'error',
message: 'flash.ms.trophy.err-4' message: 'flash.ms.trophy.err-5'
}); });
} }
}; };

View File

@ -732,11 +732,15 @@
"unlinked": "The link to your Microsoft username has been removed.", "unlinked": "The link to your Microsoft username has been removed.",
"unlink-err": "Something went wrong trying to remove the link to your Microsoft username." "unlink-err": "Something went wrong trying to remove the link to your Microsoft username."
}, },
"profile": {
"err": "We could not find a Microsoft user ID for Microsoft user \"{{msUsername}}\""
},
"trophy": { "trophy": {
"err-1": "We could not find a Microsoft username associated with your freeCodeCamp account.", "err-1": "We could not find a Microsoft username associated with your freeCodeCamp account.",
"err-2": "You are trying to submit a challenge that does not appear to be a trophy challenge.", "err-2": "You are trying to submit a challenge that does not appear to be a trophy challenge.",
"err-3": "It appears that the Microsoft user \"{{msUsername}}\" has not earned this trophy.", "err-3": "We could not get your Microsoft profile from your Microsoft ID.",
"err-4": "Something went wrong trying to verify your trophy. Please check and try again.", "err-4": "It appears that the Microsoft user \"{{msUsername}}\" has not earned this trophy.",
"err-5": "Something went wrong trying to verify your trophy. Please check and try again.",
"verified": "Your trophy from Microsoft's learning platform was verified." "verified": "Your trophy from Microsoft's learning platform was verified."
} }
}, },

View File

@ -16,7 +16,8 @@ function Flash({ flashMessage, removeFlashMessage }: FlashProps): JSX.Element {
const { type, message, id, variables } = flashMessage; const { type, message, id, variables } = flashMessage;
const { t } = useTranslation(); const { t } = useTranslation();
const flashStyle = type as AlertProps['variant']; const flashStyle =
type === 'error' ? 'danger' : (type as AlertProps['variant']);
function handleClose() { function handleClose() {
removeFlashMessage(); removeFlashMessage();

View File

@ -27,10 +27,12 @@ export enum FlashMessages {
MsTranscriptLinked = 'flash.ms.transcript.linked', MsTranscriptLinked = 'flash.ms.transcript.linked',
MsTranscriptUnlinked = 'flash.ms.transcript.unlinked', MsTranscriptUnlinked = 'flash.ms.transcript.unlinked',
MsTranscriptUnlinkErr = 'flash.ms.transcript.unlink-err', MsTranscriptUnlinkErr = 'flash.ms.transcript.unlink-err',
MsProfileErr = 'flash.ms.profile.err',
MsTrophyErr1 = 'flash.ms.trophy.err-1', MsTrophyErr1 = 'flash.ms.trophy.err-1',
MsTrophyErr2 = 'flash.ms.trophy.err-2', MsTrophyErr2 = 'flash.ms.trophy.err-2',
MsTrophyErr3 = 'flash.ms.trophy.err-3', MsTrophyErr3 = 'flash.ms.trophy.err-3',
MsTrophyErr4 = 'flash.ms.trophy.err-4', MsTrophyErr4 = 'flash.ms.trophy.err-4',
MsTrophyErr5 = 'flash.ms.trophy.err-5',
MsTrophyVerified = 'flash.ms.trophy.verified', MsTrophyVerified = 'flash.ms.trophy.verified',
NameNeeded = 'flash.name-needed', NameNeeded = 'flash.name-needed',
None = '', None = '',

View File

@ -42,10 +42,12 @@ const toneUrls = {
[FlashMessages.MsTranscriptLinked]: CHAL_COMP, [FlashMessages.MsTranscriptLinked]: CHAL_COMP,
[FlashMessages.MsTranscriptUnlinked]: CHAL_COMP, [FlashMessages.MsTranscriptUnlinked]: CHAL_COMP,
[FlashMessages.MsTranscriptUnlinkErr]: TRY_AGAIN, [FlashMessages.MsTranscriptUnlinkErr]: TRY_AGAIN,
[FlashMessages.MsProfileErr]: TRY_AGAIN,
[FlashMessages.MsTrophyErr1]: TRY_AGAIN, [FlashMessages.MsTrophyErr1]: TRY_AGAIN,
[FlashMessages.MsTrophyErr2]: TRY_AGAIN, [FlashMessages.MsTrophyErr2]: TRY_AGAIN,
[FlashMessages.MsTrophyErr3]: TRY_AGAIN, [FlashMessages.MsTrophyErr3]: TRY_AGAIN,
[FlashMessages.MsTrophyErr4]: TRY_AGAIN, [FlashMessages.MsTrophyErr4]: TRY_AGAIN,
[FlashMessages.MsTrophyErr5]: TRY_AGAIN,
[FlashMessages.MsTrophyVerified]: CHAL_COMP, [FlashMessages.MsTrophyVerified]: CHAL_COMP,
[FlashMessages.NameNeeded]: TRY_AGAIN, [FlashMessages.NameNeeded]: TRY_AGAIN,
// [FlashMessages.None]: '', // [FlashMessages.None]: '',