feat(api): create account delete endpoint (#50304)

* feat(api): create account delete endpoint

---------

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Niraj Nandish
2023-05-18 18:01:21 +04:00
committed by GitHub
parent 885cf86cd6
commit 999d8a6c03
5 changed files with 126 additions and 3 deletions
+4 -2
View File
@@ -18,7 +18,9 @@ const requests = {
GET: (resource: string) => request(fastifyTestInstance?.server).get(resource),
POST: (resource: string) =>
request(fastifyTestInstance?.server).post(resource),
PUT: (resource: string) => request(fastifyTestInstance?.server).put(resource)
PUT: (resource: string) => request(fastifyTestInstance?.server).put(resource),
DELETE: (resource: string) =>
request(fastifyTestInstance?.server).delete(resource)
};
/* eslint-enable @typescript-eslint/naming-convention */
@@ -33,7 +35,7 @@ export const getCsrfToken = (setCookies: string[]): string | undefined => {
export function superRequest(
resource: string,
config: {
method: 'GET' | 'POST' | 'PUT';
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
setCookies?: string[];
},
options?: Options
+2
View File
@@ -39,6 +39,7 @@ import {
FCC_ENABLE_DEV_LOGIN_MODE,
SENTRY_DSN
} from './utils/env';
import { userRoutes } from './routes/user';
export type FastifyInstanceWithTypeProvider = FastifyInstance<
RawServerDefault,
@@ -165,6 +166,7 @@ export const build = async (
void fastify.register(devLoginCallback, { prefix: '/auth' });
}
void fastify.register(settingRoutes);
void fastify.register(userRoutes, { prefix: '/user' });
void fastify.register(deprecatedEndpoints);
return fastify;
+19 -1
View File
@@ -2,14 +2,32 @@ import fp from 'fastify-plugin';
import { FastifyPluginAsync } from 'fastify';
import { PrismaClient } from '@prisma/client';
import { FREECODECAMP_NODE_ENV, MONGOHQ_URL } from '../utils/env';
declare module 'fastify' {
interface FastifyInstance {
prisma: PrismaClient;
}
}
function createTestConnectionURL(url: string, dbId: string) {
return url.replace(/(.*\/)(.*)(\?.*)/, `$1$2${dbId}$3`);
}
const prismaPlugin: FastifyPluginAsync = fp(async (server, _options) => {
const prisma = new PrismaClient();
const prisma =
process.env.JEST_WORKER_ID && FREECODECAMP_NODE_ENV === 'development'
? new PrismaClient({
datasources: {
db: {
url: createTestConnectionURL(
MONGOHQ_URL,
process.env.JEST_WORKER_ID
)
}
}
})
: new PrismaClient();
await prisma.$connect();
+48
View File
@@ -0,0 +1,48 @@
import request from 'supertest';
import { setupServer, superRequest } from '../../jest.utils';
describe('userRoutes', () => {
setupServer();
describe('Authenticated user', () => {
let setCookies: string[];
beforeAll(async () => {
const res = await request(fastifyTestInstance?.server).get(
'/auth/dev-callback'
);
setCookies = res.get('Set-Cookie');
});
describe('/account', () => {
test('DELETE returns 200 status code with empty object', async () => {
const response = await superRequest('/user/account', {
method: 'DELETE',
setCookies
});
const userCount = await fastifyTestInstance?.prisma.user.count({
where: { email: 'foo@bar.com' }
});
expect(response.status).toBe(200);
expect(response.body).toStrictEqual({});
expect(userCount).toBe(0);
});
});
});
describe('Unauthenticated user', () => {
// TODO: get CSRF cookies when that PR is in.
describe('/account', () => {
test('DELETE returns 401 status code with error message', async () => {
const response = await superRequest('/user/account', {
method: 'DELETE'
});
expect(response?.statusCode).toBe(401);
});
});
});
});
+53
View File
@@ -0,0 +1,53 @@
import {
Type,
type FastifyPluginCallbackTypebox
} from '@fastify/type-provider-typebox';
export const userRoutes: FastifyPluginCallbackTypebox = (
fastify,
_options,
done
) => {
fastify.addHook('onRequest', fastify.authenticateSession);
fastify.delete(
'/account',
{
schema: {
response: {
200: Type.Object({}),
500: Type.Object({
message: Type.Literal(
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.'
),
type: Type.Literal('danger')
})
}
}
},
async (req, reply) => {
try {
await fastify.prisma.userToken.deleteMany({
where: { userId: req.session.user.id }
});
await fastify.prisma.user.delete({
where: { id: req.session.user.id }
});
await req.session.destroy();
void reply.clearCookie('sessionId');
return {};
} catch (err) {
fastify.log.error(err);
void reply.code(500);
return {
message:
'Oops! Something went wrong. Please try again in a moment or contact support@freecodecamp.org if the error persists.',
type: 'danger'
};
}
}
);
done();
};