nylas 3.2.0 → 4.0.0.rc2

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: 9418683f9825515764e97bf23b26d8221583c9ca
4
- data.tar.gz: 0b6b65e34c35da57205f4b58de3f2e27ded1ae36
3
+ metadata.gz: 5fd4eb7f8982e3da95740a92b4ff5d1307d22bc7
4
+ data.tar.gz: 3806c9465a159c517fc71165570a0c787751f79f
5
5
  SHA512:
6
- metadata.gz: 75d304cd89b172f67956a4b942ded7fa25c40cb6b330fa3e7123a5daf3ae1e8c132217fd236925db6898f3870c0fc41a0b611db8dfc013433708525cdeeb7954
7
- data.tar.gz: 97d3c77893776e5d6d8aa6b5590179db2a97e50300d2b1488d7e0f03b59f9e2b001203ed925287ce64252e133440a8d043e567a32665225611468f6559a1acdb
6
+ metadata.gz: a1841957b48b50212c6026428401306cadf9c83fddb109e5567c0cb5bb5aa187e2a6383c804b546ffe1082d7bca395e73a2f63f035d20ab42a516fd44e831474
7
+ data.tar.gz: a21d29a3a95331f855b5f4a312183fea5da5dcf73de7546df452d4723b080838bdc26fe376eec1455b98b108e3d92c0e8a2ec6742e05efaa1e80d72f5ff89d61
data/lib/nylas.rb CHANGED
@@ -1,389 +1,42 @@
1
- require 'json'
2
- require 'rest-client'
3
- require 'yajl'
4
- require 'em-http'
1
+ require "json"
2
+ require "rest-client"
5
3
 
4
+ require "ostruct"
5
+ require "forwardable"
6
6
 
7
- require 'ostruct'
8
- require 'active_support/core_ext/hash'
7
+ require_relative "nylas/version"
8
+ require_relative "nylas/errors"
9
9
 
10
- require 'account'
11
- require 'api_account'
12
- require 'api_thread'
13
- require 'calendar'
14
- require 'account'
15
- require 'message'
16
- require 'expanded_message'
17
- require 'draft'
18
- require 'contact'
19
- require 'file'
20
- require 'calendar'
21
- require 'event'
22
- require 'folder'
23
- require 'restful_model'
24
- require 'restful_model_collection'
25
- require 'version'
10
+ require_relative "nylas/logging"
11
+ require_relative "nylas/registry"
12
+ require_relative "nylas/types"
13
+ require_relative "nylas/constraints"
26
14
 
27
- module Nylas
28
- Error = Class.new(::StandardError)
29
- NoAuthToken = Class.new(Error)
30
- UnexpectedAccountAction = Class.new(Error)
31
- UnexpectedResponse = Class.new(Error)
32
- class APIError < Error
33
- attr_accessor :type
34
- attr_accessor :message
35
- attr_accessor :server_error
36
-
37
- def initialize(type, message, server_error = nil)
38
- super(message)
39
- self.type = type
40
- self.message = message
41
- self.server_error = server_error
42
- end
43
- end
44
- AccessDenied = Class.new(APIError)
45
- ResourceNotFound = Class.new(APIError)
46
- InvalidRequest = Class.new(APIError)
47
- MessageRejected = Class.new(APIError)
48
- SendingQuotaExceeded = Class.new(APIError)
49
- ServiceUnavailable = Class.new(APIError)
50
- BadGateway = Class.new(APIError)
51
- InternalError = Class.new(APIError)
52
- MailProviderError = Class.new(APIError)
53
-
54
- HTTP_CODE_TO_EXCEPTIONS = {
55
- 400 => InvalidRequest,
56
- 402 => MessageRejected,
57
- 403 => AccessDenied,
58
- 404 => ResourceNotFound,
59
- 422 => MailProviderError,
60
- 429 => SendingQuotaExceeded,
61
- 500 => InternalError,
62
- 502 => BadGateway,
63
- 503 => ServiceUnavailable,
64
- }.freeze
65
-
66
- def self.http_code_to_exception(http_code)
67
- HTTP_CODE_TO_EXCEPTIONS.fetch(http_code, APIError)
68
- end
69
-
70
- def self.interpret_response(result, result_content, options = {})
71
-
72
- # We expected a certain kind of object, but the API didn't return anything
73
- raise UnexpectedResponse.new if options[:expected_class] && result_content.empty?
74
-
75
- # If it's already parsed, or if we've received an actual raw payload on success, don't parse
76
- if options[:result_parsed] || (options[:raw_response] && result.code.to_i == 200)
77
- response = result_content
78
- else
79
- response = JSON.parse(result_content)
80
- end
81
-
82
- if result.code.to_i != 200
83
- exc = Nylas.http_code_to_exception(result.code.to_i)
84
- if response.is_a?(Hash)
85
- raise exc.new(response['type'], response['message'], response.fetch('server_error', nil))
86
- end
87
- end
88
-
89
- raise UnexpectedResponse.new(result.msg) if result.is_a?(Net::HTTPClientError)
90
- raise UnexpectedResponse.new if options[:expected_class] && !response.is_a?(options[:expected_class])
91
- response
92
-
93
- rescue JSON::ParserError => e
94
- # Handle parsing errors
95
- raise UnexpectedResponse.new(e.message)
96
- end
97
-
98
-
99
- class API
100
- attr_accessor :api_server
101
- attr_reader :access_token
102
- attr_reader :app_id
103
- attr_reader :app_secret
104
-
105
- def initialize(app_id, app_secret, access_token = nil, api_server = 'https://api.nylas.com',
106
- service_domain = 'api.nylas.com')
107
- raise "When overriding the Nylas API server address, you must include https://" unless api_server.include?('://')
108
- @api_server = api_server
109
- @access_token = access_token
110
- @app_secret = app_secret
111
- @app_id = app_id
112
- @service_domain = service_domain
113
- @version = Nylas::VERSION
114
-
115
- if ::RestClient.before_execution_procs.empty?
116
- ::RestClient.add_before_execution_proc do |req, params|
117
- req.add_field('X-Nylas-API-Wrapper', 'ruby')
118
- req['User-Agent'] = "Nylas Ruby SDK #{@version} - #{RUBY_VERSION}"
119
- end
120
- end
121
- end
122
-
123
- def url_for_path(path)
124
- raise NoAuthToken.new if @access_token == nil and (@app_secret != nil or @app_id != nil)
125
- protocol, domain = @api_server.split('//')
126
- "#{protocol}//#{@access_token}:@#{domain}#{path}"
127
- end
128
-
129
- def url_for_authentication(redirect_uri, login_hint = '', options = {})
130
- params = {
131
- :client_id => @app_id,
132
- :trial => options.fetch(:trial, false),
133
- :response_type => 'code',
134
- :scope => 'email',
135
- :login_hint => login_hint,
136
- :redirect_uri => redirect_uri,
137
- }
138
-
139
- if options.has_key?(:state) then
140
- params[:state] = options[:state]
141
- end
142
-
143
- "https://#{@service_domain}/oauth/authorize?" + params.to_query
144
- end
145
-
146
- def url_for_management
147
- protocol, domain = @api_server.split('//')
148
- accounts_path = "#{protocol}//#{@app_secret}:@#{domain}/a/#{@app_id}/accounts"
149
- end
150
-
151
- def set_access_token(token)
152
- @access_token = token
153
- end
154
-
155
- def token_for_code(code)
156
- data = {
157
- 'client_id' => app_id,
158
- 'client_secret' => app_secret,
159
- 'grant_type' => 'authorization_code',
160
- 'code' => code
161
- }
162
-
163
- ::RestClient.post("https://#{@service_domain}/oauth/token", data) do |response, request, result|
164
- json = Nylas.interpret_response(result, response, :expected_class => Object)
165
- return json['access_token']
166
- end
167
- end
168
-
169
- # API Methods
170
- def threads
171
- @threads ||= RestfulModelCollection.new(Thread, self)
172
- end
173
-
174
- def messages(expanded: false)
175
- @messages ||= Hash.new do |h, is_expanded|
176
- h[is_expanded] = \
177
- if is_expanded
178
- RestfulModelCollection.new(ExpandedMessage, self, view: 'expanded')
179
- else
180
- RestfulModelCollection.new(Message, self)
181
- end
182
- end
183
- @messages[expanded]
184
- end
185
-
186
- def files
187
- @files ||= RestfulModelCollection.new(File, self)
188
- end
189
-
190
- def drafts
191
- @drafts ||= RestfulModelCollection.new(Draft, self)
192
- end
193
-
194
- def contacts
195
- @contacts ||= RestfulModelCollection.new(Contact, self)
196
- end
197
-
198
- def calendars
199
- @calendars ||= RestfulModelCollection.new(Calendar, self)
200
- end
201
-
202
- def events
203
- @events ||= RestfulModelCollection.new(Event, self)
204
- end
205
-
206
- def folders
207
- @folders ||= RestfulModelCollection.new(Folder, self)
208
- end
209
-
210
- def labels
211
- @labels ||= RestfulModelCollection.new(Label, self)
212
- end
213
-
214
- def account
215
- path = self.url_for_path("/account")
15
+ require_relative "nylas/collection"
16
+ require_relative "nylas/model"
216
17
 
217
- RestClient.get(path, {}) do |response,request,result|
218
- json = Nylas.interpret_response(result, response, {:expected_class => Object})
219
- model = APIAccount.new(self)
220
- model.inflate(json)
221
- model
222
- end
223
- end
18
+ # Attribute types supported by the API
19
+ require_relative "nylas/email_address"
20
+ require_relative "nylas/im_address"
21
+ require_relative "nylas/physical_address"
22
+ require_relative "nylas/phone_number"
23
+ require_relative "nylas/web_page"
24
+ require_relative "nylas/nylas_date"
224
25
 
225
- def using_hosted_api?
226
- return !@app_id.nil?
227
- end
26
+ # Models supported by the API
27
+ require_relative "nylas/contact"
28
+ require_relative "nylas/current_account"
228
29
 
229
- def accounts
230
- if self.using_hosted_api?
231
- @accounts ||= ManagementModelCollection.new(Account, self)
232
- else
233
- @accounts ||= RestfulModelCollection.new(APIAccount, self)
234
- end
235
- end
30
+ require_relative "nylas/http_client"
31
+ require_relative "nylas/api"
236
32
 
237
- def latest_cursor
238
- # Get the cursor corresponding to a specific timestamp.
239
- path = self.url_for_path("/delta/latest_cursor")
240
-
241
- cursor = nil
242
-
243
- RestClient.post(path, :content_type => :json) do |response,request,result|
244
- json = Nylas.interpret_response(result, response, {:expected_class => Object})
245
- cursor = json["cursor"]
246
- end
247
-
248
- cursor
249
- end
250
-
251
- OBJECTS_TABLE = {
252
- "account" => Nylas::Account,
253
- "calendar" => Nylas::Calendar,
254
- "draft" => Nylas::Draft,
255
- "thread" => Nylas::Thread,
256
- "contact" => Nylas::Contact,
257
- "event" => Nylas::Event,
258
- "file" => Nylas::File,
259
- "message" => Nylas::Message,
260
- "folder" => Nylas::Folder,
261
- "label" => Nylas::Label,
262
- }
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" => Nylas::ExpandedMessage,
269
- }
270
-
271
- def _build_types_filter_string(filter, types)
272
- return "" if types.empty?
273
- query_string = "&#{filter}_types="
274
-
275
- types.each do |value|
276
- count = 0
277
- if OBJECTS_TABLE.has_value?(value)
278
- param_name = OBJECTS_TABLE.key(value)
279
- query_string += "#{param_name},"
280
- end
281
- end
282
-
283
- query_string = query_string[0..-2]
284
- end
285
-
286
- def deltas(cursor, exclude_types=[], expanded_view=false, include_types=[])
287
- return enum_for(:deltas, cursor, exclude_types, expanded_view, include_types) unless block_given?
288
-
289
- exclude_string = _build_types_filter_string(:exclude, exclude_types)
290
- include_string = _build_types_filter_string(:include, include_types)
291
-
292
- # loop and yield deltas until we've come to the end.
293
- loop do
294
- path = self.url_for_path("/delta?exclude_folders=false&cursor=#{cursor}#{exclude_string}#{include_string}")
295
- if expanded_view
296
- path += '&view=expanded'
297
- end
298
-
299
- json = nil
300
-
301
- RestClient.get(path) do |response,request,result|
302
- json = Nylas.interpret_response(result, response, {:expected_class => Object})
303
- end
304
-
305
- start_cursor = json["cursor_start"]
306
- end_cursor = json["cursor_end"]
307
-
308
- json["deltas"].each do |delta|
309
- if not OBJECTS_TABLE.has_key?(delta['object'])
310
- next
311
- end
312
-
313
- cls = OBJECTS_TABLE[delta['object']]
314
- if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
315
- cls = EXPANDED_OBJECTS_TABLE[delta['object']]
316
- end
317
-
318
- obj = cls.new(self)
319
-
320
- case delta["event"]
321
- when 'create', 'modify'
322
- obj.inflate(delta['attributes'])
323
- obj.cursor = delta["cursor"]
324
- yield delta["event"], obj
325
- when 'delete'
326
- obj.id = delta["id"]
327
- obj.cursor = delta["cursor"]
328
- yield delta["event"], obj
329
- end
330
- end
331
-
332
- break if start_cursor == end_cursor
333
- cursor = end_cursor
334
- end
335
- end
336
-
337
- def delta_stream(cursor, exclude_types=[], timeout=0, expanded_view=false, include_types=[])
338
- raise 'Please provide a block for receiving the delta objects' if !block_given?
339
-
340
- exclude_string = _build_types_filter_string(:exclude, exclude_types)
341
- include_string = _build_types_filter_string(:include, include_types)
342
-
343
- # loop and yield deltas indefinitely.
344
- path = self.url_for_path("/delta/streaming?exclude_folders=false&cursor=#{cursor}#{exclude_string}#{include_string}")
345
- if expanded_view
346
- path += '&view=expanded'
347
- end
348
-
349
- parser = Yajl::Parser.new(:symbolize_keys => false)
350
- parser.on_parse_complete = proc do |data|
351
- delta = Nylas.interpret_response(OpenStruct.new(:code => '200'), data, {:expected_class => Object, :result_parsed => true})
352
-
353
- if not OBJECTS_TABLE.has_key?(delta['object'])
354
- next
355
- end
356
-
357
- cls = OBJECTS_TABLE[delta['object']]
358
- if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
359
- cls = EXPANDED_OBJECTS_TABLE[delta['object']]
360
- end
361
-
362
- obj = cls.new(self)
363
-
364
- case delta["event"]
365
- when 'create', 'modify'
366
- obj.inflate(delta['attributes'])
367
- obj.cursor = delta["cursor"]
368
- yield delta["event"], obj
369
- when 'delete'
370
- obj.id = delta["id"]
371
- obj.cursor = delta["cursor"]
372
- yield delta["event"], obj
373
- end
374
- end
375
-
376
- http = EventMachine::HttpRequest.new(path, :connect_timeout => 0, :inactivity_timeout => timeout).get(:keepalive => true)
377
-
378
- # set a callback on the HTTP stream that parses incoming chunks as they come in
379
- http.stream do |chunk|
380
- parser << chunk
381
- end
382
-
383
- http.errback do
384
- raise UnexpectedResponse.new http.error
385
- end
386
-
387
- end
388
- end
33
+ # an SDK for interacting with the Nylas API
34
+ # @see https://docs.nylas.com/reference
35
+ module Nylas
36
+ Types.registry[:email_address] = EmailAddressType.new
37
+ Types.registry[:im_address] = IMAddressType.new
38
+ Types.registry[:physical_address] = PhysicalAddressType.new
39
+ Types.registry[:phone_number] = PhoneNumberType.new
40
+ Types.registry[:web_page] = WebPageType.new
41
+ Types.registry[:nylas_date] = NylasDateType.new
389
42
  end
data/lib/nylas/api.rb ADDED
@@ -0,0 +1,43 @@
1
+ module Nylas
2
+ # Methods to retrieve data from the Nylas API as Ruby objects
3
+ class API
4
+ attr_accessor :client
5
+ extend Forwardable
6
+ def_delegators :client, :execute, :get, :post, :put, :delete
7
+
8
+ include Logging
9
+
10
+ # @param client [HttpClient] Http Client to use for retrieving data
11
+ # @param app_id [String] Your application id from the Nylas Dashboard
12
+ # @param app_secret [String] Your application secret from the Nylas Dashboard
13
+ # @param access_token [String] (Optional) Your users access token.
14
+ # @param api_server [String] (Optional) Which Nylas API Server to connect to. Only change this if
15
+ # you're using a self-hosted Nylas instance.
16
+ # @param service_domain [String] (Optional) Host you are authenticating OAuth against.
17
+ # @return [Nylas::API]
18
+ # rubocop:disable Metrics/ParameterLists
19
+ def initialize(client: nil, app_id: nil, app_secret: nil, access_token: nil,
20
+ api_server: "https://api.nylas.com", service_domain: "api.nylas.com")
21
+ self.client = client || HttpClient.new(app_id: app_id, app_secret: app_secret,
22
+ access_token: access_token, api_server: api_server,
23
+ service_domain: service_domain)
24
+ end
25
+ # rubocop:enable Metrics/ParameterLists
26
+
27
+ # @return [Collection<Contact>] A queryable collection of Contacts
28
+ def contacts
29
+ @contacts ||= Collection.new(model: Contact, api: self)
30
+ end
31
+
32
+ # @return [CurrentAccount] The account details for whomevers access token is set
33
+ def current_account
34
+ prevent_calling_if_missing_access_token(:current_account)
35
+ CurrentAccount.from_hash(client.execute(method: :get, path: "/account"), api: self)
36
+ end
37
+
38
+ private def prevent_calling_if_missing_access_token(method_name)
39
+ return if client.access_token && !client.access_token.empty?
40
+ raise NoAuthToken, method_name
41
+ end
42
+ end
43
+ end