Skip to content

Commit 5779315

Browse files
authored
chore: Replace rest-client with httparty for improved maintainability and security (#524)
* Replace rest-client with httparty for improved maintainability and security - Replace unmaintained rest-client dependency with actively maintained httparty - Maintain full backwards compatibility - no customer code changes required - Remove rest-client cookie jar threading workaround - Rewrite HTTP client implementation using httparty - Update error handling to map Net::HTTP exceptions to existing Nylas exceptions - Preserve JSON response parsing with symbol keys - Maintain multipart file upload functionality - Add comprehensive integration tests for migration validation - Update dependencies in gemspec and remove rest-client requires - All 227 tests passing with 99.42% code coverage maintained Resolves security vulnerabilities in unmaintained rest-client dependency while preserving complete API compatibility for customers. * Fix RuboCop style violations in integration tests - Fix RSpec/DescribeClass: Use class name instead of string - Fix RSpec/MessageSpies: Use have_received with spies instead of expect().to receive() - Fix Layout/TrailingWhitespace: Remove trailing whitespace * Add comprehensive file upload example for HTTParty migration testing - Create file_upload_example.rb demonstrating both <3MB and >3MB file handling - Test small files: JSON with base64 encoding - Test large files: Multipart form data uploads - Add NYLAS_TEST_EMAIL environment variable for sending test emails - Update examples README with new file upload example documentation - Successfully tested with real API calls - both file sizes work correctly - Validates HTTParty migration handles all file upload scenarios properly * Improve message examples: add focused send example and use disk file for large uploads - Replace vague test_example.rb with comprehensive send_message_example.rb - Demonstrate simple messages, multi-recipient, and HTML content - Update file_upload_example.rb to use persistent disk file for >3MB test - Large test file is created once and reused, avoiding repeated 5MB file creation - Ensure NYLAS_TEST_EMAIL environment variable is used consistently - Update examples README with new send_message_example documentation - Both examples successfully tested with real API calls * Add large test file to .gitignore and remove from tracking - Large test file should be generated by the example, not tracked in git - Added examples/messages/large_test_file.txt to .gitignore - Removed the file from git tracking but left it on disk for reuse * Fix multipart email sending bug with large attachments (#525) - Fixed key mismatch between FileUtils.handle_message_payload (symbol keys) and HttpClient.build_request (string key checks) - HttpClient.build_request now checks for both string and symbol multipart keys - Maintains 100% backwards compatibility with existing code - Prevents encoding errors when sending emails with attachments >3MB - Added comprehensive tests for both string and symbol key scenarios - Added integration test verifying FileUtils + HttpClient compatibility * Fixed lint errors
1 parent a6b5779 commit 5779315

File tree

14 files changed

+867
-41
lines changed

14 files changed

+867
-41
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
.env
22
**/*/.env
3+
# Example test files
4+
examples/messages/large_test_file.txt
35
*.gem
46
*.rbc
57
.bundle

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22

33
### Unreleased
4+
* Replaced `rest-client` dependency with `httparty` for improved maintainability and security
5+
- `rest-client` is no longer actively maintained and has known security vulnerabilities
6+
- `httparty` is actively maintained and provides better performance
7+
- All existing functionality remains backwards compatible - no customer code changes required
8+
- Removed workaround for `rest-client` cookie jar threading bug
49
* Added support for new `fields` query parameter values in Messages API:
510
- `include_tracking_options`: Returns messages and their tracking settings
611
- `raw_mime`: Returns the grant_id, object, id, and raw_mime fields only
@@ -11,6 +16,11 @@
1116
- `label`: String label describing the message tracking purpose
1217
* Added support for `raw_mime` field in message responses containing Base64url-encoded message data
1318
* Added `MessageFields` module with constants for all valid field values to improve developer experience
19+
* Fixed multipart email sending bug where large attachments would fail due to multipart flag key mismatch (#525)
20+
- `FileUtils.handle_message_payload` transforms keys to symbols (`:multipart`)
21+
- `HttpClient.build_request` was only checking for string keys (`"multipart"`)
22+
- Now checks for both string and symbol keys to maintain full backwards compatibility
23+
- Prevents encoding errors when sending emails with attachments larger than 3MB
1424

1525
### 6.4.0 / 2025-04-30
1626
* Added support for Notetaker APIs

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ If you have a question about the Nylas Communications Platform, [contact Nylas S
2626
### Prerequisites
2727

2828
- Ruby 3.0 or above.
29-
- Ruby Frameworks: `rest-client` and `yajl-ruby`.
29+
- Ruby Frameworks: `httparty` and `yajl-ruby`.
3030

3131
### Install
3232

examples/.env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ NYLAS_API_URI=https://api.us.nylas.com
1212

1313
# Grant ID - Required for message and event examples
1414
# You can get this from your Nylas Dashboard after connecting an account
15-
NYLAS_GRANT_ID=your_grant_id_here
15+
NYLAS_GRANT_ID=your_grant_id_here
16+
17+
# Send email - Optionl
18+
# Used for send examples
19+
NYLAS_TEST_EMAIL=[email protected]

examples/README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ examples/
1010
├── events/ # Event-related examples
1111
│ └── event_notetaker_example.rb # Example of creating events with Notetaker
1212
├── messages/ # Message-related examples
13-
│ └── message_fields_example.rb # Example of using new message fields functionality
13+
│ ├── message_fields_example.rb # Example of using new message fields functionality
14+
│ ├── file_upload_example.rb # Example of file upload functionality with HTTParty migration
15+
│ └── send_message_example.rb # Example of basic message sending functionality
1416
└── notetaker/ # Standalone Notetaker examples
1517
├── README.md # Notetaker-specific documentation
1618
└── notetaker_example.rb # Basic Notetaker functionality example
@@ -53,6 +55,31 @@ Before running any example, make sure to:
5355
export NYLAS_GRANT_ID="your_grant_id"
5456
```
5557

58+
- `messages/file_upload_example.rb`: Demonstrates file upload functionality with the HTTParty migration, including:
59+
- Sending messages with small attachments (<3MB) - handled as JSON with base64 encoding
60+
- Sending messages with large attachments (>3MB) - handled as multipart form data
61+
- Creating test files of appropriate sizes for demonstration
62+
- File handling logic and processing differences
63+
- Verification that HTTParty migration works for both upload methods
64+
65+
Additional environment variables needed:
66+
```bash
67+
export NYLAS_GRANT_ID="your_grant_id"
68+
export NYLAS_TEST_EMAIL="[email protected]" # Email address to send test messages to
69+
```
70+
71+
- `messages/send_message_example.rb`: Demonstrates basic message sending functionality, including:
72+
- Sending simple text messages
73+
- Handling multiple recipients (TO, CC, BCC)
74+
- Sending rich HTML content
75+
- Processing responses and error handling
76+
77+
Additional environment variables needed:
78+
```bash
79+
export NYLAS_GRANT_ID="your_grant_id"
80+
export NYLAS_TEST_EMAIL="[email protected]" # Email address to send test messages to
81+
```
82+
5683
### Notetaker
5784
- `notetaker/notetaker_example.rb`: Shows basic Notetaker functionality, including:
5885
- Inviting a Notetaker to a meeting
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
# Example demonstrating file upload functionality in the Nylas Ruby SDK
5+
# Tests both small (<3MB) and large (>3MB) file handling with the new HTTParty implementation
6+
#
7+
# This example shows how to:
8+
# 1. Send messages with small attachments (<3MB) - handled as JSON with base64 encoding
9+
# 2. Send messages with large attachments (>3MB) - handled as multipart form data
10+
# 3. Create test files of appropriate sizes for demonstration
11+
# 4. Handle file upload errors and responses
12+
#
13+
# Prerequisites:
14+
# - Ruby 3.0 or later
15+
# - A Nylas API key
16+
# - A grant ID (connected email account)
17+
# - A test email address to send to
18+
#
19+
# Environment variables needed:
20+
# export NYLAS_API_KEY="your_api_key"
21+
# export NYLAS_GRANT_ID="your_grant_id"
22+
# export NYLAS_TEST_EMAIL="[email protected]" # Email address to send test messages to
23+
# export NYLAS_API_URI="https://api.us.nylas.com" # Optional
24+
#
25+
# Alternatively, create a .env file in the examples directory with:
26+
# NYLAS_API_KEY=your_api_key
27+
# NYLAS_GRANT_ID=your_grant_id
28+
29+
# NYLAS_API_URI=https://api.us.nylas.com
30+
31+
$LOAD_PATH.unshift File.expand_path('../../lib', __dir__)
32+
require "nylas"
33+
require "json"
34+
require "tempfile"
35+
36+
# Simple .env file loader
37+
def load_env_file
38+
env_file = File.expand_path('../.env', __dir__)
39+
return unless File.exist?(env_file)
40+
41+
puts "Loading environment variables from .env file..."
42+
File.readlines(env_file).each do |line|
43+
line = line.strip
44+
next if line.empty? || line.start_with?('#')
45+
46+
key, value = line.split('=', 2)
47+
next unless key && value
48+
49+
# Remove quotes if present
50+
value = value.gsub(/\A['"]|['"]\z/, '')
51+
ENV[key] = value
52+
end
53+
end
54+
55+
def create_small_test_file
56+
puts "\n=== Creating Small Test File (<3MB) ==="
57+
58+
# Create a 1MB test file
59+
content = "A" * (1024 * 1024) # 1MB of 'A' characters
60+
61+
temp_file = Tempfile.new(['small_test', '.txt'])
62+
temp_file.write(content)
63+
temp_file.rewind
64+
65+
puts "- Created test file: #{temp_file.path}"
66+
puts "- File size: #{File.size(temp_file.path)} bytes (#{File.size(temp_file.path) / (1024.0 * 1024).round(2)} MB)"
67+
puts "- This will be sent as JSON with base64 encoding"
68+
69+
temp_file
70+
end
71+
72+
def find_or_create_large_test_file
73+
puts "\n=== Finding Large Test File (>3MB) ==="
74+
75+
# Look for an existing large file, or create one if needed
76+
large_file_path = File.expand_path("large_test_file.txt", __dir__)
77+
78+
unless File.exist?(large_file_path) && File.size(large_file_path) > 3 * 1024 * 1024
79+
puts "- Creating 5MB test file on disk..."
80+
content = "B" * (5 * 1024 * 1024) # 5MB of 'B' characters
81+
File.write(large_file_path, content)
82+
puts "- Created permanent test file: #{large_file_path}"
83+
else
84+
puts "- Found existing test file: #{large_file_path}"
85+
end
86+
87+
puts "- File size: #{File.size(large_file_path)} bytes (#{File.size(large_file_path) / (1024.0 * 1024).round(2)} MB)"
88+
puts "- This will be sent as multipart form data"
89+
90+
large_file_path
91+
end
92+
93+
def send_message_with_small_attachment(nylas, grant_id, recipient_email, test_file)
94+
puts "\n=== Sending Message with Small Attachment ==="
95+
96+
begin
97+
# Build the file attachment
98+
file_attachment = Nylas::FileUtils.attach_file_request_builder(test_file.path)
99+
100+
request_body = {
101+
subject: "Test Email with Small Attachment (<3MB) - HTTParty Migration Test",
102+
to: [{ email: recipient_email }],
103+
body: "This is a test email with a small attachment (<3MB) to verify the HTTParty migration works correctly.\n\nFile size: #{File.size(test_file.path)} bytes\nSent at: #{Time.now}",
104+
attachments: [file_attachment]
105+
}
106+
107+
puts "- Sending message with small attachment..."
108+
puts "- Recipient: #{recipient_email}"
109+
puts "- Attachment size: #{File.size(test_file.path)} bytes"
110+
puts "- Expected handling: JSON with base64 encoding"
111+
112+
response, request_id = nylas.messages.send(
113+
identifier: grant_id,
114+
request_body: request_body
115+
)
116+
117+
puts "✅ Message sent successfully!"
118+
puts "- Message ID: #{response[:id]}"
119+
puts "- Request ID: #{request_id}"
120+
puts "- Grant ID: #{response[:grant_id]}"
121+
122+
response
123+
rescue => e
124+
puts "❌ Failed to send message with small attachment: #{e.message}"
125+
puts "- Error class: #{e.class}"
126+
raise
127+
end
128+
end
129+
130+
def send_message_with_large_attachment(nylas, grant_id, recipient_email, test_file_path)
131+
puts "\n=== Sending Message with Large Attachment ==="
132+
133+
begin
134+
# Build the file attachment
135+
file_attachment = Nylas::FileUtils.attach_file_request_builder(test_file_path)
136+
137+
request_body = {
138+
subject: "Test Email with Large Attachment (>3MB) - HTTParty Migration Test",
139+
to: [{ email: recipient_email }],
140+
body: "This is a test email with a large attachment (>3MB) to verify the HTTParty migration works correctly.\n\nFile size: #{File.size(test_file_path)} bytes\nSent at: #{Time.now}",
141+
attachments: [file_attachment]
142+
}
143+
144+
puts "- Sending message with large attachment..."
145+
puts "- Recipient: #{recipient_email}"
146+
puts "- Attachment size: #{File.size(test_file_path)} bytes"
147+
puts "- Expected handling: Multipart form data"
148+
149+
response, request_id = nylas.messages.send(
150+
identifier: grant_id,
151+
request_body: request_body
152+
)
153+
154+
puts "✅ Message sent successfully!"
155+
puts "- Message ID: #{response[:id]}"
156+
puts "- Request ID: #{request_id}"
157+
puts "- Grant ID: #{response[:grant_id]}"
158+
159+
response
160+
rescue => e
161+
puts "❌ Failed to send message with large attachment: #{e.message}"
162+
puts "- Error class: #{e.class}"
163+
raise
164+
end
165+
end
166+
167+
def demonstrate_file_utils_handling
168+
puts "\n=== Demonstrating File Handling Logic ==="
169+
170+
# Create temporary files to test the file handling logic
171+
small_file = create_small_test_file
172+
large_file_path = find_or_create_large_test_file
173+
174+
begin
175+
# Test small file handling
176+
small_attachment = Nylas::FileUtils.attach_file_request_builder(small_file.path)
177+
puts "- Small file attachment structure: #{small_attachment.keys}"
178+
179+
# Test large file handling
180+
large_attachment = Nylas::FileUtils.attach_file_request_builder(large_file_path)
181+
puts "- Large file attachment structure: #{large_attachment.keys}"
182+
183+
# Demonstrate the SDK's file size handling
184+
small_payload = {
185+
subject: "test",
186+
attachments: [small_attachment]
187+
}
188+
189+
large_payload = {
190+
subject: "test",
191+
attachments: [large_attachment]
192+
}
193+
194+
# Show how the SDK determines handling method
195+
small_handling, small_files = Nylas::FileUtils.handle_message_payload(small_payload)
196+
large_handling, large_files = Nylas::FileUtils.handle_message_payload(large_payload)
197+
198+
puts "- Small file handling method: #{small_handling['multipart'] ? 'Form Data' : 'JSON'}"
199+
puts "- Large file handling method: #{large_handling['multipart'] ? 'Form Data' : 'JSON'}"
200+
201+
ensure
202+
small_file.close
203+
small_file.unlink
204+
# Note: We keep the large file on disk for future use
205+
end
206+
end
207+
208+
def main
209+
# Load .env file if it exists
210+
load_env_file
211+
212+
# Check for required environment variables
213+
api_key = ENV["NYLAS_API_KEY"]
214+
grant_id = ENV["NYLAS_GRANT_ID"]
215+
test_email = ENV["NYLAS_TEST_EMAIL"]
216+
217+
raise "NYLAS_API_KEY environment variable is not set" unless api_key
218+
raise "NYLAS_GRANT_ID environment variable is not set" unless grant_id
219+
raise "NYLAS_TEST_EMAIL environment variable is not set" unless test_email
220+
221+
puts "=== Nylas File Upload Example - HTTParty Migration Test ==="
222+
puts "Using API key: #{api_key[0..4]}..."
223+
puts "Using grant ID: #{grant_id[0..8]}..."
224+
puts "Test email recipient: #{test_email}"
225+
226+
# Initialize the Nylas client
227+
nylas = Nylas::Client.new(
228+
api_key: api_key,
229+
api_uri: ENV["NYLAS_API_URI"] || "https://api.us.nylas.com"
230+
)
231+
232+
begin
233+
# Demonstrate file handling logic
234+
demonstrate_file_utils_handling
235+
236+
# Create test files
237+
small_file = create_small_test_file
238+
large_file_path = find_or_create_large_test_file
239+
240+
begin
241+
# Test 1: Send message with small attachment
242+
small_response = send_message_with_small_attachment(nylas, grant_id, test_email, small_file)
243+
244+
# Test 2: Send message with large attachment
245+
large_response = send_message_with_large_attachment(nylas, grant_id, test_email, large_file_path)
246+
247+
puts "\n=== Summary ==="
248+
puts "✅ Both small and large file uploads completed successfully!"
249+
puts "- Small file message ID: #{small_response[:id]}"
250+
puts "- Large file message ID: #{large_response[:id]}"
251+
puts "- HTTParty migration verified for both file handling methods"
252+
253+
ensure
254+
# Clean up temporary small file only
255+
small_file.close
256+
small_file.unlink
257+
puts "\n🧹 Cleaned up temporary small file (large file kept on disk for reuse)"
258+
end
259+
260+
rescue => e
261+
puts "\n❌ Example failed: #{e.message}"
262+
puts "- #{e.backtrace.first}"
263+
exit 1
264+
end
265+
266+
puts "\n🎉 File upload example completed successfully!"
267+
puts "This confirms that the HTTParty migration properly handles:"
268+
puts "- Small files (<3MB): JSON with base64 encoding"
269+
puts "- Large files (>3MB): Multipart form data"
270+
puts "- File attachment building and processing"
271+
puts "- HTTP request execution with different payload types"
272+
end
273+
274+
if __FILE__ == $0
275+
main
276+
end

0 commit comments

Comments
 (0)