Repository navigation
feat(apidocs): Publish chunk upload and assemble endpoints as experimental - #128999
Merged
Merged
Conversation
…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>
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.
BYK
approved these changes
Oct 28, 2026
evanpurkhiser
approved these changes
Oct 28, 2026
This was referenced Oct 28, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #128930. Publishes the endpoints that declare
idempotent_methodsso thex-sentry-idempotentmarkers 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_EXPERIMENTALand 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-onlyChunkUploadSerializer, following theReleaseFileUploadSerializerprecedent.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
extend_schemause, so the docs can't drift from what's enforced. Validation behavior is unchanged.patternProperties, so the docs version usesadditionalProperties, and the checksum key format is explained in the description._validate_request_bodyraisedKeyError: 'properties'on map bodies. It now checks field descriptions under the map'sadditionalProperties. The newMapRequestBodyTestcovers this.batch_assemble.stateisstr, not aLiteral, becauseChunkFileStatevalues are plain strings.acceptis now a list. It was a tuple, which spectacular documents as a fixed-length array, so the GET options response returnslist(CHUNK_UPLOAD_ACCEPT)instead. The JSON output is the same.format: uri. That's how spectacular rendersFileField, and it matches the existing release-file upload docs. Happy to override it tobinaryif reviewers prefer.Verification
sentry django spectacular --validate --fail-on-warnpasses. The deref build andopenapi-examples-validator --no-additional-propertiespass with no errors.ruff check,ruff format --check,flake8, andmypyare clean on the changed files.tests/apidocs/test_hooks.py,test_operation_id_uniqueness.py,test_chunk_upload.py,test_organization_artifactbundle_assemble.py(78 tests).test_dif_assemble.pyneeds an objectstore server, which my sandbox doesn't have, so CI is the authoritative run there. I checkedparse_assemble_request_payloaddirectly instead: it accepts valid payloads and rejects bad checksum keys, a missingname, non-hex chunks, and invaliddebug_id, the same as before.via BYK.
--
View Junior Session [Sentry]