laneful-ruby 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed5ff94086f558d6ea4bad6c914ec22cbc7b8d012cc27fc3d385d3cbc05c5c49
4
- data.tar.gz: 02a6ba4d5c4dbee58809d4e15214623fc273770b3779d73fe6ae25b61de75252
3
+ metadata.gz: 3ebb676795d8c7c6b9f26ed1387e0420234f835ab57987deac701ab5cb1bef46
4
+ data.tar.gz: f75807a039ecf0b68ae2ad01ad6d6c613dc5986bec1543cdcda811ee270186f1
5
5
  SHA512:
6
- metadata.gz: cc30d8a4112104938f451247b7c24654365f4bb6f8efb5dbeacff4947b00841115563bc7e93d4e36b2eb17e3f115a3616d91a99ebff081d0f322fc9ebdca2060
7
- data.tar.gz: 560628e6bd7d5cefa3b9e3229c4be1806d8703d6a7c61e919b94d386b9b3e4a27f1ea95602096e5783236489f4c234233676ebeadf42591eb3db767ec4cf6875
6
+ metadata.gz: ffe6cbc2e0438a8100b61d8a206d088161617d1664126ec7bfe646d2523a033ec580055011cb7b17de4bea39a4e61ce152dda004dabd2d9f036a7ca3c970b5da
7
+ data.tar.gz: fd6c6e8c27c2b712fcae84ab1c424598949259c7c1a13fb80bbb40466e8d67e21d7824d20c7da5c9af0c480d1e756b8e7f7a55fb9b6e0fda8a8933a4ec2f61a0
@@ -115,19 +115,26 @@ module Laneful
115
115
  end
116
116
  end
117
117
 
118
- # Request-level mail settings (sandbox mode, return message IDs)
118
+ # Request-level mail settings (sandbox mode, return message IDs, idempotency key).
119
+ #
120
+ # idempotency_key protects against duplicate sends: Laneful accepts a key once per
121
+ # organization for one hour. A repeated request with the same key is answered with
122
+ # status "accepted" (and no message IDs) and sends nothing. Keys are 1 to 36 bytes.
119
123
  class MailSettings
120
- attr_reader :sandbox_mode, :return_message_ids
124
+ attr_reader :sandbox_mode, :return_message_ids, :idempotency_key
121
125
 
122
- def initialize(sandbox_mode: nil, return_message_ids: nil)
126
+ def initialize(sandbox_mode: nil, return_message_ids: nil, idempotency_key: nil)
123
127
  @sandbox_mode = sandbox_mode
124
128
  @return_message_ids = return_message_ids
129
+ @idempotency_key = idempotency_key
130
+ validate_idempotency_key!
125
131
  end
126
132
 
127
133
  def self.from_hash(data)
128
134
  new(
129
135
  sandbox_mode: data['sandbox_mode'],
130
- return_message_ids: data['return_message_ids']
136
+ return_message_ids: data['return_message_ids'],
137
+ idempotency_key: data['idempotency_key']
131
138
  )
132
139
  end
133
140
 
@@ -135,13 +142,15 @@ module Laneful
135
142
  hash = {}
136
143
  hash['sandbox_mode'] = sandbox_mode unless sandbox_mode.nil?
137
144
  hash['return_message_ids'] = return_message_ids unless return_message_ids.nil?
145
+ hash['idempotency_key'] = idempotency_key unless idempotency_key.nil?
138
146
  hash
139
147
  end
140
148
 
141
149
  def ==(other)
142
150
  return false unless other.is_a?(MailSettings)
143
151
 
144
- sandbox_mode == other.sandbox_mode && return_message_ids == other.return_message_ids
152
+ sandbox_mode == other.sandbox_mode && return_message_ids == other.return_message_ids &&
153
+ idempotency_key == other.idempotency_key
145
154
  end
146
155
 
147
156
  def eql?(other)
@@ -149,7 +158,16 @@ module Laneful
149
158
  end
150
159
 
151
160
  def hash
152
- [sandbox_mode, return_message_ids].hash
161
+ [sandbox_mode, return_message_ids, idempotency_key].hash
162
+ end
163
+
164
+ private
165
+
166
+ def validate_idempotency_key!
167
+ return if idempotency_key.nil?
168
+ return if idempotency_key.bytesize.between?(1, 36)
169
+
170
+ raise ValidationException, 'idempotency_key must contain between 1 and 36 bytes'
153
171
  end
154
172
  end
155
173
 
@@ -403,9 +421,10 @@ module Laneful
403
421
  if webhook_data
404
422
  raise ValidationException, 'Webhook data cannot have more than 20 keys' if webhook_data.size > 20
405
423
 
424
+ # The send API counts UTF-8 bytes, so "é" counts as 2.
406
425
  webhook_data.each do |key, value|
407
- raise ValidationException, 'Webhook data keys cannot exceed 50 characters' if key.to_s.length > 50
408
- raise ValidationException, 'Webhook data values cannot exceed 100 characters' if value.to_s.length > 100
426
+ raise ValidationException, 'Webhook data keys cannot exceed 50 bytes (UTF-8)' if key.to_s.bytesize > 50
427
+ raise ValidationException, 'Webhook data values cannot exceed 100 bytes (UTF-8)' if value.to_s.bytesize > 100
409
428
  end
410
429
  end
411
430
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Laneful
4
- VERSION = '1.2.0'
4
+ VERSION = '1.3.1'
5
5
  end
@@ -10,14 +10,12 @@ module Laneful
10
10
  SIGNATURE_PREFIX = 'sha256='
11
11
  SIGNATURE_HEADER_NAME = 'x-webhook-signature'
12
12
 
13
- # Valid event types as documented
13
+ # Documented event types. Informational: parse_webhook_payload keeps events of other
14
+ # types, so new event types don't make the whole payload fail.
14
15
  VALID_EVENT_TYPES = %w[
15
16
  request delivery open click drop spam_complaint unsubscribe bounce
16
17
  ].freeze
17
18
 
18
- # UUID pattern for lane_id validation
19
- UUID_PATTERN = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i
20
-
21
19
  # Verifies a webhook signature with support for sha256= prefix
22
20
  # @param secret [String] The webhook secret
23
21
  # @param payload [String] The webhook payload
@@ -133,9 +131,7 @@ module Laneful
133
131
  raise ArgumentError, "Missing required field: #{field}" unless event.key?(field)
134
132
  end
135
133
 
136
- # Validate event type
137
- event_type = event['event']
138
- raise ArgumentError, "Invalid event type: #{event_type}" unless VALID_EVENT_TYPES.include?(event_type)
134
+ raise ArgumentError, 'Missing required field: event' if event['event'].to_s.empty?
139
135
 
140
136
  # Validate email format
141
137
  email = event['email']
@@ -149,9 +145,7 @@ module Laneful
149
145
  raise ArgumentError, 'Invalid timestamp format'
150
146
  end
151
147
 
152
- # Validate lane_id is a valid UUID format
153
- lane_id = event['lane_id']
154
- raise ArgumentError, "Invalid lane_id format: #{lane_id}" unless UUID_PATTERN.match?(lane_id)
148
+ # lane_id isn't checked: request events have an empty lane_id.
155
149
 
156
150
  # Return event with all fields (required + optional)
157
151
  event
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: laneful-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laneful Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-09-12 00:00:00.000000000 Z
11
+ date: 2026-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -53,14 +53,14 @@ files:
53
53
  - lib/laneful/org_models.rb
54
54
  - lib/laneful/version.rb
55
55
  - lib/laneful/webhooks.rb
56
- homepage: https://github.com/laneful/laneful-ruby
56
+ homepage: https://github.com/lanefulhq/laneful-ruby
57
57
  licenses:
58
58
  - MIT
59
59
  metadata:
60
- homepage_uri: https://github.com/laneful/laneful-ruby
61
- source_code_uri: https://github.com/laneful/laneful-ruby
62
- changelog_uri: https://github.com/laneful/laneful-ruby/blob/main/CHANGELOG.md
63
- documentation_uri: https://docs.laneful.com/ruby
60
+ homepage_uri: https://github.com/lanefulhq/laneful-ruby
61
+ source_code_uri: https://github.com/lanefulhq/laneful-ruby
62
+ changelog_uri: https://github.com/lanefulhq/laneful-ruby/blob/main/CHANGELOG.md
63
+ documentation_uri: https://laneful.com/docs/ruby-sdk/
64
64
  rubygems_mfa_required: 'true'
65
65
  post_install_message:
66
66
  rdoc_options: []