Skip to content

Commit

Permalink
♻️ (dmk) [DSDK-571]: Refactor ListDeviceSession into ListConnectedDev…
Browse files Browse the repository at this point in the history
…ice (#477)
  • Loading branch information
jdabbech-ledger authored Nov 8, 2024
2 parents b715759 + 4338468 commit 8cc0194
Show file tree
Hide file tree
Showing 20 changed files with 268 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/happy-shirts-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/device-management-kit": patch
---

Replace ListDeviceSessionsUseCase with ListConnectedDevicesUseCase
21 changes: 13 additions & 8 deletions apps/sample/src/providers/DeviceSessionsProvider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ export const DeviceSessionsProvider: React.FC<React.PropsWithChildren> = ({
}

useEffect(() => {
dmk.listDeviceSessions().map((session) => {
dispatch({
type: "add_session",
payload: {
sessionId: session.id,
connectedDevice: dmk.getConnectedDevice({ sessionId: session.id }),
},
const subscription = dmk
.listenToConnectedDevice()
.subscribe((connectedDevice) => {
dispatch({
type: "add_session",
payload: {
sessionId: connectedDevice.sessionId,
connectedDevice,
},
});
});
});
return () => {
subscription.unsubscribe();
};
}, [dmk]);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ describe("DeviceManagementKit", () => {
expect(dmk.sendCommand).toBeDefined();
});

it("should have listDeviceSessions method", () => {
expect(dmk.listDeviceSessions).toBeDefined();
it("should have listConnectedDevices method", () => {
expect(dmk.listConnectedDevices).toBeDefined();
});

it("should have listenToConnectedDevice method", () => {
expect(dmk.listenToConnectedDevice).toBeDefined();
});
});

Expand Down Expand Up @@ -107,7 +111,8 @@ describe("DeviceManagementKit", () => {
[discoveryTypes.GetConnectedDeviceUseCase],
[discoveryTypes.DisconnectUseCase],
[deviceSessionTypes.GetDeviceSessionStateUseCase],
[deviceSessionTypes.ListDeviceSessionsUseCase],
[discoveryTypes.ListConnectedDevicesUseCase],
[discoveryTypes.ListenToConnectedDeviceUseCase],
])(
"should have %p use case",
(diSymbol: interfaces.ServiceIdentifier<StubUseCase>) => {
Expand Down
31 changes: 24 additions & 7 deletions packages/device-management-kit/src/api/DeviceManagementKit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import {
import { configTypes } from "@internal/config/di/configTypes";
import { type GetDmkVersionUseCase } from "@internal/config/use-case/GetDmkVersionUseCase";
import { deviceSessionTypes } from "@internal/device-session/di/deviceSessionTypes";
import { type DeviceSession } from "@internal/device-session/model/DeviceSession";
import { type CloseSessionsUseCase } from "@internal/device-session/use-case/CloseSessionsUseCase";
import { type GetDeviceSessionStateUseCase } from "@internal/device-session/use-case/GetDeviceSessionStateUseCase";
import { type ListDeviceSessionsUseCase } from "@internal/device-session/use-case/ListDeviceSessionsUseCase";
import { discoveryTypes } from "@internal/discovery/di/discoveryTypes";
import { type ConnectUseCase } from "@internal/discovery/use-case/ConnectUseCase";
import { type DisconnectUseCase } from "@internal/discovery/use-case/DisconnectUseCase";
import { type GetConnectedDeviceUseCase } from "@internal/discovery/use-case/GetConnectedDeviceUseCase";
import { type ListConnectedDevicesUseCase } from "@internal/discovery/use-case/ListConnectedDevicesUseCase";
import { type ListenToConnectedDeviceUseCase } from "@internal/discovery/use-case/ListenToConnectedDeviceUseCase";
import { type ListenToKnownDevicesUseCase } from "@internal/discovery/use-case/ListenToKnownDevicesUseCase";
import type { StartDiscoveringUseCase } from "@internal/discovery/use-case/StartDiscoveringUseCase";
import type { StopDiscoveringUseCase } from "@internal/discovery/use-case/StopDiscoveringUseCase";
Expand Down Expand Up @@ -224,21 +224,38 @@ export class DeviceManagementKit {
.execute(args);
}

/**
* Close the Device Management kit.
*
*/
close() {
return this.container
.get<CloseSessionsUseCase>(deviceSessionTypes.CloseSessionsUseCase)
.execute();
}

/**
* Lists all device sessions.
* Lists all connected devices.
*
* @returns {ConnectedDevice[]} The list of device sessions.
*/
listConnectedDevices(): ConnectedDevice[] {
return this.container
.get<ListConnectedDevicesUseCase>(
discoveryTypes.ListConnectedDevicesUseCase,
)
.execute();
}

/**
* Listen to connected device.
*
* @returns {DeviceSession[]} The list of device sessions.
* @returns {Observable<ConnectedDevice>} An observable of connected device.
*/
listDeviceSessions(): DeviceSession[] {
listenToConnectedDevice(): Observable<ConnectedDevice> {
return this.container
.get<ListDeviceSessionsUseCase>(
deviceSessionTypes.ListDeviceSessionsUseCase,
.get<ListenToConnectedDeviceUseCase>(
discoveryTypes.ListenToConnectedDeviceUseCase,
)
.execute();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { type DeviceId, type DeviceModelId } from "@api/device/DeviceModel";
import { type ConnectionType } from "@api/discovery/ConnectionType";
import { type DeviceSessionId } from "@api/types";
import { type InternalConnectedDevice } from "@internal/transport/model/InternalConnectedDevice";

type ConnectedDeviceConstructorArgs = {
readonly internalConnectedDevice: InternalConnectedDevice;
readonly sessionId: DeviceSessionId;
};

export class ConnectedDevice {
public readonly id: DeviceId;
public readonly sessionId: DeviceSessionId;
public readonly modelId: DeviceModelId;
public readonly name: string;
public readonly type: ConnectionType;
Expand All @@ -18,8 +21,10 @@ export class ConnectedDevice {
deviceModel: { id: deviceModelId, productName: deviceName },
type,
},
sessionId,
}: ConnectedDeviceConstructorArgs) {
this.id = id;
this.sessionId = sessionId;
this.modelId = deviceModelId;
this.name = deviceName;
this.type = type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
import { DefaultDeviceSessionService } from "@internal/device-session/service/DefaultDeviceSessionService";
import { CloseSessionsUseCase } from "@internal/device-session/use-case/CloseSessionsUseCase";
import { GetDeviceSessionStateUseCase } from "@internal/device-session/use-case/GetDeviceSessionStateUseCase";
import { ListDeviceSessionsUseCase } from "@internal/device-session/use-case/ListDeviceSessionsUseCase";
import { loggerTypes } from "@internal/logger-publisher/di/loggerTypes";
import { type LoggerPublisherService } from "@internal/logger-publisher/service/LoggerPublisherService";
import { StubUseCase } from "@root/src/di.stub";
Expand Down Expand Up @@ -70,13 +69,8 @@ export const deviceSessionModuleFactory = (
);
bind(deviceSessionTypes.CloseSessionsUseCase).to(CloseSessionsUseCase);

bind(deviceSessionTypes.ListDeviceSessionsUseCase).to(
ListDeviceSessionsUseCase,
);

if (stub) {
rebind(deviceSessionTypes.GetDeviceSessionStateUseCase).to(StubUseCase);
rebind(deviceSessionTypes.ListDeviceSessionsUseCase).to(StubUseCase);
}
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ export const deviceSessionTypes = {
DeviceSessionService: Symbol.for("DeviceSessionService"),
GetDeviceSessionStateUseCase: Symbol.for("GetDeviceSessionStateUseCase"),
CloseSessionsUseCase: Symbol.for("CloseSessionsUseCase"),
ListDeviceSessionsUseCase: Symbol.for("ListDeviceSessionsUseCase"),
};
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export class DeviceSession {
rawApdu,
options.triggersDisconnection,
);
console.log("errrorOrResponse", rawApdu);

return errorOrResponse.ifRight((response: ApduResponse) => {
if (CommandUtils.isLockedDeviceResponse(response)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Either, Left } from "purify-ts";
import { Observable } from "rxjs";

import { DeviceSession } from "@internal/device-session/model/DeviceSession";
import { type DeviceSession } from "@internal/device-session/model/DeviceSession";
import { deviceSessionStubBuilder } from "@internal/device-session/model/DeviceSession.stub";
import { DeviceSessionNotFound } from "@internal/device-session/model/Errors";
import { DefaultLoggerPublisherService } from "@internal/logger-publisher/service/DefaultLoggerPublisherService";
import { AxiosManagerApiDataSource } from "@internal/manager-api/data/AxiosManagerApiDataSource";
import { type ManagerApiDataSource } from "@internal/manager-api/data/ManagerApiDataSource";
import { DefaultManagerApiService } from "@internal/manager-api/service/DefaultManagerApiService";
import type { ManagerApiService } from "@internal/manager-api/service/ManagerApiService";
import { connectedDeviceStubBuilder } from "@internal/transport/model/InternalConnectedDevice.stub";

import { DefaultDeviceSessionService } from "./DefaultDeviceSessionService";

Expand All @@ -30,10 +31,8 @@ describe("DefaultDeviceSessionService", () => {
});
managerApi = new DefaultManagerApiService(managerApiDataSource);

deviceSession = new DeviceSession(
{
connectedDevice: connectedDeviceStubBuilder(),
},
deviceSession = deviceSessionStubBuilder(
{},
() => loggerService,
managerApi,
);
Expand Down Expand Up @@ -82,4 +81,40 @@ describe("DefaultDeviceSessionService", () => {
sessionService.addDeviceSession(deviceSession);
expect(sessionService.getDeviceSessions()).toEqual([deviceSession]);
});

it("should retrieve sessionObs", () => {
expect(sessionService.sessionsObs).toBeInstanceOf(
Observable<DeviceSession>,
);
});

it("should emit new session", (done) => {
const subscription = sessionService.sessionsObs.subscribe({
next(emittedDeviceSession) {
expect(emittedDeviceSession).toStrictEqual(deviceSession);
subscription.unsubscribe();
done();
},
});
sessionService.addDeviceSession(deviceSession);
});

it("should emit previous added session", () => {
const lastDeviceSession = deviceSessionStubBuilder(
{ id: "last-session" },
() => loggerService,
managerApi,
);
const emittedSessions: DeviceSession[] = [];
sessionService.addDeviceSession(deviceSession);
sessionService.addDeviceSession(lastDeviceSession);

const subscription = sessionService.sessionsObs.subscribe({
next(emittedDeviceSession) {
emittedSessions.push(emittedDeviceSession);
},
});
expect(emittedSessions).toEqual([deviceSession, lastDeviceSession]);
subscription.unsubscribe();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { inject, injectable } from "inversify";
import { Maybe } from "purify-ts";
import { Observable, ReplaySubject } from "rxjs";

import { DeviceSession } from "@internal/device-session/model/DeviceSession";
import { DeviceSessionNotFound } from "@internal/device-session/model/Errors";
Expand All @@ -10,16 +11,22 @@ import { LoggerPublisherService } from "@internal/logger-publisher/service/Logge
@injectable()
export class DefaultDeviceSessionService implements DeviceSessionService {
private _sessions: DeviceSession[];
private _logger: LoggerPublisherService;
private readonly _logger: LoggerPublisherService;
private _sessionsSubject: ReplaySubject<DeviceSession>;

constructor(
@inject(loggerTypes.LoggerPublisherServiceFactory)
loggerModuleFactory: (tag: string) => LoggerPublisherService,
) {
this._sessions = [];
this._sessionsSubject = new ReplaySubject();
this._logger = loggerModuleFactory("DeviceSessionService");
}

public get sessionsObs(): Observable<DeviceSession> {
return this._sessionsSubject.asObservable();
}

addDeviceSession(deviceSession: DeviceSession) {
const found = this._sessions.find((s) => s.id === deviceSession.id);
if (found) {
Expand All @@ -30,6 +37,7 @@ export class DefaultDeviceSessionService implements DeviceSessionService {
}

this._sessions.push(deviceSession);
this._sessionsSubject.next(deviceSession);
this._logger.info("DeviceSession added", { data: { deviceSession } });
return this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type Either } from "purify-ts";
import { type Observable } from "rxjs";

import { type DmkError } from "@api/Error";
import { type DeviceSession } from "@internal/device-session/model/DeviceSession";
Expand All @@ -9,4 +10,5 @@ export interface DeviceSessionService {
getDeviceSessionByDeviceId(deviceId: string): Either<DmkError, DeviceSession>;
removeDeviceSession(sessionId: string): DeviceSessionService;
getDeviceSessions(): DeviceSession[];
get sessionsObs(): Observable<DeviceSession>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { deviceModelModuleFactory } from "@internal/device-model/di/deviceModelM
import { deviceSessionModuleFactory } from "@internal/device-session/di/deviceSessionModule";
import { ConnectUseCase } from "@internal/discovery/use-case/ConnectUseCase";
import { DisconnectUseCase } from "@internal/discovery/use-case/DisconnectUseCase";
import { ListConnectedDevicesUseCase } from "@internal/discovery/use-case/ListConnectedDevicesUseCase";
import { ListenToKnownDevicesUseCase } from "@internal/discovery/use-case/ListenToKnownDevicesUseCase";
import { StartDiscoveringUseCase } from "@internal/discovery/use-case/StartDiscoveringUseCase";
import { StopDiscoveringUseCase } from "@internal/discovery/use-case/StopDiscoveringUseCase";
Expand Down Expand Up @@ -66,5 +67,11 @@ describe("discoveryModuleFactory", () => {
expect(listenToKnownDevicesUseCase).toBeInstanceOf(
ListenToKnownDevicesUseCase,
);
const listConnectedDevicesUseCase = container.get(
discoveryTypes.ListConnectedDevicesUseCase,
);
expect(listConnectedDevicesUseCase).toBeInstanceOf(
ListConnectedDevicesUseCase,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { ContainerModule } from "inversify";
import { ConnectUseCase } from "@internal/discovery/use-case/ConnectUseCase";
import { DisconnectUseCase } from "@internal/discovery/use-case/DisconnectUseCase";
import { GetConnectedDeviceUseCase } from "@internal/discovery/use-case/GetConnectedDeviceUseCase";
import { ListConnectedDevicesUseCase } from "@internal/discovery/use-case/ListConnectedDevicesUseCase";
import { ListenToConnectedDeviceUseCase } from "@internal/discovery/use-case/ListenToConnectedDeviceUseCase";
import { ListenToKnownDevicesUseCase } from "@internal/discovery/use-case/ListenToKnownDevicesUseCase";
import { StartDiscoveringUseCase } from "@internal/discovery/use-case/StartDiscoveringUseCase";
import { StopDiscoveringUseCase } from "@internal/discovery/use-case/StopDiscoveringUseCase";
Expand All @@ -26,6 +28,12 @@ export const discoveryModuleFactory = ({ stub = false }: FactoryProps) =>
bind(discoveryTypes.ListenToKnownDevicesUseCase).to(
ListenToKnownDevicesUseCase,
);
bind(discoveryTypes.ListenToConnectedDeviceUseCase).to(
ListenToConnectedDeviceUseCase,
);
bind(discoveryTypes.ListConnectedDevicesUseCase).to(
ListConnectedDevicesUseCase,
);

if (stub) {
rebind(discoveryTypes.StartDiscoveringUseCase).to(StubUseCase);
Expand All @@ -34,5 +42,7 @@ export const discoveryModuleFactory = ({ stub = false }: FactoryProps) =>
rebind(discoveryTypes.DisconnectUseCase).to(StubUseCase);
rebind(discoveryTypes.GetConnectedDeviceUseCase).to(StubUseCase);
rebind(discoveryTypes.ListenToKnownDevicesUseCase).to(StubUseCase);
rebind(discoveryTypes.ListenToConnectedDeviceUseCase).to(StubUseCase);
rebind(discoveryTypes.ListConnectedDevicesUseCase).to(StubUseCase);
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ export const discoveryTypes = {
DisconnectUseCase: Symbol.for("DisconnectUseCase"),
GetConnectedDeviceUseCase: Symbol.for("GetConnectedDeviceUseCase"),
ListenToKnownDevicesUseCase: Symbol.for("ListenToKnownDevicesUseCase"),
ListenToConnectedDeviceUseCase: Symbol.for("ListenToConnectedDeviceUseCase"),
ListConnectedDevicesUseCase: Symbol.for("ListConnectedDevicesUseCase"),
};
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ describe("GetConnectedDevice", () => {
expect(response).toStrictEqual(
new ConnectedDevice({
internalConnectedDevice: deviceSession.connectedDevice,
sessionId: fakeSessionId,
}),
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class GetConnectedDeviceUseCase {
Right: (deviceSession) =>
new ConnectedDevice({
internalConnectedDevice: deviceSession.connectedDevice,
sessionId: deviceSession.id,
}),
Left: (error) => {
this._logger.error("Error getting session", {
Expand Down
Loading

0 comments on commit 8cc0194

Please sign in to comment.