Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/rest/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ export interface RestClientOptions {
export abstract class RestClient {
constructor(protected readonly options: RestClientOptions) {}

/**
* The prefix every request from this client is built on, fully resolved —
* host, version segment and product. Endpoints are appended to it.
*
* The version segment is chosen by the SDK rather than written by the
* caller, so this is the only way to see what a client actually resolved to.
*/
public get baseUrl(): string {
return this.options.baseUrl;
}

protected request = async (endpoint: string, params?: Record<string, any>) => {
const url = queryString.stringifyUrl({ url: `${this.options.baseUrl}/${endpoint}`, query: params });

Expand Down
12 changes: 12 additions & 0 deletions src/websocket/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ export class WebSocketClient extends events.EventEmitter {
super();
}

/**
* The endpoint this client connects to, fully resolved — host, version
* segment, product and `/streaming`.
*
* The version segment is chosen by the SDK from the `version` option rather
* than written by the caller, so this is the only way to see which version a
* client actually ended up on.
*/
public get url(): string {
return this.options.url;
}

public connect() {
this.socket = new WebSocket(this.options.url);
this.socket.onopen = () => this.emit(CONNECT_EVENT);
Expand Down
21 changes: 7 additions & 14 deletions test/rest-client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,14 @@ describe('RestClient', () => {
const client = new RestClient({ apiKey: 'api-key', baseUrl: 'https://custom-api.example.com' });
const stock = client.stock;
expect(stock).toBeInstanceOf(RestStockClient);
// @ts-ignore - accessing private property for testing
expect(stock.options.baseUrl).toBe('https://custom-api.example.com/v1.0/stock');
expect(stock.baseUrl).toBe('https://custom-api.example.com/v1.0/stock');
});

it('should use custom baseUrl for futopt client', () => {
const client = new RestClient({ apiKey: 'api-key', baseUrl: 'https://custom-api.example.com' });
const futopt = client.futopt;
expect(futopt).toBeInstanceOf(RestFutOptClient);
// @ts-ignore - accessing private property for testing
expect(futopt.options.baseUrl).toBe('https://custom-api.example.com/v1.0/futopt');
expect(futopt.baseUrl).toBe('https://custom-api.example.com/v1.0/futopt');
});

describe('.intraday', () => {
Expand Down Expand Up @@ -931,33 +929,28 @@ describe('RestClient', () => {
it('should handle baseUrl without trailing slash', () => {
const client = new RestClient({ apiKey: 'api-key', baseUrl: 'https://api.example.com/marketdata' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.baseUrl).toBe('https://api.example.com/marketdata/v1.0/stock');
expect(stock.baseUrl).toBe('https://api.example.com/marketdata/v1.0/stock');
});

it('should handle baseUrl with single trailing slash', () => {
const client = new RestClient({ apiKey: 'api-key', baseUrl: 'https://api.example.com/marketdata/' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.baseUrl).toBe('https://api.example.com/marketdata/v1.0/stock');
expect(stock.baseUrl).toBe('https://api.example.com/marketdata/v1.0/stock');
});

it('should handle baseUrl with multiple trailing slashes', () => {
const client = new RestClient({ apiKey: 'api-key', baseUrl: 'https://api.example.com/marketdata///' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.baseUrl).toBe('https://api.example.com/marketdata/v1.0/stock');
expect(stock.baseUrl).toBe('https://api.example.com/marketdata/v1.0/stock');
});

it('should treat a path segment that is not a vX.Y version as part of the prefix', () => {
const client = new RestClient({ apiKey: 'api-key', baseUrl: 'https://api.example.com/api/v2/' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.baseUrl).toBe('https://api.example.com/api/v2/v1.0/stock');
expect(stock.baseUrl).toBe('https://api.example.com/api/v2/v1.0/stock');

const futopt = client.futopt;
// @ts-ignore - accessing private property for testing
expect(futopt.options.baseUrl).toBe('https://api.example.com/api/v2/v1.0/futopt');
expect(futopt.baseUrl).toBe('https://api.example.com/api/v2/v1.0/futopt');
});

it('should reject a baseUrl carrying its own version segment', () => {
Expand Down
24 changes: 8 additions & 16 deletions test/websocket-client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,14 @@ describe('WebSocketClient', () => {
const client = new WebSocketClient({ apiKey: 'api-key', baseUrl: 'wss://custom-ws.example.com' });
const stock = client.stock;
expect(stock).toBeInstanceOf(WebSocketStockClient);
// @ts-ignore - accessing private property for testing
expect(stock.options.url).toBe('wss://custom-ws.example.com/v1.0/stock/streaming');
expect(stock.url).toBe('wss://custom-ws.example.com/v1.0/stock/streaming');
});

it('should use custom baseUrl for futopt client', () => {
const client = new WebSocketClient({ apiKey: 'api-key', baseUrl: 'wss://custom-ws.example.com' });
const futopt = client.futopt;
expect(futopt).toBeInstanceOf(WebSocketFutOptClient);
// @ts-ignore - accessing private property for testing
expect(futopt.options.url).toBe('wss://custom-ws.example.com/v1.1/futopt/streaming');
expect(futopt.url).toBe('wss://custom-ws.example.com/v1.1/futopt/streaming');
});

describe('.connect()', () => {
Expand Down Expand Up @@ -469,39 +467,33 @@ describe('WebSocketClient', () => {
it('should handle baseUrl without trailing slash', () => {
const client = new WebSocketClient({ apiKey: 'api-key', baseUrl: 'wss://ws.example.com/marketdata' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.url).toBe('wss://ws.example.com/marketdata/v1.0/stock/streaming');
expect(stock.url).toBe('wss://ws.example.com/marketdata/v1.0/stock/streaming');
});

it('should handle baseUrl with single trailing slash', () => {
const client = new WebSocketClient({ apiKey: 'api-key', baseUrl: 'wss://ws.example.com/marketdata/' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.url).toBe('wss://ws.example.com/marketdata/v1.0/stock/streaming');
expect(stock.url).toBe('wss://ws.example.com/marketdata/v1.0/stock/streaming');
});

it('should handle baseUrl with multiple trailing slashes', () => {
const client = new WebSocketClient({ apiKey: 'api-key', baseUrl: 'wss://ws.example.com/marketdata///' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.url).toBe('wss://ws.example.com/marketdata/v1.0/stock/streaming');
expect(stock.url).toBe('wss://ws.example.com/marketdata/v1.0/stock/streaming');
});

it('should treat a path segment that is not a vX.Y version as part of the prefix', () => {
const client = new WebSocketClient({ apiKey: 'api-key', baseUrl: 'wss://ws.example.com/api/v2/' });
const stock = client.stock;
// @ts-ignore - accessing private property for testing
expect(stock.options.url).toBe('wss://ws.example.com/api/v2/v1.0/stock/streaming');
expect(stock.url).toBe('wss://ws.example.com/api/v2/v1.0/stock/streaming');

const futopt = client.futopt;
// @ts-ignore - accessing private property for testing
expect(futopt.options.url).toBe('wss://ws.example.com/api/v2/v1.1/futopt/streaming');
expect(futopt.url).toBe('wss://ws.example.com/api/v2/v1.1/futopt/streaming');
});
});

describe('streaming version', () => {
// @ts-ignore - accessing private property for testing
const urlOf = (client: WebSocketClient, product: 'stock' | 'futopt') => client[product].options.url;
const urlOf = (client: WebSocketClient, product: 'stock' | 'futopt') => client[product].url;

it('should default each product to its latest version', () => {
const client = new WebSocketClient({ apiKey: 'api-key' });
Expand Down
Loading