Skip to content

Commit

Permalink
📝 (core) [DSDK-293]: Add TSDoc for API things (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
ofreyssinet-ledger authored May 15, 2024
2 parents 9a8fe06 + 44a4a68 commit a30ff2e
Show file tree
Hide file tree
Showing 20 changed files with 282 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/spicy-dogs-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/device-sdk-core": patch
---

Add TSDoc comments for things exposed through API
58 changes: 58 additions & 0 deletions packages/core/src/api/DeviceSdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ import {
} from "@internal/usb/use-case/GetConnectedDeviceUseCase";
import { makeContainer, MakeContainerProps } from "@root/src/di";

/**
* The main class to interact with the SDK.
*
* NB: do not instantiate this class directly, instead, use `DeviceSdkBuilder`.
*/
export class DeviceSdk {
container: Container;
/** @internal */
Expand All @@ -48,54 +53,107 @@ export class DeviceSdk {
this.container = makeContainer({ stub, loggers });
}

/**
* Returns a promise resolving to the version of the SDK.
*/
getVersion(): Promise<string> {
return this.container
.get<GetSdkVersionUseCase>(configTypes.GetSdkVersionUseCase)
.getSdkVersion();
}

/**
* Starts discovering devices connected via USB HID (BLE not implemented yet).
*
* For the WeHID implementation, this use-case needs to be called as a result
* of an user interaction (button "click" event for ex).
*
* @returns {Observable<DiscoveredDevice>} An observable of discovered devices.
*/
startDiscovering(): Observable<DiscoveredDevice> {
return this.container
.get<StartDiscoveringUseCase>(discoveryTypes.StartDiscoveringUseCase)
.execute();
}

/**
* Stops discovering devices connected via USB HID (and later BLE).
*/
stopDiscovering() {
return this.container
.get<StopDiscoveringUseCase>(discoveryTypes.StopDiscoveringUseCase)
.execute();
}

/**
* Connects to a device previously discovered with `DeviceSdk.startDiscovering`.
* Creates a new device session which:
* - Represents the connection to the device.
* - Is terminated upon disconnection of the device.
* - Exposes the device state through an observable (see `DeviceSdk.getDeviceSessionState`)
* - Should be used for all subsequent communication with the device.
*
* @param {ConnectUseCaseArgs} args - The device ID (obtained in discovery) to connect to.
* @returns The session ID to use for further communication with the device.
*/
connect(args: ConnectUseCaseArgs): Promise<DeviceSessionId> {
return this.container
.get<ConnectUseCase>(discoveryTypes.ConnectUseCase)
.execute(args);
}

/**
* Disconnects to a discovered device via USB HID (and later BLE).
*
* @param {DisconnectUseCaseArgs} args - The session ID to disconnect.
*/
disconnect(args: DisconnectUseCaseArgs): Promise<void> {
return this.container
.get<DisconnectUseCase>(discoveryTypes.DisconnectUseCase)
.execute(args);
}

/**
* Sends an APDU command to a device through a device session.
*
* @param {SendApduUseCaseArgs} args - The device session ID and APDU command to send.
*/
sendApdu(args: SendApduUseCaseArgs): Promise<ApduResponse> {
return this.container
.get<SendApduUseCase>(sendTypes.SendApduUseCase)
.execute(args);
}

/**
* Sends a command to a device through a device session.
*
* @param {SendCommandUseCaseArgs<T, U>} - The device session ID, command and command parameters to send.
* @returns A promise resolving with the response from the command.
*/
sendCommand<T, U>(args: SendCommandUseCaseArgs<T, U>): Promise<T> {
return this.container
.get<SendCommandUseCase>(commandTypes.SendCommandUseCase)
.execute(args);
}

/**
* Gets the connected from its device session ID.
*
* @param {GetConnectedDeviceUseCaseArgs} args - The device session ID.
* @returns {ConnectedDevice} The connected device.
*/
getConnectedDevice(args: GetConnectedDeviceUseCaseArgs): ConnectedDevice {
return this.container
.get<GetConnectedDeviceUseCase>(usbDiTypes.GetConnectedDeviceUseCase)
.execute(args);
}

/**
* Gets the device state of a session.
*
* @param {{DeviceSessionId}} args - The device session ID.
* @returns {Observable<DeviceSessionState>} An observable of the session device state.
*/
getDeviceSessionState(args: {
sessionId: DeviceSessionId;
}): Observable<DeviceSessionState> {
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/api/DeviceSdkBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import { LoggerSubscriberService } from "./logger-subscriber/service/LoggerSubscriberService";
import { DeviceSdk } from "./DeviceSdk";

/**
* Builder for the `DeviceSdk` class.
*
* @example
* ```
* const sdk = new LedgerDeviceSdkBuilder()
* .setStub(false)
* .addLogger(myLogger)
* .build();
* ```
*/
export class LedgerDeviceSdkBuilder {
stub = false;
loggers: LoggerSubscriberService[] = [];
Expand All @@ -14,6 +25,9 @@ export class LedgerDeviceSdkBuilder {
return this;
}

/**
* Add a logger to the SDK that will receive its logs
*/
addLogger(logger: LoggerSubscriberService): LedgerDeviceSdkBuilder {
this.loggers.push(logger);
return this;
Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/api/apdu/model/Apdu.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
/**
* Represents an APDU command that can be sent to a device.
* DO NOT USE THIS CLASS DIRECTLY, use ApduBuilder instead.
*/
export class Apdu {
/**
* Instruction class (1 byte)
*/
readonly cla: number;

/**
* Instruction code (1 byte)
*/
readonly ins: number;

/**
* Instruction parameter 1 (2 bytes)
*/
readonly p1: number;

/**
* Instruction parameter 2 (2 bytes)
*/
readonly p2: number;

/**
* Bytes of data
*/
data?: Uint8Array;

constructor(
Expand All @@ -19,6 +42,10 @@ export class Apdu {
this.data = data;
}

/**
* Get the raw binary data of the APDU command
* @returns {Uint8Array} - The raw APDU command
*/
getRawApdu(): Uint8Array {
const header = Uint8Array.from([
this.cla,
Expand Down
16 changes: 14 additions & 2 deletions packages/core/src/api/apdu/utils/ApduBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,21 @@ export type ApduBuilderArgs = {
};

/**
* ApduBuilder is a utility class to help build APDU commands
* ApduBuilder is a utility class to help build APDU commands.
* It allows to easily add data to the data field of the APDU command
* and to encode data in different formats
* and to encode this data in different formats.
*
* @example
* ```
* const apduBuilder = new ApduBuilder({ ins: 0x01, cla: 0x02, p1: 0x03, p2: 0x04 })
* .add8BitUintToData(0x05)
* .add16BitUintToData(0x0607)
* .addHexaStringToData("0x0809")
* .addAsciiStringToData("hello")
*
* const apdu = apduBuilder.build();
* const builderErrors = apduBuilder.getErrors();
* ```
*/
export class ApduBuilder {
private _ins: number;
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/api/apdu/utils/ApduParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ export type TaggedField = {
value: Uint8Array;
};

/**
* ApduParser is a utility class to help parse APDU responses.
*
* It provides methods to extract fields of different types from the response.
*
* @example
* ```
* const parser = new ApduParser(apduResponse);
* const targetId = parser.encodeToHexaString(parser.extractFieldByLength(4));
* const seVersion = parser.encodeToString(parser.extractFieldLVEncoded());
* ```
*/
export class ApduParser {
private _index: number;
private _response: Uint8Array;
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/api/apdu/utils/AppBuilderError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ export class HexaStringEncodeError implements SdkAppBuilderError {
}
}

/**
* Type for all possible errors that can be thrown by the AppBuilder.
*/
export type AppBuilderError =
| InvalidValueError
| ValueOverflowError
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/api/command/Command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@ import { Apdu } from "@api/apdu/model/Apdu";
import { DeviceModelId } from "@api/device/DeviceModel";
import { ApduResponse } from "@api/device-session/ApduResponse";

/**
* Represents a command that can be sent to a device.
*/
export interface Command<T, U = void> {
/**
* Returns the APDU to be sent to the device.
*/
getApdu(args?: U): Apdu;
/**
* Parses the APDU response from the device.
*/
parseResponse(
apduResponse: ApduResponse,
deviceModelId: DeviceModelId | void,
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/api/command/os/GetAppAndVersionCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ import { CommandUtils } from "@api/command/utils/CommandUtils";
import { ApduResponse } from "@api/device-session/ApduResponse";

export type GetAppAndVersionResponse = {
/**
* The name of the application currently running on the device.
*/
name: string;
/**
* The version of the application currently running on the device.
*/
version: string;
flags?: number | Uint8Array;
};

/**
* Command to get information about the application currently running on the
* device.
*/
export class GetAppAndVersionCommand
implements Command<GetAppAndVersionResponse>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("GetBatteryStatus", () => {
});

describe("getApdu", () => {
it("should return the GetBatteryStatus APUD", () => {
it("should return the GetBatteryStatus APDU", () => {
expect(
command.getApdu(BatteryStatusType.BATTERY_PERCENTAGE).getRawApdu(),
).toStrictEqual(GET_BATTERY_STATUS_APDU_PERCENTAGE);
Expand Down
30 changes: 30 additions & 0 deletions packages/core/src/api/command/os/GetBatteryStatusCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,29 @@ import {
import { CommandUtils } from "@api/command/utils/CommandUtils";
import { ApduResponse } from "@api/device-session/ApduResponse";

/**
* The type of battery information to retrieve.
*/
export enum BatteryStatusType {
/**
* The command response will be the battery percentage.
*/
BATTERY_PERCENTAGE = 0x00,
/**
* The command response will be the battery voltage in mV.
*/
BATTERY_VOLTAGE = 0x01,
/**
* The command response will be the battery temperature in degree celsius
*/
BATTERY_TEMPERATURE = 0x02,
/**
* The command response will be the battery current in mA.
*/
BATTERY_CURRENT = 0x03,
/**
* The command response will be the battery status (cf. `BatteryStatusFlags`)
*/
BATTERY_FLAGS = 0x04,
}

Expand All @@ -42,8 +60,20 @@ type BatteryStatusFlags = {
issueBattery: boolean;
};

/**
* The response type depends on the `statusType` parameter sent with the command,
* cf. `BatteryStatusType`.
*/
export type GetBatteryStatusResponse = number | BatteryStatusFlags;

/**
* Command to get the battery status of the device.
* The parameter statusType defines the type of information to retrieve, cf.
* `BatteryStatusType`.
*
* WARNING: this command should not be sent within a logic of polling as it is
* going to decrease the overall performance of the communication with the device.
*/
export class GetBatteryStatusCommand
implements Command<GetBatteryStatusResponse, BatteryStatusType>
{
Expand Down
Loading

0 comments on commit a30ff2e

Please sign in to comment.