Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(errors): add error class and functions #113

Merged
merged 4 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/error-handling/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extension": [
"ts"
],
"spec": "**/*.test.ts",
"require": [
"ts-node/register"
],
"recursive": true
}
3 changes: 3 additions & 0 deletions packages/error-handling/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# HTTP Error Handling Module

This module provides a comprehensive system for managing errors within the indexer package. It is meant to be a centralized constants repository for Indexer related errors
6 changes: 6 additions & 0 deletions packages/error-handling/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// .eslintrc.js in the new package
module.exports = {
root:true,
extends: ['@repo/eslint-config/library.js'],
};

46 changes: 46 additions & 0 deletions packages/error-handling/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@repo/error-handling",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"build": "tsc -b",
"build:check": "tsc --noEmit",
"watch": "tsc -b --watch",
"fix": "pnpm format && pnpm lint",
"format": "prettier --write src",
"format:check": "prettier src --check",
"lint": "eslint --fix",
"lint:check": "eslint",
"check": "pnpm format:check && pnpm lint:check && pnpm build:check",
"test": "mocha",
"coverage": "nyc mocha",
"test:watch": "mocha --watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"http-status-codes": "^2.3.0"
},
"exports": {
".": "./dist/index.js"
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/chai": "^4.3.17",
"@types/mocha": "^10.0.7",
"@types/node": "^16.11.10",
"chai": "^4.5.0",
"eslint": "^8.57.0",
"mocha": "^10.7.0",
"nyc": "^17.0.0",
"prettier": "^3.3.3",
"source-map-support": "^0.5.21",
"ts-node": "^10.9.2",
"typescript": "^5.5.4",
"winston": "^3.13.1"
}
}
7 changes: 7 additions & 0 deletions packages/error-handling/src/errors/AssertError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IndexerError } from "./IndexerError";

export class AssertError extends IndexerError {
constructor(message: string) {
super(AssertError.name, message);
Copy link
Contributor

@amateima amateima Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this mean that the HTTP response body will be

{
  error: "AssertError",
  message: message
}

or not necessary
?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct - this would be the toJSON() if we tried to stringify it

}
}
25 changes: 25 additions & 0 deletions packages/error-handling/src/errors/IndexerError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Generic Error class that should be used in the Indexer to
* provide common error patterns to log
*/
export abstract class IndexerError extends Error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems more generalized than indexer, since we could use this package in other applications, should we reflect that in the naming?

Copy link
Contributor Author

@james-a-morris james-a-morris Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite possibly - I will leave it to @alexandrumatei36 as this may be out of scope to consider non-indexer applications

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@james-a-morris we can do a quick renaming. I agree with David that we should design these packages as they would be used in the future in other repos/apps too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to avoid naming it just Error when dropping the Indexer. How about FormattedError?

constructor(
private readonly errorName: string,
private readonly errorMessage: string,
private readonly errorData?: Record<string, string>,
) {
super(errorMessage);
}

/**
* A function used by `JSON.stringify` to specify which data will be serialized
* @returns A formatted JSON
*/
public toJSON(): Record<string, unknown> {
return {
error: this.errorName,
message: this.errorMessage,
data: this.errorData,
};
}
}
29 changes: 29 additions & 0 deletions packages/error-handling/src/errors/IndexerHTTPError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { StatusCodes } from "http-status-codes";
james-a-morris marked this conversation as resolved.
Show resolved Hide resolved
import { IndexerError } from "./IndexerError";

/**
* Used to distinguish a similar design pattern as {@link IndexerError} but with
* additional HTTP context
* @see {@link IndexerError}
*/
export abstract class IndexerHTTPError extends IndexerError {
constructor(
private readonly httpStatusCode: StatusCodes,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we make this optional and set a default code? 400 or 500

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be intentional otherwise we'd want to use IndexerError

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(),
};
}
}
3 changes: 3 additions & 0 deletions packages/error-handling/src/errors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./IndexerError";
export * from "./IndexerHTTPError";
export * from "./AssertError";
2 changes: 2 additions & 0 deletions packages/error-handling/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./errors";
export * from "./utils";
31 changes: 31 additions & 0 deletions packages/error-handling/src/main.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, it } from "mocha";
import { expect } from "chai";
import assert from "assert";
import { AssertError } from "./errors";
import { isIndexerError, assert as customAssert } from "./utils";

describe("Error handling Tests", function () {
it("should run the toJSON in Stringify", () => {
const e = new AssertError("test");
const jsonStringifyDirectly = JSON.stringify(e.toJSON());
const jsonIndirectly = JSON.stringify(e);
expect(jsonIndirectly).to.eq(jsonStringifyDirectly);
});

describe("typeguards", function () {
it("should validate IndexerError", () => {
const e = new AssertError("test");
expect(isIndexerError(e)).to.be.true;
});
});

describe("utils", () => {
it("should run assert as expected", () => {
expect(() => assert(false, "")).to.throw;
expect(() => customAssert(false, "")).to.throw;

expect(() => assert(true, "")).to.not.throw;
expect(() => customAssert(true, "")).to.not.throw;
});
});
});
17 changes: 17 additions & 0 deletions packages/error-handling/src/utils/assert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as assertModule from "assert";
import { AssertError } from "../errors/AssertError";

/**
* A stand-in assert function that returns a formatted error payload
* @param value An arbitrary predicate that will be asserted for truthiness
* @param message An error message that will be passed if the assert fails
* @returns An assertion of `value`.
* @throws {@link AssertError} if assert's validity fails
*/
export function assert(value: unknown, message: string): asserts value {
try {
return assertModule.ok(value, message);
} catch (e: unknown) {
throw new AssertError(message);
}
}
2 changes: 2 additions & 0 deletions packages/error-handling/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./assert";
export * from "./typeguards";
19 changes: 19 additions & 0 deletions packages/error-handling/src/utils/typeguards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IndexerError, IndexerHTTPError } from "../errors";

/**
* Typeguard to confirm that an object is an IndexerError
* @param error The error object to validate
* @returns Whether this object is an instance of `IndexerError` (or a descendent)
*/
export function isIndexerError(error: unknown): error is IndexerError {
return error instanceof IndexerError;
}

/**
* Typeguard to confirm that an object is an IndexerHTTPError
* @param error The error object to validate
* @returns Whether this object is an instance of `IndexerHTTPError` (or a descendent)
*/
export function isIndexerHTTPError(error: unknown): error is IndexerHTTPError {
return error instanceof IndexerHTTPError;
}
6 changes: 6 additions & 0 deletions packages/error-handling/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends":"@repo/typescript-config/base.json",
"compilerOptions": {
"outDir": "./dist" /* Specify an output folder for all emitted files. */
}
}
Loading