mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-01-06 06:01:31 -05:00
feat: introduce /update-my-profileui route in new API (#49827)
* feat: add response codes * fix: update TypeBox imports * refactor: convert inject based tests to supertest * feat: require authentication to use route * test: confirm db is updated as expected * fix: respond appropriately on error Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Niraj Nandish <nirajnandish@icloud.com> Co-authored-by: Tom <20648924+moT01@users.noreply.github.com>
This commit is contained in:
@@ -20,6 +20,7 @@ import jwtAuthz from './plugins/fastify-jwt-authz';
|
||||
import security from './plugins/security';
|
||||
import sessionAuth from './plugins/session-auth';
|
||||
import { testRoutes } from './routes/test';
|
||||
import { settingRoutes } from './routes/settings';
|
||||
import { auth0Routes, devLoginCallback } from './routes/auth';
|
||||
import { testValidatedRoutes } from './routes/validation-test';
|
||||
import { testMiddleware } from './middleware';
|
||||
@@ -117,6 +118,7 @@ export const build = async (
|
||||
if (FCC_ENABLE_DEV_LOGIN_MODE) {
|
||||
void fastify.register(devLoginCallback, { prefix: '/auth' });
|
||||
}
|
||||
void fastify.register(settingRoutes);
|
||||
void fastify.register(testValidatedRoutes);
|
||||
|
||||
return fastify;
|
||||
|
||||
117
api/src/routes/settings.test.ts
Normal file
117
api/src/routes/settings.test.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import request from 'supertest';
|
||||
|
||||
import { build } from '../app';
|
||||
|
||||
const baseProfileUI = {
|
||||
isLocked: false,
|
||||
showAbout: false,
|
||||
showCerts: false,
|
||||
showDonation: false,
|
||||
showHeatMap: false,
|
||||
showLocation: false,
|
||||
showName: false,
|
||||
showPoints: false,
|
||||
showPortfolio: false,
|
||||
showTimeLine: false
|
||||
};
|
||||
|
||||
const profileUI = {
|
||||
...baseProfileUI,
|
||||
isLocked: true,
|
||||
showAbout: true,
|
||||
showDonation: true,
|
||||
showLocation: true,
|
||||
showName: true,
|
||||
showPortfolio: true
|
||||
};
|
||||
|
||||
describe('settingRoutes', () => {
|
||||
let fastify: undefined | Awaited<ReturnType<typeof build>>;
|
||||
|
||||
beforeAll(async () => {
|
||||
fastify = await build();
|
||||
await fastify.ready();
|
||||
}, 20000);
|
||||
|
||||
afterAll(async () => {
|
||||
await fastify?.close();
|
||||
});
|
||||
|
||||
test('PUT /update-my-profileui returns 401 status code for un-authenticated users', async () => {
|
||||
const response = await request(fastify?.server).put('/update-my-profileui');
|
||||
|
||||
expect(response?.statusCode).toEqual(401);
|
||||
});
|
||||
|
||||
describe('Authenticated user', () => {
|
||||
let cookies: string[];
|
||||
|
||||
beforeAll(async () => {
|
||||
await fastify?.prisma.user.updateMany({
|
||||
where: { email: 'foo@bar.com' },
|
||||
data: { profileUI: baseProfileUI }
|
||||
});
|
||||
const res = await request(fastify?.server).get('/auth/dev-callback');
|
||||
cookies = res.get('Set-Cookie');
|
||||
});
|
||||
|
||||
test('PUT /update-my-profileui returns 200 status code with "success" message', async () => {
|
||||
const response = await request(fastify?.server)
|
||||
.put('/update-my-profileui')
|
||||
.set('Cookie', cookies)
|
||||
.send({ profileUI });
|
||||
|
||||
const user = await fastify?.prisma.user.findFirst({
|
||||
where: { email: 'foo@bar.com' }
|
||||
});
|
||||
|
||||
expect(response?.statusCode).toEqual(200);
|
||||
expect(response?.body).toEqual({
|
||||
message: 'flash.updated-preferences',
|
||||
type: 'success'
|
||||
});
|
||||
expect(user?.profileUI).toEqual(profileUI);
|
||||
});
|
||||
|
||||
test('PUT /update-my-profileui ignores invalid keys', async () => {
|
||||
const response = await request(fastify?.server)
|
||||
.put('/update-my-profileui')
|
||||
.set('Cookie', cookies)
|
||||
.send({
|
||||
profileUI: {
|
||||
...profileUI,
|
||||
invalidKey: 'invalidValue'
|
||||
}
|
||||
});
|
||||
|
||||
const user = await fastify?.prisma.user.findFirst({
|
||||
where: { email: 'foo@bar.com' }
|
||||
});
|
||||
|
||||
expect(response?.statusCode).toEqual(200);
|
||||
expect(user?.profileUI).toEqual(profileUI);
|
||||
});
|
||||
|
||||
test('PUT /update-my-profileui returns 400 status code with missing keys', async () => {
|
||||
const response = await request(fastify?.server)
|
||||
.put('/update-my-profileui')
|
||||
.set('Cookie', cookies)
|
||||
.send({
|
||||
profileUI: {
|
||||
isLocked: true,
|
||||
showName: true,
|
||||
showPoints: false,
|
||||
showPortfolio: true,
|
||||
showTimeLine: false
|
||||
}
|
||||
});
|
||||
|
||||
expect(response?.statusCode).toEqual(400);
|
||||
expect(response?.body).toEqual({
|
||||
error: 'Bad Request',
|
||||
message: `body/profileUI must have required property 'showAbout'`,
|
||||
statusCode: 400
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
77
api/src/routes/settings.ts
Normal file
77
api/src/routes/settings.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
Type,
|
||||
type FastifyPluginCallbackTypebox
|
||||
} from '@fastify/type-provider-typebox';
|
||||
|
||||
export const settingRoutes: FastifyPluginCallbackTypebox = (
|
||||
fastify,
|
||||
_options,
|
||||
done
|
||||
) => {
|
||||
fastify.addHook('onRequest', fastify.authenticateSession);
|
||||
|
||||
fastify.put(
|
||||
'/update-my-profileui',
|
||||
{
|
||||
schema: {
|
||||
body: Type.Object({
|
||||
profileUI: Type.Object({
|
||||
isLocked: Type.Boolean(),
|
||||
showAbout: Type.Boolean(),
|
||||
showCerts: Type.Boolean(),
|
||||
showDonation: Type.Boolean(),
|
||||
showHeatMap: Type.Boolean(),
|
||||
showLocation: Type.Boolean(),
|
||||
showName: Type.Boolean(),
|
||||
showPoints: Type.Boolean(),
|
||||
showPortfolio: Type.Boolean(),
|
||||
showTimeLine: Type.Boolean()
|
||||
})
|
||||
}),
|
||||
response: {
|
||||
200: Type.Object({
|
||||
message: Type.Literal('flash.updated-preferences'),
|
||||
type: Type.Literal('success')
|
||||
}),
|
||||
500: Type.Object({
|
||||
message: Type.Literal('flash.wrong-updating'),
|
||||
type: Type.Literal('danger')
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
async (req, reply) => {
|
||||
try {
|
||||
await fastify.prisma.user.update({
|
||||
where: { id: req.session.user.id },
|
||||
data: {
|
||||
profileUI: {
|
||||
isLocked: req.body.profileUI.isLocked,
|
||||
showAbout: req.body.profileUI.showAbout,
|
||||
showCerts: req.body.profileUI.showCerts,
|
||||
showDonation: req.body.profileUI.showDonation,
|
||||
showHeatMap: req.body.profileUI.showHeatMap,
|
||||
showLocation: req.body.profileUI.showLocation,
|
||||
showName: req.body.profileUI.showName,
|
||||
showPoints: req.body.profileUI.showPoints,
|
||||
showPortfolio: req.body.profileUI.showPortfolio,
|
||||
showTimeLine: req.body.profileUI.showTimeLine
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
message: 'flash.updated-preferences',
|
||||
type: 'success'
|
||||
} as const;
|
||||
} catch (err) {
|
||||
// TODO: send to Sentry
|
||||
fastify.log.error(err);
|
||||
void reply.code(500);
|
||||
return { message: 'flash.wrong-updating', type: 'danger' } as const;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
done();
|
||||
};
|
||||
Reference in New Issue
Block a user