nylas 2.0.1 → 3.0.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.
data/lib/inbox.rb DELETED
@@ -1,375 +0,0 @@
1
- require 'json'
2
- require 'rest-client'
3
- require 'yajl'
4
- require 'em-http'
5
- require 'ostruct'
6
- require 'active_support/core_ext/hash'
7
-
8
- require 'account'
9
- require 'api_account'
10
- require 'api_thread'
11
- require 'calendar'
12
- require 'account'
13
- require 'message'
14
- require 'draft'
15
- require 'contact'
16
- require 'file'
17
- require 'calendar'
18
- require 'event'
19
- require 'folder'
20
- require 'restful_model'
21
- require 'restful_model_collection'
22
- require 'version'
23
-
24
- module Inbox
25
- Error = Class.new(::StandardError)
26
- AccessDenied = Class.new(Error)
27
- ResourceNotFound = Class.new(Error)
28
- NoAuthToken = Class.new(Error)
29
- UnexpectedAccountAction = Class.new(Error)
30
- UnexpectedResponse = Class.new(Error)
31
- class APIError < Error
32
- attr_accessor :error_type
33
- attr_accessor :server_error
34
-
35
- def initialize(type, error, server_error = nil)
36
- super(error)
37
- self.error_type = type
38
- self.server_error = server_error
39
- end
40
- end
41
- InvalidRequest = Class.new(APIError)
42
- MessageRejected = Class.new(APIError)
43
- SendingQuotaExceeded = Class.new(APIError)
44
- ServiceUnavailable = Class.new(APIError)
45
-
46
- def self.interpret_http_status(result)
47
- # Handle HTTP errors and RestClient errors
48
- raise ResourceNotFound.new if result.code.to_i == 404
49
- raise AccessDenied.new if result.code.to_i == 403
50
- end
51
-
52
- HTTP_CODE_TO_EXCEPTIONS = {
53
- 400 => InvalidRequest,
54
- 402 => MessageRejected,
55
- 403 => AccessDenied,
56
- 404 => ResourceNotFound,
57
- 429 => SendingQuotaExceeded,
58
- 503 => ServiceUnavailable
59
- }.freeze
60
-
61
- def self.http_code_to_exception(http_code)
62
- HTTP_CODE_TO_EXCEPTIONS.fetch(http_code, APIError)
63
- end
64
-
65
- def self.interpret_response(result, result_content, options = {})
66
- # Handle HTTP errors
67
- self.interpret_http_status(result)
68
-
69
- # Handle content expectation errors
70
- raise UnexpectedResponse.new if options[:expected_class] && result_content.empty?
71
- json = options[:result_parsed]? result_content : JSON.parse(result_content)
72
- if json.is_a?(Hash) && (json['type'] == 'api_error' or json['type'] == 'invalid_request_error')
73
- exc = Inbox.http_code_to_exception(result.code.to_i)
74
- raise exc.new(json['type'], json['message'])
75
- end
76
- raise UnexpectedResponse.new(result.msg) if result.is_a?(Net::HTTPClientError)
77
- raise UnexpectedResponse.new if options[:expected_class] && !json.is_a?(options[:expected_class])
78
- json
79
-
80
- rescue JSON::ParserError => e
81
- # Handle parsing errors
82
- raise UnexpectedResponse.new(e.message)
83
- end
84
-
85
-
86
- class API
87
- attr_accessor :api_server
88
- attr_reader :access_token
89
- attr_reader :app_id
90
- attr_reader :app_secret
91
-
92
- def initialize(app_id, app_secret, access_token = nil, api_server = 'https://api.nylas.com',
93
- service_domain = 'api.nylas.com')
94
- raise "When overriding the Inbox API server address, you must include https://" unless api_server.include?('://')
95
- @api_server = api_server
96
- @access_token = access_token
97
- @app_secret = app_secret
98
- @app_id = app_id
99
- @service_domain = service_domain
100
- @version = Inbox::VERSION
101
-
102
- if ::RestClient.before_execution_procs.empty?
103
- ::RestClient.add_before_execution_proc do |req, params|
104
- req.add_field('X-Inbox-API-Wrapper', 'ruby')
105
- req['User-Agent'] = "Nylas Ruby SDK #{@version} - #{RUBY_VERSION}"
106
- end
107
- end
108
- end
109
-
110
- def url_for_path(path)
111
- raise NoAuthToken.new if @access_token == nil and (@app_secret != nil or @app_id != nil)
112
- protocol, domain = @api_server.split('//')
113
- "#{protocol}//#{@access_token}:@#{domain}#{path}"
114
- end
115
-
116
- def url_for_authentication(redirect_uri, login_hint = '', options = {})
117
- params = {
118
- :client_id => @app_id,
119
- :trial => options.fetch(:trial, false),
120
- :response_type => 'code',
121
- :scope => 'email',
122
- :login_hint => login_hint,
123
- :redirect_uri => redirect_uri,
124
- }
125
-
126
- if options.has_key?(:state) then
127
- params[:state] = options[:state]
128
- end
129
-
130
- "https://#{@service_domain}/oauth/authorize?" + params.to_query
131
- end
132
-
133
- def url_for_management
134
- protocol, domain = @api_server.split('//')
135
- accounts_path = "#{protocol}//#{@app_secret}:@#{domain}/a/#{@app_id}/accounts"
136
- end
137
-
138
- def set_access_token(token)
139
- @access_token = token
140
- end
141
-
142
- def token_for_code(code)
143
- data = {
144
- 'client_id' => app_id,
145
- 'client_secret' => app_secret,
146
- 'grant_type' => 'authorization_code',
147
- 'code' => code
148
- }
149
-
150
- ::RestClient.post("https://#{@service_domain}/oauth/token", data) do |response, request, result|
151
- json = Inbox.interpret_response(result, response, :expected_class => Object)
152
- return json['access_token']
153
- end
154
- end
155
-
156
- # API Methods
157
- def threads
158
- @threads ||= RestfulModelCollection.new(Thread, self)
159
- end
160
-
161
- def messages
162
- @messages ||= RestfulModelCollection.new(Message, self)
163
- end
164
-
165
- def files
166
- @files ||= RestfulModelCollection.new(File, self)
167
- end
168
-
169
- def drafts
170
- @drafts ||= RestfulModelCollection.new(Draft, self)
171
- end
172
-
173
- def contacts
174
- @contacts ||= RestfulModelCollection.new(Contact, self)
175
- end
176
-
177
- def calendars
178
- @calendars ||= RestfulModelCollection.new(Calendar, self)
179
- end
180
-
181
- def events
182
- @events ||= RestfulModelCollection.new(Event, self)
183
- end
184
-
185
- def folders
186
- @folders ||= RestfulModelCollection.new(Folder, self)
187
- end
188
-
189
- def labels
190
- @labels ||= RestfulModelCollection.new(Label, self)
191
- end
192
-
193
- def account
194
- path = self.url_for_path("/account")
195
-
196
- RestClient.get(path, {}) do |response,request,result|
197
- json = Inbox.interpret_response(result, response, {:expected_class => Object})
198
- model = APIAccount.new(self)
199
- model.inflate(json)
200
- model
201
- end
202
- end
203
-
204
- def using_hosted_api?
205
- return !@app_id.nil?
206
- end
207
-
208
- def accounts
209
- if self.using_hosted_api?
210
- @accounts ||= ManagementModelCollection.new(Account, self)
211
- else
212
- @accounts ||= RestfulModelCollection.new(APIAccount, self)
213
- end
214
- end
215
-
216
- def latest_cursor
217
- # Get the cursor corresponding to a specific timestamp.
218
- path = self.url_for_path("/delta/latest_cursor")
219
-
220
- cursor = nil
221
-
222
- RestClient.post(path, :content_type => :json) do |response,request,result|
223
- json = Inbox.interpret_response(result, response, {:expected_class => Object})
224
- cursor = json["cursor"]
225
- end
226
-
227
- cursor
228
- end
229
-
230
- OBJECTS_TABLE = {
231
- "account" => Inbox::Account,
232
- "calendar" => Inbox::Calendar,
233
- "draft" => Inbox::Draft,
234
- "thread" => Inbox::Thread,
235
- "contact" => Inbox::Contact,
236
- "event" => Inbox::Event,
237
- "file" => Inbox::File,
238
- "message" => Inbox::Message,
239
- "folder" => Inbox::Folder,
240
- "label" => Inbox::Label,
241
- }
242
-
243
- # It's possible to ask the API to expand objects.
244
- # In this case, we do the right thing and return
245
- # an expanded object.
246
- EXPANDED_OBJECTS_TABLE = {
247
- "message" => Inbox::ExpandedMessage,
248
- }
249
-
250
- def _build_exclude_types(exclude_types)
251
- exclude_string = "&exclude_types="
252
-
253
- exclude_types.each do |value|
254
- count = 0
255
- if OBJECTS_TABLE.has_value?(value)
256
- param_name = OBJECTS_TABLE.key(value)
257
- exclude_string += "#{param_name},"
258
- end
259
- end
260
-
261
- exclude_string = exclude_string[0..-2]
262
- end
263
-
264
- def deltas(cursor, exclude_types=[], expanded_view=false)
265
- return enum_for(:deltas, cursor, exclude_types, expanded_view) unless block_given?
266
-
267
- exclude_string = ""
268
-
269
- if exclude_types.any?
270
- exclude_string = _build_exclude_types(exclude_types)
271
- end
272
-
273
- # loop and yield deltas until we've come to the end.
274
- loop do
275
- path = self.url_for_path("/delta?exclude_folders=false&cursor=#{cursor}#{exclude_string}")
276
- if expanded_view
277
- path += '&view=expanded'
278
- end
279
-
280
- json = nil
281
-
282
- RestClient.get(path) do |response,request,result|
283
- json = Inbox.interpret_response(result, response, {:expected_class => Object})
284
- end
285
-
286
- start_cursor = json["cursor_start"]
287
- end_cursor = json["cursor_end"]
288
-
289
- json["deltas"].each do |delta|
290
- if not OBJECTS_TABLE.has_key?(delta['object'])
291
- next
292
- end
293
-
294
- cls = OBJECTS_TABLE[delta['object']]
295
- if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
296
- cls = EXPANDED_OBJECTS_TABLE[delta['object']]
297
- end
298
-
299
- obj = cls.new(self)
300
-
301
- case delta["event"]
302
- when 'create', 'modify'
303
- obj.inflate(delta['attributes'])
304
- obj.cursor = delta["cursor"]
305
- yield delta["event"], obj
306
- when 'delete'
307
- obj.id = delta["id"]
308
- obj.cursor = delta["cursor"]
309
- yield delta["event"], obj
310
- end
311
- end
312
-
313
- break if start_cursor == end_cursor
314
- cursor = end_cursor
315
- end
316
- end
317
-
318
- def delta_stream(cursor, exclude_types=[], timeout=0, expanded_view=false)
319
- raise 'Please provide a block for receiving the delta objects' if !block_given?
320
-
321
- exclude_string = ""
322
-
323
- if exclude_types.any?
324
- exclude_string = _build_exclude_types(exclude_types)
325
- end
326
-
327
- # loop and yield deltas indefinitely.
328
- path = self.url_for_path("/delta/streaming?exclude_folders=false&cursor=#{cursor}#{exclude_string}")
329
- if expanded_view
330
- path += '&view=expanded'
331
- end
332
-
333
- parser = Yajl::Parser.new(:symbolize_keys => false)
334
- parser.on_parse_complete = proc do |data|
335
- delta = Inbox.interpret_response(OpenStruct.new(:code => '200'), data, {:expected_class => Object, :result_parsed => true})
336
-
337
- if not OBJECTS_TABLE.has_key?(delta['object'])
338
- next
339
- end
340
-
341
- cls = OBJECTS_TABLE[delta['object']]
342
- if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
343
- cls = EXPANDED_OBJECTS_TABLE[delta['object']]
344
- end
345
-
346
- obj = cls.new(self)
347
-
348
- case delta["event"]
349
- when 'create', 'modify'
350
- obj.inflate(delta['attributes'])
351
- obj.cursor = delta["cursor"]
352
- yield delta["event"], obj
353
- when 'delete'
354
- obj.id = delta["id"]
355
- obj.cursor = delta["cursor"]
356
- yield delta["event"], obj
357
- end
358
- end
359
-
360
- http = EventMachine::HttpRequest.new(path, :connect_timeout => 0, :inactivity_timeout => timeout).get(:keepalive => true)
361
-
362
- # set a callback on the HTTP stream that parses incoming chunks as they come in
363
- http.stream do |chunk|
364
- parser << chunk
365
- end
366
-
367
- http.errback do
368
- raise UnexpectedResponse.new http.error
369
- end
370
-
371
- end
372
- end
373
- end
374
-
375
- Nylas = Inbox.clone