From b2c582009a637e420f3c5c41416a2618aa19b09f Mon Sep 17 00:00:00 2001 From: jdabbech-ledger Date: Fri, 3 May 2024 15:33:54 +0200 Subject: [PATCH] :recycle: (core): Use SdkError in command api module Closes DSDK-292 --- packages/core/src/api/command/Errors.ts | 46 +++++++++++++++++++ .../os/GetAppAndVersionCommand.test.ts | 8 +++- .../api/command/os/GetAppAndVersionCommand.ts | 12 +++-- .../os/GetBatteryStatusCommand.test.ts | 8 +++- .../api/command/os/GetBatteryStatusCommand.ts | 23 ++++++---- .../command/os/GetOsVersionCommand.test.ts | 3 +- .../src/api/command/os/GetOsVersionCommand.ts | 3 +- 7 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 packages/core/src/api/command/Errors.ts diff --git a/packages/core/src/api/command/Errors.ts b/packages/core/src/api/command/Errors.ts new file mode 100644 index 000000000..f57af3b4d --- /dev/null +++ b/packages/core/src/api/command/Errors.ts @@ -0,0 +1,46 @@ +import { SdkError } from "@api/Error"; + +export class InvalidStatusWordError implements SdkError { + readonly _tag = "InvalidStatusWordError"; + originalError?: Error; + + constructor(message?: string) { + this.originalError = new Error(message ?? "Invalid status word."); + } +} + +export class InvalidBatteryStatusTypeError implements SdkError { + readonly _tag = "InvalidBatteryStatusTypeError"; + originalError: Error; + + constructor(message?: string) { + this.originalError = new Error(message ?? "Invalid battery status type."); + } +} + +export class InvalidBatteryDataError implements SdkError { + readonly _tag = "InvalidBatteryDataError"; + originalError: Error; + + constructor(message?: string) { + this.originalError = new Error(message ?? "Invalid battery data."); + } +} + +export class InvalidBatteryFlagsError implements SdkError { + readonly _tag = "InvalidBatteryFlagsError"; + originalError: Error; + + constructor(message?: string) { + this.originalError = new Error(message ?? "Invalid battery flags."); + } +} + +export class InvalidResponseFormatError implements SdkError { + readonly _tag = "InvalidResponseFormatError"; + originalError: Error; + + constructor(message?: string) { + this.originalError = new Error(message ?? "Invalid response format."); + } +} diff --git a/packages/core/src/api/command/os/GetAppAndVersionCommand.test.ts b/packages/core/src/api/command/os/GetAppAndVersionCommand.test.ts index 826d182ca..5fc680830 100644 --- a/packages/core/src/api/command/os/GetAppAndVersionCommand.test.ts +++ b/packages/core/src/api/command/os/GetAppAndVersionCommand.test.ts @@ -1,4 +1,8 @@ import { Command } from "@api/command/Command"; +import { + InvalidResponseFormatError, + InvalidStatusWordError, +} from "@api/command/Errors"; import { ApduResponse } from "@internal/device-session/model/ApduResponse"; import { @@ -71,7 +75,7 @@ describe("GetAppAndVersionCommand", () => { }); expect(() => command.parseResponse(FAILED_RESPONSE)).toThrow( - "Unexpected status word: 6700", + InvalidStatusWordError, ); }); it("should throw an error if the response returned unsupported format", () => { @@ -81,7 +85,7 @@ describe("GetAppAndVersionCommand", () => { }); expect(() => command.parseResponse(ERROR_RESPONSE)).toThrow( - "getAppAndVersion: format not supported", + InvalidResponseFormatError, ); }); }); diff --git a/packages/core/src/api/command/os/GetAppAndVersionCommand.ts b/packages/core/src/api/command/os/GetAppAndVersionCommand.ts index 312e23f16..596fb5ea8 100644 --- a/packages/core/src/api/command/os/GetAppAndVersionCommand.ts +++ b/packages/core/src/api/command/os/GetAppAndVersionCommand.ts @@ -2,6 +2,10 @@ import { Apdu } from "@api/apdu/model/Apdu"; import { ApduBuilder, ApduBuilderArgs } from "@api/apdu/utils/ApduBuilder"; import { ApduParser } from "@api/apdu/utils/ApduParser"; import { Command } from "@api/command/Command"; +import { + InvalidResponseFormatError, + InvalidStatusWordError, +} from "@api/command/Errors"; import { CommandUtils } from "@api/command/utils/CommandUtils"; import { ApduResponse } from "@internal/device-session/model/ApduResponse"; @@ -26,9 +30,8 @@ export class GetAppAndVersionCommand parseResponse(apduResponse: ApduResponse): GetAppAndVersionResponse { const parser = new ApduParser(apduResponse); - // [SHOULD] Implement new error treatment logic if (!CommandUtils.isSuccessResponse(apduResponse)) { - throw new Error( + throw new InvalidStatusWordError( `Unexpected status word: ${parser.encodeToHexaString( apduResponse.statusCode, )}`, @@ -36,8 +39,9 @@ export class GetAppAndVersionCommand } if (parser.extract8BitUint() !== 1) { - // TODO: Make dedicated error object - throw new Error("getAppAndVersion: format not supported"); + throw new InvalidResponseFormatError( + "getAppAndVersion: format not supported", + ); } const name = parser.encodeToString(parser.extractFieldLVEncoded()); diff --git a/packages/core/src/api/command/os/GetBatteryStatusCommand.test.ts b/packages/core/src/api/command/os/GetBatteryStatusCommand.test.ts index 843b35d01..060997a80 100644 --- a/packages/core/src/api/command/os/GetBatteryStatusCommand.test.ts +++ b/packages/core/src/api/command/os/GetBatteryStatusCommand.test.ts @@ -1,4 +1,8 @@ import { Command } from "@api/command/Command"; +import { + InvalidBatteryStatusTypeError, + InvalidStatusWordError, +} from "@api/command/Errors"; import { ApduResponse } from "@internal/device-session/model/ApduResponse"; import { @@ -106,7 +110,7 @@ describe("GetBatteryStatus", () => { data: PERCENTAGE_RESPONSE_HEX.slice(0, -2), }); expect(() => command.parseResponse(PERCENTAGE_RESPONSE)).toThrow( - "Call getApdu to initialise battery status type.", + InvalidBatteryStatusTypeError, ); }); it("should throw an error if the response returned unsupported format", () => { @@ -116,7 +120,7 @@ describe("GetBatteryStatus", () => { }); command.getApdu(BatteryStatusType.BATTERY_PERCENTAGE); expect(() => command.parseResponse(FAILED_RESPONSE)).toThrow( - "Unexpected status word: 6700", + InvalidStatusWordError, ); }); }); diff --git a/packages/core/src/api/command/os/GetBatteryStatusCommand.ts b/packages/core/src/api/command/os/GetBatteryStatusCommand.ts index 38d1ad396..1aaac4345 100644 --- a/packages/core/src/api/command/os/GetBatteryStatusCommand.ts +++ b/packages/core/src/api/command/os/GetBatteryStatusCommand.ts @@ -2,6 +2,12 @@ import { Apdu } from "@api/apdu/model/Apdu"; import { ApduBuilder, ApduBuilderArgs } from "@api/apdu/utils/ApduBuilder"; import { ApduParser } from "@api/apdu/utils/ApduParser"; import { Command } from "@api/command/Command"; +import { + InvalidBatteryDataError, + InvalidBatteryFlagsError, + InvalidBatteryStatusTypeError, + InvalidStatusWordError, +} from "@api/command/Errors"; import { CommandUtils } from "@api/command/utils/CommandUtils"; import { ApduResponse } from "@internal/device-session/model/ApduResponse"; @@ -56,13 +62,14 @@ export class GetBatteryStatusCommand parseResponse(apduResponse: ApduResponse): GetBatteryStatusResponse { if (this._statusType === undefined) { - throw new Error("Call getApdu to initialise battery status type."); + throw new InvalidBatteryStatusTypeError( + "Call getApdu to initialise battery status type.", + ); } const parser = new ApduParser(apduResponse); - // [SHOULD] Implement new error treatment logic if (!CommandUtils.isSuccessResponse(apduResponse)) { - throw new Error( + throw new InvalidStatusWordError( `Unexpected status word: ${parser.encodeToHexaString( apduResponse.statusCode, )}`, @@ -73,14 +80,14 @@ export class GetBatteryStatusCommand case BatteryStatusType.BATTERY_PERCENTAGE: { const percentage = parser.extract8BitUint(); if (!percentage) { - throw new Error("Cannot parse APDU response"); + throw new InvalidBatteryDataError("Cannot parse APDU response"); } return percentage > 100 ? -1 : percentage; } case BatteryStatusType.BATTERY_VOLTAGE: { const data = parser.extract16BitUInt(); if (!data) { - throw new Error("Cannot parse APDU response"); + throw new InvalidBatteryDataError("Cannot parse APDU response"); } return data; } @@ -88,14 +95,14 @@ export class GetBatteryStatusCommand case BatteryStatusType.BATTERY_CURRENT: { const data = parser.extract8BitUint(); if (!data) { - throw new Error("Cannot parse APDU response"); + throw new InvalidBatteryDataError("Cannot parse APDU response"); } return (data << 24) >> 24; } case BatteryStatusType.BATTERY_FLAGS: { const flags = parser.extract32BitUInt(); if (!flags) { - throw new Error("Cannot parse APDU response"); + throw new InvalidBatteryFlagsError("Cannot parse APDU response"); } const chargingUSB = !!(flags & FlagMasks.USB_POWERED); const chargingQi = !chargingUSB && !!(flags & FlagMasks.CHARGING); @@ -116,6 +123,6 @@ export class GetBatteryStatusCommand } private _exhaustiveMatchingGuard(_: never): never { - throw new Error("One or some case(s) not covered"); + throw new InvalidBatteryStatusTypeError("One or some case(s) not covered"); } } diff --git a/packages/core/src/api/command/os/GetOsVersionCommand.test.ts b/packages/core/src/api/command/os/GetOsVersionCommand.test.ts index 8cc2e0713..fbc8358cc 100644 --- a/packages/core/src/api/command/os/GetOsVersionCommand.test.ts +++ b/packages/core/src/api/command/os/GetOsVersionCommand.test.ts @@ -1,4 +1,5 @@ import { Command } from "@api/command/Command"; +import { InvalidStatusWordError } from "@api/command/Errors"; import { DeviceModelId } from "@api/device/DeviceModel"; import { ApduResponse } from "@internal/device-session/model/ApduResponse"; @@ -129,7 +130,7 @@ describe("GetOsVersionCommand", () => { expect(() => command.parseResponse(response, DeviceModelId.NANO_X), - ).toThrow("Unexpected status word: 6e80"); + ).toThrow(InvalidStatusWordError); }); }); }); diff --git a/packages/core/src/api/command/os/GetOsVersionCommand.ts b/packages/core/src/api/command/os/GetOsVersionCommand.ts index 2fb15c6d9..4dc89c1e5 100644 --- a/packages/core/src/api/command/os/GetOsVersionCommand.ts +++ b/packages/core/src/api/command/os/GetOsVersionCommand.ts @@ -2,6 +2,7 @@ import { Apdu } from "@api/apdu/model/Apdu"; import { ApduBuilder } from "@api/apdu/utils/ApduBuilder"; import { ApduParser } from "@api/apdu/utils/ApduParser"; import { Command } from "@api/command/Command"; +import { InvalidStatusWordError } from "@api/command/Errors"; import { CommandUtils } from "@api/command/utils/CommandUtils"; import { DeviceModelId } from "@api/device/DeviceModel"; import { ApduResponse } from "@internal/device-session/model/ApduResponse"; @@ -30,7 +31,7 @@ export class GetOsVersionCommand implements Command { const parser = new ApduParser(responseApdu); if (!CommandUtils.isSuccessResponse(responseApdu)) { // [ASK] How de we handle unsuccessful responses? - throw new Error( + throw new InvalidStatusWordError( `Unexpected status word: ${parser.encodeToHexaString(responseApdu.statusCode)}`, ); }