diff --git a/src/APIClient.ts b/src/APIClient.ts index 94058c10..692ad5ad 100644 --- a/src/APIClient.ts +++ b/src/APIClient.ts @@ -1,4 +1,4 @@ -import {RESTClient} from './client/RESTClient'; +import {Authorization, RESTClient} from './client/RESTClient'; import {LightstreamerAPI} from './lightstreamer'; export class APIClient { @@ -8,7 +8,7 @@ export class APIClient { static URL_DEMO = 'https://demo-api.ig.com/gateway/deal/'; static URL_LIVE = 'https://api.ig.com/gateway/deal/'; - constructor(private readonly baseUrl: string, apiKey: string) { + constructor(private readonly baseUrl: string, apiKey: string | Authorization) { this.rest = new RESTClient(baseUrl, apiKey); this.stream = new LightstreamerAPI(this.rest.auth); } diff --git a/src/client/RESTClient.ts b/src/client/RESTClient.ts index 9eb2ee47..e138ddd5 100644 --- a/src/client/RESTClient.ts +++ b/src/client/RESTClient.ts @@ -8,8 +8,10 @@ import axiosRetry from 'axios-retry'; export interface Authorization { accessToken?: string; accountId?: string; + apiKey?: string; clientSessionToken?: string; lightstreamerEndpoint?: string; + password?: string; refreshToken?: string; securityToken?: string; username?: string; @@ -35,11 +37,19 @@ export class RESTClient { readonly httpClient: AxiosInstance; readonly auth: Authorization = {}; - constructor(baseURL: string, private readonly apiKey: string) { + constructor(baseURL: string, private readonly apiKey: string | Authorization) { this.httpClient = axios.create({ baseURL: baseURL, }); + function isAuthorization(candidate: any): candidate is Authorization { + return typeof candidate !== 'string'; + } + + if (isAuthorization(this.apiKey)) { + this.auth = this.apiKey; + } + function randomNum(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1) + min); } @@ -71,7 +81,7 @@ export class RESTClient { this.httpClient.interceptors.request.use(async config => { const updatedHeaders = { ...config.headers, - 'X-IG-API-KEY': this.apiKey, + 'X-IG-API-KEY': isAuthorization(this.apiKey) ? this.apiKey.apiKey : this.apiKey, }; const {accessToken, accountId, securityToken, clientSessionToken} = this.auth; diff --git a/src/login/LoginAPI.test.ts b/src/login/LoginAPI.test.ts index 81d31ae9..290cb981 100644 --- a/src/login/LoginAPI.test.ts +++ b/src/login/LoginAPI.test.ts @@ -90,6 +90,43 @@ describe('LoginAPI', () => { expect(switchAccountResponse.hasActiveLiveAccounts).toBe(true); expect(switchAccountResponse.trailingStopsEnabled).toBe(false); }); + + it('uses already existing credentials', async () => { + const username = 'top'; + const password = 'secret'; + + nock(APIClient.URL_DEMO) + .post(LoginAPI.URL.SESSION) + .query(true) + .reply(200, (_: string, requestBody: Record) => { + expect(requestBody.identifier).toBe(username); + expect(requestBody.password).toBe(password); + return JSON.stringify({ + accountId: 'ABC123', + clientId: '133721337', + lightstreamerEndpoint: 'https://demo-apd.marketdatasystems.com', + oauthToken: { + access_token: '6ba8e2bd-1337-40e5-9299-68f60474f986', + expires_in: '60', + refresh_token: '83c056b8-1337-46d3-821d-92a1dffd7f1e', + scope: 'profile', + token_type: 'Bearer', + }, + timezoneOffset: 1, + }); + }); + + const apiClient = new APIClient(APIClient.URL_DEMO, { + apiKey: '123', + password, + username, + }); + const getSessionToken = spyOn(apiClient.rest.login, 'getSessionToken').and.callFake(() => + Promise.resolve(true) + ); + await apiClient.rest.login.createSession(); + expect(getSessionToken).toHaveBeenCalledTimes(1); + }); }); describe('setupSessionWithToken', () => { diff --git a/src/login/LoginAPI.ts b/src/login/LoginAPI.ts index 0dfb656c..0c9f0dcc 100644 --- a/src/login/LoginAPI.ts +++ b/src/login/LoginAPI.ts @@ -41,15 +41,15 @@ export class LoginAPI { * @param password - Password * @see https://labs.ig.com/rest-trading-api-reference/service-detail?id=534 */ - async createSession(username: string, password: string): Promise { + async createSession(username?: string, password?: string): Promise { delete this.auth.accessToken; const resource = LoginAPI.URL.SESSION; const response = await this.apiClient.post( resource, { - identifier: username, - password, + identifier: this.auth.username || username, + password: this.auth.password || password, }, { 'axios-retry': {