Skip to content

Commit f9de41d

Browse files
committed
✨ (dmk): Improve refresher after new intent queue impl
1 parent 4c1498b commit f9de41d

File tree

20 files changed

+154
-166
lines changed

20 files changed

+154
-166
lines changed

.changeset/cute-hands-sin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ledgerhq/device-management-kit": patch
3+
---
4+
5+
Update refresher to don't ping when an intent is being processed

.changeset/odd-ducks-find.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ledgerhq/device-signer-kit-solana": patch
3+
---
4+
5+
Update InternalApi type in test

.changeset/vast-papayas-melt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ledgerhq/device-signer-kit-bitcoin": patch
3+
---
4+
5+
Update InternalApi type in test

.changeset/wild-adults-camp.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ledgerhq/device-signer-kit-ethereum": patch
3+
---
4+
5+
Update InternalApi type in test

.changeset/wild-loops-act.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ledgerhq/device-management-kit": minor
3+
---
4+
5+
Cleanup InternalApi to remove disableRefresher function

packages/device-management-kit/src/api/device-action/DeviceAction.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export type InternalApi = {
2626
readonly setDeviceSessionState: (
2727
state: DeviceSessionState,
2828
) => DeviceSessionState;
29-
readonly disableRefresher: (blockerId: string) => () => void;
3029
readonly getManagerApiService: () => ManagerApiService;
3130
readonly getSecureChannelService: () => SecureChannelService;
3231
};

packages/device-management-kit/src/api/device-action/__test-utils__/makeInternalApi.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ const apiGetDeviceSessionStateObservableMock = vi.fn();
1010
const setDeviceSessionStateMock = vi.fn();
1111
const getManagerApiServiceMock = vi.fn();
1212
const getSecureChannelServiceMock = vi.fn();
13-
const disableRefresherMock = vi.fn();
1413

1514
export function makeDeviceActionInternalApiMock(): Mocked<InternalApi> {
1615
return {
@@ -22,6 +21,5 @@ export function makeDeviceActionInternalApiMock(): Mocked<InternalApi> {
2221
setDeviceSessionState: setDeviceSessionStateMock,
2322
getManagerApiService: getManagerApiServiceMock,
2423
getSecureChannelService: getSecureChannelServiceMock,
25-
disableRefresher: disableRefresherMock,
2624
};
2725
}

packages/device-management-kit/src/api/secure-channel/task/ConnectToSecureChannelTask.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ export class ConnectToSecureChannelTask {
4141
}
4242

4343
run(): Observable<SecureChannelEvent> {
44-
const reenableRefresher = this._api.disableRefresher(
45-
"connectToSecureChannel",
46-
);
47-
4844
const obs = new Observable<SecureChannelEvent>((subscriber) => {
4945
let unsubscribed: boolean = false;
5046
let ignoreNetworkEvents = false;
@@ -318,7 +314,6 @@ export class ConnectToSecureChannelTask {
318314
};
319315

320316
return () => {
321-
reenableRefresher();
322317
unsubscribed = true;
323318
// Close the connection if it is open when unsubscribing
324319
if (this._connection.readyState === WebSocket.OPEN) {

packages/device-management-kit/src/internal/device-session/model/DeviceSession.test.ts

Lines changed: 21 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe("DeviceSession", () => {
3333
let mockManagerApi: Mocked<ManagerApiService>;
3434
let mockSecureChannel: Mocked<SecureChannelService>;
3535
let mockIntentQueueService: Mocked<IntentQueueService>;
36+
let mockIntentQueueServiceFactory: () => IntentQueueService;
3637
let refresherOptions: DeviceSessionRefresherOptions;
3738
const mockConnectedDevice = connectedDeviceStubBuilder();
3839

@@ -66,6 +67,8 @@ describe("DeviceSession", () => {
6667
}),
6768
} as unknown as Mocked<IntentQueueService>;
6869

70+
mockIntentQueueServiceFactory = () => mockIntentQueueService;
71+
6972
refresherOptions = {
7073
isRefresherDisabled: true,
7174
};
@@ -84,7 +87,7 @@ describe("DeviceSession", () => {
8487
mockManagerApi,
8588
mockSecureChannel,
8689
refresherOptions,
87-
mockIntentQueueService,
90+
mockIntentQueueServiceFactory,
8891
);
8992

9093
// then
@@ -104,7 +107,7 @@ describe("DeviceSession", () => {
104107
mockManagerApi,
105108
mockSecureChannel,
106109
refresherOptions,
107-
mockIntentQueueService,
110+
mockIntentQueueServiceFactory,
108111
);
109112

110113
// then
@@ -113,57 +116,27 @@ describe("DeviceSession", () => {
113116
});
114117

115118
describe("initialiseSession", () => {
116-
it("should successfully initialize session", async () => {
119+
it("should successfully initialize session", () => {
117120
// given
118-
const mockObservable = of({
119-
status: "success",
120-
data: { name: "Dashboard", version: "1.0.0" },
121-
}).pipe(delay(1));
122-
123-
mockIntentQueueService.enqueue.mockReturnValue({
124-
observable: mockObservable,
125-
cancel: vi.fn(),
126-
});
127-
128121
deviceSession = new DeviceSession(
129122
{ connectedDevice: mockConnectedDevice },
130123
mockLoggerFactory,
131124
mockManagerApi,
132125
mockSecureChannel,
133-
refresherOptions,
134-
mockIntentQueueService,
126+
{ isRefresherDisabled: false, pollingInterval: 1000 },
127+
mockIntentQueueServiceFactory,
128+
);
129+
130+
const mockRefresherStartSpy = vi.spyOn(
131+
deviceSession["_deviceSessionRefresher"],
132+
"startRefresher",
135133
);
136134

137135
// when
138-
await deviceSession.initialiseSession();
136+
deviceSession.initialiseSession();
139137

140138
// then
141-
expect(mockIntentQueueService.enqueue).toHaveBeenCalled();
142-
});
143-
144-
it("should throw error if initialization fails", async () => {
145-
// given
146-
const error = new Error("Initialization failed");
147-
const mockObservable = throwError(() => error);
148-
149-
mockIntentQueueService.enqueue.mockReturnValue({
150-
observable: mockObservable,
151-
cancel: vi.fn(),
152-
});
153-
154-
deviceSession = new DeviceSession(
155-
{ connectedDevice: mockConnectedDevice },
156-
mockLoggerFactory,
157-
mockManagerApi,
158-
mockSecureChannel,
159-
refresherOptions,
160-
mockIntentQueueService,
161-
);
162-
163-
// when/then
164-
await expect(deviceSession.initialiseSession()).rejects.toThrow(
165-
"Initialization failed",
166-
);
139+
expect(mockRefresherStartSpy).toHaveBeenCalled();
167140
});
168141
});
169142

@@ -175,7 +148,7 @@ describe("DeviceSession", () => {
175148
mockManagerApi,
176149
mockSecureChannel,
177150
refresherOptions,
178-
mockIntentQueueService,
151+
mockIntentQueueServiceFactory,
179152
);
180153
});
181154

@@ -208,7 +181,7 @@ describe("DeviceSession", () => {
208181
mockManagerApi,
209182
mockSecureChannel,
210183
refresherOptions,
211-
mockIntentQueueService,
184+
mockIntentQueueServiceFactory,
212185
);
213186
});
214187

@@ -271,7 +244,7 @@ describe("DeviceSession", () => {
271244
mockManagerApi,
272245
mockSecureChannel,
273246
refresherOptions,
274-
mockIntentQueueService,
247+
mockIntentQueueServiceFactory,
275248
);
276249
});
277250

@@ -377,7 +350,7 @@ describe("DeviceSession", () => {
377350
mockManagerApi,
378351
mockSecureChannel,
379352
refresherOptions,
380-
mockIntentQueueService,
353+
mockIntentQueueServiceFactory,
381354
);
382355
});
383356

@@ -486,7 +459,7 @@ describe("DeviceSession", () => {
486459
mockManagerApi,
487460
mockSecureChannel,
488461
refresherOptions,
489-
mockIntentQueueService,
462+
mockIntentQueueServiceFactory,
490463
);
491464
});
492465

@@ -560,7 +533,6 @@ describe("DeviceSession", () => {
560533
getDeviceSessionState: expect.any(Function),
561534
getDeviceSessionStateObservable: expect.any(Function),
562535
setDeviceSessionState: expect.any(Function),
563-
disableRefresher: expect.any(Function),
564536
getManagerApiService: expect.any(Function),
565537
getSecureChannelService: expect.any(Function),
566538
});
@@ -594,7 +566,7 @@ describe("DeviceSession", () => {
594566
mockManagerApi,
595567
mockSecureChannel,
596568
refresherOptions,
597-
mockIntentQueueService,
569+
mockIntentQueueServiceFactory,
598570
);
599571

600572
let isComplete = false;
@@ -613,60 +585,4 @@ describe("DeviceSession", () => {
613585
expect(isComplete).toBe(true);
614586
});
615587
});
616-
617-
describe("disableRefresher", () => {
618-
beforeEach(() => {
619-
// Use proper mock for refresher tests
620-
const mockCommandResult = {
621-
status: "success",
622-
data: { name: "Dashboard", version: "1.0.0" },
623-
};
624-
625-
mockIntentQueueService.enqueue.mockReturnValue({
626-
observable: of(mockCommandResult).pipe(delay(1)),
627-
cancel: vi.fn(),
628-
});
629-
630-
deviceSession = new DeviceSession(
631-
{ connectedDevice: mockConnectedDevice },
632-
mockLoggerFactory,
633-
mockManagerApi,
634-
mockSecureChannel,
635-
{ isRefresherDisabled: false, pollingInterval: 1000 },
636-
mockIntentQueueService,
637-
);
638-
});
639-
640-
afterEach(() => {
641-
// Clean up refresher
642-
if (deviceSession) {
643-
deviceSession.close();
644-
}
645-
});
646-
647-
it("should return a function to re-enable refresher", () => {
648-
// when
649-
const enableRefresher = deviceSession.disableRefresher("test-blocker-id");
650-
651-
// then
652-
expect(enableRefresher).toBeInstanceOf(Function);
653-
654-
// Should be able to call the enable function
655-
enableRefresher();
656-
});
657-
658-
it("should handle multiple refresher blockers", () => {
659-
// when
660-
const enable1 = deviceSession.disableRefresher("blocker-1");
661-
const enable2 = deviceSession.disableRefresher("blocker-2");
662-
663-
// then
664-
expect(enable1).toBeInstanceOf(Function);
665-
expect(enable2).toBeInstanceOf(Function);
666-
667-
// Both should be callable
668-
enable1();
669-
enable2();
670-
});
671-
});
672588
});

packages/device-management-kit/src/internal/device-session/model/DeviceSession.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,18 @@ export class DeviceSession {
8484
managerApiService: ManagerApiService,
8585
secureChannelService: SecureChannelService,
8686
deviceSessionRefresherOptions: DeviceSessionRefresherOptions | undefined,
87-
intentQueueService: IntentQueueService = new IntentQueueService(
88-
loggerModuleFactory,
89-
),
87+
intentQueueServiceFactory: (
88+
sessionEventDispatcher: DeviceSessionEventDispatcher,
89+
) => IntentQueueService = (sessionEventDispatcher) =>
90+
new IntentQueueService(loggerModuleFactory, sessionEventDispatcher),
9091
) {
9192
this._id = id;
9293
this._connectedDevice = connectedDevice;
9394
this._logger = loggerModuleFactory("device-session");
94-
this._intentQueueService = intentQueueService;
9595
this._managerApiService = managerApiService;
96+
this._intentQueueService = intentQueueServiceFactory(
97+
this._sessionEventDispatcher,
98+
);
9699
this._secureChannelService = secureChannelService;
97100
this._refresherOptions = {
98101
...DEVICE_SESSION_REFRESHER_DEFAULT_OPTIONS,
@@ -201,10 +204,6 @@ export class DeviceSession {
201204
triggersDisconnection: false,
202205
},
203206
): Promise<Either<DmkError, ApduResponse>> {
204-
this._sessionEventDispatcher.dispatch({
205-
eventName: SessionEvents.DEVICE_STATE_UPDATE_BUSY,
206-
});
207-
208207
this._logger.debug(`[exchange] => ${bufferToHexaString(rawApdu, false)}`);
209208
const result = await this._connectedDevice.sendApdu(
210209
rawApdu,
@@ -228,7 +227,7 @@ export class DeviceSession {
228227
})
229228
.ifLeft(() => {
230229
this._sessionEventDispatcher.dispatch({
231-
eventName: SessionEvents.DEVICE_STATE_UPDATE_CONNECTED,
230+
eventName: SessionEvents.DEVICE_STATE_UPDATE_UNKNOWN,
232231
});
233232
});
234233
return result;
@@ -308,8 +307,6 @@ export class DeviceSession {
308307
this.setDeviceSessionState(state);
309308
return this._deviceState.getValue();
310309
},
311-
disableRefresher: (blockerId: string) =>
312-
this._refresherService.disableRefresher(blockerId),
313310
getManagerApiService: () => this._managerApiService,
314311
getSecureChannelService: () => this._secureChannelService,
315312
});
@@ -328,6 +325,7 @@ export class DeviceSession {
328325
this._updateDeviceStatus(DeviceStatus.NOT_CONNECTED);
329326
this._deviceState.complete();
330327
this._deviceSessionRefresher.stopRefresher();
328+
this._pinger.unsubscribe();
331329
}
332330

333331
public disableRefresher(id: string): () => void {

0 commit comments

Comments
 (0)