Skip to content

Commit 9a70b24

Browse files
authored
refactor(event): replace "latest" magic string with @latest sentinel constant (#583)
## Summary Follow-up to #574 — the bare string `"latest"` was used as a sentinel `eventId` value across 4 assignment sites and 1 comparison with no named constant. Replaced with `LATEST_EVENT_SENTINEL = "@latest"`, using the `@`-prefix convention established by `IssueSelector` magic selectors (`@latest`, `@most_frequent`) in `arg-parsing.ts`. ## Changes - `src/commands/event/view.ts`: Added `LATEST_EVENT_SENTINEL` constant, replaced all 5 occurrences + updated comment - `test/commands/event/view.test.ts`: Updated 8 assertions to expect `"@latest"`
1 parent bdd75c0 commit 9a70b24

File tree

2 files changed

+22
-16
lines changed

2 files changed

+22
-16
lines changed

src/commands/event/view.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ function jsonTransformEventView(
105105
/** Usage hint for ContextError messages */
106106
const USAGE_HINT = "sentry event view <org>/<project> <event-id>";
107107

108+
/**
109+
* Sentinel eventId for "fetch the latest event for this issue."
110+
* Uses the @-prefix convention from {@link IssueSelector} magic selectors.
111+
*/
112+
const LATEST_EVENT_SENTINEL = "@latest";
113+
108114
/**
109115
* Parse a single positional arg for event view, handling issue short ID
110116
* detection both in bare form ("BRUNCHIE-APP-29") and org-prefixed form
@@ -128,7 +134,7 @@ function parseSingleArg(arg: string): ParsedPositionalArgs {
128134
// Use "org/" (trailing slash) to signal OrgAll mode so downstream
129135
// parseOrgProjectArg interprets this as an org, not a project search.
130136
return {
131-
eventId: "latest",
137+
eventId: LATEST_EVENT_SENTINEL,
132138
targetArg: `${beforeSlash}/`,
133139
issueShortId: afterSlash,
134140
};
@@ -159,7 +165,7 @@ function parseSingleArg(arg: string): ParsedPositionalArgs {
159165
// Detect bare issue short ID passed as event ID (e.g., "BRUNCHIE-APP-29").
160166
if (!targetArg && looksLikeIssueShortId(eventId)) {
161167
return {
162-
eventId: "latest",
168+
eventId: LATEST_EVENT_SENTINEL,
163169
targetArg: undefined,
164170
issueShortId: eventId,
165171
};
@@ -222,9 +228,9 @@ export function parsePositionalArgs(args: string[]): ParsedPositionalArgs {
222228
}
223229
if (urlParsed.issueId) {
224230
// Issue URL without event ID — fetch the latest event for this issue.
225-
// Use a placeholder eventId; the caller uses issueId to fetch via getLatestEvent.
231+
// The caller uses issueId to fetch via getLatestEvent.
226232
return {
227-
eventId: "latest",
233+
eventId: LATEST_EVENT_SENTINEL,
228234
targetArg: `${urlParsed.org}/`,
229235
issueId: urlParsed.issueId,
230236
};
@@ -258,7 +264,7 @@ export function parsePositionalArgs(args: string[]): ParsedPositionalArgs {
258264
// are uppercase). The second arg is ignored since we fetch the latest event.
259265
if (looksLikeIssueShortId(first)) {
260266
return {
261-
eventId: "latest",
267+
eventId: LATEST_EVENT_SENTINEL,
262268
targetArg: undefined,
263269
issueShortId: first,
264270
warning: `'${first}' is an issue short ID, not a project slug. Ignoring second argument '${second}'.`,
@@ -593,7 +599,7 @@ async function resolveIssueShortcut(
593599

594600
// When the user specified a specific event ID (SHORT-ID/EVENT-ID),
595601
// resolve the issue to get the project, then fetch the specific event.
596-
if (eventId !== "latest") {
602+
if (eventId !== LATEST_EVENT_SENTINEL) {
597603
const issue = await getIssueByShortId(resolved.org, issueShortId);
598604
const issueProject = issue.project?.slug;
599605
if (!issueProject) {
@@ -681,8 +687,8 @@ export const viewCommand = buildCommand({
681687
const parsed = parseOrgProjectArg(targetArg);
682688

683689
// Handle issue-based shortcuts (issue URLs and short IDs) before
684-
// normal event resolution. When eventId is "latest", fetches the
685-
// latest event; otherwise fetches the specific event.
690+
// normal event resolution. When eventId is LATEST_EVENT_SENTINEL,
691+
// fetches the latest event; otherwise fetches the specific event.
686692
const issueShortcut = await resolveIssueShortcut({
687693
parsed,
688694
eventId,

test/commands/event/view.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ describe("parsePositionalArgs", () => {
6767

6868
test("detects issue short ID and sets issueShortId", () => {
6969
const result = parsePositionalArgs(["BRUNCHIE-APP-29"]);
70-
expect(result.eventId).toBe("latest");
70+
expect(result.eventId).toBe("@latest");
7171
expect(result.targetArg).toBeUndefined();
7272
expect(result.issueShortId).toBe("BRUNCHIE-APP-29");
7373
});
7474

7575
test("detects short issue ID like CLI-G", () => {
7676
const result = parsePositionalArgs(["CLI-G"]);
77-
expect(result.eventId).toBe("latest");
77+
expect(result.eventId).toBe("@latest");
7878
expect(result.issueShortId).toBe("CLI-G");
7979
});
8080

@@ -86,15 +86,15 @@ describe("parsePositionalArgs", () => {
8686

8787
test("detects org/ISSUE-SHORT-ID pattern (CLI-9K)", () => {
8888
const result = parsePositionalArgs(["figma/FULLSCREEN-2RN"]);
89-
expect(result.eventId).toBe("latest");
89+
expect(result.eventId).toBe("@latest");
9090
// Trailing slash signals OrgAll mode so downstream resolves org correctly
9191
expect(result.targetArg).toBe("figma/");
9292
expect(result.issueShortId).toBe("FULLSCREEN-2RN");
9393
});
9494

9595
test("detects org/CLI-G pattern", () => {
9696
const result = parsePositionalArgs(["sentry/CLI-G"]);
97-
expect(result.eventId).toBe("latest");
97+
expect(result.eventId).toBe("@latest");
9898
expect(result.targetArg).toBe("sentry/");
9999
expect(result.issueShortId).toBe("CLI-G");
100100
});
@@ -128,7 +128,7 @@ describe("parsePositionalArgs", () => {
128128
test("org/SHORT-ID takes precedence over SHORT-ID/EVENT-ID", () => {
129129
// "figma/FULLSCREEN-2RN" → org + issue, not issue + event
130130
const result = parsePositionalArgs(["figma/FULLSCREEN-2RN"]);
131-
expect(result.eventId).toBe("latest");
131+
expect(result.eventId).toBe("@latest");
132132
expect(result.targetArg).toBe("figma/");
133133
expect(result.issueShortId).toBe("FULLSCREEN-2RN");
134134
});
@@ -169,15 +169,15 @@ describe("parsePositionalArgs", () => {
169169
"abc123def456",
170170
]);
171171
expect(result.issueShortId).toBe("JAVASCRIPT-NUXT-52");
172-
expect(result.eventId).toBe("latest");
172+
expect(result.eventId).toBe("@latest");
173173
expect(result.targetArg).toBeUndefined();
174174
expect(result.warning).toContain("issue short ID");
175175
});
176176

177177
test("auto-redirects simple issue short ID like CAM-82X", () => {
178178
const result = parsePositionalArgs(["CAM-82X", "95fd7f5a"]);
179179
expect(result.issueShortId).toBe("CAM-82X");
180-
expect(result.eventId).toBe("latest");
180+
expect(result.eventId).toBe("@latest");
181181
expect(result.targetArg).toBeUndefined();
182182
expect(result.warning).toContain("issue short ID");
183183
});
@@ -304,7 +304,7 @@ describe("parsePositionalArgs", () => {
304304
"https://sentry.io/organizations/my-org/issues/32886/",
305305
]);
306306
expect(result.issueId).toBe("32886");
307-
expect(result.eventId).toBe("latest");
307+
expect(result.eventId).toBe("@latest");
308308
expect(result.targetArg).toBe("my-org/");
309309
});
310310

0 commit comments

Comments
 (0)