From c89b1803e75fd9c6acacde1ca38bd204e71c854b Mon Sep 17 00:00:00 2001 From: jacog2 Date: Sun, 10 May 2026 23:57:01 -0700 Subject: [PATCH] Add listen key lifecycle methods --- README.md | 4 +- .../services/listen-key.service.spec.ts | 55 +++++++++++++++++++ .../services/listen-key.service.ts | 20 +++++++ .../bingx-delete-listen-key-endpoint.ts | 33 +++++++++++ .../bingx-extend-listen-key-endpoint.ts | 12 ++-- .../bingx-listen-key-endpoints.spec.ts | 41 ++++++++++++++ src/bingx/endpoints/index.ts | 2 + 7 files changed, 158 insertions(+), 9 deletions(-) create mode 100644 src/bingx-client/services/listen-key.service.spec.ts create mode 100644 src/bingx/endpoints/bingx-delete-listen-key-endpoint.ts create mode 100644 src/bingx/endpoints/bingx-listen-key-endpoints.spec.ts diff --git a/README.md b/README.md index 6dd246f..c4a52a7 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,8 @@ stream.latestTradeDetail$.subscribe((v) => {}) - [ ] Query historical transaction orders * Listen Key - [x] Generate Listen Key - - [ ] Extend Listen Key Validity period - - [ ] Delete Listen Key + - [x] Extend Listen Key Validity period + - [x] Delete Listen Key * Socket API * Market Data - [ ] Subscribe Market Depth Data diff --git a/src/bingx-client/services/listen-key.service.spec.ts b/src/bingx-client/services/listen-key.service.spec.ts new file mode 100644 index 0000000..12a6766 --- /dev/null +++ b/src/bingx-client/services/listen-key.service.spec.ts @@ -0,0 +1,55 @@ +import { ApiAccount } from 'bingx-api/bingx/account/api-account'; +import { BingxDeleteListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-delete-listen-key-endpoint'; +import { BingxExtendListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-extend-listen-key-endpoint'; +import { BingxGenerateListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-generate-listen-key-endpoint'; +import { RequestExecutorInterface } from 'bingx-api/bingx/request-executor/request-executor.interface'; +import { ListenKeyService } from 'bingx-api/bingx-client/services/listen-key.service'; + +describe('listen key service', () => { + const account = new ApiAccount('api-key', 'secret-key'); + const listenKey = 'listen-key'; + let execute: jest.Mock; + let service: ListenKeyService; + + beforeEach(() => { + execute = jest.fn(); + service = new ListenKeyService({ + execute, + } as unknown as RequestExecutorInterface); + }); + + it('generates listen keys', async () => { + const response = { listenKey }; + execute.mockResolvedValueOnce(response); + + await expect(service.generateListenKey(account)).resolves.toBe(response); + expect(execute).toHaveBeenCalledTimes(1); + expect(execute).toHaveBeenCalledWith( + expect.any(BingxGenerateListenKeyEndpoint), + ); + }); + + it('extends listen key validity periods', async () => { + execute.mockResolvedValueOnce(undefined); + + await expect(service.extendListenKey(listenKey, account)).resolves.toBe( + undefined, + ); + expect(execute).toHaveBeenCalledTimes(1); + expect(execute).toHaveBeenCalledWith( + expect.any(BingxExtendListenKeyEndpoint), + ); + }); + + it('deletes listen keys', async () => { + execute.mockResolvedValueOnce(undefined); + + await expect(service.deleteListenKey(listenKey, account)).resolves.toBe( + undefined, + ); + expect(execute).toHaveBeenCalledTimes(1); + expect(execute).toHaveBeenCalledWith( + expect.any(BingxDeleteListenKeyEndpoint), + ); + }); +}); diff --git a/src/bingx-client/services/listen-key.service.ts b/src/bingx-client/services/listen-key.service.ts index 6535d93..656242e 100644 --- a/src/bingx-client/services/listen-key.service.ts +++ b/src/bingx-client/services/listen-key.service.ts @@ -1,4 +1,6 @@ import { AccountInterface } from 'bingx-api/bingx/account/account.interface'; +import { BingxDeleteListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-delete-listen-key-endpoint'; +import { BingxExtendListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-extend-listen-key-endpoint'; import { BingxGenerateListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-generate-listen-key-endpoint'; import { BingxGenerateListenKeyResponse } from 'bingx-api/bingx/endpoints/bingx-generate-listen-key-response'; import { RequestExecutorInterface } from 'bingx-api/bingx/request-executor/request-executor.interface'; @@ -13,4 +15,22 @@ export class ListenKeyService { new BingxGenerateListenKeyEndpoint(account), ); } + + async extendListenKey( + listenKey: string, + account: AccountInterface, + ): Promise { + return this.requestExecutor.execute( + new BingxExtendListenKeyEndpoint(listenKey, account), + ); + } + + async deleteListenKey( + listenKey: string, + account: AccountInterface, + ): Promise { + return this.requestExecutor.execute( + new BingxDeleteListenKeyEndpoint(listenKey, account), + ); + } } diff --git a/src/bingx/endpoints/bingx-delete-listen-key-endpoint.ts b/src/bingx/endpoints/bingx-delete-listen-key-endpoint.ts new file mode 100644 index 0000000..82652c4 --- /dev/null +++ b/src/bingx/endpoints/bingx-delete-listen-key-endpoint.ts @@ -0,0 +1,33 @@ +import { AccountInterface } from 'bingx-api/bingx/account/account.interface'; +import { DefaultSignatureParameters } from 'bingx-api/bingx/account/default-signature-parameters'; +import { SignatureParametersInterface } from 'bingx-api/bingx/account/signature-parameters.interface'; +import { Endpoint } from 'bingx-api/bingx/endpoints/endpoint'; +import { EndpointInterface } from 'bingx-api/bingx/endpoints/endpoint.interface'; + +export class BingxDeleteListenKeyEndpoint + extends Endpoint + implements EndpointInterface +{ + constructor( + private readonly listenKey: string, + account: AccountInterface, + ) { + super(account); + } + + method(): 'get' | 'post' | 'put' | 'patch' | 'delete' { + return 'delete'; + } + + parameters(): SignatureParametersInterface { + return new DefaultSignatureParameters({ + listenKey: this.listenKey, + }); + } + + path(): string { + return '/openApi/user/auth/userDataStream'; + } + + readonly t!: R; +} diff --git a/src/bingx/endpoints/bingx-extend-listen-key-endpoint.ts b/src/bingx/endpoints/bingx-extend-listen-key-endpoint.ts index a71c3b4..32a4ca4 100644 --- a/src/bingx/endpoints/bingx-extend-listen-key-endpoint.ts +++ b/src/bingx/endpoints/bingx-extend-listen-key-endpoint.ts @@ -1,10 +1,8 @@ -import { - AccountInterface, - DefaultSignatureParameters, - Endpoint, - EndpointInterface, - SignatureParametersInterface, -} from 'bingx-api/bingx'; +import { AccountInterface } from 'bingx-api/bingx/account/account.interface'; +import { DefaultSignatureParameters } from 'bingx-api/bingx/account/default-signature-parameters'; +import { SignatureParametersInterface } from 'bingx-api/bingx/account/signature-parameters.interface'; +import { Endpoint } from 'bingx-api/bingx/endpoints/endpoint'; +import { EndpointInterface } from 'bingx-api/bingx/endpoints/endpoint.interface'; export class BingxExtendListenKeyEndpoint extends Endpoint diff --git a/src/bingx/endpoints/bingx-listen-key-endpoints.spec.ts b/src/bingx/endpoints/bingx-listen-key-endpoints.spec.ts new file mode 100644 index 0000000..fa95ab5 --- /dev/null +++ b/src/bingx/endpoints/bingx-listen-key-endpoints.spec.ts @@ -0,0 +1,41 @@ +import { ApiAccount } from 'bingx-api/bingx/account/api-account'; +import { BingxDeleteListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-delete-listen-key-endpoint'; +import { BingxExtendListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-extend-listen-key-endpoint'; +import { BingxGenerateListenKeyEndpoint } from 'bingx-api/bingx/endpoints/bingx-generate-listen-key-endpoint'; + +describe('listen key endpoints', () => { + const account = new ApiAccount('api-key', 'secret-key'); + const listenKey = 'listen-key'; + + it('generates listen keys', () => { + const endpoint = new BingxGenerateListenKeyEndpoint(account); + const parameters = endpoint.parameters().asRecord(); + + expect(endpoint.method()).toBe('post'); + expect(endpoint.path()).toBe('/openApi/user/auth/userDataStream'); + expect(Object.keys(parameters)).toEqual(['timestamp']); + expect(Number(parameters.timestamp)).not.toBeNaN(); + }); + + it('extends listen key validity periods', () => { + const endpoint = new BingxExtendListenKeyEndpoint(listenKey, account); + const parameters = endpoint.parameters().asRecord(); + + expect(endpoint.method()).toBe('put'); + expect(endpoint.path()).toBe('/openApi/user/auth/userDataStream'); + expect(parameters).toMatchObject({ listenKey }); + expect(Object.keys(parameters).sort()).toEqual(['listenKey', 'timestamp']); + expect(Number(parameters.timestamp)).not.toBeNaN(); + }); + + it('deletes listen keys', () => { + const endpoint = new BingxDeleteListenKeyEndpoint(listenKey, account); + const parameters = endpoint.parameters().asRecord(); + + expect(endpoint.method()).toBe('delete'); + expect(endpoint.path()).toBe('/openApi/user/auth/userDataStream'); + expect(parameters).toMatchObject({ listenKey }); + expect(Object.keys(parameters).sort()).toEqual(['listenKey', 'timestamp']); + expect(Number(parameters.timestamp)).not.toBeNaN(); + }); +}); diff --git a/src/bingx/endpoints/index.ts b/src/bingx/endpoints/index.ts index 6866f8b..37d123f 100644 --- a/src/bingx/endpoints/index.ts +++ b/src/bingx/endpoints/index.ts @@ -1,5 +1,7 @@ export * from './bingx-cancel-all-orders-endpoint'; export * from './bingx-close-all-positions-endpoint'; +export * from './bingx-delete-listen-key-endpoint'; +export * from './bingx-extend-listen-key-endpoint'; export * from './bingx-generate-listen-key-endpoint'; export * from './bingx-generate-listen-key-response'; export * from './bingx-get-perpetual-swap-account-asset-endpoint';