resend 1.4.0 → 1.6.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 +7 -0
- data/lib/resend/contacts/imports.rb +64 -0
- data/lib/resend/domains/claims.rb +56 -0
- data/lib/resend/multipart_request.rb +67 -0
- data/lib/resend/oauth_grants.rb +20 -0
- data/lib/resend/version.rb +1 -1
- data/lib/resend.rb +4 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23a4b97089acf88e3b1a768ebb7b9db1c5733ea1b225ca94cdeaae55abe4d0bc
|
|
4
|
+
data.tar.gz: 24e7052354d80707fe5d1d4aafeac46b85926deec93089f853c11c22965501a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: df538ce18a8a477d1527a270088691c98c5c2f1f6a793502f6abafd605f04e47a9c23b7366a7ef6c694c90ec4804ac2a909d8288d80c61062af7dc179c7d8d33
|
|
7
|
+
data.tar.gz: 73cbbda1da1cf01ebdb93c5f542481a6e33bebd2ae9d7a2ddbe696fac3d6ff550f2f270e3ce805fb72546a0af72557826dcf9c74509205c14935dfa49f8cb817
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.6.0] - 2026-07-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **OAuth grants** - New `Resend::OAuthGrants` module to list (`GET /oauth/grants`) and revoke (`DELETE /oauth/grants/:id`) OAuth grants. `list` supports the standard `limit`/`after`/`before` pagination params. ([#197](https://github.com/resend/resend-ruby/pull/197))
|
|
13
|
+
- **Domain claims** - New `Resend::Domains::Claims` module to claim a domain verified by another account (`create`), retrieve the latest claim (`get`), and trigger verification/ownership transfer (`verify`). ([#194](https://github.com/resend/resend-ruby/pull/194))
|
|
14
|
+
|
|
8
15
|
## [1.0.0] - 2025-10-31
|
|
9
16
|
|
|
10
17
|
### Overview
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resend
|
|
4
|
+
module Contacts
|
|
5
|
+
# Contact Imports API wrapper
|
|
6
|
+
module Imports
|
|
7
|
+
class << self
|
|
8
|
+
#
|
|
9
|
+
# Create a new contact import from a CSV file.
|
|
10
|
+
#
|
|
11
|
+
# @param params [Hash] the parameters
|
|
12
|
+
# @option params [String, IO] :file CSV file content (required). Maximum size is 50MB.
|
|
13
|
+
# @option params [Hash, String] :column_map optional mapping of contact fields to CSV columns.
|
|
14
|
+
# Accepts a Hash (will be JSON-encoded) or a pre-encoded JSON String.
|
|
15
|
+
# @option params [String] :on_conflict optional conflict strategy: 'upsert' or 'skip' (default 'skip').
|
|
16
|
+
# @option params [Array, String] :segments optional list of segment IDs to add contacts to.
|
|
17
|
+
# Accepts an Array of segment ID strings (will be JSON-encoded as [{"id":"..."}])
|
|
18
|
+
# or a pre-encoded JSON String.
|
|
19
|
+
# @option params [Array, String] :topics optional list of topic subscription objects.
|
|
20
|
+
# Each element must be a Hash with `id` (String) and `subscription` ('opt_in' or 'opt_out').
|
|
21
|
+
# Accepts an Array of Hashes (will be JSON-encoded) or a pre-encoded JSON String.
|
|
22
|
+
#
|
|
23
|
+
# https://resend.com/docs/api-reference/contacts/create-contact-import
|
|
24
|
+
def create(params)
|
|
25
|
+
raise ArgumentError, "Missing required `file` field" if params[:file].nil?
|
|
26
|
+
|
|
27
|
+
# Normalize segments: convert array of IDs to [{id: ...}] format
|
|
28
|
+
if params[:segments].is_a?(Array)
|
|
29
|
+
params = params.merge(segments: params[:segments].map { |s| s.is_a?(String) ? { id: s } : s })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Resend::MultipartRequest.new("contacts/imports", params, "post").perform
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
# Retrieve a single contact import by ID.
|
|
37
|
+
#
|
|
38
|
+
# @param id [String] the contact import ID (required)
|
|
39
|
+
#
|
|
40
|
+
# https://resend.com/docs/api-reference/contacts/get-contact-import
|
|
41
|
+
def get(id)
|
|
42
|
+
raise ArgumentError, "Missing required `id` field" if id.nil? || id.empty?
|
|
43
|
+
|
|
44
|
+
Resend::Request.new("contacts/imports/#{id}", {}, "get").perform
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# Retrieve a list of contact imports.
|
|
49
|
+
#
|
|
50
|
+
# @param params [Hash] optional filtering and pagination parameters
|
|
51
|
+
# @option params [String] :status filter by status: 'queued', 'in_progress', 'completed', or 'failed'
|
|
52
|
+
# @option params [Integer] :limit number of results to return
|
|
53
|
+
# @option params [String] :after cursor for forward pagination
|
|
54
|
+
# @option params [String] :before cursor for backward pagination
|
|
55
|
+
#
|
|
56
|
+
# https://resend.com/docs/api-reference/contacts/list-contact-imports
|
|
57
|
+
def list(params = {})
|
|
58
|
+
path = Resend::PaginationHelper.build_paginated_path("contacts/imports", params)
|
|
59
|
+
Resend::Request.new(path, {}, "get").perform
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resend
|
|
4
|
+
module Domains
|
|
5
|
+
# Domain Claims API wrapper
|
|
6
|
+
module Claims
|
|
7
|
+
class << self
|
|
8
|
+
#
|
|
9
|
+
# Start a claim for a domain that another Resend account has already verified.
|
|
10
|
+
# The domain is recreated under your account with fresh DKIM keys, so the
|
|
11
|
+
# previous account's DNS records cannot be reused. Returns a TXT record to add
|
|
12
|
+
# to your DNS to prove ownership. Uses the same request body as creating a domain.
|
|
13
|
+
#
|
|
14
|
+
# @param params [Hash] the parameters
|
|
15
|
+
# @option params [String] :name the name of the domain you want to claim (required)
|
|
16
|
+
# @option params [String] :region the region where emails will be sent from.
|
|
17
|
+
# Possible values: 'us-east-1' | 'eu-west-1' | 'sa-east-1' | 'ap-northeast-1'
|
|
18
|
+
# @option params [String] :custom_return_path subdomain for the Return-Path address (default 'send')
|
|
19
|
+
# @option params [String] :tracking_subdomain the custom subdomain used for click and open tracking links
|
|
20
|
+
# @option params [Boolean] :click_tracking track clicks within the body of each HTML email
|
|
21
|
+
# @option params [Boolean] :open_tracking track the open rate of each email
|
|
22
|
+
#
|
|
23
|
+
# https://resend.com/docs/api-reference/domains/claim-domain
|
|
24
|
+
def create(params)
|
|
25
|
+
path = "domains/claim"
|
|
26
|
+
Resend::Request.new(path, params, "post").perform
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Retrieve the latest claim for the placeholder domain created by the claim.
|
|
31
|
+
#
|
|
32
|
+
# @param domain_id [String] the ID of the placeholder domain created by the claim
|
|
33
|
+
#
|
|
34
|
+
# https://resend.com/docs/api-reference/domains/get-domain-claim
|
|
35
|
+
def get(domain_id = "")
|
|
36
|
+
path = "domains/#{domain_id}/claim"
|
|
37
|
+
Resend::Request.new(path, {}, "get").perform
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# Trigger asynchronous DNS verification and ownership transfer for a domain claim.
|
|
42
|
+
# The claim stays 'pending' while verification runs; poll `get` for status. Once
|
|
43
|
+
# 'completed', the transferred domain has new DKIM records that must be added to
|
|
44
|
+
# DNS and verified via Resend::Domains.verify.
|
|
45
|
+
#
|
|
46
|
+
# @param domain_id [String] the ID of the placeholder domain created by the claim
|
|
47
|
+
#
|
|
48
|
+
# https://resend.com/docs/api-reference/domains/verify-domain-claim
|
|
49
|
+
def verify(domain_id = "")
|
|
50
|
+
path = "domains/#{domain_id}/claim/verify"
|
|
51
|
+
Resend::Request.new(path, {}, "post").perform
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module Resend
|
|
6
|
+
# Handles multipart/form-data requests (file uploads).
|
|
7
|
+
# Builds the multipart body directly instead of relying on HTTParty's
|
|
8
|
+
# duck-typing file detection, which requires a :path method.
|
|
9
|
+
class MultipartRequest < Request
|
|
10
|
+
NEWLINE = "\r\n"
|
|
11
|
+
private_constant :NEWLINE
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def build_request_options
|
|
16
|
+
boundary = SecureRandom.hex(16)
|
|
17
|
+
headers = @headers.merge("Content-Type" => "multipart/form-data; boundary=#{boundary}")
|
|
18
|
+
{ headers: headers, body: build_multipart_body(boundary) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def build_multipart_body(boundary)
|
|
22
|
+
body = "".b
|
|
23
|
+
|
|
24
|
+
body << part(boundary, "file", read_file(@body[:file]), filename: "import.csv", content_type: "text/csv")
|
|
25
|
+
optional_fields.each { |name, value| body << part(boundary, name, value) }
|
|
26
|
+
body << "--#{boundary}--#{NEWLINE}".b
|
|
27
|
+
|
|
28
|
+
body
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def part(boundary, name, value, filename: nil, content_type: nil)
|
|
32
|
+
disposition = %(Content-Disposition: form-data; name="#{name}")
|
|
33
|
+
disposition += %(; filename="#{filename}") if filename
|
|
34
|
+
|
|
35
|
+
header = "--#{boundary}#{NEWLINE}#{disposition}#{NEWLINE}"
|
|
36
|
+
header += "Content-Type: #{content_type}#{NEWLINE}" if content_type
|
|
37
|
+
header += NEWLINE
|
|
38
|
+
|
|
39
|
+
header.b + value.to_s.b + NEWLINE.b
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def read_file(file_data)
|
|
43
|
+
if file_data.respond_to?(:read)
|
|
44
|
+
content = file_data.read
|
|
45
|
+
file_data.rewind if file_data.respond_to?(:rewind)
|
|
46
|
+
content.b
|
|
47
|
+
else
|
|
48
|
+
file_data.to_s.b
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def optional_fields
|
|
53
|
+
{
|
|
54
|
+
column_map: @body[:column_map] && serialize_json(@body[:column_map]),
|
|
55
|
+
on_conflict: @body[:on_conflict],
|
|
56
|
+
segments: @body[:segments] && serialize_json(@body[:segments]),
|
|
57
|
+
topics: @body[:topics] && serialize_json(@body[:topics])
|
|
58
|
+
}.compact
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def serialize_json(value)
|
|
62
|
+
return value if value.is_a?(String)
|
|
63
|
+
|
|
64
|
+
value.to_json
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Resend
|
|
4
|
+
# oauth grants api wrapper
|
|
5
|
+
module OAuthGrants
|
|
6
|
+
class << self
|
|
7
|
+
# https://resend.com/docs/api-reference/oauth/list-grants
|
|
8
|
+
def list(params = {})
|
|
9
|
+
path = Resend::PaginationHelper.build_paginated_path("oauth/grants", params)
|
|
10
|
+
Resend::Request.new(path, {}, "get").perform
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# https://resend.com/docs/api-reference/oauth/revoke-grant
|
|
14
|
+
def revoke(oauth_grant_id = "")
|
|
15
|
+
path = "oauth/grants/#{oauth_grant_id}"
|
|
16
|
+
Resend::Request.new(path, {}, "delete").perform
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/resend/version.rb
CHANGED
data/lib/resend.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "resend/errors"
|
|
|
11
11
|
require "resend/response"
|
|
12
12
|
require "resend/client"
|
|
13
13
|
require "resend/request"
|
|
14
|
+
require "resend/multipart_request"
|
|
14
15
|
require "resend/pagination_helper"
|
|
15
16
|
|
|
16
17
|
# API Operations
|
|
@@ -19,10 +20,12 @@ require "resend/api_keys"
|
|
|
19
20
|
require "resend/broadcasts"
|
|
20
21
|
require "resend/batch"
|
|
21
22
|
require "resend/contacts"
|
|
23
|
+
require "resend/contacts/imports"
|
|
22
24
|
require "resend/contacts/segments"
|
|
23
25
|
require "resend/contacts/topics"
|
|
24
26
|
require "resend/contact_properties"
|
|
25
27
|
require "resend/domains"
|
|
28
|
+
require "resend/domains/claims"
|
|
26
29
|
require "resend/emails"
|
|
27
30
|
require "resend/templates"
|
|
28
31
|
require "resend/emails/receiving"
|
|
@@ -31,6 +34,7 @@ require "resend/emails/receiving/attachments"
|
|
|
31
34
|
require "resend/logs"
|
|
32
35
|
require "resend/topics"
|
|
33
36
|
require "resend/webhooks"
|
|
37
|
+
require "resend/oauth_grants"
|
|
34
38
|
require "resend/automations"
|
|
35
39
|
require "resend/automations/runs"
|
|
36
40
|
require "resend/events"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: resend
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Derich Pacheco
|
|
@@ -53,9 +53,11 @@ files:
|
|
|
53
53
|
- lib/resend/client.rb
|
|
54
54
|
- lib/resend/contact_properties.rb
|
|
55
55
|
- lib/resend/contacts.rb
|
|
56
|
+
- lib/resend/contacts/imports.rb
|
|
56
57
|
- lib/resend/contacts/segments.rb
|
|
57
58
|
- lib/resend/contacts/topics.rb
|
|
58
59
|
- lib/resend/domains.rb
|
|
60
|
+
- lib/resend/domains/claims.rb
|
|
59
61
|
- lib/resend/emails.rb
|
|
60
62
|
- lib/resend/emails/attachments.rb
|
|
61
63
|
- lib/resend/emails/receiving.rb
|
|
@@ -64,6 +66,8 @@ files:
|
|
|
64
66
|
- lib/resend/events.rb
|
|
65
67
|
- lib/resend/logs.rb
|
|
66
68
|
- lib/resend/mailer.rb
|
|
69
|
+
- lib/resend/multipart_request.rb
|
|
70
|
+
- lib/resend/oauth_grants.rb
|
|
67
71
|
- lib/resend/pagination_helper.rb
|
|
68
72
|
- lib/resend/railtie.rb
|
|
69
73
|
- lib/resend/request.rb
|