mailtrap 2.17.0 → 2.19.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/lib/mailtrap/inbound_forward_outcome.rb +21 -0
- data/lib/mailtrap/inbound_forward_rule.rb +21 -0
- data/lib/mailtrap/inbound_forward_rule_condition.rb +17 -0
- data/lib/mailtrap/inbound_forward_rule_destination.rb +11 -0
- data/lib/mailtrap/inbound_forward_rules_api.rb +106 -0
- data/lib/mailtrap/inbound_message.rb +2 -0
- data/lib/mailtrap/inbound_messages_api.rb +8 -0
- data/lib/mailtrap/inbound_thread_message.rb +5 -8
- data/lib/mailtrap/inbound_thread_message_delivery.rb +17 -0
- data/lib/mailtrap/inbound_threads_api.rb +17 -2
- data/lib/mailtrap/marketing_stats.rb +39 -0
- data/lib/mailtrap/marketing_stats_api.rb +43 -0
- data/lib/mailtrap/stats_api.rb +2 -34
- data/lib/mailtrap/stats_query_params.rb +29 -0
- data/lib/mailtrap/version.rb +1 -1
- data/lib/mailtrap.rb +2 -0
- metadata +11 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2749218455a3db4dce0286b7c6aacda197ac2fa7e97547def63703d5df982aa9
|
|
4
|
+
data.tar.gz: c324fcd9a6d8b788609ae48f5a4a1ca68aaf74887a036c6625631b1e643f79b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c739fdf5614960c3e36a239ab922d879a91fe6063652f8bf3ef7dfd2b22f145a969fd72cbd2c6b0dcdf977481e1fa5f35d37104fad2d6fc2d71f83eed136fa2
|
|
7
|
+
data.tar.gz: cfd90df52872218ddfbb80082a885ccb07898e7e1fc42fef8da12361d9e614e58bfe0e8672a75c5e4238974dd22615000a997001b9c78eac19709e09396aa45b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## [2.19.0] - 2026-10-29
|
|
2
|
+
|
|
3
|
+
## What's Changed
|
|
4
|
+
* Add MarketingStatsAPI for GET /api/marketing/stats by @Rabsztok in https://github.com/mailtrap/mailtrap-ruby/pull/136
|
|
5
|
+
* Fix CI on Ubuntu 26.04 by testing Ruby series, not exact patches by @Rabsztok in https://github.com/mailtrap/mailtrap-ruby/pull/137
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
**Full Changelog**: https://github.com/mailtrap/mailtrap-ruby/compare/v2.18.0...v2.19.0
|
|
9
|
+
|
|
10
|
+
## [2.18.0] - 2026-10-07
|
|
11
|
+
|
|
12
|
+
## What's Changed
|
|
13
|
+
* Add inbound forward rules, forward outcomes, thread delivery and search by @mklocek in https://github.com/mailtrap/mailtrap-ruby/pull/132
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
**Full Changelog**: https://github.com/mailtrap/mailtrap-ruby/compare/v2.17.0...v2.18.0
|
|
17
|
+
|
|
1
18
|
## [2.17.0] - 2026-10-07
|
|
2
19
|
|
|
3
20
|
## What's Changed
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -195,6 +195,7 @@ Inbound Email:
|
|
|
195
195
|
- Inbound Inboxes API – [`inbound_inboxes_api.rb`](examples/inbound_inboxes_api.rb)
|
|
196
196
|
- Inbound Messages API (list, get, delete, reply, reply_all, forward) – [`inbound_messages_api.rb`](examples/inbound_messages_api.rb)
|
|
197
197
|
- Inbound Threads API – [`inbound_threads_api.rb`](examples/inbound_threads_api.rb)
|
|
198
|
+
- Inbound Forward Rules API – [`inbound_forward_rules_api.rb`](examples/inbound_forward_rules_api.rb)
|
|
198
199
|
|
|
199
200
|
Contact management:
|
|
200
201
|
|
|
@@ -203,6 +204,7 @@ Contact management:
|
|
|
203
204
|
Email marketing:
|
|
204
205
|
|
|
205
206
|
- Email Campaigns CRUD, Lifecycle Actions & Stats – [`email_campaigns_api.rb`](examples/email_campaigns_api.rb)
|
|
207
|
+
- Email Marketing Stats API – [`marketing_stats_api.rb`](examples/marketing_stats_api.rb)
|
|
206
208
|
|
|
207
209
|
General:
|
|
208
210
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound forward outcome
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader rule_id [Integer] The forward rule ID
|
|
7
|
+
# @attr_reader rule_name [String, nil] The forward rule name
|
|
8
|
+
# @attr_reader destination [String] The destination address
|
|
9
|
+
# @attr_reader status [String] forwarded or rejected
|
|
10
|
+
# @attr_reader reason [String, nil] The rejection reason
|
|
11
|
+
# @attr_reader message_id [String, nil] The forwarded message ID
|
|
12
|
+
InboundForwardOutcome = Struct.new(
|
|
13
|
+
:rule_id,
|
|
14
|
+
:rule_name,
|
|
15
|
+
:destination,
|
|
16
|
+
:status,
|
|
17
|
+
:reason,
|
|
18
|
+
:message_id,
|
|
19
|
+
keyword_init: true
|
|
20
|
+
)
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound forward rule
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader id [Integer] The forward rule ID
|
|
7
|
+
# @attr_reader name [String] The rule name
|
|
8
|
+
# @attr_reader created_at [String] Creation timestamp
|
|
9
|
+
# @attr_reader updated_at [String] Last update timestamp
|
|
10
|
+
# @attr_reader conditions [Array<InboundForwardRuleCondition>] The rule conditions
|
|
11
|
+
# @attr_reader destinations [Array<InboundForwardRuleDestination>] The rule destinations
|
|
12
|
+
InboundForwardRule = Struct.new(
|
|
13
|
+
:id,
|
|
14
|
+
:name,
|
|
15
|
+
:created_at,
|
|
16
|
+
:updated_at,
|
|
17
|
+
:conditions,
|
|
18
|
+
:destinations,
|
|
19
|
+
keyword_init: true
|
|
20
|
+
)
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound forward rule condition
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader match_type [String] The match type
|
|
7
|
+
# @attr_reader operator [String] The comparison operator
|
|
8
|
+
# @attr_reader value [String, nil] The value to compare against
|
|
9
|
+
# @attr_reader header_key [String, nil] The header name
|
|
10
|
+
InboundForwardRuleCondition = Struct.new(
|
|
11
|
+
:match_type,
|
|
12
|
+
:operator,
|
|
13
|
+
:value,
|
|
14
|
+
:header_key,
|
|
15
|
+
keyword_init: true
|
|
16
|
+
)
|
|
17
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound forward rule destination
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader email [String] The destination email address
|
|
7
|
+
InboundForwardRuleDestination = Struct.new(
|
|
8
|
+
:email,
|
|
9
|
+
keyword_init: true
|
|
10
|
+
)
|
|
11
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_api'
|
|
4
|
+
require_relative 'inbound_forward_rule'
|
|
5
|
+
require_relative 'inbound_forward_rule_condition'
|
|
6
|
+
require_relative 'inbound_forward_rule_destination'
|
|
7
|
+
|
|
8
|
+
module Mailtrap
|
|
9
|
+
class InboundForwardRulesAPI
|
|
10
|
+
include BaseAPI
|
|
11
|
+
|
|
12
|
+
self.supported_options = %i[name conditions destinations]
|
|
13
|
+
self.response_class = InboundForwardRule
|
|
14
|
+
|
|
15
|
+
# @param client [Mailtrap::Client] The client instance
|
|
16
|
+
def initialize(client = Mailtrap::Client.new)
|
|
17
|
+
@client = client
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Lists forward rules in an inbox
|
|
21
|
+
# @param inbox_id [Integer] The inbox ID
|
|
22
|
+
# @return [Array<InboundForwardRule>] Array of forward rules
|
|
23
|
+
# @!macro api_errors
|
|
24
|
+
def list(inbox_id)
|
|
25
|
+
response = client.get(forward_rules_path(inbox_id))
|
|
26
|
+
Array(response[:data]).map { |item| build_rule(item) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Retrieves a specific forward rule
|
|
30
|
+
# @param inbox_id [Integer] The inbox ID
|
|
31
|
+
# @param forward_rule_id [Integer] The forward rule ID
|
|
32
|
+
# @return [InboundForwardRule] Forward rule object
|
|
33
|
+
# @!macro api_errors
|
|
34
|
+
def get(inbox_id, forward_rule_id)
|
|
35
|
+
handle_response(client.get("#{forward_rules_path(inbox_id)}/#{forward_rule_id}"))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Creates a new forward rule
|
|
39
|
+
# @param inbox_id [Integer] The inbox ID
|
|
40
|
+
# @param [Hash] options The parameters to create
|
|
41
|
+
# @option options [String] :name The rule name
|
|
42
|
+
# @option options [Array<Hash>] :conditions The rule conditions
|
|
43
|
+
# @option options [Array<Hash>] :destinations The rule destinations
|
|
44
|
+
# @return [InboundForwardRule] Created forward rule
|
|
45
|
+
# @!macro api_errors
|
|
46
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
47
|
+
def create(inbox_id, options)
|
|
48
|
+
validate_options!(options, supported_options)
|
|
49
|
+
handle_response(client.post(forward_rules_path(inbox_id), options))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Updates a forward rule
|
|
53
|
+
# @param inbox_id [Integer] The inbox ID
|
|
54
|
+
# @param forward_rule_id [Integer] The forward rule ID
|
|
55
|
+
# @param [Hash] options The parameters to update
|
|
56
|
+
# @option options [String] :name The rule name
|
|
57
|
+
# @option options [Array<Hash>] :conditions The rule conditions
|
|
58
|
+
# @option options [Array<Hash>] :destinations The rule destinations
|
|
59
|
+
# @return [InboundForwardRule] Updated forward rule
|
|
60
|
+
# @!macro api_errors
|
|
61
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
62
|
+
def update(inbox_id, forward_rule_id, options)
|
|
63
|
+
validate_options!(options, supported_options)
|
|
64
|
+
handle_response(client.patch("#{forward_rules_path(inbox_id)}/#{forward_rule_id}", options))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Deletes a forward rule
|
|
68
|
+
# @param inbox_id [Integer] The inbox ID
|
|
69
|
+
# @param forward_rule_id [Integer] The forward rule ID
|
|
70
|
+
# @return nil
|
|
71
|
+
# @!macro api_errors
|
|
72
|
+
def delete(inbox_id, forward_rule_id)
|
|
73
|
+
client.delete("#{forward_rules_path(inbox_id)}/#{forward_rule_id}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def forward_rules_path(inbox_id)
|
|
79
|
+
"/api/inbound/inboxes/#{inbox_id}/forward_rules"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def handle_response(response)
|
|
83
|
+
build_rule(response[:data])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def build_rule(hash)
|
|
87
|
+
attrs = hash.slice(*InboundForwardRule.members)
|
|
88
|
+
attrs[:conditions] = build_conditions(attrs[:conditions]) if attrs[:conditions]
|
|
89
|
+
attrs[:destinations] = build_destinations(attrs[:destinations]) if attrs[:destinations]
|
|
90
|
+
|
|
91
|
+
InboundForwardRule.new(**attrs)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def build_conditions(conditions)
|
|
95
|
+
Array(conditions).map do |condition|
|
|
96
|
+
InboundForwardRuleCondition.new(**condition.slice(*InboundForwardRuleCondition.members))
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def build_destinations(destinations)
|
|
101
|
+
Array(destinations).map do |destination|
|
|
102
|
+
InboundForwardRuleDestination.new(**destination.slice(*InboundForwardRuleDestination.members))
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -25,6 +25,7 @@ module Mailtrap
|
|
|
25
25
|
# @attr_reader raw_message_expires_at [String, nil] When the raw message URL expires (only when fetched by ID)
|
|
26
26
|
# @attr_reader html_body [String, nil] Decoded HTML body (only when fetched by ID)
|
|
27
27
|
# @attr_reader text_body [String, nil] Decoded text body (only when fetched by ID)
|
|
28
|
+
# @attr_reader forwards [Array<InboundForwardOutcome>] The forward outcomes
|
|
28
29
|
# rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size
|
|
29
30
|
InboundMessage = Struct.new(
|
|
30
31
|
:id,
|
|
@@ -49,6 +50,7 @@ module Mailtrap
|
|
|
49
50
|
:raw_message_expires_at,
|
|
50
51
|
:html_body,
|
|
51
52
|
:text_body,
|
|
53
|
+
:forwards,
|
|
52
54
|
keyword_init: true
|
|
53
55
|
)
|
|
54
56
|
# rubocop:enable Lint/StructNewOverride
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative 'base_api'
|
|
4
4
|
require_relative 'inbound_message'
|
|
5
5
|
require_relative 'inbound_attachment'
|
|
6
|
+
require_relative 'inbound_forward_outcome'
|
|
6
7
|
require_relative 'inbound_messages_list_response'
|
|
7
8
|
require_relative 'inbound_send_result'
|
|
8
9
|
|
|
@@ -100,6 +101,7 @@ module Mailtrap
|
|
|
100
101
|
def build_message(hash)
|
|
101
102
|
attrs = hash.slice(*InboundMessage.members)
|
|
102
103
|
attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments]
|
|
104
|
+
attrs[:forwards] = build_forwards(attrs[:forwards]) if attrs[:forwards]
|
|
103
105
|
|
|
104
106
|
InboundMessage.new(**attrs)
|
|
105
107
|
end
|
|
@@ -109,5 +111,11 @@ module Mailtrap
|
|
|
109
111
|
InboundAttachment.new(**attachment.slice(*InboundAttachment.members))
|
|
110
112
|
end
|
|
111
113
|
end
|
|
114
|
+
|
|
115
|
+
def build_forwards(forwards)
|
|
116
|
+
Array(forwards).map do |forward|
|
|
117
|
+
InboundForwardOutcome.new(**forward.slice(*InboundForwardOutcome.members))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
112
120
|
end
|
|
113
121
|
end
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Mailtrap
|
|
4
4
|
# Data Transfer Object for a message inside an inbound thread.
|
|
5
|
-
# Only +visibility_status+ and +direction+ are always present; +placeholder+ entries omit the rest
|
|
6
|
-
# and the delivery lifecycle fields are only set on available outbound messages.
|
|
5
|
+
# Only +visibility_status+ and +direction+ are always present; +placeholder+ entries omit the rest.
|
|
7
6
|
# @see https://docs.mailtrap.io/developers/inbound
|
|
8
7
|
# @attr_reader visibility_status [String] available or placeholder
|
|
9
8
|
# @attr_reader direction [String] inbound or outbound
|
|
@@ -23,9 +22,8 @@ module Mailtrap
|
|
|
23
22
|
# @attr_reader text_body [String, nil] Decoded text body
|
|
24
23
|
# @attr_reader html_body [String, nil] Decoded HTML body
|
|
25
24
|
# @attr_reader attachments [Array<InboundAttachment>, nil] The message attachments
|
|
26
|
-
# @attr_reader
|
|
27
|
-
# @attr_reader
|
|
28
|
-
# @attr_reader bounced_at [String, nil] Bounce timestamp (outbound messages only)
|
|
25
|
+
# @attr_reader delivery [InboundThreadMessageDelivery, nil] Delivery outcome (outbound messages only)
|
|
26
|
+
# @attr_reader forwards [Array<InboundForwardOutcome>, nil] Forward outcomes (inbound messages only)
|
|
29
27
|
InboundThreadMessage = Struct.new(
|
|
30
28
|
:visibility_status,
|
|
31
29
|
:direction,
|
|
@@ -45,9 +43,8 @@ module Mailtrap
|
|
|
45
43
|
:text_body,
|
|
46
44
|
:html_body,
|
|
47
45
|
:attachments,
|
|
48
|
-
:
|
|
49
|
-
:
|
|
50
|
-
:bounced_at,
|
|
46
|
+
:delivery,
|
|
47
|
+
:forwards,
|
|
51
48
|
keyword_init: true
|
|
52
49
|
)
|
|
53
50
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for the delivery of an outbound message inside an inbound thread
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader to [String] The recipient address
|
|
7
|
+
# @attr_reader status [String] The delivery status
|
|
8
|
+
# @attr_reader delivered_at [String, nil] Delivery timestamp
|
|
9
|
+
# @attr_reader bounced_at [String, nil] Bounce timestamp
|
|
10
|
+
InboundThreadMessageDelivery = Struct.new(
|
|
11
|
+
:to,
|
|
12
|
+
:status,
|
|
13
|
+
:delivered_at,
|
|
14
|
+
:bounced_at,
|
|
15
|
+
keyword_init: true
|
|
16
|
+
)
|
|
17
|
+
end
|
|
@@ -4,6 +4,8 @@ require_relative 'base_api'
|
|
|
4
4
|
require_relative 'inbound_thread'
|
|
5
5
|
require_relative 'inbound_thread_message'
|
|
6
6
|
require_relative 'inbound_attachment'
|
|
7
|
+
require_relative 'inbound_forward_outcome'
|
|
8
|
+
require_relative 'inbound_thread_message_delivery'
|
|
7
9
|
require_relative 'inbound_threads_list_response'
|
|
8
10
|
|
|
9
11
|
module Mailtrap
|
|
@@ -20,10 +22,11 @@ module Mailtrap
|
|
|
20
22
|
# Lists threads in an inbox (cursor-paginated)
|
|
21
23
|
# @param inbox_id [Integer] The inbox ID
|
|
22
24
|
# @param last_id [String, nil] Cursor from the previous response's +last_id+ for the next page
|
|
25
|
+
# @param search [String, nil] Search query
|
|
23
26
|
# @return [InboundThreadsListResponse] data, total_count, and last_id
|
|
24
27
|
# @!macro api_errors
|
|
25
|
-
def list(inbox_id, last_id: nil)
|
|
26
|
-
query_params =
|
|
28
|
+
def list(inbox_id, last_id: nil, search: nil)
|
|
29
|
+
query_params = { last_id:, search: }.compact
|
|
27
30
|
response = client.get(threads_path(inbox_id), query_params)
|
|
28
31
|
|
|
29
32
|
InboundThreadsListResponse.new(
|
|
@@ -69,6 +72,8 @@ module Mailtrap
|
|
|
69
72
|
Array(messages).map do |message|
|
|
70
73
|
attrs = message.slice(*InboundThreadMessage.members)
|
|
71
74
|
attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments]
|
|
75
|
+
attrs[:delivery] = build_delivery(attrs[:delivery]) if attrs[:delivery]
|
|
76
|
+
attrs[:forwards] = build_forwards(attrs[:forwards]) if attrs[:forwards]
|
|
72
77
|
|
|
73
78
|
InboundThreadMessage.new(**attrs)
|
|
74
79
|
end
|
|
@@ -79,5 +84,15 @@ module Mailtrap
|
|
|
79
84
|
InboundAttachment.new(**attachment.slice(*InboundAttachment.members))
|
|
80
85
|
end
|
|
81
86
|
end
|
|
87
|
+
|
|
88
|
+
def build_delivery(delivery)
|
|
89
|
+
InboundThreadMessageDelivery.new(**delivery.slice(*InboundThreadMessageDelivery.members))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def build_forwards(forwards)
|
|
93
|
+
Array(forwards).map do |forward|
|
|
94
|
+
InboundForwardOutcome.new(**forward.slice(*InboundForwardOutcome.members))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
82
97
|
end
|
|
83
98
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for Email Marketing Stats
|
|
5
|
+
#
|
|
6
|
+
# Aggregated metrics of the account's Email Marketing traffic (campaigns and automations) for
|
|
7
|
+
# the requested period and filters. Rates are fractions from 0 to 1. Every count and rate is +0+
|
|
8
|
+
# when nothing matched.
|
|
9
|
+
# @see https://docs.mailtrap.io/developers/email-marketing/marketing-stats
|
|
10
|
+
# @attr_reader delivery_count [Integer] Number of delivered messages
|
|
11
|
+
# @attr_reader delivery_rate [Float, Integer] Share of sent messages that were delivered (0–1)
|
|
12
|
+
# @attr_reader bounce_count [Integer] Number of bounced messages
|
|
13
|
+
# @attr_reader bounce_rate [Float, Integer] Share of sent messages that bounced (0–1)
|
|
14
|
+
# @attr_reader open_count [Integer] Number of opened messages
|
|
15
|
+
# @attr_reader open_rate [Float, Integer] Share of delivered messages that were opened (0–1)
|
|
16
|
+
# @attr_reader click_count [Integer] Number of clicked messages
|
|
17
|
+
# @attr_reader click_rate [Float, Integer] Share of delivered messages that were clicked (0–1)
|
|
18
|
+
# @attr_reader spam_count [Integer] Number of spam complaints
|
|
19
|
+
# @attr_reader spam_rate [Float, Integer] Share of delivered messages marked as spam (0–1)
|
|
20
|
+
# @attr_reader unsubscription_count [Integer] Number of unsubscriptions
|
|
21
|
+
# @attr_reader unsubscription_rate [Float, Integer] Share of delivered messages that unsubscribed (0–1)
|
|
22
|
+
# @attr_reader sent_count [Integer] Number of sent messages (delivered plus bounced)
|
|
23
|
+
MarketingStats = Struct.new(
|
|
24
|
+
:delivery_count,
|
|
25
|
+
:delivery_rate,
|
|
26
|
+
:bounce_count,
|
|
27
|
+
:bounce_rate,
|
|
28
|
+
:open_count,
|
|
29
|
+
:open_rate,
|
|
30
|
+
:click_count,
|
|
31
|
+
:click_rate,
|
|
32
|
+
:spam_count,
|
|
33
|
+
:spam_rate,
|
|
34
|
+
:unsubscription_count,
|
|
35
|
+
:unsubscription_rate,
|
|
36
|
+
:sent_count,
|
|
37
|
+
keyword_init: true
|
|
38
|
+
)
|
|
39
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_api'
|
|
4
|
+
require_relative 'stats_query_params'
|
|
5
|
+
require_relative 'marketing_stats'
|
|
6
|
+
|
|
7
|
+
module Mailtrap
|
|
8
|
+
class MarketingStatsAPI
|
|
9
|
+
include BaseAPI
|
|
10
|
+
include StatsQueryParams
|
|
11
|
+
|
|
12
|
+
# @param client [Mailtrap::Client] The client instance
|
|
13
|
+
def initialize(client = Mailtrap::Client.new)
|
|
14
|
+
@client = client
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get aggregated Email Marketing stats: campaigns and automations together, the same numbers
|
|
18
|
+
# as Email Marketing > Stats Overview in the app. Email API/SMTP traffic is reported by {StatsAPI}.
|
|
19
|
+
# @param start_date [String, Date, Time] Start of the period, inclusive (required)
|
|
20
|
+
# @param end_date [String, Date, Time] End of the period, inclusive (required)
|
|
21
|
+
# @param sending_domain_ids [Array<Integer>] Filter by sending domain IDs
|
|
22
|
+
# @param sources [Array<String>] Filter by source: +campaigns+ or +automations+;
|
|
23
|
+
# omit it, or pass both, to include campaigns and automations together
|
|
24
|
+
# @param email_service_providers [Array<String>] Filter by recipient email service providers
|
|
25
|
+
# @return [MarketingStats] Aggregated Email Marketing stats
|
|
26
|
+
# @!macro api_errors
|
|
27
|
+
# @raise [ArgumentError] If a date is not a Date, Time, or String in +YYYY-MM-DD+ format
|
|
28
|
+
def get(start_date:, end_date:, sending_domain_ids: nil, sources: nil, email_service_providers: nil)
|
|
29
|
+
query_params = build_query_params(
|
|
30
|
+
start_date, end_date,
|
|
31
|
+
{ sending_domain_ids:, sources:, email_service_providers: }
|
|
32
|
+
)
|
|
33
|
+
response = client.get(base_path, query_params)
|
|
34
|
+
build_entity(response[:data], MarketingStats)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def base_path
|
|
40
|
+
'/api/marketing/stats'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/mailtrap/stats_api.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'date'
|
|
4
3
|
require_relative 'base_api'
|
|
4
|
+
require_relative 'stats_query_params'
|
|
5
5
|
require_relative 'sending_stats'
|
|
6
6
|
require_relative 'sending_stat_group'
|
|
7
7
|
|
|
8
8
|
module Mailtrap
|
|
9
9
|
class StatsAPI
|
|
10
10
|
include BaseAPI
|
|
11
|
+
include StatsQueryParams
|
|
11
12
|
|
|
12
|
-
ARRAY_FILTERS = %i[sending_domain_ids sending_streams categories email_service_providers].freeze
|
|
13
13
|
GROUP_KEYS = {
|
|
14
14
|
'domains' => :sending_domain_id,
|
|
15
15
|
'categories' => :category,
|
|
@@ -112,38 +112,6 @@ module Mailtrap
|
|
|
112
112
|
end
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
-
def build_query_params(start_date, end_date, filters)
|
|
116
|
-
params = { start_date: normalize_date(start_date), end_date: normalize_date(end_date) }
|
|
117
|
-
|
|
118
|
-
ARRAY_FILTERS.each do |filter_key|
|
|
119
|
-
values = filters[filter_key]
|
|
120
|
-
params["#{filter_key}[]"] = values if values
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
params
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def normalize_date(value)
|
|
127
|
-
case value
|
|
128
|
-
when Date
|
|
129
|
-
value.iso8601
|
|
130
|
-
when Time
|
|
131
|
-
value.strftime('%F')
|
|
132
|
-
when String
|
|
133
|
-
unless /\A\d{4}-\d{2}-\d{2}\z/.match?(value)
|
|
134
|
-
raise ArgumentError,
|
|
135
|
-
"Invalid date: #{value.inspect}. Expected a Date, Time, or String in YYYY-MM-DD format."
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
Date.iso8601(value).iso8601
|
|
139
|
-
else
|
|
140
|
-
raise ArgumentError,
|
|
141
|
-
"Invalid date: #{value.inspect}. Expected a Date, Time, or String in YYYY-MM-DD format."
|
|
142
|
-
end
|
|
143
|
-
rescue Date::Error
|
|
144
|
-
raise ArgumentError, "Invalid date: #{value.inspect}. Expected a Date, Time, or String in YYYY-MM-DD format."
|
|
145
|
-
end
|
|
146
|
-
|
|
147
115
|
def base_path
|
|
148
116
|
"/api/accounts/#{account_id}/stats"
|
|
149
117
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'date'
|
|
4
|
+
|
|
5
|
+
module Mailtrap
|
|
6
|
+
# Query parameters of the sending stats and Email Marketing stats endpoints: the period as
|
|
7
|
+
# +YYYY-MM-DD+ dates and every array filter as a repeated +key[]+ parameter. A +nil+ or empty
|
|
8
|
+
# filter is left out.
|
|
9
|
+
module StatsQueryParams
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def build_query_params(start_date, end_date, filters)
|
|
13
|
+
params = { start_date: normalize_date(start_date), end_date: normalize_date(end_date) }
|
|
14
|
+
filters.each { |filter_key, values| params["#{filter_key}[]"] = values unless Array(values).empty? }
|
|
15
|
+
params
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def normalize_date(value)
|
|
19
|
+
case value
|
|
20
|
+
when Date, Time # DateTime is a Date, but its #iso8601 includes the time of day
|
|
21
|
+
return value.strftime('%F')
|
|
22
|
+
when String
|
|
23
|
+
return value if /\A\d{4}-\d{2}-\d{2}\z/.match?(value) && Date.valid_date?(*value.split('-').map(&:to_i))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
raise ArgumentError, "Invalid date: #{value.inspect}. Expected a Date, Time, or String in YYYY-MM-DD format."
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/mailtrap/version.rb
CHANGED
data/lib/mailtrap.rb
CHANGED
|
@@ -21,6 +21,7 @@ require_relative 'mailtrap/contact_events_api'
|
|
|
21
21
|
require_relative 'mailtrap/suppressions_api'
|
|
22
22
|
require_relative 'mailtrap/tracking_opt_outs_api'
|
|
23
23
|
require_relative 'mailtrap/email_campaigns_api'
|
|
24
|
+
require_relative 'mailtrap/marketing_stats_api'
|
|
24
25
|
require_relative 'mailtrap/sending_domains_api'
|
|
25
26
|
require_relative 'mailtrap/company_info_api'
|
|
26
27
|
require_relative 'mailtrap/email_logs_api'
|
|
@@ -35,6 +36,7 @@ require_relative 'mailtrap/inbound_folders_api'
|
|
|
35
36
|
require_relative 'mailtrap/inbound_inboxes_api'
|
|
36
37
|
require_relative 'mailtrap/inbound_messages_api'
|
|
37
38
|
require_relative 'mailtrap/inbound_threads_api'
|
|
39
|
+
require_relative 'mailtrap/inbound_forward_rules_api'
|
|
38
40
|
|
|
39
41
|
module Mailtrap
|
|
40
42
|
# @!macro api_errors
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mailtrap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Railsware Products Studio LLC
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -85,6 +85,11 @@ files:
|
|
|
85
85
|
- lib/mailtrap/inbound_attachment.rb
|
|
86
86
|
- lib/mailtrap/inbound_folder.rb
|
|
87
87
|
- lib/mailtrap/inbound_folders_api.rb
|
|
88
|
+
- lib/mailtrap/inbound_forward_outcome.rb
|
|
89
|
+
- lib/mailtrap/inbound_forward_rule.rb
|
|
90
|
+
- lib/mailtrap/inbound_forward_rule_condition.rb
|
|
91
|
+
- lib/mailtrap/inbound_forward_rule_destination.rb
|
|
92
|
+
- lib/mailtrap/inbound_forward_rules_api.rb
|
|
88
93
|
- lib/mailtrap/inbound_inbox.rb
|
|
89
94
|
- lib/mailtrap/inbound_inboxes_api.rb
|
|
90
95
|
- lib/mailtrap/inbound_message.rb
|
|
@@ -93,6 +98,7 @@ files:
|
|
|
93
98
|
- lib/mailtrap/inbound_send_result.rb
|
|
94
99
|
- lib/mailtrap/inbound_thread.rb
|
|
95
100
|
- lib/mailtrap/inbound_thread_message.rb
|
|
101
|
+
- lib/mailtrap/inbound_thread_message_delivery.rb
|
|
96
102
|
- lib/mailtrap/inbound_threads_api.rb
|
|
97
103
|
- lib/mailtrap/inbound_threads_list_response.rb
|
|
98
104
|
- lib/mailtrap/inbox.rb
|
|
@@ -100,6 +106,8 @@ files:
|
|
|
100
106
|
- lib/mailtrap/mail.rb
|
|
101
107
|
- lib/mailtrap/mail/base.rb
|
|
102
108
|
- lib/mailtrap/mail/from_template.rb
|
|
109
|
+
- lib/mailtrap/marketing_stats.rb
|
|
110
|
+
- lib/mailtrap/marketing_stats_api.rb
|
|
103
111
|
- lib/mailtrap/permission_resource.rb
|
|
104
112
|
- lib/mailtrap/permissions_api.rb
|
|
105
113
|
- lib/mailtrap/project.rb
|
|
@@ -113,6 +121,7 @@ files:
|
|
|
113
121
|
- lib/mailtrap/sending_stat_group.rb
|
|
114
122
|
- lib/mailtrap/sending_stats.rb
|
|
115
123
|
- lib/mailtrap/stats_api.rb
|
|
124
|
+
- lib/mailtrap/stats_query_params.rb
|
|
116
125
|
- lib/mailtrap/sub_account.rb
|
|
117
126
|
- lib/mailtrap/sub_accounts_api.rb
|
|
118
127
|
- lib/mailtrap/suppression.rb
|