Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable SDK to reattach large files to messages on retry #487

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

### Unreleased
* Enable SDK to reattach large files to messages on retry

### 6.1.1 / 2024-08-20
* Fixed sending attachments less than 3MB

Expand Down
20 changes: 15 additions & 5 deletions lib/nylas/utils/file_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,29 @@ module FileUtils
# @return The form data to send to the API and the opened files.
# @!visibility private
def self.build_form_request(request_body)
attachments = request_body.delete(:attachments) || request_body.delete("attachments") || []
attachments = request_body[:attachments] || request_body["attachments"] || []
serializable_body = request_body.reject { |key, _| [:attachments, "attachments"].include?(key) }
request_body_copy = Marshal.load(Marshal.dump(serializable_body))

# RestClient will not send a multipart request if there are no attachments
# so we need to return the message payload to be used as a json payload
return [request_body, []] if attachments.empty?
return [request_body_copy, []] if attachments.empty?

# Prepare the data to return
message_payload = request_body.to_json
message_payload = request_body_copy.to_json

form_data = {}
opened_files = []

attachments.each_with_index do |attachment, index|
file = attachment[:content] || attachment["content"]
if file.respond_to?(:closed?) && file.closed?
unless attachment[:file_path]
raise ArgumentError, "The file at index #{index} is closed and no file_path was provided."
end

file = File.open(attachment[:file_path], "rb")
end

form_data.merge!({ "file#{index}" => file })
opened_files << file
end
Expand Down Expand Up @@ -98,7 +107,8 @@ def self.attach_file_request_builder(file_path)
filename: filename,
content_type: content_type,
size: size,
content: content
content: content,
file_path: file_path
}
end
end
Expand Down
43 changes: 41 additions & 2 deletions spec/nylas/utils/file_utils_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
filename: "file.txt",
content_type: "text/plain",
size: 100,
content: mock_file
content: mock_file,
file_path: file_path
)
end

Expand All @@ -37,7 +38,8 @@
filename: "file.txt",
content_type: "application/octet-stream",
size: file_size,
content: mock_file
content: mock_file,
file_path: file_path
)
end
end
Expand Down Expand Up @@ -77,6 +79,43 @@

expect(form_request).to eq([request_body, []])
end

it "raises an error if the file is closed and no file_path is provided" do
attachments = [{ content: mock_file }]
request_body = { attachments: attachments }

allow(mock_file).to receive(:closed?).and_return(true)

expect do
described_class.build_form_request(request_body)
end.to raise_error(ArgumentError, "The file at index 0 is closed and no file_path was provided.")
end

it "opens the file if it is closed and file_path is provided" do
file_path = "/path/to/file.txt"
attachments = [{ content: mock_file, file_path: file_path }]
request_body = { attachments: attachments }

allow(mock_file).to receive(:closed?).and_return(true)
allow(File).to receive(:open).with(file_path, "rb").and_return(mock_file)

form_data, opened_files = described_class.build_form_request(request_body)

expect(form_data).to include("file0" => mock_file)
expect(opened_files).to include(mock_file)
end

it "adds the file to form_data if it is open" do
attachments = [{ content: mock_file }]
request_body = { attachments: attachments }

allow(mock_file).to receive(:closed?).and_return(false)

form_data, opened_files = described_class.build_form_request(request_body)

expect(form_data).to include("file0" => mock_file)
expect(opened_files).to include(mock_file)
end
end

describe "#build_json_request" do
Expand Down
Loading