Skip to content

Commit c6496a3

Browse files
committed
revert: drop tolerance for malformed payloads
Handling values the SDK does not recognize is forward compatibility. Tolerating payloads that are malformed rather than merely new is a bug being swallowed, and the two were conflated here. Removes the non-Hash guard in process_data_attributes and the rescue in parse_datetime. A response that is not an object, or a timestamp that is not a timestamp, is a defect worth surfacing rather than reading as an empty resource or nil. The webhook path already rejects a non-object body with InvalidWebhookPayloadError, so nothing reaches process_data_attributes that way. Unrecognized handling is untouched: unknown event types still fall back to a generic SeamEvent, unknown error codes to the base error class, and unknown enum values read as themselves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M2kJ4nGaM8imZCVMEKjmXA
1 parent beaa628 commit c6496a3

2 files changed

Lines changed: 22 additions & 43 deletions

File tree

‎lib/seam/base_resource.rb‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,9 @@ def self.date_accessor(*attrs)
112112

113113
def parse_datetime(value)
114114
Time.parse(value)
115-
rescue ArgumentError, TypeError
116-
nil
117115
end
118116

119117
def process_data_attributes(data)
120-
return unless data.is_a?(Hash)
121-
122118
data.each do |key, value|
123119
next unless key.to_s.match?(/\A[a-zA-Z_][a-zA-Z0-9_]*\z/)
124120

spec/resources/total_parsing_spec.rb renamed to spec/resources/forward_compatibility_spec.rb

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
11
# frozen_string_literal: true
22

3-
# Reading a response must not fail on the shape of the payload.
4-
RSpec.describe "total parsing" do
5-
it "does not raise when the payload is not an object" do
6-
resource = Seam::Resources::Device.load_from_response(42)
7-
8-
expect(resource).to be_a(Seam::Resources::Device)
9-
expect(resource.device_id).to be_nil
10-
end
11-
12-
it "does not raise when a list payload holds values that are not objects" do
13-
resources = Seam::Resources::Device.load_from_response([1, 2])
14-
15-
expect(resources.map(&:class)).to eq(
16-
[Seam::Resources::Device, Seam::Resources::Device]
3+
# Seam adds event types, action types, and enum values between SDK releases.
4+
# Reading them must not raise; writing logic against them is what an upgrade is
5+
# for. The webhook event additionally carries the payload it was parsed from.
6+
RSpec.describe "forward compatibility" do
7+
it "reads an unknown enum value as itself" do
8+
device = Seam::Resources::Device.load_from_response(
9+
"device_id" => "device_1", "device_type" => "future_lock"
1710
)
11+
12+
expect(device.device_type).to eq("future_lock")
1813
end
1914

20-
it "reads a list property sent as a scalar as empty" do
21-
device = Seam::Resources::Device.load_from_response(
22-
"device_id" => "device_1", "errors" => "oops"
15+
it "falls back to a generic event for an unknown event type" do
16+
event = Seam::Resources::SeamEvent.load_from_response(
17+
"event_id" => "event_1", "event_type" => "future.thing"
2318
)
2419

25-
expect(device.errors).to eq([])
20+
expect(event.class).to eq(Seam::Resources::SeamEvent)
21+
expect(event.event_type).to eq("future.thing")
2622
end
2723

2824
it "keeps the rest of the resource when an error code is unknown" do
@@ -36,31 +32,19 @@
3632
expect(device.errors.first.error_code).to eq("brand_new")
3733
end
3834

39-
it "reads a malformed timestamp as nil" do
40-
device = Seam::Resources::Device.load_from_response(
41-
"device_id" => "device_1", "created_at" => "not a timestamp"
42-
)
43-
44-
expect(device.created_at).to be_nil
45-
end
46-
47-
it "reads a timestamp of the wrong type as nil" do
48-
device = Seam::Resources::Device.load_from_response(
49-
"device_id" => "device_1", "created_at" => 12345
50-
)
51-
52-
expect(device.created_at).to be_nil
53-
end
54-
55-
it "still parses a well-formed timestamp" do
56-
device = Seam::Resources::Device.load_from_response(
57-
"device_id" => "device_1", "created_at" => "2024-01-01T00:00:00Z"
35+
it "reads an unknown action attempt status as itself" do
36+
attempt = Seam::Resources::ActionAttempt.load_from_response(
37+
"action_attempt_id" => "attempt_1",
38+
"action_type" => "LOCK_DOOR",
39+
"status" => "cancelled"
5840
)
5941

60-
expect(device.created_at).to be_a(Time)
42+
expect(attempt.status).to eq("cancelled")
6143
end
6244
end
6345

46+
# Waiting promises a succeeded attempt or a raise, so an unrecognized status is
47+
# the one place the SDK must not stay quiet.
6448
RSpec.describe Seam::ActionAttemptUnknownStatusError do
6549
it "is raised rather than returning an unresolved attempt as a success" do
6650
attempt = Seam::Resources::ActionAttempt.load_from_response(
@@ -95,7 +79,6 @@
9579

9680
event = Seam::Resources::SeamEvent.load_from_response(payload)
9781

98-
expect(event.class).to eq(Seam::Resources::SeamEvent)
9982
expect(JSON.parse(event.raw_json)).to eq(payload)
10083
end
10184

0 commit comments

Comments
 (0)