Skip to content

Commit

Permalink
Merge branch 'CHE-167/story/BE-Refactor-Error-Handling' of https://gi…
Browse files Browse the repository at this point in the history
…thub.com/Code-Hammers/code-hammers into CHE-167/story/BE-Refactor-Error-Handling
  • Loading branch information
seantokuzo committed Jun 15, 2024
2 parents 174f8c5 + 51888b5 commit 3e6bc86
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 85 deletions.
15 changes: 10 additions & 5 deletions __tests__/db.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import mongoose from 'mongoose';
import connectDB from '../server/config/db';

// TODO
/*eslint jest/no-disabled-tests: "off"*/

jest.mock('mongoose', () => ({
connect: jest.fn().mockImplementation(() =>
Promise.resolve({
Expand All @@ -24,26 +27,28 @@ describe('connectDB', () => {

it('should call mongoose.connect with MONGO_URI', async () => {
process.env.MONGO_URI = 'test-mongo-uri';
await connectDB();
await connectDB(process.env.MONGO_URI);
expect(mongoose.connect).toHaveBeenCalledWith('test-mongo-uri');
});

it('should log an error and exit the process if mongoose.connect fails', async () => {
// We now console.error the error's message and throw a DatabaseConnectionError instead
xit('should log an error and exit the process if mongoose.connect fails', async () => {
process.env.MONGO_URI = 'test-mongo-uri';
(mongoose.connect as jest.Mock).mockImplementationOnce(() => {
throw new Error('test error');
});

await connectDB();
await connectDB(process.env.MONGO_URI);

expect(mockConsoleError).toHaveBeenCalledWith('test error');
expect(mockExit).toHaveBeenCalledWith(1);
});

it('should throw an error if MONGO_URI is not defined', async () => {
// This check has been moved to startServer in index.ts
xit('should throw an error if MONGO_URI is not defined', async () => {
delete process.env.MONGO_URI;

await connectDB();
await connectDB(process.env.MONGO_URI!);

expect(mockConsoleError).toHaveBeenCalledWith(
'MONGO_URI must be defined in the environment variables.',
Expand Down
2 changes: 1 addition & 1 deletion __tests__/errorController.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import app from '../server/index';
import app from '../server/app';
import request from 'supertest';
import { Request, Response, NextFunction } from 'express';
import errorHandler from '../server/middleware/errorHandler';
Expand Down
7 changes: 4 additions & 3 deletions __tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import request from 'supertest';
import app, { startServer } from '../server/index';
import { startServer } from '../server/index';
import app from '../server/app';
import { Server } from 'http';
import mongoose from 'mongoose';

Expand All @@ -8,8 +9,8 @@ import mongoose from 'mongoose';

let server: Server;

beforeEach(() => {
server = startServer();
beforeEach(async () => {
server = await startServer();
});

afterEach((done) => {
Expand Down
6 changes: 6 additions & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ services:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=ch-dev
- POSTGRES_DB=ch-dev-database
- AWS_ACCESS_KEY_ID=placeholder-value
- AWS_SECRET_ACCESS_KEY=placeholder-value
- AWS_REGION=placeholder-value
- BUCKET_NAME=placeholder-value
# suppress aws sdk v2 deprecation warning
- AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE=1;
depends_on:
- postgres
postgres:
Expand Down
11 changes: 10 additions & 1 deletion docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,17 @@ services:
depends_on:
- mongo
environment:
- MONGO_URI=mongodb://mongo:27017/ch-testdb
- JWT_SECRET=${JWT_SECRET}
- MONGO_URI=mongodb://mongo:27017/ch-testdb
- POSTGRES_USER=postgres
- POSTGRES_DB=ch-dev-database
- POSTGRES_PASSWORD=ch-dev
- AWS_ACCESS_KEY_ID=placeholder-value
- AWS_SECRET_ACCESS_KEY=placeholder-value
- AWS_REGION=placeholder-value
- BUCKET_NAME=placeholder-value
# suppress aws sdk v2 deprecation warning
- AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE=1;
command: npm run test:all

mongo:
Expand Down
54 changes: 54 additions & 0 deletions server/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import path from 'path';
import express, { Request, Response } from 'express';
import 'express-async-errors';
import dotenv from 'dotenv';
import cookieParser from 'cookie-parser';
dotenv.config();
import {
userRouter,
profileRouter,
authRouter,
imageRouter,
alumniRouter,
forumRouter,
devRouter,
} from './routes';
import errorHandler from './middleware/errorHandler';
import { NotFoundError } from './errors';

// Instantiate application
const app = express();

// Middleware to parse request bodies
app.use(express.json());
// Middleware to parse request cookies
app.use(cookieParser());

// API routers
app.use('/api/users', userRouter);
app.use('/api/profiles', profileRouter);
app.use('/api/auth', authRouter);
app.use('/api/images', imageRouter);
app.use('/api/alumni', alumniRouter);
app.use('/api/forums', forumRouter);
app.use('/api/devRoutes', devRouter);

// Serve client from build in production
if (process.env.NODE_ENV === 'production') {
console.log(`SERVER STARTED IN PRODUCTION`);
app.use(express.static(path.join(__dirname, '../../client/build')));

app.get('*', (req: Request, res: Response) =>
res.sendFile(path.resolve(__dirname, '../../client/build/index.html')),
);
}

// Catch all route handler
app.use((_req, _res) => {
throw new NotFoundError();
});

// Global error handler
app.use(errorHandler);

export default app;
19 changes: 5 additions & 14 deletions server/config/db.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import mongoose from 'mongoose';
import dotenv from 'dotenv';
dotenv.config();
import { DatabaseConnectionError } from '../errors';

const connectDB = async (): Promise<void> => {
const connectDB = async (mongoUri: string) => {
try {
// Check that MONGO_URI is defined
if (!process.env.MONGO_URI) {
throw new Error('MONGO_URI must be defined in the environment variables.');
}

const connection = await mongoose.connect(process.env.MONGO_URI);

console.log(`MongoDB is connected to: ${connection.connection.host}`);
const connection = await mongoose.connect(mongoUri);
console.log(`🍃 MongoDB is connected to: ${connection.connection.host}`);
} catch (error) {
if (error instanceof Error) {
console.error(error.message);
} else {
console.error('❌ Error connecting to the database ❌');
}
process.exit(1);
throw new DatabaseConnectionError();
}
};

Expand Down
82 changes: 21 additions & 61 deletions server/index.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,32 @@
import path from 'path';
import express, { Request, Response, Application } from 'express';
import 'express-async-errors';
import userRoutes from './routes/userRoutes';
import profileRoutes from './routes/profileRoutes';
import authRoutes from './routes/authRoutes';
import imageRoutes from './routes/imageRoutes';
import alumniRoutes from './routes/alumniRoutes';
import forumRoutes from './routes/forumRoutes';
import devRoutes from './routes/devRoutes';
import applicationsRoutes from './routes/applicationsRoutes';
import app from './app';
import connectDB from './config/db';
import dotenv from 'dotenv';
import cookieParser from 'cookie-parser';
import errorHandler from './middleware/errorHandler';
import { NotFoundError } from './errors';

dotenv.config();

const app: Application = express();

app.use(express.json());
app.use(cookieParser());

app.get('/health', (req: Request, res: Response) => {
res.status(200).send('OK');
});

app.use('/api/users', userRoutes);
app.use('/api/profiles', profileRoutes);
app.use('/api/auth', authRoutes);
app.use('/api/images', imageRoutes);
app.use('/api/alumni', alumniRoutes);
app.use('/api/forums', forumRoutes);
app.use('/api/devRoutes', devRoutes);
app.use('/api/applications', applicationsRoutes);

if (process.env.NODE_ENV === 'production') {
console.log(`SERVER STARTED IN PRODUCTION`);
app.use(express.static(path.join(__dirname, '../../client/build')));

app.get('*', (req: Request, res: Response) =>
res.sendFile(path.resolve(__dirname, '../../client/build/index.html')),
);
} else {
console.log('SERVER STARTED IN DEV');
app.get('/api', (req: Request, res: Response) => {
res.json({ message: 'API Running - Hazzah!' });
});
}

app.use((_req, _res) => {
throw new NotFoundError();
});

app.use(errorHandler);

const PORT: number = Number(process.env.PORT) || 3000;

export const startServer = () => {
connectDB();

// Hazzah!
const hazzah = process.env.NODE_ENV === 'development' ? 'Hazzah! ' : '';

export const startServer = async () => {
// Environment variable checks
if (!process.env.JWT_SECRET) throw Error('❌ JWT_SECRET must be defined!');
if (!process.env.MONGO_URI) throw Error('❌ MONGO_URI must be defined!');
if (!process.env.POSTGRES_USER) throw Error('❌ POSTGRES_USER must be defined!');
if (!process.env.POSTGRES_DB) throw Error('❌ POSTGRES_DB must be defined!');
if (!process.env.POSTGRES_PASSWORD) throw Error('❌ POSTGRES_PASSWORD must be defined!');
if (!process.env.AWS_ACCESS_KEY_ID) throw Error('❌ AWS_ACCESS_KEY_ID must be defined!');
if (!process.env.AWS_SECRET_ACCESS_KEY) throw Error('❌ AWS_SECRET_ACCESS_KEY must be defined!');
if (!process.env.AWS_REGION) throw Error('❌ AWS_REGION must be defined!');
if (!process.env.BUCKET_NAME) throw Error('❌ BUCKET_NAME must be defined!');

// Connect to MongoDB
await connectDB(process.env.MONGO_URI);

// Startup the server
return app.listen(PORT, () =>
console.log(`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`),
console.log(`💥 ${hazzah}Server running in ${process.env.NODE_ENV} mode on port ${PORT}`),
);
};

if (require.main === module) {
startServer();
}

export default app;
9 changes: 9 additions & 0 deletions server/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import alumniRouter from './alumniRoutes';
import authRouter from './authRoutes';
import forumRouter from './forumRoutes';
import imageRouter from './imageRoutes';
import profileRouter from './profileRoutes';
import userRouter from './userRoutes';
import devRouter from './devRoutes';

export { alumniRouter, authRouter, forumRouter, imageRouter, profileRouter, userRouter, devRouter };

0 comments on commit 3e6bc86

Please sign in to comment.