resend 1.0.0 → 1.0.1
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/resend/broadcasts.rb +3 -0
- data/lib/resend/mailer.rb +1 -1
- data/lib/resend/request.rb +23 -10
- data/lib/resend/response.rb +141 -0
- data/lib/resend/version.rb +1 -1
- data/lib/resend.rb +1 -0
- metadata +8 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 67dadb5f7acb77801501030612149bf52b6c46626fd5630a233a77ab6fc552d7
|
|
4
|
+
data.tar.gz: 8842020484f59a566282b8ff1258bfeea02594274b2f117c548ae2ad49284ad2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d889989325170107795bed29b590b45382909a8b4812381c4fa330e8c94c6cf7d8c3be1474793c0b6a93c85fc3f0647fd868f5b540902cd9e6ca260821f3b5ec
|
|
7
|
+
data.tar.gz: 48ec677afd58504f02b4099fb1f4d926defa2aa64f05cb2d8e8e1c724b08f1d1b3950f79535ac89481a8f58f1b1884962c4e46247bbdb6b03e0364325822fab9
|
data/lib/resend/broadcasts.rb
CHANGED
|
@@ -7,6 +7,9 @@ module Resend
|
|
|
7
7
|
# https://resend.com/docs/api-reference/broadcasts/create-broadcast
|
|
8
8
|
# @note Supports both segment_id and audience_id. At least one is required.
|
|
9
9
|
# audience_id is deprecated - use segment_id instead.
|
|
10
|
+
# @note When send: true is passed, the broadcast is sent immediately instead of
|
|
11
|
+
# creating a draft. When using send: true, you can also include scheduled_at
|
|
12
|
+
# to schedule the broadcast. Passing scheduled_at without send: true is an error.
|
|
10
13
|
def create(params = {})
|
|
11
14
|
if params[:audience_id] && !params[:segment_id]
|
|
12
15
|
warn "[DEPRECATION] Using audience_id in broadcasts is deprecated. Use segment_id instead."
|
data/lib/resend/mailer.rb
CHANGED
data/lib/resend/request.rb
CHANGED
|
@@ -34,13 +34,16 @@ module Resend
|
|
|
34
34
|
resp = HTTParty.send(@verb.to_sym, "#{BASE_URL}#{@path}", options)
|
|
35
35
|
|
|
36
36
|
check_json!(resp)
|
|
37
|
-
process_response(resp)
|
|
37
|
+
data = process_response(resp)
|
|
38
|
+
headers = extract_headers(resp)
|
|
39
|
+
|
|
40
|
+
Resend::Response.new(data, headers)
|
|
38
41
|
end
|
|
39
42
|
|
|
40
|
-
def handle_error!(resp)
|
|
41
|
-
code =
|
|
42
|
-
body =
|
|
43
|
-
headers = resp.respond_to?(:headers) ? resp.headers : (
|
|
43
|
+
def handle_error!(data, resp = nil)
|
|
44
|
+
code = data[:statusCode]
|
|
45
|
+
body = data[:message]
|
|
46
|
+
headers = resp.respond_to?(:headers) ? resp.headers : (data[:headers] || {})
|
|
44
47
|
|
|
45
48
|
# get error from the known list of errors
|
|
46
49
|
error_class = Resend::Error::ERRORS[code] || Resend::Error
|
|
@@ -66,20 +69,23 @@ module Resend
|
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
def process_response(resp)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
# Extract the parsed data from HTTParty response or use the hash directly (for tests/mocks)
|
|
73
|
+
data = resp.respond_to?(:parsed_response) ? resp.parsed_response : resp
|
|
74
|
+
data ||= {}
|
|
75
|
+
data.transform_keys!(&:to_sym) unless resp.body.empty?
|
|
76
|
+
handle_error!(data, resp) if error_response?(data)
|
|
77
|
+
data
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
def error_response?(resp)
|
|
75
|
-
resp[:statusCode] &&
|
|
81
|
+
resp[:statusCode] && resp[:statusCode] != 200 && resp[:statusCode] != 201
|
|
76
82
|
end
|
|
77
83
|
|
|
78
84
|
def set_idempotency_key
|
|
79
85
|
# Only set idempotency key if the verb is POST for now.
|
|
80
86
|
#
|
|
81
87
|
# Does not set it if the idempotency_key is nil or empty
|
|
82
|
-
if @verb.downcase == "post" &&
|
|
88
|
+
if @verb.downcase == "post" && !@options[:idempotency_key].nil? && !@options[:idempotency_key].empty?
|
|
83
89
|
@headers["Idempotency-Key"] = @options[:idempotency_key]
|
|
84
90
|
end
|
|
85
91
|
end
|
|
@@ -101,5 +107,12 @@ module Resend
|
|
|
101
107
|
rescue JSON::ParserError, TypeError
|
|
102
108
|
raise Resend::Error::InternalServerError.new("Resend API returned an unexpected response", nil)
|
|
103
109
|
end
|
|
110
|
+
|
|
111
|
+
# Extract and normalize headers from the HTTParty response
|
|
112
|
+
def extract_headers(resp)
|
|
113
|
+
return {} unless resp.respond_to?(:headers)
|
|
114
|
+
|
|
115
|
+
resp.headers.to_h.transform_keys { |k| k.to_s.downcase }
|
|
116
|
+
end
|
|
104
117
|
end
|
|
105
118
|
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resend
|
|
4
|
+
# Response wrapper that maintains backwards compatibility while exposing headers
|
|
5
|
+
#
|
|
6
|
+
# This class wraps API responses and behaves like a Hash for backwards compatibility,
|
|
7
|
+
# while also providing access to response headers via the #headers method.
|
|
8
|
+
#
|
|
9
|
+
# @example Backwards compatible hash access
|
|
10
|
+
# response = Resend::Emails.send(params)
|
|
11
|
+
# response[:id] # => "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
|
|
12
|
+
#
|
|
13
|
+
# @example Accessing response headers
|
|
14
|
+
# response = Resend::Emails.send(params)
|
|
15
|
+
# response.headers # => {"content-type" => "application/json", ...}
|
|
16
|
+
# response.headers['x-ratelimit-remaining'] # => "50"
|
|
17
|
+
class Response
|
|
18
|
+
include Enumerable
|
|
19
|
+
|
|
20
|
+
# @param data [Hash] The response data
|
|
21
|
+
# @param headers [Hash, HTTParty::Response] The response headers
|
|
22
|
+
def initialize(data, headers)
|
|
23
|
+
@data = data.is_a?(Hash) ? data : {}
|
|
24
|
+
@headers = normalize_headers(headers)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Access response headers
|
|
28
|
+
# @return [Hash] Response headers as a hash with lowercase string keys
|
|
29
|
+
attr_reader :headers
|
|
30
|
+
|
|
31
|
+
# Hash-like access via []
|
|
32
|
+
# @param key [Symbol, String] The key to access
|
|
33
|
+
# @return [Object] The value at the key
|
|
34
|
+
def [](key)
|
|
35
|
+
@data[key]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Hash-like assignment via []=
|
|
39
|
+
# @param key [Symbol, String] The key to set
|
|
40
|
+
# @param value [Object] The value to set
|
|
41
|
+
def []=(key, value)
|
|
42
|
+
@data[key] = value
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Dig into nested hash structure
|
|
46
|
+
# @param keys [Array<Symbol, String>] Keys to dig through
|
|
47
|
+
# @return [Object] The value at the nested key path
|
|
48
|
+
def dig(*keys)
|
|
49
|
+
@data.dig(*keys)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Convert to plain hash
|
|
53
|
+
# @return [Hash] The underlying data hash
|
|
54
|
+
def to_h
|
|
55
|
+
@data
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
alias to_hash to_h
|
|
59
|
+
|
|
60
|
+
# Get all keys from the data
|
|
61
|
+
# @return [Array] Array of keys
|
|
62
|
+
def keys
|
|
63
|
+
@data.keys
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get all values from the data
|
|
67
|
+
# @return [Array] Array of values
|
|
68
|
+
def values
|
|
69
|
+
@data.values
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Check if key exists
|
|
73
|
+
# @param key [Symbol, String] The key to check
|
|
74
|
+
# @return [Boolean] True if key exists
|
|
75
|
+
def key?(key)
|
|
76
|
+
@data.key?(key)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
alias has_key? key?
|
|
80
|
+
|
|
81
|
+
# Enable enumeration over the data
|
|
82
|
+
def each(&block)
|
|
83
|
+
@data.each(&block)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Transform keys in the underlying data
|
|
87
|
+
# @return [Resend::Response] Self for chaining
|
|
88
|
+
def transform_keys!(&block)
|
|
89
|
+
@data.transform_keys!(&block)
|
|
90
|
+
self
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Check if response is empty
|
|
94
|
+
# @return [Boolean] True if data is empty
|
|
95
|
+
def empty?
|
|
96
|
+
@data.empty?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Respond to hash-like methods
|
|
100
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
101
|
+
@data.respond_to?(method_name) || super
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Delegate unknown methods to the underlying data hash
|
|
105
|
+
def method_missing(method_name, *args, &block)
|
|
106
|
+
if @data.respond_to?(method_name)
|
|
107
|
+
result = @data.send(method_name, *args, &block)
|
|
108
|
+
# If the method returns the hash itself, return self to maintain wrapper
|
|
109
|
+
result.equal?(@data) ? self : result
|
|
110
|
+
else
|
|
111
|
+
super
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# String representation for debugging
|
|
116
|
+
# @return [String] String representation of the response
|
|
117
|
+
def inspect
|
|
118
|
+
"#<Resend::Response data=#{@data.inspect} headers=#{@headers.keys.inspect}>"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
# Normalize headers to a simple hash with lowercase string keys
|
|
124
|
+
# @param headers [Hash, HTTParty::Response, nil] The headers to normalize
|
|
125
|
+
# @return [Hash] Normalized headers hash
|
|
126
|
+
def normalize_headers(headers)
|
|
127
|
+
return {} if headers.nil?
|
|
128
|
+
|
|
129
|
+
# Handle HTTParty::Response object
|
|
130
|
+
headers = headers.headers if headers.respond_to?(:headers)
|
|
131
|
+
|
|
132
|
+
# Convert to hash and normalize keys to lowercase strings
|
|
133
|
+
case headers
|
|
134
|
+
when Hash
|
|
135
|
+
headers.to_h.transform_keys { |k| k.to_s.downcase }
|
|
136
|
+
else
|
|
137
|
+
{}
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
data/lib/resend/version.rb
CHANGED
data/lib/resend.rb
CHANGED
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: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Derich Pacheco
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-02-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: httparty
|
|
@@ -38,7 +38,7 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
-
description:
|
|
41
|
+
description:
|
|
42
42
|
email: carlosderich@gmail.com
|
|
43
43
|
executables: []
|
|
44
44
|
extensions: []
|
|
@@ -65,6 +65,7 @@ files:
|
|
|
65
65
|
- lib/resend/pagination_helper.rb
|
|
66
66
|
- lib/resend/railtie.rb
|
|
67
67
|
- lib/resend/request.rb
|
|
68
|
+
- lib/resend/response.rb
|
|
68
69
|
- lib/resend/segments.rb
|
|
69
70
|
- lib/resend/templates.rb
|
|
70
71
|
- lib/resend/topics.rb
|
|
@@ -74,7 +75,7 @@ homepage: https://github.com/resend/resend-ruby
|
|
|
74
75
|
licenses:
|
|
75
76
|
- MIT
|
|
76
77
|
metadata: {}
|
|
77
|
-
post_install_message:
|
|
78
|
+
post_install_message:
|
|
78
79
|
rdoc_options: []
|
|
79
80
|
require_paths:
|
|
80
81
|
- lib
|
|
@@ -89,8 +90,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
89
90
|
- !ruby/object:Gem::Version
|
|
90
91
|
version: '0'
|
|
91
92
|
requirements: []
|
|
92
|
-
rubygems_version: 3.
|
|
93
|
-
signing_key:
|
|
93
|
+
rubygems_version: 3.0.3.1
|
|
94
|
+
signing_key:
|
|
94
95
|
specification_version: 4
|
|
95
96
|
summary: The Ruby and Rails SDK for resend.com
|
|
96
97
|
test_files: []
|