diff --git a/packages/cli/src/lib/api/auth.ts b/packages/cli/src/lib/api/auth.ts index 4251b7038..3e1146728 100644 --- a/packages/cli/src/lib/api/auth.ts +++ b/packages/cli/src/lib/api/auth.ts @@ -1,7 +1,9 @@ +import { retrieveApiIndex } from "@sentry/api"; import { array, nullable, object, string } from "valibot"; import { getControlSiloUrl } from "../sentry-client.js"; -import { apiRequestToRegion } from "./infrastructure.js"; +import { getSdkClientConfig } from "../sentry-sdk-client.js"; +import { unwrapValidatedResult } from "./infrastructure.js"; const AuthStatusSchema = object({ auth: nullable( @@ -14,8 +16,14 @@ const AuthStatusSchema = object({ export async function getCurrentAuthScopes(): Promise< readonly string[] | null > { - const { data } = await apiRequestToRegion(getControlSiloUrl(), "", { - schema: AuthStatusSchema, + const result = await retrieveApiIndex({ + ...getSdkClientConfig(getControlSiloUrl()), }); + const data = unwrapValidatedResult( + result, + "Failed to read token scopes", + AuthStatusSchema, + "/", + ); return data.auth?.scopes ?? null; } diff --git a/packages/cli/test/lib/api/auth.test.ts b/packages/cli/test/lib/api/auth.test.ts index af369f76d..4e91bf34e 100644 --- a/packages/cli/test/lib/api/auth.test.ts +++ b/packages/cli/test/lib/api/auth.test.ts @@ -1,53 +1,63 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; -vi.mock("../../../src/lib/api/infrastructure.js", () => ({ - apiRequestToRegion: vi.fn(), +const { retrieveApiIndexMock } = vi.hoisted(() => ({ + retrieveApiIndexMock: vi.fn(), +})); + +vi.mock("@sentry/api", async (importOriginal) => ({ + ...(await importOriginal()), + retrieveApiIndex: retrieveApiIndexMock, +})); + +vi.mock("../../../src/lib/sentry-client.js", async (importOriginal) => ({ + ...(await importOriginal< + typeof import("../../../src/lib/sentry-client.js") + >()), + getControlSiloUrl: () => "https://sentry.io", })); import { getCurrentAuthScopes } from "../../../src/lib/api/auth.js"; -import { apiRequestToRegion } from "../../../src/lib/api/infrastructure.js"; +import { ApiError } from "../../../src/lib/errors.js"; describe("getCurrentAuthScopes", () => { - beforeEach(() => vi.mocked(apiRequestToRegion).mockReset()); + beforeEach(() => retrieveApiIndexMock.mockReset()); test("reads effective token scopes from the API index", async () => { - vi.mocked(apiRequestToRegion).mockResolvedValue({ - data: { auth: { scopes: ["org:read", "team:admin"] } }, - headers: new Headers(), + retrieveApiIndexMock.mockResolvedValue({ + data: { + version: "0", + auth: { scopes: ["org:read", "team:admin"] }, + user: null, + }, + response: new Response(), }); await expect(getCurrentAuthScopes()).resolves.toEqual([ "org:read", "team:admin", ]); - expect(apiRequestToRegion).toHaveBeenCalledWith( - expect.any(String), - "", - expect.objectContaining({ schema: expect.any(Object) }), + expect(retrieveApiIndexMock).toHaveBeenCalledWith( + expect.objectContaining({ baseUrl: "https://sentry.io" }), ); }); test("returns null when the API index reports no authenticated token", async () => { - vi.mocked(apiRequestToRegion).mockResolvedValue({ - data: { auth: null }, - headers: new Headers(), + retrieveApiIndexMock.mockResolvedValue({ + data: { version: "0", auth: null, user: null }, + response: new Response(), }); await expect(getCurrentAuthScopes()).resolves.toBeNull(); }); - test("preserves a 401 from an invalid bearer", async () => { - const error = new Error("401 Unauthorized"); - vi.mocked(apiRequestToRegion).mockImplementationOnce(async () => { - throw error; + test("reports a 401 from an invalid bearer as an ApiError", async () => { + retrieveApiIndexMock.mockResolvedValue({ + error: { detail: "Invalid token" }, + response: new Response(null, { status: 401 }), }); - let thrown: unknown; - try { - await getCurrentAuthScopes(); - } catch (caught) { - thrown = caught; - } - expect(thrown).toBe(error); + const thrown = await getCurrentAuthScopes().catch((error) => error); + expect(thrown).toBeInstanceOf(ApiError); + expect(thrown).toMatchObject({ status: 401 }); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c3b6c713..8aa8a981f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,8 +43,8 @@ catalogs: specifier: ^1.2.3 version: 1.2.3 '@sentry/api': - specifier: 1.53.0 - version: 1.53.0 + specifier: 1.55.0 + version: 1.55.0 '@sentry/cloudflare': specifier: 11.6.0 version: 11.6.0 @@ -513,7 +513,7 @@ importers: version: 2.1.5(hono@4.13.13) '@sentry/api': specifier: 'catalog:' - version: 1.53.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) + version: 1.55.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) '@sentry/core': specifier: 'catalog:' version: 11.6.0 @@ -865,7 +865,7 @@ importers: version: 2.4.0 '@sentry/api': specifier: 'catalog:' - version: 1.53.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) + version: 1.55.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) '@sentry/core': specifier: 'catalog:' version: 11.6.0 @@ -1048,7 +1048,7 @@ importers: dependencies: '@sentry/api': specifier: 'catalog:' - version: 1.53.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) + version: 1.55.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) '@sentry/search-syntax': specifier: 'catalog:' version: 1.0.0 @@ -4275,8 +4275,8 @@ packages: '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} - '@sentry/api@1.53.0': - resolution: {integrity: sha512-xAQ6q/BZSOULvs3BHeRY99sEZkVFdaINRKE6a2s3KIjLRfjK/bniaNv9jKNiLFIHLH0NG+k7kDmdyKhl1+u5mQ==} + '@sentry/api@1.55.0': + resolution: {integrity: sha512-3gTO9kJrJzWOHUBMn+5snq82acD429v8fSasA7Bw3ThtJk1Q+DM3ePMmw/dpoei80OVhAYxN3e1cdep3g4OF8g==} engines: {node: '>=22'} peerDependencies: typescript: '*' @@ -12112,7 +12112,7 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@sentry/api@1.53.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7)': + '@sentry/api@1.55.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7)': dependencies: http-cache-semantics: 4.2.0 p-limit: 7.3.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 133ce1bd1..88b4007c0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -51,7 +51,7 @@ catalog: "@modelcontextprotocol/server": 2.4.0 "@radix-ui/react-accordion": ^1.2.20 "@radix-ui/react-slot": ^1.2.3 - "@sentry/api": 1.53.0 + "@sentry/api": 1.55.0 "@sentry/cloudflare": 11.6.0 "@sentry/core": 11.6.0 "@sentry/node": 11.6.0 @@ -168,7 +168,7 @@ minimumReleaseAgeExclude: - "@oxlint/binding-win32-x64-msvc@1.87.0" - oxfmt@0.72.0 - oxlint@1.87.0 - - "@sentry/api@1.53.0" + - "@sentry/api@1.55.0" - miniflare@5.20261101.0-beta - wrangler@4.156.1 - "@sentry/symbolic@14.0.0-alpha.5"