nylas_v2 5.14.2
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 +7 -0
- data/lib/nylas_v2/account.rb +56 -0
- data/lib/nylas_v2/api.rb +244 -0
- data/lib/nylas_v2/application_details.rb +13 -0
- data/lib/nylas_v2/calendar.rb +46 -0
- data/lib/nylas_v2/calendar_collection.rb +144 -0
- data/lib/nylas_v2/categorize.rb +14 -0
- data/lib/nylas_v2/collection.rb +175 -0
- data/lib/nylas_v2/component.rb +35 -0
- data/lib/nylas_v2/component_collection.rb +10 -0
- data/lib/nylas_v2/constraints.rb +56 -0
- data/lib/nylas_v2/contact.rb +53 -0
- data/lib/nylas_v2/contact_group.rb +23 -0
- data/lib/nylas_v2/current_account.rb +23 -0
- data/lib/nylas_v2/delta.rb +56 -0
- data/lib/nylas_v2/deltas.rb +19 -0
- data/lib/nylas_v2/deltas_collection.rb +40 -0
- data/lib/nylas_v2/draft.rb +100 -0
- data/lib/nylas_v2/email_address.rb +12 -0
- data/lib/nylas_v2/errors.rb +111 -0
- data/lib/nylas_v2/event.rb +141 -0
- data/lib/nylas_v2/event_collection.rb +15 -0
- data/lib/nylas_v2/event_conferencing.rb +12 -0
- data/lib/nylas_v2/event_conferencing_autocreate.rb +10 -0
- data/lib/nylas_v2/event_conferencing_details.rb +14 -0
- data/lib/nylas_v2/event_notification.rb +17 -0
- data/lib/nylas_v2/file.rb +75 -0
- data/lib/nylas_v2/filter_attributes.rb +25 -0
- data/lib/nylas_v2/folder.rb +26 -0
- data/lib/nylas_v2/free_busy.rb +13 -0
- data/lib/nylas_v2/free_busy_collection.rb +48 -0
- data/lib/nylas_v2/http_client.rb +279 -0
- data/lib/nylas_v2/im_address.rb +11 -0
- data/lib/nylas_v2/job_status.rb +27 -0
- data/lib/nylas_v2/job_status_collection.rb +21 -0
- data/lib/nylas_v2/label.rb +27 -0
- data/lib/nylas_v2/logging.rb +41 -0
- data/lib/nylas_v2/message.rb +98 -0
- data/lib/nylas_v2/message_headers.rb +27 -0
- data/lib/nylas_v2/message_tracking.rb +13 -0
- data/lib/nylas_v2/model/attributable.rb +89 -0
- data/lib/nylas_v2/model/attribute_definition.rb +24 -0
- data/lib/nylas_v2/model/attributes.rb +97 -0
- data/lib/nylas_v2/model/list_attribute_definition.rb +39 -0
- data/lib/nylas_v2/model/transferable.rb +53 -0
- data/lib/nylas_v2/model.rb +217 -0
- data/lib/nylas_v2/native_authentication.rb +39 -0
- data/lib/nylas_v2/neural.rb +87 -0
- data/lib/nylas_v2/neural_categorizer.rb +29 -0
- data/lib/nylas_v2/neural_clean_conversation.rb +33 -0
- data/lib/nylas_v2/neural_contact_link.rb +11 -0
- data/lib/nylas_v2/neural_contact_name.rb +11 -0
- data/lib/nylas_v2/neural_message_options.rb +35 -0
- data/lib/nylas_v2/neural_ocr.rb +16 -0
- data/lib/nylas_v2/neural_sentiment_analysis.rb +17 -0
- data/lib/nylas_v2/neural_signature_contact.rb +81 -0
- data/lib/nylas_v2/neural_signature_extraction.rb +18 -0
- data/lib/nylas_v2/new_message.rb +39 -0
- data/lib/nylas_v2/nylas_date.rb +25 -0
- data/lib/nylas_v2/open_hours.rb +15 -0
- data/lib/nylas_v2/outbox.rb +116 -0
- data/lib/nylas_v2/outbox_job_status.rb +19 -0
- data/lib/nylas_v2/outbox_message.rb +17 -0
- data/lib/nylas_v2/participant.rb +13 -0
- data/lib/nylas_v2/phone_number.rb +11 -0
- data/lib/nylas_v2/physical_address.rb +17 -0
- data/lib/nylas_v2/raw_message.rb +25 -0
- data/lib/nylas_v2/recurrence.rb +11 -0
- data/lib/nylas_v2/registry.rb +42 -0
- data/lib/nylas_v2/room_resource.rb +19 -0
- data/lib/nylas_v2/rsvp.rb +24 -0
- data/lib/nylas_v2/scheduler.rb +51 -0
- data/lib/nylas_v2/scheduler_booking_confirmation.rb +24 -0
- data/lib/nylas_v2/scheduler_booking_request.rb +17 -0
- data/lib/nylas_v2/scheduler_collection.rb +104 -0
- data/lib/nylas_v2/scheduler_config.rb +20 -0
- data/lib/nylas_v2/scheduler_time_slot.rb +14 -0
- data/lib/nylas_v2/search_collection.rb +10 -0
- data/lib/nylas_v2/send_grid_verified_status.rb +12 -0
- data/lib/nylas_v2/thread.rb +66 -0
- data/lib/nylas_v2/time_slot.rb +16 -0
- data/lib/nylas_v2/time_slot_capacity.rb +13 -0
- data/lib/nylas_v2/timespan.rb +20 -0
- data/lib/nylas_v2/token_info.rb +20 -0
- data/lib/nylas_v2/types.rb +168 -0
- data/lib/nylas_v2/version.rb +5 -0
- data/lib/nylas_v2/web_page.rb +11 -0
- data/lib/nylas_v2/webhook.rb +98 -0
- data/lib/nylas_v2/when.rb +75 -0
- data/lib/nylas_v2.rb +162 -0
- metadata +415 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
require "yajl"
|
|
5
|
+
require "base64"
|
|
6
|
+
|
|
7
|
+
# Plain HTTP client that can be used to interact with the Nylas API sans any type casting.
|
|
8
|
+
class HttpClient
|
|
9
|
+
module AuthMethod
|
|
10
|
+
BEARER = 1
|
|
11
|
+
BASIC = 2
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
HTTP_SUCCESS_CODES = [200, 201, 202, 302].freeze
|
|
15
|
+
|
|
16
|
+
HTTP_CODE_TO_EXCEPTIONS = {
|
|
17
|
+
400 => InvalidRequest,
|
|
18
|
+
401 => UnauthorizedRequest,
|
|
19
|
+
402 => MessageRejected,
|
|
20
|
+
403 => AccessDenied,
|
|
21
|
+
404 => ResourceNotFound,
|
|
22
|
+
405 => MethodNotAllowed,
|
|
23
|
+
410 => ResourceRemoved,
|
|
24
|
+
418 => TeapotError,
|
|
25
|
+
422 => MailProviderError,
|
|
26
|
+
429 => SendingQuotaExceeded,
|
|
27
|
+
500 => InternalError,
|
|
28
|
+
501 => EndpointNotYetImplemented,
|
|
29
|
+
502 => BadGateway,
|
|
30
|
+
503 => ServiceUnavailable,
|
|
31
|
+
504 => RequestTimedOut
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
ENDPOINT_TIMEOUTS = {
|
|
35
|
+
"/oauth/authorize" => 345,
|
|
36
|
+
"/messages/search" => 350,
|
|
37
|
+
"/threads/search" => 350,
|
|
38
|
+
"/delta" => 3650,
|
|
39
|
+
"/delta/longpoll" => 3650,
|
|
40
|
+
"/delta/streaming" => 3650
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
SUPPORTED_API_VERSION = "2.5"
|
|
44
|
+
|
|
45
|
+
include Logging
|
|
46
|
+
attr_accessor :api_server
|
|
47
|
+
attr_writer :default_headers
|
|
48
|
+
attr_reader :access_token
|
|
49
|
+
attr_reader :app_id
|
|
50
|
+
attr_reader :app_secret
|
|
51
|
+
|
|
52
|
+
# @param app_id [String] Your application id from the Nylas Dashboard
|
|
53
|
+
# @param app_secret [String] Your application secret from the Nylas Dashboard
|
|
54
|
+
# @param access_token [String] (Optional) Your users access token.
|
|
55
|
+
# @param api_server [String] (Optional) Which Nylas API Server to connect to. Only change this if
|
|
56
|
+
# you're using a self-hosted Nylas instance.
|
|
57
|
+
# @return [NylasV2::HttpClient]
|
|
58
|
+
def initialize(app_id:, app_secret:, access_token: nil, api_server: "https://api.nylas.com")
|
|
59
|
+
unless api_server.include?("://")
|
|
60
|
+
raise "When overriding the Nylas API server address, you must include https://"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
@api_server = api_server
|
|
64
|
+
@access_token = access_token
|
|
65
|
+
@app_secret = app_secret
|
|
66
|
+
@app_id = app_id
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [NylasV2::HttpClient[]
|
|
70
|
+
def as(access_token)
|
|
71
|
+
HttpClient.new(app_id: app_id, access_token: access_token,
|
|
72
|
+
app_secret: app_secret, api_server: api_server)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Sends a request to the Nylas API and rai
|
|
76
|
+
# @param method [Symbol] HTTP method for the API call. Either :get, :post, :delete, or :patch
|
|
77
|
+
# @param path [String] (Optional, defaults to nil) - Relative path from the API Base. Preferred way to
|
|
78
|
+
# execute arbitrary or-not-yet-SDK-ified API commands.
|
|
79
|
+
# @param headers [Hash] (Optional, defaults to {}) - Additional HTTP headers to include in the payload.
|
|
80
|
+
# @param query [Hash] (Optional, defaults to {}) - Hash of names and values to include in the query
|
|
81
|
+
# section of the URI fragment
|
|
82
|
+
# @param payload [String,Hash] (Optional, defaults to nil) - Body to send with the request.
|
|
83
|
+
# @param auth_method [AuthMethod] (Optional, defaults to BEARER) - The authentication method.
|
|
84
|
+
# @return [Array Hash Stringn]
|
|
85
|
+
# rubocop:disable Metrics/MethodLength
|
|
86
|
+
def execute(method:, path: nil, headers: {}, query: {}, payload: nil, auth_method: nil)
|
|
87
|
+
timeout = ENDPOINT_TIMEOUTS.fetch(path, 230)
|
|
88
|
+
request = build_request(
|
|
89
|
+
method: method,
|
|
90
|
+
path: path,
|
|
91
|
+
headers: headers,
|
|
92
|
+
query: query,
|
|
93
|
+
payload: payload,
|
|
94
|
+
timeout: timeout,
|
|
95
|
+
auth_method: auth_method || AuthMethod::BEARER
|
|
96
|
+
)
|
|
97
|
+
rest_client_execute(**request) do |response, _request, result|
|
|
98
|
+
content_type = nil
|
|
99
|
+
|
|
100
|
+
if response.headers && response.headers[:content_type]
|
|
101
|
+
content_type = response.headers[:content_type].downcase
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
begin
|
|
105
|
+
response = parse_response(response) if content_type == "application/json"
|
|
106
|
+
rescue NylasV2::JsonParseError
|
|
107
|
+
handle_failed_response(result: result, response: response)
|
|
108
|
+
raise
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
handle_failed_response(result: result, response: response)
|
|
112
|
+
response
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
# rubocop:enable Metrics/MethodLength
|
|
116
|
+
inform_on :execute, level: :debug,
|
|
117
|
+
also_log: { result: true, values: %i[method url path headers query payload] }
|
|
118
|
+
|
|
119
|
+
def build_request(
|
|
120
|
+
method:,
|
|
121
|
+
path: nil,
|
|
122
|
+
headers: {},
|
|
123
|
+
query: {},
|
|
124
|
+
payload: nil,
|
|
125
|
+
timeout: nil,
|
|
126
|
+
auth_method: nil
|
|
127
|
+
)
|
|
128
|
+
url ||= url_for_path(path)
|
|
129
|
+
url = add_query_params_to_url(url, query)
|
|
130
|
+
resulting_headers = default_headers.merge(headers).merge(auth_header(auth_method))
|
|
131
|
+
{ method: method, url: url, payload: payload, headers: resulting_headers, timeout: timeout }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Syntactical sugar for making GET requests via the API.
|
|
135
|
+
# @see #execute
|
|
136
|
+
def get(path: nil, headers: {}, query: {}, auth_method: nil)
|
|
137
|
+
execute(method: :get, path: path, query: query, headers: headers, auth_method: auth_method)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Syntactical sugar for making POST requests via the API.
|
|
141
|
+
# @see #execute
|
|
142
|
+
def post(path: nil, payload: nil, headers: {}, query: {}, auth_method: nil)
|
|
143
|
+
execute(
|
|
144
|
+
method: :post,
|
|
145
|
+
path: path,
|
|
146
|
+
headers: headers,
|
|
147
|
+
query: query,
|
|
148
|
+
payload: payload,
|
|
149
|
+
auth_method: auth_method
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Syntactical sugar for making PUT requests via the API.
|
|
154
|
+
# @see #execute
|
|
155
|
+
def put(path: nil, payload:, headers: {}, query: {}, auth_method: nil)
|
|
156
|
+
execute(
|
|
157
|
+
method: :put,
|
|
158
|
+
path: path,
|
|
159
|
+
headers: headers,
|
|
160
|
+
query: query,
|
|
161
|
+
payload: payload,
|
|
162
|
+
auth_method: auth_method
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Syntactical sugar for making DELETE requests via the API.
|
|
167
|
+
# @see #execute
|
|
168
|
+
def delete(path: nil, payload: nil, headers: {}, query: {}, auth_method: nil)
|
|
169
|
+
execute(
|
|
170
|
+
method: :delete,
|
|
171
|
+
path: path,
|
|
172
|
+
headers: headers,
|
|
173
|
+
query: query,
|
|
174
|
+
payload: payload,
|
|
175
|
+
auth_method: auth_method
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def default_headers
|
|
180
|
+
@default_headers ||= {
|
|
181
|
+
"X-Nylas-API-Wrapper" => "ruby",
|
|
182
|
+
"X-Nylas-Client-Id" => @app_id,
|
|
183
|
+
"Nylas-API-Version" => SUPPORTED_API_VERSION,
|
|
184
|
+
"User-Agent" => "Nylas Ruby SDK #{NylasV2::VERSION} - #{RUBY_VERSION}",
|
|
185
|
+
"Content-type" => "application/json"
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def parse_response(response)
|
|
190
|
+
return response if response.is_a?(Enumerable)
|
|
191
|
+
|
|
192
|
+
Yajl::Parser.new(symbolize_names: true).parse(response)
|
|
193
|
+
rescue Yajl::ParseError
|
|
194
|
+
raise NylasV2::JsonParseError
|
|
195
|
+
end
|
|
196
|
+
inform_on :parse_response, level: :debug, also_log: { result: true }
|
|
197
|
+
|
|
198
|
+
def url_for_path(path)
|
|
199
|
+
protocol, domain = api_server.split("//")
|
|
200
|
+
"#{protocol}//#{access_token}:@#{domain}#{path}"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
private
|
|
204
|
+
|
|
205
|
+
def rest_client_execute(method:, url:, headers:, payload:, timeout:, &block)
|
|
206
|
+
::RestClient::Request.execute(method: method, url: url, payload: payload,
|
|
207
|
+
headers: headers, timeout: timeout, &block)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
inform_on :rest_client_execute, level: :debug,
|
|
211
|
+
also_log: { result: true, values: %i[method url headers payload] }
|
|
212
|
+
|
|
213
|
+
def handle_failed_response(result:, response:)
|
|
214
|
+
http_code = result.code.to_i
|
|
215
|
+
|
|
216
|
+
handle_anticipated_failure_mode(http_code: http_code, response: response)
|
|
217
|
+
raise UnexpectedResponse, result.msg if result.is_a?(Net::HTTPClientError)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def handle_anticipated_failure_mode(http_code:, response:)
|
|
221
|
+
return if HTTP_SUCCESS_CODES.include?(http_code)
|
|
222
|
+
|
|
223
|
+
exception = HTTP_CODE_TO_EXCEPTIONS.fetch(http_code, APIError)
|
|
224
|
+
case response
|
|
225
|
+
when Hash
|
|
226
|
+
raise error_hash_to_exception(exception, response)
|
|
227
|
+
when RestClient::Response
|
|
228
|
+
raise exception.parse_error_response(response)
|
|
229
|
+
else
|
|
230
|
+
raise exception.new(http_code, response)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def error_hash_to_exception(exception, response)
|
|
235
|
+
exception.new(
|
|
236
|
+
response[:type],
|
|
237
|
+
response[:message],
|
|
238
|
+
response.fetch(:server_error, nil)
|
|
239
|
+
)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def add_query_params_to_url(url, query)
|
|
243
|
+
unless query.empty?
|
|
244
|
+
uri = URI.parse(url)
|
|
245
|
+
query = custom_params(query)
|
|
246
|
+
params = URI.decode_www_form(uri.query || "") + query.to_a
|
|
247
|
+
uri.query = URI.encode_www_form(params)
|
|
248
|
+
url = uri.to_s
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
url
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def custom_params(query)
|
|
255
|
+
# Convert hash to "<key>:<value>" form for metadata_pair query
|
|
256
|
+
if query.key?(:metadata_pair)
|
|
257
|
+
pairs = query[:metadata_pair].map do |key, value|
|
|
258
|
+
"#{key}:#{value}"
|
|
259
|
+
end
|
|
260
|
+
query[:metadata_pair] = pairs
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
query
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def auth_header(auth_method)
|
|
267
|
+
authorization_string = case auth_method
|
|
268
|
+
when AuthMethod::BEARER
|
|
269
|
+
"Bearer #{access_token}"
|
|
270
|
+
when AuthMethod::BASIC
|
|
271
|
+
"Basic #{Base64.encode64("#{access_token}:")}"
|
|
272
|
+
else
|
|
273
|
+
"Bearer #{access_token}"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
{ "Authorization" => authorization_string }
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
# Structure to represent the IM Address Schema
|
|
5
|
+
# @see https://docs.nylas.com/reference#contactsid
|
|
6
|
+
class IMAddress
|
|
7
|
+
include Model::Attributable
|
|
8
|
+
attribute :type, :string
|
|
9
|
+
attribute :im_address, :string
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
# Ruby representation of a Nylas Job Status object
|
|
5
|
+
# @see https://developer.nylas.com/docs/api/#tag--Job-Status
|
|
6
|
+
class JobStatus
|
|
7
|
+
include Model
|
|
8
|
+
self.resources_path = "/job-statuses"
|
|
9
|
+
self.listable = true
|
|
10
|
+
|
|
11
|
+
attribute :id, :string, read_only: true
|
|
12
|
+
attribute :account_id, :string, read_only: true
|
|
13
|
+
attribute :job_status_id, :string, read_only: true
|
|
14
|
+
attribute :action, :string, read_only: true
|
|
15
|
+
attribute :object, :string, read_only: true
|
|
16
|
+
attribute :status, :string, read_only: true
|
|
17
|
+
attribute :created_at, :unix_timestamp, read_only: true
|
|
18
|
+
attribute :reason, :string, read_only: true
|
|
19
|
+
attribute :metadata, :hash, read_only: true
|
|
20
|
+
|
|
21
|
+
# Returns the status of a job as a boolean
|
|
22
|
+
# @return [Boolean] If the job was successful
|
|
23
|
+
def successful?
|
|
24
|
+
status == "successful"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
# Additional methods for some of Calendar's other functionality
|
|
5
|
+
# @see https://developer.nylas.com/docs/connectivity/calendar
|
|
6
|
+
class JobStatusCollection < Collection
|
|
7
|
+
def find_model(id)
|
|
8
|
+
response = api.execute(
|
|
9
|
+
**to_be_executed.merge(
|
|
10
|
+
path: "#{resources_path}/#{id}",
|
|
11
|
+
query: view_query
|
|
12
|
+
)
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
object_type = response[:object]
|
|
16
|
+
return OutboxJobStatus.from_hash(response, api: api) if object_type == "message"
|
|
17
|
+
|
|
18
|
+
model.from_hash(response, api: api)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
# Structure to represent the Label Schema
|
|
5
|
+
# @see https://docs.nylas.com/reference#labels
|
|
6
|
+
class Label
|
|
7
|
+
include Model
|
|
8
|
+
self.resources_path = "/labels"
|
|
9
|
+
self.creatable = true
|
|
10
|
+
self.listable = true
|
|
11
|
+
self.showable = true
|
|
12
|
+
self.filterable = false
|
|
13
|
+
self.updatable = true
|
|
14
|
+
self.destroyable = true
|
|
15
|
+
self.id_listable = true
|
|
16
|
+
self.countable = true
|
|
17
|
+
|
|
18
|
+
attribute :id, :string
|
|
19
|
+
attribute :account_id, :string
|
|
20
|
+
|
|
21
|
+
attribute :object, :string
|
|
22
|
+
|
|
23
|
+
attribute :name, :string
|
|
24
|
+
attribute :display_name, :string
|
|
25
|
+
attribute :job_status_id, :string, read_only: true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "informed"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module NylasV2
|
|
9
|
+
# Exposes a shared logger for debugging purposes
|
|
10
|
+
module Logging
|
|
11
|
+
def self.included(object)
|
|
12
|
+
if const_defined? :Informed
|
|
13
|
+
object.include Informed
|
|
14
|
+
Informed.logger = logger
|
|
15
|
+
else
|
|
16
|
+
object.extend NoOpInformOn
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.logger
|
|
21
|
+
return @logger if @logger
|
|
22
|
+
|
|
23
|
+
@logger = Logger.new(STDOUT)
|
|
24
|
+
@logger.level = level
|
|
25
|
+
@logger
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.level
|
|
29
|
+
Logger.const_get(ENV["NYLAS_LOG_LEVEL"] || :WARN)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.logger=(logger)
|
|
33
|
+
@logger = logger
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# No op for inform_on if user does not have the informed gem installed.
|
|
37
|
+
module NoOpInformOn
|
|
38
|
+
def inform_on(method, level: :debug, also_log: {}); end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
# Ruby representatin of a Nylas Message object
|
|
5
|
+
# @see https://docs.nylas.com/reference#messages
|
|
6
|
+
class Message
|
|
7
|
+
include Model
|
|
8
|
+
self.raw_mime_type = "message/rfc822"
|
|
9
|
+
self.resources_path = "/messages"
|
|
10
|
+
self.listable = true
|
|
11
|
+
self.showable = true
|
|
12
|
+
self.filterable = true
|
|
13
|
+
self.updatable = true
|
|
14
|
+
self.searchable = true
|
|
15
|
+
self.id_listable = true
|
|
16
|
+
self.countable = true
|
|
17
|
+
UPDATABLE_ATTRIBUTES = %i[label_ids folder_id starred unread metadata].freeze
|
|
18
|
+
|
|
19
|
+
attribute :id, :string
|
|
20
|
+
attribute :object, :string
|
|
21
|
+
attribute :account_id, :string
|
|
22
|
+
attribute :thread_id, :string
|
|
23
|
+
|
|
24
|
+
attribute :headers, :message_headers
|
|
25
|
+
|
|
26
|
+
has_n_of_attribute :to, :email_address
|
|
27
|
+
has_n_of_attribute :from, :email_address
|
|
28
|
+
has_n_of_attribute :cc, :email_address
|
|
29
|
+
has_n_of_attribute :bcc, :email_address
|
|
30
|
+
has_n_of_attribute :reply_to, :email_address
|
|
31
|
+
|
|
32
|
+
attribute :date, :unix_timestamp
|
|
33
|
+
# This is only used when receiving a message received notification via a webhook
|
|
34
|
+
attribute :received_date, :unix_timestamp
|
|
35
|
+
attribute :subject, :string
|
|
36
|
+
attribute :snippet, :string
|
|
37
|
+
attribute :body, :string
|
|
38
|
+
attribute :starred, :boolean
|
|
39
|
+
attribute :unread, :boolean
|
|
40
|
+
|
|
41
|
+
has_n_of_attribute :events, :event
|
|
42
|
+
has_n_of_attribute :files, :file
|
|
43
|
+
attribute :folder, :folder
|
|
44
|
+
attribute :folder_id, :string
|
|
45
|
+
attribute :metadata, :hash
|
|
46
|
+
attribute :reply_to_message_id, :string, read_only: true
|
|
47
|
+
attribute :job_status_id, :string, read_only: true
|
|
48
|
+
|
|
49
|
+
has_n_of_attribute :labels, :label, read_only: true
|
|
50
|
+
has_n_of_attribute :label_ids, :string
|
|
51
|
+
|
|
52
|
+
transfer :api, to: %i[events files folder labels]
|
|
53
|
+
|
|
54
|
+
def starred?
|
|
55
|
+
starred
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def unread?
|
|
59
|
+
unread
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def update(payload)
|
|
63
|
+
FilterAttributes.new(
|
|
64
|
+
attributes: payload.keys,
|
|
65
|
+
allowed_attributes: UPDATABLE_ATTRIBUTES
|
|
66
|
+
).check
|
|
67
|
+
|
|
68
|
+
super(**payload)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def update_folder(folder_id)
|
|
72
|
+
update(folder_id: folder_id)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def expanded
|
|
76
|
+
return self unless headers.nil?
|
|
77
|
+
|
|
78
|
+
assign(**api.execute(method: :get, path: resource_path, query: { view: "expanded" }))
|
|
79
|
+
# Transfer reference to the API to attributes that need it
|
|
80
|
+
transfer_attributes
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def save
|
|
85
|
+
handle_folder
|
|
86
|
+
|
|
87
|
+
super
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def handle_folder
|
|
91
|
+
return if folder.nil?
|
|
92
|
+
|
|
93
|
+
self.folder_id = folder.id if folder_id.nil? && !self.to_h.dig(:folder, :id).nil?
|
|
94
|
+
|
|
95
|
+
self.folder = nil
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
# Translates message headers into a Ruby object
|
|
5
|
+
# @see https://docs.nylas.com/reference#section-message-views
|
|
6
|
+
class MessageHeaders
|
|
7
|
+
include Model::Attributable
|
|
8
|
+
attribute :in_reply_to, :string
|
|
9
|
+
attribute :message_id, :string
|
|
10
|
+
has_n_of_attribute :references, :string
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Serializes, Deserializes between {MessageHeaders} objects and a Hash
|
|
14
|
+
class MessageHeadersType < Types::ModelType
|
|
15
|
+
def initialize
|
|
16
|
+
super(model: MessageHeaders)
|
|
17
|
+
end
|
|
18
|
+
RUBY_KEY_TO_JSON_KEY_MAP = {
|
|
19
|
+
in_reply_to: :"In-Reply-To",
|
|
20
|
+
message_id: :"Message-Id",
|
|
21
|
+
references: :References
|
|
22
|
+
}.freeze
|
|
23
|
+
def json_key_from_attribute_name(attribute_name)
|
|
24
|
+
RUBY_KEY_TO_JSON_KEY_MAP.fetch(attribute_name)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
# Message tracking features
|
|
5
|
+
# @see https://docs.nylas.com/reference#message-tracking-overview
|
|
6
|
+
class MessageTracking
|
|
7
|
+
include Model::Attributable
|
|
8
|
+
attribute :links, :boolean
|
|
9
|
+
attribute :opens, :boolean
|
|
10
|
+
attribute :thread_replies, :boolean
|
|
11
|
+
attribute :payload, :string
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
module Model
|
|
5
|
+
# Allows defining of typecastable attributes on a model
|
|
6
|
+
module Attributable
|
|
7
|
+
def self.included(model)
|
|
8
|
+
model.extend(ClassMethods)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(**initial_data)
|
|
12
|
+
assign(**initial_data)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def attributes
|
|
16
|
+
@attributes ||= Attributes.new(self.class.attribute_definitions)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [Hash] Representation of the model with values serialized into primitives based on their Type
|
|
20
|
+
def to_h(enforce_read_only: false)
|
|
21
|
+
attributes.to_h(enforce_read_only: enforce_read_only)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
def assign(**data)
|
|
27
|
+
data.each do |attribute_name, value|
|
|
28
|
+
next if value.nil?
|
|
29
|
+
|
|
30
|
+
if respond_to?(:"#{attribute_name}=")
|
|
31
|
+
send(:"#{attribute_name}=", value)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Methods to call when tweaking Attributable classes
|
|
37
|
+
module ClassMethods
|
|
38
|
+
# rubocop:disable Naming/PredicateName
|
|
39
|
+
def has_n_of_attribute(name, type_name, read_only: false, default: [])
|
|
40
|
+
attribute_definitions[name] = ListAttributeDefinition.new(
|
|
41
|
+
type_name: type_name,
|
|
42
|
+
read_only: read_only,
|
|
43
|
+
default: default
|
|
44
|
+
)
|
|
45
|
+
define_accessors(name)
|
|
46
|
+
end
|
|
47
|
+
# rubocop:enable Naming/PredicateName
|
|
48
|
+
|
|
49
|
+
def attribute(name, type_name, read_only: false, default: nil)
|
|
50
|
+
attribute_definitions[name] = AttributeDefinition.new(
|
|
51
|
+
type_name: type_name,
|
|
52
|
+
read_only: read_only,
|
|
53
|
+
default: default
|
|
54
|
+
)
|
|
55
|
+
define_accessors(name)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def define_accessors(name)
|
|
59
|
+
define_method :"#{name}" do
|
|
60
|
+
attributes[name]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
define_method :"#{name}=" do |value|
|
|
64
|
+
attributes[name] = value
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Allows a class to inherit parent's attributes
|
|
69
|
+
def inherit_attributes
|
|
70
|
+
return if superclass.nil?
|
|
71
|
+
|
|
72
|
+
parent_attributes = superclass.attribute_definitions
|
|
73
|
+
parent_attributes.each do |parent_attribute|
|
|
74
|
+
name = parent_attribute[0]
|
|
75
|
+
attr = parent_attribute[1]
|
|
76
|
+
next if attribute_definitions.key?(name)
|
|
77
|
+
|
|
78
|
+
attribute_definitions[name] = attr
|
|
79
|
+
define_accessors(name)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def attribute_definitions
|
|
84
|
+
@attribute_definitions ||= Registry.new
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module NylasV2
|
|
4
|
+
module Model
|
|
5
|
+
# Define a particular attribute for a given model
|
|
6
|
+
class AttributeDefinition
|
|
7
|
+
extend Forwardable
|
|
8
|
+
def_delegators :type, :cast, :serialize, :serialize_for_api
|
|
9
|
+
attr_accessor :type_name, :read_only, :default
|
|
10
|
+
|
|
11
|
+
def initialize(type_name:, read_only:, default:)
|
|
12
|
+
self.type_name = type_name
|
|
13
|
+
self.read_only = read_only
|
|
14
|
+
self.default = default
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def type
|
|
20
|
+
Types.registry[type_name]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|