( sessionId: string, accessToken: string, orgUUID: string, )
| 289 | * 50k is gone; we loop. |
| 290 | */ |
| 291 | export async function getTeleportEvents( |
| 292 | sessionId: string, |
| 293 | accessToken: string, |
| 294 | orgUUID: string, |
| 295 | ): Promise<Entry[] | null> { |
| 296 | const baseUrl = `${getOauthConfig().BASE_API_URL}/v1/code/sessions/${sessionId}/teleport-events` |
| 297 | const headers = { |
| 298 | ...getOAuthHeaders(accessToken), |
| 299 | 'x-organization-uuid': orgUUID, |
| 300 | } |
| 301 | |
| 302 | logForDebugging(`[teleport] Fetching events from: ${baseUrl}`) |
| 303 | |
| 304 | const all: Entry[] = [] |
| 305 | let cursor: string | undefined |
| 306 | let pages = 0 |
| 307 | |
| 308 | // Infinite-loop guard: 1000/page × 100 pages = 100k events. Larger than |
| 309 | // session-ingress's 50k one-shot. If we hit this, something's wrong |
| 310 | // (server not advancing cursor) — bail rather than hang. |
| 311 | const maxPages = 100 |
| 312 | |
| 313 | while (pages < maxPages) { |
| 314 | const params: Record<string, string | number> = { limit: 1000 } |
| 315 | if (cursor !== undefined) { |
| 316 | params.cursor = cursor |
| 317 | } |
| 318 | |
| 319 | let response |
| 320 | try { |
| 321 | response = await axios.get<TeleportEventsResponse>(baseUrl, { |
| 322 | headers, |
| 323 | params, |
| 324 | timeout: 20000, |
| 325 | validateStatus: status => status < 500, |
| 326 | }) |
| 327 | } catch (e) { |
| 328 | const err = e as AxiosError |
| 329 | logError(new Error(`Teleport events fetch failed: ${err.message}`)) |
| 330 | logForDiagnosticsNoPII('error', 'teleport_events_fetch_fail') |
| 331 | return null |
| 332 | } |
| 333 | |
| 334 | if (response.status === 404) { |
| 335 | // 404 on page 0 is ambiguous during the migration window: |
| 336 | // (a) Session genuinely not found (not in Spanner AND not in |
| 337 | // threadstore) — nothing to fetch. |
| 338 | // (b) Route-level 404: endpoint not deployed yet, or session is |
| 339 | // a threadstore session not yet backfilled into Spanner. |
| 340 | // We can't tell them apart from the response alone. Returning null |
| 341 | // lets the caller fall back to session-ingress, which will correctly |
| 342 | // return empty for case (a) and data for case (b). Once the backfill |
| 343 | // is complete and session-ingress is gone, the fallback also returns |
| 344 | // null → same "Failed to fetch session logs" error as today. |
| 345 | // |
| 346 | // 404 mid-pagination (pages > 0) means session was deleted between |
| 347 | // pages — return what we have. |
| 348 | logForDebugging( |
no test coverage detected