Commit ba7fc85
authored
perf(api): pass ?collapse=organization on project detail fetches (#818)
## Summary
Cuts ~400-500ms off `GET /api/0/projects/{org}/{project}/` by asking the
server to skip full-org serialization. The collapsed response carries
only `{id, slug}` for `organization` instead of the full payload
(feature flags, options, etc).
Colleague flagged this optimization noting the UI already benefits —
every CLI hot path that fetches project details can ride the same win.
## Hot paths that benefit
- DSN auto-detect (numeric `orgId/projectId` → slug resolution)
- Explicit `<org>/<project>` target validation in `resolveTarget`
- `findProjectsBySlug` (one `getProject` per accessible org, multiplying
the saving)
- `project view`, `project delete`, `dashboard create`, `release list`,
`trace view`, `init`, and others
All project detail fetches in the CLI funnel through `getProject()` in
`src/lib/api/projects.ts`, so a single change covers everything.
## Handling the trimmed response
The collapsed payload drops `organization.name`. Four call sites
displayed it:
- `src/lib/resolve-target.ts` — three DSN resolution paths + one
cache-seed after explicit target validation
- `src/lib/dsn/resolver.ts` — DSN → ResolvedProject
- `src/lib/formatters/human.ts` — `project view`'s `Organization:` row
All now use a new `resolveOrgDisplayName(orgSlug, explicitName?)` helper
with the fallback chain:
1. Explicit `name` if the server returned it (self-hosted / older Sentry
ignore `collapse` and return the full payload — handled transparently)
2. `getCachedOrganizations()` lookup — populated on login and every
org-fanout operation
3. Slug itself as last resort
The cosmetic "name → slug" degradation only surfaces on cold-cache +
collapse-honoring server, which is vanishingly rare (every auth flow
warms the cache).
## JSON output shape stays stable
`sentry project view --json` previously returned the full `organization`
object (including `name`, feature flags, `options`). Since
`getProject()` now collapses the server response, `project view` gains a
`jsonTransform` that re-hydrates `organization.name` via
`resolveOrgDisplayName()` before serialization. Downstream agents and
scripts that scrape `.organization.name` continue to see a value.
When the server returns `name` (self-hosted / ignores `collapse`), the
transform preserves it as-is. When absent, it falls back through the
cached org list to the slug. Feature flags and `options` are no longer
returned — this is a deliberate tradeoff, as those fields aren't
documented stable CLI output and dropping them is where the perf win
comes from.
## Response cache invalidation is now prefix-based
The response cache keys by full URL (including sorted query params via
`buildCacheKey`/`normalizeUrl`). Pre-PR,
`invalidateCachedResponse(baseUrl)` would match the cached entry
exactly. Post-PR, cached entries live under
`baseUrl?collapse=organization`, so an exact-match invalidation would
silently miss — `project delete` → `project view` would serve
pre-deletion data.
`invalidateProjectCaches` switches to
`invalidateCachedResponsesMatching` (prefix sweep) to catch the
collapsed variant plus any future query-param additions. The trailing
slash on `buildApiUrl` output guarantees the prefix can't collide with
sibling projects (`projects/acme/frontend/` does not prefix-match
`projects/acme/frontend-old/`).
Side effect: sub-resources like `/projects/acme/frontend/keys/` and
`/projects/acme/frontend/trace-items/...` also get swept on `project
delete`. This is desirable — those children would 404 after the parent
is gone anyway.
## Tests
Added to `test/lib/api-client.test.ts`:
- `getProject sends ?collapse=organization query parameter` — asserts
the outgoing URL carries the query param.
- `getProject tolerates collapsed response missing organization.name` —
confirms no blow-up when `name` is absent.
- 4 `resolveOrgDisplayName` cases: explicit name wins / cache fallback /
slug fallback / empty-string falsy handling.
Added to `test/lib/formatters/human.details.test.ts`:
- `falls back to org slug when collapsed response omits name` —
module-level `useTestConfigDir` ensures an empty `org_regions` table,
regex-match asserts the slug appears in **both** the display-name and
parens positions (stray cached name would fail).
Added to `test/commands/project/view.func.test.ts`:
- `JSON output re-hydrates organization.name when API response omits it`
— exercises the new `jsonTransform` slug-fallback path.
- `JSON output preserves organization.name when API response includes
it` — self-hosted / older-Sentry compat.
- `JSON output still strips detectedFrom (human-only field)` —
regression guard for the `jsonExclude` → `jsonTransform` switch.
- `JSON output honours --fields filter` — confirms field filtering still
works on top of the transform.
## Verification
- `bun run typecheck` ✓
- `bun run lint` ✓ (only a pre-existing unrelated warning)
- `bun run test:unit` → 5676 pass / 0 fail (+4 new from follow-up)
- `bun run test:isolated` → 138 pass / 0 fail
## Follow-ups (out of scope)
- Team detail (`retrieveATeam`) has the same `?collapse=organization`
support per the SDK types — same perf win available for `team list` etc.
- Similar-shape audit on other endpoints returning nested org payloads.1 parent 2374112 commit ba7fc85
10 files changed
Lines changed: 408 additions & 21 deletions
File tree
- src
- commands/project
- lib
- api
- dsn
- formatters
- types
- test
- commands/project
- lib
- formatters
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
10 | 14 | | |
11 | 15 | | |
12 | 16 | | |
| |||
15 | 19 | | |
16 | 20 | | |
17 | 21 | | |
| 22 | + | |
18 | 23 | | |
19 | 24 | | |
20 | 25 | | |
| |||
179 | 184 | | |
180 | 185 | | |
181 | 186 | | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
182 | 231 | | |
183 | 232 | | |
184 | 233 | | |
| |||
221 | 270 | | |
222 | 271 | | |
223 | 272 | | |
224 | | - | |
| 273 | + | |
225 | 274 | | |
226 | 275 | | |
227 | 276 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
| 100 | + | |
100 | 101 | | |
101 | 102 | | |
102 | 103 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
| 29 | + | |
33 | 30 | | |
34 | 31 | | |
35 | 32 | | |
| |||
229 | 226 | | |
230 | 227 | | |
231 | 228 | | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
232 | 235 | | |
233 | 236 | | |
234 | 237 | | |
| |||
237 | 240 | | |
238 | 241 | | |
239 | 242 | | |
240 | | - | |
| 243 | + | |
241 | 244 | | |
242 | 245 | | |
243 | 246 | | |
| |||
453 | 456 | | |
454 | 457 | | |
455 | 458 | | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
456 | 469 | | |
457 | 470 | | |
458 | 471 | | |
459 | 472 | | |
460 | 473 | | |
461 | 474 | | |
462 | 475 | | |
463 | | - | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
464 | 482 | | |
465 | 483 | | |
466 | 484 | | |
467 | 485 | | |
468 | 486 | | |
469 | | - | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
470 | 491 | | |
471 | 492 | | |
472 | 493 | | |
473 | 494 | | |
474 | 495 | | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
475 | 526 | | |
476 | 527 | | |
477 | 528 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
68 | | - | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
69 | 73 | | |
70 | 74 | | |
71 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
1537 | 1538 | | |
1538 | 1539 | | |
1539 | 1540 | | |
| 1541 | + | |
| 1542 | + | |
| 1543 | + | |
| 1544 | + | |
1540 | 1545 | | |
1541 | 1546 | | |
1542 | | - | |
| 1547 | + | |
1543 | 1548 | | |
1544 | 1549 | | |
1545 | 1550 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
193 | 194 | | |
194 | 195 | | |
195 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
196 | 201 | | |
197 | 202 | | |
198 | | - | |
| 203 | + | |
199 | 204 | | |
200 | 205 | | |
201 | 206 | | |
| |||
205 | 210 | | |
206 | 211 | | |
207 | 212 | | |
208 | | - | |
| 213 | + | |
209 | 214 | | |
210 | 215 | | |
211 | 216 | | |
| |||
333 | 338 | | |
334 | 339 | | |
335 | 340 | | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
336 | 345 | | |
337 | 346 | | |
338 | | - | |
| 347 | + | |
339 | 348 | | |
340 | 349 | | |
341 | 350 | | |
| |||
345 | 354 | | |
346 | 355 | | |
347 | 356 | | |
348 | | - | |
| 357 | + | |
349 | 358 | | |
350 | 359 | | |
351 | 360 | | |
| |||
402 | 411 | | |
403 | 412 | | |
404 | 413 | | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
405 | 418 | | |
406 | 419 | | |
407 | | - | |
| 420 | + | |
408 | 421 | | |
409 | 422 | | |
410 | 423 | | |
| |||
414 | 427 | | |
415 | 428 | | |
416 | 429 | | |
417 | | - | |
| 430 | + | |
418 | 431 | | |
419 | 432 | | |
420 | 433 | | |
| |||
880 | 893 | | |
881 | 894 | | |
882 | 895 | | |
883 | | - | |
| 896 | + | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
884 | 900 | | |
885 | 901 | | |
886 | 902 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
62 | 70 | | |
63 | 71 | | |
64 | 72 | | |
65 | | - | |
| 73 | + | |
66 | 74 | | |
67 | 75 | | |
68 | 76 | | |
| |||
0 commit comments