nylas-shipmnts 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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