nylas 1.0.0 → 1.1.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
  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