feat: add UnrecognizedEvent and raw_json() to the webhook event - #654
Closed
razor-x wants to merge 2 commits into
Closed
feat: add UnrecognizedEvent and raw_json() to the webhook event#654razor-x wants to merge 2 commits into
razor-x wants to merge 2 commits into
Conversation
…ying raw_json
An event whose event_type this SDK version does not know deserialized to a
DeepAttrDict, where a missing key raises AttributeError rather than reading as
None. So a handler reading a field every event shares worked for every event
type the SDK knows and threw on the ones it does not:
ev = seam_event_from_dict({"event_id": "e", "event_type": "future.thing"})
ev.event_type # 'future.thing'
ev.workspace_id # AttributeError
Every other Seam SDK returns null here, because their fallback is a class that
declares the shared event fields. Python now matches: UnrecognizedEvent, built
from the generic event resource in the blueprint, with every shared field
optional. UnrecognizedActionAttempt does the same for an unrecognized
action_type or status, built from the properties every variant agrees on.
This drops ad-hoc access to fields the SDK does not know, which the previous
DeepAttrDict allowed. That is the intended trade -- writing logic against a new
field is what an upgrade is for -- but it means the payload has to stay
reachable, so events also gain raw_json():
json.loads(event.raw_json())["a_field_this_version_predates"]
Scoped to events, for the webhook verify return; a test asserts Device does not
have it. The retained payload is repr=False and compare=False, so it changes
neither the repr nor equality.
The declared SeamEvent and ActionAttempt unions are unchanged. Adding an
all-optional member would make every field on them optional for type checkers,
which costs callers more than the honest union buys them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA
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.
Stacked on #653. Retarget to
mainonce that merges.The hole
An event whose
event_typethis SDK version does not know deserialized to aDeepAttrDict, where a missing key raisesAttributeErrorrather than reading asNone:A handler reading a field every event shares worked for every event type the SDK knows and threw on the ones it does not — exactly backwards. Python was the only SDK with this behavior; Ruby, PHP, C# and JS all return null, because their fallback is a class declaring the shared event fields.
The fix
UnrecognizedEvent, built from the genericeventresource the blueprint already carries, with every shared field optional.UnrecognizedActionAttemptdoes the same for an unrecognizedaction_typeorstatus— there is no genericaction_attemptresource in the blueprint, so it is built from the properties every variant agrees on.Why raw_json() is in the same PR
This drops ad-hoc access to fields the SDK does not know, which
DeepAttrDictallowed. Three existing tests asserted exactly that (event.future_field.nested,unknown.future_api_field,event.foo.bar) and are updated here.That trade is intended — writing logic against a new field is what an upgrade is for — but it would be a pure capability loss without a way to reach the payload. So events also gain
raw_json():Scoped to events, for the webhook
verifyreturn; a test assertsDevicedoes not have it. A method rather than a property because the call is where the serialization happens. The retained payload isrepr=Falseandcompare=False, so it changes neither the repr nor equality.What is deliberately not changed
The declared
SeamEventandActionAttemptunions. Adding an all-optional member would make every field on them optional for type checkers —action_attempt_idbecomingstr | Nonealone produced 14 mypy errors in this repo, and would produce more in callers. The fallback is returned viacast, andisinstanceis how you detect it.282 tests, mypy clean, pylint 10.00, black clean.
🤖 Generated with Claude Code
https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA
Generated by Claude Code