Skip to content

Commit 6cb4b95

Browse files
Raise on invalid timestamps instead of silently dropping them (#144)
Previously track(), track_anonymous(), and track_delivery_metric() silently discarded invalid timestamps (milliseconds, strings, Time objects). This was inconsistent with track_push_notification_event which raises ParamError. Now all methods raise ParamError with a descriptive message when an invalid timestamp is provided.
1 parent f7c16da commit 6cb4b95

2 files changed

Lines changed: 32 additions & 57 deletions

File tree

lib/customerio/client.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ def track_delivery_metric(metric_name, attributes = {})
141141
raise ParamError, "delivery_id must be a non-empty string" if empty?(attributes[:delivery_id])
142142

143143
body = { delivery_id: attributes[:delivery_id], metric: metric_name }
144+
validate_timestamp!(attributes[:timestamp])
144145
body[:timestamp] = attributes[:timestamp] if valid_timestamp?(attributes[:timestamp])
145146
body[:recipient] = attributes[:recipient] unless empty?(attributes[:recipient])
146147
body[:reason] = attributes[:reason] unless empty?(attributes[:reason])
@@ -271,8 +272,11 @@ def create_pageview_event(customer_id, page, attributes = {})
271272

272273
def create_event(url:, event_name:, anonymous_id: nil, event_type: nil, attributes: {}, id: nil, timestamp: nil) # rubocop:disable Metrics/ParameterLists
273274
body = { name: event_name, data: attributes }
274-
body[:timestamp] = timestamp if valid_timestamp?(timestamp)
275-
body[:timestamp] = attributes[:timestamp] if body[:timestamp].nil? && valid_timestamp?(attributes[:timestamp])
275+
effective_timestamp = timestamp || attributes[:timestamp]
276+
if effective_timestamp
277+
validate_timestamp!(effective_timestamp)
278+
body[:timestamp] = effective_timestamp
279+
end
276280
body[:anonymous_id] = anonymous_id unless empty?(anonymous_id)
277281
body[:type] = event_type unless empty?(event_type)
278282
body[:id] = id unless empty?(id)
@@ -284,6 +288,13 @@ def valid_timestamp?(timestamp)
284288
timestamp.is_a?(Integer) && timestamp > 999_999_999 && timestamp < 100_000_000_000
285289
end
286290

291+
def validate_timestamp!(timestamp)
292+
return if timestamp.nil?
293+
return if valid_timestamp?(timestamp)
294+
295+
raise ParamError, "timestamp must be a valid integer (seconds since epoch, 10-digit range)"
296+
end
297+
287298
def empty?(val)
288299
val.nil? || (val.is_a?(String) && val.strip.empty?)
289300
end

spec/client_spec.rb

Lines changed: 19 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -433,49 +433,22 @@ def json(data)
433433
client.track(5, "purchase", {type: "socks", price: "13.99", timestamp: 1561231234})
434434
end
435435

436-
it "doesn't send timestamp if timestamp is in milliseconds" do
437-
stub_request(:post, api_uri('/api/v1/customers/5/events')).
438-
with(body: json({
439-
name: "purchase",
440-
data: {
441-
type: "socks",
442-
price: "13.99"
443-
}
444-
})).
445-
to_return(status: 200, body: "", headers: {})
446-
447-
client.track(5, "purchase", {type: "socks", price: "13.99"}, timestamp: 1561231234000)
436+
it "raises an error if timestamp is in milliseconds" do
437+
lambda {
438+
client.track(5, "purchase", {type: "socks", price: "13.99"}, timestamp: 1561231234000)
439+
}.should raise_error(Customerio::Client::ParamError, /timestamp must be a valid integer/)
448440
end
449441

450-
it "doesn't send timestamp if timestamp is a date" do
451-
date = Time.now
452-
453-
stub_request(:post, api_uri('/api/v1/customers/5/events')).
454-
with(body: {
455-
name: "purchase",
456-
data: {
457-
type: "socks",
458-
price: "13.99"
459-
}
460-
}).
461-
to_return(status: 200, body: "", headers: {})
462-
463-
client.track(5, "purchase", {type: "socks", price: "13.99"}, timestamp: date)
442+
it "raises an error if timestamp is a date" do
443+
lambda {
444+
client.track(5, "purchase", {type: "socks", price: "13.99"}, timestamp: Time.now)
445+
}.should raise_error(Customerio::Client::ParamError, /timestamp must be a valid integer/)
464446
end
465447

466-
it "doesn't send timestamp if timestamp isn't an integer" do
467-
stub_request(:post, api_uri('/api/v1/customers/5/events')).
468-
with(body: json({
469-
name: "purchase",
470-
data: {
471-
type: "socks",
472-
price: "13.99"
473-
}
474-
})).
475-
476-
to_return(status: 200, body: "", headers: {})
477-
478-
client.track(5, "purchase", {type: "socks", price: "13.99"}, timestamp: "Hello world")
448+
it "raises an error if timestamp isn't an integer" do
449+
lambda {
450+
client.track(5, "purchase", {type: "socks", price: "13.99"}, timestamp: "Hello world")
451+
}.should raise_error(Customerio::Client::ParamError, /timestamp must be a valid integer/)
479452
end
480453

481454
it "sends an event id for deduplication when provided" do
@@ -845,22 +818,13 @@ def json(data)
845818
})
846819
end
847820

848-
it "omits timestamp when not a valid integer" do
849-
stub_request(:post, api_uri("/api/v1/metrics")).
850-
with(
851-
:body => json({
852-
:delivery_id => "abc123",
853-
:metric => "delivered"
854-
}),
855-
:headers => {
856-
"Content-Type" => "application/json"
857-
}).
858-
to_return(:status => 200, :body => "", :headers => {})
859-
860-
client.track_delivery_metric("delivered", {
861-
:delivery_id => "abc123",
862-
:timestamp => "not-a-timestamp"
863-
})
821+
it "raises an error when timestamp is not a valid integer" do
822+
lambda {
823+
client.track_delivery_metric("delivered", {
824+
:delivery_id => "abc123",
825+
:timestamp => "not-a-timestamp"
826+
})
827+
}.should raise_error(Customerio::Client::ParamError, /timestamp must be a valid integer/)
864828
end
865829

866830
it "should raise if metric_name is invalid" do

0 commit comments

Comments
 (0)