resend 1.4.0 → 1.5.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: 5bac6323a992135612079621a1d9933831c44e6202838cff57377397c2214f2c
4
- data.tar.gz: c584c139bad503543f7cad52639f9dc6fe86812da1550b38729cd96638b9988b
3
+ metadata.gz: a131df322ae7ac448440742ce3cda1487b763a094f1940710236cc1e4f76534d
4
+ data.tar.gz: bb824f2076b51bf7eb82dd9a75ba4bd86c2d15162e8a499ef6b6e66555c37b2b
5
5
  SHA512:
6
- metadata.gz: 4411842066220cc450056f799a94d6cba513a6bf5c0c67c8f467e68e1cc8320911c3f47f983ec32430dc09961d4e005fb1f1ce429997b2ae5be5045eb7b85de6
7
- data.tar.gz: '0586cc9b333744f728d34847cc7de9bb18109b5658e870354f3b6a69b1988d4366e0bf4f37e3564a6a63f16de9e33f1070b3f15410be0dc88a996e8d3eff9336'
6
+ metadata.gz: 01efcf455f3b05c468ee1aede05bfeae5d7040b3dae1b2faf6bc38571b422b273a097f1b7eeb41cdc6fcc55f0c2b2f81eb4718814897a982c61915b52aae7a15
7
+ data.tar.gz: 4259936777bb6815e5abca38babf4b399efe5e2a0dfa55d87e2b1536b9f85b344683e0e9ee84ef21978630ad4e41dd6bc709d59bb5def667b412a37781af86df
@@ -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,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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Resend
4
- VERSION = "1.4.0"
4
+ VERSION = "1.5.0"
5
5
  end
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,6 +20,7 @@ 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"
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.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derich Pacheco
@@ -53,6 +53,7 @@ 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
@@ -64,6 +65,7 @@ files:
64
65
  - lib/resend/events.rb
65
66
  - lib/resend/logs.rb
66
67
  - lib/resend/mailer.rb
68
+ - lib/resend/multipart_request.rb
67
69
  - lib/resend/pagination_helper.rb
68
70
  - lib/resend/railtie.rb
69
71
  - lib/resend/request.rb