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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed5308d363064b9bea74f6d008a55bde447dc2f956689a2d357e8ce8467775b9
4
- data.tar.gz: 259675d6b858aba7da91e34b31e7a199ab5f98b70b81bbc0c64b8e72c959c01f
3
+ metadata.gz: d1b4f565a1eeaa3276ef82ece624d8fb7f0e2f04f1391b6c6ac3eac119a4e828
4
+ data.tar.gz: f1bf41d6a0c55dfe7c7c0a799effa3c2d0d666dff1a2e766a43209f97890ba65
5
5
  SHA512:
6
- metadata.gz: eb5deeb16c7d71d1873557cbbdace95c73e3d3c913f4a157f8c55345ce3c06636d16abbbb69d5a268b461b4dbec81faa15d435db1a6e77f311f60183b4127027
7
- data.tar.gz: cd86a4342f1994e0462f90537de60d36e29baea5b737fa383bd6d5cdb8bde5e943dc4910a3fd3332c4b50eeeba0c9d8132344c641bcb773373f8f319928c0bcf
6
+ metadata.gz: 48d156ee8fc31d249f1f89fa8f06407a2675ba243cb2955c3a82bf91ccd0b9b30ea5efc6294e38f9c32f3bbe8821cc1c16d343c9880960e2e9bfbfafb07bcf4f
7
+ data.tar.gz: 29a56256dbeabe5dcdcc09aa468495dab2bdb9c4fb62cbe1d39106bc82bd54cc4b4a925ff2b8025b21eef56ea5705bae2109cb7a41193ec4e94d68461e493104
@@ -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
@@ -5,40 +5,69 @@ module EmailFuse
5
5
  module Emails
6
6
  class << self
7
7
  # Sends or schedules an email.
8
- # see more: https://resend.com/docs/api-reference/emails/send-email
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
- # see more: https://resend.com/docs/api-reference/emails/retrieve-email
16
- def get(email_id = "")
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
- # see more: https://resend.com/docs/api-reference/emails/update-email
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
- path = "emails/#{params[:email_id]}"
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
- # see more: https://resend.com/docs/api-reference/emails/cancel-email
30
- def cancel(email_id = "")
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
 
@@ -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(ServerError)
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 < ServerError
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::InvalidRequestError,
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)
@@ -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: part.body.decoded.bytes
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
@@ -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?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EmailFuse
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -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.2.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: '0'
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: '0'
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: '2.6'
73
+ version: '3.3'
74
74
  required_rubygems_version: !ruby/object:Gem::Requirement
75
75
  requirements:
76
76
  - - ">="