diff --git a/.changeset/tender-rings-leave.md b/.changeset/tender-rings-leave.md new file mode 100644 index 000000000..698f206da --- /dev/null +++ b/.changeset/tender-rings-leave.md @@ -0,0 +1,5 @@ +--- +"@ledgerhq/device-sdk-core": patch +--- + +Add ListDeviceSessions use case diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6070d91fb..64ae3f3d2 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -25,9 +25,9 @@ _In case of visual features, please attach screenshots or video recordings to de Pull Requests must pass the CI and be code reviewed. Set as Draft if the PR is not ready. - [ ] **Covered by automatic tests.** -- [ ] **Impact of the changes:** - [ ] **Changeset is provided** - +- [ ] **Impact of the changes:** + - ... --- ### 🧐 Checklist for the PR Reviewers diff --git a/packages/core/src/api/DeviceSdk.test.ts b/packages/core/src/api/DeviceSdk.test.ts index ba3c6648f..6ddb8fde7 100644 --- a/packages/core/src/api/DeviceSdk.test.ts +++ b/packages/core/src/api/DeviceSdk.test.ts @@ -61,6 +61,10 @@ describe("DeviceSdk", () => { it("should have sendCommand method", () => { expect(sdk.sendCommand).toBeDefined(); }); + + it("should have listDeviceSessions method", () => { + expect(sdk.listDeviceSessions).toBeDefined(); + }); }); describe("stubbed", () => { @@ -100,6 +104,7 @@ describe("DeviceSdk", () => { [usbDiTypes.GetConnectedDeviceUseCase], [discoveryTypes.DisconnectUseCase], [deviceSessionTypes.GetDeviceSessionStateUseCase], + [deviceSessionTypes.ListDeviceSessionsUseCase], ])("should have %p use case", (diSymbol) => { const uc = sdk.container.get(diSymbol); expect(uc).toBeInstanceOf(StubUseCase); diff --git a/packages/core/src/api/DeviceSdk.ts b/packages/core/src/api/DeviceSdk.ts index ad93e60a5..bc623d6c1 100644 --- a/packages/core/src/api/DeviceSdk.ts +++ b/packages/core/src/api/DeviceSdk.ts @@ -24,7 +24,9 @@ import { ConnectedDevice } from "@api/usb/model/ConnectedDevice"; import { configTypes } from "@internal/config/di/configTypes"; import { GetSdkVersionUseCase } from "@internal/config/use-case/GetSdkVersionUseCase"; import { deviceSessionTypes } from "@internal/device-session/di/deviceSessionTypes"; +import { DeviceSession } from "@internal/device-session/model/DeviceSession"; import { GetDeviceSessionStateUseCase } from "@internal/device-session/use-case/GetDeviceSessionStateUseCase"; +import { ListDeviceSessionsUseCase } from "@internal/device-session/use-case/ListDeviceSessionsUseCase"; import { discoveryTypes } from "@internal/discovery/di/discoveryTypes"; import { ConnectUseCase } from "@internal/discovery/use-case/ConnectUseCase"; import { DisconnectUseCase } from "@internal/discovery/use-case/DisconnectUseCase"; @@ -193,4 +195,17 @@ export class DeviceSdk { ) .execute(args); } + + /** + * Lists all device sessions. + * + * @returns {DeviceSession[]} The list of device sessions. + */ + listDeviceSessions(): DeviceSession[] { + return this.container + .get( + deviceSessionTypes.ListDeviceSessionsUseCase, + ) + .execute(); + } } diff --git a/packages/core/src/internal/device-session/di/deviceSessionModule.ts b/packages/core/src/internal/device-session/di/deviceSessionModule.ts index 11d133b55..ea0ae2b66 100644 --- a/packages/core/src/internal/device-session/di/deviceSessionModule.ts +++ b/packages/core/src/internal/device-session/di/deviceSessionModule.ts @@ -12,6 +12,7 @@ import { } from "@internal/device-session/service/DefaultApduSenderService"; import { DefaultDeviceSessionService } from "@internal/device-session/service/DefaultDeviceSessionService"; 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 { LoggerPublisherService } from "@internal/logger-publisher/service/LoggerPublisherService"; import { StubUseCase } from "@root/src/di.stub"; @@ -67,8 +68,13 @@ export const deviceSessionModuleFactory = ( GetDeviceSessionStateUseCase, ); + bind(deviceSessionTypes.ListDeviceSessionsUseCase).to( + ListDeviceSessionsUseCase, + ); + if (stub) { rebind(deviceSessionTypes.GetDeviceSessionStateUseCase).to(StubUseCase); + rebind(deviceSessionTypes.ListDeviceSessionsUseCase).to(StubUseCase); } }, ); diff --git a/packages/core/src/internal/device-session/di/deviceSessionTypes.ts b/packages/core/src/internal/device-session/di/deviceSessionTypes.ts index 9361ab55b..13d7faeec 100644 --- a/packages/core/src/internal/device-session/di/deviceSessionTypes.ts +++ b/packages/core/src/internal/device-session/di/deviceSessionTypes.ts @@ -3,4 +3,5 @@ export const deviceSessionTypes = { ApduReceiverServiceFactory: Symbol.for("ApduReceiverServiceFactory"), DeviceSessionService: Symbol.for("DeviceSessionService"), GetDeviceSessionStateUseCase: Symbol.for("GetDeviceSessionStateUseCase"), + ListDeviceSessionsUseCase: Symbol.for("ListDeviceSessionsUseCase"), }; diff --git a/packages/core/src/internal/device-session/use-case/ListDeviceSessionsUseCase.test.ts b/packages/core/src/internal/device-session/use-case/ListDeviceSessionsUseCase.test.ts new file mode 100644 index 000000000..2ecbc9105 --- /dev/null +++ b/packages/core/src/internal/device-session/use-case/ListDeviceSessionsUseCase.test.ts @@ -0,0 +1,64 @@ +import { deviceSessionStubBuilder } from "@internal/device-session/model/DeviceSession.stub"; +import { DefaultDeviceSessionService } from "@internal/device-session/service/DefaultDeviceSessionService"; +import { DeviceSessionService } from "@internal/device-session/service/DeviceSessionService"; +import { DefaultLoggerPublisherService } from "@internal/logger-publisher/service/DefaultLoggerPublisherService"; +import { LoggerPublisherService } from "@internal/logger-publisher/service/LoggerPublisherService"; +import { AxiosManagerApiDataSource } from "@internal/manager-api/data/AxiosManagerApiDataSource"; +import { ManagerApiDataSource } from "@internal/manager-api/data/ManagerApiDataSource"; +import { DefaultManagerApiService } from "@internal/manager-api/service/DefaultManagerApiService"; +import { ManagerApiService } from "@internal/manager-api/service/ManagerApiService"; + +import { ListDeviceSessionsUseCase } from "./ListDeviceSessionsUseCase"; + +let logger: LoggerPublisherService; +let sessionService: DeviceSessionService; +let managerApiDataSource: ManagerApiDataSource; +let managerApi: ManagerApiService; + +describe("ListDeviceSessionsUseCase", () => { + beforeEach(() => { + logger = new DefaultLoggerPublisherService( + [], + "list-device-sessions-use-case-test", + ); + managerApiDataSource = new AxiosManagerApiDataSource({ + managerApiUrl: "http://fake.url", + }); + managerApi = new DefaultManagerApiService(managerApiDataSource); + sessionService = new DefaultDeviceSessionService(() => logger); + }); + + it("should list all device sessions", () => { + // given + const deviceSession1 = deviceSessionStubBuilder( + { id: "1" }, + () => logger, + managerApi, + ); + const deviceSession2 = deviceSessionStubBuilder( + { id: "2" }, + () => logger, + managerApi, + ); + sessionService.addDeviceSession(deviceSession1); + sessionService.addDeviceSession(deviceSession2); + const useCase = new ListDeviceSessionsUseCase(sessionService, () => logger); + + // when + const response = useCase.execute(); + + // then + expect(response).toStrictEqual([deviceSession1, deviceSession2]); + }); + + it("should return empty array if no device sessions", () => { + // given + const useCase = new ListDeviceSessionsUseCase(sessionService, () => logger); + + // when + const response = useCase.execute(); + + // then + expect(response).toStrictEqual([]); + }); +}); diff --git a/packages/core/src/internal/device-session/use-case/ListDeviceSessionsUseCase.ts b/packages/core/src/internal/device-session/use-case/ListDeviceSessionsUseCase.ts new file mode 100644 index 000000000..9a4cf678f --- /dev/null +++ b/packages/core/src/internal/device-session/use-case/ListDeviceSessionsUseCase.ts @@ -0,0 +1,31 @@ +import { inject, injectable } from "inversify"; + +import { deviceSessionTypes } from "@internal/device-session/di/deviceSessionTypes"; +import { DeviceSession } from "@internal/device-session/model/DeviceSession"; +import type { DeviceSessionService } from "@internal/device-session/service/DeviceSessionService"; +import { loggerTypes } from "@internal/logger-publisher/di/loggerTypes"; +import { LoggerPublisherService } from "@internal/logger-publisher/service/LoggerPublisherService"; + +/** + * List all device sessions. + */ +@injectable() +export class ListDeviceSessionsUseCase { + private readonly _sessionService: DeviceSessionService; + private readonly _logger: LoggerPublisherService; + + constructor( + @inject(deviceSessionTypes.DeviceSessionService) + sessionService: DeviceSessionService, + @inject(loggerTypes.LoggerPublisherServiceFactory) + loggerFactory: (tag: string) => LoggerPublisherService, + ) { + this._sessionService = sessionService; + this._logger = loggerFactory("ListDeviceSessionsUseCase"); + } + + execute(): DeviceSession[] { + this._logger.info("Listing device sessions"); + return this._sessionService.getDeviceSessions(); + } +}