nylas_v2 5.14.2

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