nylas 6.7.0 → 6.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc90a16ef74fc2f47d6f35ada72d9cd80e3e842e08d9d5b1916a819ae36c0808
4
- data.tar.gz: b2c8494899f1b2ff4620dfbcce0aeb52e037adfe97f1609bcaaf455bfb843745
3
+ metadata.gz: 39fb36891572d67e348da1b79d8d0f73c0c542f073252733a17c7a11ac5f041b
4
+ data.tar.gz: 234c99ad89acd1ef2069bea31a70f7f86e8acf8e5b6a0a38b6000e79e7a570e4
5
5
  SHA512:
6
- metadata.gz: c9cda94765c3957d6457071ac616dd12a3a807378773b566da0be82b39c5a89db370049f21744b91ed8f3a5abf211b6c75363f8df10f52d102007becfcd2a635
7
- data.tar.gz: e50d00d31fdb2ae23058d146b38cc75a5a75f239ba19220b5c28280defe4624b4ab4d858ec3abd496555ab6b0f35033ac2c606ed2bc346572f0593df0cebb5cc
6
+ metadata.gz: ae326a217e211366f66a2079cc98990c9ebad04bd0c48a88c6a1634adae55db6d9ee505060655eb4faee1a2deffb7d4447e007a5e5b09a3fdc44157743b20034
7
+ data.tar.gz: b5c494daa9bd4062765eb5182382f550d06abb20ee85d0c890dee54cede76af4ea5650765ebd9bc3bb151d404bb853b409e9695219a5bcf29156d27df6fd696d
data/lib/nylas/client.rb CHANGED
@@ -8,6 +8,7 @@ require_relative "resources/auth"
8
8
  require_relative "resources/webhooks"
9
9
  require_relative "resources/applications"
10
10
  require_relative "resources/folders"
11
+ require_relative "resources/lists"
11
12
  require_relative "resources/notetakers"
12
13
  require_relative "resources/scheduler"
13
14
 
@@ -99,6 +100,13 @@ module Nylas
99
100
  Grants.new(self)
100
101
  end
101
102
 
103
+ # The list resources for your Nylas application.
104
+ #
105
+ # @return [Nylas::Lists] List resources for your Nylas application
106
+ def lists
107
+ Lists.new(self)
108
+ end
109
+
102
110
  # The message resources for your Nylas application.
103
111
  #
104
112
  # @return [Nylas::Messages] Message resources for your Nylas application
@@ -113,6 +121,34 @@ module Nylas
113
121
  Threads.new(self)
114
122
  end
115
123
 
124
+ # The policy resources for your Nylas application.
125
+ #
126
+ # @return [Nylas::Policies] Policy resources for your Nylas application.
127
+ def policies
128
+ Policies.new(self)
129
+ end
130
+
131
+ # The rule resources for your Nylas application.
132
+ #
133
+ # @return [Nylas::Rules] Rule resources for your Nylas application.
134
+ def rules
135
+ Rules.new(self)
136
+ end
137
+
138
+ # The workspace resources for your Nylas application.
139
+ #
140
+ # @return [Nylas::Workspaces] Workspace resources for your Nylas application.
141
+ def workspaces
142
+ Workspaces.new(self)
143
+ end
144
+
145
+ # The domain resources for your Nylas application.
146
+ #
147
+ # @return [Nylas::Domains] Domain resources for your Nylas application.
148
+ def domains
149
+ Domains.new(self)
150
+ end
151
+
116
152
  # The webhook resources for your Nylas application.
117
153
  #
118
154
  # @return [Nylas::Webhooks] Webhook resources for your Nylas application.
@@ -15,9 +15,10 @@ module Nylas
15
15
  #
16
16
  # @param path [String] Destination path for the call.
17
17
  # @param query_params [Hash, {}] Query params to pass to the call.
18
+ # @param headers [Hash, {}] Additional HTTP headers to include in the payload.
18
19
  # @return [Array([Hash, Array], String, Hash)] Nylas data object, API Request ID, and response headers.
19
- def get(path:, query_params: {})
20
- response = get_raw(path: path, query_params: query_params)
20
+ def get(path:, query_params: {}, headers: {})
21
+ response = get_raw(path: path, query_params: query_params, headers: headers)
21
22
 
22
23
  [response[:data], response[:request_id], response[:headers]]
23
24
  end
@@ -26,10 +27,11 @@ module Nylas
26
27
  #
27
28
  # @param path [String] Destination path for the call.
28
29
  # @param query_params [Hash, {}] Query params to pass to the call.
30
+ # @param headers [Hash, {}] Additional HTTP headers to include in the payload.
29
31
  # @return [Array<Array<Hash>, String, String, Hash>]
30
32
  # Nylas data array, API Request ID, next cursor, and response headers.response headers.
31
- def get_list(path:, query_params: {})
32
- response = get_raw(path: path, query_params: query_params)
33
+ def get_list(path:, query_params: {}, headers: {})
34
+ response = get_raw(path: path, query_params: query_params, headers: headers)
33
35
 
34
36
  [response[:data], response[:request_id], response[:next_cursor], response[:headers]]
35
37
  end
@@ -40,16 +42,20 @@ module Nylas
40
42
  #
41
43
  # @param path [String] Destination path for the call.
42
44
  # @param query_params [Hash, {}] Query params to pass to the call.
45
+ # @param headers [Hash, {}] Additional HTTP headers to include in the payload.
43
46
  # @return [Hash] The JSON response from the Nylas API.
44
- def get_raw(path:, query_params: {})
45
- execute(
47
+ def get_raw(path:, query_params: {}, headers: {})
48
+ request = {
46
49
  method: :get,
47
50
  path: path,
48
51
  query: query_params,
49
52
  payload: nil,
50
53
  api_key: api_key,
51
54
  timeout: timeout
52
- )
55
+ }
56
+ request[:headers] = headers unless headers.empty?
57
+
58
+ execute(**request)
53
59
  end
54
60
  end
55
61
 
@@ -63,18 +69,21 @@ module Nylas
63
69
  # @param path [String] Destination path for the call.
64
70
  # @param query_params [Hash, {}] Query params to pass to the call.
65
71
  # @param request_body [Hash, nil] Request body to pass to the call.
72
+ # Defaults to {} when nil to ensure Content-Type: application/json is sent.
66
73
  # @param headers [Hash, {}] Additional HTTP headers to include in the payload.
67
74
  # @return [Array(Hash, String, Hash)] Nylas data object, API Request ID, and response headers.
68
- def post(path:, query_params: {}, request_body: nil, headers: {})
69
- response = execute(
75
+ def post(path:, query_params: {}, request_body: nil, headers: {}, serialized_json_body: nil)
76
+ request = {
70
77
  method: :post,
71
78
  path: path,
72
79
  query: query_params,
73
- payload: request_body,
80
+ payload: request_body || {},
74
81
  headers: headers,
75
82
  api_key: api_key,
76
83
  timeout: timeout
77
- )
84
+ }
85
+ request[:serialized_json_body] = serialized_json_body unless serialized_json_body.nil?
86
+ response = execute(**request)
78
87
 
79
88
  [response[:data], response[:request_id], response[:headers]]
80
89
  end
@@ -90,18 +99,21 @@ module Nylas
90
99
  # @param path [String] Destination path for the call.
91
100
  # @param query_params [Hash, {}] Query params to pass to the call.
92
101
  # @param request_body [Hash, nil] Request body to pass to the call.
102
+ # Defaults to {} when nil to ensure Content-Type: application/json is sent.
93
103
  # @param headers [Hash, {}] Additional HTTP headers to include in the payload.
94
104
  # @return Nylas data object and API Request ID.
95
- def put(path:, query_params: {}, request_body: nil, headers: {})
96
- response = execute(
105
+ def put(path:, query_params: {}, request_body: nil, headers: {}, serialized_json_body: nil)
106
+ request = {
97
107
  method: :put,
98
108
  path: path,
99
109
  query: query_params,
100
- payload: request_body,
110
+ payload: request_body || {},
101
111
  headers: headers,
102
112
  api_key: api_key,
103
113
  timeout: timeout
104
- )
114
+ }
115
+ request[:serialized_json_body] = serialized_json_body unless serialized_json_body.nil?
116
+ response = execute(**request)
105
117
 
106
118
  [response[:data], response[:request_id]]
107
119
  end
@@ -117,18 +129,21 @@ module Nylas
117
129
  # @param path [String] Destination path for the call.
118
130
  # @param query_params [Hash, {}] Query params to pass to the call.
119
131
  # @param request_body [Hash, nil] Request body to pass to the call.
132
+ # Defaults to {} when nil to ensure Content-Type: application/json is sent.
120
133
  # @param headers [Hash, {}] Additional HTTP headers to include in the payload.
121
134
  # @return Nylas data object and API Request ID.
122
- def patch(path:, query_params: {}, request_body: nil, headers: {})
123
- response = execute(
135
+ def patch(path:, query_params: {}, request_body: nil, headers: {}, serialized_json_body: nil)
136
+ request = {
124
137
  method: :patch,
125
138
  path: path,
126
139
  query: query_params,
127
- payload: request_body,
140
+ payload: request_body || {},
128
141
  headers: headers,
129
142
  api_key: api_key,
130
143
  timeout: timeout
131
- )
144
+ }
145
+ request[:serialized_json_body] = serialized_json_body unless serialized_json_body.nil?
146
+ response = execute(**request)
132
147
 
133
148
  [response[:data], response[:request_id]]
134
149
  end
@@ -143,15 +158,17 @@ module Nylas
143
158
  #
144
159
  # @param path [String] Destination path for the call.
145
160
  # @param query_params [Hash, {}] Query params to pass to the call.
161
+ # @param request_body [Hash, nil] Optional request body (e.g. cancellation_reason for bookings).
162
+ # Defaults to {} to ensure Content-Type: application/json is sent.
146
163
  # @param headers [Hash, {}] Additional HTTP headers to include in the payload.
147
164
  # @return Nylas data object and API Request ID.
148
- def delete(path:, query_params: {}, headers: {})
165
+ def delete(path:, query_params: {}, request_body: nil, headers: {})
149
166
  response = execute(
150
167
  method: :delete,
151
168
  path: path,
152
169
  query: query_params,
153
170
  headers: headers,
154
- payload: nil,
171
+ payload: request_body || {},
155
172
  api_key: api_key,
156
173
  timeout: timeout
157
174
  )
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "httparty"
4
4
  require "net/http"
5
+ require "net/http/post/multipart"
5
6
 
6
7
  require_relative "../errors"
7
8
  require_relative "../version"
@@ -29,11 +30,14 @@ module Nylas
29
30
  # @param query [Hash, {}] Hash of names and values to include in the query section of the URI
30
31
  # fragment.
31
32
  # @param payload [Hash, nil] Body to send with the request.
33
+ # @param serialized_json_body [String, nil] Pre-serialized JSON body to send as-is.
32
34
  # @param api_key [Hash, nil] API key to send with the request.
33
35
  # @return [Object] Parsed JSON response from the API.
34
- def execute(method:, path:, timeout:, headers: {}, query: {}, payload: nil, api_key: nil)
36
+ def execute(method:, path:, timeout:, headers: {}, query: {}, payload: nil, api_key: nil,
37
+ serialized_json_body: nil)
35
38
  request = build_request(method: method, path: path, headers: headers,
36
- query: query, payload: payload, api_key: api_key, timeout: timeout)
39
+ query: query, payload: payload, api_key: api_key, timeout: timeout,
40
+ serialized_json_body: serialized_json_body)
37
41
  begin
38
42
  httparty_execute(**request) do |response, _request, result|
39
43
  content_type = nil
@@ -90,12 +94,14 @@ module Nylas
90
94
  # @param query [Hash, {}] Hash of names and values to include in the query section of the URI
91
95
  # fragment.
92
96
  # @param payload [Hash, nil] Body to send with the request.
97
+ # @param serialized_json_body [String, nil] Pre-serialized JSON body to send as-is.
93
98
  # @param timeout [Integer, nil] Timeout value to send with the request.
94
99
  # @param api_key [Hash, nil] API key to send with the request.
95
100
  # @return [Object] The request information after processing. This includes an updated payload
96
101
  # and headers.
97
102
  def build_request(
98
- method:, path: nil, headers: {}, query: {}, payload: nil, timeout: nil, api_key: nil
103
+ method:, path: nil, headers: {}, query: {}, payload: nil, timeout: nil, api_key: nil,
104
+ serialized_json_body: nil
99
105
  )
100
106
  url = build_url(path, query)
101
107
  resulting_headers = default_headers.merge(headers).merge(auth_header(api_key))
@@ -103,7 +109,10 @@ module Nylas
103
109
  # Check for multipart flag using both string and symbol keys for backwards compatibility
104
110
  is_multipart = !payload.nil? && (payload["multipart"] || payload[:multipart])
105
111
 
106
- if !payload.nil? && !is_multipart
112
+ if !serialized_json_body.nil?
113
+ payload = serialized_json_body
114
+ resulting_headers["Content-type"] = "application/json"
115
+ elsif !payload.nil? && !is_multipart
107
116
  normalize_json_encodings!(payload)
108
117
  payload = payload&.to_json
109
118
  resulting_headers["Content-type"] = "application/json"
@@ -135,7 +144,9 @@ module Nylas
135
144
 
136
145
  private
137
146
 
138
- # Sends a request to the Nylas REST API using HTTParty.
147
+ # Sends a request to the Nylas REST API using HTTParty or Net::HTTP for multipart.
148
+ # Multipart requests use Net::HTTP::Post::Multipart (multipart-post gem) because
149
+ # HTTParty's multipart handling produces malformed requests that the Nylas API rejects.
139
150
  #
140
151
  # @param method [Symbol] HTTP method for the API call. Either :get, :post, :delete, or :patch.
141
152
  # @param url [String] URL for the API call.
@@ -143,25 +154,16 @@ module Nylas
143
154
  # @param payload [String, Hash] Body to send with the request.
144
155
  # @param timeout [Hash] Timeout value to send with the request.
145
156
  def httparty_execute(method:, url:, headers:, payload:, timeout:)
146
- options = {
147
- headers: headers,
148
- timeout: timeout
149
- }
150
-
151
- # Handle multipart uploads
152
- if payload.is_a?(Hash) && file_upload?(payload)
153
- options[:multipart] = true
154
- options[:body] = prepare_multipart_payload(payload)
155
- elsif payload
156
- options[:body] = payload
157
+ if method == :post && payload.is_a?(Hash) && file_upload?(payload)
158
+ response = execute_multipart_request(url: url, headers: headers, payload: payload, timeout: timeout)
159
+ else
160
+ options = { headers: headers, timeout: timeout }
161
+ options[:body] = payload if payload
162
+ response = HTTParty.send(method, url, options)
157
163
  end
158
164
 
159
- response = HTTParty.send(method, url, options)
160
-
161
- # Create a compatible response object that mimics RestClient::Response
162
165
  result = create_response_wrapper(response)
163
166
 
164
- # Call the block with the response in the same format as rest-client
165
167
  if block_given?
166
168
  yield response, nil, result
167
169
  else
@@ -169,6 +171,77 @@ module Nylas
169
171
  end
170
172
  end
171
173
 
174
+ # Executes multipart POST using Net::HTTP::Post::Multipart (fixes issue #538).
175
+ # HTTParty's multipart produces malformed requests; multipart-post/UploadIO works correctly.
176
+ def execute_multipart_request(url:, headers:, payload:, timeout:)
177
+ uri = URI.parse(url)
178
+ params = build_multipart_params(payload)
179
+
180
+ req = Net::HTTP::Post::Multipart.new(uri.path, params)
181
+ headers.each { |key, value| req[key] = value }
182
+
183
+ http = Net::HTTP.new(uri.host, uri.port)
184
+ http.use_ssl = (uri.scheme == "https")
185
+ http.read_timeout = timeout
186
+ http.open_timeout = timeout
187
+
188
+ response = http.request(req)
189
+
190
+ create_httparty_like_response(response)
191
+ end
192
+
193
+ # Build params hash for Net::HTTP::Post::Multipart with UploadIO for file fields.
194
+ def build_multipart_params(payload)
195
+ params = {}
196
+ payload.each do |key, value|
197
+ params[key] = if key.is_a?(String) && key != "message" && file_like_value?(value)
198
+ value_to_upload_io(value)
199
+ else
200
+ value.to_s
201
+ end
202
+ end
203
+ params
204
+ end
205
+
206
+ def file_like_value?(value)
207
+ return true if value.respond_to?(:read) && (value.is_a?(File) ? !value.closed? : true)
208
+ if value.is_a?(String) && (value.respond_to?(:original_filename) || value.respond_to?(:content_type))
209
+ return true
210
+ end
211
+
212
+ false
213
+ end
214
+
215
+ # Convert File, String, or StringIO to UploadIO for multipart-post.
216
+ def value_to_upload_io(value)
217
+ content_type = value.respond_to?(:content_type) ? value.content_type : "application/octet-stream"
218
+ filename = value.respond_to?(:original_filename) ? value.original_filename : "file.bin"
219
+
220
+ io = if value.respond_to?(:read) && value.respond_to?(:rewind)
221
+ value.rewind if value.respond_to?(:rewind)
222
+ value
223
+ else
224
+ require "stringio"
225
+ content = value.to_s
226
+ content = content.dup.force_encoding(Encoding::ASCII_8BIT) if content.is_a?(String)
227
+ StringIO.new(content)
228
+ end
229
+
230
+ UploadIO.new(io, content_type, filename)
231
+ end
232
+
233
+ # Create response object compatible with HTTParty::Response interface.
234
+ def create_httparty_like_response(net_http_response)
235
+ headers = net_http_response.to_hash
236
+ headers = headers.transform_values { |v| v.is_a?(Array) && v.one? ? v.first : v }
237
+
238
+ OpenStruct.new(
239
+ body: net_http_response.body,
240
+ code: net_http_response.code.to_i,
241
+ headers: headers
242
+ )
243
+ end
244
+
172
245
  # Create a response wrapper that mimics RestClient::Response.code behavior
173
246
  def create_response_wrapper(response)
174
247
  OpenStruct.new(code: response.code)
@@ -188,10 +261,20 @@ module Nylas
188
261
  # Check if payload was prepared by FileUtils.build_form_request for multipart uploads
189
262
  # This handles binary content attachments that are strings with added singleton methods
190
263
  has_message_field = payload.key?("message") && payload["message"].is_a?(String)
191
- has_attachment_fields = payload.keys.any? { |key| key.is_a?(String) && key.match?(/^file\d+$/) }
192
264
 
193
- # If we have both a "message" field and "file{N}" fields, this indicates
194
- # the payload was prepared by FileUtils.build_form_request for multipart upload
265
+ # Check for attachment fields - these can have custom content_id values (not just "file{N}")
266
+ # FileUtils.build_form_request creates entries with string values that have singleton methods
267
+ # like original_filename and content_type defined on them
268
+ has_attachment_fields = payload.any? do |key, value|
269
+ next false unless key.is_a?(String) && key != "message"
270
+
271
+ # Check if the value is a string with attachment-like singleton methods
272
+ # (original_filename or content_type), which indicates it's a file content
273
+ value.is_a?(String) && (value.respond_to?(:original_filename) || value.respond_to?(:content_type))
274
+ end
275
+
276
+ # If we have both a "message" field and attachment fields with file metadata,
277
+ # this indicates the payload was prepared by FileUtils.build_form_request
195
278
  has_message_field && has_attachment_fields
196
279
  end
197
280
 
@@ -414,6 +497,8 @@ module Nylas
414
497
 
415
498
  # Set the authorization header for an API query.
416
499
  def auth_header(api_key)
500
+ return {} if api_key.nil?
501
+
417
502
  { "Authorization" => "Bearer #{api_key}" }
418
503
  end
419
504
  end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "json"
5
+ require "openssl"
6
+ require "securerandom"
7
+
8
+ module Nylas
9
+ # Builds Nylas Service Account request signing headers for organization admin APIs.
10
+ #
11
+ # @see https://developer.nylas.com/docs/v3/auth/nylas-service-account/
12
+ class ServiceAccountSigner
13
+ NONCE_ALPHABET = ("a".."z").to_a.concat(("A".."Z").to_a, ("0".."9").to_a).freeze
14
+ DEFAULT_NONCE_LENGTH = 20
15
+ SIGNED_BODY_METHODS = %w[post put patch].freeze
16
+
17
+ attr_reader :private_key_id
18
+
19
+ # @param private_key_pem [String] RSA private key in PEM format.
20
+ # @param private_key_id [String] Value for the X-Nylas-Kid header.
21
+ def initialize(private_key_pem:, private_key_id:)
22
+ @private_key = self.class.load_rsa_private_key(private_key_pem)
23
+ @private_key_id = private_key_id
24
+ end
25
+
26
+ # Returns deterministic JSON with keys sorted at every object level and no extra whitespace.
27
+ #
28
+ # @param data [Hash, Array, String, Numeric, true, false, nil] Data to serialize.
29
+ # @return [String] Canonical JSON string.
30
+ def self.canonical_json(data)
31
+ JSON.generate(canonicalize(data))
32
+ end
33
+
34
+ # Loads an RSA private key from a PEM string.
35
+ #
36
+ # @param private_key_pem [String] RSA private key in PEM format.
37
+ # @return [OpenSSL::PKey::RSA]
38
+ def self.load_rsa_private_key(private_key_pem)
39
+ key = OpenSSL::PKey::RSA.new(private_key_pem)
40
+ raise ArgumentError, "Private key must be RSA private key" unless key.private?
41
+ raise ArgumentError, "Private key must be at least 2048 bits" if key.n.num_bits < 2048
42
+
43
+ key
44
+ rescue OpenSSL::PKey::PKeyError
45
+ raise ArgumentError, "Private key must be RSA PEM"
46
+ end
47
+
48
+ # Generates a cryptographically secure alphanumeric nonce.
49
+ #
50
+ # @param length [Integer] Length of the nonce to generate.
51
+ # @return [String] Generated nonce.
52
+ def self.generate_nonce(length = DEFAULT_NONCE_LENGTH)
53
+ Array.new(length) { NONCE_ALPHABET[SecureRandom.random_number(NONCE_ALPHABET.length)] }.join
54
+ end
55
+
56
+ # Builds signed headers and, for JSON body methods, the exact canonical body to send.
57
+ #
58
+ # @param method [String, Symbol] HTTP method.
59
+ # @param path [String] Relative request path, for example "/v3/admin/domains".
60
+ # @param body [Hash, nil] Request body for POST/PUT/PATCH requests.
61
+ # @param timestamp [Integer, nil] Optional Unix timestamp in seconds, mainly for tests.
62
+ # @param nonce [String, nil] Optional nonce, mainly for tests.
63
+ # @return [Array(Hash, String)] Signed headers and optional serialized JSON body.
64
+ def build_headers(method:, path:, body: nil, timestamp: nil, nonce: nil)
65
+ timestamp ||= Time.now.to_i
66
+ nonce ||= self.class.generate_nonce
67
+ method_value = method.to_s.downcase
68
+ serialized_body = nil
69
+
70
+ if SIGNED_BODY_METHODS.include?(method_value) && !body.nil?
71
+ serialized_body = self.class.canonical_json(body)
72
+ end
73
+
74
+ envelope = {
75
+ method: method_value,
76
+ nonce: nonce,
77
+ path: path,
78
+ timestamp: timestamp
79
+ }
80
+ envelope[:payload] = serialized_body if serialized_body
81
+
82
+ signature = @private_key.sign(OpenSSL::Digest.new("SHA256"), self.class.canonical_json(envelope))
83
+
84
+ [
85
+ {
86
+ "X-Nylas-Kid" => private_key_id,
87
+ "X-Nylas-Nonce" => nonce,
88
+ "X-Nylas-Timestamp" => timestamp.to_s,
89
+ "X-Nylas-Signature" => Base64.strict_encode64(signature)
90
+ },
91
+ serialized_body
92
+ ]
93
+ end
94
+
95
+ class << self
96
+ private
97
+
98
+ def canonicalize(value)
99
+ case value
100
+ when Hash
101
+ value.keys.sort_by(&:to_s).each_with_object({}) do |key, result|
102
+ result[key.to_s] = canonicalize(value[key])
103
+ end
104
+ when Array
105
+ value.map { |item| canonicalize(item) }
106
+ else
107
+ value
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -8,6 +8,7 @@ module Nylas
8
8
  # Application
9
9
  class Applications < Resource
10
10
  include ApiOperations::Get
11
+ include ApiOperations::Patch
11
12
 
12
13
  attr_reader :redirect_uris
13
14
 
@@ -23,5 +24,18 @@ module Nylas
23
24
  def get_details
24
25
  get(path: "#{api_uri}/v3/applications")
25
26
  end
27
+
28
+ # Update application details.
29
+ #
30
+ # @param request_body [Hash] The values to update the application with. Include
31
+ # +callback_uris+ entries with +id+ when preserving or updating existing
32
+ # callback URIs.
33
+ # @return [Array(Hash, String)] The updated application details and API Request ID.
34
+ def update(request_body:)
35
+ patch(
36
+ path: "#{api_uri}/v3/applications",
37
+ request_body: request_body
38
+ )
39
+ end
26
40
  end
27
41
  end
@@ -64,11 +64,13 @@ module Nylas
64
64
  # Delete a booking.
65
65
  # @param booking_id [String] The id of the booking to delete.
66
66
  # @param query_params [Hash, nil] Query params to pass to the request.
67
+ # @param request_body [Hash, nil] Optional body params (e.g. cancellation_reason).
67
68
  # @return [Array(TrueClass, String)] True and the API Request ID for the delete operation.
68
- def destroy(booking_id:, query_params: nil)
69
+ def destroy(booking_id:, query_params: nil, request_body: nil)
69
70
  _, request_id = delete(
70
71
  path: "#{api_uri}/v3/scheduling/bookings/#{booking_id}",
71
- query_params: query_params
72
+ query_params: query_params,
73
+ request_body: request_body
72
74
  )
73
75
 
74
76
  [true, request_id]