Skip to content

feat(apidocs): Publish chunk upload and assemble endpoints as experimental - #128999

Merged
BYK merged 2 commits into
masterfrom
feat/publish-assemble-endpoints
Oct 28, 2026
Merged

BYK merged 2 commits into
masterfrom
feat/publish-assemble-endpoints

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #128930. Publishes the endpoints that declare idempotent_methods so the x-sentry-idempotent markers actually reach the public schema, and through it @sentry/api (getsentry/sentry-api-schema#111 already reads the extension).

Refs #128927.

What changes

All four operations are PUBLIC_EXPERIMENTAL and fully documented:

  • GET /organizations/{org}/chunk-upload/ (getOrganizationChunkUploadOptions): the upload URL and limits. Clients need it before they upload, so it's published with the POST.
  • POST /organizations/{org}/chunk-upload/ (uploadOrganizationChunks): multipart body described by a docs-only ChunkUploadSerializer, following the ReleaseFileUploadSerializer precedent.
  • POST /projects/{org}/{project}/files/difs/assemble/ (assembleProjectDebugFiles)
  • POST /organizations/{org}/artifactbundle/assemble/ (assembleOrganizationArtifactBundle)

The three POSTs carry x-sentry-idempotent: true. GET doesn't, because safe methods are implicit.

Notes for review

  • One schema for validation and docs. The assemble endpoints' jsonschema moved to module constants that both validation and extend_schema use, so the docs can't drift from what's enforced. Validation behavior is unchanged.
  • DIF body is a map keyed by checksum. OpenAPI 3.0 has no patternProperties, so the docs version uses additionalProperties, and the checksum key format is explained in the description. _validate_request_body raised KeyError: 'properties' on map bodies. It now checks field descriptions under the map's additionalProperties. The new MapRequestBodyTest covers this.
  • Types. Added response TypedDicts and typed batch_assemble. state is str, not a Literal, because ChunkFileState values are plain strings.
  • accept is now a list. It was a tuple, which spectacular documents as a fixed-length array, so the GET options response returns list(CHUNK_UPLOAD_ACCEPT) instead. The JSON output is the same.
  • File parts show as format: uri. That's how spectacular renders FileField, and it matches the existing release-file upload docs. Happy to override it to binary if reviewers prefer.

Verification

  • sentry django spectacular --validate --fail-on-warn passes. The deref build and openapi-examples-validator --no-additional-properties pass with no errors.
  • ruff check, ruff format --check, flake8, and mypy are clean on the changed files.
  • Passing: tests/apidocs/test_hooks.py, test_operation_id_uniqueness.py, test_chunk_upload.py, test_organization_artifactbundle_assemble.py (78 tests).
  • test_dif_assemble.py needs an objectstore server, which my sandbox doesn't have, so CI is the authoritative run there. I checked parse_assemble_request_payload directly instead: it accepts valid payloads and rejects bad checksum keys, a missing name, non-hex chunks, and invalid debug_id, the same as before.

via BYK.

--

View Junior Session [Sentry]

…ental

Mark chunk upload (GET options, POST chunks), DIF assemble, and artifact
bundle assemble as PUBLIC_EXPERIMENTAL with full request and response
schemas, so generated clients such as `@sentry/api` can call them and pick
up the `x-sentry-idempotent` markers from #128930.

The assemble endpoints share their jsonschema between validation and the
docs. The DIF body is a map keyed by file checksum; OpenAPI 3.0 has no
`patternProperties`, so its schema uses `additionalProperties`, and the
request body check now validates field descriptions under a map's
`additionalProperties`.

Refs #128927

Co-Authored-By: BYK <burak.kaya@sentry.io>
@sentry-junior
sentry-junior Bot requested review from a team as code owners October 28, 2026 14:26
@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Oct 28, 2026
The public mutation matrix now covers the artifact bundle assemble endpoint,
which rejects an empty body. Send a status poll (no chunks) so the session
control succeeds without starting an assemble task.
@sentry-junior
sentry-junior Bot requested a review from a team as a code owner October 28, 2026 14:37
@BYK
BYK merged commit 8dee77e into master Oct 28, 2026
73 of 74 checks passed
@BYK
BYK deleted the feat/publish-assemble-endpoints branch October 28, 2026 21:57
BYK pushed a commit to getsentry/toolkit that referenced this pull request Oct 28, 2026
The CLI now reads which writes are safe to replay from the `@sentry/api`
operation catalog. Before, each call site hard-coded `idempotent: true`.

Sentry publishes chunk-upload and the DIF and artifact bundle assemble
endpoints as `PUBLIC_EXPERIMENTAL`, with `x-sentry-idempotent` set
([sentry#128999](getsentry/sentry#128999)).
`@sentry/api` exposes that as `idempotent` on each operation
([sentry-api-schema#111](getsentry/sentry-api-schema#111)).

**What changes**

- `@sentry/api` goes from `0.400.0` to `0.420.0` in the pnpm catalog, so
the CLI and MCP both get it.
- `generate:schema` also writes
`src/generated/idempotent-operations.json`. The file lists the non-safe
operations that the catalog marks `idempotent`, which today are 3 POSTs.
It is a separate file so the CLI does not load the 140 KB endpoint index
on every request. Like `banner-sixel.ts`, it is committed, because code
outside the CLI (such as `apps/local`) imports `sentry-client.ts`
without running `generate:schema`. Commit it again after an
`@sentry/api` bump. The build regenerates it either way.
- `isIdempotentOperation()` (`src/lib/idempotent-operations.ts`) matches
a request URL against those path templates. It matches from `/api/`
onward, so self-hosted installs served under a path prefix still match.
`fetchWithRetry` treats a match like an explicit opt-in.
- These call sites no longer pass `idempotent: true`: chunk uploads, DIF
assemble (debug files, ProGuard, Dart symbols), and artifact bundle
assemble (source maps, `pollAssembly`).
- Preprod assemble keeps its explicit opt-in. Sentry deliberately leaves
it unmarked, because a resend after the upload stage can create a second
artifact. The CLI's existing argument still applies: its poll loop
already resends the same body.

There is no behavior change for these endpoints today. They retried
before because of the opt-in, and now they retry because Sentry marks
them. If Sentry marks or unmarks an endpoint later, the next
`@sentry/api` bump picks it up.

**Verification**

- `pnpm run tsc` (whole workspace), CLI `pnpm run lint`, and root `pnpm
run lint` all pass.
- `apps/local` vitest (the `Local` CI job) passes with only the
committed generated files present: 38 files, 322 tests.
- MCP core vitest: 1881 passed.
- CLI unit tests: 11652 passed and 6 failed. The same 6 fail on
unmodified `main` in my sandbox. They are file-permission tests
(`EACCES`/`EPERM`, read-only dirs).
- New `test/lib/idempotent-operations.test.ts` covers matching, the
self-hosted prefix, case, and the trailing slash. It also checks that
unmarked writes (preprod assemble, autofix) don't match and that a
template segment can't span two path segments.
- New `sentry-client` tests check that a marked POST retries after a 503
and after a network error with no opt-in. I confirmed both fail when the
catalog lookup is removed.

Refs getsentry/sentry#128927

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3A828b4ad5f18c5305232eec52 -->

--

[View Junior Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3A828b4ad5f18c5305232eec52/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants