feat: split rdbms into individual blocks and two challengeTypes (#44978)
* feat: split english rdbms into individual blocks fix: stuff fix: remove from partiallyComplete array on submit fix: add suggestion Update client/i18n/locales/english/translations.json Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/i18n/locales/english/intro.json Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/i18n/locales/english/intro.json Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/i18n/locales/english/intro.json Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Update client/src/templates/Challenges/codeally/show.tsx Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> * fix: prettier * fix: style suggestion * Apply suggestions from code review Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@ -995,6 +995,25 @@ export default function initializeUser(User) {
|
||||
});
|
||||
};
|
||||
|
||||
User.prototype.getPartiallyCompletedChallenges$ =
|
||||
function getPartiallyCompletedChallenges$() {
|
||||
if (
|
||||
Array.isArray(this.partiallyCompletedChallenges) &&
|
||||
this.partiallyCompletedChallenges.length
|
||||
) {
|
||||
return Observable.of(this.partiallyCompletedChallenges);
|
||||
}
|
||||
const id = this.getId();
|
||||
const filter = {
|
||||
where: { id },
|
||||
fields: { partiallyCompletedChallenges: true }
|
||||
};
|
||||
return this.constructor.findOne$(filter).map(user => {
|
||||
this.partiallyCompletedChallenges = user.partiallyCompletedChallenges;
|
||||
return user.partiallyCompletedChallenges;
|
||||
});
|
||||
};
|
||||
|
||||
User.getMessages = messages => Promise.resolve(messages);
|
||||
|
||||
User.remoteMethod('getMessages', {
|
||||
|
@ -236,6 +236,15 @@
|
||||
],
|
||||
"default": []
|
||||
},
|
||||
"partiallyCompletedChallenges": {
|
||||
"type": [
|
||||
{
|
||||
"completedDate": "number",
|
||||
"id": "string"
|
||||
}
|
||||
],
|
||||
"default": []
|
||||
},
|
||||
"portfolio": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
|
@ -20,3 +20,6 @@ export const fixCompletedChallengeItem = obj =>
|
||||
'files',
|
||||
'isManuallyApproved'
|
||||
]);
|
||||
|
||||
export const fixPartiallyCompletedChallengeItem = obj =>
|
||||
pick(obj, ['id', 'completedDate']);
|
||||
|
@ -13,7 +13,10 @@ import isNumeric from 'validator/lib/isNumeric';
|
||||
import isURL from 'validator/lib/isURL';
|
||||
|
||||
import { environment, deploymentEnv } from '../../../../config/env.json';
|
||||
import { fixCompletedChallengeItem } from '../../common/utils';
|
||||
import {
|
||||
fixCompletedChallengeItem,
|
||||
fixPartiallyCompletedChallengeItem
|
||||
} from '../../common/utils';
|
||||
import { getChallenges } from '../utils/get-curriculum';
|
||||
import { ifNoUserSend } from '../utils/middleware';
|
||||
import {
|
||||
@ -134,6 +137,10 @@ export function buildUserUpdate(
|
||||
)
|
||||
};
|
||||
|
||||
updateData.$pull = {
|
||||
partiallyCompletedChallenges: { id: challengeId }
|
||||
};
|
||||
|
||||
if (
|
||||
timezone &&
|
||||
timezone !== 'UTC' &&
|
||||
@ -283,6 +290,27 @@ function projectCompleted(req, res, next) {
|
||||
});
|
||||
}
|
||||
|
||||
// CodeRoad cert project
|
||||
if (completedChallenge.challengeType === 13) {
|
||||
const { partiallyCompletedChallenges = [], completedChallenges = [] } =
|
||||
user;
|
||||
|
||||
const isPartiallyCompleted = partiallyCompletedChallenges.some(
|
||||
challenge => challenge.id === completedChallenge.id
|
||||
);
|
||||
|
||||
const isCompleted = completedChallenges.some(
|
||||
challenge => challenge.id === completedChallenge.id
|
||||
);
|
||||
|
||||
if (!isPartiallyCompleted && !isCompleted) {
|
||||
return res.status(403).json({
|
||||
type: 'error',
|
||||
message: 'You have to complete the project before you can submit a URL.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return user
|
||||
.getCompletedChallenges$()
|
||||
.flatMap(() => {
|
||||
@ -345,6 +373,10 @@ function backendChallengeCompleted(req, res, next) {
|
||||
.subscribe(() => {}, next);
|
||||
}
|
||||
|
||||
const codeRoadChallenges = getChallenges().filter(
|
||||
({ challengeType }) => challengeType === 12 || challengeType === 13
|
||||
);
|
||||
|
||||
function createCoderoadChallengeCompleted(app) {
|
||||
/* Example request coming from CodeRoad:
|
||||
* req.body: { tutorialId: 'freeCodeCamp/learn-bash-by-building-a-boilerplate:v1.0.0' }
|
||||
@ -374,18 +406,14 @@ function createCoderoadChallengeCompleted(app) {
|
||||
return res.send('Tutorial not hosted on freeCodeCamp GitHub account');
|
||||
}
|
||||
|
||||
const codeRoadChallenges = getChallenges().filter(
|
||||
challenge => challenge.challengeType === 12
|
||||
);
|
||||
|
||||
// validate tutorial name is in codeRoadChallenges object
|
||||
const tutorialInfo = codeRoadChallenges.find(tutorial =>
|
||||
tutorial.url?.includes(tutorialRepoName)
|
||||
const challenge = codeRoadChallenges.find(challenge =>
|
||||
challenge.url?.includes(tutorialRepoName)
|
||||
);
|
||||
|
||||
if (!tutorialInfo) return res.send('Tutorial name is not valid');
|
||||
if (!challenge) return res.send('Tutorial name is not valid');
|
||||
|
||||
const tutorialMongoId = tutorialInfo?.id;
|
||||
const { id: challengeId, challengeType } = challenge;
|
||||
|
||||
try {
|
||||
// check if webhook token is in database
|
||||
@ -406,12 +434,43 @@ function createCoderoadChallengeCompleted(app) {
|
||||
|
||||
// submit challenge
|
||||
const completedDate = Date.now();
|
||||
const { completedChallenges = [], partiallyCompletedChallenges = [] } =
|
||||
user;
|
||||
|
||||
const userUpdateInfo = buildUserUpdate(user, tutorialMongoId, {
|
||||
id: tutorialMongoId,
|
||||
completedDate
|
||||
});
|
||||
let userUpdateInfo = {};
|
||||
|
||||
const isCompleted = completedChallenges.some(
|
||||
challenge => challenge.id === challengeId
|
||||
);
|
||||
|
||||
// if CodeRoad cert project and not in completedChallenges,
|
||||
// add to partiallyCompletedChallenges
|
||||
if (challengeType === 13 && !isCompleted) {
|
||||
const finalChallenge = {
|
||||
id: challengeId,
|
||||
completedDate
|
||||
};
|
||||
|
||||
userUpdateInfo.updateData = {};
|
||||
userUpdateInfo.updateData.$set = {
|
||||
partiallyCompletedChallenges: uniqBy(
|
||||
[
|
||||
finalChallenge,
|
||||
...partiallyCompletedChallenges.map(
|
||||
fixPartiallyCompletedChallengeItem
|
||||
)
|
||||
],
|
||||
'id'
|
||||
)
|
||||
};
|
||||
|
||||
// else, add to or update completedChallenges
|
||||
} else {
|
||||
userUpdateInfo = buildUserUpdate(user, challengeId, {
|
||||
id: challengeId,
|
||||
completedDate
|
||||
});
|
||||
}
|
||||
const updatedUser = await user.updateAttributes(
|
||||
userUpdateInfo?.updateData
|
||||
);
|
||||
|
@ -4,7 +4,10 @@ import { body } from 'express-validator';
|
||||
import { pick } from 'lodash';
|
||||
import { Observable } from 'rx';
|
||||
|
||||
import { fixCompletedChallengeItem } from '../../common/utils';
|
||||
import {
|
||||
fixCompletedChallengeItem,
|
||||
fixPartiallyCompletedChallengeItem
|
||||
} from '../../common/utils';
|
||||
import { removeCookies } from '../utils/getSetAccessToken';
|
||||
import { ifNoUser401, ifNoUserRedirectHome } from '../utils/middleware';
|
||||
import {
|
||||
@ -99,11 +102,18 @@ function createReadSessionUser(app) {
|
||||
queryUser &&
|
||||
Observable.forkJoin(
|
||||
queryUser.getCompletedChallenges$(),
|
||||
queryUser.getPartiallyCompletedChallenges$(),
|
||||
queryUser.getPoints$(),
|
||||
Donation.getCurrentActiveDonationCount$(),
|
||||
(completedChallenges, progressTimestamps, activeDonations) => ({
|
||||
(
|
||||
completedChallenges,
|
||||
partiallyCompletedChallenges,
|
||||
progressTimestamps,
|
||||
activeDonations
|
||||
) => ({
|
||||
activeDonations,
|
||||
completedChallenges,
|
||||
partiallyCompletedChallenges,
|
||||
progress: getProgress(progressTimestamps, queryUser.timezone)
|
||||
})
|
||||
);
|
||||
@ -111,16 +121,26 @@ function createReadSessionUser(app) {
|
||||
() => !queryUser,
|
||||
Observable.of({ user: {}, result: '' }),
|
||||
Observable.defer(() => source)
|
||||
.map(({ activeDonations, completedChallenges, progress }) => ({
|
||||
user: {
|
||||
...queryUser.toJSON(),
|
||||
...progress,
|
||||
completedChallenges: completedChallenges.map(
|
||||
fixCompletedChallengeItem
|
||||
)
|
||||
},
|
||||
sessionMeta: { activeDonations }
|
||||
}))
|
||||
.map(
|
||||
({
|
||||
activeDonations,
|
||||
completedChallenges,
|
||||
partiallyCompletedChallenges,
|
||||
progress
|
||||
}) => ({
|
||||
user: {
|
||||
...queryUser.toJSON(),
|
||||
...progress,
|
||||
completedChallenges: completedChallenges.map(
|
||||
fixCompletedChallengeItem
|
||||
),
|
||||
partiallyCompletedChallenges: partiallyCompletedChallenges.map(
|
||||
fixPartiallyCompletedChallengeItem
|
||||
)
|
||||
},
|
||||
sessionMeta: { activeDonations }
|
||||
})
|
||||
)
|
||||
.map(({ user, sessionMeta }) => ({
|
||||
user: {
|
||||
[user.username]: {
|
||||
|
@ -32,6 +32,7 @@ export const publicUserProps = [
|
||||
'linkedin',
|
||||
'location',
|
||||
'name',
|
||||
'partiallyCompletedChallenges',
|
||||
'points',
|
||||
'portfolio',
|
||||
'profileUI',
|
||||
|
Reference in New Issue
Block a user