nylas 4.0.0.rc2 → 4.0.0.rc3

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.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/lib/nylas.rb +48 -5
  3. data/lib/nylas/account.rb +31 -0
  4. data/lib/nylas/api.rb +86 -2
  5. data/lib/nylas/calendar.rb +27 -0
  6. data/lib/nylas/collection.rb +46 -19
  7. data/lib/nylas/constraints.rb +12 -4
  8. data/lib/nylas/contact.rb +10 -2
  9. data/lib/nylas/current_account.rb +1 -4
  10. data/lib/nylas/delta.rb +46 -0
  11. data/lib/nylas/deltas.rb +17 -0
  12. data/lib/nylas/deltas_collection.rb +36 -0
  13. data/lib/nylas/draft.rb +51 -0
  14. data/lib/nylas/email_address.rb +1 -5
  15. data/lib/nylas/errors.rb +13 -0
  16. data/lib/nylas/event.rb +42 -0
  17. data/lib/nylas/event_collection.rb +13 -0
  18. data/lib/nylas/file.rb +58 -0
  19. data/lib/nylas/folder.rb +22 -0
  20. data/lib/nylas/http_client.rb +38 -29
  21. data/lib/nylas/im_address.rb +0 -5
  22. data/lib/nylas/label.rb +22 -0
  23. data/lib/nylas/message.rb +45 -0
  24. data/lib/nylas/message_headers.rb +25 -0
  25. data/lib/nylas/message_tracking.rb +11 -0
  26. data/lib/nylas/model.rb +75 -24
  27. data/lib/nylas/model/attributable.rb +2 -2
  28. data/lib/nylas/model/attributes.rb +3 -1
  29. data/lib/nylas/native_authentication.rb +31 -0
  30. data/lib/nylas/new_message.rb +29 -0
  31. data/lib/nylas/nylas_date.rb +5 -2
  32. data/lib/nylas/participant.rb +10 -0
  33. data/lib/nylas/phone_number.rb +0 -5
  34. data/lib/nylas/physical_address.rb +0 -5
  35. data/lib/nylas/raw_message.rb +15 -0
  36. data/lib/nylas/recurrence.rb +9 -0
  37. data/lib/nylas/rsvp.rb +18 -0
  38. data/lib/nylas/search_collection.rb +8 -0
  39. data/lib/nylas/thread.rb +52 -0
  40. data/lib/nylas/timespan.rb +18 -0
  41. data/lib/nylas/types.rb +62 -9
  42. data/lib/nylas/version.rb +1 -1
  43. data/lib/nylas/web_page.rb +0 -5
  44. data/lib/nylas/webhook.rb +19 -0
  45. metadata +31 -7
@@ -3,10 +3,7 @@ module Nylas
3
3
  # @see https://docs.nylas.com/reference#account
4
4
  class CurrentAccount
5
5
  include Model
6
-
7
- self.read_only = true
8
- self.searchable = false
9
- self.collectionable = false
6
+ self.showable = true
10
7
 
11
8
  self.resources_path = "/account"
12
9
 
@@ -0,0 +1,46 @@
1
+ module Nylas
2
+ # Ruby object to represent a single change. Used both when receiving a webhook, as well as the deltas API.
3
+ # @see https://docs.nylas.com/reference#receiving-notifications
4
+ # @see https://docs.nylas.com/reference#deltas
5
+ class Delta
6
+ include Model::Attributable
7
+ attribute :id, :string
8
+ attribute :type, :string
9
+ attribute :object, :string
10
+ attribute :event, :string
11
+ attribute :cursor, :string
12
+ attribute :namespace_id, :string
13
+ attribute :account_id, :string
14
+
15
+ attribute :date, :unix_timestamp
16
+ attribute :metadata, :hash
17
+ attribute :object_attributes, :hash
18
+
19
+ def model
20
+ return nil if object.nil?
21
+ @model ||= Types.registry[object.to_sym].cast(object_attributes_with_ids)
22
+ end
23
+
24
+ def object_attributes_with_ids
25
+ (object_attributes || {}).merge(id: id, account_id: account_id)
26
+ end
27
+ end
28
+
29
+ # Casts Delta data from either a webhook or a delta stream to a Delta
30
+ class DeltaType < Types::ModelType
31
+ def initialize
32
+ super(model: Delta)
33
+ end
34
+
35
+ def cast(data)
36
+ data = if data.key?(:object_data)
37
+ object_data = data.delete(:object_data)
38
+ data.merge(object_data)
39
+ else
40
+ data
41
+ end
42
+ data[:object_attributes] = data.delete(:attributes)
43
+ super(data)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ module Nylas
2
+ # Ruby object to represent a collection of changes. Used both when receiving a webhook, as well as the
3
+ # deltas API.
4
+ # @see https://docs.nylas.com/reference#receiving-notifications
5
+ # @see https://docs.nylas.com/reference#deltas
6
+ class Deltas
7
+ include Model
8
+ self.resources_path = "/delta"
9
+ allows_operations(filterable: true)
10
+ has_n_of_attribute :deltas, :delta
11
+ attribute :cursor_start, :string
12
+ attribute :cursor_end, :string
13
+
14
+ extend Forwardable
15
+ def_delegators :deltas, :count, :length, :each, :map, :first, :to_a, :empty?
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ module Nylas
2
+ # Special collection for delta objects
3
+ class DeltasCollection < Collection
4
+ attr_accessor :deltas
5
+ extend Forwardable
6
+ def_delegators :execute, :cursor_start, :cursor_end,
7
+ :count, :each, :to_h, :to_a, :empty?
8
+
9
+ def initialize(api:, constraints: nil, model: Deltas)
10
+ super(api: api, model: model, constraints: constraints)
11
+ end
12
+
13
+ def latest_cursor
14
+ api.execute(method: :post, path: "#{resources_path}/latest_cursor")[:cursor]
15
+ end
16
+
17
+ def latest
18
+ since(latest_cursor)
19
+ end
20
+
21
+ def since(cursor)
22
+ where(cursor: cursor)
23
+ end
24
+
25
+ def next_page(*)
26
+ return nil if empty?
27
+ where(cursor: cursor_end)
28
+ end
29
+
30
+ # Retrieves the data from the API for the particular constraints
31
+ # @return [Detlas]
32
+ def execute
33
+ self.deltas ||= Deltas.new(api.execute(to_be_executed))
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,51 @@
1
+ module Nylas
2
+ # Ruby representatin of a Nylas Draft object
3
+ # @see https://docs.nylas.com/reference#drafts
4
+ class Draft
5
+ include Model
6
+ self.resources_path = "/drafts"
7
+ allows_operations(creatable: true, showable: true, listable: true, updatable: true, destroyable: true)
8
+
9
+ attribute :id, :string
10
+ attribute :object, :string
11
+ attribute :version, :integer
12
+ attribute :account_id, :string
13
+ attribute :thread_id, :string
14
+ attribute :reply_to_message_id, :string
15
+
16
+ has_n_of_attribute :to, :email_address
17
+ has_n_of_attribute :from, :email_address
18
+ has_n_of_attribute :cc, :email_address
19
+ has_n_of_attribute :bcc, :email_address
20
+ has_n_of_attribute :reply_to, :email_address
21
+
22
+ attribute :date, :unix_timestamp
23
+ attribute :subject, :string
24
+ attribute :snippet, :string
25
+ attribute :body, :string
26
+ attribute :starred, :boolean
27
+ attribute :unread, :boolean
28
+
29
+ has_n_of_attribute :events, :event
30
+ has_n_of_attribute :files, :file
31
+ attribute :folder, :label
32
+ has_n_of_attribute :labels, :label
33
+
34
+ def send!
35
+ save
36
+ execute(method: :post, path: "/send", payload: JSON.dump(draft_id: id, version: version))
37
+ end
38
+
39
+ def starred?
40
+ starred
41
+ end
42
+
43
+ def unread?
44
+ unread
45
+ end
46
+
47
+ def destroy
48
+ execute(method: :delete, path: resource_path, payload: attributes.serialize(keys: [:version]))
49
+ end
50
+ end
51
+ end
@@ -5,10 +5,6 @@ module Nylas
5
5
  include Model::Attributable
6
6
  attribute :type, :string
7
7
  attribute :email, :string
8
- end
9
-
10
- # Serializes, Deserializes between {EmailAddress} objects and a {Hash}
11
- class EmailAddressType < Types::HashType
12
- casts_to EmailAddress
8
+ attribute :name, :string
13
9
  end
14
10
  end
data/lib/nylas/errors.rb CHANGED
@@ -1,6 +1,17 @@
1
1
  module Nylas
2
2
  Error = Class.new(::StandardError)
3
3
 
4
+ class ModelActionError < Error; end
5
+ class ModelNotFilterableError < ModelActionError; end
6
+ class ModelNotCreatableError < ModelActionError; end
7
+ class ModelNotShowableError < ModelActionError; end
8
+ class ModelNotAvailableAsRawError < ModelActionError; end
9
+ class ModelNotListableError < ModelActionError; end
10
+ class ModelNotFilterableError < ModelActionError; end
11
+ class ModelNotSearchableError < ModelActionError; end
12
+ class ModelNotUpdatableError < ModelActionError; end
13
+ class ModelNotDestroyableError < ModelActionError; end
14
+
4
15
  # Indicates that a given method needs an access token to work.
5
16
  class NoAuthToken < Error
6
17
  def initialize(method_name)
@@ -26,11 +37,13 @@ module Nylas
26
37
  end
27
38
  AccessDenied = Class.new(APIError)
28
39
  ResourceNotFound = Class.new(APIError)
40
+ MethodNotAllowed = Class.new(APIError)
29
41
  InvalidRequest = Class.new(APIError)
30
42
  MessageRejected = Class.new(APIError)
31
43
  SendingQuotaExceeded = Class.new(APIError)
32
44
  ServiceUnavailable = Class.new(APIError)
33
45
  BadGateway = Class.new(APIError)
34
46
  InternalError = Class.new(APIError)
47
+ EndpointNotYetImplemented = Class.new(APIError)
35
48
  MailProviderError = Class.new(APIError)
36
49
  end
@@ -0,0 +1,42 @@
1
+ module Nylas
2
+ # Structure to represent a the Event Schema.
3
+ # @see https://docs.nylas.com/reference#events
4
+ class Event
5
+ include Model
6
+ self.resources_path = "/events"
7
+ allows_operations(creatable: true, listable: true, filterable: true, showable: true, updatable: true,
8
+ destroyable: true)
9
+
10
+ attribute :id, :string
11
+ attribute :object, :string
12
+ attribute :account_id, :string
13
+ attribute :calendar_id, :string
14
+ attribute :master_event_id, :string
15
+ attribute :message_id, :string
16
+
17
+ attribute :busy, :boolean
18
+ attribute :description, :string
19
+ attribute :location, :string
20
+ attribute :owner, :string
21
+ attribute :recurrence, :recurrence
22
+ has_n_of_attribute :participants, :participant
23
+ attribute :read_only, :boolean
24
+ attribute :status, :string
25
+ attribute :title, :string
26
+ attribute :when, :timespan
27
+
28
+ def busy?
29
+ busy
30
+ end
31
+
32
+ def read_only?
33
+ read_only
34
+ end
35
+
36
+ def rsvp(status, notify_participants:)
37
+ rsvp = Rsvp.new(api: api, status: status, notify_participants: notify_participants,
38
+ event_id: id, account_id: account_id)
39
+ rsvp.save
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ module Nylas
2
+ # Syntactical sugar methods for some of the Event's filters
3
+ # @see https://docs.nylas.com/reference#get-events
4
+ class EventCollection < Collection
5
+ def expand_recurring
6
+ where(expand_recurring: true)
7
+ end
8
+
9
+ def show_cancelled
10
+ where(show_cancelled: true)
11
+ end
12
+ end
13
+ end
data/lib/nylas/file.rb ADDED
@@ -0,0 +1,58 @@
1
+ module Nylas
2
+ # Structure to represent a the File Schema.
3
+ # @see https://docs.nylas.com/reference#events
4
+ class File
5
+ include Model
6
+ self.resources_path = "/files"
7
+ allows_operations(listable: true, showable: true, filterable: true, creatable: true)
8
+
9
+ attribute :id, :string
10
+ attribute :account_id, :string
11
+ attribute :content_id, :string
12
+ has_n_of_attribute :message_ids, :string
13
+
14
+ attribute :object, :string
15
+
16
+ attribute :content_type, :string
17
+ attribute :filename, :string
18
+ attribute :size, :integer
19
+
20
+ attr_accessor :file
21
+ # Downloads and caches a local copy of the file.
22
+ # @return [Tempfile] - Local copy of the file
23
+ def download
24
+ return file if file
25
+ self.file = retrieve_file
26
+ end
27
+
28
+ # Redownloads a file even if it's been cached. Closes and unlinks the tempfile to help memory usage.
29
+ def download!
30
+ return download if file.nil?
31
+ file.close
32
+ file.unlink
33
+ self.file = nil
34
+ download
35
+ end
36
+
37
+ def create
38
+ save
39
+ end
40
+
41
+ def save
42
+ raise ModelNotUpdatableError if persisted?
43
+ response = api.execute(path: "/files", method: :post, headers: { multipart: true },
44
+ payload: { file: file })
45
+ attributes.merge(response.first)
46
+ true
47
+ end
48
+
49
+ private def retrieve_file
50
+ response = api.get(path: "#{resource_path}/download")
51
+ filename = response.headers.fetch(:content_disposition, "").gsub("attachment; filename=", "")
52
+ temp_file = Tempfile.new(filename)
53
+ temp_file.write(response.body)
54
+ temp_file.seek(0)
55
+ temp_file
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,22 @@
1
+ module Nylas
2
+ # Structure to represent the Folder Schema
3
+ # @see https://docs.nylas.com/reference#folders
4
+ class Folder
5
+ include Model
6
+ self.resources_path = "/folders"
7
+ self.creatable = true
8
+ self.listable = true
9
+ self.showable = true
10
+ self.filterable = false
11
+ self.updatable = true
12
+ self.destroyable = true
13
+
14
+ attribute :id, :string
15
+ attribute :account_id, :string
16
+
17
+ attribute :object, :string
18
+
19
+ attribute :name, :string
20
+ attribute :display_name, :string
21
+ end
22
+ end
@@ -6,15 +6,18 @@ module Nylas
6
6
  402 => MessageRejected,
7
7
  403 => AccessDenied,
8
8
  404 => ResourceNotFound,
9
+ 405 => MethodNotAllowed,
9
10
  422 => MailProviderError,
10
11
  429 => SendingQuotaExceeded,
11
12
  500 => InternalError,
13
+ 501 => EndpointNotYetImplemented,
12
14
  502 => BadGateway,
13
15
  503 => ServiceUnavailable
14
16
  }.freeze
15
17
 
16
18
  include Logging
17
- attr_accessor :api_server, :default_headers
19
+ attr_accessor :api_server, :service_domain
20
+ attr_writer :default_headers
18
21
  attr_reader :access_token
19
22
  attr_reader :app_id
20
23
  attr_reader :app_secret
@@ -25,7 +28,7 @@ module Nylas
25
28
  # @param api_server [String] (Optional) Which Nylas API Server to connect to. Only change this if
26
29
  # you're using a self-hosted Nylas instance.
27
30
  # @param service_domain [String] (Optional) Host you are authenticating OAuth against.
28
- # @return [Nylas::API]
31
+ # @return [Nylas::HttpClient]
29
32
  def initialize(app_id:, app_secret:, access_token: nil, api_server: "https://api.nylas.com",
30
33
  service_domain: "api.nylas.com")
31
34
  unless api_server.include?("://")
@@ -38,10 +41,14 @@ module Nylas
38
41
  @service_domain = service_domain
39
42
  end
40
43
 
44
+ # @return [Nylas::HttpClient[]
45
+ def as(access_token)
46
+ HttpClient.new(app_id: app_id, access_token: access_token,
47
+ app_secret: app_secret, api_server: api_server, service_domain: service_domain)
48
+ end
49
+
41
50
  # Sends a request to the Nylas API and rai
42
51
  # @param method [Symbol] HTTP method for the API call. Either :get, :post, :delete, or :patch
43
- # @param url [String] (Optional, defaults to nil) - Full URL to access. Deprecated and will be removed in
44
- # 5.0.
45
52
  # @param path [String] (Optional, defaults to nil) - Relative path from the API Base. Preferred way to
46
53
  # execute arbitrary or-not-yet-SDK-ified API commands.
47
54
  # @param headers [Hash] (Optional, defaults to {}) - Additional HTTP headers to include in the payload.
@@ -49,45 +56,47 @@ module Nylas
49
56
  # section of the URI fragment
50
57
  # @param payload [String,Hash] (Optional, defaults to nil) - Body to send with the request.
51
58
  # @return [Array Hash Stringn]
52
- # rubocop:disable Metrics/ParameterLists
53
- def execute(method:, url: nil, path: nil, headers: {}, query: {}, payload: nil)
54
- headers[:params] = query
55
- url ||= url_for_path(path)
56
- resulting_headers = default_headers.merge(headers)
57
- rest_client_execute(method: method, url: url, payload: payload,
58
- headers: resulting_headers) do |response, _request, result|
59
-
59
+ def execute(method:, path: nil, headers: {}, query: {}, payload: nil)
60
+ request = build_request(method: method, path: path, headers: headers, query: query, payload: payload)
61
+ rest_client_execute(**request) do |response, _request, result|
60
62
  response = parse_response(response)
61
63
  handle_failed_response(result: result, response: response)
62
64
  response
63
65
  end
64
66
  end
65
- # rubocop:enable Metrics/ParameterLists
66
67
  inform_on :execute, level: :debug,
67
- also_log: { result: true, values: %i(method url path headers query payload) }
68
+ also_log: { result: true, values: %i[method url path headers query payload] }
69
+
70
+ def build_request(method:, path: nil, headers: {}, query: {}, payload: nil)
71
+ headers[:params] = query
72
+ url ||= url_for_path(path)
73
+ resulting_headers = default_headers.merge(headers)
74
+ { method: method, url: url, payload: payload, headers: resulting_headers }
75
+ end
76
+ # rubocop:enable Metrics/ParameterLists
68
77
 
69
78
  # Syntactical sugar for making GET requests via the API.
70
79
  # @see #execute
71
- def get(path: nil, url: nil, headers: {}, query: {})
72
- execute(method: :get, path: path, query: query, url: url, headers: headers)
80
+ def get(path: nil, headers: {}, query: {})
81
+ execute(method: :get, path: path, query: query, headers: headers)
73
82
  end
74
83
 
75
84
  # Syntactical sugar for making POST requests via the API.
76
85
  # @see #execute
77
- def post(path: nil, url: nil, payload: nil, headers: {}, query: {})
78
- execute(method: :post, path: path, url: url, headers: headers, query: query, payload: payload)
86
+ def post(path: nil, payload: nil, headers: {}, query: {})
87
+ execute(method: :post, path: path, headers: headers, query: query, payload: payload)
79
88
  end
80
89
 
81
90
  # Syntactical sugar for making PUT requests via the API.
82
91
  # @see #execute
83
- def put(path: nil, url: nil, payload:, headers: {}, query: {})
84
- execute(method: :put, path: path, url: url, headers: headers, query: query, payload: payload)
92
+ def put(path: nil, payload:, headers: {}, query: {})
93
+ execute(method: :put, path: path, headers: headers, query: query, payload: payload)
85
94
  end
86
95
 
87
96
  # Syntactical sugar for making DELETE requests via the API.
88
97
  # @see #execute
89
- def delete(path: nil, url: nil, payload: nil, headers: {}, query: {})
90
- execute(method: :delete, path: path, url: url, headers: headers, query: query, payload: payload)
98
+ def delete(path: nil, payload: nil, headers: {}, query: {})
99
+ execute(method: :delete, path: path, headers: headers, query: query, payload: payload)
91
100
  end
92
101
  # rubocop:enable Metrics/ParameterList
93
102
 
@@ -96,7 +105,7 @@ module Nylas
96
105
  headers: headers, &block)
97
106
  end
98
107
  inform_on :rest_client_execute, level: :debug,
99
- also_log: { result: true, values: %i(method url headers payload) }
108
+ also_log: { result: true, values: %i[method url headers payload] }
100
109
 
101
110
  def default_headers
102
111
  @default_headers ||= {
@@ -106,16 +115,16 @@ module Nylas
106
115
  }
107
116
  end
108
117
 
109
- private def parse_response(response)
118
+ def parse_response(response)
110
119
  response.is_a?(Enumerable) ? response : JSON.parse(response, symbolize_names: true)
111
120
  rescue JSON::ParserError
112
121
  response
113
122
  end
123
+ inform_on :parse_response, level: :debug, also_log: { result: true }
114
124
 
115
- private def url_for_path(path)
116
- raise NoAuthToken if @access_token.nil? && (!@app_secret.nil? || !@app_id.nil?)
117
- protocol, domain = @api_server.split("//")
118
- "#{protocol}//#{@access_token}:@#{domain}#{path}"
125
+ def url_for_path(path)
126
+ protocol, domain = api_server.split("//")
127
+ "#{protocol}//#{access_token}:@#{domain}#{path}"
119
128
  end
120
129
 
121
130
  private def handle_failed_response(result:, response:)
@@ -129,7 +138,7 @@ module Nylas
129
138
  return if http_code == 200
130
139
  return unless response.is_a?(Hash)
131
140
  exception = HTTP_CODE_TO_EXCEPTIONS.fetch(http_code, APIError)
132
- raise exception.new(response["type"], response["message"], response.fetch("server_error", nil))
141
+ raise exception.new(response[:type], response[:message], response.fetch(:server_error, nil))
133
142
  end
134
143
  end
135
144
  end