From 029df96f097d8a0ad0a692246f2d23f264f2d371 Mon Sep 17 00:00:00 2001 From: Gordan Ovcaric Date: Wed, 25 Mar 2026 15:19:31 +0100 Subject: [PATCH 1/2] CUST-5221: add "destroy" with json-body to booking deletion --- src/main/kotlin/com/nylas/NylasClient.kt | 22 ++++++++ .../kotlin/com/nylas/resources/Bookings.kt | 52 ++++++++++++++++++- src/test/kotlin/com/nylas/NylasClientTest.kt | 17 ++++++ .../com/nylas/resources/BookingsTest.kt | 30 +++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/nylas/NylasClient.kt b/src/main/kotlin/com/nylas/NylasClient.kt index fa864645..dd6130b8 100644 --- a/src/main/kotlin/com/nylas/NylasClient.kt +++ b/src/main/kotlin/com/nylas/NylasClient.kt @@ -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 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. diff --git a/src/main/kotlin/com/nylas/resources/Bookings.kt b/src/main/kotlin/com/nylas/resources/Bookings.kt index bdebdd7f..14b44ee0 100644 --- a/src/main/kotlin/com/nylas/resources/Bookings.kt +++ b/src/main/kotlin/com/nylas/resources/Bookings.kt @@ -94,7 +94,57 @@ class Bookings(client: NylasClient) : Resource(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 diff --git a/src/test/kotlin/com/nylas/NylasClientTest.kt b/src/test/kotlin/com/nylas/NylasClientTest.kt index c9131de1..339f1515 100644 --- a/src/test/kotlin/com/nylas/NylasClientTest.kt +++ b/src/test/kotlin/com/nylas/NylasClientTest.kt @@ -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>("test/path", type, deleteBody) + + val requestCaptor = argumentCaptor() + 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 diff --git a/src/test/kotlin/com/nylas/resources/BookingsTest.kt b/src/test/kotlin/com/nylas/resources/BookingsTest.kt index abb6fcb3..754996cf 100644 --- a/src/test/kotlin/com/nylas/resources/BookingsTest.kt +++ b/src/test/kotlin/com/nylas/resources/BookingsTest.kt @@ -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() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + val overrideParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete( + 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) + } } } From 1119e380646fce3d26e8bc7add04c1e3693cd81b Mon Sep 17 00:00:00 2001 From: Gordan Ovcaric Date: Thu, 26 Mar 2026 11:50:32 +0100 Subject: [PATCH 2/2] add changelog --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff22cdf8..d6705030 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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