laneful-ruby 1.1.0 → 1.2.0

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.
@@ -0,0 +1,452 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Laneful
4
+ # An unsubscribe group in a workspace
5
+ class UnsubscribeGroup
6
+ attr_reader :unsubscribe_group_id, :name, :created_at
7
+
8
+ def initialize(unsubscribe_group_id:, name:, created_at: 0)
9
+ @unsubscribe_group_id = unsubscribe_group_id
10
+ @name = name
11
+ @created_at = created_at
12
+ end
13
+
14
+ def self.from_hash(data)
15
+ new(
16
+ unsubscribe_group_id: (data['unsubscribe_group_id'] || 0).to_i,
17
+ name: data['name'] || '',
18
+ created_at: (data['created_at'] || 0).to_i
19
+ )
20
+ end
21
+
22
+ def to_hash
23
+ {
24
+ 'unsubscribe_group_id' => unsubscribe_group_id,
25
+ 'name' => name,
26
+ 'created_at' => created_at
27
+ }
28
+ end
29
+ end
30
+
31
+ # Paginated list of unsubscribe groups
32
+ class ListUnsubscribeGroupsResponse
33
+ attr_reader :unsubscribe_groups, :next_cursor
34
+
35
+ def initialize(unsubscribe_groups:, next_cursor: nil)
36
+ @unsubscribe_groups = unsubscribe_groups || []
37
+ @next_cursor = next_cursor
38
+ end
39
+
40
+ def self.from_hash(data)
41
+ groups = (data['unsubscribe_groups'] || []).map { |group| UnsubscribeGroup.from_hash(group) }
42
+ new(unsubscribe_groups: groups, next_cursor: data['next_cursor'])
43
+ end
44
+ end
45
+
46
+ # Query parameters for listing unsubscribe groups
47
+ class ListUnsubscribeGroupsParams
48
+ attr_reader :cursor, :limit, :search
49
+
50
+ def initialize(cursor: nil, limit: nil, search: nil)
51
+ @cursor = cursor
52
+ @limit = limit
53
+ @search = search
54
+ end
55
+
56
+ def to_query
57
+ query = {}
58
+ query['cursor'] = cursor if present?(cursor)
59
+ query['limit'] = limit if limit&.positive?
60
+ query['search'] = search if present?(search)
61
+ query
62
+ end
63
+
64
+ private
65
+
66
+ def present?(value)
67
+ !value.nil? && !value.to_s.empty?
68
+ end
69
+ end
70
+
71
+ # A sending domain and its verification state
72
+ class Domain
73
+ attr_reader :domain, :tracking, :return_path, :verified, :tracking_verified,
74
+ :return_path_verified, :dkim1_verified, :dkim2_verified,
75
+ :dmarc_verified, :require_tls, :email_track_id
76
+
77
+ def initialize(domain:, tracking: '', return_path: '', verified: false, tracking_verified: false,
78
+ return_path_verified: false, dkim1_verified: false, dkim2_verified: false,
79
+ dmarc_verified: false, require_tls: false, email_track_id: '')
80
+ @domain = domain
81
+ @tracking = tracking
82
+ @return_path = return_path
83
+ @verified = verified
84
+ @tracking_verified = tracking_verified
85
+ @return_path_verified = return_path_verified
86
+ @dkim1_verified = dkim1_verified
87
+ @dkim2_verified = dkim2_verified
88
+ @dmarc_verified = dmarc_verified
89
+ @require_tls = require_tls
90
+ @email_track_id = email_track_id
91
+ end
92
+
93
+ def self.from_hash(data)
94
+ new(
95
+ domain: data['domain'] || '',
96
+ tracking: data['tracking'] || '',
97
+ return_path: data['return_path'] || '',
98
+ verified: truthy?(data['verified']),
99
+ tracking_verified: truthy?(data['tracking_verified']),
100
+ return_path_verified: truthy?(data['return_path_verified']),
101
+ dkim1_verified: truthy?(data['dkim1_verified']),
102
+ dkim2_verified: truthy?(data['dkim2_verified']),
103
+ dmarc_verified: truthy?(data['dmarc_verified']),
104
+ require_tls: truthy?(data['require_tls']),
105
+ email_track_id: data['email_track_id'] || ''
106
+ )
107
+ end
108
+
109
+ def to_hash
110
+ {
111
+ 'domain' => domain,
112
+ 'tracking' => tracking,
113
+ 'return_path' => return_path,
114
+ 'verified' => verified,
115
+ 'tracking_verified' => tracking_verified,
116
+ 'return_path_verified' => return_path_verified,
117
+ 'dkim1_verified' => dkim1_verified,
118
+ 'dkim2_verified' => dkim2_verified,
119
+ 'dmarc_verified' => dmarc_verified,
120
+ 'require_tls' => require_tls,
121
+ 'email_track_id' => email_track_id
122
+ }
123
+ end
124
+
125
+ def self.truthy?(value)
126
+ value == true
127
+ end
128
+ end
129
+
130
+ # Paginated list of sending domains
131
+ class ListDomainsResponse
132
+ attr_reader :domains, :next_cursor
133
+
134
+ def initialize(domains:, next_cursor: nil)
135
+ @domains = domains || []
136
+ @next_cursor = next_cursor
137
+ end
138
+
139
+ def self.from_hash(data)
140
+ pagination = data['pagination'] || {}
141
+ new(
142
+ domains: (data['domains'] || []).map { |domain| Domain.from_hash(domain) },
143
+ next_cursor: pagination['next_cursor']
144
+ )
145
+ end
146
+ end
147
+
148
+ # Query parameters for listing domains
149
+ class ListDomainsParams
150
+ attr_reader :cursor, :limit, :filter_domain
151
+
152
+ def initialize(cursor: nil, limit: nil, filter_domain: nil)
153
+ @cursor = cursor
154
+ @limit = limit
155
+ @filter_domain = filter_domain
156
+ end
157
+
158
+ def to_query
159
+ query = {}
160
+ query['cursor'] = cursor if present?(cursor)
161
+ query['limit'] = limit if limit&.positive?
162
+ query['filter[domain]'] = filter_domain if present?(filter_domain)
163
+ query
164
+ end
165
+
166
+ private
167
+
168
+ def present?(value)
169
+ !value.nil? && !value.to_s.empty?
170
+ end
171
+ end
172
+
173
+ # Request body for creating a sending domain
174
+ class CreateDomainRequest
175
+ attr_reader :domain, :tracking, :return_path, :require_tls, :email_track_id
176
+
177
+ def initialize(domain:, tracking:, return_path:, require_tls: nil, email_track_id: nil)
178
+ @domain = domain
179
+ @tracking = tracking
180
+ @return_path = return_path
181
+ @require_tls = require_tls
182
+ @email_track_id = email_track_id
183
+ end
184
+
185
+ def to_hash
186
+ hash = {
187
+ 'domain' => domain,
188
+ 'tracking' => tracking,
189
+ 'return_path' => return_path
190
+ }
191
+ hash['require_tls'] = require_tls unless require_tls.nil?
192
+ hash['email_track_id'] = email_track_id if email_track_id && !email_track_id.empty?
193
+ hash
194
+ end
195
+ end
196
+
197
+ # Request body for updating a domain's mutable settings.
198
+ # Pass a track ID to set the email track, an empty string to clear it,
199
+ # or nil to leave it unchanged.
200
+ class UpdateDomainRequest
201
+ attr_reader :email_track_id
202
+
203
+ def initialize(email_track_id = nil)
204
+ @email_track_id = email_track_id
205
+ end
206
+
207
+ def to_hash
208
+ return {} if email_track_id.nil?
209
+
210
+ { 'email_track_id' => email_track_id }
211
+ end
212
+ end
213
+
214
+ # Generic success message from mutating endpoints
215
+ class SuccessResponse
216
+ attr_reader :message
217
+
218
+ def initialize(message:)
219
+ @message = message
220
+ end
221
+
222
+ def self.from_hash(data)
223
+ new(message: data['message'] || '')
224
+ end
225
+
226
+ def to_hash
227
+ { 'message' => message }
228
+ end
229
+ end
230
+
231
+ # A sending domain whose spam complaint ratio reached a critical level
232
+ class DomainSpamRatioRadar
233
+ attr_reader :workspace_id, :domain, :esp, :spam_ratio, :date
234
+
235
+ def initialize(workspace_id:, domain:, esp:, spam_ratio:, date:)
236
+ @workspace_id = workspace_id
237
+ @domain = domain
238
+ @esp = esp
239
+ @spam_ratio = spam_ratio
240
+ @date = date
241
+ end
242
+
243
+ def self.from_hash(data)
244
+ new(
245
+ workspace_id: (data['workspace_id'] || 0).to_i,
246
+ domain: data['domain'] || '',
247
+ esp: data['esp'] || '',
248
+ spam_ratio: (data['spam_ratio'] || 0).to_f,
249
+ date: (data['date'] || '').to_s
250
+ )
251
+ end
252
+ end
253
+
254
+ # Paginated list of domain spam-ratio radar entries
255
+ class ListDomainSpamRatioRadarResponse
256
+ attr_reader :radar, :next_cursor
257
+
258
+ def initialize(radar:, next_cursor: nil)
259
+ @radar = radar || []
260
+ @next_cursor = next_cursor
261
+ end
262
+
263
+ def self.from_hash(data)
264
+ new(
265
+ radar: (data['radar'] || []).map { |entry| DomainSpamRatioRadar.from_hash(entry) },
266
+ next_cursor: data['next_cursor']
267
+ )
268
+ end
269
+ end
270
+
271
+ # Query parameters for listing domain spam-ratio radar entries
272
+ class ListDomainSpamRatioRadarParams
273
+ attr_reader :workspace_ids, :domain, :start_date, :end_date, :cursor, :limit
274
+
275
+ def initialize(workspace_ids: [], domain: nil, start_date: nil, end_date: nil, cursor: nil, limit: nil)
276
+ @workspace_ids = workspace_ids || []
277
+ @domain = domain
278
+ @start_date = start_date
279
+ @end_date = end_date
280
+ @cursor = cursor
281
+ @limit = limit
282
+ end
283
+
284
+ def to_query
285
+ query = {}
286
+ query['workspace_ids'] = workspace_ids unless workspace_ids.empty?
287
+ query['domain'] = domain if present?(domain)
288
+ query['start_date'] = start_date if present?(start_date)
289
+ query['end_date'] = end_date if present?(end_date)
290
+ query['cursor'] = cursor if present?(cursor)
291
+ query['limit'] = limit if limit&.positive?
292
+ query
293
+ end
294
+
295
+ private
296
+
297
+ def present?(value)
298
+ !value.nil? && !value.to_s.empty?
299
+ end
300
+ end
301
+
302
+ # A daily Gmail spam-rate report from Google Postmaster Tools
303
+ class GooglePostmasterSpamReport
304
+ attr_reader :workspace_id, :domain, :date, :spam_ratio
305
+
306
+ def initialize(workspace_id:, domain:, date:, spam_ratio:)
307
+ @workspace_id = workspace_id
308
+ @domain = domain
309
+ @date = date
310
+ @spam_ratio = spam_ratio
311
+ end
312
+
313
+ def self.from_hash(data)
314
+ new(
315
+ workspace_id: (data['workspace_id'] || 0).to_i,
316
+ domain: data['domain'] || '',
317
+ date: (data['date'] || '').to_s,
318
+ spam_ratio: (data['spam_ratio'] || 0).to_f
319
+ )
320
+ end
321
+ end
322
+
323
+ # Paginated list of Google Postmaster spam reports
324
+ class ListGooglePostmasterSpamReportsResponse
325
+ attr_reader :spam_reports, :next_cursor
326
+
327
+ def initialize(spam_reports:, next_cursor: nil)
328
+ @spam_reports = spam_reports || []
329
+ @next_cursor = next_cursor
330
+ end
331
+
332
+ def self.from_hash(data)
333
+ new(
334
+ spam_reports: (data['spam_reports'] || []).map { |report| GooglePostmasterSpamReport.from_hash(report) },
335
+ next_cursor: data['next_cursor']
336
+ )
337
+ end
338
+ end
339
+
340
+ # Query parameters for listing Google Postmaster spam reports
341
+ class ListGooglePostmasterSpamReportsParams
342
+ attr_reader :workspace_ids, :domain, :start_date, :end_date, :cursor, :limit
343
+
344
+ def initialize(workspace_ids: [], domain: nil, start_date: nil, end_date: nil, cursor: nil, limit: nil)
345
+ @workspace_ids = workspace_ids || []
346
+ @domain = domain
347
+ @start_date = start_date
348
+ @end_date = end_date
349
+ @cursor = cursor
350
+ @limit = limit
351
+ end
352
+
353
+ def to_query
354
+ query = {}
355
+ query['workspace_ids'] = workspace_ids unless workspace_ids.empty?
356
+ query['domain'] = domain if present?(domain)
357
+ query['start_date'] = start_date if present?(start_date)
358
+ query['end_date'] = end_date if present?(end_date)
359
+ query['cursor'] = cursor if present?(cursor)
360
+ query['limit'] = limit if limit&.positive?
361
+ query
362
+ end
363
+
364
+ private
365
+
366
+ def present?(value)
367
+ !value.nil? && !value.to_s.empty?
368
+ end
369
+ end
370
+
371
+ # A daily Microsoft SNDS report for a sending IP
372
+ class SndsReport
373
+ FILTER_UNKNOWN = ''
374
+ FILTER_GREEN = 'GREEN'
375
+ FILTER_YELLOW = 'YELLOW'
376
+ FILTER_RED = 'RED'
377
+
378
+ attr_reader :ip, :date, :rcpt_commands, :data_commands, :message_recipients,
379
+ :filter_result, :complaint_rate, :trap_hits
380
+
381
+ def initialize(ip:, date:, rcpt_commands:, data_commands:, message_recipients:,
382
+ filter_result:, complaint_rate:, trap_hits:)
383
+ @ip = ip
384
+ @date = date
385
+ @rcpt_commands = rcpt_commands
386
+ @data_commands = data_commands
387
+ @message_recipients = message_recipients
388
+ @filter_result = filter_result
389
+ @complaint_rate = complaint_rate
390
+ @trap_hits = trap_hits
391
+ end
392
+
393
+ def self.from_hash(data)
394
+ new(
395
+ ip: data['ip'] || '',
396
+ date: (data['date'] || '').to_s,
397
+ rcpt_commands: (data['rcpt_commands'] || 0).to_i,
398
+ data_commands: (data['data_commands'] || 0).to_i,
399
+ message_recipients: (data['message_recipients'] || 0).to_i,
400
+ filter_result: data['filter_result'] || FILTER_UNKNOWN,
401
+ complaint_rate: (data['complaint_rate'] || 0).to_f,
402
+ trap_hits: (data['trap_hits'] || 0).to_i
403
+ )
404
+ end
405
+ end
406
+
407
+ # Paginated list of Microsoft SNDS reports
408
+ class ListSndsReportsResponse
409
+ attr_reader :snds_reports, :next_cursor
410
+
411
+ def initialize(snds_reports:, next_cursor: nil)
412
+ @snds_reports = snds_reports || []
413
+ @next_cursor = next_cursor
414
+ end
415
+
416
+ def self.from_hash(data)
417
+ new(
418
+ snds_reports: (data['snds_reports'] || []).map { |report| SndsReport.from_hash(report) },
419
+ next_cursor: data['next_cursor']
420
+ )
421
+ end
422
+ end
423
+
424
+ # Query parameters for listing Microsoft SNDS reports
425
+ class ListSndsReportsParams
426
+ attr_reader :ip, :start_date, :end_date, :cursor, :limit
427
+
428
+ def initialize(ip: nil, start_date: nil, end_date: nil, cursor: nil, limit: nil)
429
+ @ip = ip
430
+ @start_date = start_date
431
+ @end_date = end_date
432
+ @cursor = cursor
433
+ @limit = limit
434
+ end
435
+
436
+ def to_query
437
+ query = {}
438
+ query['ip'] = ip if present?(ip)
439
+ query['start_date'] = start_date if present?(start_date)
440
+ query['end_date'] = end_date if present?(end_date)
441
+ query['cursor'] = cursor if present?(cursor)
442
+ query['limit'] = limit if limit&.positive?
443
+ query
444
+ end
445
+
446
+ private
447
+
448
+ def present?(value)
449
+ !value.nil? && !value.to_s.empty?
450
+ end
451
+ end
452
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Laneful
4
- VERSION = '1.1.0'
4
+ VERSION = '1.2.0'
5
5
  end
@@ -12,7 +12,7 @@ module Laneful
12
12
 
13
13
  # Valid event types as documented
14
14
  VALID_EVENT_TYPES = %w[
15
- delivery open click drop spam_complaint unsubscribe bounce
15
+ request delivery open click drop spam_complaint unsubscribe bounce
16
16
  ].freeze
17
17
 
18
18
  # UUID pattern for lane_id validation
data/lib/laneful.rb CHANGED
@@ -8,6 +8,7 @@ require 'base64'
8
8
  require_relative 'laneful/version'
9
9
  require_relative 'laneful/exceptions'
10
10
  require_relative 'laneful/models'
11
+ require_relative 'laneful/org_models'
11
12
  require_relative 'laneful/client'
12
13
  require_relative 'laneful/webhooks'
13
14
 
@@ -23,5 +24,5 @@ module Laneful
23
24
  DEFAULT_TIMEOUT = 30
24
25
 
25
26
  # User agent string
26
- USER_AGENT = 'laneful-ruby/1.0.1'
27
+ USER_AGENT = "laneful-ruby/#{VERSION}".freeze
27
28
  end
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.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laneful Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-08 00:00:00.000000000 Z
11
+ date: 2026-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -46,21 +46,13 @@ executables: []
46
46
  extensions: []
47
47
  extra_rdoc_files: []
48
48
  files:
49
- - ".rubocop.yml"
50
- - LICENSE
51
- - Makefile
52
- - README.md
53
- - Rakefile
54
- - examples/Gemfile
55
- - examples/simple_example.rb
56
- - laneful-ruby.gemspec
57
49
  - lib/laneful.rb
58
50
  - lib/laneful/client.rb
59
51
  - lib/laneful/exceptions.rb
60
52
  - lib/laneful/models.rb
53
+ - lib/laneful/org_models.rb
61
54
  - lib/laneful/version.rb
62
55
  - lib/laneful/webhooks.rb
63
- - scripts/publish.sh
64
56
  homepage: https://github.com/laneful/laneful-ruby
65
57
  licenses:
66
58
  - MIT
data/.rubocop.yml DELETED
@@ -1,31 +0,0 @@
1
- AllCops:
2
- NewCops: enable
3
- TargetRubyVersion: 3.0
4
- SuggestExtensions: false
5
-
6
- # Disable some strict rules for test files and gemspec
7
- Metrics/BlockLength:
8
- Exclude:
9
- - 'spec/**/*'
10
- - '*.gemspec'
11
-
12
- Metrics/MethodLength:
13
- Max: 30
14
-
15
- Metrics/ClassLength:
16
- Max: 150
17
-
18
- Metrics/AbcSize:
19
- Max: 80
20
-
21
- Metrics/CyclomaticComplexity:
22
- Max: 40
23
-
24
- Metrics/PerceivedComplexity:
25
- Max: 40
26
-
27
- Metrics/ParameterLists:
28
- Max: 5
29
-
30
- Layout/LineLength:
31
- Max: 120
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Laneful
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
data/Makefile DELETED
@@ -1,49 +0,0 @@
1
- # Laneful Ruby SDK Makefile
2
-
3
- .PHONY: help test lint build publish clean install setup
4
-
5
- # Default target
6
- help:
7
- @echo "Available commands:"
8
- @echo " setup - Install dependencies"
9
- @echo " test - Run tests"
10
- @echo " lint - Run code linting"
11
- @echo " build - Build the gem"
12
- @echo " publish - Publish to RubyGems (requires RUBYGEMS_API_KEY)"
13
- @echo " install - Install gem locally"
14
- @echo " clean - Clean up built gems"
15
-
16
- # Install dependencies
17
- setup:
18
- bundle install
19
-
20
- # Run tests
21
- test:
22
- bundle exec rspec
23
-
24
- # Run linting
25
- lint:
26
- bundle exec rubocop
27
-
28
- # Build gem
29
- build:
30
- gem build laneful-ruby.gemspec
31
-
32
- # Publish to RubyGems
33
- publish:
34
- @if [ -z "$$GEM_HOST_API_KEY" ]; then \
35
- echo "Error: GEM_HOST_API_KEY environment variable is not set"; \
36
- echo "Please set your RubyGems API key: export GEM_HOST_API_KEY=your_api_key_here"; \
37
- exit 1; \
38
- fi
39
- ./scripts/publish.sh
40
-
41
- # Install gem locally
42
- install:
43
- gem build laneful-ruby.gemspec
44
- gem install laneful-ruby-*.gem
45
-
46
- # Clean up
47
- clean:
48
- rm -f laneful-ruby-*.gem
49
-