nylas 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 20d79e414ea5a408e953d47d9c71884849c1f571
4
- data.tar.gz: 21d652e65392272e0ae01f3778c051255870c760
3
+ metadata.gz: 075a309a5e7a82e98ece07a34dcc7336f515963a
4
+ data.tar.gz: 43e4a1a29aae4b27185cdde900aeb1c50d9d99cb
5
5
  SHA512:
6
- metadata.gz: 086f229fcb101679b211ecfaa3b249a86cff2eee6a268c005532e0399ebd9cb014b963bf6d7018d9702db935ee2037101bc1bb0c711db3b933dea5cb1cae06c5
7
- data.tar.gz: 5838bf2c6bcc05b20642e15f2b7f93ddf788c71248d595e1b01140bc239cf5a3acd3661a474716bc7103dc52497188c69e3d0d19ff151fc6909dfe637942200e
6
+ metadata.gz: 214c708d9f7d6e0d36fcd370fac22543d3e3f0091471ad3562e90ed90e533756c81b9327dfb50a48be23bf7e5a6bc179c13cb086caddaab54a34f7d26537fbaa
7
+ data.tar.gz: 1cb18be9cc49539161a8bf54815f7e5ef16a548df939c416df6b8af31d0bfb4c9c62ac32583863399f1c09046ecb08af4391e79bb073bc932f9f9f8d7f66af81
data/README.md CHANGED
@@ -105,10 +105,10 @@ If you're using the open-source version of the Nylas Sync Engine or have fewer t
105
105
  # Query the status of every account linked to the app
106
106
  nylas = Nylas::API.new(config.nylas_app_id, config.nylas_app_secret, nylas_token)
107
107
  accounts = nylas.accounts
108
- accounts.each { |a| [a.account_id, a.sync_state] } # Available fields are: account_id, sync_state, trial, trial_expires, billing_state and namespace_id. See lib/account.rb for more details.
108
+ accounts.each { |a| [a.account_id, a.sync_state] } # Available fields are: account_id, sync_state, trial, trial_expires and billing_state. See lib/account.rb for more details.
109
109
  ```
110
110
 
111
- ### Fetching Namespaces
111
+ ### Fetching Accounts
112
112
 
113
113
  ```ruby
114
114
  nylas = Nylas::API.new(config.nylas_app_id, config.nylas_app_secret, nylas_token)
@@ -206,6 +206,9 @@ end
206
206
  # Create a new file
207
207
  file = nylas.files.build(:file => File.new("./public/favicon.ico", 'rb'))
208
208
  file.save!
209
+
210
+ # Download a file's contents
211
+ content = file.download
209
212
  ```
210
213
 
211
214
  ### Working with Labels/Folders
@@ -249,6 +252,19 @@ messages = nylas.messages.where(:to => 'ben@nylas.com`).all
249
252
 
250
253
  The `where` method accepts a hash of filters, as documented in the [Filters Documentation](https://nylas.com/docs/platform#filters).
251
254
 
255
+ ### Getting a message's Message-Id, References and In-Reply-To headers
256
+
257
+ If you've building your own threading solution, you'll probably need access to a handful of headers like
258
+ `Message-Id`, `In-Reply-To` and `References`. Here's how to access them:
259
+
260
+ ```ruby
261
+ msg = nylas.messages.first
262
+ expanded_message = msg.expanded
263
+ puts expanded_message.message_id
264
+ puts expanded_message.references
265
+ puts expanded_message.in_reply_to
266
+ ```
267
+
252
268
  ### Getting the raw contents of a message
253
269
 
254
270
  It's possible to access the unprocessed contents of a message using the raw method:
@@ -279,6 +295,17 @@ draft.save!
279
295
 
280
296
  # Send the draft.
281
297
  draft.send!
298
+
299
+ # Sometimes sending isn't possible --- handle the exception and
300
+ # print the error message returned by the SMTP server:
301
+ begin
302
+ draft.send!
303
+ rescue Nylas::APIError => e
304
+ puts "Failed with error: #{e.message}"
305
+ if not e.server_error.nil?
306
+ puts "The SMTP server replied: #{e.server_error}"
307
+ end
308
+ end
282
309
  ```
283
310
 
284
311
  ### Creating an event
@@ -379,6 +406,22 @@ if event == 'create' or event == 'modify'
379
406
  end
380
407
  ```
381
408
 
409
+ ### Expand Messages from the Delta stream
410
+
411
+ It's possible to ask the Deltas and delta stream API to return [expanded messages](https://nylas.com/docs/platform#expanded_message_view) directly:
412
+ ````ruby
413
+ nylas.deltas(cursor, exclude=[Nylas::Contact,
414
+ Nylas::Event,
415
+ Nylas::File,
416
+ Nylas::Tag,
417
+ Nylas::Thread], expanded_view=true) do |event, object|
418
+ if event == 'create' or event == 'modify'
419
+ if obj.is_a?(Inbox::Message)
420
+ puts obj.subject
421
+ puts obj.message_id
422
+ end
423
+ end
424
+ ```
382
425
 
383
426
  ### Handling Errors
384
427
  The Nylas API uses conventional HTTP response codes to indicate success or failure of an API request. The ruby gem raises these as native exceptions.
@@ -37,11 +37,11 @@ module Inbox
37
37
  end
38
38
 
39
39
  def messages
40
- @messages ||= RestfulModelCollection.new(Message, @_api, @namespace_id, {:thread_id=>@id})
40
+ @messages ||= RestfulModelCollection.new(Message, @_api, {:thread_id=>@id})
41
41
  end
42
42
 
43
43
  def drafts
44
- @drafts ||= RestfulModelCollection.new(Draft, @_api, @namespace_id, {:thread_id=>@id})
44
+ @drafts ||= RestfulModelCollection.new(Draft, @_api, {:thread_id=>@id})
45
45
  end
46
46
 
47
47
  def update_tags!(tags_to_add = [], tags_to_remove = [])
@@ -9,7 +9,7 @@ module Inbox
9
9
  parameter :read_only
10
10
 
11
11
  def events
12
- @events ||= RestfulModelCollection.new(Event, @_api, @namespace_id, {:calendar_id=>@id})
12
+ @events ||= RestfulModelCollection.new(Event, @_api, {:calendar_id=>@id})
13
13
  end
14
14
 
15
15
  end
@@ -6,9 +6,11 @@ module Inbox
6
6
  parameter :thread_id
7
7
  parameter :version
8
8
  parameter :reply_to_message_id
9
+ parameter :file_ids
9
10
 
10
11
  def attach(file)
11
12
  file.save! unless file.id
13
+ @file_ids ||= []
12
14
  @file_ids.push(file.id)
13
15
  end
14
16
 
@@ -30,7 +32,20 @@ module Inbox
30
32
  end
31
33
 
32
34
  ::RestClient.post(url, data.to_json, :content_type => :json) do |response, request, result|
33
- json = Inbox.interpret_response(result, response, :expected_class => Object)
35
+
36
+ # This is mostly lifted from Inbox#interpret_response. We're not using the original function
37
+ # because we need to pass an additional error message to the Exception constructor.
38
+ Inbox.interpret_http_status(result)
39
+ json = JSON.parse(response)
40
+ if json.is_a?(Hash) && (json['type'] == 'api_error' or json['type'] == 'invalid_request_error')
41
+ exc = Inbox.http_code_to_exception(result.code.to_i)
42
+ exc_type = json['type']
43
+ exc_message = json['message']
44
+ exc_server_error = json['server_error']
45
+ raise exc.new(exc_type, exc_message, server_error=exc_server_error)
46
+ end
47
+ raise UnexpectedResponse.new(result.msg) if result.is_a?(Net::HTTPClientError)
48
+
34
49
  self.inflate(json)
35
50
  end
36
51
 
@@ -5,6 +5,7 @@ module Inbox
5
5
 
6
6
  parameter :size
7
7
  parameter :filename
8
+ parameter :content_id
8
9
  parameter :content_type
9
10
  parameter :is_embedded
10
11
  parameter :message_id
@@ -26,6 +27,14 @@ module Inbox
26
27
  self
27
28
  end
28
29
 
30
+ def download
31
+ download_url = self.url('download')
32
+ ::RestClient.get(download_url) do |response, request, result|
33
+ Inbox.interpret_http_status(result)
34
+ response
35
+ end
36
+ end
37
+
29
38
  end
30
39
  end
31
40
 
@@ -31,9 +31,12 @@ module Inbox
31
31
  class UnexpectedResponse < StandardError; end
32
32
  class APIError < StandardError
33
33
  attr_accessor :error_type
34
- def initialize(type, error)
34
+ attr_accessor :server_error
35
+
36
+ def initialize(type, error, server_error = nil)
35
37
  super(error)
36
38
  self.error_type = type
39
+ self.server_error = server_error
37
40
  end
38
41
  end
39
42
  class InvalidRequest < APIError; end
@@ -47,6 +50,26 @@ module Inbox
47
50
  raise AccessDenied.new if result.code.to_i == 403
48
51
  end
49
52
 
53
+ def self.http_code_to_exception(http_code)
54
+ if http_code == 400
55
+ exc = InvalidRequest
56
+ elsif http_code == 402
57
+ exc = MessageRejected
58
+ elsif http_code == 403
59
+ exc = AccessDenied
60
+ elsif http_code == 404
61
+ exc = ResourceNotFound
62
+ elsif http_code == 429
63
+ exc = SendingQuotaExceeded
64
+ elsif http_code == 503
65
+ exc = ServiceUnavailable
66
+ else
67
+ exc = APIError
68
+ end
69
+
70
+ exc
71
+ end
72
+
50
73
  def self.interpret_response(result, result_content, options = {})
51
74
  # Handle HTTP errors
52
75
  Inbox.interpret_http_status(result)
@@ -55,17 +78,7 @@ module Inbox
55
78
  raise UnexpectedResponse.new if options[:expected_class] && result_content.empty?
56
79
  json = options[:result_parsed]? result_content : JSON.parse(result_content)
57
80
  if json.is_a?(Hash) && (json['type'] == 'api_error' or json['type'] == 'invalid_request_error')
58
- if result.code.to_i == 400
59
- exc = InvalidRequest
60
- elsif result.code.to_i == 402
61
- exc = MessageRejected
62
- elsif result.code.to_i == 429
63
- exc = SendingQuotaExceeded
64
- elsif result.code.to_i == 503
65
- exc = ServiceUnavailable
66
- else
67
- exc = APIError
68
- end
81
+ exc = Inbox.http_code_to_exception(result.code.to_i)
69
82
  raise exc.new(json['type'], json['message'])
70
83
  end
71
84
  raise UnexpectedResponse.new(result.msg) if result.is_a?(Net::HTTPClientError)
@@ -248,6 +261,13 @@ module Inbox
248
261
  "label" => Inbox::Label,
249
262
  }
250
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" => Inbox::ExpandedMessage,
269
+ }
270
+
251
271
  def _build_exclude_types(exclude_types)
252
272
  exclude_string = "&exclude_types="
253
273
 
@@ -262,7 +282,7 @@ module Inbox
262
282
  exclude_string = exclude_string[0..-2]
263
283
  end
264
284
 
265
- def deltas(cursor, exclude_types=[])
285
+ def deltas(cursor, exclude_types=[], expanded_view=false)
266
286
  raise 'Please provide a block for receiving the delta objects' if !block_given?
267
287
  exclude_string = ""
268
288
 
@@ -273,6 +293,10 @@ module Inbox
273
293
  # loop and yield deltas until we've come to the end.
274
294
  loop do
275
295
  path = self.url_for_path("/delta?cursor=#{cursor}#{exclude_string}")
296
+ if expanded_view
297
+ path += '&view=expanded'
298
+ end
299
+
276
300
  json = nil
277
301
 
278
302
  RestClient.get(path) do |response,request,result|
@@ -288,6 +312,10 @@ module Inbox
288
312
  end
289
313
 
290
314
  cls = OBJECTS_TABLE[delta['object']]
315
+ if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
316
+ cls = EXPANDED_OBJECTS_TABLE[delta['object']]
317
+ end
318
+
291
319
  obj = cls.new(self)
292
320
 
293
321
  case delta["event"]
@@ -307,7 +335,7 @@ module Inbox
307
335
  end
308
336
  end
309
337
 
310
- def delta_stream(cursor, exclude_types=[], timeout=0)
338
+ def delta_stream(cursor, exclude_types=[], timeout=0, expanded_view=false)
311
339
  raise 'Please provide a block for receiving the delta objects' if !block_given?
312
340
 
313
341
  exclude_string = ""
@@ -318,6 +346,9 @@ module Inbox
318
346
 
319
347
  # loop and yield deltas indefinitely.
320
348
  path = self.url_for_path("/delta/streaming?cursor=#{cursor}#{exclude_string}")
349
+ if expanded_view
350
+ path += '&view=expanded'
351
+ end
321
352
 
322
353
  parser = Yajl::Parser.new(:symbolize_keys => false)
323
354
  parser.on_parse_complete = proc do |data|
@@ -328,6 +359,10 @@ module Inbox
328
359
  end
329
360
 
330
361
  cls = OBJECTS_TABLE[delta['object']]
362
+ if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
363
+ cls = EXPANDED_OBJECTS_TABLE[delta['object']]
364
+ end
365
+
331
366
  obj = cls.new(self)
332
367
 
333
368
  case delta["event"]
@@ -75,8 +75,36 @@ module Inbox
75
75
  model = nil
76
76
  collection = RestfulModelCollection.new(Message, @_api, {:message_id=>@id})
77
77
  RestClient.get("#{collection.url}/#{id}/", :accept => 'message/rfc822'){ |response,request,result|
78
+ Inbox.interpret_http_status(result)
78
79
  response
79
80
  }
80
81
  end
82
+
83
+ def expanded
84
+ expanded_url = url(action='?view=expanded')
85
+
86
+ RestClient.get(expanded_url){ |response,request,result|
87
+ json = Inbox.interpret_response(result, response, :expected_class => Object)
88
+ expanded_message = Inbox::ExpandedMessage.new(@_api)
89
+ expanded_message.inflate(json)
90
+ expanded_message
91
+ }
92
+
93
+ end
94
+ end
95
+
96
+ class ExpandedMessage < Message
97
+ # override inflate because expanded messages have some special parameters
98
+ # like In-Reply-To and Message-Id.
99
+ attr_reader :message_id
100
+ attr_reader :in_reply_to
101
+ attr_reader :references
102
+
103
+ def inflate(json)
104
+ super
105
+ @message_id = json['headers']['Message-Id']
106
+ @in_reply_to = json['headers']['In-Reply-To']
107
+ @references = json['headers']['References']
108
+ end
81
109
  end
82
110
  end
@@ -31,9 +31,12 @@ module Inbox
31
31
  class UnexpectedResponse < StandardError; end
32
32
  class APIError < StandardError
33
33
  attr_accessor :error_type
34
- def initialize(type, error)
34
+ attr_accessor :server_error
35
+
36
+ def initialize(type, error, server_error = nil)
35
37
  super(error)
36
38
  self.error_type = type
39
+ self.server_error = server_error
37
40
  end
38
41
  end
39
42
  class InvalidRequest < APIError; end
@@ -47,6 +50,26 @@ module Inbox
47
50
  raise AccessDenied.new if result.code.to_i == 403
48
51
  end
49
52
 
53
+ def self.http_code_to_exception(http_code)
54
+ if http_code == 400
55
+ exc = InvalidRequest
56
+ elsif http_code == 402
57
+ exc = MessageRejected
58
+ elsif http_code == 403
59
+ exc = AccessDenied
60
+ elsif http_code == 404
61
+ exc = ResourceNotFound
62
+ elsif http_code == 429
63
+ exc = SendingQuotaExceeded
64
+ elsif http_code == 503
65
+ exc = ServiceUnavailable
66
+ else
67
+ exc = APIError
68
+ end
69
+
70
+ exc
71
+ end
72
+
50
73
  def self.interpret_response(result, result_content, options = {})
51
74
  # Handle HTTP errors
52
75
  Inbox.interpret_http_status(result)
@@ -55,17 +78,7 @@ module Inbox
55
78
  raise UnexpectedResponse.new if options[:expected_class] && result_content.empty?
56
79
  json = options[:result_parsed]? result_content : JSON.parse(result_content)
57
80
  if json.is_a?(Hash) && (json['type'] == 'api_error' or json['type'] == 'invalid_request_error')
58
- if result.code.to_i == 400
59
- exc = InvalidRequest
60
- elsif result.code.to_i == 402
61
- exc = MessageRejected
62
- elsif result.code.to_i == 429
63
- exc = SendingQuotaExceeded
64
- elsif result.code.to_i == 503
65
- exc = ServiceUnavailable
66
- else
67
- exc = APIError
68
- end
81
+ exc = Inbox.http_code_to_exception(result.code.to_i)
69
82
  raise exc.new(json['type'], json['message'])
70
83
  end
71
84
  raise UnexpectedResponse.new(result.msg) if result.is_a?(Net::HTTPClientError)
@@ -248,6 +261,13 @@ module Inbox
248
261
  "label" => Inbox::Label,
249
262
  }
250
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" => Inbox::ExpandedMessage,
269
+ }
270
+
251
271
  def _build_exclude_types(exclude_types)
252
272
  exclude_string = "&exclude_types="
253
273
 
@@ -262,7 +282,7 @@ module Inbox
262
282
  exclude_string = exclude_string[0..-2]
263
283
  end
264
284
 
265
- def deltas(cursor, exclude_types=[])
285
+ def deltas(cursor, exclude_types=[], expanded_view=false)
266
286
  raise 'Please provide a block for receiving the delta objects' if !block_given?
267
287
  exclude_string = ""
268
288
 
@@ -273,6 +293,10 @@ module Inbox
273
293
  # loop and yield deltas until we've come to the end.
274
294
  loop do
275
295
  path = self.url_for_path("/delta?cursor=#{cursor}#{exclude_string}")
296
+ if expanded_view
297
+ path += '&view=expanded'
298
+ end
299
+
276
300
  json = nil
277
301
 
278
302
  RestClient.get(path) do |response,request,result|
@@ -288,6 +312,10 @@ module Inbox
288
312
  end
289
313
 
290
314
  cls = OBJECTS_TABLE[delta['object']]
315
+ if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
316
+ cls = EXPANDED_OBJECTS_TABLE[delta['object']]
317
+ end
318
+
291
319
  obj = cls.new(self)
292
320
 
293
321
  case delta["event"]
@@ -307,7 +335,7 @@ module Inbox
307
335
  end
308
336
  end
309
337
 
310
- def delta_stream(cursor, exclude_types=[], timeout=0)
338
+ def delta_stream(cursor, exclude_types=[], timeout=0, expanded_view=false)
311
339
  raise 'Please provide a block for receiving the delta objects' if !block_given?
312
340
 
313
341
  exclude_string = ""
@@ -318,6 +346,9 @@ module Inbox
318
346
 
319
347
  # loop and yield deltas indefinitely.
320
348
  path = self.url_for_path("/delta/streaming?cursor=#{cursor}#{exclude_string}")
349
+ if expanded_view
350
+ path += '&view=expanded'
351
+ end
321
352
 
322
353
  parser = Yajl::Parser.new(:symbolize_keys => false)
323
354
  parser.on_parse_complete = proc do |data|
@@ -328,6 +359,10 @@ module Inbox
328
359
  end
329
360
 
330
361
  cls = OBJECTS_TABLE[delta['object']]
362
+ if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
363
+ cls = EXPANDED_OBJECTS_TABLE[delta['object']]
364
+ end
365
+
331
366
  obj = cls.new(self)
332
367
 
333
368
  case delta["event"]
@@ -10,6 +10,7 @@ module Inbox
10
10
  parameter :account_id
11
11
  parameter :cursor # Only used by the delta sync API
12
12
  time_attr_accessor :created_at
13
+ attr_reader :raw_json
13
14
 
14
15
  def self.collection_name
15
16
  "#{self.to_s.downcase}s".split('::').last
@@ -26,6 +27,7 @@ module Inbox
26
27
  end
27
28
 
28
29
  def inflate(json)
30
+ @raw_json = json
29
31
  parameters.each do |property_name|
30
32
  send("#{property_name}=", json[property_name]) if json.has_key?(property_name)
31
33
  end
@@ -70,8 +72,8 @@ module Inbox
70
72
  self
71
73
  end
72
74
 
73
- def destroy
74
- ::RestClient.send('delete', self.url) do |response, request|
75
+ def destroy(params = {})
76
+ ::RestClient.send('delete', self.url, :params => params) do |response, request|
75
77
  Inbox.interpret_http_status(response)
76
78
  end
77
79
  end
@@ -1,3 +1,3 @@
1
1
  module Inbox
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nylas
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Gotow
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-08-26 00:00:00.000000000 Z
13
+ date: 2015-09-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client