nylas 6.5.0 → 6.7.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: f9529fffa30430e822edf113d38aa1a179f0995b29d2bec8c3ac5f9a26d4d8c7
4
- data.tar.gz: 8710b98962a46bef277461df24f5e64dedd9bdbe7949838b2b3d8aab38b36f10
3
+ metadata.gz: cc90a16ef74fc2f47d6f35ada72d9cd80e3e842e08d9d5b1916a819ae36c0808
4
+ data.tar.gz: b2c8494899f1b2ff4620dfbcce0aeb52e037adfe97f1609bcaaf455bfb843745
5
5
  SHA512:
6
- metadata.gz: 20e35b192df7f3dfaf0d50065556d3ef0c19e959504dc124ec3bd2a6f657ffebb7a61b4808517494b2d79d4ceccdd8ce88e45d49109c142ad366592481a67b56
7
- data.tar.gz: d957251f63c3c8aa8b90adde51ba2ea7fe52be3b08ca8017e1b91a22c4e42c4d428a720c1b3a18191d1861d2bf379ebd93e481b30547ffbe4ce8f99578e85fb1
6
+ metadata.gz: c9cda94765c3957d6457071ac616dd12a3a807378773b566da0be82b39c5a89db370049f21744b91ed8f3a5abf211b6c75363f8df10f52d102007becfcd2a635
7
+ data.tar.gz: e50d00d31fdb2ae23058d146b38cc75a5a75f239ba19220b5c28280defe4624b4ab4d858ec3abd496555ab6b0f35033ac2c606ed2bc346572f0593df0cebb5cc
@@ -41,7 +41,8 @@ module Nylas
41
41
  content_type = response.headers["content-type"].downcase
42
42
  end
43
43
 
44
- parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type)
44
+ parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type,
45
+ response.headers)
45
46
  # Include headers in the response
46
47
  parsed_response[:headers] = response.headers unless parsed_response.nil?
47
48
  parsed_response
@@ -103,6 +104,7 @@ module Nylas
103
104
  is_multipart = !payload.nil? && (payload["multipart"] || payload[:multipart])
104
105
 
105
106
  if !payload.nil? && !is_multipart
107
+ normalize_json_encodings!(payload)
106
108
  payload = payload&.to_json
107
109
  resulting_headers["Content-type"] = "application/json"
108
110
  elsif is_multipart
@@ -149,7 +151,7 @@ module Nylas
149
151
  # Handle multipart uploads
150
152
  if payload.is_a?(Hash) && file_upload?(payload)
151
153
  options[:multipart] = true
152
- options[:body] = payload
154
+ options[:body] = prepare_multipart_payload(payload)
153
155
  elsif payload
154
156
  options[:body] = payload
155
157
  end
@@ -176,9 +178,113 @@ module Nylas
176
178
  def file_upload?(payload)
177
179
  return false unless payload.is_a?(Hash)
178
180
 
179
- payload.values.any? do |value|
181
+ # Check for traditional file uploads (File objects or objects that respond to :read)
182
+ has_file_objects = payload.values.any? do |value|
180
183
  value.respond_to?(:read) || (value.is_a?(File) && !value.closed?)
181
184
  end
185
+
186
+ return true if has_file_objects
187
+
188
+ # Check if payload was prepared by FileUtils.build_form_request for multipart uploads
189
+ # This handles binary content attachments that are strings with added singleton methods
190
+ has_message_field = payload.key?("message") && payload["message"].is_a?(String)
191
+ has_attachment_fields = payload.keys.any? { |key| key.is_a?(String) && key.match?(/^file\d+$/) }
192
+
193
+ # If we have both a "message" field and "file{N}" fields, this indicates
194
+ # the payload was prepared by FileUtils.build_form_request for multipart upload
195
+ has_message_field && has_attachment_fields
196
+ end
197
+
198
+ # Prepare multipart payload for HTTParty compatibility
199
+ # HTTParty requires all multipart fields to have compatible encodings
200
+ def prepare_multipart_payload(payload)
201
+ require "stringio"
202
+
203
+ modified_payload = payload.dup
204
+
205
+ # First, normalize all string encodings to prevent HTTParty encoding conflicts
206
+ normalize_multipart_encodings!(modified_payload)
207
+
208
+ # Handle binary content attachments (file0, file1, etc.) by converting them to enhanced StringIO
209
+ # HTTParty expects file uploads to be objects with full file-like interface
210
+ modified_payload.each do |key, value|
211
+ next unless key.is_a?(String) && key.match?(/^file\d+$/) && value.is_a?(String)
212
+
213
+ # Get the original value to check for singleton methods
214
+ original_value = payload[key]
215
+
216
+ # Create an enhanced StringIO object for HTTParty compatibility
217
+ string_io = create_file_like_stringio(value)
218
+
219
+ # Preserve filename and content_type if they exist as singleton methods
220
+ if original_value.respond_to?(:original_filename)
221
+ string_io.define_singleton_method(:original_filename) { original_value.original_filename }
222
+ end
223
+
224
+ if original_value.respond_to?(:content_type)
225
+ string_io.define_singleton_method(:content_type) { original_value.content_type }
226
+ end
227
+
228
+ modified_payload[key] = string_io
229
+ end
230
+
231
+ modified_payload
232
+ end
233
+
234
+ # Normalize string encodings in multipart payload to prevent HTTParty encoding conflicts
235
+ # This ensures all string fields use consistent ASCII-8BIT encoding for multipart compatibility
236
+ def normalize_multipart_encodings!(payload)
237
+ payload.each do |key, value|
238
+ next unless value.is_a?(String)
239
+
240
+ # Force all string values to ASCII-8BIT encoding for multipart compatibility
241
+ # HTTParty/multipart-post expects binary encoding for consistent concatenation
242
+ payload[key] = value.dup.force_encoding(Encoding::ASCII_8BIT)
243
+ end
244
+ end
245
+
246
+ # Normalize JSON encodings for attachment content to ensure binary data is base64 encoded.
247
+ # This handles cases where users pass raw binary content directly instead of file objects.
248
+ def normalize_json_encodings!(payload)
249
+ return unless payload.is_a?(Hash)
250
+
251
+ # Handle attachment content encoding for JSON serialization
252
+ attachments = payload[:attachments] || payload["attachments"]
253
+ return unless attachments
254
+
255
+ attachments.each do |attachment|
256
+ content = attachment[:content] || attachment["content"]
257
+ next unless content.is_a?(String)
258
+
259
+ # If content appears to be binary (non-UTF-8), base64 encode it
260
+ next unless content.encoding == Encoding::ASCII_8BIT || !content.valid_encoding?
261
+
262
+ encoded_content = Base64.strict_encode64(content)
263
+ if attachment.key?(:content)
264
+ attachment[:content] = encoded_content
265
+ else
266
+ attachment["content"] = encoded_content
267
+ end
268
+ end
269
+ end
270
+
271
+ # Create a StringIO object that behaves more like a File for HTTParty compatibility
272
+ def create_file_like_stringio(content)
273
+ # Content is already normalized to ASCII-8BIT by normalize_multipart_encodings!
274
+ # Create StringIO with the normalized binary content
275
+ string_io = StringIO.new(content)
276
+
277
+ # Add methods that HTTParty/multipart-post might expect
278
+ string_io.define_singleton_method(:path) { nil }
279
+ string_io.define_singleton_method(:local_path) { nil }
280
+ string_io.define_singleton_method(:respond_to_missing?) do |method_name, include_private = false|
281
+ File.instance_methods.include?(method_name) || super(method_name, include_private)
282
+ end
283
+
284
+ # Set binary mode for file-like behavior
285
+ string_io.binmode if string_io.respond_to?(:binmode)
286
+
287
+ string_io
182
288
  end
183
289
 
184
290
  def setup_http(path, timeout, headers, query, api_key)
@@ -206,37 +312,34 @@ module Nylas
206
312
  end
207
313
 
208
314
  # Parses the response from the Nylas API and evaluates for errors.
209
- def parse_json_evaluate_error(http_code, response, path, content_type = nil)
315
+ def parse_json_evaluate_error(http_code, response, path, content_type = nil, headers = nil)
210
316
  begin
211
317
  response = parse_response(response) if content_type == "application/json"
212
318
  rescue Nylas::JsonParseError
213
- handle_failed_response(http_code, response, path)
319
+ handle_failed_response(http_code, response, path, headers)
214
320
  raise
215
321
  end
216
322
 
217
- handle_failed_response(http_code, response, path)
323
+ handle_failed_response(http_code, response, path, headers)
218
324
  response
219
325
  end
220
326
 
221
327
  # Handles failed responses from the Nylas API.
222
- def handle_failed_response(http_code, response, path)
328
+ def handle_failed_response(http_code, response, path, headers = nil)
223
329
  return if HTTP_SUCCESS_CODES.include?(http_code)
224
330
 
225
331
  case response
226
332
  when Hash
227
- raise error_hash_to_exception(response, http_code, path)
333
+ raise error_hash_to_exception(response, http_code, path, headers)
228
334
  else
229
335
  raise NylasApiError.parse_error_response(response, http_code)
230
336
  end
231
337
  end
232
338
 
233
339
  # Converts error hashes to exceptions.
234
- def error_hash_to_exception(response, status_code, path)
340
+ def error_hash_to_exception(response, status_code, path, headers = nil)
235
341
  return if !response || !response.key?(:error)
236
342
 
237
- # Safely get headers without risking KeyError
238
- headers = response.key?(:headers) ? response[:headers] : nil
239
-
240
343
  if %W[#{api_uri}/v3/connect/token #{api_uri}/v3/connect/revoke].include?(path)
241
344
  NylasOAuthError.new(response[:error], response[:error_description], response[:error_uri],
242
345
  response[:error_code], status_code)
@@ -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(
@@ -126,7 +126,7 @@ module Nylas
126
126
  request_body: payload
127
127
  )
128
128
 
129
- opened_files.each(&:close)
129
+ opened_files.each { |file| file.close if file.respond_to?(:close) }
130
130
 
131
131
  response
132
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.5.0"
4
+ VERSION = "6.7.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nylas
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.5.0
4
+ version: 6.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nylas, Inc.
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-06-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: base64
@@ -295,7 +294,6 @@ files:
295
294
  - lib/nylas/resources/webhooks.rb
296
295
  - lib/nylas/utils/file_utils.rb
297
296
  - lib/nylas/version.rb
298
- homepage:
299
297
  licenses:
300
298
  - MIT
301
299
  metadata:
@@ -305,7 +303,6 @@ metadata:
305
303
  homepage_uri: https://www.nylas.com
306
304
  source_code_uri: https://github.com/nylas/nylas-ruby
307
305
  github_repo: https://github.com/nylas/nylas-ruby
308
- post_install_message:
309
306
  rdoc_options: []
310
307
  require_paths:
311
308
  - lib
@@ -320,8 +317,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
320
317
  - !ruby/object:Gem::Version
321
318
  version: '0'
322
319
  requirements: []
323
- rubygems_version: 3.4.18
324
- signing_key:
320
+ rubygems_version: 3.7.2
325
321
  specification_version: 4
326
322
  summary: Gem for interacting with the Nylas API
327
323
  test_files: []