Skip to content

Commit

Permalink
fix(api): update backend project validation (freeCodeCamp#54218)
Browse files Browse the repository at this point in the history
Co-authored-by: Sem Bauke <semboot699@gmail.com>
  • Loading branch information
ShaunSHamilton and Sembauke authored Mar 28, 2024
1 parent 3c8b60d commit c333a74
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
28 changes: 27 additions & 1 deletion api/src/routes/challenge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,33 @@ describe('challengeRoutes', () => {
expect(response.body).toStrictEqual(
isValidChallengeCompletionErrorMsg
);
expect(response.statusCode).toBe(400);
expect(response.statusCode).toBe(403);
});

it('POST rejects backendProject requests without URL githubLinks', async () => {
const response = await superPost('/project-completed').send({
id: id1,
challengeType: challengeTypes.backEndProject,
// Solution is allowed to be localhost for backEndProject
solution: 'http://localhost:3000'
});

expect(response.body).toStrictEqual(
isValidChallengeCompletionErrorMsg
);
expect(response.statusCode).toBe(403);

const response_2 = await superPost('/project-completed').send({
id: id1,
challengeType: challengeTypes.backEndProject,
solution: 'http://localhost:3000',
githubLink: 'not-a-valid-url'
});

expect(response_2.body).toStrictEqual(
isValidChallengeCompletionErrorMsg
);
expect(response_2.statusCode).toBe(403);
});

it('POST rejects CodeRoad/CodeAlly projects when the user has not completed the required challenges', async () => {
Expand Down
21 changes: 21 additions & 0 deletions api/src/routes/challenge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebo
import jwt from 'jsonwebtoken';
import { uniqBy } from 'lodash';
import { CompletedExam, ExamResults } from '@prisma/client';
import isURL from 'validator/lib/isURL';

import { challengeTypes } from '../../../shared/config/challenge-types';
import { schemas } from '../schemas';
Expand Down Expand Up @@ -192,9 +193,28 @@ export const challengeRoutes: FastifyPluginCallbackTypebox = (
}
},
async (req, reply) => {
// TODO: considering validation is determined by `challengeType`, it should not come from the client
// Determine `challengeType` by `id`
const { id: projectId, challengeType, solution, githubLink } = req.body;
const userId = req.user?.id;

// If `backEndProject`:
// - `solution` needs to exist, but does not have to be valid URL
// - `githubLink` needs to exist and be valid URL
if (challengeType === challengeTypes.backEndProject) {
if (!solution || !isURL(githubLink + '')) {
return void reply.code(403).send({
type: 'error',
message: 'That does not appear to be a valid challenge submission.'
});
}
} else if (solution && !isURL(solution + '')) {
return void reply.code(403).send({
type: 'error',
message: 'That does not appear to be a valid challenge submission.'
});
}

try {
const user = await fastify.prisma.user.findUniqueOrThrow({
where: { id: userId },
Expand All @@ -221,6 +241,7 @@ export const challengeRoutes: FastifyPluginCallbackTypebox = (
const oldChallenge = user.completedChallenges?.find(
({ id }) => id === projectId
);

const updatedChallenge = {
challengeType,
solution,
Expand Down
15 changes: 10 additions & 5 deletions api/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,8 +526,8 @@ export const schemas = {
body: Type.Object({
id: Type.String({ format: 'objectid', maxLength: 24, minLength: 24 }),
challengeType: Type.Optional(Type.Number()),
solution: Type.String({ format: 'url', maxLength: 1024 }),
// TODO(Post-MVP): require format: 'url' for githubLink
// The solution must be a valid URL only if it is a `backEndProject`.
solution: Type.String({ maxLength: 1024 }),
githubLink: Type.Optional(Type.String())
}),
response: {
Expand All @@ -551,9 +551,14 @@ export const schemas = {
}),
403: Type.Object({
type: Type.Literal('error'),
message: Type.Literal(
'You have to complete the project before you can submit a URL.'
)
message: Type.Union([
Type.Literal(
'You have to complete the project before you can submit a URL.'
),
Type.Literal(
'That does not appear to be a valid challenge submission.'
)
])
}),
500: Type.Object({
message: Type.Literal(
Expand Down

0 comments on commit c333a74

Please sign in to comment.