Skip to content

Commit 67afdaa

Browse files
dcramercodex
andcommitted
fix(core): Validate issue event time params
Route issue event time filters through shared validation before building the SDK query so conflicting or unpaired ranges fail before a request is sent. Co-Authored-By: GPT-5 Codex <noreply@openai.com>
1 parent 8788b36 commit 67afdaa

2 files changed

Lines changed: 70 additions & 3 deletions

File tree

packages/mcp-core/src/api-client/client.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,72 @@ describe("API query builders", () => {
11191119
);
11201120
expect(globalThis.fetch).not.toHaveBeenCalled();
11211121
});
1122+
1123+
it("should build issue event requests with stats period", async () => {
1124+
const apiService = new SentryApiService({
1125+
host: "sentry.io",
1126+
accessToken: "test-token",
1127+
});
1128+
1129+
globalThis.fetch = makeSdkMock([]);
1130+
1131+
await apiService.listEventsForIssue({
1132+
organizationSlug: "test-org",
1133+
issueId: "123",
1134+
query: "level:error",
1135+
sort: "-timestamp",
1136+
statsPeriod: "7d",
1137+
limit: 25,
1138+
});
1139+
1140+
const url = extractFetchUrl(
1141+
(globalThis.fetch as ReturnType<typeof vi.fn>).mock.calls[0],
1142+
);
1143+
const parsedUrl = new URL(url);
1144+
expect(parsedUrl.searchParams.get("statsPeriod")).toBe("7d");
1145+
expect(parsedUrl.searchParams.get("start")).toBeNull();
1146+
expect(parsedUrl.searchParams.get("end")).toBeNull();
1147+
});
1148+
1149+
it("should reject conflicting issue event time parameters", async () => {
1150+
const apiService = new SentryApiService({
1151+
host: "sentry.io",
1152+
accessToken: "test-token",
1153+
});
1154+
1155+
globalThis.fetch = makeSdkMock([]);
1156+
1157+
await expect(
1158+
apiService.listEventsForIssue({
1159+
organizationSlug: "test-org",
1160+
issueId: "123",
1161+
statsPeriod: "24h",
1162+
start: "2025-01-01T00:00:00Z",
1163+
end: "2025-01-02T00:00:00Z",
1164+
}),
1165+
).rejects.toThrow("Cannot use both statsPeriod and start/end parameters");
1166+
expect(globalThis.fetch).not.toHaveBeenCalled();
1167+
});
1168+
1169+
it("should require paired issue event start and end parameters", async () => {
1170+
const apiService = new SentryApiService({
1171+
host: "sentry.io",
1172+
accessToken: "test-token",
1173+
});
1174+
1175+
globalThis.fetch = makeSdkMock([]);
1176+
1177+
await expect(
1178+
apiService.listEventsForIssue({
1179+
organizationSlug: "test-org",
1180+
issueId: "123",
1181+
start: "2025-01-01T00:00:00Z",
1182+
}),
1183+
).rejects.toThrow(
1184+
"Both start and end parameters must be provided together",
1185+
);
1186+
expect(globalThis.fetch).not.toHaveBeenCalled();
1187+
});
11221188
});
11231189

11241190
describe("Web URL builders", () => {

packages/mcp-core/src/api-client/client.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,6 +2144,9 @@ export class SentryApiService {
21442144
},
21452145
opts?: RequestOptions,
21462146
) {
2147+
const timeParams = new URLSearchParams();
2148+
this.applyTimeParams(timeParams, statsPeriod, start, end);
2149+
21472150
const result = await sdkListAnIssueSEvents({
21482151
...this.getSdkConfig(opts),
21492152
path: {
@@ -2154,9 +2157,7 @@ export class SentryApiService {
21542157
query,
21552158
per_page: limit,
21562159
sort,
2157-
statsPeriod,
2158-
start,
2159-
end,
2160+
...Object.fromEntries(timeParams),
21602161
full,
21612162
} as Record<string, unknown>,
21622163
} as Parameters<typeof sdkListAnIssueSEvents>[0]);

0 commit comments

Comments
 (0)