1 | |
2 |
|
3 | |
4 | * List alerts |
5 | * List alerts raised in a time window, one page at a time. Pass next_offset back as offset to fetch the next page. |
6 | */ |
7 | export async function main( |
8 | auth: RT.Netskope, |
9 | start_time: string, |
10 | end_time: string | undefined, |
11 | limit: number | undefined, |
12 | offset: number | undefined, |
13 | alert_type: string | undefined, |
14 | query: string | undefined, |
15 | fields: string | undefined |
16 | ) { |
17 | const toEpoch = (value: string) => { |
18 | const ms = Date.parse(value) |
19 | if (Number.isNaN(ms)) { |
20 | throw new Error(`Invalid date: ${value}`) |
21 | } |
22 | return Math.floor(ms / 1000).toString() |
23 | } |
24 | const pageSize = limit || 100 |
25 | if (pageSize > 10000) { |
26 | throw new Error("limit cannot exceed 10000 (datasearch page cap)") |
27 | } |
28 | const skip = offset || 0 |
29 |
|
30 | const filters: string[] = [] |
31 | if (query != null && query !== "") { |
32 | filters.push(query) |
33 | } |
34 | if (alert_type != null && alert_type !== "") { |
35 | if (!/^[a-zA-Z0-9_ -]+$/.test(alert_type)) { |
36 | throw new Error(`Invalid alert_type: ${alert_type}`) |
37 | } |
38 | filters.push(`alert_type eq "${alert_type}"`) |
39 | } |
40 |
|
41 | const url = new URL( |
42 | `${auth.tenant_url.replace(/\/+$/, "")}/api/v2/events/datasearch/alert` |
43 | ) |
44 | url.searchParams.append("starttime", toEpoch(start_time)) |
45 | url.searchParams.append( |
46 | "endtime", |
47 | end_time ? toEpoch(end_time) : Math.floor(Date.now() / 1000).toString() |
48 | ) |
49 | url.searchParams.append("limit", pageSize.toString()) |
50 | url.searchParams.append("offset", skip.toString()) |
51 | if (filters.length) { |
52 | url.searchParams.append("query", filters.join(" AND ")) |
53 | } |
54 | if (fields != null && fields !== "") { |
55 | url.searchParams.append("fields", fields) |
56 | } |
57 |
|
58 | const response = await fetch(url, { |
59 | method: "GET", |
60 | headers: { |
61 | "Netskope-Api-Token": auth.token, |
62 | Accept: "application/json", |
63 | }, |
64 | }) |
65 |
|
66 | if (!response.ok) { |
67 | throw new Error(`${response.status} ${await response.text()}`) |
68 | } |
69 |
|
70 | const page = (await response.json()) as { |
71 | result?: unknown[] |
72 | status?: { execution?: string; total?: number } |
73 | } |
74 | if (page.status?.execution === "FAILED") { |
75 | throw new Error(`Netskope query failed: ${JSON.stringify(page.status)}`) |
76 | } |
77 | const count = page.result?.length ?? 0 |
78 | const total = page.status?.total |
79 | const hasMore = total != null ? skip + count < total : count === pageSize |
80 | return { |
81 | ...page, |
82 | next_offset: hasMore && count > 0 ? skip + count : null, |
83 | } |
84 | } |
85 |
|