nylas 5.13.0 → 5.15.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/api.rb +4 -7
- data/lib/nylas/errors.rb +48 -1
- data/lib/nylas/http_client.rb +12 -4
- data/lib/nylas/services/tunnel.rb +128 -0
- data/lib/nylas/version.rb +1 -1
- data/lib/nylas.rb +2 -0
- metadata +51 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b432f464fcd9fec327a4aa014c499cadcf34aa8c4626ae1585d32b4aff9c7b21
|
4
|
+
data.tar.gz: 5c62a00ba6e2f1de70346aa7fe4cdb7d7a129c6b91c76a35c6349e9597ab51c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5220e063570324c94c8baf4e01645017510e497619382e2f872f110d9fbfa9f7c454b50bd87098382f0339cb6da2208eb45e3e6b622c92db330c978a85887f5e
|
7
|
+
data.tar.gz: 0e9388e8c5b7931212bbcdc1b21d324b4025634665aff3a3c3be7bf08f823d08c9b2b820d203287e4dfabaf3b95737a070cc8cb6957b86b80f0dd45dc5e3711d
|
data/lib/nylas/api.rb
CHANGED
@@ -36,18 +36,15 @@ module Nylas
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def authentication_url(redirect_uri:, scopes:, response_type: "code", login_hint: nil, state: nil,
|
39
|
-
provider: nil, redirect_on_error: nil)
|
40
|
-
params = {
|
41
|
-
|
42
|
-
redirect_uri: redirect_uri,
|
43
|
-
response_type: response_type,
|
44
|
-
login_hint: login_hint
|
45
|
-
}
|
39
|
+
provider: nil, redirect_on_error: nil, disable_provider_selection: nil)
|
40
|
+
params = { client_id: app_id, redirect_uri: redirect_uri, response_type: response_type,
|
41
|
+
login_hint: login_hint }
|
46
42
|
|
47
43
|
params[:state] = state if state
|
48
44
|
params[:scopes] = scopes.join(",") if scopes
|
49
45
|
params[:provider] = provider if provider
|
50
46
|
params[:redirect_on_error] = redirect_on_error if redirect_on_error
|
47
|
+
params[:disable_provider_selection] = disable_provider_selection if disable_provider_selection
|
51
48
|
|
52
49
|
"#{api_server}/oauth/authorize?#{URI.encode_www_form(params)}"
|
53
50
|
end
|
data/lib/nylas/errors.rb
CHANGED
@@ -45,7 +45,54 @@ module Nylas
|
|
45
45
|
self.message = message
|
46
46
|
self.server_error = server_error
|
47
47
|
end
|
48
|
+
|
49
|
+
def self.parse_error_response(response)
|
50
|
+
new(
|
51
|
+
response["type"],
|
52
|
+
response["message"],
|
53
|
+
response["server_error"]
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Error class representing a 429 error response, with details on the rate limit
|
59
|
+
class RateLimitError < APIError
|
60
|
+
attr_accessor :rate_limit
|
61
|
+
attr_accessor :rate_limit_reset
|
62
|
+
|
63
|
+
RATE_LIMIT_LIMIT_HEADER = "x_ratelimit_limit"
|
64
|
+
RATE_LIMIT_RESET_HEADER = "x_ratelimit_reset"
|
65
|
+
|
66
|
+
def initialize(type, message, server_error = nil, rate_limit = nil, rate_limit_reset = nil)
|
67
|
+
super(type, message, server_error)
|
68
|
+
self.rate_limit = rate_limit
|
69
|
+
self.rate_limit_reset = rate_limit_reset
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.parse_error_response(response)
|
73
|
+
rate_limit, rate_limit_rest = extract_rate_limit_details(response)
|
74
|
+
|
75
|
+
new(
|
76
|
+
response["type"],
|
77
|
+
response["message"],
|
78
|
+
response["server_error"],
|
79
|
+
rate_limit,
|
80
|
+
rate_limit_rest
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.extract_rate_limit_details(response)
|
85
|
+
return nil, nil unless response.respond_to?(:headers)
|
86
|
+
|
87
|
+
rate_limit = response.headers[RATE_LIMIT_LIMIT_HEADER.to_sym].to_i
|
88
|
+
rate_limit_rest = response.headers[RATE_LIMIT_RESET_HEADER.to_sym].to_i
|
89
|
+
|
90
|
+
[rate_limit, rate_limit_rest]
|
91
|
+
end
|
92
|
+
|
93
|
+
private_class_method :extract_rate_limit_details
|
48
94
|
end
|
95
|
+
|
49
96
|
AccessDenied = Class.new(APIError)
|
50
97
|
ResourceNotFound = Class.new(APIError)
|
51
98
|
MethodNotAllowed = Class.new(APIError)
|
@@ -55,7 +102,7 @@ module Nylas
|
|
55
102
|
TeapotError = Class.new(APIError)
|
56
103
|
RequestTimedOut = Class.new(APIError)
|
57
104
|
MessageRejected = Class.new(APIError)
|
58
|
-
SendingQuotaExceeded = Class.new(
|
105
|
+
SendingQuotaExceeded = Class.new(RateLimitError)
|
59
106
|
ServiceUnavailable = Class.new(APIError)
|
60
107
|
BadGateway = Class.new(APIError)
|
61
108
|
InternalError = Class.new(APIError)
|
data/lib/nylas/http_client.rb
CHANGED
@@ -189,8 +189,7 @@ module Nylas
|
|
189
189
|
def parse_response(response)
|
190
190
|
return response if response.is_a?(Enumerable)
|
191
191
|
|
192
|
-
|
193
|
-
Yajl::Parser.new(symbolize_names: true).parse(json)
|
192
|
+
Yajl::Parser.new(symbolize_names: true).parse(response)
|
194
193
|
rescue Yajl::ParseError
|
195
194
|
raise Nylas::JsonParseError
|
196
195
|
end
|
@@ -222,9 +221,18 @@ module Nylas
|
|
222
221
|
return if HTTP_SUCCESS_CODES.include?(http_code)
|
223
222
|
|
224
223
|
exception = HTTP_CODE_TO_EXCEPTIONS.fetch(http_code, APIError)
|
225
|
-
|
224
|
+
case response
|
225
|
+
when Hash
|
226
|
+
raise error_hash_to_exception(exception, response)
|
227
|
+
when RestClient::Response
|
228
|
+
raise exception.parse_error_response(response)
|
229
|
+
else
|
230
|
+
raise exception.new(http_code, response)
|
231
|
+
end
|
232
|
+
end
|
226
233
|
|
227
|
-
|
234
|
+
def error_hash_to_exception(exception, response)
|
235
|
+
exception.new(
|
228
236
|
response[:type],
|
229
237
|
response[:message],
|
230
238
|
response.fetch(:server_error, nil)
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
require "faye/websocket"
|
5
|
+
require "eventmachine"
|
6
|
+
|
7
|
+
module Nylas
|
8
|
+
# Class containing methods to spin up a developmental websocket connection to test webhooks
|
9
|
+
class Tunnel
|
10
|
+
# Open a webhook tunnel and register it with the Nylas API
|
11
|
+
# 1. Creates a UUID
|
12
|
+
# 2. Opens a websocket connection to Nylas' webhook forwarding service, with the UUID as a header
|
13
|
+
# 3. Creates a new webhook pointed at the forwarding service with the UUID as the path
|
14
|
+
# When an event is received by the forwarding service, it will push directly to this websocket connection
|
15
|
+
#
|
16
|
+
# @param api [Nylas::API] The configured Nylas API client
|
17
|
+
# @param config [Hash] Configuration for the webhook tunnel, including callback functions, region, and
|
18
|
+
# events to subscribe to
|
19
|
+
def self.open_webhook_tunnel(api, config = {})
|
20
|
+
tunnel_id = SecureRandom.uuid
|
21
|
+
triggers = config[:triggers] || WebhookTrigger.constants(false).map { |c| WebhookTrigger.const_get c }
|
22
|
+
region = config[:region] || "us"
|
23
|
+
websocket_domain = "tunnel.nylas.com"
|
24
|
+
callback_domain = "cb.nylas.com"
|
25
|
+
|
26
|
+
EM.run do
|
27
|
+
setup_websocket_client(websocket_domain, api, tunnel_id, region, config)
|
28
|
+
register_webhook_callback(api, callback_domain, tunnel_id, triggers)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Register callback with the Nylas forwarding service which will pass messages to the websocket
|
33
|
+
# @param api [Nylas::API] The configured Nylas API client
|
34
|
+
# @param callback_domain [String] The domain name of the callback
|
35
|
+
# @param tunnel_path [String] The path to the tunnel
|
36
|
+
# @param triggers [Array<WebhookTrigger>] The list of triggers to subscribe to
|
37
|
+
# @return [Nylas::Webhook] The webhook details response from the API
|
38
|
+
def self.register_webhook_callback(api, callback_domain, tunnel_path, triggers)
|
39
|
+
callback_url = "https://#{callback_domain}/#{tunnel_path}"
|
40
|
+
|
41
|
+
api.webhooks.create(
|
42
|
+
callback_url: callback_url,
|
43
|
+
state: WebhookState::ACTIVE,
|
44
|
+
triggers: triggers
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Setup the websocket client and register the callbacks
|
49
|
+
# @param websocket_domain [String] The domain of the websocket to connect to
|
50
|
+
# @param api [Nylas::API] The configured Nylas API client
|
51
|
+
# @param tunnel_id [String] The ID of the tunnel
|
52
|
+
# @param region [String] The Nylas region to configure for
|
53
|
+
# @param config [Hash] The object containing all the callback methods
|
54
|
+
# @return [WebSocket::Client] The configured websocket client
|
55
|
+
def self.setup_websocket_client(websocket_domain, api, tunnel_id, region, config)
|
56
|
+
ws = Faye::WebSocket::Client.new(
|
57
|
+
"wss://#{websocket_domain}",
|
58
|
+
[],
|
59
|
+
{
|
60
|
+
headers: {
|
61
|
+
"Client-Id" => api.client.app_id,
|
62
|
+
"Client-Secret" => api.client.app_secret,
|
63
|
+
"Tunnel-Id" => tunnel_id,
|
64
|
+
"Region" => region
|
65
|
+
}
|
66
|
+
}
|
67
|
+
)
|
68
|
+
|
69
|
+
ws.on :open do |event|
|
70
|
+
config[:on_open].call(event) if callable(config[:on_open])
|
71
|
+
end
|
72
|
+
|
73
|
+
ws.on :close do |close|
|
74
|
+
config[:on_close].call(close) if callable(config[:on_close])
|
75
|
+
EM.stop
|
76
|
+
end
|
77
|
+
|
78
|
+
ws.on :error do |error|
|
79
|
+
config[:on_error].call(error) if callable(config[:on_error])
|
80
|
+
end
|
81
|
+
|
82
|
+
ws.on :message do |message|
|
83
|
+
deltas = parse_deltas_from_message(message)
|
84
|
+
next if deltas.nil?
|
85
|
+
|
86
|
+
deltas.each do |delta|
|
87
|
+
delta = merge_and_create_delta(delta)
|
88
|
+
config[:on_message].call(delta) if callable(config[:on_message])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
ws
|
93
|
+
end
|
94
|
+
|
95
|
+
# Check if the object is a method
|
96
|
+
# @param obj [Any] The object to check
|
97
|
+
# @return [Boolean] True if the object is a method
|
98
|
+
def self.callable(obj)
|
99
|
+
!obj.nil? && obj.respond_to?(:call)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Parse deltas from the message object
|
103
|
+
# @param message [Any] The message object containing the deltas
|
104
|
+
# @return [Hash] The parsed list of deltas
|
105
|
+
def self.parse_deltas_from_message(message)
|
106
|
+
return unless message.data
|
107
|
+
|
108
|
+
json = JSON.parse(message.data)
|
109
|
+
JSON.parse(json["body"])["deltas"]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Clean up and create the delta object
|
113
|
+
# @param delta [Hash] The hash containing the delta attributes from the API
|
114
|
+
# @return [Nylas::Delta] The delta object
|
115
|
+
def self.merge_and_create_delta(delta)
|
116
|
+
object_data = delta.delete("object_data")
|
117
|
+
attributes = object_data.delete("attributes")
|
118
|
+
object_data["object_attributes"] = attributes
|
119
|
+
delta = delta.merge(object_data).transform_keys(&:to_sym)
|
120
|
+
Delta.new(**delta)
|
121
|
+
end
|
122
|
+
|
123
|
+
private_class_method :setup_websocket_client,
|
124
|
+
:callable,
|
125
|
+
:parse_deltas_from_message,
|
126
|
+
:merge_and_create_delta
|
127
|
+
end
|
128
|
+
end
|
data/lib/nylas/version.rb
CHANGED
data/lib/nylas.rb
CHANGED
@@ -116,6 +116,8 @@ require_relative "nylas/scheduler_booking_confirmation"
|
|
116
116
|
require_relative "nylas/native_authentication"
|
117
117
|
|
118
118
|
require_relative "nylas/filter_attributes"
|
119
|
+
|
120
|
+
require_relative "nylas/services/tunnel"
|
119
121
|
# an SDK for interacting with the Nylas API
|
120
122
|
# @see https://docs.nylas.com/reference
|
121
123
|
module Nylas
|
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: 5.
|
4
|
+
version: 5.15.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:
|
11
|
+
date: 2023-02-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,20 +80,6 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 2.7.0
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: tzinfo
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: 2.0.5
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 2.0.5
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: overcommit
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,42 +100,42 @@ dependencies:
|
|
114
100
|
requirements:
|
115
101
|
- - "~>"
|
116
102
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
103
|
+
version: 0.14.1
|
118
104
|
type: :development
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
107
|
requirements:
|
122
108
|
- - "~>"
|
123
109
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.
|
110
|
+
version: 0.14.1
|
125
111
|
- !ruby/object:Gem::Dependency
|
126
112
|
name: pry-nav
|
127
113
|
requirement: !ruby/object:Gem::Requirement
|
128
114
|
requirements:
|
129
115
|
- - "~>"
|
130
116
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
117
|
+
version: 1.0.0
|
132
118
|
type: :development
|
133
119
|
prerelease: false
|
134
120
|
version_requirements: !ruby/object:Gem::Requirement
|
135
121
|
requirements:
|
136
122
|
- - "~>"
|
137
123
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
124
|
+
version: 1.0.0
|
139
125
|
- !ruby/object:Gem::Dependency
|
140
126
|
name: pry-stack_explorer
|
141
127
|
requirement: !ruby/object:Gem::Requirement
|
142
128
|
requirements:
|
143
129
|
- - "~>"
|
144
130
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.4.9
|
131
|
+
version: 0.4.9.3
|
146
132
|
type: :development
|
147
133
|
prerelease: false
|
148
134
|
version_requirements: !ruby/object:Gem::Requirement
|
149
135
|
requirements:
|
150
136
|
- - "~>"
|
151
137
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.4.9
|
138
|
+
version: 0.4.9.3
|
153
139
|
- !ruby/object:Gem::Dependency
|
154
140
|
name: rspec
|
155
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -248,6 +234,34 @@ dependencies:
|
|
248
234
|
- - "~>"
|
249
235
|
- !ruby/object:Gem::Version
|
250
236
|
version: 2.1.0
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: eventmachine
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - "~>"
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: 1.2.7
|
244
|
+
type: :runtime
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - "~>"
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: 1.2.7
|
251
|
+
- !ruby/object:Gem::Dependency
|
252
|
+
name: faye-websocket
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - "~>"
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: 0.11.1
|
258
|
+
type: :runtime
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - "~>"
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: 0.11.1
|
251
265
|
- !ruby/object:Gem::Dependency
|
252
266
|
name: rest-client
|
253
267
|
requirement: !ruby/object:Gem::Requirement
|
@@ -268,6 +282,20 @@ dependencies:
|
|
268
282
|
- - "<"
|
269
283
|
- !ruby/object:Gem::Version
|
270
284
|
version: '3.0'
|
285
|
+
- !ruby/object:Gem::Dependency
|
286
|
+
name: tzinfo
|
287
|
+
requirement: !ruby/object:Gem::Requirement
|
288
|
+
requirements:
|
289
|
+
- - "~>"
|
290
|
+
- !ruby/object:Gem::Version
|
291
|
+
version: 2.0.5
|
292
|
+
type: :runtime
|
293
|
+
prerelease: false
|
294
|
+
version_requirements: !ruby/object:Gem::Requirement
|
295
|
+
requirements:
|
296
|
+
- - "~>"
|
297
|
+
- !ruby/object:Gem::Version
|
298
|
+
version: 2.0.5
|
271
299
|
- !ruby/object:Gem::Dependency
|
272
300
|
name: yajl-ruby
|
273
301
|
requirement: !ruby/object:Gem::Requirement
|
@@ -373,6 +401,7 @@ files:
|
|
373
401
|
- lib/nylas/scheduler_time_slot.rb
|
374
402
|
- lib/nylas/search_collection.rb
|
375
403
|
- lib/nylas/send_grid_verified_status.rb
|
404
|
+
- lib/nylas/services/tunnel.rb
|
376
405
|
- lib/nylas/thread.rb
|
377
406
|
- lib/nylas/time_slot.rb
|
378
407
|
- lib/nylas/time_slot_capacity.rb
|