Skip to content

Commit

Permalink
Add translations to errors
Browse files Browse the repository at this point in the history
  • Loading branch information
horstenwillem authored and knor-el-snor committed May 2, 2018
1 parent b28ceca commit 3991b6c
Show file tree
Hide file tree
Showing 16 changed files with 827 additions and 79 deletions.
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ SWAGGER_BASE_URL=localhost:3000
## Authentication
JWT_SECRET=

## New Relic
## Translations
TRANSLATION_API_URL=
TRANSLATION_API_TOKEN=

## Tracking
NEW_RELIC_LICENSE_KEY=

## Optional
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ yarn-error.log

# Database data
data

# Translations
locales/
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dotenv-safe": "~5.0.1",
"express": "~4.16.2",
"express-validation": "^1.0.2",
"http-status": "~1.0.1",
"http-status": "~1.1.0",
"joi": "~13.2.0",
"jsonade": "~1.1.0",
"knex": "~0.14.6",
Expand All @@ -34,9 +34,9 @@
"newrelic": "~4.1.0",
"pg": "~7.4.1",
"redis": "~2.8.0",
"tree-house": "~3.2.0",
"tree-house": "~3.3.0",
"tree-house-authentication": "~2.0.5",
"tree-house-errors": "~1.0.3",
"tree-house-errors": "~1.1.1",
"uuid": "~3.2.1",
"winston": "~2.4.2"
},
Expand All @@ -49,7 +49,7 @@
"@types/helmet": "~0.0.37",
"@types/http-status": "~0.2.30",
"@types/jest": "~22.2.2",
"@types/joi": "~13.0.7",
"@types/joi": "~13.0.8",
"@types/knex": "~0.14.12",
"@types/lodash": "~4.14.108",
"@types/mandrill-api": "~1.0.30",
Expand All @@ -58,7 +58,7 @@
"@types/uuid": "~3.4.3",
"@types/winston": "~2.3.9",
"auditjs": "^2.4.3",
"coveralls": "^3.0.0",
"coveralls": "^3.0.1",
"faker": "^4.1.0",
"jest": "^22.4.3",
"node-mocks-http": "^1.5.8",
Expand Down
2 changes: 1 addition & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ for (const x in appConfig.VERSIONS) {
}

// Error handling
app.use((error, _req, res, _next) => responder.error(res, error));
app.use((error, req, res, _next) => responder.error(req, res, error));
app.all('*', (_req, res) => res.sendStatus(404));

export { app };
Expand Down
8 changes: 6 additions & 2 deletions src/config/errors.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { errors as defaults } from 'tree-house-errors';
import { errors as defaults, ErrorType } from 'tree-house-errors';

// tslint:disable:max-line-length
export const errors = Object.assign({}, defaults, {
export const errors = <Error>Object.assign({}, defaults, {
USER_INACTIVE: { code: 'USER_INACTIVE', message: 'Activate user account before login' },
USER_DUPLICATE: { code: 'USER_DUPLICATE', message: 'A user with this email already exists' },
USER_NOT_FOUND: { code: 'USER_NOT_FOUND', message: 'User not found' },
Expand All @@ -11,3 +11,7 @@ export const errors = Object.assign({}, defaults, {
CODE_DUPLICATE: { code: 'CODE_DUPLICATE', message: 'A code with this code already exists' },
});
// tslint:enable:max-line-length

export interface Error {
[key: string]: ErrorType;
}
6 changes: 3 additions & 3 deletions src/config/roles.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ export const roles = <Roles>{
USER: {
code: 'USER',
name: 'user',
description: '', // i18n ?
description: 'roles:user', // i18n key
level: 0,
},
ADMIN: {
code: 'ADMIN',
name: 'admin',
description: '', // i18n?
description: 'roles:admin', // i18n key
level: 10,
},
SUPERUSER: {
code: 'SUPER_USER',
name: 'superuser',
description: '', // i18n?
description: 'roles:superuser', // i18n key
level: 999,
},
};
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Filters } from './models/filters.model';

export const errorTranslations = `${process.cwd()}/locales`;

export const envs = {
TEST: 'test',
DEVELOP: 'development',
Expand Down
13 changes: 9 additions & 4 deletions src/lib/responder.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as httpStatus from 'http-status';
import { Response } from 'express';
import { parseErrors } from 'tree-house-errors';
import { parseErrors, I18nOptions } from 'tree-house-errors';
import { ErrorSerializer } from 'jsonade';
import { envs } from '../constants';
import { envs, errorTranslations } from '../constants';
import { logger } from '../lib/logger';

/**
Expand All @@ -18,9 +18,14 @@ export const responder: { success: Function, error: Function } = {
logger.debug('Response: ', serializer.serialize(payload, { totalCount }));
return res.status(status).json(serializer.serialize(payload, { totalCount }));
},
error: (res: Response, errors: any) => {
error: (req: Request, res: Response, errors: any) => {
logger.debug('Error:', errors);
const parsedError = parseErrors(errors);

const i18nOptions: I18nOptions = {
language: req.headers ? (req.headers['accept-language'] || 'en') : 'en',
path: errorTranslations,
};
const parsedError = parseErrors(errors, i18nOptions);

if (process.env.NODE_ENV === envs.PRODUCTION) Object.assign(parsedError, { meta: undefined }); // Do not send stacktrace in production
const serializerError = ErrorSerializer.serialize([parsedError]);
Expand Down
2 changes: 1 addition & 1 deletion src/repositories/user.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function update(userId: string, values: UserUpdate | PartialUserUpd
*/
export async function updatePassword(userId: string, password: string): Promise<User> {
const hashedPw = await getHashedPassword(password, settings.saltCount);
return update(userId, { password: hashedPw, resetPwToken: null });
return update(userId, { password: hashedPw, resetPwToken: null, registrationCompleted: true });
}


Expand Down
3 changes: 3 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import './config/load-env'; // Load our environment variables
import 'newrelic';

import * as treehouse from 'tree-house';
import { importTranslations } from 'tree-house-errors';
import { logger } from './lib/logger';
import { app } from './app';
import { errorTranslations } from './constants';

treehouse.startServer(app, {
title: 'Silverback',
port: parseInt(process.env.PORT || '3000', 10),
pre: () => importTranslations(process.env.TRANSLATION_API_URL, process.env.TRANSLATION_API_TOKEN, { destination: errorTranslations }),
});

process.on('unhandledRejection', (e) => {
Expand Down
4 changes: 3 additions & 1 deletion tests/integration/auth.route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ describe('/auth', () => {
});

describe('POST /login', () => {
// TODO: Test if brute force protection gets reset after successful attempt!
it('Should succesfully login a user with correct credentials', async () => {
const { body, status } = await request(app)
.post(`${prefix}/auth/login`)
Expand Down Expand Up @@ -270,6 +269,9 @@ describe('/auth', () => {
expect(status).toEqual(httpStatus.OK);
expect(body).toEqual({});

const updatedUser = await findById(newUser.id);
expect(updatedUser.registrationCompleted).toEqual(true);

// Try to login with changed password
const { body: body2, status: status2 } = await request(app)
.post(`${prefix}/auth/login`)
Expand Down
1 change: 1 addition & 0 deletions tests/integration/user.route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ describe('/users', () => {
it('Should throw an error when user has no admin rights', async () => {
const { body, status } = await request(app)
.get(`${prefix}/users`)
.set('Accept-Language', 'nl')
.set('Authorization', `Bearer ${userToken}`);

expect(status).toEqual(httpStatus.UNAUTHORIZED);
Expand Down
6 changes: 4 additions & 2 deletions tests/lib/responder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,11 @@ describe('lib/responder', () => {

describe('error', () => {
it('Should return error with matching properties and status code', () => {
const req = httpMocks.createRequest();
const res = httpMocks.createResponse();
const error = new BadRequestError(errors.INVALID_INPUT);

responder.error(res, error);
responder.error(req, res, error);
expect(res.statusCode).toEqual(httpStatus.BAD_REQUEST);
expect(res._isJSON()).toEqual(true);
expect(JSON.parse(res._getData())).toEqual({
Expand All @@ -86,10 +87,11 @@ describe('lib/responder', () => {
it('Should return error without stacktrace in production', () => {
process.env.NODE_ENV = envs.PRODUCTION;

const req = httpMocks.createRequest();
const res = httpMocks.createResponse();
const error = new BadRequestError(errors.INVALID_INPUT, { stack: 'MYSTACK' });

responder.error(res, error);
responder.error(req, res, error);
expect(res.statusCode).toEqual(httpStatus.BAD_REQUEST);
expect(res._isJSON()).toEqual(true);
expect(JSON.parse(res._getData())).toEqual({
Expand Down
4 changes: 2 additions & 2 deletions tests/middleware/bruteforce.middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ describe('bruteforce middleware', () => {
await clearMemoryStore();
});

it('Should start blocking requests with the same ip and username after number of retries', async () => {
fit('Should start blocking requests with the same ip and username after number of retries', async () => {
app.use('/test', setUserBruteForce, (_req, res) => res.status(httpStatus.OK).send('Welcome'));
app.use((error, _req, res, _next) => responder.error(res, error));
app.use((error, req, res, _next) => responder.error(req, res, error));

const numberOfCalls = userBruteConfig.freeRetries + 1;

Expand Down
8 changes: 7 additions & 1 deletion tests/test.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { envs } from '../src/constants';
import { envs, errorTranslations } from '../src/constants';
import { existsSync, mkdirSync } from 'fs';

export const environment = {
NODE_ENV: envs.TEST,
Expand All @@ -23,5 +24,10 @@ Object.keys(environment).forEach((key) => {
// Must be after env variables
import { logger } from '../src/lib/logger';

// Locales folder
if (!existsSync(errorTranslations)) {
mkdirSync(errorTranslations);
}

// Overwrite error console.logs
logger.error = jest.fn((error) => { });

0 comments on commit 3991b6c

Please sign in to comment.