Skip to content

Commit

Permalink
feat: create uniform error structure (#115)
Browse files Browse the repository at this point in the history
Signed-off-by: james-a-morris <[email protected]>
  • Loading branch information
james-a-morris authored Nov 27, 2024
1 parent f2b8e95 commit df6c94d
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 62 deletions.
1 change: 1 addition & 0 deletions apps/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@repo/indexer-api": "workspace:*",
"@repo/persistence-example": "workspace:*",
"@repo/template": "workspace:*",
"@repo/error-handling": "workspace:*",
"@uma/logger": "^1.3.0",
"dotenv": "^16.4.5",
"source-map-support": "^0.5.21",
Expand Down
2 changes: 1 addition & 1 deletion apps/node/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import "source-map-support/register";

import dotenv from "dotenv";
import assert from "assert";

import * as Template from "@repo/template";
import * as Indexer from "@repo/indexer";
import * as PersistenceExample from "@repo/persistence-example";
import * as IndexerApi from "@repo/indexer-api";
import { assert } from "@repo/error-handling";
import { Logger } from "@uma/logger";

dotenv.config();
Expand Down
13 changes: 1 addition & 12 deletions packages/error-handling/src/errors/IndexerHTTPError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,11 @@ export { StatusCodes };
*/
export abstract class IndexerHTTPError extends IndexerError {
constructor(
private readonly httpStatusCode: StatusCodes,
public readonly httpStatusCode: StatusCodes,
errorName: string,
errorMessage: string,
errorData?: Record<string, string>,
) {
super(errorName, errorMessage, errorData);
}

/**
* A function used by `JSON.stringify` to specify which data will be serialized
* @returns A formatted JSON
*/
public toJSON(): Record<string, unknown> {
return {
statusCode: this.httpStatusCode,
...super.toJSON(),
};
}
}
1 change: 1 addition & 0 deletions packages/indexer-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@repo/error-handling": "workspace:*",
"@repo/indexer": "workspace:*",
"@repo/webhooks": "workspace:*",
"@repo/indexer-database": "workspace:*",
Expand Down
10 changes: 7 additions & 3 deletions packages/indexer-api/src/dtos/deposits.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import * as s from "superstruct";
import { entities } from "@repo/indexer-database";

const stringToInt = s.coerce(s.number(), s.string(), (value) =>
parseInt(value),
);
const stringToInt = s.coerce(s.number(), s.string(), (value) => {
// Ensure the value is a valid integer string
if (!/^-?\d+$/.test(value)) {
return value;
}
return parseInt(value, 10);
});

export const DepositsParams = s.object({
depositor: s.optional(s.string()),
Expand Down
45 changes: 45 additions & 0 deletions packages/indexer-api/src/error-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
isIndexerError,
isIndexerHTTPError,
StatusCodes,
} from "@repo/error-handling";
import { Request, Response, NextFunction } from "express";
import { isHttpError } from "./express-app";
import { StructError } from "superstruct";

const DEFAULT_STATUS = StatusCodes.BAD_REQUEST;

const errorHandler = (
err: unknown,
req: Request,
res: Response,
_: NextFunction,
): void => {
// At a base level we need to confirm that this isn't a valid
// passthrough - if so ignore
if (isIndexerError(err)) {
// If we have a custom sub-type to specify the error code, use it
// otherwise default to a status 400
const httpStatus = isIndexerHTTPError(err)
? err.httpStatusCode
: DEFAULT_STATUS;
res.status(httpStatus).json(err.toJSON());
} else if (isHttpError(err)) {
res.status(err.status ?? DEFAULT_STATUS).json({
message: err.message,
error: "NavigationError",
});
} else if (err instanceof StructError) {
res.status(StatusCodes.BAD_REQUEST).json({
error: "ValidationError",
message: err.message,
});
} else if (err instanceof Error) {
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
error: "UnknownError",
message: err.message,
});
}
};

export default errorHandler;
28 changes: 5 additions & 23 deletions packages/indexer-api/src/express-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import cors from "cors";
import bodyParser from "body-parser";
import type { Request, Response, NextFunction, Express, Router } from "express";
import express from "express";
import errorHandler from "./error-handler";

export class HttpError extends Error {
status?: number;
Expand Down Expand Up @@ -31,33 +32,14 @@ export function ExpressApp(routers: RouterConfigs): Express {
});

app.use(function (_: Request, __: Response, next: NextFunction) {
const error = new HttpError("Not Found");
const error = new HttpError("Route does not exist.");
error["status"] = 404;
next(error);
});

app.use(function (
err: HttpError | Error,
req: Request,
res: Response,
// this needs to be included even if unused, since 4 param call triggers error handler
_: NextFunction,
) {
const request = {
method: req.method,
path: req.path,
body: req.body,
};
let status = 500;
if (isHttpError(err)) {
status = err.status ?? status;
}
res.status(status).json({
message: err.message,
request,
stack: err.stack,
});
});
// Register an error handler as the last part of the
// express pipeline
app.use(errorHandler);

return app;
}
2 changes: 1 addition & 1 deletion packages/indexer-api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import assert from "assert";
import { assert } from "@repo/error-handling";
import { ExpressApp } from "./express-app";
import { createDataSource, DatabaseConfig } from "@repo/indexer-database";
import * as routers from "./routers";
Expand Down
2 changes: 1 addition & 1 deletion packages/indexer-api/src/services/balances.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import assert from "assert";
import { assert } from "@repo/error-handling";
import Redis from "ioredis";
import * as Indexer from "@repo/indexer";
import {
Expand Down
19 changes: 9 additions & 10 deletions packages/indexer-api/src/services/exceptions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { HttpError } from "../express-app";
import { HttpStatus } from "../model/httpStatus";
import { IndexerHTTPError, StatusCodes } from "@repo/error-handling";

export class DepositNotFoundException extends HttpError {
export class DepositNotFoundException extends IndexerHTTPError {
constructor() {
super("Deposit not found");
this.name = "DepositNotFoundException";
this.status = HttpStatus.NOT_FOUND;
super(
StatusCodes.NOT_FOUND,
DepositNotFoundException.name,
"Deposit not found given the provided constraints",
);
}
}

export class IndexParamOutOfRangeException extends HttpError {
export class IndexParamOutOfRangeException extends IndexerHTTPError {
constructor(message: string) {
super(message);
this.name = "IndexParamOutOfRangeException";
this.status = HttpStatus.BAD_REQUEST;
super(StatusCodes.BAD_REQUEST, IndexParamOutOfRangeException.name, message);
}
}
1 change: 1 addition & 0 deletions packages/indexer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"redis": "^4.7.0",
"superstruct": "^2.0.3-1",
"@repo/webhooks": "workspace:*",
"@repo/error-handling": "workspace:*",
"winston": "^3.13.1"
},
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion packages/indexer/src/parseEnv.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from "assert";
import * as s from "superstruct";
import { DatabaseConfig } from "@repo/indexer-database";
import { getNoTtlBlockDistance } from "./web3/constants";
import { assert } from "@repo/error-handling";

export type Config = {
redisConfig: RedisConfig;
Expand Down
2 changes: 1 addition & 1 deletion packages/indexer/src/redis/rangeQueryStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import assert from "assert";
import { assert } from "@repo/error-handling";
import Redis from "ioredis";

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/indexer/src/services/BundleBuilderService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { caching, clients, typechain, utils } from "@across-protocol/sdk";
import { entities } from "@repo/indexer-database";
import assert from "assert";
import { assert } from "@repo/error-handling";
import Redis from "ioredis";
import winston from "winston";
import { BundleRepository } from "../database/BundleRepository";
Expand Down
1 change: 1 addition & 0 deletions packages/webhooks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@repo/error-handling": "workspace:*",
"@repo/indexer-database": "workspace:*",
"bullmq": "^5.12.12",
"express": "^4.19.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/webhooks/src/eventProcessorManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Logger } from "winston";
import assert from "assert";

import { DataSource, entities } from "@repo/indexer-database";
import { assert } from "@repo/error-handling";

import { WebhookClientRepository } from "./database/webhookClientRepository";
import { JSONValue, IEventProcessor } from "./types";
Expand Down
3 changes: 2 additions & 1 deletion packages/webhooks/src/eventProcessors/depositStatus.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import assert from "assert";
import * as ss from "superstruct";
import { Logger } from "winston";

import { assert } from "@repo/error-handling";
import { DataSource, entities } from "@repo/indexer-database";

import { WebhookRequestRepository } from "../database/webhookRequestRepository";
import { customId } from "../utils";

Expand Down
3 changes: 2 additions & 1 deletion packages/webhooks/src/factory.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import assert from "assert";
import { Logger } from "winston";
import { Redis } from "ioredis";

import { DataSource } from "@repo/indexer-database";
import { assert } from "@repo/error-handling";

import { EventProcessorManager } from "./eventProcessorManager";
import { WebhookNotifier } from "./notifier";
import { DepositStatusProcessor } from "./eventProcessors";
Expand Down
41 changes: 36 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit df6c94d

Please sign in to comment.