From 277f9e0074f5106b32685adf1508c5ab3ce6c845 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Sat, 7 Nov 2026 02:19:09 +0000 Subject: [PATCH] ref(cli): Bump @sentry/api to 1.46.0 and move unblocked call sites @sentry/api 1.46.0 carries getsentry/sentry#130348, #130349 and #130350, so these calls now match their generated operations: - issue status update: updateOrganizationIssue - issue merge: updateOrganizationIssues with numeric ids and merge: true - dashboard widget and explore time series: listOrganizationEventsTimeseries - agent conversations list with sort: listOrganizationAiConversations - trace detail with timestamp: getOrganizationTrace Responses are still checked with the CLI's own schemas where they were before. The regenerated issue skill reference picks up new field docs. --- .../skills/sentry-cli/references/issue.md | 6 +- packages/cli/src/lib/api/conversations.ts | 102 ++++++++--------- packages/cli/src/lib/api/dashboards.ts | 56 +++++----- packages/cli/src/lib/api/explore.ts | 45 ++++---- packages/cli/src/lib/api/issues.ts | 105 ++++++++---------- packages/cli/src/lib/api/traces.ts | 24 ++-- .../cli/test/lib/api-client.coverage.test.ts | 32 ++++++ packages/cli/test/lib/api/dashboards.test.ts | 77 ++++++------- pnpm-lock.yaml | 16 +-- pnpm-workspace.yaml | 4 +- 10 files changed, 236 insertions(+), 231 deletions(-) diff --git a/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/issue.md b/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/issue.md index 864b99df6..71964f067 100644 --- a/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/issue.md +++ b/packages/cli/plugins/sentry-cli/skills/sentry-cli/references/issue.md @@ -173,11 +173,11 @@ Show the top values of an issue tag | Field | Type | Description | |-------|------|-------------| -| `uniqueValues` | number \| null | | +| `uniqueValues` | number \| null | Number of distinct values seen for the tag key | | `totalValues` | number \| null | Total retained events, including missing tag values; not a lifetime count | | `topValues` | array \| null | Up to 9 values ordered by count, with nullable counts and first/last seen timestamps | -| `key` | string | | -| `name` | string | | +| `key` | string | Tag key, with the `sentry:` prefix removed from built-in keys | +| `name` | string | Display label of the tag key | **Examples:** diff --git a/packages/cli/src/lib/api/conversations.ts b/packages/cli/src/lib/api/conversations.ts index d2504c4e4..4a2cbd5be 100644 --- a/packages/cli/src/lib/api/conversations.ts +++ b/packages/cli/src/lib/api/conversations.ts @@ -4,14 +4,17 @@ * Functions for listing and retrieving agent conversation data from the Sentry * Explore agent conversations endpoints. * - * Both endpoints are in `@sentry/api`. The list still goes through - * `apiRequestToRegion` because its generated query has no `sort`. The details - * call uses the generated operation; its spans are untyped records there, so - * the response is still checked with `AgentConversationDetailsSchema`. + * Both endpoints use their generated `@sentry/api` operations, and their + * responses are still checked with the CLI's own schemas + * (`ConversationListItemSchema`, `AgentConversationDetailsSchema`). * Pagination follows the `Link` header. */ -import { retrieveOrganizationAiConversation } from "@sentry/api"; +import { + type ListOrganizationAiConversationsData, + listOrganizationAiConversations, + retrieveOrganizationAiConversation, +} from "@sentry/api"; import { array } from "valibot"; import { @@ -25,21 +28,30 @@ import { import { logger } from "../logger.js"; import { resolveOrgRegion } from "../region.js"; +import { getSdkClientConfig } from "../sentry-sdk-client.js"; import { API_MAX_PER_PAGE, - apiRequestToRegion, getOrgSdkConfig, MAX_PAGINATION_PAGES, type PaginatedResponse, paginate, - parseLinkHeader, unwrapPaginatedResult, validateResponse, } from "./infrastructure.js"; const log = logger.withTag("api.conversations"); +type ConversationListOptions = { + query?: string; + sort?: NonNullable["sort"]; + cursor?: string; + statsPeriod?: string; + start?: string; + end?: string; + project?: string; +}; + /** * Fetch a single page of conversations from the agent conversations endpoint. * @@ -49,51 +61,34 @@ const log = logger.withTag("api.conversations"); async function fetchConversationsPage( regionUrl: string, orgSlug: string, - options: { - query?: string; - sort?: string; - cursor?: string; - statsPeriod?: string; - start?: string; - end?: string; - project?: string; - }, + options: ConversationListOptions, perPage: number, ): Promise> { - const params: Record = { - per_page: String(perPage), - }; - if (options.statsPeriod) { - params.statsPeriod = options.statsPeriod; - } - if (options.start) { - params.start = options.start; - } - if (options.end) { - params.end = options.end; - } - if (options.cursor) { - params.cursor = options.cursor; - } - if (options.query) { - params.query = options.query; - } - if (options.sort) { - params.sort = options.sort; - } - if (options.project) { - params.project = options.project; - } - - const { data, headers } = await apiRequestToRegion( - regionUrl, + const result = await listOrganizationAiConversations({ + ...getSdkClientConfig(regionUrl), + path: { organization_id_or_slug: orgSlug }, + query: { + per_page: perPage, + statsPeriod: options.statsPeriod || undefined, + start: options.start || undefined, + end: options.end || undefined, + cursor: options.cursor || undefined, + query: options.query || undefined, + sort: options.sort, + project: options.project ? [options.project] : undefined, + }, + }); + const page = unwrapPaginatedResult( + result, + "Failed to list agent conversations", + ); + const data = validateResponse( + array(ConversationListItemSchema), + page.data, `/organizations/${orgSlug}/agents/conversations/`, - { params, schema: array(ConversationListItemSchema) }, + page.response?.status ?? 0, ); - - const { nextCursor } = parseLinkHeader(headers.get("link") ?? null); - - return { data, nextCursor }; + return { data, nextCursor: page.nextCursor }; } /** @@ -108,16 +103,7 @@ async function fetchConversationsPage( */ export async function listConversations( orgSlug: string, - options: { - query?: string; - sort?: string; - limit?: number; - cursor?: string; - statsPeriod?: string; - start?: string; - end?: string; - project?: string; - } = {}, + options: ConversationListOptions & { limit?: number } = {}, ): Promise> { const regionUrl = await resolveOrgRegion(orgSlug); return await paginate(options, (perPage, cursor) => diff --git a/packages/cli/src/lib/api/dashboards.ts b/packages/cli/src/lib/api/dashboards.ts index 152dd80e0..5738eb6cd 100644 --- a/packages/cli/src/lib/api/dashboards.ts +++ b/packages/cli/src/lib/api/dashboards.ts @@ -9,7 +9,9 @@ import { createOrganizationDashboard, getOrganizationDashboard, listOrganizationDashboards, + type ListOrganizationEventsTimeseriesData, listOrganizationEvents, + listOrganizationEventsTimeseries, updateOrganizationDashboard, } from "@sentry/api"; import { resolveEventsSort } from "@sentry/toolkit-core/events-sort"; @@ -466,12 +468,15 @@ function buildTopEventsParams( columns: string[], aggregates: string[], orderby: string | undefined, -): Record { +): Pick< + EventsTimeseriesQuery, + "groupBy" | "sort" | "topEvents" | "excludeOther" +> { const { fields, sort } = resolveEventsSort({ fields: [...columns, ...aggregates], sort: orderby, }); - const params: Record = { + const params: ReturnType = { groupBy: fields, sort, topEvents: widget.limit ?? 5, @@ -510,6 +515,8 @@ type WidgetQueryParams = { options?: WidgetQueryOptions; }; +type EventsTimeseriesQuery = ListOrganizationEventsTimeseriesData["query"]; + /** Widget query params once the widget's type has resolved to a dataset. */ type DatasetWidgetQueryParams = WidgetQueryParams & { dataset: EventsDataset }; @@ -533,30 +540,29 @@ async function queryWidgetTimeseries( const aggregates = query.aggregates ?? []; const columns = query.columns ?? []; - const reqParams: Record = { - yAxis: aggregates, - query: query.conditions || undefined, - dataset, - statsPeriod, - start, - end, - interval: computeOptimalInterval(statsPeriod, widget, periodSeconds), - environment: options.environment, - project: options.project?.map(String), - }; - - // Group-by columns enable topEvents mode - if (columns.length > 0) { - Object.assign( - reqParams, - buildTopEventsParams(widget, columns, aggregates, query.orderby), - ); - } - - const { data } = await apiRequestToRegion( - regionUrl, + const result = await listOrganizationEventsTimeseries({ + ...getSdkClientConfig(regionUrl), + path: { organization_id_or_slug: orgSlug }, + query: { + yAxis: aggregates, + query: query.conditions || undefined, + dataset, + statsPeriod, + start, + end, + interval: computeOptimalInterval(statsPeriod, widget, periodSeconds), + environment: options.environment, + project: options.project, + // Group-by columns enable topEvents mode + ...(columns.length > 0 && + buildTopEventsParams(widget, columns, aggregates, query.orderby)), + }, + }); + const data = unwrapValidatedResult( + result, + "Failed to query widget data", + EventsTimeseriesResponseSchema, `/organizations/${orgSlug}/events-timeseries/`, - { params: reqParams, schema: EventsTimeseriesResponseSchema }, ); const parsed = parseEventsTimeseriesResponse(data, aggregates); diff --git a/packages/cli/src/lib/api/explore.ts b/packages/cli/src/lib/api/explore.ts index 2ad2eff37..584236a50 100644 --- a/packages/cli/src/lib/api/explore.ts +++ b/packages/cli/src/lib/api/explore.ts @@ -7,6 +7,7 @@ import { listOrganizationEvents, + listOrganizationEventsTimeseries, type OrganizationEventsTimeseriesResponse, } from "@sentry/api"; @@ -18,11 +19,12 @@ import { resolveOrgRegion } from "../region.js"; import { getSdkClientConfig } from "../sentry-sdk-client.js"; import { API_MAX_PER_PAGE, - apiRequestToRegion, + getOrgSdkConfig, MAX_PAGINATION_PAGES, type PaginatedResponse, paginate, unwrapPaginatedResult, + unwrapResult, validateResponse, } from "./infrastructure.js"; @@ -93,35 +95,34 @@ export type EventsTimeSeriesResponse = Omit< /** * Query aggregate values over time without grouping or pagination. * - * Uses the same regional transport as `queryEvents` because the generated - * SDK input types do not support repeated `yAxis` or duration-string intervals. * Returns the complete API response, including sampling and ingestion metadata. */ export async function queryEventsTimeSeries( orgSlug: string, options: ExploreTimeSeriesQueryOptions, ): Promise { - const regionUrl = await resolveOrgRegion(orgSlug); - const { data } = await apiRequestToRegion( - regionUrl, - `/organizations/${orgSlug}/events-timeseries/`, - { - params: { - dataset: options.dataset ?? "errors", - yAxis: options.fields, - query: options.query || undefined, - project: options.projectId, - interval: options.interval, - statsPeriod: - options.start || options.end - ? undefined - : (options.statsPeriod ?? "24h"), - start: options.start, - end: options.end, - }, + const result = await listOrganizationEventsTimeseries({ + ...(await getOrgSdkConfig(orgSlug)), + path: { organization_id_or_slug: orgSlug }, + query: { + dataset: options.dataset ?? "errors", + yAxis: options.fields, + query: options.query || undefined, + project: + options.projectId === undefined ? undefined : [options.projectId], + interval: options.interval, + statsPeriod: + options.start || options.end + ? undefined + : (options.statsPeriod ?? "24h"), + start: options.start, + end: options.end, }, + }); + return unwrapResult( + result, + "Failed to query events time series", ); - return data; } /** diff --git a/packages/cli/src/lib/api/issues.ts b/packages/cli/src/lib/api/issues.ts index ac082a08e..949391a1c 100644 --- a/packages/cli/src/lib/api/issues.ts +++ b/packages/cli/src/lib/api/issues.ts @@ -12,6 +12,8 @@ import { getOrganizationIssue, listOrganizationIssues, resolveOrganizationShortId, + updateOrganizationIssue, + updateOrganizationIssues, } from "@sentry/api"; import type { SentryIssue } from "../../types/index.js"; @@ -24,14 +26,12 @@ import { } from "../custom-ca.js"; import { applyCustomHeaders } from "../custom-headers.js"; import { ApiError, ValidationError } from "../errors.js"; -import { resolveOrgRegion } from "../region.js"; import { invalidateCachedResponsesMatching } from "../response-cache.js"; import { getApiBaseUrl } from "../sentry-client.js"; import { API_MAX_PER_PAGE, apiRequest, - apiRequestToRegion, getOrgSdkConfig, type PaginatedResponse, paginate, @@ -612,23 +612,20 @@ export async function updateIssueStatus( orgSlug?: string; }, ): Promise { - const body: Record = { status }; - if (options?.statusDetails) { - body.statusDetails = options.statusDetails; - } - if (options?.substatus) { - body.substatus = options.substatus; - } + const body = { + status, + ...(options?.statusDetails && { statusDetails: options.statusDetails }), + ...(options?.substatus && { substatus: options.substatus }), + }; if (options?.orgSlug) { // Region-aware org-scoped endpoint — preferred when org is known. - const regionUrl = await resolveOrgRegion(options.orgSlug); - const { data } = await apiRequestToRegion( - regionUrl, - `/organizations/${encodeURIComponent(options.orgSlug)}/issues/${encodeURIComponent(issueId)}/`, - { method: "PUT", body }, - ); - return data; + const result = await updateOrganizationIssue({ + ...(await getOrgSdkConfig(options.orgSlug)), + path: { organization_id_or_slug: options.orgSlug, issue_id: issueId }, + body, + }); + return unwrapResult(result, "Failed to update issue"); } // Legacy global endpoint — works without org but not region-aware. @@ -670,49 +667,43 @@ export async function mergeIssues( `Need at least 2 issues to merge (got ${groupIds.length}).`, ); } - // The bulk mutate endpoint takes repeated numeric `?id=X` query params plus - // a `{merge: true}` body. `updateOrganizationIssues` would fit, but its body - // type marks every field required until getsentry/sentry#130348 lands. - const regionUrl = await resolveOrgRegion(orgSlug); - const query = groupIds.map((id) => `id=${encodeURIComponent(id)}`).join("&"); - const path = `/organizations/${encodeURIComponent(orgSlug)}/issues/?${query}`; - type MergeResponse = { merge: MergeIssuesResult }; - try { - const { data } = await apiRequestToRegion(regionUrl, path, { - method: "PUT", - body: { merge: true }, - }); - // HTTP-layer invalidation covers the region-scoped caches via the - // prefix sweep on `/organizations/{org}/issues/`, but it can't see - // the affected IDs (they're in query params, stripped from the - // URL the hook sees). Manually clear each affected issue's legacy - // cross-origin cache so subsequent `getIssue(id)` doesn't serve - // stale data. - const apiBase = getApiBaseUrl().replace(TRAILING_SLASH_RE, ""); - const affectedIds = data.merge.children.toSpliced(0, 0, data.merge.parent); - await Promise.all( - affectedIds.map((id) => - invalidateCachedResponsesMatching( - `${apiBase}/api/0/issues/${encodeURIComponent(id)}/`, - ), - ), + const result = await updateOrganizationIssues({ + ...(await getOrgSdkConfig(orgSlug)), + path: { organization_id_or_slug: orgSlug }, + query: { id: groupIds.map(Number) }, + body: { merge: true }, + }); + const merge = unwrapResult<{ merge?: MergeIssuesResult } | null>( + result, + "Failed to merge issues", + )?.merge; + // The bulk-mutate endpoint returns 204 when no matching issues are + // found — e.g. IDs out of scope, or issues deleted between resolution + // and the merge call. + if (!merge) { + throw new ApiError( + `No matching issues found for merge in '${orgSlug}'.`, + 204, + "All provided issue IDs are out of scope or no longer exist.", + `/organizations/${encodeURIComponent(orgSlug)}/issues/?${groupIds.map((id) => `id=${encodeURIComponent(id)}`).join("&")}`, ); - return data.merge; - } catch (error) { - // The bulk-mutate endpoint returns 204 when no matching issues are - // found — e.g. IDs out of scope, or issues deleted between resolution - // and the merge call. Catch the generic "no body" ApiError and re-throw - // with a friendlier user-facing message. - if (error instanceof ApiError && error.status === 204) { - throw new ApiError( - `No matching issues found for merge in '${orgSlug}'.`, - 204, - "All provided issue IDs are out of scope or no longer exist.", - path, - ); - } - throw error; } + // HTTP-layer invalidation covers the region-scoped caches via the + // prefix sweep on `/organizations/{org}/issues/`, but it can't see + // the affected IDs (they're in query params, stripped from the + // URL the hook sees). Manually clear each affected issue's legacy + // cross-origin cache so subsequent `getIssue(id)` doesn't serve + // stale data. + const apiBase = getApiBaseUrl().replace(TRAILING_SLASH_RE, ""); + const affectedIds = merge.children.toSpliced(0, 0, merge.parent); + await Promise.all( + affectedIds.map((id) => + invalidateCachedResponsesMatching( + `${apiBase}/api/0/issues/${encodeURIComponent(id)}/`, + ), + ), + ); + return merge; } /** diff --git a/packages/cli/src/lib/api/traces.ts b/packages/cli/src/lib/api/traces.ts index 5abc06a7f..f3f238f19 100644 --- a/packages/cli/src/lib/api/traces.ts +++ b/packages/cli/src/lib/api/traces.ts @@ -5,6 +5,7 @@ */ import { + getOrganizationTrace, getOrganizationTraceMeta, type ListOrganizationTraceItemAttributesData, type ListOrganizationTraceItemAttributesQueryAttributeType, @@ -34,6 +35,7 @@ export type { TraceItemAttribute, TraceItemDetail } from "../../types/index.js"; import { logger } from "../logger.js"; import { resolveOrgRegion } from "../region.js"; +import { getSdkClientConfig } from "../sentry-sdk-client.js"; import { isAllDigits } from "../utils.js"; import { @@ -43,6 +45,7 @@ import { type PaginatedResponse, paginate, unwrapPaginatedResult, + unwrapResult, unwrapValidatedResult, validateResponse, } from "./infrastructure.js"; @@ -129,23 +132,22 @@ async function requestTrace( traceId: string, params: GetDetailedTraceOptions & { statsPeriod?: string }, ): Promise { - const { data } = await apiRequestToRegion( - regionUrl, - `/organizations/${orgSlug}/trace/${traceId}/`, - { - params: { - timestamp: params.timestamp, - statsPeriod: params.statsPeriod, - additional_attributes: params.additionalAttributes, - }, + const result = await getOrganizationTrace({ + ...getSdkClientConfig(regionUrl), + path: { organization_id_or_slug: orgSlug, trace_id: traceId }, + query: { + timestamp: + params.timestamp === undefined ? undefined : String(params.timestamp), + statsPeriod: params.statsPeriod, + additional_attributes: params.additionalAttributes, }, - ); + }); + const data = unwrapResult(result, "Failed to get trace"); return data.map(normalizeTraceSpan); } /** * Get detailed trace with nested children structure. - * This is an internal endpoint not covered by the public API. * Uses region-aware routing for multi-region support. * * The endpoint always returns spans from every project in the trace; it diff --git a/packages/cli/test/lib/api-client.coverage.test.ts b/packages/cli/test/lib/api-client.coverage.test.ts index cc1dbeb7f..cc910a80c 100644 --- a/packages/cli/test/lib/api-client.coverage.test.ts +++ b/packages/cli/test/lib/api-client.coverage.test.ts @@ -310,6 +310,38 @@ describe("issues.ts", () => { expect(result.status).toBe("resolved"); expect(capturedBody).toEqual({ status: "resolved" }); }); + + test("sends PUT to the org-scoped endpoint when the org is known", async () => { + const issue = mockIssue({ status: "ignored" }); + let capturedUrl = ""; + let capturedBody: unknown; + + globalThis.fetch = mockFetch(async (input, init) => { + const req = new Request(input!, init); + capturedUrl = req.url; + capturedBody = await req.json(); + expect(req.method).toBe("PUT"); + return new Response(JSON.stringify(issue), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + }); + + const result = await updateIssueStatus("12345", "ignored", { + orgSlug: "test-org", + substatus: "archived_until_condition_met", + statusDetails: { ignoreCount: 10, ignoreWindow: 60 }, + }); + expect(result.status).toBe("ignored"); + expect(capturedUrl).toContain( + "/api/0/organizations/test-org/issues/12345/", + ); + expect(capturedBody).toEqual({ + status: "ignored", + statusDetails: { ignoreCount: 10, ignoreWindow: 60 }, + substatus: "archived_until_condition_met", + }); + }); }); describe("listIssuesAllPages", () => { diff --git a/packages/cli/test/lib/api/dashboards.test.ts b/packages/cli/test/lib/api/dashboards.test.ts index 55f20a584..83eb01137 100644 --- a/packages/cli/test/lib/api/dashboards.test.ts +++ b/packages/cli/test/lib/api/dashboards.test.ts @@ -5,7 +5,6 @@ * from src/lib/api/dashboards.ts. */ -import { safeParse } from "valibot"; import { beforeEach, describe, expect, test, vi } from "vitest"; import { computeOptimalInterval, @@ -22,18 +21,24 @@ import type { const { apiRequestToRegionMock, listOrganizationEventsMock, + listOrganizationEventsTimeseriesMock, reportCliErrorMock, resolveOrgRegionMock, } = vi.hoisted(() => ({ apiRequestToRegionMock: vi.fn(), listOrganizationEventsMock: vi.fn(), + listOrganizationEventsTimeseriesMock: vi.fn(), reportCliErrorMock: vi.fn(), resolveOrgRegionMock: vi.fn(), })); vi.mock("@sentry/api", async (importOriginal) => { const actual = await importOriginal(); - return { ...actual, listOrganizationEvents: listOrganizationEventsMock }; + return { + ...actual, + listOrganizationEvents: listOrganizationEventsMock, + listOrganizationEventsTimeseries: listOrganizationEventsTimeseriesMock, + }; }); vi.mock("../../../src/lib/error-reporting.js", () => ({ @@ -332,8 +337,11 @@ describe("widget sort resolution", () => { }); test("selects the aggregate for grouped error timeseries so the sort is accepted", async () => { - apiRequestToRegionMock.mockReset(); - apiRequestToRegionMock.mockResolvedValue({ data: { timeSeries: [] } }); + listOrganizationEventsTimeseriesMock.mockReset(); + listOrganizationEventsTimeseriesMock.mockResolvedValue({ + data: { timeSeries: [] }, + response: new Response(), + }); const dashboard = makeDashboard([ { title: "Errors by node", @@ -354,11 +362,11 @@ describe("widget sort resolution", () => { await queryAllWidgets(REGION, "test-org", dashboard); - expect(apiRequestToRegionMock).toHaveBeenCalledWith( - REGION, - "/organizations/test-org/events-timeseries/", + expect(listOrganizationEventsTimeseriesMock).toHaveBeenCalledWith( expect.objectContaining({ - params: expect.objectContaining({ + baseUrl: REGION, + path: { organization_id_or_slug: "test-org" }, + query: expect.objectContaining({ dataset: "errors", query: "has:failure.node", groupBy: ["failure.node", "count()"], @@ -368,8 +376,9 @@ describe("widget sort resolution", () => { }), ); // Single axis, single query: the "Other" series is requested. - const params = apiRequestToRegionMock.mock.calls[0]?.[2]?.params; - expect(params).not.toHaveProperty("excludeOther"); + const query = + listOrganizationEventsTimeseriesMock.mock.calls[0]?.[0]?.query; + expect(query).not.toHaveProperty("excludeOther"); }); }); @@ -447,34 +456,15 @@ describe("queryAllWidgets timeseries", () => { const ORG = "my-org"; beforeEach(() => { - apiRequestToRegionMock.mockReset(); + listOrganizationEventsTimeseriesMock.mockReset(); }); - /** - * Resolve the request with `data`, validating it against the `schema` - * option the way the real `apiRequestToRegion` does. - */ + /** Resolve the request with `data`; the CLI validates it against its schema. */ function mockResponse(data: unknown): void { - apiRequestToRegionMock.mockImplementation( - async ( - _region: string, - endpoint: string, - options: { schema?: Parameters[0] } = {}, - ) => { - if (!options.schema) { - return { data }; - } - const result = safeParse(options.schema, data); - if (!result.success) { - throw new ApiError( - `${endpoint}: unexpected response format`, - 0, - result.issues.map((issue) => issue.message).join(", "), - ); - } - return { data: result.output }; - }, - ); + listOrganizationEventsTimeseriesMock.mockResolvedValue({ + data, + response: new Response(), + }); } /** Series meta the API always sends. */ @@ -492,17 +482,15 @@ describe("queryAllWidgets timeseries", () => { return { timestamp, value, incomplete: false }; } - /** Extract the single call's endpoint and params. */ + /** Extract the single call's path and query params. */ function lastRequest(): { - endpoint: string; + path: Record; params: Record; } { - const call = apiRequestToRegionMock.mock.calls[0] as [ - string, - string, - { params: Record }, + const [options] = listOrganizationEventsTimeseriesMock.mock.calls[0] as [ + { path: Record; query: Record }, ]; - return { endpoint: call[1], params: call[2].params }; + return { path: options.path, params: options.query }; } test("queries events-timeseries and normalizes an ungrouped series", async () => { @@ -540,9 +528,8 @@ describe("queryAllWidgets timeseries", () => { period: "24h", }); - const { endpoint, params } = lastRequest(); - expect(endpoint).toBe(`/organizations/${ORG}/events-timeseries/`); - expect(apiRequestToRegionMock.mock.calls[0]?.[2]).toHaveProperty("schema"); + const { path, params } = lastRequest(); + expect(path).toEqual({ organization_id_or_slug: ORG }); expect(params.dataset).toBe("errors"); expect(params.yAxis).toEqual(["count()"]); expect(params.statsPeriod).toBe("24h"); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb33914d8..9cde43239 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.44.0 - version: 1.44.0 + specifier: 1.46.0 + version: 1.46.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.44.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) + version: 1.46.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.44.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) + version: 1.46.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.44.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7) + version: 1.46.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.44.0': - resolution: {integrity: sha512-8PPlt5YuYLOn3btCatpogIa+Q2NW9dmtB5JPJA6IF7fKNLPUHqkojVBfOSNUb9fD5UE0ZxUzZsBUnVhW5/JvrQ==} + '@sentry/api@1.46.0': + resolution: {integrity: sha512-B3QSyPIgLVY4+ndIAdvB50FLDA6XeDmNtyY8FZOb0vJKf6XVZeOemOvDzWBgJPvyVpGs2mkh5+b+VhBq7+ASrw==} engines: {node: '>=22'} peerDependencies: typescript: '*' @@ -12112,7 +12112,7 @@ snapshots: '@sec-ant/readable-stream@0.4.1': {} - '@sentry/api@1.44.0(typescript@5.9.3)(valibot@1.4.2(typescript@5.9.3))(zod@4.6.7)': + '@sentry/api@1.46.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 d198de786..b00a90285 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.44.0 + "@sentry/api": 1.46.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.44.0" + - "@sentry/api@1.46.0" - miniflare@5.20261101.0-beta - wrangler@4.156.1 - "@sentry/symbolic@14.0.0-alpha.5"