mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
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:
+4
-2
@@ -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
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
};
|
||||
Reference in New Issue
Block a user