Skip to content

Commit

Permalink
feat(login): Support login credentials in constructor (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
bennycode committed May 10, 2021
1 parent 4aa818a commit 119b153
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 7 deletions.
4 changes: 2 additions & 2 deletions 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 {
Expand All @@ -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);
}
Expand Down
14 changes: 12 additions & 2 deletions src/client/RESTClient.ts
Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
37 changes: 37 additions & 0 deletions src/login/LoginAPI.test.ts
Expand Up @@ -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<string, any>) => {
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<LoginAPI>(apiClient.rest.login, 'getSessionToken').and.callFake(() =>
Promise.resolve(true)
);
await apiClient.rest.login.createSession();
expect(getSessionToken).toHaveBeenCalledTimes(1);
});
});

describe('setupSessionWithToken', () => {
Expand Down
6 changes: 3 additions & 3 deletions src/login/LoginAPI.ts
Expand Up @@ -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<TradingSession> {
async createSession(username?: string, password?: string): Promise<TradingSession> {
delete this.auth.accessToken;

const resource = LoginAPI.URL.SESSION;
const response = await this.apiClient.post<TradingSession>(
resource,
{
identifier: username,
password,
identifier: this.auth.username || username,
password: this.auth.password || password,
},
{
'axios-retry': {
Expand Down

0 comments on commit 119b153

Please sign in to comment.