Skip to content

Commit 1dfdb68

Browse files
committed
fix: address review findings for org/project get commands
- Make features field optional in SentryOrganization and SentryProject types to match actual API behavior - Add formatDetailsHeader() helper to handle empty name/slug edge cases - Add test for partial flags error (--org without project slug)
1 parent dc3401a commit 1dfdb68

File tree

3 files changed

+47
-14
lines changed

3 files changed

+47
-14
lines changed

packages/cli/src/lib/formatters/human.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,28 @@ function formatFeaturesList(features: string[] | undefined): string[] {
5555
return lines;
5656
}
5757

58+
/** Minimum width for header separator line */
59+
const MIN_HEADER_WIDTH = 20;
60+
61+
/**
62+
* Format a details header with slug and name.
63+
* Handles empty values gracefully.
64+
*
65+
* @param slug - Resource slug (e.g., org or project slug)
66+
* @param name - Resource display name
67+
* @returns Array with header line and separator
68+
*/
69+
function formatDetailsHeader(slug: string, name: string): [string, string] {
70+
const displaySlug = slug || "(no slug)";
71+
const displayName = name || "(unnamed)";
72+
const header = `${displaySlug}: ${displayName}`;
73+
const separatorWidth = Math.max(
74+
MIN_HEADER_WIDTH,
75+
Math.min(80, header.length)
76+
);
77+
return [header, "═".repeat(separatorWidth)];
78+
}
79+
5880
/**
5981
* Get status icon for an issue status
6082
*/
@@ -292,13 +314,12 @@ export function formatOrgDetails(org: SentryOrganization): string[] {
292314
const lines: string[] = [];
293315

294316
// Header
295-
lines.push(`${org.slug}: ${org.name}`);
296-
lines.push("═".repeat(Math.min(80, org.name.length + org.slug.length + 2)));
297-
lines.push("");
317+
const [header, separator] = formatDetailsHeader(org.slug, org.name);
318+
lines.push(header, separator, "");
298319

299320
// Basic info
300-
lines.push(`Slug: ${org.slug}`);
301-
lines.push(`Name: ${org.name}`);
321+
lines.push(`Slug: ${org.slug || "(none)"}`);
322+
lines.push(`Name: ${org.name || "(unnamed)"}`);
302323
lines.push(`ID: ${org.id}`);
303324
lines.push(`Created: ${new Date(org.dateCreated).toLocaleString()}`);
304325
lines.push("");
@@ -363,15 +384,12 @@ export function formatProjectDetails(project: SentryProject): string[] {
363384
const lines: string[] = [];
364385

365386
// Header
366-
lines.push(`${project.slug}: ${project.name}`);
367-
lines.push(
368-
"═".repeat(Math.min(80, project.name.length + project.slug.length + 2))
369-
);
370-
lines.push("");
387+
const [header, separator] = formatDetailsHeader(project.slug, project.name);
388+
lines.push(header, separator, "");
371389

372390
// Basic info
373-
lines.push(`Slug: ${project.slug}`);
374-
lines.push(`Name: ${project.name}`);
391+
lines.push(`Slug: ${project.slug || "(none)"}`);
392+
lines.push(`Name: ${project.name || "(unnamed)"}`);
375393
lines.push(`ID: ${project.id}`);
376394
lines.push(`Platform: ${project.platform || "Not set"}`);
377395
lines.push(`Status: ${project.status}`);

packages/cli/src/types/sentry.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export type SentryOrganization = {
1515
avatarType: string;
1616
avatarUuid: string | null;
1717
};
18-
features: string[];
18+
/** Feature flags enabled for this organization (may be omitted in some API responses) */
19+
features?: string[];
1920
};
2021

2122
export type SentryProject = {
@@ -26,7 +27,8 @@ export type SentryProject = {
2627
dateCreated: string;
2728
isBookmarked: boolean;
2829
isMember: boolean;
29-
features: string[];
30+
/** Feature flags enabled for this project (may be omitted in some API responses) */
31+
features?: string[];
3032
firstEvent: string | null;
3133
firstTransactionEvent: boolean;
3234
access: string[];

packages/cli/test/e2e/project.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,19 @@ describe("sentry project get", () => {
188188
expect(result.stderr + result.stdout).toMatch(/organization|project/i);
189189
});
190190

191+
test("rejects partial flags (--org without project)", async () => {
192+
await setAuthToken(TEST_TOKEN);
193+
194+
const result = await runCli(["project", "get", "--org", TEST_ORG], {
195+
env: { SENTRY_CLI_CONFIG_DIR: testConfigDir },
196+
});
197+
198+
expect(result.exitCode).toBe(1);
199+
expect(result.stderr + result.stdout).toMatch(
200+
/--org was specified but --project is also required/i
201+
);
202+
});
203+
191204
test("gets project details", async () => {
192205
await setAuthToken(TEST_TOKEN);
193206

0 commit comments

Comments
 (0)