Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Nylas Java SDK Changelog

## [Unreleased]

### Changed
* Standardized event status handling on `maybe`
- Added `EventStatus.MAYBE` as the primary SDK value for this status
- Deprecated `EventStatus.TENTATIVE` as a legacy alias and serialize it as `maybe`
- Normalize both API values `maybe` and `tentative` to `EventStatus.MAYBE`
- Fall back to `null` for unknown event status values instead of failing deserialization

### Fixed
* Added request-body support to booking cancellation via `Bookings.destroy()` and `NylasClient.executeDelete()`, allowing `cancellation_reason` to be sent while keeping the legacy no-body booking destroy overload unchanged

## [v2.15.0] - Release 2026-02-22

### Added
Expand Down Expand Up @@ -607,7 +619,7 @@ This second release aims toward API stability so that we can get to v1.0.0.

Initial preview release

[Unreleased]: https://github.com/nylas/nylas-java/compare/v2.5.0...HEAD
[Unreleased]: https://github.com/nylas/nylas-java/compare/v2.15.0...HEAD
[2.5.0]: https://github.com/nylas/nylas-java/releases/tag/v2.5.0
[2.4.1]: https://github.com/nylas/nylas-java/releases/tag/v2.4.1
[2.4.0]: https://github.com/nylas/nylas-java/releases/tag/v2.4.0
Expand Down
22 changes: 22 additions & 0 deletions src/main/kotlin/com/nylas/NylasClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,28 @@ open class NylasClient(
return executeRequest(url, HttpMethod.DELETE, null, resultType, overrides)
}

/**
* Execute a DELETE request to the Nylas API with a JSON request body.
* @param path The path to request.
* @param resultType The type of the response body.
* @param requestBody The request body.
* @param queryParams The query parameters.
* @param overrides The request overrides.
* @suppress Not for public use.
*/
@Throws(AbstractNylasApiError::class, NylasSdkTimeoutError::class)
open fun <T> executeDelete(
path: String,
resultType: Type,
requestBody: String,
queryParams: IQueryParams? = null,
overrides: RequestOverrides? = null,
): T {
val url = buildUrl(path, queryParams, overrides)
val jsonBody = JsonHelper.jsonRequestBody(requestBody)
return executeRequest(url, HttpMethod.DELETE, jsonBody, resultType, overrides)
}

/**
* Execute a request with a form-body payload to the Nylas API.
* @param path The path to request.
Expand Down
52 changes: 51 additions & 1 deletion src/main/kotlin/com/nylas/resources/Bookings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,57 @@ class Bookings(client: NylasClient) : Resource<Booking>(client, Booking::class.j
}

/**
* Destroy a booking
* Destroy a booking with a request body.
*
* This is a convenience overload for callers who need to send a
* [DestroyBookingRequest] but do not need query params or request overrides.
* It delegates to the full request-body overload below.
*
* @param bookingId The ID of the booking to destroy.
* @param requestBody The data to destroy the booking with.
* @return The DeleteResponse object
*/
@Throws(NylasApiError::class, NylasSdkTimeoutError::class)
fun destroy(
bookingId: String,
requestBody: DestroyBookingRequest,
): DeleteResponse {
return destroy(bookingId, requestBody, null, null)
}

/**
* Destroy a booking with a request body.
*
* This is the full request-body overload used for cancellation flows that
* need to send fields such as `cancellation_reason` while still supporting
* optional query params and request overrides.
*
* @param bookingId The ID of the booking to destroy.
* @param requestBody The data to destroy the booking with.
* @param queryParams Optional query parameters to apply
* @param overrides Optional request overrides to apply
* @return The DeleteResponse object
*/
@Throws(NylasApiError::class, NylasSdkTimeoutError::class)
fun destroy(
bookingId: String,
requestBody: DestroyBookingRequest,
queryParams: DestroyBookingQueryParams?,
overrides: RequestOverrides?,
): DeleteResponse {
val path = String.format("v3/scheduling/bookings/%s", bookingId)
val adapter = JsonHelper.moshi().adapter(DestroyBookingRequest::class.java)
val serializedRequestBody = adapter.toJson(requestBody)
return client.executeDelete(path, DeleteResponse::class.java, serializedRequestBody, queryParams, overrides)
}

/**
* Destroy a booking using the legacy no-body behavior.
*
* This overload is kept for backward compatibility with existing SDK users.
* It preserves the previous method shape and sends the DELETE request without
* a request body.
*
* @param bookingId The ID of the booking to destroy.
* @param queryParams Optional query parameters to apply
* @param overrides Optional request overrides to apply
Expand Down
17 changes: 17 additions & 0 deletions src/test/kotlin/com/nylas/NylasClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,23 @@ class NylasClientTest {
assertEquals(capturedRequest.method, "DELETE")
}

@Test
fun `executeDelete with a request body should set up the request with the correct params`() {
val deleteBody = "{ \"cancellation_reason\": \"No longer available\" }"
val type = JsonHelper.mapTypeOf(String::class.java, String::class.java)
whenever(mockResponseBody.source()).thenReturn(Buffer().writeUtf8("{ \"foo\": \"bar\" }"))
nylasClient.executeDelete<Map<String, String>>("test/path", type, deleteBody)

val requestCaptor = argumentCaptor<Request>()
verify(mockHttpClient).newCall(requestCaptor.capture())
val capturedRequest = requestCaptor.firstValue
val requestBodyBuffer = capturedRequest.body.asString()

assertEquals(capturedRequest.url.toString(), "https://api.us.nylas.com/test/path")
assertEquals(capturedRequest.method, "DELETE")
assertEquals(requestBodyBuffer, deleteBody)
}

/**
* Helper function to get the string value of a RequestBody
* @return String value of the RequestBody
Expand Down
30 changes: 30 additions & 0 deletions src/test/kotlin/com/nylas/resources/BookingsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,35 @@ class BookingsTest {
assertEquals("v3/scheduling/bookings/$bookingId", pathCaptor.firstValue)
assertEquals(DeleteResponse::class.java, typeCaptor.firstValue)
}

@Test
fun `destroying a booking with a request body calls request with the correct params`() {
val adapter = JsonHelper.moshi().adapter(DestroyBookingRequest::class.java)
val bookingId = "booking-id"
val requestBody = DestroyBookingRequest("No longer available")
val queryParams = DestroyBookingQueryParams.Builder()
.configurationId("config-id")
.build()

bookings.destroy(bookingId, requestBody, queryParams, null)

val pathCaptor = argumentCaptor<String>()
val typeCaptor = argumentCaptor<Type>()
val requestBodyCaptor = argumentCaptor<String>()
val queryParamCaptor = argumentCaptor<DestroyBookingQueryParams>()
val overrideParamCaptor = argumentCaptor<RequestOverrides>()
verify(mockNylasClient).executeDelete<DeleteResponse>(
pathCaptor.capture(),
typeCaptor.capture(),
requestBodyCaptor.capture(),
queryParamCaptor.capture(),
overrideParamCaptor.capture(),
)

assertEquals("v3/scheduling/bookings/$bookingId", pathCaptor.firstValue)
assertEquals(DeleteResponse::class.java, typeCaptor.firstValue)
assertEquals(adapter.toJson(requestBody), requestBodyCaptor.firstValue)
assertEquals(queryParams, queryParamCaptor.firstValue)
}
}
}
Loading