nylas 6.4.0 → 6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 275a09b48ad361ba1f441efa8b32a026fa547c0296ea3b681da440e6c7652516
4
- data.tar.gz: 813293f38fabeb6cbfae1a9974a6e27622c68352442982a49890c176c354a2d5
3
+ metadata.gz: b88e46de5b92ca56b82971334bc996b373407c76612b7ae7cf560a6496c5fb57
4
+ data.tar.gz: 25caea97521efd4fd2f903eed63b375b91a47142c797fd109cb60169a4d51920
5
5
  SHA512:
6
- metadata.gz: 3453ab99f8e2ab3bec6abd2f2cad0c8ea0d43e26a328eb3ff318b2c250f3cc4d699396c7638648118f4b2af47d37aca767f4106ad04dc5a0858b2c99f6814d5e
7
- data.tar.gz: 236711014f51f24b29c64fbe578cf0eea851cb44bb486dec3ec39c73a9311cb84a42d1f78d5c29e5eeae8025aef154e9e2ae74789fc50c9b5ebfe39eb9226a93
6
+ metadata.gz: 4af7e805753ad3df593249379933f58cfc42b25c962fafbfae041554d87fe66d59dd4ee1b18475bcf0c69e65dcfe57f1a384d9e9289e9c74fbda20b92d568fb6
7
+ data.tar.gz: 704f122a8273212641bcb02c4b1dd6b225d00f64f23655cd3c35ec81d654e1c1e6585d44a99a7caf7a991f4ee8f3fc6f05c61008cc1496b4c2f5fc5521ee9c28
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rest-client"
3
+ require "httparty"
4
+ require "net/http"
4
5
 
5
6
  require_relative "../errors"
6
7
  require_relative "../version"
@@ -34,18 +35,18 @@ module Nylas
34
35
  request = build_request(method: method, path: path, headers: headers,
35
36
  query: query, payload: payload, api_key: api_key, timeout: timeout)
36
37
  begin
37
- rest_client_execute(**request) do |response, _request, result|
38
+ httparty_execute(**request) do |response, _request, result|
38
39
  content_type = nil
39
- if response.headers && response.headers[:content_type]
40
- content_type = response.headers[:content_type].downcase
40
+ if response.headers && response.headers["content-type"]
41
+ content_type = response.headers["content-type"].downcase
41
42
  end
42
43
 
43
- parsed_response = parse_json_evaluate_error(result.code.to_i, response, path, content_type)
44
+ parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type)
44
45
  # Include headers in the response
45
46
  parsed_response[:headers] = response.headers unless parsed_response.nil?
46
47
  parsed_response
47
48
  end
48
- rescue RestClient::Exceptions::OpenTimeout, RestClient::Exceptions::ReadTimeout
49
+ rescue Net::OpenTimeout, Net::ReadTimeout
49
50
  raise Nylas::NylasSdkTimeoutError.new(request[:path], timeout)
50
51
  end
51
52
  end
@@ -97,11 +98,18 @@ module Nylas
97
98
  )
98
99
  url = build_url(path, query)
99
100
  resulting_headers = default_headers.merge(headers).merge(auth_header(api_key))
100
- if !payload.nil? && !payload["multipart"]
101
+
102
+ # Check for multipart flag using both string and symbol keys for backwards compatibility
103
+ is_multipart = !payload.nil? && (payload["multipart"] || payload[:multipart])
104
+
105
+ if !payload.nil? && !is_multipart
106
+ normalize_json_encodings!(payload)
101
107
  payload = payload&.to_json
102
108
  resulting_headers["Content-type"] = "application/json"
103
- elsif !payload.nil? && payload["multipart"]
109
+ elsif is_multipart
110
+ # Remove multipart flag from both possible key types
104
111
  payload.delete("multipart")
112
+ payload.delete(:multipart)
105
113
  end
106
114
 
107
115
  { method: method, url: url, payload: payload, headers: resulting_headers, timeout: timeout }
@@ -126,16 +134,156 @@ module Nylas
126
134
 
127
135
  private
128
136
 
129
- # Sends a request to the Nylas REST API.
137
+ # Sends a request to the Nylas REST API using HTTParty.
130
138
  #
131
139
  # @param method [Symbol] HTTP method for the API call. Either :get, :post, :delete, or :patch.
132
140
  # @param url [String] URL for the API call.
133
141
  # @param headers [Hash] HTTP headers to include in the payload.
134
142
  # @param payload [String, Hash] Body to send with the request.
135
143
  # @param timeout [Hash] Timeout value to send with the request.
136
- def rest_client_execute(method:, url:, headers:, payload:, timeout:, &block)
137
- ::RestClient::Request.execute(method: method, url: url, payload: payload,
138
- headers: headers, timeout: timeout, &block)
144
+ def httparty_execute(method:, url:, headers:, payload:, timeout:)
145
+ options = {
146
+ headers: headers,
147
+ timeout: timeout
148
+ }
149
+
150
+ # Handle multipart uploads
151
+ if payload.is_a?(Hash) && file_upload?(payload)
152
+ options[:multipart] = true
153
+ options[:body] = prepare_multipart_payload(payload)
154
+ elsif payload
155
+ options[:body] = payload
156
+ end
157
+
158
+ response = HTTParty.send(method, url, options)
159
+
160
+ # Create a compatible response object that mimics RestClient::Response
161
+ result = create_response_wrapper(response)
162
+
163
+ # Call the block with the response in the same format as rest-client
164
+ if block_given?
165
+ yield response, nil, result
166
+ else
167
+ response
168
+ end
169
+ end
170
+
171
+ # Create a response wrapper that mimics RestClient::Response.code behavior
172
+ def create_response_wrapper(response)
173
+ OpenStruct.new(code: response.code)
174
+ end
175
+
176
+ # Check if payload contains file uploads
177
+ def file_upload?(payload)
178
+ return false unless payload.is_a?(Hash)
179
+
180
+ # Check for traditional file uploads (File objects or objects that respond to :read)
181
+ has_file_objects = payload.values.any? do |value|
182
+ value.respond_to?(:read) || (value.is_a?(File) && !value.closed?)
183
+ end
184
+
185
+ return true if has_file_objects
186
+
187
+ # Check if payload was prepared by FileUtils.build_form_request for multipart uploads
188
+ # This handles binary content attachments that are strings with added singleton methods
189
+ has_message_field = payload.key?("message") && payload["message"].is_a?(String)
190
+ has_attachment_fields = payload.keys.any? { |key| key.is_a?(String) && key.match?(/^file\d+$/) }
191
+
192
+ # If we have both a "message" field and "file{N}" fields, this indicates
193
+ # the payload was prepared by FileUtils.build_form_request for multipart upload
194
+ has_message_field && has_attachment_fields
195
+ end
196
+
197
+ # Prepare multipart payload for HTTParty compatibility
198
+ # HTTParty requires all multipart fields to have compatible encodings
199
+ def prepare_multipart_payload(payload)
200
+ require "stringio"
201
+
202
+ modified_payload = payload.dup
203
+
204
+ # First, normalize all string encodings to prevent HTTParty encoding conflicts
205
+ normalize_multipart_encodings!(modified_payload)
206
+
207
+ # Handle binary content attachments (file0, file1, etc.) by converting them to enhanced StringIO
208
+ # HTTParty expects file uploads to be objects with full file-like interface
209
+ modified_payload.each do |key, value|
210
+ next unless key.is_a?(String) && key.match?(/^file\d+$/) && value.is_a?(String)
211
+
212
+ # Get the original value to check for singleton methods
213
+ original_value = payload[key]
214
+
215
+ # Create an enhanced StringIO object for HTTParty compatibility
216
+ string_io = create_file_like_stringio(value)
217
+
218
+ # Preserve filename and content_type if they exist as singleton methods
219
+ if original_value.respond_to?(:original_filename)
220
+ string_io.define_singleton_method(:original_filename) { original_value.original_filename }
221
+ end
222
+
223
+ if original_value.respond_to?(:content_type)
224
+ string_io.define_singleton_method(:content_type) { original_value.content_type }
225
+ end
226
+
227
+ modified_payload[key] = string_io
228
+ end
229
+
230
+ modified_payload
231
+ end
232
+
233
+ # Normalize string encodings in multipart payload to prevent HTTParty encoding conflicts
234
+ # This ensures all string fields use consistent ASCII-8BIT encoding for multipart compatibility
235
+ def normalize_multipart_encodings!(payload)
236
+ payload.each do |key, value|
237
+ next unless value.is_a?(String)
238
+
239
+ # Force all string values to ASCII-8BIT encoding for multipart compatibility
240
+ # HTTParty/multipart-post expects binary encoding for consistent concatenation
241
+ payload[key] = value.dup.force_encoding(Encoding::ASCII_8BIT)
242
+ end
243
+ end
244
+
245
+ # Normalize JSON encodings for attachment content to ensure binary data is base64 encoded.
246
+ # This handles cases where users pass raw binary content directly instead of file objects.
247
+ def normalize_json_encodings!(payload)
248
+ return unless payload.is_a?(Hash)
249
+
250
+ # Handle attachment content encoding for JSON serialization
251
+ attachments = payload[:attachments] || payload["attachments"]
252
+ return unless attachments
253
+
254
+ attachments.each do |attachment|
255
+ content = attachment[:content] || attachment["content"]
256
+ next unless content.is_a?(String)
257
+
258
+ # If content appears to be binary (non-UTF-8), base64 encode it
259
+ next unless content.encoding == Encoding::ASCII_8BIT || !content.valid_encoding?
260
+
261
+ encoded_content = Base64.strict_encode64(content)
262
+ if attachment.key?(:content)
263
+ attachment[:content] = encoded_content
264
+ else
265
+ attachment["content"] = encoded_content
266
+ end
267
+ end
268
+ end
269
+
270
+ # Create a StringIO object that behaves more like a File for HTTParty compatibility
271
+ def create_file_like_stringio(content)
272
+ # Content is already normalized to ASCII-8BIT by normalize_multipart_encodings!
273
+ # Create StringIO with the normalized binary content
274
+ string_io = StringIO.new(content)
275
+
276
+ # Add methods that HTTParty/multipart-post might expect
277
+ string_io.define_singleton_method(:path) { nil }
278
+ string_io.define_singleton_method(:local_path) { nil }
279
+ string_io.define_singleton_method(:respond_to_missing?) do |method_name, include_private = false|
280
+ File.instance_methods.include?(method_name) || super(method_name, include_private)
281
+ end
282
+
283
+ # Set binary mode for file-like behavior
284
+ string_io.binmode if string_io.respond_to?(:binmode)
285
+
286
+ string_io
139
287
  end
140
288
 
141
289
  def setup_http(path, timeout, headers, query, api_key)
@@ -15,6 +15,11 @@ module Nylas
15
15
  #
16
16
  # @param identifier [String] Grant ID or email account to query.
17
17
  # @param query_params [Hash, nil] Query params to pass to the request.
18
+ # Supported parameters include:
19
+ # - single_level: (Boolean) For Microsoft accounts only. If true, retrieves folders from
20
+ # a single-level hierarchy only. If false (default), retrieves folders across a
21
+ # multi-level hierarchy.
22
+ # - include_hidden_folders [Boolean] (Microsoft only) When true, includes hidden folders.
18
23
  # @return [Array(Array(Hash), String, String)] The list of folders, API Request ID, and next cursor.
19
24
  def list(identifier:, query_params: nil)
20
25
  get_list(
@@ -6,6 +6,19 @@ require_relative "../handler/api_operations"
6
6
  require_relative "../utils/file_utils"
7
7
 
8
8
  module Nylas
9
+ # Module representing the possible 'fields' values for Messages API requests.
10
+ # @see https://developer.nylas.com/docs/api/messages#get-/v3/grants/-identifier-/messages
11
+ module MessageFields
12
+ # Return the standard message payload (default)
13
+ STANDARD = "standard"
14
+ # Return messages and their custom headers
15
+ INCLUDE_HEADERS = "include_headers"
16
+ # Return messages and their tracking settings
17
+ INCLUDE_TRACKING_OPTIONS = "include_tracking_options"
18
+ # Return the grant_id, object, id, and raw_mime fields only
19
+ RAW_MIME = "raw_mime"
20
+ end
21
+
9
22
  # Nylas Messages API
10
23
  class Messages < Resource
11
24
  include ApiOperations::Get
@@ -26,6 +39,11 @@ module Nylas
26
39
  #
27
40
  # @param identifier [String] Grant ID or email account to query.
28
41
  # @param query_params [Hash, nil] Query params to pass to the request.
42
+ # You can use the fields parameter to specify which data to return:
43
+ # - MessageFields::STANDARD (default): Returns the standard message payload
44
+ # - MessageFields::INCLUDE_HEADERS: Returns messages and their custom headers
45
+ # - MessageFields::INCLUDE_TRACKING_OPTIONS: Returns messages and their tracking settings
46
+ # - MessageFields::RAW_MIME: Returns the grant_id, object, id, and raw_mime fields only
29
47
  # @return [Array(Array(Hash), String, String)] The list of messages, API Request ID, and next cursor.
30
48
  def list(identifier:, query_params: nil)
31
49
  get_list(
@@ -39,6 +57,11 @@ module Nylas
39
57
  # @param identifier [String] Grant ID or email account to query.
40
58
  # @param message_id [String] The id of the message to return.
41
59
  # @param query_params [Hash, nil] Query params to pass to the request.
60
+ # You can use the fields parameter to specify which data to return:
61
+ # - MessageFields::STANDARD (default): Returns the standard message payload
62
+ # - MessageFields::INCLUDE_HEADERS: Returns messages and their custom headers
63
+ # - MessageFields::INCLUDE_TRACKING_OPTIONS: Returns messages and their tracking settings
64
+ # - MessageFields::RAW_MIME: Returns the grant_id, object, id, and raw_mime fields only
42
65
  # @return [Array(Hash, String)] The message and API request ID.
43
66
  def find(identifier:, message_id:, query_params: nil)
44
67
  get(
@@ -103,7 +126,7 @@ module Nylas
103
126
  request_body: payload
104
127
  )
105
128
 
106
- opened_files.each(&:close)
129
+ opened_files.each { |file| file.close if file.respond_to?(:close) }
107
130
 
108
131
  response
109
132
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "cgi"
3
4
  require_relative "resource"
4
5
  require_relative "../handler/api_operations"
5
6
 
data/lib/nylas/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nylas
4
- VERSION = "6.4.0"
4
+ VERSION = "6.6.0"
5
5
  end
data/lib/nylas.rb CHANGED
@@ -1,21 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
- require "rest-client"
5
-
6
- # BUGFIX
7
- # See https://github.com/sparklemotion/http-cookie/issues/27
8
- # and https://github.com/sparklemotion/http-cookie/issues/6
9
- #
10
- # CookieJar uses unsafe class caching for dynamically loading cookie jars.
11
- # If two rest-client instances are instantiated at the same time (in threads), non-deterministic
12
- # behaviour can occur whereby the Hash cookie jar isn't properly loaded and cached.
13
- # Forcing an instantiation of the jar onload will force the CookieJar to load before the system has
14
- # a chance to spawn any threads.
15
- # Note that this should technically be fixed in rest-client itself, however that library appears to
16
- # be stagnant so we're forced to fix it here.
17
- # This object should get GC'd as it's not referenced by anything.
18
- HTTP::CookieJar.new
4
+ require "httparty"
19
5
 
20
6
  require "ostruct"
21
7
  require "forwardable"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nylas
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.4.0
4
+ version: 6.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nylas, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-30 00:00:00.000000000 Z
11
+ date: 2025-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.21'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.21'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: mime-types
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,26 +72,6 @@ dependencies:
58
72
  - - "~>"
59
73
  - !ruby/object:Gem::Version
60
74
  version: '0.6'
61
- - !ruby/object:Gem::Dependency
62
- name: rest-client
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: 2.0.0
68
- - - "<"
69
- - !ruby/object:Gem::Version
70
- version: '3.0'
71
- type: :runtime
72
- prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
74
- requirements:
75
- - - ">="
76
- - !ruby/object:Gem::Version
77
- version: 2.0.0
78
- - - "<"
79
- - !ruby/object:Gem::Version
80
- version: '3.0'
81
75
  - !ruby/object:Gem::Dependency
82
76
  name: yajl-ruby
83
77
  requirement: !ruby/object:Gem::Requirement