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:
Sem Bauke
2023-04-18 17:01:26 +02:00
committed by GitHub
parent 27c75679cd
commit a980ac03e5
3 changed files with 196 additions and 0 deletions

View File

@@ -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;

View 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
});
});
});
});

View 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();
};