mailtrap 2.11.0 → 2.12.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 +22 -0
- data/Gemfile.lock +1 -1
- data/README.md +12 -5
- data/lib/mailtrap/contact_lists_api.rb +6 -2
- data/lib/mailtrap/email_log_message.rb +8 -0
- data/lib/mailtrap/inbound_attachment.rb +27 -0
- data/lib/mailtrap/inbound_folder.rb +13 -0
- data/lib/mailtrap/inbound_folders_api.rb +69 -0
- data/lib/mailtrap/inbound_inbox.rb +17 -0
- data/lib/mailtrap/inbound_inboxes_api.rb +79 -0
- data/lib/mailtrap/inbound_message.rb +55 -0
- data/lib/mailtrap/inbound_messages_api.rb +114 -0
- data/lib/mailtrap/inbound_messages_list_response.rb +15 -0
- data/lib/mailtrap/inbound_send_result.rb +11 -0
- data/lib/mailtrap/inbound_thread.rb +37 -0
- data/lib/mailtrap/inbound_thread_message.rb +53 -0
- data/lib/mailtrap/inbound_threads_api.rb +84 -0
- data/lib/mailtrap/inbound_threads_list_response.rb +15 -0
- data/lib/mailtrap/sending_domain.rb +4 -0
- data/lib/mailtrap/version.rb +1 -1
- data/lib/mailtrap/webhooks.rb +49 -0
- data/lib/mailtrap.rb +5 -0
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40fcde70d63616c345252736a68e3de8b1f0cfa7c1cf615cb0cace7e2158c752
|
|
4
|
+
data.tar.gz: 5e3ab5dd73034c6fc752f0d13b683e767d6f29f170c1f2aeed5fbffedc9bef1c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b971a8cc52133ce0aed38ed202742fdb45e4be73256abe284e40a1d4ac94b729674cd4faea526397685f26d80fcd307669777941b29b1a28fcbd821adbf6beba
|
|
7
|
+
data.tar.gz: 00bc7f6c7db02b4d70f45070e250ad726b72150eb976dcf4476f72b58a44ad99d1f59a6ac89033cc5269d2a5f13987a10a8d1cd4d8d6dc0ed4d894e1134baade
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [2.12.0] - 2026-07-24
|
|
2
|
+
|
|
3
|
+
## What's Changed
|
|
4
|
+
* MT-22678: Add name search filter to contact lists listing by @Rabsztok in https://github.com/mailtrap/mailtrap-ruby/pull/116
|
|
5
|
+
* Add Inbound Email v2 API support by @mklocek in https://github.com/mailtrap/mailtrap-ruby/pull/117
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
**Full Changelog**: https://github.com/mailtrap/mailtrap-ruby/compare/v2.11.1...v2.12.0
|
|
9
|
+
|
|
10
|
+
## [2.11.1] - 2026-07-02
|
|
11
|
+
|
|
12
|
+
## What's Changed
|
|
13
|
+
* Release v2.11.0 by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/109
|
|
14
|
+
* MT-22022: Add webhook signature verification helper by @Rabsztok in https://github.com/mailtrap/mailtrap-ruby/pull/110
|
|
15
|
+
* Add draft-release workflow placeholder by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/111
|
|
16
|
+
* Implement draft-release workflow by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/112
|
|
17
|
+
|
|
18
|
+
## New Contributors
|
|
19
|
+
* @Rabsztok made their first contribution in https://github.com/mailtrap/mailtrap-ruby/pull/110
|
|
20
|
+
|
|
21
|
+
**Full Changelog**: https://github.com/mailtrap/mailtrap-ruby/compare/v2.11.0...v2.11.1
|
|
22
|
+
|
|
1
23
|
## [2.11.0] - 2026-05-14
|
|
2
24
|
## What's Changed
|
|
3
25
|
* Update sandboxes api by @IgorDobryn in https://github.com/mailtrap/mailtrap-ruby/pull/103
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
This client uses API v2, for v1 refer to [this documentation](https://mailtrap.docs.apiary.io/)
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
## Prerequisites
|
|
13
12
|
|
|
14
13
|
To get the most out of this official Mailtrap.io Ruby SDK:
|
|
@@ -176,7 +175,7 @@ Email API:
|
|
|
176
175
|
|
|
177
176
|
- Full Email Sending – [`full.rb`](examples/full.rb)
|
|
178
177
|
- Batch Sending – [`batch.rb`](examples/batch.rb)
|
|
179
|
-
- Sending Domains API
|
|
178
|
+
- Sending Domains API – [`sending_domains_api.rb`](examples/sending_domains_api.rb)
|
|
180
179
|
- Sending Stats API – [`stats_api.rb`](examples/stats_api.rb)
|
|
181
180
|
- Email Logs API – [`email_logs_api.rb`](examples/email_logs_api.rb)
|
|
182
181
|
- Webhooks API – [`webhooks_api.rb`](examples/webhooks_api.rb)
|
|
@@ -188,6 +187,13 @@ Email Sandbox (Testing):
|
|
|
188
187
|
- Sandbox Messages CRUD - [`sandbox_messages_api.rb`](examples/sandbox_messages_api.rb)
|
|
189
188
|
- Sandbox Attachments API - [`sandbox_attachments_api.rb`](examples/sandbox_attachments_api.rb)
|
|
190
189
|
|
|
190
|
+
Inbound Email:
|
|
191
|
+
|
|
192
|
+
- Inbound Folders API – [`inbound_folders_api.rb`](examples/inbound_folders_api.rb)
|
|
193
|
+
- Inbound Inboxes API – [`inbound_inboxes_api.rb`](examples/inbound_inboxes_api.rb)
|
|
194
|
+
- Inbound Messages API (list, get, delete, reply, reply_all, forward) – [`inbound_messages_api.rb`](examples/inbound_messages_api.rb)
|
|
195
|
+
- Inbound Threads API – [`inbound_threads_api.rb`](examples/inbound_threads_api.rb)
|
|
196
|
+
|
|
191
197
|
Contact management:
|
|
192
198
|
|
|
193
199
|
- Contacts CRUD & Listing – [`contacts_api.rb`](examples/contacts_api.rb)
|
|
@@ -199,6 +205,7 @@ General:
|
|
|
199
205
|
- Billing API – [`billing_api.rb`](examples/billing_api.rb)
|
|
200
206
|
- Templates API – [`email_templates_api.rb`](examples/email_templates_api.rb)
|
|
201
207
|
- Action Mailer – [`action_mailer.rb`](examples/action_mailer.rb)
|
|
208
|
+
- Verifying webhook signatures – [`webhooks_signature_verification.ru`](examples/webhooks_signature_verification.ru)
|
|
202
209
|
|
|
203
210
|
## Migration guide v1 → v2
|
|
204
211
|
|
|
@@ -213,14 +220,14 @@ Bug reports and pull requests are welcome on [GitHub](https://github.com/railswa
|
|
|
213
220
|
## Development
|
|
214
221
|
|
|
215
222
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
216
|
-
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
|
223
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
|
217
224
|
prompt that will allow you to experiment.
|
|
218
225
|
|
|
219
226
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
220
227
|
|
|
221
228
|
To release a new version, update the version number in `version.rb`, and then
|
|
222
229
|
run `bundle exec rake release`, which will create a git tag for the version,
|
|
223
|
-
push git commits and the created tag, and push the `.gem` file to
|
|
230
|
+
push git commits and the created tag, and push the `.gem` file to
|
|
224
231
|
[rubygems.org](https://rubygems.org).
|
|
225
232
|
|
|
226
233
|
To run the documentation server, first generate the documentation with
|
|
@@ -241,4 +248,4 @@ Everyone interacting in the Mailtrap project's codebases, issue trackers, chat r
|
|
|
241
248
|
|
|
242
249
|
## Compatibility with previous releases
|
|
243
250
|
|
|
244
|
-
Versions of this package up to 2.0.2 were an [unofficial client](https://github.com/vchin/mailtrap-client) developed by [@vchin](https://github.com/vchin). Package version 3 is a completely new package.
|
|
251
|
+
Versions of this package up to 2.0.2 were an [unofficial client](https://github.com/vchin/mailtrap-client) developed by [@vchin](https://github.com/vchin). Package version 3 is a completely new package.
|
|
@@ -49,10 +49,14 @@ module Mailtrap
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# Lists all contact lists for the account
|
|
52
|
+
# @param search [String] Filters contact lists by name (case-insensitive prefix match)
|
|
52
53
|
# @return [Array<ContactList>] Array of contact list objects
|
|
53
54
|
# @!macro api_errors
|
|
54
|
-
def list
|
|
55
|
-
|
|
55
|
+
def list(search: nil)
|
|
56
|
+
query_params = {}
|
|
57
|
+
query_params[:search] = search unless search.nil?
|
|
58
|
+
|
|
59
|
+
base_list(query_params)
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
private
|
|
@@ -6,6 +6,10 @@ module Mailtrap
|
|
|
6
6
|
# @attr_reader message_id [String] Message UUID
|
|
7
7
|
# @attr_reader status [String] delivered, not_delivered, enqueued, opted_out
|
|
8
8
|
# @attr_reader subject [String, nil] Email subject
|
|
9
|
+
# @attr_reader rfc_message_id [String, nil] Value of the RFC 5322 Message-ID header
|
|
10
|
+
# @attr_reader in_reply_to [String, nil] Value of the RFC 5322 In-Reply-To header
|
|
11
|
+
# @attr_reader references [Array<String>] Values of the RFC 5322 References header
|
|
12
|
+
# @attr_reader thread_id [String, nil] ID of the inbound thread this message belongs to, if any
|
|
9
13
|
# @attr_reader from [String] Sender address
|
|
10
14
|
# @attr_reader to [String] Recipient address
|
|
11
15
|
# @attr_reader sent_at [String] ISO 8601 timestamp
|
|
@@ -24,6 +28,10 @@ module Mailtrap
|
|
|
24
28
|
:message_id,
|
|
25
29
|
:status,
|
|
26
30
|
:subject,
|
|
31
|
+
:rfc_message_id,
|
|
32
|
+
:in_reply_to,
|
|
33
|
+
:references,
|
|
34
|
+
:thread_id,
|
|
27
35
|
:from,
|
|
28
36
|
:to,
|
|
29
37
|
:sent_at,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound message attachment
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader attachment_id [String] The attachment ID
|
|
7
|
+
# @attr_reader size [Integer, nil] The attachment size in bytes
|
|
8
|
+
# @attr_reader filename [String, nil] The attachment filename
|
|
9
|
+
# @attr_reader content_type [String, nil] The attachment MIME type
|
|
10
|
+
# @attr_reader content_disposition [String, nil] attachment or inline
|
|
11
|
+
# @attr_reader content_id [String, nil] The content ID for inline attachments
|
|
12
|
+
# @attr_reader download_url [String, nil] Signed URL to download the attachment (only when fetched by ID)
|
|
13
|
+
# @attr_reader download_url_expires_at [String, nil] When the download URL expires (only when fetched by ID)
|
|
14
|
+
# rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size
|
|
15
|
+
InboundAttachment = Struct.new(
|
|
16
|
+
:attachment_id,
|
|
17
|
+
:size,
|
|
18
|
+
:filename,
|
|
19
|
+
:content_type,
|
|
20
|
+
:content_disposition,
|
|
21
|
+
:content_id,
|
|
22
|
+
:download_url,
|
|
23
|
+
:download_url_expires_at,
|
|
24
|
+
keyword_init: true
|
|
25
|
+
)
|
|
26
|
+
# rubocop:enable Lint/StructNewOverride
|
|
27
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound folder
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader id [Integer] The folder ID
|
|
7
|
+
# @attr_reader name [String] The folder name
|
|
8
|
+
InboundFolder = Struct.new(
|
|
9
|
+
:id,
|
|
10
|
+
:name,
|
|
11
|
+
keyword_init: true
|
|
12
|
+
)
|
|
13
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_api'
|
|
4
|
+
require_relative 'inbound_folder'
|
|
5
|
+
|
|
6
|
+
module Mailtrap
|
|
7
|
+
class InboundFoldersAPI
|
|
8
|
+
include BaseAPI
|
|
9
|
+
|
|
10
|
+
self.supported_options = %i[name]
|
|
11
|
+
self.response_class = InboundFolder
|
|
12
|
+
|
|
13
|
+
# Inbound is scoped to the token's account, so no account_id is required.
|
|
14
|
+
# @param client [Mailtrap::Client] The client instance
|
|
15
|
+
def initialize(client = Mailtrap::Client.new)
|
|
16
|
+
@client = client
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Lists all inbound folders in the account
|
|
20
|
+
# @return [Array<InboundFolder>] Array of folders
|
|
21
|
+
# @!macro api_errors
|
|
22
|
+
def list
|
|
23
|
+
base_list
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Retrieves a specific inbound folder
|
|
27
|
+
# @param folder_id [Integer] The folder ID
|
|
28
|
+
# @return [InboundFolder] Folder object
|
|
29
|
+
# @!macro api_errors
|
|
30
|
+
def get(folder_id)
|
|
31
|
+
base_get(folder_id)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Creates a new inbound folder
|
|
35
|
+
# @param [Hash] options The parameters to create
|
|
36
|
+
# @option options [String] :name The folder name
|
|
37
|
+
# @return [InboundFolder] Created folder
|
|
38
|
+
# @!macro api_errors
|
|
39
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
40
|
+
def create(options)
|
|
41
|
+
base_create(options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Updates an inbound folder
|
|
45
|
+
# @param folder_id [Integer] The folder ID
|
|
46
|
+
# @param [Hash] options The parameters to update
|
|
47
|
+
# @option options [String] :name The folder name
|
|
48
|
+
# @return [InboundFolder] Updated folder
|
|
49
|
+
# @!macro api_errors
|
|
50
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
51
|
+
def update(folder_id, options)
|
|
52
|
+
base_update(folder_id, options)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Deletes an inbound folder along with all of its inboxes
|
|
56
|
+
# @param folder_id [Integer] The folder ID
|
|
57
|
+
# @return nil
|
|
58
|
+
# @!macro api_errors
|
|
59
|
+
def delete(folder_id)
|
|
60
|
+
base_delete(folder_id)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def base_path
|
|
66
|
+
'/api/inbound/folders'
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound inbox
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader id [Integer] The inbox ID
|
|
7
|
+
# @attr_reader name [String] The inbox name
|
|
8
|
+
# @attr_reader address [String] The inbox's inbound address
|
|
9
|
+
# @attr_reader domain_id [Integer] The sending domain the inbox is attached to
|
|
10
|
+
InboundInbox = Struct.new(
|
|
11
|
+
:id,
|
|
12
|
+
:name,
|
|
13
|
+
:address,
|
|
14
|
+
:domain_id,
|
|
15
|
+
keyword_init: true
|
|
16
|
+
)
|
|
17
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_api'
|
|
4
|
+
require_relative 'inbound_inbox'
|
|
5
|
+
|
|
6
|
+
module Mailtrap
|
|
7
|
+
class InboundInboxesAPI
|
|
8
|
+
include BaseAPI
|
|
9
|
+
|
|
10
|
+
self.response_class = InboundInbox
|
|
11
|
+
|
|
12
|
+
CREATE_OPTIONS = %i[name domain_id].freeze
|
|
13
|
+
UPDATE_OPTIONS = %i[name].freeze
|
|
14
|
+
|
|
15
|
+
# Inbound is scoped to the token's account, so no account_id is required.
|
|
16
|
+
# @param client [Mailtrap::Client] The client instance
|
|
17
|
+
def initialize(client = Mailtrap::Client.new)
|
|
18
|
+
@client = client
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Lists all inboxes in a folder
|
|
22
|
+
# @param folder_id [Integer] The folder ID
|
|
23
|
+
# @return [Array<InboundInbox>] Array of inboxes
|
|
24
|
+
# @!macro api_errors
|
|
25
|
+
def list(folder_id)
|
|
26
|
+
client.get(inboxes_path(folder_id)).map { |item| handle_response(item) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Retrieves a specific inbox
|
|
30
|
+
# @param folder_id [Integer] The folder ID
|
|
31
|
+
# @param inbox_id [Integer] The inbox ID
|
|
32
|
+
# @return [InboundInbox] Inbox object
|
|
33
|
+
# @!macro api_errors
|
|
34
|
+
def get(folder_id, inbox_id)
|
|
35
|
+
handle_response(client.get("#{inboxes_path(folder_id)}/#{inbox_id}"))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Creates a new inbox in a folder
|
|
39
|
+
# @param folder_id [Integer] The folder ID
|
|
40
|
+
# @param [Hash] options The parameters to create
|
|
41
|
+
# @option options [String] :name The inbox name
|
|
42
|
+
# @option options [Integer] :domain_id Attach to a custom domain; omit for a Mailtrap-hosted inbox
|
|
43
|
+
# @return [InboundInbox] Created inbox
|
|
44
|
+
# @!macro api_errors
|
|
45
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
46
|
+
def create(folder_id, options)
|
|
47
|
+
validate_options!(options, CREATE_OPTIONS)
|
|
48
|
+
handle_response(client.post(inboxes_path(folder_id), options))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Updates an inbox
|
|
52
|
+
# @param folder_id [Integer] The folder ID
|
|
53
|
+
# @param inbox_id [Integer] The inbox ID
|
|
54
|
+
# @param [Hash] options The parameters to update
|
|
55
|
+
# @option options [String] :name The inbox name
|
|
56
|
+
# @return [InboundInbox] Updated inbox
|
|
57
|
+
# @!macro api_errors
|
|
58
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
59
|
+
def update(folder_id, inbox_id, options)
|
|
60
|
+
validate_options!(options, UPDATE_OPTIONS)
|
|
61
|
+
handle_response(client.patch("#{inboxes_path(folder_id)}/#{inbox_id}", options))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Deletes an inbox
|
|
65
|
+
# @param folder_id [Integer] The folder ID
|
|
66
|
+
# @param inbox_id [Integer] The inbox ID
|
|
67
|
+
# @return nil
|
|
68
|
+
# @!macro api_errors
|
|
69
|
+
def delete(folder_id, inbox_id)
|
|
70
|
+
client.delete("#{inboxes_path(folder_id)}/#{inbox_id}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def inboxes_path(folder_id)
|
|
76
|
+
"/api/inbound/folders/#{folder_id}/inboxes"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound message (summary in list, full details when fetched by ID)
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader id [String] The Mailtrap object ID (not the RFC Message-ID header)
|
|
7
|
+
# @attr_reader inbox_id [Integer] The inbox the message was received in
|
|
8
|
+
# @attr_reader from [String, nil] Sender address
|
|
9
|
+
# @attr_reader to [Array<String>] Recipient addresses
|
|
10
|
+
# @attr_reader cc [Array<String>] Cc addresses
|
|
11
|
+
# @attr_reader bcc [Array<String>] Bcc addresses
|
|
12
|
+
# @attr_reader reply_to [String, nil] Reply-To address
|
|
13
|
+
# @attr_reader subject [String, nil] Email subject
|
|
14
|
+
# @attr_reader rfc_message_id [String, nil] Value of the RFC 5322 Message-ID header
|
|
15
|
+
# @attr_reader in_reply_to [String, nil] Value of the RFC 5322 In-Reply-To header
|
|
16
|
+
# @attr_reader references [Array<String>] Values of the RFC 5322 References header
|
|
17
|
+
# @attr_reader headers [Hash, nil] Raw message headers
|
|
18
|
+
# @attr_reader size [Integer, nil] Total message size in bytes
|
|
19
|
+
# @attr_reader html_size [Integer, nil] HTML body size in bytes
|
|
20
|
+
# @attr_reader text_size [Integer, nil] Text body size in bytes
|
|
21
|
+
# @attr_reader received_at [String] ISO 8601 timestamp when the message was received
|
|
22
|
+
# @attr_reader thread_id [String, nil] ID of the thread this message belongs to, if any
|
|
23
|
+
# @attr_reader attachments [Array<InboundAttachment>] The message attachments
|
|
24
|
+
# @attr_reader raw_message_url [String, nil] Signed URL to download raw .eml (only when fetched by ID)
|
|
25
|
+
# @attr_reader raw_message_expires_at [String, nil] When the raw message URL expires (only when fetched by ID)
|
|
26
|
+
# @attr_reader html_body [String, nil] Decoded HTML body (only when fetched by ID)
|
|
27
|
+
# @attr_reader text_body [String, nil] Decoded text body (only when fetched by ID)
|
|
28
|
+
# rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size
|
|
29
|
+
InboundMessage = Struct.new(
|
|
30
|
+
:id,
|
|
31
|
+
:inbox_id,
|
|
32
|
+
:from,
|
|
33
|
+
:to,
|
|
34
|
+
:cc,
|
|
35
|
+
:bcc,
|
|
36
|
+
:reply_to,
|
|
37
|
+
:subject,
|
|
38
|
+
:rfc_message_id,
|
|
39
|
+
:in_reply_to,
|
|
40
|
+
:references,
|
|
41
|
+
:headers,
|
|
42
|
+
:size,
|
|
43
|
+
:html_size,
|
|
44
|
+
:text_size,
|
|
45
|
+
:received_at,
|
|
46
|
+
:thread_id,
|
|
47
|
+
:attachments,
|
|
48
|
+
:raw_message_url,
|
|
49
|
+
:raw_message_expires_at,
|
|
50
|
+
:html_body,
|
|
51
|
+
:text_body,
|
|
52
|
+
keyword_init: true
|
|
53
|
+
)
|
|
54
|
+
# rubocop:enable Lint/StructNewOverride
|
|
55
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_api'
|
|
4
|
+
require_relative 'inbound_message'
|
|
5
|
+
require_relative 'inbound_attachment'
|
|
6
|
+
require_relative 'inbound_messages_list_response'
|
|
7
|
+
require_relative 'inbound_send_result'
|
|
8
|
+
|
|
9
|
+
module Mailtrap
|
|
10
|
+
class InboundMessagesAPI
|
|
11
|
+
include BaseAPI
|
|
12
|
+
|
|
13
|
+
self.response_class = InboundMessage
|
|
14
|
+
|
|
15
|
+
SEND_OPTIONS = %i[from to cc bcc reply_to text html category attachments headers custom_variables].freeze
|
|
16
|
+
|
|
17
|
+
# Inbound is scoped to the token's account, so no account_id is required.
|
|
18
|
+
# @param client [Mailtrap::Client] The client instance
|
|
19
|
+
def initialize(client = Mailtrap::Client.new)
|
|
20
|
+
@client = client
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Lists messages in an inbox (cursor-paginated)
|
|
24
|
+
# @param inbox_id [Integer] The inbox ID
|
|
25
|
+
# @param last_id [String, nil] Cursor from the previous response's +last_id+ for the next page
|
|
26
|
+
# @return [InboundMessagesListResponse] data, total_count, and last_id
|
|
27
|
+
# @!macro api_errors
|
|
28
|
+
def list(inbox_id, last_id: nil)
|
|
29
|
+
query_params = last_id ? { last_id: } : {}
|
|
30
|
+
response = client.get(messages_path(inbox_id), query_params)
|
|
31
|
+
|
|
32
|
+
InboundMessagesListResponse.new(
|
|
33
|
+
data: Array(response[:data]).map { |item| build_message(item) },
|
|
34
|
+
total_count: response[:total_count],
|
|
35
|
+
last_id: response[:last_id]
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Fetches a single message with its full body and attachment download URLs
|
|
40
|
+
# @param inbox_id [Integer] The inbox ID
|
|
41
|
+
# @param message_id [String] The message ID
|
|
42
|
+
# @return [InboundMessage] Message details
|
|
43
|
+
# @!macro api_errors
|
|
44
|
+
def get(inbox_id, message_id)
|
|
45
|
+
build_message(client.get("#{messages_path(inbox_id)}/#{message_id}"))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Deletes a message
|
|
49
|
+
# @param inbox_id [Integer] The inbox ID
|
|
50
|
+
# @param message_id [String] The message ID
|
|
51
|
+
# @return nil
|
|
52
|
+
# @!macro api_errors
|
|
53
|
+
def delete(inbox_id, message_id)
|
|
54
|
+
client.delete("#{messages_path(inbox_id)}/#{message_id}")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Replies to a message (sends to the original sender)
|
|
58
|
+
# @param inbox_id [Integer] The inbox ID
|
|
59
|
+
# @param message_id [String] The message ID
|
|
60
|
+
# @param [Hash] options The send parameters (see {SEND_OPTIONS})
|
|
61
|
+
# @return [InboundSendResult] The sent message IDs
|
|
62
|
+
# @!macro api_errors
|
|
63
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
64
|
+
def reply(inbox_id, message_id, options)
|
|
65
|
+
send_action(inbox_id, message_id, 'reply', options)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Replies to a message and copies the original's other recipients
|
|
69
|
+
# @param (see #reply)
|
|
70
|
+
# @return [InboundSendResult] The sent message IDs
|
|
71
|
+
# @!macro api_errors
|
|
72
|
+
# @raise [ArgumentError] If invalid options are provided
|
|
73
|
+
def reply_all(inbox_id, message_id, options)
|
|
74
|
+
send_action(inbox_id, message_id, 'reply_all', options)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Forwards a message to new recipients
|
|
78
|
+
# @param (see #reply)
|
|
79
|
+
# @return [InboundSendResult] The sent message IDs
|
|
80
|
+
# @!macro api_errors
|
|
81
|
+
# @raise [ArgumentError] If invalid options are provided, or if no +to+ recipient is given
|
|
82
|
+
def forward(inbox_id, message_id, options)
|
|
83
|
+
raise ArgumentError, 'to is required for forward' if Array(options[:to]).empty?
|
|
84
|
+
|
|
85
|
+
send_action(inbox_id, message_id, 'forward', options)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def messages_path(inbox_id)
|
|
91
|
+
"/api/inbound/inboxes/#{inbox_id}/messages"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def send_action(inbox_id, message_id, action, options)
|
|
95
|
+
validate_options!(options, SEND_OPTIONS)
|
|
96
|
+
response = client.post("#{messages_path(inbox_id)}/#{message_id}/#{action}", options)
|
|
97
|
+
|
|
98
|
+
InboundSendResult.new(message_ids: response[:message_ids])
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def build_message(hash)
|
|
102
|
+
attrs = hash.slice(*InboundMessage.members)
|
|
103
|
+
attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments]
|
|
104
|
+
|
|
105
|
+
InboundMessage.new(**attrs)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def build_attachments(attachments)
|
|
109
|
+
Array(attachments).map do |attachment|
|
|
110
|
+
InboundAttachment.new(**attachment.slice(*InboundAttachment.members))
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for a paginated list of inbound messages
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader data [Array<InboundMessage>] The messages on this page
|
|
7
|
+
# @attr_reader total_count [Integer] Total number of messages in the inbox
|
|
8
|
+
# @attr_reader last_id [String, nil] Cursor for the next page, or nil if this is the last page
|
|
9
|
+
InboundMessagesListResponse = Struct.new(
|
|
10
|
+
:data,
|
|
11
|
+
:total_count,
|
|
12
|
+
:last_id,
|
|
13
|
+
keyword_init: true
|
|
14
|
+
)
|
|
15
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for the result of a reply, reply-all, or forward.
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader message_ids [Array<String>] UUIDs of the sent messages, one per recipient
|
|
7
|
+
InboundSendResult = Struct.new(
|
|
8
|
+
:message_ids,
|
|
9
|
+
keyword_init: true
|
|
10
|
+
)
|
|
11
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for an inbound thread (summary in list, with embedded messages when fetched by ID)
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader id [String] The thread ID
|
|
7
|
+
# @attr_reader subject [String, nil] The thread subject
|
|
8
|
+
# @attr_reader message_count [Integer] Number of messages in the thread
|
|
9
|
+
# @attr_reader size [Integer] Total size of the thread in bytes
|
|
10
|
+
# @attr_reader first_message_at [String] ISO 8601 timestamp of the first message
|
|
11
|
+
# @attr_reader last_received_at [String, nil] ISO 8601 timestamp of the last received message
|
|
12
|
+
# @attr_reader last_sent_at [String, nil] ISO 8601 timestamp of the last sent message
|
|
13
|
+
# @attr_reader last_activity_at [String] ISO 8601 timestamp of the last activity
|
|
14
|
+
# @attr_reader last_message_id [String, nil] ID of the most recent message
|
|
15
|
+
# @attr_reader senders [Array<String>] Distinct sender addresses in the thread
|
|
16
|
+
# @attr_reader recipients [Array<String>] Distinct recipient addresses in the thread
|
|
17
|
+
# @attr_reader attachments [Array<InboundAttachment>] Attachments across the thread
|
|
18
|
+
# @attr_reader messages [Array<InboundThreadMessage>, nil] The thread's messages (only when fetched by ID)
|
|
19
|
+
# rubocop:disable Lint/StructNewOverride -- +size+ is an API field that shadows Struct#size
|
|
20
|
+
InboundThread = Struct.new(
|
|
21
|
+
:id,
|
|
22
|
+
:subject,
|
|
23
|
+
:message_count,
|
|
24
|
+
:size,
|
|
25
|
+
:first_message_at,
|
|
26
|
+
:last_received_at,
|
|
27
|
+
:last_sent_at,
|
|
28
|
+
:last_activity_at,
|
|
29
|
+
:last_message_id,
|
|
30
|
+
:senders,
|
|
31
|
+
:recipients,
|
|
32
|
+
:attachments,
|
|
33
|
+
:messages,
|
|
34
|
+
keyword_init: true
|
|
35
|
+
)
|
|
36
|
+
# rubocop:enable Lint/StructNewOverride
|
|
37
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
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.
|
|
7
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
8
|
+
# @attr_reader visibility_status [String] available or placeholder
|
|
9
|
+
# @attr_reader direction [String] inbound or outbound
|
|
10
|
+
# @attr_reader id [String, nil] The message ID
|
|
11
|
+
# @attr_reader message_group_id [String, nil] Groups messages sent together as one logical send
|
|
12
|
+
# @attr_reader subject [String, nil] Email subject
|
|
13
|
+
# @attr_reader rfc_message_id [String, nil] Value of the RFC 5322 Message-ID header
|
|
14
|
+
# @attr_reader in_reply_to [String, nil] Value of the RFC 5322 In-Reply-To header
|
|
15
|
+
# @attr_reader references [Array<String>, nil] Values of the RFC 5322 References header
|
|
16
|
+
# @attr_reader from [String, nil] Sender address
|
|
17
|
+
# @attr_reader to [Array<String>, nil] Recipient addresses
|
|
18
|
+
# @attr_reader cc [Array<String>, nil] Cc addresses
|
|
19
|
+
# @attr_reader bcc [Array<String>, nil] Bcc addresses
|
|
20
|
+
# @attr_reader reply_to [String, nil] Reply-To address
|
|
21
|
+
# @attr_reader created_at [String, nil] ISO 8601 timestamp
|
|
22
|
+
# @attr_reader email_size [Integer, nil] Message size in bytes
|
|
23
|
+
# @attr_reader text_body [String, nil] Decoded text body
|
|
24
|
+
# @attr_reader html_body [String, nil] Decoded HTML body
|
|
25
|
+
# @attr_reader attachments [Array<InboundAttachment>, nil] The message attachments
|
|
26
|
+
# @attr_reader delivery_status [String, nil] Delivery status (outbound messages only)
|
|
27
|
+
# @attr_reader delivered_at [String, nil] Delivery timestamp (outbound messages only)
|
|
28
|
+
# @attr_reader bounced_at [String, nil] Bounce timestamp (outbound messages only)
|
|
29
|
+
InboundThreadMessage = Struct.new(
|
|
30
|
+
:visibility_status,
|
|
31
|
+
:direction,
|
|
32
|
+
:id,
|
|
33
|
+
:message_group_id,
|
|
34
|
+
:subject,
|
|
35
|
+
:rfc_message_id,
|
|
36
|
+
:in_reply_to,
|
|
37
|
+
:references,
|
|
38
|
+
:from,
|
|
39
|
+
:to,
|
|
40
|
+
:cc,
|
|
41
|
+
:bcc,
|
|
42
|
+
:reply_to,
|
|
43
|
+
:created_at,
|
|
44
|
+
:email_size,
|
|
45
|
+
:text_body,
|
|
46
|
+
:html_body,
|
|
47
|
+
:attachments,
|
|
48
|
+
:delivery_status,
|
|
49
|
+
:delivered_at,
|
|
50
|
+
:bounced_at,
|
|
51
|
+
keyword_init: true
|
|
52
|
+
)
|
|
53
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_api'
|
|
4
|
+
require_relative 'inbound_thread'
|
|
5
|
+
require_relative 'inbound_thread_message'
|
|
6
|
+
require_relative 'inbound_attachment'
|
|
7
|
+
require_relative 'inbound_threads_list_response'
|
|
8
|
+
|
|
9
|
+
module Mailtrap
|
|
10
|
+
class InboundThreadsAPI
|
|
11
|
+
include BaseAPI
|
|
12
|
+
|
|
13
|
+
self.response_class = InboundThread
|
|
14
|
+
|
|
15
|
+
# Inbound is scoped to the token's account, so no account_id is required.
|
|
16
|
+
# @param client [Mailtrap::Client] The client instance
|
|
17
|
+
def initialize(client = Mailtrap::Client.new)
|
|
18
|
+
@client = client
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Lists threads in an inbox (cursor-paginated)
|
|
22
|
+
# @param inbox_id [Integer] The inbox ID
|
|
23
|
+
# @param last_id [String, nil] Cursor from the previous response's +last_id+ for the next page
|
|
24
|
+
# @return [InboundThreadsListResponse] data, total_count, and last_id
|
|
25
|
+
# @!macro api_errors
|
|
26
|
+
def list(inbox_id, last_id: nil)
|
|
27
|
+
query_params = last_id ? { last_id: } : {}
|
|
28
|
+
response = client.get(threads_path(inbox_id), query_params)
|
|
29
|
+
|
|
30
|
+
InboundThreadsListResponse.new(
|
|
31
|
+
data: Array(response[:data]).map { |item| build_thread(item) },
|
|
32
|
+
total_count: response[:total_count],
|
|
33
|
+
last_id: response[:last_id]
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Fetches a single thread with its messages embedded (oldest first)
|
|
38
|
+
# @param inbox_id [Integer] The inbox ID
|
|
39
|
+
# @param thread_id [String] The thread ID
|
|
40
|
+
# @return [InboundThread] Thread with messages
|
|
41
|
+
# @!macro api_errors
|
|
42
|
+
def get(inbox_id, thread_id)
|
|
43
|
+
build_thread(client.get("#{threads_path(inbox_id)}/#{thread_id}"))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Deletes a thread
|
|
47
|
+
# @param inbox_id [Integer] The inbox ID
|
|
48
|
+
# @param thread_id [String] The thread ID
|
|
49
|
+
# @return nil
|
|
50
|
+
# @!macro api_errors
|
|
51
|
+
def delete(inbox_id, thread_id)
|
|
52
|
+
client.delete("#{threads_path(inbox_id)}/#{thread_id}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def threads_path(inbox_id)
|
|
58
|
+
"/api/inbound/inboxes/#{inbox_id}/threads"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def build_thread(hash)
|
|
62
|
+
attrs = hash.slice(*InboundThread.members)
|
|
63
|
+
attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments]
|
|
64
|
+
attrs[:messages] = build_messages(attrs[:messages]) if attrs[:messages]
|
|
65
|
+
|
|
66
|
+
InboundThread.new(**attrs)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def build_messages(messages)
|
|
70
|
+
Array(messages).map do |message|
|
|
71
|
+
attrs = message.slice(*InboundThreadMessage.members)
|
|
72
|
+
attrs[:attachments] = build_attachments(attrs[:attachments]) if attrs[:attachments]
|
|
73
|
+
|
|
74
|
+
InboundThreadMessage.new(**attrs)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def build_attachments(attachments)
|
|
79
|
+
Array(attachments).map do |attachment|
|
|
80
|
+
InboundAttachment.new(**attachment.slice(*InboundAttachment.members))
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mailtrap
|
|
4
|
+
# Data Transfer Object for a paginated list of inbound threads
|
|
5
|
+
# @see https://docs.mailtrap.io/developers/inbound
|
|
6
|
+
# @attr_reader data [Array<InboundThread>] The threads on this page
|
|
7
|
+
# @attr_reader total_count [Integer] Total number of threads in the inbox
|
|
8
|
+
# @attr_reader last_id [String, nil] Cursor for the next page, or nil if this is the last page
|
|
9
|
+
InboundThreadsListResponse = Struct.new(
|
|
10
|
+
:data,
|
|
11
|
+
:total_count,
|
|
12
|
+
:last_id,
|
|
13
|
+
keyword_init: true
|
|
14
|
+
)
|
|
15
|
+
end
|
|
@@ -17,6 +17,8 @@ module Mailtrap
|
|
|
17
17
|
# @attr_reader health_alerts_enabled [Boolean] Whether health alerts are enabled
|
|
18
18
|
# @attr_reader critical_alerts_enabled [Boolean] Whether critical alerts are enabled
|
|
19
19
|
# @attr_reader alert_recipient_email [String, nil] The email address for alert recipients
|
|
20
|
+
# @attr_reader inbound_enabled [Boolean] Whether inbound email is enabled for this domain
|
|
21
|
+
# @attr_reader inbound_verified [Boolean] Whether the domain's inbound MX records are verified
|
|
20
22
|
# @attr_reader permissions [Hash] The permissions for the sending domain
|
|
21
23
|
#
|
|
22
24
|
SendingDomain = Struct.new(
|
|
@@ -34,6 +36,8 @@ module Mailtrap
|
|
|
34
36
|
:health_alerts_enabled,
|
|
35
37
|
:critical_alerts_enabled,
|
|
36
38
|
:alert_recipient_email,
|
|
39
|
+
:inbound_enabled,
|
|
40
|
+
:inbound_verified,
|
|
37
41
|
:permissions,
|
|
38
42
|
:created_at,
|
|
39
43
|
:updated_at,
|
data/lib/mailtrap/version.rb
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'openssl'
|
|
4
|
+
|
|
5
|
+
module Mailtrap
|
|
6
|
+
# Helpers for working with inbound Mailtrap webhooks.
|
|
7
|
+
#
|
|
8
|
+
# @see https://docs.mailtrap.io/email-api-smtp/advanced/webhooks#verifying-the-signature
|
|
9
|
+
module Webhooks
|
|
10
|
+
# Hex-encoded HMAC-SHA256 signature length.
|
|
11
|
+
SIGNATURE_HEX_LENGTH = 64
|
|
12
|
+
|
|
13
|
+
# Verifies the HMAC-SHA256 signature of a Mailtrap webhook payload.
|
|
14
|
+
#
|
|
15
|
+
# Mailtrap signs every outbound webhook by computing
|
|
16
|
+
# `HMAC-SHA256(signing_secret, raw_request_body)` and sending the lowercase
|
|
17
|
+
# hex digest in the `Mailtrap-Signature` HTTP header. Compute the same
|
|
18
|
+
# digest on your side and compare it in constant time.
|
|
19
|
+
#
|
|
20
|
+
# The comparison is performed with {OpenSSL.fixed_length_secure_compare} to
|
|
21
|
+
# avoid timing side-channels.
|
|
22
|
+
#
|
|
23
|
+
# The method never raises on inputs that could plausibly arrive over the
|
|
24
|
+
# wire (empty strings, wrong-length signatures, non-hex characters, missing
|
|
25
|
+
# secret) — it simply returns `false`. This makes it safe to call directly
|
|
26
|
+
# from a controller without rescuing.
|
|
27
|
+
#
|
|
28
|
+
# @param payload [String] The raw request body, exactly as received.
|
|
29
|
+
# **Do not** parse and re-serialize the JSON — re-encoding may reorder
|
|
30
|
+
# keys or alter whitespace and invalidate the signature.
|
|
31
|
+
# @param signature [String] The value of the `Mailtrap-Signature` HTTP
|
|
32
|
+
# header (lowercase hex string).
|
|
33
|
+
# @param signing_secret [String] The webhook's `signing_secret`, returned
|
|
34
|
+
# by {WebhooksAPI#create} on webhook creation.
|
|
35
|
+
# @return [Boolean] `true` if the signature is valid for the given payload
|
|
36
|
+
# and secret, `false` otherwise.
|
|
37
|
+
def self.verify_signature(payload:, signature:, signing_secret:)
|
|
38
|
+
return false unless [payload, signature, signing_secret].all? { |v| v.is_a?(String) && !v.empty? }
|
|
39
|
+
return false if signature.bytesize != SIGNATURE_HEX_LENGTH
|
|
40
|
+
|
|
41
|
+
expected = OpenSSL::HMAC.hexdigest('SHA256', signing_secret, payload)
|
|
42
|
+
|
|
43
|
+
OpenSSL.fixed_length_secure_compare(expected, signature)
|
|
44
|
+
rescue ArgumentError
|
|
45
|
+
# fixed_length_secure_compare raises ArgumentError on length mismatch
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/mailtrap.rb
CHANGED
|
@@ -27,6 +27,11 @@ require_relative 'mailtrap/sandbox_messages_api'
|
|
|
27
27
|
require_relative 'mailtrap/sandbox_attachments_api'
|
|
28
28
|
require_relative 'mailtrap/stats_api'
|
|
29
29
|
require_relative 'mailtrap/webhooks_api'
|
|
30
|
+
require_relative 'mailtrap/webhooks'
|
|
31
|
+
require_relative 'mailtrap/inbound_folders_api'
|
|
32
|
+
require_relative 'mailtrap/inbound_inboxes_api'
|
|
33
|
+
require_relative 'mailtrap/inbound_messages_api'
|
|
34
|
+
require_relative 'mailtrap/inbound_threads_api'
|
|
30
35
|
|
|
31
36
|
module Mailtrap
|
|
32
37
|
# @!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.12.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-
|
|
11
|
+
date: 2026-07-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -80,6 +80,19 @@ files:
|
|
|
80
80
|
- lib/mailtrap/email_template.rb
|
|
81
81
|
- lib/mailtrap/email_templates_api.rb
|
|
82
82
|
- lib/mailtrap/errors.rb
|
|
83
|
+
- lib/mailtrap/inbound_attachment.rb
|
|
84
|
+
- lib/mailtrap/inbound_folder.rb
|
|
85
|
+
- lib/mailtrap/inbound_folders_api.rb
|
|
86
|
+
- lib/mailtrap/inbound_inbox.rb
|
|
87
|
+
- lib/mailtrap/inbound_inboxes_api.rb
|
|
88
|
+
- lib/mailtrap/inbound_message.rb
|
|
89
|
+
- lib/mailtrap/inbound_messages_api.rb
|
|
90
|
+
- lib/mailtrap/inbound_messages_list_response.rb
|
|
91
|
+
- lib/mailtrap/inbound_send_result.rb
|
|
92
|
+
- lib/mailtrap/inbound_thread.rb
|
|
93
|
+
- lib/mailtrap/inbound_thread_message.rb
|
|
94
|
+
- lib/mailtrap/inbound_threads_api.rb
|
|
95
|
+
- lib/mailtrap/inbound_threads_list_response.rb
|
|
83
96
|
- lib/mailtrap/inbox.rb
|
|
84
97
|
- lib/mailtrap/inboxes_api.rb
|
|
85
98
|
- lib/mailtrap/mail.rb
|
|
@@ -104,6 +117,7 @@ files:
|
|
|
104
117
|
- lib/mailtrap/suppressions_api.rb
|
|
105
118
|
- lib/mailtrap/version.rb
|
|
106
119
|
- lib/mailtrap/webhook.rb
|
|
120
|
+
- lib/mailtrap/webhooks.rb
|
|
107
121
|
- lib/mailtrap/webhooks_api.rb
|
|
108
122
|
- mailtrap.gemspec
|
|
109
123
|
homepage: https://github.com/mailtrap/mailtrap-ruby
|
|
@@ -129,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
129
143
|
- !ruby/object:Gem::Version
|
|
130
144
|
version: '0'
|
|
131
145
|
requirements: []
|
|
132
|
-
rubygems_version: 3.5.
|
|
146
|
+
rubygems_version: 3.5.22
|
|
133
147
|
signing_key:
|
|
134
148
|
specification_version: 4
|
|
135
149
|
summary: Official mailtrap.io API client
|