nylas 2.0.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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