emailfuse 0.2.0 → 0.3.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/lib/email_fuse/batch.rb +3 -0
- data/lib/email_fuse/emails/attachments.rb +5 -0
- data/lib/email_fuse/emails/receiving/attachments.rb +5 -0
- data/lib/email_fuse/emails/receiving.rb +3 -1
- data/lib/email_fuse/emails.rb +37 -8
- data/lib/email_fuse/errors.rb +9 -10
- data/lib/email_fuse/mailer.rb +8 -1
- data/lib/email_fuse/request.rb +5 -1
- data/lib/email_fuse/version.rb +1 -1
- data/lib/email_fuse/webhooks.rb +11 -4
- data/lib/email_fuse.rb +3 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d1b4f565a1eeaa3276ef82ece624d8fb7f0e2f04f1391b6c6ac3eac119a4e828
|
|
4
|
+
data.tar.gz: f1bf41d6a0c55dfe7c7c0a799effa3c2d0d666dff1a2e766a43209f97890ba65
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48d156ee8fc31d249f1f89fa8f06407a2675ba243cb2955c3a82bf91ccd0b9b30ea5efc6294e38f9c32f3bbe8821cc1c16d343c9880960e2e9bfbfafb07bcf4f
|
|
7
|
+
data.tar.gz: 29a56256dbeabe5dcdcc09aa468495dab2bdb9c4fb62cbe1d39106bc82bd54cc4b4a925ff2b8025b21eef56ea5705bae2109cb7a41193ec4e94d68461e493104
|
data/lib/email_fuse/batch.rb
CHANGED
|
@@ -27,6 +27,9 @@ module EmailFuse
|
|
|
27
27
|
#
|
|
28
28
|
# https://resend.com/docs/api-reference/emails/send-batch-emails
|
|
29
29
|
def send(params = [], options: {})
|
|
30
|
+
raise ArgumentError, "params must be an array of email hashes" unless params.is_a?(Array)
|
|
31
|
+
raise ArgumentError, "params cannot be empty" if params.empty?
|
|
32
|
+
|
|
30
33
|
path = "emails/batch"
|
|
31
34
|
|
|
32
35
|
EmailFuse::Request.new(path, params, "post", options: options).perform
|
|
@@ -21,6 +21,9 @@ module EmailFuse
|
|
|
21
21
|
attachment_id = params[:id]
|
|
22
22
|
email_id = params[:email_id]
|
|
23
23
|
|
|
24
|
+
raise ArgumentError, ":email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
25
|
+
raise ArgumentError, ":id is required" if attachment_id.nil? || attachment_id.to_s.empty?
|
|
26
|
+
|
|
24
27
|
path = "emails/#{email_id}/attachments/#{attachment_id}"
|
|
25
28
|
EmailFuse::Request.new(path, {}, "get").perform
|
|
26
29
|
end
|
|
@@ -53,6 +56,8 @@ module EmailFuse
|
|
|
53
56
|
# )
|
|
54
57
|
def list(params = {})
|
|
55
58
|
email_id = params[:email_id]
|
|
59
|
+
raise ArgumentError, ":email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
60
|
+
|
|
56
61
|
base_path = "emails/#{email_id}/attachments"
|
|
57
62
|
|
|
58
63
|
# Extract pagination parameters
|
|
@@ -22,6 +22,9 @@ module EmailFuse
|
|
|
22
22
|
attachment_id = params[:id]
|
|
23
23
|
email_id = params[:email_id]
|
|
24
24
|
|
|
25
|
+
raise ArgumentError, ":email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
26
|
+
raise ArgumentError, ":id is required" if attachment_id.nil? || attachment_id.to_s.empty?
|
|
27
|
+
|
|
25
28
|
path = "emails/receiving/#{email_id}/attachments/#{attachment_id}"
|
|
26
29
|
EmailFuse::Request.new(path, {}, "get").perform
|
|
27
30
|
end
|
|
@@ -54,6 +57,8 @@ module EmailFuse
|
|
|
54
57
|
# )
|
|
55
58
|
def list(params = {})
|
|
56
59
|
email_id = params[:email_id]
|
|
60
|
+
raise ArgumentError, ":email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
61
|
+
|
|
57
62
|
base_path = "emails/receiving/#{email_id}/attachments"
|
|
58
63
|
|
|
59
64
|
# Extract pagination parameters
|
|
@@ -12,7 +12,9 @@ module EmailFuse
|
|
|
12
12
|
#
|
|
13
13
|
# @example
|
|
14
14
|
# EmailFuse::Emails::Receiving.get("4ef9a417-02e9-4d39-ad75-9611e0fcc33c")
|
|
15
|
-
def get(email_id
|
|
15
|
+
def get(email_id)
|
|
16
|
+
raise ArgumentError, "email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
17
|
+
|
|
16
18
|
path = "emails/receiving/#{email_id}"
|
|
17
19
|
EmailFuse::Request.new(path, {}, "get").perform
|
|
18
20
|
end
|
data/lib/email_fuse/emails.rb
CHANGED
|
@@ -5,40 +5,69 @@ module EmailFuse
|
|
|
5
5
|
module Emails
|
|
6
6
|
class << self
|
|
7
7
|
# Sends or schedules an email.
|
|
8
|
-
#
|
|
8
|
+
#
|
|
9
|
+
# @param params [Hash] Email parameters
|
|
10
|
+
# @option params [String] :from The sender email address (required)
|
|
11
|
+
# @option params [Array<String>, String] :to The recipient email address(es) (required)
|
|
12
|
+
# @option params [String] :subject The email subject
|
|
13
|
+
# @option params [String] :html The HTML content of the email
|
|
14
|
+
# @option params [String] :text The plain text content of the email
|
|
15
|
+
#
|
|
16
|
+
# @return [EmailFuse::Response] The response containing the email ID
|
|
9
17
|
def send(params, options: {})
|
|
18
|
+
raise ArgumentError, ":from is required" unless params[:from] || params["from"]
|
|
19
|
+
raise ArgumentError, ":to is required" unless params[:to] || params["to"]
|
|
20
|
+
|
|
10
21
|
path = "emails"
|
|
11
22
|
EmailFuse::Request.new(path, params, "post", options: options).perform
|
|
12
23
|
end
|
|
13
24
|
|
|
14
25
|
# Retrieve a single email.
|
|
15
|
-
#
|
|
16
|
-
|
|
26
|
+
#
|
|
27
|
+
# @param email_id [String] The email ID (required)
|
|
28
|
+
#
|
|
29
|
+
# @return [EmailFuse::Response] The email object
|
|
30
|
+
def get(email_id)
|
|
31
|
+
raise ArgumentError, "email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
32
|
+
|
|
17
33
|
path = "emails/#{email_id}"
|
|
18
34
|
EmailFuse::Request.new(path, {}, "get").perform
|
|
19
35
|
end
|
|
20
36
|
|
|
21
37
|
# Update a scheduled email.
|
|
22
|
-
#
|
|
38
|
+
#
|
|
39
|
+
# @param params [Hash] Update parameters
|
|
40
|
+
# @option params [String] :email_id The email ID (required)
|
|
41
|
+
#
|
|
42
|
+
# @return [EmailFuse::Response] The updated email object
|
|
23
43
|
def update(params)
|
|
24
|
-
|
|
44
|
+
email_id = params[:email_id] || params["email_id"]
|
|
45
|
+
raise ArgumentError, ":email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
46
|
+
|
|
47
|
+
path = "emails/#{email_id}"
|
|
25
48
|
EmailFuse::Request.new(path, params, "patch").perform
|
|
26
49
|
end
|
|
27
50
|
|
|
28
51
|
# Cancel a scheduled email.
|
|
29
|
-
#
|
|
30
|
-
|
|
52
|
+
#
|
|
53
|
+
# @param email_id [String] The email ID (required)
|
|
54
|
+
#
|
|
55
|
+
# @return [EmailFuse::Response] Confirmation of cancellation
|
|
56
|
+
def cancel(email_id)
|
|
57
|
+
raise ArgumentError, "email_id is required" if email_id.nil? || email_id.to_s.empty?
|
|
58
|
+
|
|
31
59
|
path = "emails/#{email_id}/cancel"
|
|
32
60
|
EmailFuse::Request.new(path, {}, "post").perform
|
|
33
61
|
end
|
|
34
62
|
|
|
35
63
|
# List emails with optional pagination.
|
|
36
|
-
# see more: https://resend.com/docs/api-reference/emails/list-emails
|
|
37
64
|
#
|
|
38
65
|
# @param options [Hash] Optional parameters for pagination
|
|
39
66
|
# @option options [Integer] :limit Maximum number of emails to return (1-100, default 20)
|
|
40
67
|
# @option options [String] :after Cursor for pagination (newer emails)
|
|
41
68
|
# @option options [String] :before Cursor for pagination (older emails)
|
|
69
|
+
#
|
|
70
|
+
# @return [EmailFuse::Response] Paginated list of emails
|
|
42
71
|
def list(options = {})
|
|
43
72
|
path = "emails"
|
|
44
73
|
|
data/lib/email_fuse/errors.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
module EmailFuse
|
|
4
4
|
# Errors wrapper class
|
|
5
|
-
# For more info: https://resend.com/docs/api-reference/error-codes
|
|
6
5
|
class Error < StandardError
|
|
7
6
|
# 4xx HTTP status code
|
|
8
7
|
ClientError = Class.new(self)
|
|
@@ -13,11 +12,14 @@ module EmailFuse
|
|
|
13
12
|
# code 500
|
|
14
13
|
InternalServerError = Class.new(ServerError)
|
|
15
14
|
|
|
16
|
-
# code 422
|
|
17
|
-
InvalidRequestError = Class.new(
|
|
15
|
+
# code 400, 401, 404, 422 - these are client errors (4xx)
|
|
16
|
+
InvalidRequestError = Class.new(ClientError)
|
|
17
|
+
|
|
18
|
+
# code 404
|
|
19
|
+
NotFoundError = Class.new(ClientError)
|
|
18
20
|
|
|
19
21
|
# code 429
|
|
20
|
-
class RateLimitExceededError <
|
|
22
|
+
class RateLimitExceededError < ClientError
|
|
21
23
|
attr_reader :rate_limit_limit, :rate_limit_remaining, :rate_limit_reset, :retry_after
|
|
22
24
|
|
|
23
25
|
def initialize(msg, code = nil, headers = {})
|
|
@@ -29,19 +31,16 @@ module EmailFuse
|
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
# code 404
|
|
33
|
-
NotFoundError = Class.new(ServerError)
|
|
34
|
-
|
|
35
34
|
ERRORS = {
|
|
35
|
+
400 => EmailFuse::Error::InvalidRequestError,
|
|
36
36
|
401 => EmailFuse::Error::InvalidRequestError,
|
|
37
|
-
404 => EmailFuse::Error::
|
|
37
|
+
404 => EmailFuse::Error::NotFoundError,
|
|
38
38
|
422 => EmailFuse::Error::InvalidRequestError,
|
|
39
39
|
429 => EmailFuse::Error::RateLimitExceededError,
|
|
40
|
-
400 => EmailFuse::Error::InvalidRequestError,
|
|
41
40
|
500 => EmailFuse::Error::InternalServerError
|
|
42
41
|
}.freeze
|
|
43
42
|
|
|
44
|
-
attr_reader :headers
|
|
43
|
+
attr_reader :code, :headers
|
|
45
44
|
|
|
46
45
|
def initialize(msg, code = nil, headers = {})
|
|
47
46
|
super(msg)
|
data/lib/email_fuse/mailer.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "base64"
|
|
3
4
|
require "email_fuse"
|
|
4
5
|
|
|
5
6
|
module EmailFuse
|
|
@@ -225,15 +226,21 @@ module EmailFuse
|
|
|
225
226
|
|
|
226
227
|
#
|
|
227
228
|
# Handle attachments when present
|
|
229
|
+
# Uses base64 encoding for better API compatibility
|
|
228
230
|
#
|
|
229
231
|
# @return Array attachments array
|
|
230
232
|
#
|
|
231
233
|
def get_attachments(mail)
|
|
232
234
|
attachments = []
|
|
233
235
|
mail.attachments.each do |part|
|
|
236
|
+
# Get decoded content and ensure binary encoding for consistent base64 output
|
|
237
|
+
content = part.body.decoded.dup
|
|
238
|
+
content = content.force_encoding(Encoding::BINARY)
|
|
239
|
+
|
|
234
240
|
attachment = {
|
|
235
241
|
filename: part.filename,
|
|
236
|
-
content:
|
|
242
|
+
content: Base64.strict_encode64(content),
|
|
243
|
+
content_type: part.content_type
|
|
237
244
|
}
|
|
238
245
|
|
|
239
246
|
# Rails uses the auto generated cid for inline attachments
|
data/lib/email_fuse/request.rb
CHANGED
|
@@ -9,7 +9,7 @@ module EmailFuse
|
|
|
9
9
|
def initialize(path = "", body = {}, verb = "POST", options: {})
|
|
10
10
|
# Allow api_key override via options, fall back to global config
|
|
11
11
|
api_key = options[:api_key] || EmailFuse.api_key
|
|
12
|
-
raise if api_key.nil?
|
|
12
|
+
raise ArgumentError, "API key is required. Set via EmailFuse.api_key= or pass :api_key in options" if api_key.nil?
|
|
13
13
|
|
|
14
14
|
api_key = api_key.call if api_key.is_a?(Proc)
|
|
15
15
|
|
|
@@ -58,6 +58,10 @@ module EmailFuse
|
|
|
58
58
|
def build_request_options
|
|
59
59
|
options = { headers: @headers }
|
|
60
60
|
|
|
61
|
+
# Set timeout from options, global config, or default
|
|
62
|
+
timeout = @options[:timeout] || EmailFuse.timeout || EmailFuse::DEFAULT_TIMEOUT
|
|
63
|
+
options[:timeout] = timeout
|
|
64
|
+
|
|
61
65
|
if get_request_with_query?
|
|
62
66
|
options[:query] = @body
|
|
63
67
|
elsif !@body.empty?
|
data/lib/email_fuse/version.rb
CHANGED
data/lib/email_fuse/webhooks.rb
CHANGED
|
@@ -73,13 +73,15 @@ module EmailFuse
|
|
|
73
73
|
|
|
74
74
|
# Retrieve a single webhook for the authenticated user
|
|
75
75
|
#
|
|
76
|
-
# @param webhook_id [String] The webhook ID
|
|
76
|
+
# @param webhook_id [String] The webhook ID (required)
|
|
77
77
|
#
|
|
78
78
|
# @return [Hash] The webhook object with full details
|
|
79
79
|
#
|
|
80
80
|
# @example
|
|
81
81
|
# EmailFuse::Webhooks.get("4dd369bc-aa82-4ff3-97de-514ae3000ee0")
|
|
82
|
-
def get(webhook_id
|
|
82
|
+
def get(webhook_id)
|
|
83
|
+
raise ArgumentError, "webhook_id is required" if webhook_id.nil? || webhook_id.to_s.empty?
|
|
84
|
+
|
|
83
85
|
path = "webhooks/#{webhook_id}"
|
|
84
86
|
EmailFuse::Request.new(path, {}, "get").perform
|
|
85
87
|
end
|
|
@@ -102,20 +104,25 @@ module EmailFuse
|
|
|
102
104
|
# status: "enabled"
|
|
103
105
|
# )
|
|
104
106
|
def update(params = {})
|
|
107
|
+
params = params.dup # Don't mutate caller's hash
|
|
105
108
|
webhook_id = params.delete(:webhook_id)
|
|
109
|
+
raise ArgumentError, ":webhook_id is required" if webhook_id.nil? || webhook_id.to_s.empty?
|
|
110
|
+
|
|
106
111
|
path = "webhooks/#{webhook_id}"
|
|
107
112
|
EmailFuse::Request.new(path, params, "patch").perform
|
|
108
113
|
end
|
|
109
114
|
|
|
110
115
|
# Remove an existing webhook
|
|
111
116
|
#
|
|
112
|
-
# @param webhook_id [String] The webhook ID
|
|
117
|
+
# @param webhook_id [String] The webhook ID (required)
|
|
113
118
|
#
|
|
114
119
|
# @return [Hash] Confirmation object with id, object type, and deleted status
|
|
115
120
|
#
|
|
116
121
|
# @example
|
|
117
122
|
# EmailFuse::Webhooks.remove("4dd369bc-aa82-4ff3-97de-514ae3000ee0")
|
|
118
|
-
def remove(webhook_id
|
|
123
|
+
def remove(webhook_id)
|
|
124
|
+
raise ArgumentError, "webhook_id is required" if webhook_id.nil? || webhook_id.to_s.empty?
|
|
125
|
+
|
|
119
126
|
path = "webhooks/#{webhook_id}"
|
|
120
127
|
EmailFuse::Request.new(path, {}, "delete").perform
|
|
121
128
|
end
|
data/lib/email_fuse.rb
CHANGED
|
@@ -26,8 +26,10 @@ require "email_fuse/railtie" if defined?(Rails) && defined?(ActionMailer)
|
|
|
26
26
|
|
|
27
27
|
# Main EmailFuse module
|
|
28
28
|
module EmailFuse
|
|
29
|
+
DEFAULT_TIMEOUT = 30
|
|
30
|
+
|
|
29
31
|
class << self
|
|
30
|
-
attr_accessor :api_key
|
|
32
|
+
attr_accessor :api_key, :timeout
|
|
31
33
|
attr_writer :base_url
|
|
32
34
|
|
|
33
35
|
def configure
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: emailfuse
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dean Perry
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '7.2'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '7.2'
|
|
40
40
|
email: dean@voupe.com
|
|
41
41
|
executables: []
|
|
42
42
|
extensions: []
|
|
@@ -70,7 +70,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
70
70
|
requirements:
|
|
71
71
|
- - ">="
|
|
72
72
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: '
|
|
73
|
+
version: '3.3'
|
|
74
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
75
|
requirements:
|
|
76
76
|
- - ">="
|