-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PSG-3862: improve error handling (#15)
* refactor: removes transaction method parameter wrapper types for the generated types * fix: now passes the error info from the api response through to the PassageError object * Change files * refactor: renames PassageError fields to be more aligned with the client sdks * chore: wraps generated error code exports and sets them as a string literal union for PassageError.statusText
- Loading branch information
Showing
8 changed files
with
164 additions
and
82 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@passageidentity-passage-flex-node-5cfb61ba-2e79-4e6c-a10a-9cac13fe9ab4.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "patch", | ||
"comment": "fix: now passes the error info from the api response through to the PassageError object", | ||
"packageName": "@passageidentity/passage-flex-node", | ||
"email": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,70 @@ | ||
import { ResponseError } from '../generated'; | ||
import { | ||
Model400ErrorCodeEnum, | ||
Model401ErrorCodeEnum, | ||
Model403ErrorCodeEnum, | ||
Model404ErrorCodeEnum, | ||
Model409ErrorCodeEnum, | ||
Model500ErrorCodeEnum, | ||
ResponseError, | ||
} from '../generated'; | ||
|
||
export const ErrorStatusText = { | ||
...Model400ErrorCodeEnum, | ||
...Model401ErrorCodeEnum, | ||
...Model403ErrorCodeEnum, | ||
...Model404ErrorCodeEnum, | ||
...Model409ErrorCodeEnum, | ||
...Model500ErrorCodeEnum, | ||
}; | ||
|
||
type ErrorStatusText = (typeof ErrorStatusText)[keyof typeof ErrorStatusText]; | ||
type APIResponseError = { statusCode: number; statusText: ErrorStatusText; errorMessage: string }; | ||
|
||
/** | ||
* PassageError Class used to handle errors from PassageFlex | ||
*/ | ||
export class PassageError extends Error { | ||
readonly statusCode: number | undefined; | ||
readonly error: string | undefined; | ||
readonly message: string; | ||
public readonly statusCode: number | undefined; | ||
public readonly statusText: ErrorStatusText | undefined; | ||
|
||
/** | ||
* Initialize a new PassageError instance. | ||
* @param {string} message friendly message, | ||
* @param {Error} err error from node-fetch request | ||
* @param {string} message friendly message | ||
* @param {APIResponseError} response error information from PassageFlex API | ||
*/ | ||
constructor(message: string, err?: ResponseError) { | ||
super(); | ||
|
||
if (err) { | ||
this.message = `${message}: ${err.message}`; | ||
this.statusCode = 500; | ||
this.error = err.message; | ||
} else { | ||
this.message = message; | ||
private constructor(message: string, response?: APIResponseError) { | ||
super(message); | ||
|
||
if (!response) { | ||
return; | ||
} | ||
|
||
this.message = `${message}: ${response.errorMessage}`; | ||
this.statusCode = response.statusCode; | ||
this.statusText = response.statusText; | ||
} | ||
|
||
/** | ||
* Initialize a new PassageError instance. | ||
* @param {string} message friendly message | ||
* @return {PassageError} | ||
*/ | ||
public static fromMessage(message: string): PassageError { | ||
return new PassageError(message); | ||
} | ||
|
||
/** | ||
* Initialize a new PassageError instance. | ||
* @param {string} message friendly message | ||
* @param {ResponseError} err error from node-fetch request | ||
* @return {Promise<PassageError>} | ||
*/ | ||
public static async fromResponseError(message: string, err: ResponseError): Promise<PassageError> { | ||
const body: { code: ErrorStatusText; error: string } = await err.response.json(); | ||
return new PassageError(message, { | ||
statusCode: err.response.status, | ||
statusText: body.code, | ||
errorMessage: body.error, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,13 @@ | ||
export { PassageFlex } from './classes/PassageFlex'; | ||
export { PassageError } from './classes/PassageError'; | ||
export { PassageError, ErrorStatusText } from './classes/PassageError'; | ||
export { AppInfo } from './models/AppInfo'; | ||
export { UserInfo } from './models/UserInfo'; | ||
export type { PassageConfig } from './types/PassageConfig'; | ||
export type { RegisterTransactionArgs } from './types/RegisterTransactionArgs'; | ||
export type { AuthenticateTransactionArgs } from './types/AuthenticateTransactionArgs'; | ||
export { UserStatus, WebAuthnDevices, WebAuthnIcons, WebAuthnType } from './generated'; | ||
export { | ||
CreateTransactionAuthenticateRequest, | ||
CreateTransactionRegisterRequest, | ||
UserStatus, | ||
WebAuthnDevices, | ||
WebAuthnIcons, | ||
WebAuthnType, | ||
} from './generated/models'; |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,45 @@ | ||
import { ResponseError } from '../src/generated'; | ||
import { PassageError } from '../src/classes/PassageError'; | ||
import { faker } from '@faker-js/faker'; | ||
|
||
describe('PassageError', () => { | ||
test('message only', async () => { | ||
const msg = 'Could not find valid cookie for authentication. You must catch this error.'; | ||
const err = new PassageError(msg); | ||
describe('fromMessage', () => { | ||
it('should set PassageError.message to message', async () => { | ||
const expected = faker.string.sample(); | ||
const actual = PassageError.fromMessage(expected); | ||
|
||
expect(err.message).toEqual(msg); | ||
expect(err.error).toBeUndefined; | ||
expect(actual.message).toEqual(expected); | ||
expect(actual.stack).toBeDefined(); | ||
expect(actual.statusText).toBeUndefined(); | ||
expect(actual.statusCode).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
test('with Response Error', async () => { | ||
const responseError = { | ||
message: 'some error message', | ||
name: 'ResponseError', | ||
} as ResponseError; | ||
describe('fromResponseError', () => { | ||
it('should set PassageError.code and PassageError.status from ResponseError', async () => { | ||
const expectedMessage = 'friendly message'; | ||
const expectedResponseCode = faker.string.sample(); | ||
const expectedResponseError = 'error body message'; | ||
|
||
const msg = 'Could not find valid cookie for authentication. You must catch this error'; | ||
const err = new PassageError(msg, responseError); | ||
const responseError = { | ||
message: faker.string.sample(), | ||
response: { | ||
status: faker.internet.httpStatusCode(), | ||
json: async () => { | ||
return { | ||
code: expectedResponseCode, | ||
error: expectedResponseError, | ||
}; | ||
}, | ||
} as Response, | ||
} as ResponseError; | ||
|
||
expect(err.message).toEqual(`${msg}: ${responseError.message}`); | ||
expect(err.statusCode).toBe(500); | ||
const actual = await PassageError.fromResponseError(expectedMessage, responseError); | ||
|
||
expect(err.error).toBe('some error message'); | ||
expect(actual.message).toEqual(`${expectedMessage}: ${expectedResponseError}`); | ||
expect(actual.statusText).toEqual(expectedResponseCode); | ||
expect(actual.statusCode).toEqual(responseError.response.status); | ||
expect(actual.stack).toBeDefined(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.