Skip to content

Commit

Permalink
feat: integrate ewl
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaspearson committed Apr 11, 2022
1 parent 7492de1 commit d558edb
Show file tree
Hide file tree
Showing 179 changed files with 1,300 additions and 1,513 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
API_HOST=localhost
API_PORT=3000
LOG_LEVEL=debug
JWT_EXPIRATION=1d
JWT_SECRET=secret
NODE_ENV=development
Expand Down
1,479 changes: 710 additions & 769 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .yarn/cache/boom-npm-7.3.0-ac5e6f05ec-86d22bef32.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .yarn/cache/hoek-npm-6.1.3-a4c640d59f-d0af183df1.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .yarn/cache/resolve-patch-2bdd9af071-a0a4d1f740.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12 changes: 5 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"db:migration:generate": "typeorm migration:generate -n",
"db:migration:revert": "typeorm migration:revert",
"db:migration:run": "typeorm migration:run",
"db:start": "run exec:docker-compose up -d --renew-anon-volumes --force-recreate postgres",
"db:start": "run exec:docker-compose up -d --renew-anon-volumes --force-recreate db",
"debug": "run build && run watch:debug",
"docker:build": "yarn exec:docker-compose build --no-cache api",
"docker:logs": "yarn exec:docker-compose logs -f api",
Expand All @@ -37,7 +37,7 @@
"start:dev": "run db:start && nodemon -w $(pwd)/src -x ts-node-dev -r tsconfig-paths/register --transpile-only $(pwd)/src/index.ts",
"start:docker": "run build && run exec:docker-compose up -d --build api && run exec:docker-compose logs -f api",
"test": "run test:unit && run test:integration",
"test:integration": "run exec:docker-compose up -d -V --force-recreate postgres && run exec:jest --config $(pwd)/test/integration/jest.config.js",
"test:integration": "run exec:docker-compose up -d -V --force-recreate db && run exec:jest --config $(pwd)/test/integration/jest.config.js",
"test:integration:ci": "run exec:jest --config $(pwd)/test/integration/jest.config.js",
"test:unit": "run exec:jest --config $(pwd)/test/unit/jest.config.js",
"test:unit:badges": "run test:unit && jest-coverage-badges input $(pwd)/coverage/unit/coverage-summary.json output $(pwd)/coverage/unit",
Expand All @@ -48,24 +48,23 @@
"watch:ts": "tsc -w"
},
"dependencies": {
"@hapi/boom": "^9.1.4",
"bcryptjs": "^2.4.3",
"boom": "^7.3.0",
"class-transformer": "^0.5.1",
"class-transformer-validator": "^0.9.1",
"class-validator": "^0.13.2",
"dotenv": "^16.0.0",
"ewl": "^0.1.5",
"express": "^4.17.3",
"joi": "^17.6.0",
"jsonwebtoken": "^8.5.1",
"module-alias": "^2.2.2",
"pg": "^8.7.3",
"reflect-metadata": "^0.1.13",
"typeorm": "^0.2.45",
"winston": "^3.7.2"
"typeorm": "^0.2.45"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/boom": "^7.3.1",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.13",
"@types/jest": "^27.4.1",
Expand All @@ -88,7 +87,6 @@
"jest": "^27.5.1",
"jest-coverage-badges": "^1.1.2",
"jest-environment-node": "^27.5.1",
"logform": "^2.4.0",
"nodemon": "^2.0.15",
"prettier": "^2.6.2",
"pretty-quick": "^3.1.3",
Expand Down
5 changes: 3 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import express from 'express';
import { Server } from 'http';

import userController from '@/user/user.controller';
import { logger } from '@/logger';
import { ewl, initEwl } from '@/logger';
import { errorMiddleware } from '@/middleware/error.middleware';
import { loggerMiddleware } from '@/middleware/logger.middleware';
import { notFoundMiddleware } from '@/middleware/not-found.middleware';
Expand All @@ -13,6 +13,7 @@ export default class App {

constructor() {
this.app = express();
initEwl(this.app);
this.initializePreControllerMiddleware();
this.initializeControllers();
this.initializePostControllerMiddleware();
Expand All @@ -31,7 +32,7 @@ export default class App {
this.server = this.app.listen(
port,
/* istanbul ignore next: ignore callback */ () => {
logger.debug(`App: Listening on port ${port}!`);
ewl.debug(`App: Listening on port ${port}!`);
}
);
}
Expand Down
3 changes: 3 additions & 0 deletions src/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ function getValidationSchema(): Joi.ObjectSchema {
return Joi.object({
API_HOST: Joi.string().hostname().description('The API server host url').default('localhost'),
API_PORT: Joi.number().port().description('The API server port').default(3000),
LOG_LEVEL: Joi.string()
.valid('debug', 'error', 'info', 'log', 'verbose', 'warn')
.default('log'),
JWT_EXPIRATION: Joi.string()
.regex(/^\d+[smhd]$/)
.description('The validity period of the JWT token')
Expand Down
2 changes: 1 addition & 1 deletion src/common/models/http-exception.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Boom from 'boom';
import { Boom } from '@hapi/boom';

export interface HttpException extends Boom {
status?: number;
Expand Down
4 changes: 2 additions & 2 deletions src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Connection, createConnection, getConnectionOptions } from 'typeorm';
import { Environment } from '@/common/enums/environment.enum';
import { configureConnectionOptions } from '@/db/config.db';
import { seedDatabase } from '@/db/fixtures/seeder';
import { logger } from '@/logger';
import { ewl } from '@/logger';

interface AdditionalConnectionOptions {
database?: string;
Expand All @@ -21,7 +21,7 @@ export async function init(options?: AdditionalConnectionOptions): Promise<Conne
configureConnectionOptions(connectionOptions);
const connection = await createConnection(Object.assign(connectionOptions, options));
if (process.env.NODE_ENV === Environment.Development) {
logger.debug('Seeding database');
ewl.debug('Seeding database');
await seedDatabase(connection);
}
return connection;
Expand Down
2 changes: 1 addition & 1 deletion src/db/repositories/user.repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Boom from 'boom';
import * as Boom from '@hapi/boom';
import {
AbstractRepository,
DeepPartial,
Expand Down
13 changes: 4 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,19 @@ import { resolve } from 'path';
import App from '@/app';
import * as config from '@/common/config';
import * as db from '@/db';
import * as logger from '@/logger';
import { ewl } from '@/logger';

// We use dotenv and joi to set the
// environment variables in the app.
config.init({ envFilePath: [resolve(process.cwd(), '.env')] });

// Winston is used for logging, lets
// prepare the logger implementation.
logger.init();

(async () => {
const app = new App();
try {
await db.init();
} catch (error) {
logger.logger.error(`Database: Error connecting!`, error);
return error;
ewl.error('Database: Error connecting!');
throw error;
}
// Finally, initialize the app.
const app = new App();
app.listen();
})();
110 changes: 38 additions & 72 deletions src/logger/index.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,44 @@
import * as winston from 'winston';
import { Ewl, LogLevel, httpContextMiddleware, requestIdHandler } from 'ewl';
import { Application } from 'express';

export let logger: winston.Logger;
export let ewl: Ewl;

export function init(): void {
// Configure levels and transports, and set local variable
logger = winston.createLogger({
level: 'debug',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
winston.format.align()
),
levels: getCustomLogLevels().levels,
transports: [
new winston.transports.File({
filename: `./logs/debug.log`,
level: 'debug',
}),
new winston.transports.File({
filename: `./logs/info.log`,
level: 'info',
}),
new winston.transports.File({
filename: `./logs/warn.log`,
level: 'warning',
}),
new winston.transports.File({
filename: `./logs/error.log`,
level: 'error',
handleExceptions: true,
}),
],
export function initEwl(app: Application): void {
ewl = new Ewl({
attachRequestId: true,
environment: process.env.ENVIRONMENT || 'development',
label: 'app',
logLevel: (process.env.LOG_LEVEL as LogLevel) || 'error',
useLogstashFormat: false,
version: process.env.VERSION || 'local',
});

if (process.env.NODE_ENV !== 'production') {
logger.add(
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
winston.format.colorize(),
winston.format.align(),
winston.format.printf((info) => {
let msg = `${info.timestamp}`;
msg = `${msg}: ${info.level}`;
msg = `${msg}: ${info.message.trim()}`;
return msg;
})
),
})
);
}
// Add custom colours
winston.addColors(getCustomLogLevels().colors);
}
// Use express-winston for logging request information
app.use(
ewl.createHandler({
bodyBlacklist: ['accessToken', 'password', 'refreshToken'],
colorize: true,
expressFormat: false,
headerBlacklist: ['cookie', 'token'],
ignoreRoute: () => false,
meta: true,
metaField: 'express',
msg: 'HTTP {{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}',
requestWhitelist: [
'headers',
'method',
'httpVersion',
'originalUrl',
'query',
'params',
'url',
],
responseWhitelist: ['headers', 'statusCode'],
statusLevels: true,
})
);

function getCustomLogLevels() {
return {
levels: {
error: 1,
warn: 2,
info: 3,
verbose: 4,
debug: 5,
silly: 6,
},
colors: {
error: 'red',
warn: 'orange',
info: 'yellow',
debug: 'blue',
verbose: 'white',
silly: 'purple',
},
};
// Use express-http-context for context injection (request id)
app.use(httpContextMiddleware);
app.use(requestIdHandler);
}
2 changes: 1 addition & 1 deletion src/middleware/error.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Boom from 'boom';
import * as Boom from '@hapi/boom';
import { NextFunction, Request, Response } from 'express';

import { HttpException } from '@/common/models/http-exception.model';
Expand Down
4 changes: 2 additions & 2 deletions src/middleware/logger.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { NextFunction, Request, Response } from 'express';

import { logger } from '@/logger';
import { ewl } from '@/logger';

export function loggerMiddleware(req: Request, _: Response, next: NextFunction): void {
logger.debug(`${req.method} ${req.path}`);
ewl.debug(`${req.method} ${req.path}`);
next();
}
2 changes: 1 addition & 1 deletion src/middleware/not-found.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Boom from 'boom';
import * as Boom from '@hapi/boom';
import { NextFunction, Request, Response } from 'express';

export function notFoundMiddleware(req: Request, res: Response, next: NextFunction): void {
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/validation.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Boom from 'boom';
import * as Boom from '@hapi/boom';
import { ClassType, transformAndValidate } from 'class-transformer-validator';
import { NextFunction, Request, RequestHandler, Response } from 'express';

Expand Down
2 changes: 1 addition & 1 deletion src/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Boom from 'boom';
import * as Boom from '@hapi/boom';
import { getCustomRepository } from 'typeorm';

import {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/express.utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express from 'express';

import { logger } from '@/logger';
import { ewl } from '@/logger';

/**
* Create a new function that passes any error to the next function.
Expand All @@ -16,7 +16,7 @@ export function safe(
try {
await handler(req, res, next);
} catch (error) {
logger.error((error as Error).message);
ewl.error((error as Error).message);
next(error);
}
};
Expand Down
6 changes: 3 additions & 3 deletions src/utils/jwt.utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Boom from 'boom';
import express from 'express';
import * as Boom from '@hapi/boom';
import * as jsonwebtoken from 'jsonwebtoken';

import { logger } from '@/logger';
import { ewl } from '@/logger';

export function generateJwtTokens(jwtPayload: Api.JwtPayload): Api.JwtTokens {
const accessTokenOptions: jsonwebtoken.SignOptions = {
Expand All @@ -24,7 +24,7 @@ export function getJwtFromRequest(jwtString: JwtString): Api.Jwt {
}
return verifyJwt(jwtString) as Api.Jwt;
} catch (error) {
logger.debug(`Request is missing a valid jwt: ${(error as Error).message}`);
ewl.debug(`Request is missing a valid jwt: ${(error as Error).message}`);
throw Boom.unauthorized('Invalid jwt provided.');
}
}
Expand Down
3 changes: 2 additions & 1 deletion test/integration/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import * as config from '@/common/config';
config.init({ envFilePath: [resolve(__dirname, '.env.integration')] });

jest.mock('@/logger', () => ({
logger: {
ewl: {
debug: jest.fn(),
error: jest.fn(),
info: jest.fn(),
},
initEwl: jest.fn(),
}));
3 changes: 2 additions & 1 deletion test/unit/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import * as config from '@/common/config';
config.init({ envFilePath: [resolve(__dirname, '.env.unit')] });

jest.mock('@/logger', () => ({
logger: {
ewl: {
debug: jest.fn(),
error: jest.fn(),
info: jest.fn(),
},
initEwl: jest.fn(),
}));
5 changes: 3 additions & 2 deletions test/unit/specs/middleware/auth.middleware.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Boom from 'boom';
import * as Boom from '@hapi/boom';
import { Request, Response } from 'express';

import { authMiddleware } from '@/middleware/auth.middleware';
Expand Down Expand Up @@ -44,7 +44,8 @@ describe('Auth Middleware', () => {

test('should throw if the jwt payload is invalid', () => {
const mockNext = jest.fn();
const jwtString = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' as JwtString;
const jwtString =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' as JwtString;
const request = { headers: { authorization: `Bearer ${jwtString}` } } as Request;
expect(() => authMiddleware(request, {} as Response, mockNext)).toThrowError(
Boom.unauthorized('Invalid jwt provided.')
Expand Down
Loading

0 comments on commit d558edb

Please sign in to comment.