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.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/lib/nylas_v2/account.rb +56 -0
  3. data/lib/nylas_v2/api.rb +244 -0
  4. data/lib/nylas_v2/application_details.rb +13 -0
  5. data/lib/nylas_v2/calendar.rb +46 -0
  6. data/lib/nylas_v2/calendar_collection.rb +144 -0
  7. data/lib/nylas_v2/categorize.rb +14 -0
  8. data/lib/nylas_v2/collection.rb +175 -0
  9. data/lib/nylas_v2/component.rb +35 -0
  10. data/lib/nylas_v2/component_collection.rb +10 -0
  11. data/lib/nylas_v2/constraints.rb +56 -0
  12. data/lib/nylas_v2/contact.rb +53 -0
  13. data/lib/nylas_v2/contact_group.rb +23 -0
  14. data/lib/nylas_v2/current_account.rb +23 -0
  15. data/lib/nylas_v2/delta.rb +56 -0
  16. data/lib/nylas_v2/deltas.rb +19 -0
  17. data/lib/nylas_v2/deltas_collection.rb +40 -0
  18. data/lib/nylas_v2/draft.rb +100 -0
  19. data/lib/nylas_v2/email_address.rb +12 -0
  20. data/lib/nylas_v2/errors.rb +111 -0
  21. data/lib/nylas_v2/event.rb +141 -0
  22. data/lib/nylas_v2/event_collection.rb +15 -0
  23. data/lib/nylas_v2/event_conferencing.rb +12 -0
  24. data/lib/nylas_v2/event_conferencing_autocreate.rb +10 -0
  25. data/lib/nylas_v2/event_conferencing_details.rb +14 -0
  26. data/lib/nylas_v2/event_notification.rb +17 -0
  27. data/lib/nylas_v2/file.rb +75 -0
  28. data/lib/nylas_v2/filter_attributes.rb +25 -0
  29. data/lib/nylas_v2/folder.rb +26 -0
  30. data/lib/nylas_v2/free_busy.rb +13 -0
  31. data/lib/nylas_v2/free_busy_collection.rb +48 -0
  32. data/lib/nylas_v2/http_client.rb +279 -0
  33. data/lib/nylas_v2/im_address.rb +11 -0
  34. data/lib/nylas_v2/job_status.rb +27 -0
  35. data/lib/nylas_v2/job_status_collection.rb +21 -0
  36. data/lib/nylas_v2/label.rb +27 -0
  37. data/lib/nylas_v2/logging.rb +41 -0
  38. data/lib/nylas_v2/message.rb +98 -0
  39. data/lib/nylas_v2/message_headers.rb +27 -0
  40. data/lib/nylas_v2/message_tracking.rb +13 -0
  41. data/lib/nylas_v2/model/attributable.rb +89 -0
  42. data/lib/nylas_v2/model/attribute_definition.rb +24 -0
  43. data/lib/nylas_v2/model/attributes.rb +97 -0
  44. data/lib/nylas_v2/model/list_attribute_definition.rb +39 -0
  45. data/lib/nylas_v2/model/transferable.rb +53 -0
  46. data/lib/nylas_v2/model.rb +217 -0
  47. data/lib/nylas_v2/native_authentication.rb +39 -0
  48. data/lib/nylas_v2/neural.rb +87 -0
  49. data/lib/nylas_v2/neural_categorizer.rb +29 -0
  50. data/lib/nylas_v2/neural_clean_conversation.rb +33 -0
  51. data/lib/nylas_v2/neural_contact_link.rb +11 -0
  52. data/lib/nylas_v2/neural_contact_name.rb +11 -0
  53. data/lib/nylas_v2/neural_message_options.rb +35 -0
  54. data/lib/nylas_v2/neural_ocr.rb +16 -0
  55. data/lib/nylas_v2/neural_sentiment_analysis.rb +17 -0
  56. data/lib/nylas_v2/neural_signature_contact.rb +81 -0
  57. data/lib/nylas_v2/neural_signature_extraction.rb +18 -0
  58. data/lib/nylas_v2/new_message.rb +39 -0
  59. data/lib/nylas_v2/nylas_date.rb +25 -0
  60. data/lib/nylas_v2/open_hours.rb +15 -0
  61. data/lib/nylas_v2/outbox.rb +116 -0
  62. data/lib/nylas_v2/outbox_job_status.rb +19 -0
  63. data/lib/nylas_v2/outbox_message.rb +17 -0
  64. data/lib/nylas_v2/participant.rb +13 -0
  65. data/lib/nylas_v2/phone_number.rb +11 -0
  66. data/lib/nylas_v2/physical_address.rb +17 -0
  67. data/lib/nylas_v2/raw_message.rb +25 -0
  68. data/lib/nylas_v2/recurrence.rb +11 -0
  69. data/lib/nylas_v2/registry.rb +42 -0
  70. data/lib/nylas_v2/room_resource.rb +19 -0
  71. data/lib/nylas_v2/rsvp.rb +24 -0
  72. data/lib/nylas_v2/scheduler.rb +51 -0
  73. data/lib/nylas_v2/scheduler_booking_confirmation.rb +24 -0
  74. data/lib/nylas_v2/scheduler_booking_request.rb +17 -0
  75. data/lib/nylas_v2/scheduler_collection.rb +104 -0
  76. data/lib/nylas_v2/scheduler_config.rb +20 -0
  77. data/lib/nylas_v2/scheduler_time_slot.rb +14 -0
  78. data/lib/nylas_v2/search_collection.rb +10 -0
  79. data/lib/nylas_v2/send_grid_verified_status.rb +12 -0
  80. data/lib/nylas_v2/thread.rb +66 -0
  81. data/lib/nylas_v2/time_slot.rb +16 -0
  82. data/lib/nylas_v2/time_slot_capacity.rb +13 -0
  83. data/lib/nylas_v2/timespan.rb +20 -0
  84. data/lib/nylas_v2/token_info.rb +20 -0
  85. data/lib/nylas_v2/types.rb +168 -0
  86. data/lib/nylas_v2/version.rb +5 -0
  87. data/lib/nylas_v2/web_page.rb +11 -0
  88. data/lib/nylas_v2/webhook.rb +98 -0
  89. data/lib/nylas_v2/when.rb +75 -0
  90. data/lib/nylas_v2.rb +162 -0
  91. 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