-
Notifications
You must be signed in to change notification settings - Fork 0
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
} |
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 |
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'], | ||
}; | ||
|
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" | ||
} | ||
} |
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); | ||
} | ||
} |
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer to avoid naming it just |
||
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, | ||
}; | ||
} | ||
} |
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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be intentional otherwise we'd want to use |
||
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(), | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from "./IndexerError"; | ||
export * from "./IndexerHTTPError"; | ||
export * from "./AssertError"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./errors"; | ||
export * from "./utils"; |
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; | ||
}); | ||
}); | ||
}); |
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); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./assert"; | ||
export * from "./typeguards"; |
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; | ||
} |
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. */ | ||
} | ||
} |
There was a problem hiding this comment.
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
or not necessary
?
There was a problem hiding this comment.
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