nylas 3.2.0 → 4.0.0.rc2
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.rb +33 -380
- data/lib/nylas/api.rb +43 -0
- data/lib/nylas/collection.rb +110 -0
- data/lib/nylas/constraints.rb +46 -0
- data/lib/nylas/contact.rb +30 -0
- data/lib/nylas/current_account.rb +23 -0
- data/lib/nylas/email_address.rb +14 -0
- data/lib/nylas/errors.rb +36 -0
- data/lib/nylas/http_client.rb +135 -0
- data/lib/nylas/im_address.rb +14 -0
- data/lib/nylas/logging.rb +41 -0
- data/lib/nylas/model.rb +92 -0
- data/lib/nylas/model/attributable.rb +57 -0
- data/lib/nylas/model/attribute_definition.rb +19 -0
- data/lib/nylas/model/attributes.rb +47 -0
- data/lib/nylas/model/list_attribute_definition.rb +28 -0
- data/lib/nylas/nylas_date.rb +20 -0
- data/lib/nylas/phone_number.rb +14 -0
- data/lib/nylas/physical_address.rb +19 -0
- data/lib/nylas/registry.rb +37 -0
- data/lib/nylas/types.rb +83 -0
- data/lib/nylas/version.rb +3 -0
- data/lib/nylas/web_page.rb +14 -0
- metadata +144 -98
- data/lib/account.rb +0 -32
- data/lib/api_account.rb +0 -22
- data/lib/api_thread.rb +0 -85
- data/lib/calendar.rb +0 -16
- data/lib/contact.rb +0 -10
- data/lib/draft.rb +0 -50
- data/lib/event.rb +0 -47
- data/lib/expanded_message.rb +0 -20
- data/lib/file.rb +0 -40
- data/lib/folder.rb +0 -12
- data/lib/label.rb +0 -4
- data/lib/message.rb +0 -102
- data/lib/mixins.rb +0 -26
- data/lib/parameters.rb +0 -26
- data/lib/restful_model.rb +0 -82
- data/lib/restful_model_collection.rb +0 -160
- data/lib/time_attr_accessor.rb +0 -12
- data/lib/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fd4eb7f8982e3da95740a92b4ff5d1307d22bc7
|
4
|
+
data.tar.gz: 3806c9465a159c517fc71165570a0c787751f79f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1841957b48b50212c6026428401306cadf9c83fddb109e5567c0cb5bb5aa187e2a6383c804b546ffe1082d7bca395e73a2f63f035d20ab42a516fd44e831474
|
7
|
+
data.tar.gz: a21d29a3a95331f855b5f4a312183fea5da5dcf73de7546df452d4723b080838bdc26fe376eec1455b98b108e3d92c0e8a2ec6742e05efaa1e80d72f5ff89d61
|
data/lib/nylas.rb
CHANGED
@@ -1,389 +1,42 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require 'yajl'
|
4
|
-
require 'em-http'
|
1
|
+
require "json"
|
2
|
+
require "rest-client"
|
5
3
|
|
4
|
+
require "ostruct"
|
5
|
+
require "forwardable"
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
require_relative "nylas/version"
|
8
|
+
require_relative "nylas/errors"
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
require 'account'
|
15
|
-
require 'message'
|
16
|
-
require 'expanded_message'
|
17
|
-
require 'draft'
|
18
|
-
require 'contact'
|
19
|
-
require 'file'
|
20
|
-
require 'calendar'
|
21
|
-
require 'event'
|
22
|
-
require 'folder'
|
23
|
-
require 'restful_model'
|
24
|
-
require 'restful_model_collection'
|
25
|
-
require 'version'
|
10
|
+
require_relative "nylas/logging"
|
11
|
+
require_relative "nylas/registry"
|
12
|
+
require_relative "nylas/types"
|
13
|
+
require_relative "nylas/constraints"
|
26
14
|
|
27
|
-
|
28
|
-
|
29
|
-
NoAuthToken = Class.new(Error)
|
30
|
-
UnexpectedAccountAction = Class.new(Error)
|
31
|
-
UnexpectedResponse = Class.new(Error)
|
32
|
-
class APIError < Error
|
33
|
-
attr_accessor :type
|
34
|
-
attr_accessor :message
|
35
|
-
attr_accessor :server_error
|
36
|
-
|
37
|
-
def initialize(type, message, server_error = nil)
|
38
|
-
super(message)
|
39
|
-
self.type = type
|
40
|
-
self.message = message
|
41
|
-
self.server_error = server_error
|
42
|
-
end
|
43
|
-
end
|
44
|
-
AccessDenied = Class.new(APIError)
|
45
|
-
ResourceNotFound = Class.new(APIError)
|
46
|
-
InvalidRequest = Class.new(APIError)
|
47
|
-
MessageRejected = Class.new(APIError)
|
48
|
-
SendingQuotaExceeded = Class.new(APIError)
|
49
|
-
ServiceUnavailable = Class.new(APIError)
|
50
|
-
BadGateway = Class.new(APIError)
|
51
|
-
InternalError = Class.new(APIError)
|
52
|
-
MailProviderError = Class.new(APIError)
|
53
|
-
|
54
|
-
HTTP_CODE_TO_EXCEPTIONS = {
|
55
|
-
400 => InvalidRequest,
|
56
|
-
402 => MessageRejected,
|
57
|
-
403 => AccessDenied,
|
58
|
-
404 => ResourceNotFound,
|
59
|
-
422 => MailProviderError,
|
60
|
-
429 => SendingQuotaExceeded,
|
61
|
-
500 => InternalError,
|
62
|
-
502 => BadGateway,
|
63
|
-
503 => ServiceUnavailable,
|
64
|
-
}.freeze
|
65
|
-
|
66
|
-
def self.http_code_to_exception(http_code)
|
67
|
-
HTTP_CODE_TO_EXCEPTIONS.fetch(http_code, APIError)
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.interpret_response(result, result_content, options = {})
|
71
|
-
|
72
|
-
# We expected a certain kind of object, but the API didn't return anything
|
73
|
-
raise UnexpectedResponse.new if options[:expected_class] && result_content.empty?
|
74
|
-
|
75
|
-
# If it's already parsed, or if we've received an actual raw payload on success, don't parse
|
76
|
-
if options[:result_parsed] || (options[:raw_response] && result.code.to_i == 200)
|
77
|
-
response = result_content
|
78
|
-
else
|
79
|
-
response = JSON.parse(result_content)
|
80
|
-
end
|
81
|
-
|
82
|
-
if result.code.to_i != 200
|
83
|
-
exc = Nylas.http_code_to_exception(result.code.to_i)
|
84
|
-
if response.is_a?(Hash)
|
85
|
-
raise exc.new(response['type'], response['message'], response.fetch('server_error', nil))
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
raise UnexpectedResponse.new(result.msg) if result.is_a?(Net::HTTPClientError)
|
90
|
-
raise UnexpectedResponse.new if options[:expected_class] && !response.is_a?(options[:expected_class])
|
91
|
-
response
|
92
|
-
|
93
|
-
rescue JSON::ParserError => e
|
94
|
-
# Handle parsing errors
|
95
|
-
raise UnexpectedResponse.new(e.message)
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
class API
|
100
|
-
attr_accessor :api_server
|
101
|
-
attr_reader :access_token
|
102
|
-
attr_reader :app_id
|
103
|
-
attr_reader :app_secret
|
104
|
-
|
105
|
-
def initialize(app_id, app_secret, access_token = nil, api_server = 'https://api.nylas.com',
|
106
|
-
service_domain = 'api.nylas.com')
|
107
|
-
raise "When overriding the Nylas API server address, you must include https://" unless api_server.include?('://')
|
108
|
-
@api_server = api_server
|
109
|
-
@access_token = access_token
|
110
|
-
@app_secret = app_secret
|
111
|
-
@app_id = app_id
|
112
|
-
@service_domain = service_domain
|
113
|
-
@version = Nylas::VERSION
|
114
|
-
|
115
|
-
if ::RestClient.before_execution_procs.empty?
|
116
|
-
::RestClient.add_before_execution_proc do |req, params|
|
117
|
-
req.add_field('X-Nylas-API-Wrapper', 'ruby')
|
118
|
-
req['User-Agent'] = "Nylas Ruby SDK #{@version} - #{RUBY_VERSION}"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def url_for_path(path)
|
124
|
-
raise NoAuthToken.new if @access_token == nil and (@app_secret != nil or @app_id != nil)
|
125
|
-
protocol, domain = @api_server.split('//')
|
126
|
-
"#{protocol}//#{@access_token}:@#{domain}#{path}"
|
127
|
-
end
|
128
|
-
|
129
|
-
def url_for_authentication(redirect_uri, login_hint = '', options = {})
|
130
|
-
params = {
|
131
|
-
:client_id => @app_id,
|
132
|
-
:trial => options.fetch(:trial, false),
|
133
|
-
:response_type => 'code',
|
134
|
-
:scope => 'email',
|
135
|
-
:login_hint => login_hint,
|
136
|
-
:redirect_uri => redirect_uri,
|
137
|
-
}
|
138
|
-
|
139
|
-
if options.has_key?(:state) then
|
140
|
-
params[:state] = options[:state]
|
141
|
-
end
|
142
|
-
|
143
|
-
"https://#{@service_domain}/oauth/authorize?" + params.to_query
|
144
|
-
end
|
145
|
-
|
146
|
-
def url_for_management
|
147
|
-
protocol, domain = @api_server.split('//')
|
148
|
-
accounts_path = "#{protocol}//#{@app_secret}:@#{domain}/a/#{@app_id}/accounts"
|
149
|
-
end
|
150
|
-
|
151
|
-
def set_access_token(token)
|
152
|
-
@access_token = token
|
153
|
-
end
|
154
|
-
|
155
|
-
def token_for_code(code)
|
156
|
-
data = {
|
157
|
-
'client_id' => app_id,
|
158
|
-
'client_secret' => app_secret,
|
159
|
-
'grant_type' => 'authorization_code',
|
160
|
-
'code' => code
|
161
|
-
}
|
162
|
-
|
163
|
-
::RestClient.post("https://#{@service_domain}/oauth/token", data) do |response, request, result|
|
164
|
-
json = Nylas.interpret_response(result, response, :expected_class => Object)
|
165
|
-
return json['access_token']
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# API Methods
|
170
|
-
def threads
|
171
|
-
@threads ||= RestfulModelCollection.new(Thread, self)
|
172
|
-
end
|
173
|
-
|
174
|
-
def messages(expanded: false)
|
175
|
-
@messages ||= Hash.new do |h, is_expanded|
|
176
|
-
h[is_expanded] = \
|
177
|
-
if is_expanded
|
178
|
-
RestfulModelCollection.new(ExpandedMessage, self, view: 'expanded')
|
179
|
-
else
|
180
|
-
RestfulModelCollection.new(Message, self)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
@messages[expanded]
|
184
|
-
end
|
185
|
-
|
186
|
-
def files
|
187
|
-
@files ||= RestfulModelCollection.new(File, self)
|
188
|
-
end
|
189
|
-
|
190
|
-
def drafts
|
191
|
-
@drafts ||= RestfulModelCollection.new(Draft, self)
|
192
|
-
end
|
193
|
-
|
194
|
-
def contacts
|
195
|
-
@contacts ||= RestfulModelCollection.new(Contact, self)
|
196
|
-
end
|
197
|
-
|
198
|
-
def calendars
|
199
|
-
@calendars ||= RestfulModelCollection.new(Calendar, self)
|
200
|
-
end
|
201
|
-
|
202
|
-
def events
|
203
|
-
@events ||= RestfulModelCollection.new(Event, self)
|
204
|
-
end
|
205
|
-
|
206
|
-
def folders
|
207
|
-
@folders ||= RestfulModelCollection.new(Folder, self)
|
208
|
-
end
|
209
|
-
|
210
|
-
def labels
|
211
|
-
@labels ||= RestfulModelCollection.new(Label, self)
|
212
|
-
end
|
213
|
-
|
214
|
-
def account
|
215
|
-
path = self.url_for_path("/account")
|
15
|
+
require_relative "nylas/collection"
|
16
|
+
require_relative "nylas/model"
|
216
17
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
18
|
+
# Attribute types supported by the API
|
19
|
+
require_relative "nylas/email_address"
|
20
|
+
require_relative "nylas/im_address"
|
21
|
+
require_relative "nylas/physical_address"
|
22
|
+
require_relative "nylas/phone_number"
|
23
|
+
require_relative "nylas/web_page"
|
24
|
+
require_relative "nylas/nylas_date"
|
224
25
|
|
225
|
-
|
226
|
-
|
227
|
-
|
26
|
+
# Models supported by the API
|
27
|
+
require_relative "nylas/contact"
|
28
|
+
require_relative "nylas/current_account"
|
228
29
|
|
229
|
-
|
230
|
-
|
231
|
-
@accounts ||= ManagementModelCollection.new(Account, self)
|
232
|
-
else
|
233
|
-
@accounts ||= RestfulModelCollection.new(APIAccount, self)
|
234
|
-
end
|
235
|
-
end
|
30
|
+
require_relative "nylas/http_client"
|
31
|
+
require_relative "nylas/api"
|
236
32
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
end
|
247
|
-
|
248
|
-
cursor
|
249
|
-
end
|
250
|
-
|
251
|
-
OBJECTS_TABLE = {
|
252
|
-
"account" => Nylas::Account,
|
253
|
-
"calendar" => Nylas::Calendar,
|
254
|
-
"draft" => Nylas::Draft,
|
255
|
-
"thread" => Nylas::Thread,
|
256
|
-
"contact" => Nylas::Contact,
|
257
|
-
"event" => Nylas::Event,
|
258
|
-
"file" => Nylas::File,
|
259
|
-
"message" => Nylas::Message,
|
260
|
-
"folder" => Nylas::Folder,
|
261
|
-
"label" => Nylas::Label,
|
262
|
-
}
|
263
|
-
|
264
|
-
# It's possible to ask the API to expand objects.
|
265
|
-
# In this case, we do the right thing and return
|
266
|
-
# an expanded object.
|
267
|
-
EXPANDED_OBJECTS_TABLE = {
|
268
|
-
"message" => Nylas::ExpandedMessage,
|
269
|
-
}
|
270
|
-
|
271
|
-
def _build_types_filter_string(filter, types)
|
272
|
-
return "" if types.empty?
|
273
|
-
query_string = "&#{filter}_types="
|
274
|
-
|
275
|
-
types.each do |value|
|
276
|
-
count = 0
|
277
|
-
if OBJECTS_TABLE.has_value?(value)
|
278
|
-
param_name = OBJECTS_TABLE.key(value)
|
279
|
-
query_string += "#{param_name},"
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
query_string = query_string[0..-2]
|
284
|
-
end
|
285
|
-
|
286
|
-
def deltas(cursor, exclude_types=[], expanded_view=false, include_types=[])
|
287
|
-
return enum_for(:deltas, cursor, exclude_types, expanded_view, include_types) unless block_given?
|
288
|
-
|
289
|
-
exclude_string = _build_types_filter_string(:exclude, exclude_types)
|
290
|
-
include_string = _build_types_filter_string(:include, include_types)
|
291
|
-
|
292
|
-
# loop and yield deltas until we've come to the end.
|
293
|
-
loop do
|
294
|
-
path = self.url_for_path("/delta?exclude_folders=false&cursor=#{cursor}#{exclude_string}#{include_string}")
|
295
|
-
if expanded_view
|
296
|
-
path += '&view=expanded'
|
297
|
-
end
|
298
|
-
|
299
|
-
json = nil
|
300
|
-
|
301
|
-
RestClient.get(path) do |response,request,result|
|
302
|
-
json = Nylas.interpret_response(result, response, {:expected_class => Object})
|
303
|
-
end
|
304
|
-
|
305
|
-
start_cursor = json["cursor_start"]
|
306
|
-
end_cursor = json["cursor_end"]
|
307
|
-
|
308
|
-
json["deltas"].each do |delta|
|
309
|
-
if not OBJECTS_TABLE.has_key?(delta['object'])
|
310
|
-
next
|
311
|
-
end
|
312
|
-
|
313
|
-
cls = OBJECTS_TABLE[delta['object']]
|
314
|
-
if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
|
315
|
-
cls = EXPANDED_OBJECTS_TABLE[delta['object']]
|
316
|
-
end
|
317
|
-
|
318
|
-
obj = cls.new(self)
|
319
|
-
|
320
|
-
case delta["event"]
|
321
|
-
when 'create', 'modify'
|
322
|
-
obj.inflate(delta['attributes'])
|
323
|
-
obj.cursor = delta["cursor"]
|
324
|
-
yield delta["event"], obj
|
325
|
-
when 'delete'
|
326
|
-
obj.id = delta["id"]
|
327
|
-
obj.cursor = delta["cursor"]
|
328
|
-
yield delta["event"], obj
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
break if start_cursor == end_cursor
|
333
|
-
cursor = end_cursor
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
def delta_stream(cursor, exclude_types=[], timeout=0, expanded_view=false, include_types=[])
|
338
|
-
raise 'Please provide a block for receiving the delta objects' if !block_given?
|
339
|
-
|
340
|
-
exclude_string = _build_types_filter_string(:exclude, exclude_types)
|
341
|
-
include_string = _build_types_filter_string(:include, include_types)
|
342
|
-
|
343
|
-
# loop and yield deltas indefinitely.
|
344
|
-
path = self.url_for_path("/delta/streaming?exclude_folders=false&cursor=#{cursor}#{exclude_string}#{include_string}")
|
345
|
-
if expanded_view
|
346
|
-
path += '&view=expanded'
|
347
|
-
end
|
348
|
-
|
349
|
-
parser = Yajl::Parser.new(:symbolize_keys => false)
|
350
|
-
parser.on_parse_complete = proc do |data|
|
351
|
-
delta = Nylas.interpret_response(OpenStruct.new(:code => '200'), data, {:expected_class => Object, :result_parsed => true})
|
352
|
-
|
353
|
-
if not OBJECTS_TABLE.has_key?(delta['object'])
|
354
|
-
next
|
355
|
-
end
|
356
|
-
|
357
|
-
cls = OBJECTS_TABLE[delta['object']]
|
358
|
-
if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
|
359
|
-
cls = EXPANDED_OBJECTS_TABLE[delta['object']]
|
360
|
-
end
|
361
|
-
|
362
|
-
obj = cls.new(self)
|
363
|
-
|
364
|
-
case delta["event"]
|
365
|
-
when 'create', 'modify'
|
366
|
-
obj.inflate(delta['attributes'])
|
367
|
-
obj.cursor = delta["cursor"]
|
368
|
-
yield delta["event"], obj
|
369
|
-
when 'delete'
|
370
|
-
obj.id = delta["id"]
|
371
|
-
obj.cursor = delta["cursor"]
|
372
|
-
yield delta["event"], obj
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
http = EventMachine::HttpRequest.new(path, :connect_timeout => 0, :inactivity_timeout => timeout).get(:keepalive => true)
|
377
|
-
|
378
|
-
# set a callback on the HTTP stream that parses incoming chunks as they come in
|
379
|
-
http.stream do |chunk|
|
380
|
-
parser << chunk
|
381
|
-
end
|
382
|
-
|
383
|
-
http.errback do
|
384
|
-
raise UnexpectedResponse.new http.error
|
385
|
-
end
|
386
|
-
|
387
|
-
end
|
388
|
-
end
|
33
|
+
# an SDK for interacting with the Nylas API
|
34
|
+
# @see https://docs.nylas.com/reference
|
35
|
+
module Nylas
|
36
|
+
Types.registry[:email_address] = EmailAddressType.new
|
37
|
+
Types.registry[:im_address] = IMAddressType.new
|
38
|
+
Types.registry[:physical_address] = PhysicalAddressType.new
|
39
|
+
Types.registry[:phone_number] = PhoneNumberType.new
|
40
|
+
Types.registry[:web_page] = WebPageType.new
|
41
|
+
Types.registry[:nylas_date] = NylasDateType.new
|
389
42
|
end
|
data/lib/nylas/api.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Nylas
|
2
|
+
# Methods to retrieve data from the Nylas API as Ruby objects
|
3
|
+
class API
|
4
|
+
attr_accessor :client
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :client, :execute, :get, :post, :put, :delete
|
7
|
+
|
8
|
+
include Logging
|
9
|
+
|
10
|
+
# @param client [HttpClient] Http Client to use for retrieving data
|
11
|
+
# @param app_id [String] Your application id from the Nylas Dashboard
|
12
|
+
# @param app_secret [String] Your application secret from the Nylas Dashboard
|
13
|
+
# @param access_token [String] (Optional) Your users access token.
|
14
|
+
# @param api_server [String] (Optional) Which Nylas API Server to connect to. Only change this if
|
15
|
+
# you're using a self-hosted Nylas instance.
|
16
|
+
# @param service_domain [String] (Optional) Host you are authenticating OAuth against.
|
17
|
+
# @return [Nylas::API]
|
18
|
+
# rubocop:disable Metrics/ParameterLists
|
19
|
+
def initialize(client: nil, app_id: nil, app_secret: nil, access_token: nil,
|
20
|
+
api_server: "https://api.nylas.com", service_domain: "api.nylas.com")
|
21
|
+
self.client = client || HttpClient.new(app_id: app_id, app_secret: app_secret,
|
22
|
+
access_token: access_token, api_server: api_server,
|
23
|
+
service_domain: service_domain)
|
24
|
+
end
|
25
|
+
# rubocop:enable Metrics/ParameterLists
|
26
|
+
|
27
|
+
# @return [Collection<Contact>] A queryable collection of Contacts
|
28
|
+
def contacts
|
29
|
+
@contacts ||= Collection.new(model: Contact, api: self)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [CurrentAccount] The account details for whomevers access token is set
|
33
|
+
def current_account
|
34
|
+
prevent_calling_if_missing_access_token(:current_account)
|
35
|
+
CurrentAccount.from_hash(client.execute(method: :get, path: "/account"), api: self)
|
36
|
+
end
|
37
|
+
|
38
|
+
private def prevent_calling_if_missing_access_token(method_name)
|
39
|
+
return if client.access_token && !client.access_token.empty?
|
40
|
+
raise NoAuthToken, method_name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|