resend 0.24.0 → 0.26.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/README.md +1 -1
- data/lib/resend/api_keys.rb +2 -2
- data/lib/resend/audiences.rb +2 -2
- data/lib/resend/batch.rb +21 -0
- data/lib/resend/broadcasts.rb +2 -2
- data/lib/resend/contacts.rb +3 -2
- data/lib/resend/domains.rb +3 -3
- data/lib/resend/emails.rb +19 -0
- data/lib/resend/errors.rb +15 -2
- data/lib/resend/pagination_helper.rb +30 -0
- data/lib/resend/request.rb +40 -14
- data/lib/resend/version.rb +1 -1
- data/lib/resend.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 380fc77dd4f78646cea876d8a41c040c2456f5d83de51794adb366ae0cdf4c68
|
4
|
+
data.tar.gz: 6e868e3144a11781a20dcdd7fc2613e01885820d621595421845bceefee4c334
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6aecfeb42d92ef7a7c90620a0a529f522b26c00b9341cdcf2ca0ae71c100710703b4162b96d4889f29a09825285a923ce1feaef465765ce2d060dffcf17405d4
|
7
|
+
data.tar.gz: c14fb10e9a778579ad5c1615899db85b90e2feba28a7b6441f0b3dcde4679a1d4bafe26ef93e9631ca2087540ade271687b174d797acad10afda53eacef4f10b
|
data/README.md
CHANGED
@@ -72,7 +72,7 @@ puts r
|
|
72
72
|
|
73
73
|
You can view all the examples in the [examples folder](https://github.com/drish/resend-ruby/tree/main/examples)
|
74
74
|
|
75
|
-
# Rails and
|
75
|
+
# Rails and ActionMailer support
|
76
76
|
|
77
77
|
This gem can be used as an ActionMailer delivery method, add this to your `config/environments/environment.rb` file.
|
78
78
|
|
data/lib/resend/api_keys.rb
CHANGED
@@ -11,8 +11,8 @@ module Resend
|
|
11
11
|
end
|
12
12
|
|
13
13
|
# https://resend.com/docs/api-reference/api-keys/list-api-keys
|
14
|
-
def list
|
15
|
-
path = "api-keys"
|
14
|
+
def list(params = {})
|
15
|
+
path = Resend::PaginationHelper.build_paginated_path("api-keys", params)
|
16
16
|
Resend::Request.new(path, {}, "get").perform
|
17
17
|
end
|
18
18
|
|
data/lib/resend/audiences.rb
CHANGED
@@ -17,8 +17,8 @@ module Resend
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# https://resend.com/docs/api-reference/audiences/list-audiences
|
20
|
-
def list
|
21
|
-
path = "audiences"
|
20
|
+
def list(params = {})
|
21
|
+
path = Resend::PaginationHelper.build_paginated_path("audiences", params)
|
22
22
|
Resend::Request.new(path, {}, "get").perform
|
23
23
|
end
|
24
24
|
|
data/lib/resend/batch.rb
CHANGED
@@ -4,6 +4,27 @@ module Resend
|
|
4
4
|
# Module responsible for wrapping Batch email sending API
|
5
5
|
module Batch
|
6
6
|
class << self
|
7
|
+
# Send a batch of emails
|
8
|
+
#
|
9
|
+
# @param params [Array<Hash>] Array of email parameters (max 100 emails)
|
10
|
+
# @param options [Hash] Additional options for the request
|
11
|
+
# @option options [String] :idempotency_key Optional idempotency key
|
12
|
+
# @option options [String] :batch_validation Batch validation mode: "strict" (default) or "permissive"
|
13
|
+
# - "strict": Entire batch fails if any email is invalid
|
14
|
+
# - "permissive": Sends valid emails and returns errors for invalid ones
|
15
|
+
#
|
16
|
+
# @return [Hash] Response with :data array and optional :errors array (in permissive mode)
|
17
|
+
#
|
18
|
+
# @example Send batch with strict validation (default)
|
19
|
+
# Resend::Batch.send([
|
20
|
+
# { from: "sender@example.com", to: ["recipient@example.com"], subject: "Hello", html: "<p>Hi</p>" }
|
21
|
+
# ])
|
22
|
+
#
|
23
|
+
# @example Send batch with permissive validation
|
24
|
+
# response = Resend::Batch.send(emails, options: { batch_validation: "permissive" })
|
25
|
+
# # response[:data] contains successful email IDs
|
26
|
+
# # response[:errors] contains validation errors with index and message
|
27
|
+
#
|
7
28
|
# https://resend.com/docs/api-reference/emails/send-batch-emails
|
8
29
|
def send(params = [], options: {})
|
9
30
|
path = "emails/batch"
|
data/lib/resend/broadcasts.rb
CHANGED
@@ -23,8 +23,8 @@ module Resend
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# https://resend.com/docs/api-reference/broadcasts/list-broadcasts
|
26
|
-
def list
|
27
|
-
path = "broadcasts"
|
26
|
+
def list(params = {})
|
27
|
+
path = Resend::PaginationHelper.build_paginated_path("broadcasts", params)
|
28
28
|
Resend::Request.new(path, {}, "get").perform
|
29
29
|
end
|
30
30
|
|
data/lib/resend/contacts.rb
CHANGED
@@ -26,9 +26,10 @@ module Resend
|
|
26
26
|
# List contacts in an audience
|
27
27
|
#
|
28
28
|
# @param audience_id [String] the audience id
|
29
|
+
# @param params [Hash] optional pagination parameters
|
29
30
|
# https://resend.com/docs/api-reference/contacts/list-contacts
|
30
|
-
def list(audience_id)
|
31
|
-
path = "audiences/#{audience_id}/contacts"
|
31
|
+
def list(audience_id, params = {})
|
32
|
+
path = Resend::PaginationHelper.build_paginated_path("audiences/#{audience_id}/contacts", params)
|
32
33
|
Resend::Request.new(path, {}, "get").perform
|
33
34
|
end
|
34
35
|
|
data/lib/resend/domains.rb
CHANGED
@@ -22,9 +22,9 @@ module Resend
|
|
22
22
|
Resend::Request.new(path, {}, "get").perform
|
23
23
|
end
|
24
24
|
|
25
|
-
# https://resend.com/docs/api-reference/
|
26
|
-
def list
|
27
|
-
path = "domains"
|
25
|
+
# https://resend.com/docs/api-reference/domains/list-domains
|
26
|
+
def list(params = {})
|
27
|
+
path = Resend::PaginationHelper.build_paginated_path("domains", params)
|
28
28
|
Resend::Request.new(path, {}, "get").perform
|
29
29
|
end
|
30
30
|
|
data/lib/resend/emails.rb
CHANGED
@@ -31,6 +31,25 @@ module Resend
|
|
31
31
|
path = "emails/#{email_id}/cancel"
|
32
32
|
Resend::Request.new(path, {}, "post").perform
|
33
33
|
end
|
34
|
+
|
35
|
+
# List emails with optional pagination.
|
36
|
+
# see more: https://resend.com/docs/api-reference/emails/list-emails
|
37
|
+
#
|
38
|
+
# @param options [Hash] Optional parameters for pagination
|
39
|
+
# @option options [Integer] :limit Maximum number of emails to return (1-100, default 20)
|
40
|
+
# @option options [String] :after Cursor for pagination (newer emails)
|
41
|
+
# @option options [String] :before Cursor for pagination (older emails)
|
42
|
+
def list(options = {})
|
43
|
+
path = "emails"
|
44
|
+
|
45
|
+
# Build query parameters, filtering out nil values
|
46
|
+
query_params = {}
|
47
|
+
query_params[:limit] = options[:limit] if options[:limit]
|
48
|
+
query_params[:after] = options[:after] if options[:after]
|
49
|
+
query_params[:before] = options[:before] if options[:before]
|
50
|
+
|
51
|
+
Resend::Request.new(path, query_params, "get").perform
|
52
|
+
end
|
34
53
|
end
|
35
54
|
|
36
55
|
# This method is kept here for backwards compatibility
|
data/lib/resend/errors.rb
CHANGED
@@ -17,7 +17,17 @@ module Resend
|
|
17
17
|
InvalidRequestError = Class.new(ServerError)
|
18
18
|
|
19
19
|
# code 429
|
20
|
-
RateLimitExceededError
|
20
|
+
class RateLimitExceededError < ServerError
|
21
|
+
attr_reader :rate_limit_limit, :rate_limit_remaining, :rate_limit_reset, :retry_after
|
22
|
+
|
23
|
+
def initialize(msg, code = nil, headers = {})
|
24
|
+
super(msg, code, headers)
|
25
|
+
@rate_limit_limit = headers["ratelimit-limit"]&.to_i
|
26
|
+
@rate_limit_remaining = headers["ratelimit-remaining"]&.to_i
|
27
|
+
@rate_limit_reset = headers["ratelimit-reset"]&.to_i
|
28
|
+
@retry_after = headers["retry-after"]&.to_i
|
29
|
+
end
|
30
|
+
end
|
21
31
|
|
22
32
|
# code 404
|
23
33
|
NotFoundError = Class.new(ServerError)
|
@@ -31,9 +41,12 @@ module Resend
|
|
31
41
|
500 => Resend::Error::InternalServerError
|
32
42
|
}.freeze
|
33
43
|
|
34
|
-
|
44
|
+
attr_reader :headers
|
45
|
+
|
46
|
+
def initialize(msg, code = nil, headers = {})
|
35
47
|
super(msg)
|
36
48
|
@code = code
|
49
|
+
@headers = headers
|
37
50
|
end
|
38
51
|
end
|
39
52
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Resend
|
4
|
+
# Helper class for building paginated query strings
|
5
|
+
class PaginationHelper
|
6
|
+
class << self
|
7
|
+
# Builds a paginated path with query parameters
|
8
|
+
#
|
9
|
+
# @param base_path [String] the base API path
|
10
|
+
# @param query_params [Hash] optional pagination parameters
|
11
|
+
# @option query_params [Integer] :limit Number of items to retrieve (max 100)
|
12
|
+
# @option query_params [String] :after ID after which to retrieve more items
|
13
|
+
# @option query_params [String] :before ID before which to retrieve more items
|
14
|
+
# @return [String] the path with query parameters
|
15
|
+
def build_paginated_path(base_path, query_params = nil)
|
16
|
+
return base_path if query_params.nil? || query_params.empty?
|
17
|
+
|
18
|
+
# Filter out nil values and convert to string keys
|
19
|
+
filtered_params = query_params.compact.transform_keys(&:to_s)
|
20
|
+
|
21
|
+
# Build query string
|
22
|
+
query_string = filtered_params.map { |k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join("&")
|
23
|
+
|
24
|
+
return base_path if query_string.empty?
|
25
|
+
|
26
|
+
"#{base_path}?#{query_string}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/resend/request.rb
CHANGED
@@ -25,38 +25,56 @@ module Resend
|
|
25
25
|
}
|
26
26
|
|
27
27
|
set_idempotency_key
|
28
|
+
set_batch_validation
|
28
29
|
end
|
29
30
|
|
30
31
|
# Performs the HTTP call
|
31
32
|
def perform
|
32
|
-
options =
|
33
|
-
headers: @headers
|
34
|
-
}
|
35
|
-
options[:body] = @body.to_json unless @body.empty?
|
36
|
-
|
33
|
+
options = build_request_options
|
37
34
|
resp = HTTParty.send(@verb.to_sym, "#{BASE_URL}#{@path}", options)
|
38
35
|
|
39
36
|
check_json!(resp)
|
40
|
-
|
41
|
-
resp.transform_keys!(&:to_sym) unless resp.body.empty?
|
42
|
-
handle_error!(resp) if resp[:statusCode] && (resp[:statusCode] != 200 || resp[:statusCode] != 201)
|
43
|
-
resp
|
37
|
+
process_response(resp)
|
44
38
|
end
|
45
39
|
|
46
40
|
def handle_error!(resp)
|
47
41
|
code = resp[:statusCode]
|
48
42
|
body = resp[:message]
|
43
|
+
headers = resp.respond_to?(:headers) ? resp.headers : (resp[:headers] || {})
|
49
44
|
|
50
45
|
# get error from the known list of errors
|
51
|
-
|
52
|
-
raise
|
53
|
-
|
54
|
-
# Raise generic Resend error when the error code is not part of the known errors
|
55
|
-
raise Resend::Error.new(body, code)
|
46
|
+
error_class = Resend::Error::ERRORS[code] || Resend::Error
|
47
|
+
raise error_class.new(body, code, headers)
|
56
48
|
end
|
57
49
|
|
58
50
|
private
|
59
51
|
|
52
|
+
def build_request_options
|
53
|
+
options = { headers: @headers }
|
54
|
+
|
55
|
+
if get_request_with_query?
|
56
|
+
options[:query] = @body
|
57
|
+
elsif !@body.empty?
|
58
|
+
options[:body] = @body.to_json
|
59
|
+
end
|
60
|
+
|
61
|
+
options
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_request_with_query?
|
65
|
+
@verb.downcase == "get" && !@body.empty?
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_response(resp)
|
69
|
+
resp.transform_keys!(&:to_sym) unless resp.body.empty?
|
70
|
+
handle_error!(resp) if error_response?(resp)
|
71
|
+
resp
|
72
|
+
end
|
73
|
+
|
74
|
+
def error_response?(resp)
|
75
|
+
resp[:statusCode] && (resp[:statusCode] != 200 && resp[:statusCode] != 201)
|
76
|
+
end
|
77
|
+
|
60
78
|
def set_idempotency_key
|
61
79
|
# Only set idempotency key if the verb is POST for now.
|
62
80
|
#
|
@@ -66,6 +84,14 @@ module Resend
|
|
66
84
|
end
|
67
85
|
end
|
68
86
|
|
87
|
+
def set_batch_validation
|
88
|
+
# Set x-batch-validation header for batch emails
|
89
|
+
# Supported values: 'strict' (default) or 'permissive'
|
90
|
+
if @path == "emails/batch" && @options[:batch_validation]
|
91
|
+
@headers["x-batch-validation"] = @options[:batch_validation]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
69
95
|
def check_json!(resp)
|
70
96
|
if resp.body.is_a?(Hash)
|
71
97
|
JSON.parse(resp.body.to_json)
|
data/lib/resend/version.rb
CHANGED
data/lib/resend.rb
CHANGED
@@ -6,9 +6,11 @@ require "resend/version"
|
|
6
6
|
# Utils
|
7
7
|
require "httparty"
|
8
8
|
require "json"
|
9
|
+
require "cgi"
|
9
10
|
require "resend/errors"
|
10
11
|
require "resend/client"
|
11
12
|
require "resend/request"
|
13
|
+
require "resend/pagination_helper"
|
12
14
|
|
13
15
|
# API Operations
|
14
16
|
require "resend/audiences"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resend
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derich Pacheco
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/resend/emails.rb
|
57
57
|
- lib/resend/errors.rb
|
58
58
|
- lib/resend/mailer.rb
|
59
|
+
- lib/resend/pagination_helper.rb
|
59
60
|
- lib/resend/railtie.rb
|
60
61
|
- lib/resend/request.rb
|
61
62
|
- lib/resend/version.rb
|