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 +4 -4
- data/lib/nylas/handler/http_client.rb +160 -12
- data/lib/nylas/resources/folders.rb +5 -0
- data/lib/nylas/resources/messages.rb +24 -1
- data/lib/nylas/resources/webhooks.rb +1 -0
- data/lib/nylas/version.rb +1 -1
- data/lib/nylas.rb +1 -15
- metadata +16 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b88e46de5b92ca56b82971334bc996b373407c76612b7ae7cf560a6496c5fb57
|
4
|
+
data.tar.gz: 25caea97521efd4fd2f903eed63b375b91a47142c797fd109cb60169a4d51920
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4af7e805753ad3df593249379933f58cfc42b25c962fafbfae041554d87fe66d59dd4ee1b18475bcf0c69e65dcfe57f1a384d9e9289e9c74fbda20b92d568fb6
|
7
|
+
data.tar.gz: 704f122a8273212641bcb02c4b1dd6b225d00f64f23655cd3c35ec81d654e1c1e6585d44a99a7caf7a991f4ee8f3fc6f05c61008cc1496b4c2f5fc5521ee9c28
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
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
|
-
|
38
|
+
httparty_execute(**request) do |response, _request, result|
|
38
39
|
content_type = nil
|
39
|
-
if response.headers && response.headers[
|
40
|
-
content_type = response.headers[
|
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
|
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
|
-
|
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
|
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
|
137
|
-
|
138
|
-
|
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(
|
129
|
+
opened_files.each { |file| file.close if file.respond_to?(:close) }
|
107
130
|
|
108
131
|
response
|
109
132
|
end
|
data/lib/nylas/version.rb
CHANGED
data/lib/nylas.rb
CHANGED
@@ -1,21 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "json"
|
4
|
-
require "
|
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
|
+
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-
|
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
|