laplace-api 5.3.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/client/news.ts +62 -30
- package/src/test/news.test.ts +138 -31
package/package.json
CHANGED
package/src/client/news.ts
CHANGED
|
@@ -25,7 +25,16 @@ export enum NewsOrderBy {
|
|
|
25
25
|
QUALITY_SCORE = "quality_score",
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export
|
|
28
|
+
export enum NewsLane {
|
|
29
|
+
GLOBAL_MACRO = "global_macro",
|
|
30
|
+
TR_EKONOMI = "tr_ekonomi",
|
|
31
|
+
BIST = "bist",
|
|
32
|
+
FAST_MOVERS = "fast_movers",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface GetNewsParams {
|
|
36
|
+
lane?: NewsLane;
|
|
37
|
+
apiSource?: string;
|
|
29
38
|
newsType?: NewsType;
|
|
30
39
|
orderBy?: NewsOrderBy;
|
|
31
40
|
orderByDirection?: SortDirection;
|
|
@@ -41,6 +50,9 @@ export interface GetNewsV2Params {
|
|
|
41
50
|
size?: number;
|
|
42
51
|
}
|
|
43
52
|
|
|
53
|
+
/** @deprecated Use {@link GetNewsParams}; v1 and v2 now accept the same filters. */
|
|
54
|
+
export type GetNewsV2Params = GetNewsParams;
|
|
55
|
+
|
|
44
56
|
export interface News {
|
|
45
57
|
url: string;
|
|
46
58
|
imageUrl: string;
|
|
@@ -102,6 +114,16 @@ export interface NewsCategory {
|
|
|
102
114
|
name: string;
|
|
103
115
|
}
|
|
104
116
|
|
|
117
|
+
export interface NewsLaneInfo {
|
|
118
|
+
id: NewsLane;
|
|
119
|
+
label: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface NewsApiSource {
|
|
123
|
+
id: string;
|
|
124
|
+
name: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
105
127
|
export class NewsClient extends Client {
|
|
106
128
|
async getHighlights(
|
|
107
129
|
region: Region,
|
|
@@ -128,42 +150,30 @@ export class NewsClient extends Client {
|
|
|
128
150
|
});
|
|
129
151
|
}
|
|
130
152
|
|
|
131
|
-
async
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
orderBy?: NewsOrderBy,
|
|
138
|
-
orderByDirection?: SortDirection,
|
|
139
|
-
extraFilters?: string
|
|
140
|
-
): Promise<PaginatedResponse<News>> {
|
|
141
|
-
const params = {
|
|
142
|
-
region,
|
|
143
|
-
locale,
|
|
144
|
-
...(newsType != null && { newsType }),
|
|
145
|
-
...(page != null && { page }),
|
|
146
|
-
...(size != null && { size }),
|
|
147
|
-
...(orderBy != null && { orderBy }),
|
|
148
|
-
...(orderByDirection != null && { orderByDirection }),
|
|
149
|
-
...(extraFilters != null && { extraFilters }),
|
|
150
|
-
};
|
|
153
|
+
async getNewsLanes(): Promise<NewsLaneInfo[]> {
|
|
154
|
+
return this.sendRequest<NewsLaneInfo[]>({
|
|
155
|
+
method: "GET",
|
|
156
|
+
url: "/api/v1/news/lanes",
|
|
157
|
+
});
|
|
158
|
+
}
|
|
151
159
|
|
|
152
|
-
|
|
160
|
+
async getApiSourceNames(): Promise<NewsApiSource[]> {
|
|
161
|
+
return this.sendRequest<NewsApiSource[]>({
|
|
153
162
|
method: "GET",
|
|
154
|
-
url: "/api/v1/news",
|
|
155
|
-
params,
|
|
163
|
+
url: "/api/v1/news/api-source-names",
|
|
156
164
|
});
|
|
157
165
|
}
|
|
158
166
|
|
|
159
|
-
|
|
167
|
+
private buildNewsFilterParams(
|
|
160
168
|
region: Region,
|
|
161
169
|
locale: Locale,
|
|
162
|
-
options?:
|
|
163
|
-
):
|
|
164
|
-
|
|
170
|
+
options?: GetNewsParams
|
|
171
|
+
): Record<string, unknown> {
|
|
172
|
+
return {
|
|
165
173
|
region,
|
|
166
174
|
locale,
|
|
175
|
+
...(options?.lane != null && { lane: options.lane }),
|
|
176
|
+
...(options?.apiSource != null && { apiSource: options.apiSource }),
|
|
167
177
|
...(options?.newsType != null && { newsType: options.newsType }),
|
|
168
178
|
...(options?.orderBy != null && { orderBy: options.orderBy }),
|
|
169
179
|
...(options?.orderByDirection != null && {
|
|
@@ -186,11 +196,29 @@ export class NewsClient extends Client {
|
|
|
186
196
|
...(options?.page != null && { page: options.page }),
|
|
187
197
|
...(options?.size != null && { size: options.size }),
|
|
188
198
|
};
|
|
199
|
+
}
|
|
189
200
|
|
|
201
|
+
async getNews(
|
|
202
|
+
region: Region,
|
|
203
|
+
locale: Locale,
|
|
204
|
+
options?: GetNewsParams
|
|
205
|
+
): Promise<PaginatedResponse<News>> {
|
|
206
|
+
return this.sendRequest<PaginatedResponse<News>>({
|
|
207
|
+
method: "GET",
|
|
208
|
+
url: "/api/v1/news",
|
|
209
|
+
params: this.buildNewsFilterParams(region, locale, options),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async getNewsV2(
|
|
214
|
+
region: Region,
|
|
215
|
+
locale: Locale,
|
|
216
|
+
options?: GetNewsParams
|
|
217
|
+
): Promise<PaginatedResponse<NewsV2>> {
|
|
190
218
|
return this.sendRequest<PaginatedResponse<NewsV2>>({
|
|
191
219
|
method: "GET",
|
|
192
220
|
url: "/api/v2/news",
|
|
193
|
-
params,
|
|
221
|
+
params: this.buildNewsFilterParams(region, locale, options),
|
|
194
222
|
});
|
|
195
223
|
}
|
|
196
224
|
|
|
@@ -200,9 +228,13 @@ export class NewsClient extends Client {
|
|
|
200
228
|
sectors?: string[],
|
|
201
229
|
tickers?: string[],
|
|
202
230
|
categories?: string[],
|
|
203
|
-
industries?: string[]
|
|
231
|
+
industries?: string[],
|
|
232
|
+
lane?: NewsLane,
|
|
233
|
+
apiSource?: string[]
|
|
204
234
|
): { events: AsyncIterable<NewsV2[]>, cancel: () => void } {
|
|
205
235
|
let url = `${this["baseUrl"]}/api/v1/news/stream?locale=${locale}®ion=${region}`;
|
|
236
|
+
if (lane != null) url += `&lane=${encodeURIComponent(lane)}`;
|
|
237
|
+
if (apiSource?.length) url += `&apiSource=${encodeURIComponent(apiSource.join(","))}`;
|
|
206
238
|
if (sectors?.length) url += `§ors=${encodeURIComponent(sectors.join(","))}`;
|
|
207
239
|
if (tickers?.length) url += `&tickers=${encodeURIComponent(tickers.join(","))}`;
|
|
208
240
|
if (categories?.length) url += `&categories=${encodeURIComponent(categories.join(","))}`;
|
package/src/test/news.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
NewsClient,
|
|
6
6
|
NewsType,
|
|
7
7
|
NewsOrderBy,
|
|
8
|
+
NewsLane,
|
|
8
9
|
} from "../client/news";
|
|
9
10
|
import "./client_test_suite";
|
|
10
11
|
import { Region, Locale } from "../client/collections";
|
|
@@ -41,6 +42,13 @@ const mockNewsCategoriesResponse = [
|
|
|
41
42
|
{ id: "13705", name: "Stock Spesific News" }
|
|
42
43
|
];
|
|
43
44
|
|
|
45
|
+
const mockNewsLanesResponse = [
|
|
46
|
+
{ id: "global_macro", label: "Global Macro" },
|
|
47
|
+
{ id: "tr_ekonomi", label: "TR Ekonomi" },
|
|
48
|
+
{ id: "bist", label: "BIST" },
|
|
49
|
+
{ id: "fast_movers", label: "Fast Movers" }
|
|
50
|
+
];
|
|
51
|
+
|
|
44
52
|
const mockNewsResponse = {
|
|
45
53
|
items: [
|
|
46
54
|
{
|
|
@@ -125,16 +133,36 @@ describe("NewsClient", () => {
|
|
|
125
133
|
expect(typeof c.name).toBe("string");
|
|
126
134
|
});
|
|
127
135
|
|
|
136
|
+
test("getNewsLanes returns valid data", async () => {
|
|
137
|
+
const resp = await client.getNewsLanes();
|
|
138
|
+
|
|
139
|
+
expect(Array.isArray(resp)).toBe(true);
|
|
140
|
+
if (resp.length > 0) {
|
|
141
|
+
const l = resp[0];
|
|
142
|
+
expect(typeof l.id).toBe("string");
|
|
143
|
+
expect(typeof l.label).toBe("string");
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("getApiSourceNames returns valid data", async () => {
|
|
148
|
+
const resp = await client.getApiSourceNames();
|
|
149
|
+
|
|
150
|
+
expect(Array.isArray(resp)).toBe(true);
|
|
151
|
+
if (resp.length > 0) {
|
|
152
|
+
const s = resp[0];
|
|
153
|
+
expect(typeof s.id).toBe("string");
|
|
154
|
+
expect(typeof s.name).toBe("string");
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
128
158
|
test("getNews returns valid paginated data", async () => {
|
|
129
|
-
const resp = await client.getNews(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
SortDirection.Desc
|
|
137
|
-
);
|
|
159
|
+
const resp = await client.getNews(Region.Us, Locale.Tr, {
|
|
160
|
+
newsType: NewsType.BRIEFS,
|
|
161
|
+
page: 0,
|
|
162
|
+
size: 10,
|
|
163
|
+
orderBy: NewsOrderBy.TIMESTAMP,
|
|
164
|
+
orderByDirection: SortDirection.Desc,
|
|
165
|
+
});
|
|
138
166
|
|
|
139
167
|
expect(resp).toBeDefined();
|
|
140
168
|
expect(typeof resp.recordCount).toBe("number");
|
|
@@ -352,20 +380,87 @@ describe("NewsClient", () => {
|
|
|
352
380
|
});
|
|
353
381
|
});
|
|
354
382
|
|
|
383
|
+
describe("getNewsLanes", () => {
|
|
384
|
+
test("calls correct endpoint and matches raw response", async () => {
|
|
385
|
+
cli.request.mockResolvedValueOnce({ data: mockNewsLanesResponse });
|
|
386
|
+
|
|
387
|
+
const resp = await client.getNewsLanes();
|
|
388
|
+
|
|
389
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
390
|
+
const call = cli.request.mock.calls[0][0];
|
|
391
|
+
|
|
392
|
+
expect(call.method).toBe("GET");
|
|
393
|
+
expect(call.url).toBe("/api/v1/news/lanes");
|
|
394
|
+
|
|
395
|
+
expect(resp).toEqual(mockNewsLanesResponse);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("bubbles up request error", async () => {
|
|
399
|
+
cli.request.mockRejectedValueOnce(new Error("Failed to fetch lanes"));
|
|
400
|
+
|
|
401
|
+
await expect(client.getNewsLanes()).rejects.toThrow(
|
|
402
|
+
"Failed to fetch lanes"
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
describe("getApiSourceNames", () => {
|
|
410
|
+
const mockApiSourceNames = [
|
|
411
|
+
{ id: "BBCBusiness", name: "BBC Business" },
|
|
412
|
+
{ id: "MarketWatch", name: "MarketWatch" },
|
|
413
|
+
{ id: "GazeteOksijen", name: "Gazete Oksijen" }
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
test("calls correct endpoint and matches raw response", async () => {
|
|
417
|
+
cli.request.mockResolvedValueOnce({ data: mockApiSourceNames });
|
|
418
|
+
|
|
419
|
+
const resp = await client.getApiSourceNames();
|
|
420
|
+
|
|
421
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
422
|
+
const call = cli.request.mock.calls[0][0];
|
|
423
|
+
|
|
424
|
+
expect(call.method).toBe("GET");
|
|
425
|
+
expect(call.url).toBe("/api/v1/news/api-source-names");
|
|
426
|
+
|
|
427
|
+
expect(resp).toEqual(mockApiSourceNames);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test("bubbles up request error", async () => {
|
|
431
|
+
cli.request.mockRejectedValueOnce(
|
|
432
|
+
new Error("Failed to fetch api source names")
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
await expect(client.getApiSourceNames()).rejects.toThrow(
|
|
436
|
+
"Failed to fetch api source names"
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
355
443
|
describe("getNews", () => {
|
|
356
444
|
test("calls correct endpoint/params and matches raw response", async () => {
|
|
357
445
|
cli.request.mockResolvedValueOnce({ data: mockNewsResponse });
|
|
358
446
|
|
|
359
|
-
const resp = await client.getNews(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
NewsType.BRIEFS,
|
|
363
|
-
1,
|
|
364
|
-
10,
|
|
365
|
-
NewsOrderBy.TIMESTAMP,
|
|
366
|
-
SortDirection.Desc,
|
|
367
|
-
|
|
368
|
-
|
|
447
|
+
const resp = await client.getNews(Region.Tr, Locale.Tr, {
|
|
448
|
+
lane: NewsLane.BIST,
|
|
449
|
+
apiSource: "BBCBusiness,MarketWatch",
|
|
450
|
+
newsType: NewsType.BRIEFS,
|
|
451
|
+
page: 1,
|
|
452
|
+
size: 10,
|
|
453
|
+
orderBy: NewsOrderBy.TIMESTAMP,
|
|
454
|
+
orderByDirection: SortDirection.Desc,
|
|
455
|
+
symbols: "AAPL,MSFT",
|
|
456
|
+
categories: "Sector News",
|
|
457
|
+
sectors: "Technology",
|
|
458
|
+
industries: "Software",
|
|
459
|
+
qualityScoreMin: 7,
|
|
460
|
+
qualityScoreMax: 10,
|
|
461
|
+
timestampFrom: "2026-05-01",
|
|
462
|
+
timestampTo: "2026-06-01",
|
|
463
|
+
});
|
|
369
464
|
|
|
370
465
|
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
371
466
|
const call = cli.request.mock.calls[0][0];
|
|
@@ -375,11 +470,21 @@ describe("NewsClient", () => {
|
|
|
375
470
|
expect(call.params).toEqual({
|
|
376
471
|
region: Region.Tr,
|
|
377
472
|
locale: Locale.Tr,
|
|
473
|
+
lane: NewsLane.BIST,
|
|
474
|
+
apiSource: "BBCBusiness,MarketWatch",
|
|
378
475
|
newsType: NewsType.BRIEFS,
|
|
379
476
|
page: 1,
|
|
380
477
|
size: 10,
|
|
381
478
|
orderBy: NewsOrderBy.TIMESTAMP,
|
|
382
|
-
orderByDirection: SortDirection.Desc
|
|
479
|
+
orderByDirection: SortDirection.Desc,
|
|
480
|
+
symbols: "AAPL,MSFT",
|
|
481
|
+
categories: "Sector News",
|
|
482
|
+
sectors: "Technology",
|
|
483
|
+
industries: "Software",
|
|
484
|
+
qualityScoreMin: 7,
|
|
485
|
+
qualityScoreMax: 10,
|
|
486
|
+
timestampFrom: "2026-05-01",
|
|
487
|
+
timestampTo: "2026-06-01",
|
|
383
488
|
});
|
|
384
489
|
|
|
385
490
|
expect(resp.recordCount).toBe(352);
|
|
@@ -439,15 +544,13 @@ describe("NewsClient", () => {
|
|
|
439
544
|
cli.request.mockRejectedValueOnce(new Error("Failed to fetch news"));
|
|
440
545
|
|
|
441
546
|
await expect(
|
|
442
|
-
client.getNews(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
SortDirection.Desc
|
|
450
|
-
)
|
|
547
|
+
client.getNews(Region.Tr, Locale.Tr, {
|
|
548
|
+
newsType: NewsType.REUTERS,
|
|
549
|
+
page: 0,
|
|
550
|
+
size: 10,
|
|
551
|
+
orderBy: NewsOrderBy.TIMESTAMP,
|
|
552
|
+
orderByDirection: SortDirection.Desc,
|
|
553
|
+
})
|
|
451
554
|
).rejects.toThrow("Failed to fetch news");
|
|
452
555
|
|
|
453
556
|
expect(cli.request).toHaveBeenCalledTimes(1);
|
|
@@ -459,6 +562,8 @@ describe("NewsClient", () => {
|
|
|
459
562
|
cli.request.mockResolvedValueOnce({ data: mockNewsV2Response });
|
|
460
563
|
|
|
461
564
|
const resp = await client.getNewsV2(Region.Tr, Locale.Tr, {
|
|
565
|
+
lane: NewsLane.GLOBAL_MACRO,
|
|
566
|
+
apiSource: "BBCBusiness,MarketWatch",
|
|
462
567
|
newsType: NewsType.BRIEFS,
|
|
463
568
|
page: 1,
|
|
464
569
|
size: 10,
|
|
@@ -482,6 +587,8 @@ describe("NewsClient", () => {
|
|
|
482
587
|
expect(call.params).toEqual({
|
|
483
588
|
region: Region.Tr,
|
|
484
589
|
locale: Locale.Tr,
|
|
590
|
+
lane: NewsLane.GLOBAL_MACRO,
|
|
591
|
+
apiSource: "BBCBusiness,MarketWatch",
|
|
485
592
|
newsType: NewsType.BRIEFS,
|
|
486
593
|
page: 1,
|
|
487
594
|
size: 10,
|
|
@@ -587,7 +694,7 @@ describe("NewsClient", () => {
|
|
|
587
694
|
data: mockAsyncIterator
|
|
588
695
|
});
|
|
589
696
|
|
|
590
|
-
const { events, cancel } = client.streamNews(Region.Us, Locale.En, ["tech"], ["AAPL"], ["category"], ["software"]);
|
|
697
|
+
const { events, cancel } = client.streamNews(Region.Us, Locale.En, ["tech"], ["AAPL"], ["category"], ["software"], NewsLane.GLOBAL_MACRO, ["BBCBusiness"]);
|
|
591
698
|
|
|
592
699
|
for await (const _ of events) {
|
|
593
700
|
break;
|
|
@@ -595,7 +702,7 @@ describe("NewsClient", () => {
|
|
|
595
702
|
|
|
596
703
|
expect(axiosGetSpy).toHaveBeenCalledTimes(1);
|
|
597
704
|
const callArgs = axiosGetSpy.mock.calls[0];
|
|
598
|
-
expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=en®ion=us§ors=tech&tickers=AAPL&categories=category&industries=software`);
|
|
705
|
+
expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=en®ion=us&lane=global_macro&apiSource=BBCBusiness§ors=tech&tickers=AAPL&categories=category&industries=software`);
|
|
599
706
|
|
|
600
707
|
cancel();
|
|
601
708
|
axiosGetSpy.mockRestore();
|