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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c270504996973ab93d5c1cac213a194f776fb27302010be6eee1a41570204ff
4
- data.tar.gz: 01a2bcc7473080a9fad5c39bd1851487ed1d20f81fab68277a37c6fd5e31c195
3
+ metadata.gz: b432f464fcd9fec327a4aa014c499cadcf34aa8c4626ae1585d32b4aff9c7b21
4
+ data.tar.gz: 5c62a00ba6e2f1de70346aa7fe4cdb7d7a129c6b91c76a35c6349e9597ab51c0
5
5
  SHA512:
6
- metadata.gz: '0485d5539c28e6626cef26ee3b34a3f61eab8c0436f6abddb017ead7034b644efac6502f32fc40236446d04fa7562d615c1fcfc732afbeeee852118874281492'
7
- data.tar.gz: 63229ee0942e29703d6a4a2fd6feafd3f7ce639f13e5fd7387c01c195cd549e6b6cd6a6d931fa2ccf98871c1929b4c3c7cacfe46e1fa3d22ed454c8978f8810f
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
- client_id: app_id,
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(APIError)
105
+ SendingQuotaExceeded = Class.new(RateLimitError)
59
106
  ServiceUnavailable = Class.new(APIError)
60
107
  BadGateway = Class.new(APIError)
61
108
  InternalError = Class.new(APIError)
@@ -189,8 +189,7 @@ module Nylas
189
189
  def parse_response(response)
190
190
  return response if response.is_a?(Enumerable)
191
191
 
192
- json = StringIO.new(response)
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
- raise exception.new(http_code, response) unless response.is_a?(Hash)
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
- raise exception.new(
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nylas
4
- VERSION = "5.13.0"
4
+ VERSION = "5.15.0"
5
5
  end
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.13.0
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: 2022-10-21 00:00:00.000000000 Z
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.10.4
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.10.4
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.2.4
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.2.4
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