diff --git a/api-server/src/server/boot/challenge.js b/api-server/src/server/boot/challenge.js index 6891a9db15c..2ae10c0b95a 100644 --- a/api-server/src/server/boot/challenge.js +++ b/api-server/src/server/boot/challenge.js @@ -701,7 +701,9 @@ function createMsTrophyChallengeCompleted(app) { }); if (!msUser || !msUser.msUsername) { - throw new Error('Microsoft username not found.'); + return res + .status(403) + .json({ type: 'error', message: 'flash.ms.trophy.err-1' }); } const { msUsername } = msUser; @@ -711,15 +713,24 @@ function createMsTrophyChallengeCompleted(app) { ); if (!challenge) { - throw new Error('Challenge not found'); + return res + .status(400) + .json({ type: 'error', message: 'flash.ms.trophy.err-2' }); } 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) { - throw new Error('Unable to validate trophy'); + if (!msApiRes.ok || msTrophyJson.awardType !== 'Trophy') { + return res.status(403).json({ + type: 'error', + message: 'flash.ms.trophy.err-3', + variables: { + msUsername + } + }); } const completedChallenge = pick(body, ['id']); @@ -751,9 +762,10 @@ function createMsTrophyChallengeCompleted(app) { }); }); } catch (e) { + log(e); return res.status(500).json({ type: 'error', - message: e.message + message: 'flash.ms.trophy.err-4' }); } }; diff --git a/api-server/src/server/boot/user.js b/api-server/src/server/boot/user.js index 73fea8d3475..00d30e3e877 100644 --- a/api-server/src/server/boot/user.js +++ b/api-server/src/server/boot/user.js @@ -123,9 +123,10 @@ function createPostMsUsername(app) { const { msTranscriptUrl } = req.body; if (!msTranscriptUrl) { - return res - .status(400) - .send('Please include a Microsoft transcript URL in request'); + return res.status(400).json({ + type: 'error', + message: 'flash.ms.transcript.link-err-1' + }); } const msTranscriptId = msTranscriptUrl.split('/').pop(); @@ -135,19 +136,17 @@ function createPostMsUsername(app) { const msApiRes = await fetch(msTranscriptApiUrl); if (!msApiRes.ok) { - res.status(500); - throw new Error( - 'An error occurred trying to get your Microsoft transcript' - ); + return res + .status(404) + .json({ type: 'error', message: 'flash.ms.transcript.link-err-2' }); } const { userName } = await msApiRes.json(); if (!userName) { - res.status(500); - throw new Error( - 'An error occured trying to link your Microsoft account' - ); + return res + .status(500) + .json({ type: 'error', message: 'flash.ms.transcript.link-err-3' }); } // Don't create if username is used by another fCC account @@ -156,7 +155,9 @@ function createPostMsUsername(app) { }); if (usernameUsed) { - throw new Error('That username is already used'); + return res + .status(403) + .json({ type: 'error', message: 'flash.ms.transcript.link-err-4' }); } await MsUsername.destroyAll({ userId: req.user.id }); @@ -169,25 +170,27 @@ function createPostMsUsername(app) { }); if (!newMsUsername?.id) { - res.status(500); - throw new Error( - 'An error occured trying to link your Microsoft account' - ); + return res + .status(500) + .json({ type: 'error', message: 'flash.ms.transcript.link-err-5' }); } return res.json({ msUsername: userName }); } catch (e) { log(e); - return res.send(e.message); + return res + .status(500) + .json({ type: 'error', message: 'flash.ms.transcript.link-err-6' }); } }; } function deleteMsUsernameResponse(req, res) { if (!req.msUsernameDeleted) { - return res - .status(500) - .send('An error occurred trying to unlink your Microsoft username'); + return res.status(500).json({ + type: 'error', + message: 'flash.ms.transcript.unlink-err' + }); } return res.send({ msUsername: null }); diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 2e010b975aa..976f44edc88 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -718,12 +718,26 @@ "challenge-submit-too-big": "Sorry, you cannot submit your code. Your code is {{user-size}} bytes. We allow a maximum of {{max-size}} bytes. Please make your code smaller and try again or request assistance on https://forum.freecodecamp.org", "invalid-update-flag": "You are attempting to access forbidden resources. Please request assistance on https://forum.freecodecamp.org if this is a valid request.", "generate-exam-error": "An error occurred trying to generate your exam.", - "ms-trophy-err": "We were unable to verify your trophy from Microsoft's learning platform.", - "ms-trophy-verified": "Your trophy from Microsoft's learning platform was verified.", - "ms-linked": "Your Microsoft username been linked to your freeCodeCamp account.", - "ms-link-err": "An error occurred trying to link your Microsoft username to your freeCodeCamp account.", - "ms-unlinked": "The link to your Microsoft username has been removed.", - "ms-unlink-err": "An error occurred trying to remove the link to your Microsoft username." + "ms": { + "transcript": { + "link-err-1": "Please include a Microsoft transcript URL in the request.", + "link-err-2": "Something went wrong trying to get your transcript from Microsoft.", + "link-err-3": "A username was not found in your Microsoft transcript.", + "link-err-4": "That Microsoft username is being used by another freeCodeCamp account.", + "link-err-5": "Something went wrong trying to save your Microsoft account.", + "link-err-6": "Something went wrong trying to link your Microsoft username to your freeCodeCamp account.", + "linked": "Your Microsoft username has been linked to your freeCodeCamp account.", + "unlinked": "The link to your Microsoft username has been removed.", + "unlink-err": "Something went wrong trying to remove the link to your Microsoft username." + }, + "trophy": { + "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-3": "It appears that the Microsoft user \"{{msUsername}}\" has not earned this trophy.", + "err-4": "Something went wrong trying to verify your trophy. Please check and try again.", + "verified": "Your trophy from Microsoft's learning platform was verified." + } + } }, "validation": { "max-characters": "There is a maximum limit of 288 characters, you have {{charsLeft}} left", diff --git a/client/src/components/Flash/index.tsx b/client/src/components/Flash/index.tsx index e753212cf45..3c2603a3375 100644 --- a/client/src/components/Flash/index.tsx +++ b/client/src/components/Flash/index.tsx @@ -20,11 +20,13 @@ function Flash({ flashMessage, removeFlashMessage }: FlashProps): JSX.Element { removeFlashMessage(); } + const flashStyle = type === 'error' ? 'danger' : type; + return ( { - const { savedChallenges, points, type, examResults } = data; + const { savedChallenges, points, message, examResults } = data; const payloadWithClientProperties = { ...omit(update.payload, ['files']) }; @@ -85,8 +84,8 @@ function postChallenge(update, username) { submitChallengeComplete() ]; - if (challengeType === challengeTypes.msTrophy && type === 'error') { - actions = [createFlashMessage(msTrophyError), submitChallengeError()]; + if (message && challengeType === challengeTypes.msTrophy) { + actions = [createFlashMessage(data), submitChallengeError()]; } else if (challengeType === challengeTypes.msTrophy) { actions.push(createFlashMessage(msTrophyVerified)); } diff --git a/client/src/utils/error-messages.ts b/client/src/utils/error-messages.ts index 9d755a0085e..19f49d81d0f 100644 --- a/client/src/utils/error-messages.ts +++ b/client/src/utils/error-messages.ts @@ -5,11 +5,6 @@ export const standardErrorMessage = { message: FlashMessages.WentWrong }; -export const msTrophyError = { - type: 'danger', - message: FlashMessages.MsTrophyErr -}; - export const reallyWeirdErrorMessage = { type: 'danger', message: FlashMessages.ReallyWeird diff --git a/client/src/utils/tone/index.ts b/client/src/utils/tone/index.ts index 179c6a45c6b..9f056869e63 100644 --- a/client/src/utils/tone/index.ts +++ b/client/src/utils/tone/index.ts @@ -33,6 +33,20 @@ const toneUrls = { [FlashMessages.IncompleteSteps]: TRY_AGAIN, [FlashMessages.LocalCodeSaved]: CHAL_COMP, [FlashMessages.LocalCodeSaveError]: TRY_AGAIN, + [FlashMessages.MsTranscriptErr1]: TRY_AGAIN, + [FlashMessages.MsTranscriptErr2]: TRY_AGAIN, + [FlashMessages.MsTranscriptErr3]: TRY_AGAIN, + [FlashMessages.MsTranscriptErr4]: TRY_AGAIN, + [FlashMessages.MsTranscriptErr5]: TRY_AGAIN, + [FlashMessages.MsTranscriptErr6]: TRY_AGAIN, + [FlashMessages.MsTranscriptLinked]: CHAL_COMP, + [FlashMessages.MsTranscriptUnlinked]: CHAL_COMP, + [FlashMessages.MsTranscriptUnlinkErr]: TRY_AGAIN, + [FlashMessages.MsTrophyErr1]: TRY_AGAIN, + [FlashMessages.MsTrophyErr2]: TRY_AGAIN, + [FlashMessages.MsTrophyErr3]: TRY_AGAIN, + [FlashMessages.MsTrophyErr4]: TRY_AGAIN, + [FlashMessages.MsTrophyVerified]: CHAL_COMP, [FlashMessages.NameNeeded]: TRY_AGAIN, // [FlashMessages.None]: '', [FlashMessages.NotEligible]: TRY_AGAIN,