Skip to content

Commit

Permalink
✨ (signer-utils): Create CommandErrorHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
jdabbech-ledger committed Jan 13, 2025
1 parent df4ef37 commit a00e30c
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/breezy-plums-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/signer-utils": patch
---

Create CommandErrorHelper to handle command errors
1 change: 1 addition & 0 deletions packages/signer/signer-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./utils/CommandErrorHelper";
export * from "./utils/DerivationPathUtils";
62 changes: 62 additions & 0 deletions packages/signer/signer-utils/src/utils/CommandErrorHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
ApduResponse,
CommandResultFactory,
GlobalCommandErrorHandler,
} from "@ledgerhq/device-management-kit";

import { CommandErrorHelper } from "./CommandErrorHelper";

describe("CommandErrorHelper", () => {
it("should return the correct error args and call factory when error is found", () => {
// given
const errors = {
"4224": { message: "An error occurred" },
};
const errorFactory = jest.fn();
const helper = new CommandErrorHelper(errors, errorFactory);
const apduResponse = new ApduResponse({
statusCode: Uint8Array.from([0x42, 0x24]),
data: Uint8Array.from([]),
});
// when
helper.getError(apduResponse);
// then
expect(errorFactory).toHaveBeenNthCalledWith(1, {
...errors["4224"],
errorCode: "4224",
});
});
it("should return a global error when no error is found", () => {
// given
const errors = {};
const errorFactory = jest.fn();
const helper = new CommandErrorHelper(errors, errorFactory);
const apduResponse = new ApduResponse({
statusCode: Uint8Array.from([0x55, 0x15]),
data: Uint8Array.from([]),
});
// when
const error = helper.getError(apduResponse);
// then
expect(errorFactory).toHaveBeenCalledTimes(0);
expect(error).toStrictEqual(
CommandResultFactory({
error: GlobalCommandErrorHandler.handle(apduResponse),
}),
);
});
it("should return undefined if success apdu response", () => {
// given
const errors = {};
const errorFactory = jest.fn();
const helper = new CommandErrorHelper(errors, errorFactory);
const apduResponse = new ApduResponse({
statusCode: Uint8Array.from([0x90, 0x00]),
data: Uint8Array.from([]),
});
// when
const error = helper.getError(apduResponse);
// then
expect(error).toBeUndefined();
});
});
44 changes: 44 additions & 0 deletions packages/signer/signer-utils/src/utils/CommandErrorHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
ApduParser,
type ApduResponse,
type CommandErrorArgs,
type CommandErrors,
type CommandResult,
CommandResultFactory,
CommandUtils,
type DeviceExchangeError,
GlobalCommandErrorHandler,
isCommandErrorCode,
} from "@ledgerhq/device-management-kit";

export class CommandErrorHelper<Response, ErrorCodes extends string> {
constructor(
private readonly _errors: CommandErrors<ErrorCodes>,
private readonly _errorFactory: (
args: CommandErrorArgs<ErrorCodes>,
) => DeviceExchangeError<ErrorCodes>,
private readonly _isSuccessResponse = CommandUtils.isSuccessResponse,
) {}

getError(
apduResponse: ApduResponse,
): CommandResult<Response, ErrorCodes> | undefined {
const apduParser = new ApduParser(apduResponse);
const errorCode = apduParser.encodeToHexaString(apduResponse.statusCode);

if (isCommandErrorCode(errorCode, this._errors)) {
return CommandResultFactory<Response, ErrorCodes>({
error: this._errorFactory({
...this._errors[errorCode],
errorCode,
}),
});
}
if (!this._isSuccessResponse(apduResponse)) {
return CommandResultFactory({
error: GlobalCommandErrorHandler.handle(apduResponse),
});
}
return undefined;
}
}

0 comments on commit a00e30c

Please sign in to comment.