nylas-shipmnts 3.0.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.
@@ -0,0 +1,26 @@
1
+ module Nylas
2
+ module ReadUnreadMethods
3
+ def update_param!(param, value)
4
+ update('PUT', '', {
5
+ param => value,
6
+ })
7
+ end
8
+
9
+ def mark_as_read!
10
+ update_param!(:unread, false)
11
+ end
12
+
13
+ def mark_as_unread!
14
+ update_param!(:unread, true)
15
+ end
16
+
17
+ def star!
18
+ update_param!(:starred, true)
19
+ end
20
+
21
+ def unstar!
22
+ update_param!(:starred, false)
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,394 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+ require 'yajl'
4
+ require 'em-http'
5
+
6
+
7
+ require 'ostruct'
8
+ require 'active_support/core_ext/hash'
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'
26
+
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-Inbox-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")
216
+
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
224
+
225
+ def using_hosted_api?
226
+ return !@app_id.nil?
227
+ end
228
+
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
236
+
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_exclude_types(exclude_types)
272
+ exclude_string = "&exclude_types="
273
+
274
+ exclude_types.each do |value|
275
+ count = 0
276
+ if OBJECTS_TABLE.has_value?(value)
277
+ param_name = OBJECTS_TABLE.key(value)
278
+ exclude_string += "#{param_name},"
279
+ end
280
+ end
281
+
282
+ exclude_string = exclude_string[0..-2]
283
+ end
284
+
285
+ def deltas(cursor, exclude_types=[], expanded_view=false)
286
+ return enum_for(:deltas, cursor, exclude_types, expanded_view) unless block_given?
287
+
288
+ exclude_string = ""
289
+
290
+ if exclude_types.any?
291
+ exclude_string = _build_exclude_types(exclude_types)
292
+ end
293
+
294
+ # loop and yield deltas until we've come to the end.
295
+ loop do
296
+ path = self.url_for_path("/delta?exclude_folders=false&cursor=#{cursor}#{exclude_string}")
297
+ if expanded_view
298
+ path += '&view=expanded'
299
+ end
300
+
301
+ json = nil
302
+
303
+ RestClient.get(path) do |response,request,result|
304
+ json = Nylas.interpret_response(result, response, {:expected_class => Object})
305
+ end
306
+
307
+ start_cursor = json["cursor_start"]
308
+ end_cursor = json["cursor_end"]
309
+
310
+ json["deltas"].each do |delta|
311
+ if not OBJECTS_TABLE.has_key?(delta['object'])
312
+ next
313
+ end
314
+
315
+ cls = OBJECTS_TABLE[delta['object']]
316
+ if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
317
+ cls = EXPANDED_OBJECTS_TABLE[delta['object']]
318
+ end
319
+
320
+ obj = cls.new(self)
321
+
322
+ case delta["event"]
323
+ when 'create', 'modify'
324
+ obj.inflate(delta['attributes'])
325
+ obj.cursor = delta["cursor"]
326
+ yield delta["event"], obj
327
+ when 'delete'
328
+ obj.id = delta["id"]
329
+ obj.cursor = delta["cursor"]
330
+ yield delta["event"], obj
331
+ end
332
+ end
333
+
334
+ break if start_cursor == end_cursor
335
+ cursor = end_cursor
336
+ end
337
+ end
338
+
339
+ def delta_stream(cursor, exclude_types=[], timeout=0, expanded_view=false)
340
+ raise 'Please provide a block for receiving the delta objects' if !block_given?
341
+
342
+ exclude_string = ""
343
+
344
+ if exclude_types.any?
345
+ exclude_string = _build_exclude_types(exclude_types)
346
+ end
347
+
348
+ # loop and yield deltas indefinitely.
349
+ path = self.url_for_path("/delta/streaming?exclude_folders=false&cursor=#{cursor}#{exclude_string}")
350
+ if expanded_view
351
+ path += '&view=expanded'
352
+ end
353
+
354
+ parser = Yajl::Parser.new(:symbolize_keys => false)
355
+ parser.on_parse_complete = proc do |data|
356
+ delta = Nylas.interpret_response(OpenStruct.new(:code => '200'), data, {:expected_class => Object, :result_parsed => true})
357
+
358
+ if not OBJECTS_TABLE.has_key?(delta['object'])
359
+ next
360
+ end
361
+
362
+ cls = OBJECTS_TABLE[delta['object']]
363
+ if EXPANDED_OBJECTS_TABLE.has_key?(delta['object']) and expanded_view
364
+ cls = EXPANDED_OBJECTS_TABLE[delta['object']]
365
+ end
366
+
367
+ obj = cls.new(self)
368
+
369
+ case delta["event"]
370
+ when 'create', 'modify'
371
+ obj.inflate(delta['attributes'])
372
+ obj.cursor = delta["cursor"]
373
+ yield delta["event"], obj
374
+ when 'delete'
375
+ obj.id = delta["id"]
376
+ obj.cursor = delta["cursor"]
377
+ yield delta["event"], obj
378
+ end
379
+ end
380
+
381
+ http = EventMachine::HttpRequest.new(path, :connect_timeout => 0, :inactivity_timeout => timeout).get(:keepalive => true)
382
+
383
+ # set a callback on the HTTP stream that parses incoming chunks as they come in
384
+ http.stream do |chunk|
385
+ parser << chunk
386
+ end
387
+
388
+ http.errback do
389
+ raise UnexpectedResponse.new http.error
390
+ end
391
+
392
+ end
393
+ end
394
+ end
@@ -0,0 +1,26 @@
1
+ module Nylas
2
+ module Parameters
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ def parameters
8
+ self.class.instance_variable_get("@parameters")
9
+ end
10
+
11
+ module ClassMethods
12
+ def parameter(*params)
13
+ @parameters ||= []
14
+ params.each do |param|
15
+ attr_accessor param
16
+ @parameters << param.to_s
17
+ end
18
+ end
19
+
20
+ def inherited(subclass)
21
+ parameters = instance_variable_get("@parameters") || []
22
+ subclass.instance_variable_set("@parameters", parameters.clone)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,82 @@
1
+ require 'time_attr_accessor'
2
+ require 'parameters'
3
+
4
+ module Nylas
5
+ class RestfulModel
6
+ extend Nylas::TimeAttrAccessor
7
+ include Nylas::Parameters
8
+
9
+ parameter :id
10
+ parameter :account_id
11
+ parameter :cursor # Only used by the delta sync API
12
+ time_attr_accessor :created_at
13
+ attr_reader :raw_json
14
+
15
+ def self.collection_name
16
+ "#{self.to_s.downcase}s".split('::').last
17
+ end
18
+
19
+ def initialize(api, account_id = nil)
20
+ raise StandardError.new unless api.class <= Nylas::API
21
+ @account_id = account_id
22
+ @_api = api
23
+ end
24
+
25
+ def ==(comparison_object)
26
+ comparison_object.equal?(self) || (comparison_object.instance_of?(self.class) && comparison_object.id == id)
27
+ end
28
+
29
+ def inflate(json)
30
+ @raw_json = json
31
+ parameters.each do |property_name|
32
+ send("#{property_name}=", json[property_name]) if json.has_key?(property_name)
33
+ end
34
+ end
35
+
36
+ def save!(params={})
37
+ if id
38
+ update('PUT', '', as_json(), params)
39
+ else
40
+ update('POST', '', as_json(), params)
41
+ end
42
+ end
43
+
44
+ def url(action = "")
45
+ action = "/#{action}" unless action.empty?
46
+ @_api.url_for_path("/#{self.class.collection_name}/#{id}#{action}")
47
+ end
48
+
49
+ def as_json(options = {})
50
+ hash = {}
51
+ parameters.each do |getter|
52
+ unless options[:except] && options[:except].include?(getter)
53
+ value = send(getter)
54
+ unless value.is_a?(RestfulModelCollection)
55
+ value = value.as_json(options) if value.respond_to?(:as_json)
56
+ hash[getter] = value
57
+ end
58
+ end
59
+ end
60
+ hash
61
+ end
62
+
63
+ def update(http_method, action, data = {}, params = {})
64
+ http_method = http_method.downcase
65
+
66
+ ::RestClient.send(http_method, self.url(action), data.to_json, :content_type => :json, :params => params) do |response, request, result|
67
+ unless http_method == 'delete'
68
+ json = Nylas.interpret_response(result, response, :expected_class => Object)
69
+ inflate(json)
70
+ end
71
+ end
72
+ self
73
+ end
74
+
75
+ def destroy(params = {})
76
+ ::RestClient.send('delete', self.url, :params => params) do |response, request, result|
77
+ Nylas.interpret_response(result, response, {:raw_response => true})
78
+ end
79
+ end
80
+
81
+ end
82
+ end