Repository navigation
feat(operations): Expose idempotent flag in the operation catalog - #111
Merged
Merged
Conversation
Read Sentry's x-sentry-idempotent operation extension so retrying clients can tell which operations are safe to resend after a timeout or 5xx, instead of hard-coding it per call site. Refs getsentry/sentry#128927 Co-Authored-By: BYK <burak.kaya@sentry.io>
sentry-junior
Bot
requested review from
BYK,
MathurAditya724 and
betegon
as code owners
October 27, 2026 22:32
BYK
approved these changes
Oct 27, 2026
2 tasks done
julienhmmt
pushed a commit
to julienhmmt/fork-sentry
that referenced
this pull request
Oct 28, 2026
…ental (getsentry#128999) Follow-up to getsentry#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 getsentry#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. <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC08J1NSPU6S%3A1792772794.175999 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC08J1NSPU6S%3A1792772794.175999/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <burak.kaya@sentry.io>
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.
Adds
idempotent: booleanto everyApiOperationin@sentry/api/operations. Retrying clients such as the CLI and MCP can then decide from the catalog whether to resend a request after a timeout or 5xx, instead of hard-coding that per call site.Refs getsentry/sentry#128927. The Sentry side, getsentry/sentry#128930, now stamps
x-sentry-idempotent: trueon operations whose endpoint declares the method inidempotent_methods.Semantics
GET,HEAD,OPTIONS,TRACE: alwaystrue(RFC 9110 §9.2.1). Clients get one field to check, with no method special-casing.trueonly when the operation hasx-sentry-idempotent: true.PUTandDELETEare not assumed idempotent because not every Sentry handler follows RFC 9110 §9.2.2. Sentry makes the same choice.falsemeans "not known to be idempotent", not "known to be unsafe". It is documented that way in the type docs and the README.Current effect
Today's
openapi-derefed.jsonhas nox-sentry-idempotentmarkers. The endpoints declared so far (chunk upload, DIF assemble, artifact bundle assemble) are allPRIVATE, so nothing non-safe is marked yet. In the current build, 147 of 266 operations areidempotent: true, all safe methods. Non-safe operations will be marked as they're published with the declaration.Verification
bun run build,bun run typecheck,bun test(167 pass),bun run test:nodePOSTistrue; an unmarkedPOST/PUT/DELETEisfalse. Existing catalog fixtures asserttrueforGETandfalsefor an unmarkedPOST.via BYK.
--
View Junior Session in Sentry