Skip to content

Commit

Permalink
Enforce "no-magic-numbers" liniting rule by using constants instead o…
Browse files Browse the repository at this point in the history
…f the concrete value (e.g. HTTP and RPC status codes)
  • Loading branch information
derbenoo committed Jan 8, 2019
1 parent f48865b commit ae15aed
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 16 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"scripts": {
"build": "tsc -p src/",
"test": "jest --config test/jest.config.json",
"lint": "tslint --project src/",
"debug": "TS_NODE_PROJECT=src/tsconfig.json TS_NODE_FILE=true node -r ts-node/register --inspect=0.0.0.0 --inspect-brk example/server.ts",
"start": "ts-node --project src/tsconfig.json example/server.ts",
"gen-doc": "typedoc --hideGenerator --tsconfig src/tsconfig.json --out docs/ src/"
Expand Down
5 changes: 3 additions & 2 deletions src/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import * as crypto from 'crypto';
import { HTTP } from './constants';
import { ILogger } from './interfaces';

export function generateAuthMiddleware(apiKeyHash: string, log: ILogger) {
Expand All @@ -12,15 +13,15 @@ export function generateAuthMiddleware(apiKeyHash: string, log: ILogger) {
// Check if API key was provided
if (!apiKey) {
log.warn('Unsuccessful authentication attempt (missing API key)');
return res.status(401).json({ error: 'MISSING_APIKEY', message: 'No API key provided via the HTTP \'authorization\' header' });
return res.status(HTTP.UNAUTHORIZED).json({ error: 'MISSING_APIKEY', message: 'No API key provided via the HTTP \'authorization\' header' });
}

// Check if API key is correct
const hash: string = crypto.createHash('sha256').update(apiKey, 'utf8').digest('hex');

if (hash !== apiKeyHash) {
log.warn('Unsuccessful authentication attempt (incorrect API key)');
return res.status(401).json({ error: 'WRONG_APIKEY', message: 'The provided API key is incorrect' });
return res.status(HTTP.UNAUTHORIZED).json({ error: 'WRONG_APIKEY', message: 'The provided API key is incorrect' });
}

// Authenticated successfully
Expand Down
22 changes: 21 additions & 1 deletion src/rpc-schema.ts → src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
/** HTTP constants (https://tools.ietf.org/html/rfc2616#section-10) */
export const HTTP = {
OK: 200,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
INTERNAL_SERVER_ERROR: 500,
};

/** JSON RPC 2.0 constants (https://www.jsonrpc.org/specification) */
export const RPC = {
VERSION: '2.0',
ERROR: {
PARSE_ERROR: -32700,
INVALID_REQUEST: -32600,
METHOD_NOT_FOUND: -32601,
INVALID_PARAMS: -32602,
INTERNAL_ERROR: -32603,
SERVER_ERROR: -32000,
},
};

/**
* JSON schema for validating RPC requests
* Taken from: https://github.com/fge/sample-json-schemas/blob/master/jsonrpc2.0/jsonrpc-request-2.0.json
*/

export const rpcRequestSchema = {
description: 'A JSON RPC 2.0 request',
oneOf: [
Expand Down
16 changes: 8 additions & 8 deletions src/rpc-endpoint.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Ajv from 'ajv';
import { Express } from 'express';
import { HTTP, RPC, rpcRequestSchema } from './constants';
import { IEndpointOptions, ILogger, IRpcModule, IRpcRequest, IRpcResponse } from './interfaces';
import { rpcRequestSchema } from './rpc-schema';

/**
* JSON-RPC 2.0 endpoint class
Expand Down Expand Up @@ -36,7 +36,7 @@ export class RpcEndpoint {
static coerceRequestsToJsonRpc2Middleware(req, res, next) {
RpcEndpoint.getRpcRequests(req).forEach((rpcRequest) => {
if (typeof rpcRequest === 'object' && !rpcRequest.hasOwnProperty('jsonrpc')) {
rpcRequest.jsonrpc = '2.0';
rpcRequest.jsonrpc = RPC.VERSION;
if (!rpcRequest.hasOwnProperty('id')) {
rpcRequest.id = null;
}
Expand All @@ -51,8 +51,8 @@ export class RpcEndpoint {
const ajv = new Ajv();
const validate: Ajv.ValidateFunction = ajv.compile(rpcRequestSchema);
if (!validate(req.body)) {
return res.status(400).json({
code: -32600,
return res.status(HTTP.BAD_REQUEST).json({
code: RPC.ERROR.INVALID_REQUEST,
message: 'Invalid Request: The JSON sent is not a valid request object.',
data: validate.errors,
});
Expand All @@ -71,7 +71,7 @@ export class RpcEndpoint {
throw {
name: 'Method not found',
message: `The method '${method}' does not exist / is not available.`,
rpcErrCode: -32601,
rpcErrCode: RPC.ERROR.METHOD_NOT_FOUND,
};
}
}
Expand Down Expand Up @@ -110,7 +110,7 @@ export class RpcEndpoint {
} catch (err) {
return {
error: {
code: err.rpcErrCode || -32000,
code: err.rpcErrCode || RPC.ERROR.SERVER_ERROR,
message: `${err.name}: ${err.message}`,
data: err.stack,
},
Expand All @@ -120,13 +120,13 @@ export class RpcEndpoint {

const responses: IRpcResponse[] = results.map((result, index) => {
return {
jsonrpc: '2.0',
jsonrpc: RPC.VERSION,
id: rpcRequests[index].id,
...result,
};
});

const statusCode = responses.filter((rsp) => rsp.error).length > 0 ? 500 : 200;
const statusCode = responses.filter((rsp) => rsp.error).length > 0 ? HTTP.INTERNAL_SERVER_ERROR : HTTP.OK;

if (!Array.isArray(req.body)) {
return res.status(statusCode).json(responses[0]);
Expand Down
9 changes: 5 additions & 4 deletions src/rpc-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as morgan from 'morgan';
import * as nconf from 'nconf';
import { Writable } from 'stream';
import { generateAuthMiddleware } from './authentication';
import { HTTP, RPC } from './constants';
import { IEndpointOptions, ILogger, IServerOptions } from './interfaces';
import { RpcEndpoint } from './rpc-endpoint';
export { RpcEndpoint } from './rpc-endpoint';
Expand Down Expand Up @@ -164,15 +165,15 @@ export class RpcServer {
// Register error middleware
app.use(function errorMiddleware(err, req, res, next) {
if (err.type === 'entity.parse.failed') {
return res.status(400).json({
code: -32700,
return res.status(HTTP.BAD_REQUEST).json({
code: RPC.ERROR.PARSE_ERROR,
message: 'Parse error: Invalid JSON was received by the server.',
data: err.stack,
});
}

return res.status(500).json({
code: -32000,
return res.status(HTTP.INTERNAL_SERVER_ERROR).json({
code: RPC.ERROR.SERVER_ERROR,
message: 'An unexpected server error has occurred',
data: err.stack,
});
Expand Down
2 changes: 1 addition & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"extends": "@decentro/tslint",
"rules": {
"ban-types": false,
"no-magic-numbers": false
"no-magic-numbers": true
}
}

0 comments on commit ae15aed

Please sign in to comment.