nylas 6.6.0 → 6.7.1
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 +102 -29
- data/lib/nylas/resources/webhooks.rb +4 -5
- data/lib/nylas/utils/file_utils.rb +6 -3
- data/lib/nylas/version.rb +1 -1
- metadata +19 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa916629fd9482f57599bd8cb7887f69575cc12cffd4f2d3d0fa37c5cd163b70
|
|
4
|
+
data.tar.gz: 906042891eb1519df1ff287ae27104b5f871edcbb938e25cc23e65a1d71452a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1f7203ab924ec04f7efda91875003a8ffa10a182800ae7f0ffa8fb1cb7ad01a5cb4403bf99d79d84e58514acd25b45e8446a5522f771b7bd110e1c17595d94c1
|
|
7
|
+
data.tar.gz: fb23ac5760b2ed22f253dba8316baf5542ca29f654b1ca5b92345d34918e138065051b207435a62c82d41cefdbb7d3f6ecec0df0eee8294c3471518cc54f3151
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "httparty"
|
|
4
4
|
require "net/http"
|
|
5
|
+
require "net/http/post/multipart"
|
|
5
6
|
|
|
6
7
|
require_relative "../errors"
|
|
7
8
|
require_relative "../version"
|
|
@@ -41,7 +42,8 @@ module Nylas
|
|
|
41
42
|
content_type = response.headers["content-type"].downcase
|
|
42
43
|
end
|
|
43
44
|
|
|
44
|
-
parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type
|
|
45
|
+
parsed_response = parse_json_evaluate_error(result.code.to_i, response.body, path, content_type,
|
|
46
|
+
response.headers)
|
|
45
47
|
# Include headers in the response
|
|
46
48
|
parsed_response[:headers] = response.headers unless parsed_response.nil?
|
|
47
49
|
parsed_response
|
|
@@ -134,7 +136,9 @@ module Nylas
|
|
|
134
136
|
|
|
135
137
|
private
|
|
136
138
|
|
|
137
|
-
# Sends a request to the Nylas REST API using HTTParty.
|
|
139
|
+
# Sends a request to the Nylas REST API using HTTParty or Net::HTTP for multipart.
|
|
140
|
+
# Multipart requests use Net::HTTP::Post::Multipart (multipart-post gem) because
|
|
141
|
+
# HTTParty's multipart handling produces malformed requests that the Nylas API rejects.
|
|
138
142
|
#
|
|
139
143
|
# @param method [Symbol] HTTP method for the API call. Either :get, :post, :delete, or :patch.
|
|
140
144
|
# @param url [String] URL for the API call.
|
|
@@ -142,25 +146,16 @@ module Nylas
|
|
|
142
146
|
# @param payload [String, Hash] Body to send with the request.
|
|
143
147
|
# @param timeout [Hash] Timeout value to send with the request.
|
|
144
148
|
def httparty_execute(method:, url:, headers:, payload:, timeout:)
|
|
145
|
-
|
|
146
|
-
headers: headers,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
149
|
+
if method == :post && payload.is_a?(Hash) && file_upload?(payload)
|
|
150
|
+
response = execute_multipart_request(url: url, headers: headers, payload: payload, timeout: timeout)
|
|
151
|
+
else
|
|
152
|
+
options = { headers: headers, timeout: timeout }
|
|
153
|
+
options[:body] = payload if payload
|
|
154
|
+
response = HTTParty.send(method, url, options)
|
|
156
155
|
end
|
|
157
156
|
|
|
158
|
-
response = HTTParty.send(method, url, options)
|
|
159
|
-
|
|
160
|
-
# Create a compatible response object that mimics RestClient::Response
|
|
161
157
|
result = create_response_wrapper(response)
|
|
162
158
|
|
|
163
|
-
# Call the block with the response in the same format as rest-client
|
|
164
159
|
if block_given?
|
|
165
160
|
yield response, nil, result
|
|
166
161
|
else
|
|
@@ -168,6 +163,77 @@ module Nylas
|
|
|
168
163
|
end
|
|
169
164
|
end
|
|
170
165
|
|
|
166
|
+
# Executes multipart POST using Net::HTTP::Post::Multipart (fixes issue #538).
|
|
167
|
+
# HTTParty's multipart produces malformed requests; multipart-post/UploadIO works correctly.
|
|
168
|
+
def execute_multipart_request(url:, headers:, payload:, timeout:)
|
|
169
|
+
uri = URI.parse(url)
|
|
170
|
+
params = build_multipart_params(payload)
|
|
171
|
+
|
|
172
|
+
req = Net::HTTP::Post::Multipart.new(uri.path, params)
|
|
173
|
+
headers.each { |key, value| req[key] = value }
|
|
174
|
+
|
|
175
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
176
|
+
http.use_ssl = (uri.scheme == "https")
|
|
177
|
+
http.read_timeout = timeout
|
|
178
|
+
http.open_timeout = timeout
|
|
179
|
+
|
|
180
|
+
response = http.request(req)
|
|
181
|
+
|
|
182
|
+
create_httparty_like_response(response)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Build params hash for Net::HTTP::Post::Multipart with UploadIO for file fields.
|
|
186
|
+
def build_multipart_params(payload)
|
|
187
|
+
params = {}
|
|
188
|
+
payload.each do |key, value|
|
|
189
|
+
params[key] = if key.is_a?(String) && key != "message" && file_like_value?(value)
|
|
190
|
+
value_to_upload_io(value)
|
|
191
|
+
else
|
|
192
|
+
value.to_s
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
params
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def file_like_value?(value)
|
|
199
|
+
return true if value.respond_to?(:read) && (value.is_a?(File) ? !value.closed? : true)
|
|
200
|
+
if value.is_a?(String) && (value.respond_to?(:original_filename) || value.respond_to?(:content_type))
|
|
201
|
+
return true
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
false
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Convert File, String, or StringIO to UploadIO for multipart-post.
|
|
208
|
+
def value_to_upload_io(value)
|
|
209
|
+
content_type = value.respond_to?(:content_type) ? value.content_type : "application/octet-stream"
|
|
210
|
+
filename = value.respond_to?(:original_filename) ? value.original_filename : "file.bin"
|
|
211
|
+
|
|
212
|
+
io = if value.respond_to?(:read) && value.respond_to?(:rewind)
|
|
213
|
+
value.rewind if value.respond_to?(:rewind)
|
|
214
|
+
value
|
|
215
|
+
else
|
|
216
|
+
require "stringio"
|
|
217
|
+
content = value.to_s
|
|
218
|
+
content = content.dup.force_encoding(Encoding::ASCII_8BIT) if content.is_a?(String)
|
|
219
|
+
StringIO.new(content)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
UploadIO.new(io, content_type, filename)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Create response object compatible with HTTParty::Response interface.
|
|
226
|
+
def create_httparty_like_response(net_http_response)
|
|
227
|
+
headers = net_http_response.to_hash
|
|
228
|
+
headers = headers.transform_values { |v| v.is_a?(Array) && v.one? ? v.first : v }
|
|
229
|
+
|
|
230
|
+
OpenStruct.new(
|
|
231
|
+
body: net_http_response.body,
|
|
232
|
+
code: net_http_response.code.to_i,
|
|
233
|
+
headers: headers
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
171
237
|
# Create a response wrapper that mimics RestClient::Response.code behavior
|
|
172
238
|
def create_response_wrapper(response)
|
|
173
239
|
OpenStruct.new(code: response.code)
|
|
@@ -187,10 +253,20 @@ module Nylas
|
|
|
187
253
|
# Check if payload was prepared by FileUtils.build_form_request for multipart uploads
|
|
188
254
|
# This handles binary content attachments that are strings with added singleton methods
|
|
189
255
|
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
256
|
|
|
192
|
-
#
|
|
193
|
-
#
|
|
257
|
+
# Check for attachment fields - these can have custom content_id values (not just "file{N}")
|
|
258
|
+
# FileUtils.build_form_request creates entries with string values that have singleton methods
|
|
259
|
+
# like original_filename and content_type defined on them
|
|
260
|
+
has_attachment_fields = payload.any? do |key, value|
|
|
261
|
+
next false unless key.is_a?(String) && key != "message"
|
|
262
|
+
|
|
263
|
+
# Check if the value is a string with attachment-like singleton methods
|
|
264
|
+
# (original_filename or content_type), which indicates it's a file content
|
|
265
|
+
value.is_a?(String) && (value.respond_to?(:original_filename) || value.respond_to?(:content_type))
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# If we have both a "message" field and attachment fields with file metadata,
|
|
269
|
+
# this indicates the payload was prepared by FileUtils.build_form_request
|
|
194
270
|
has_message_field && has_attachment_fields
|
|
195
271
|
end
|
|
196
272
|
|
|
@@ -311,37 +387,34 @@ module Nylas
|
|
|
311
387
|
end
|
|
312
388
|
|
|
313
389
|
# Parses the response from the Nylas API and evaluates for errors.
|
|
314
|
-
def parse_json_evaluate_error(http_code, response, path, content_type = nil)
|
|
390
|
+
def parse_json_evaluate_error(http_code, response, path, content_type = nil, headers = nil)
|
|
315
391
|
begin
|
|
316
392
|
response = parse_response(response) if content_type == "application/json"
|
|
317
393
|
rescue Nylas::JsonParseError
|
|
318
|
-
handle_failed_response(http_code, response, path)
|
|
394
|
+
handle_failed_response(http_code, response, path, headers)
|
|
319
395
|
raise
|
|
320
396
|
end
|
|
321
397
|
|
|
322
|
-
handle_failed_response(http_code, response, path)
|
|
398
|
+
handle_failed_response(http_code, response, path, headers)
|
|
323
399
|
response
|
|
324
400
|
end
|
|
325
401
|
|
|
326
402
|
# Handles failed responses from the Nylas API.
|
|
327
|
-
def handle_failed_response(http_code, response, path)
|
|
403
|
+
def handle_failed_response(http_code, response, path, headers = nil)
|
|
328
404
|
return if HTTP_SUCCESS_CODES.include?(http_code)
|
|
329
405
|
|
|
330
406
|
case response
|
|
331
407
|
when Hash
|
|
332
|
-
raise error_hash_to_exception(response, http_code, path)
|
|
408
|
+
raise error_hash_to_exception(response, http_code, path, headers)
|
|
333
409
|
else
|
|
334
410
|
raise NylasApiError.parse_error_response(response, http_code)
|
|
335
411
|
end
|
|
336
412
|
end
|
|
337
413
|
|
|
338
414
|
# Converts error hashes to exceptions.
|
|
339
|
-
def error_hash_to_exception(response, status_code, path)
|
|
415
|
+
def error_hash_to_exception(response, status_code, path, headers = nil)
|
|
340
416
|
return if !response || !response.key?(:error)
|
|
341
417
|
|
|
342
|
-
# Safely get headers without risking KeyError
|
|
343
|
-
headers = response.key?(:headers) ? response[:headers] : nil
|
|
344
|
-
|
|
345
418
|
if %W[#{api_uri}/v3/connect/token #{api_uri}/v3/connect/revoke].include?(path)
|
|
346
419
|
NylasOAuthError.new(response[:error], response[:error_description], response[:error_uri],
|
|
347
420
|
response[:error_code], status_code)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "cgi"
|
|
4
3
|
require_relative "resource"
|
|
5
4
|
require_relative "../handler/api_operations"
|
|
6
5
|
|
|
@@ -115,14 +114,14 @@ module Nylas
|
|
|
115
114
|
# @return [String] The challenge parameter
|
|
116
115
|
def self.extract_challenge_parameter(url)
|
|
117
116
|
url_object = URI.parse(url)
|
|
118
|
-
|
|
117
|
+
params = URI.decode_www_form(url_object.query || "")
|
|
118
|
+
challenge_pair = params.find { |k, _| k == "challenge" }
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
if challenge_parameter.nil? || challenge_parameter.empty? || challenge_parameter.first.nil?
|
|
120
|
+
if challenge_pair.nil? || challenge_pair.last.to_s.empty?
|
|
122
121
|
raise "Invalid URL or no challenge parameter found."
|
|
123
122
|
end
|
|
124
123
|
|
|
125
|
-
|
|
124
|
+
challenge_pair.last
|
|
126
125
|
end
|
|
127
126
|
end
|
|
128
127
|
end
|
|
@@ -28,12 +28,13 @@ module Nylas
|
|
|
28
28
|
|
|
29
29
|
attachments.each_with_index do |attachment, index|
|
|
30
30
|
file = attachment[:content] || attachment["content"]
|
|
31
|
+
file_path = attachment[:file_path] || attachment["file_path"]
|
|
31
32
|
if file.respond_to?(:closed?) && file.closed?
|
|
32
|
-
unless
|
|
33
|
+
unless file_path
|
|
33
34
|
raise ArgumentError, "The file at index #{index} is closed and no file_path was provided."
|
|
34
35
|
end
|
|
35
36
|
|
|
36
|
-
file = File.open(
|
|
37
|
+
file = File.open(file_path, "rb")
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
# Setting original filename and content type if available. See rest-client#lib/restclient/payload.rb
|
|
@@ -87,7 +88,9 @@ module Nylas
|
|
|
87
88
|
|
|
88
89
|
# Use form data only if the attachment size is greater than 3mb
|
|
89
90
|
attachments = payload[:attachments]
|
|
90
|
-
|
|
91
|
+
# Support both string and symbol keys for attachment size to handle
|
|
92
|
+
# user-provided hashes that may use either key type
|
|
93
|
+
attachment_size = attachments&.sum { |attachment| attachment[:size] || attachment["size"] || 0 } || 0
|
|
91
94
|
|
|
92
95
|
# Handle the attachment encoding depending on the size
|
|
93
96
|
if attachment_size >= FORM_DATA_ATTACHMENT_SIZE
|
data/lib/nylas/version.rb
CHANGED
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.
|
|
4
|
+
version: 6.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nylas, Inc.
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: base64
|
|
@@ -58,6 +57,20 @@ dependencies:
|
|
|
58
57
|
- - ">="
|
|
59
58
|
- !ruby/object:Gem::Version
|
|
60
59
|
version: 3.5.1
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: multipart-post
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '2.0'
|
|
67
|
+
type: :runtime
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '2.0'
|
|
61
74
|
- !ruby/object:Gem::Dependency
|
|
62
75
|
name: ostruct
|
|
63
76
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -140,14 +153,14 @@ dependencies:
|
|
|
140
153
|
requirements:
|
|
141
154
|
- - "~>"
|
|
142
155
|
- !ruby/object:Gem::Version
|
|
143
|
-
version: '
|
|
156
|
+
version: '3.5'
|
|
144
157
|
type: :development
|
|
145
158
|
prerelease: false
|
|
146
159
|
version_requirements: !ruby/object:Gem::Requirement
|
|
147
160
|
requirements:
|
|
148
161
|
- - "~>"
|
|
149
162
|
- !ruby/object:Gem::Version
|
|
150
|
-
version: '
|
|
163
|
+
version: '3.5'
|
|
151
164
|
- !ruby/object:Gem::Dependency
|
|
152
165
|
name: rubocop-capybara
|
|
153
166
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -295,7 +308,6 @@ files:
|
|
|
295
308
|
- lib/nylas/resources/webhooks.rb
|
|
296
309
|
- lib/nylas/utils/file_utils.rb
|
|
297
310
|
- lib/nylas/version.rb
|
|
298
|
-
homepage:
|
|
299
311
|
licenses:
|
|
300
312
|
- MIT
|
|
301
313
|
metadata:
|
|
@@ -305,7 +317,6 @@ metadata:
|
|
|
305
317
|
homepage_uri: https://www.nylas.com
|
|
306
318
|
source_code_uri: https://github.com/nylas/nylas-ruby
|
|
307
319
|
github_repo: https://github.com/nylas/nylas-ruby
|
|
308
|
-
post_install_message:
|
|
309
320
|
rdoc_options: []
|
|
310
321
|
require_paths:
|
|
311
322
|
- lib
|
|
@@ -320,8 +331,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
320
331
|
- !ruby/object:Gem::Version
|
|
321
332
|
version: '0'
|
|
322
333
|
requirements: []
|
|
323
|
-
rubygems_version:
|
|
324
|
-
signing_key:
|
|
334
|
+
rubygems_version: 4.0.3
|
|
325
335
|
specification_version: 4
|
|
326
336
|
summary: Gem for interacting with the Nylas API
|
|
327
337
|
test_files: []
|