From 49810cef694107b76eecf9f93a4efd606df3940a Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:52:24 -0500 Subject: [PATCH 01/16] Contact fixes and additional tests --- .../kotlin/com/nylas/resources/Contacts.kt | 2 +- .../com/nylas/resources/ContactsTests.kt | 64 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/nylas/resources/Contacts.kt b/src/main/kotlin/com/nylas/resources/Contacts.kt index e6b6bfb6..dd0ba203 100644 --- a/src/main/kotlin/com/nylas/resources/Contacts.kt +++ b/src/main/kotlin/com/nylas/resources/Contacts.kt @@ -30,7 +30,7 @@ class Contacts(client: NylasClient) : Resource(client, Contact::class.j @JvmOverloads fun find(identifier: String, contactId: String, queryParams: FindContactQueryParams? = null): Response { val path = String.format("v3/grants/%s/contacts/%s", identifier, contactId) - return findResource(path) + return findResource(path, queryParams) } /** diff --git a/src/test/kotlin/com/nylas/resources/ContactsTests.kt b/src/test/kotlin/com/nylas/resources/ContactsTests.kt index cbc3af8c..54460c67 100644 --- a/src/test/kotlin/com/nylas/resources/ContactsTests.kt +++ b/src/test/kotlin/com/nylas/resources/ContactsTests.kt @@ -16,6 +16,7 @@ import java.lang.reflect.Type import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs +import kotlin.test.assertNull class ContactsTests { private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) @@ -211,6 +212,25 @@ class ContactsTests { assertEquals("v3/grants/$grantId/contacts", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(ListResponse::class.java, Contact::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `listing contacts without query params calls requests with the correct params`() { + contacts.list(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/contacts", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Contact::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) } @Test @@ -233,6 +253,27 @@ class ContactsTests { assertEquals("v3/grants/$grantId/contacts/$contactId", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(Response::class.java, Contact::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `finding a contact without query params calls requests with the correct params`() { + val contactId = "contact-123" + + contacts.find(grantId, contactId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/contacts/$contactId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Contact::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) } @Test @@ -415,7 +456,7 @@ class ContactsTests { } @Test - fun `getting free busy calls requests with the correct params`() { + fun `getting contact groups calls requests with the correct params`() { val grantId = "abc-123-grant-id" val queryParams = ListContactGroupsQueryParams( limit = 10, @@ -434,6 +475,27 @@ class ContactsTests { assertEquals("v3/grants/$grantId/contacts/groups", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(ListResponse::class.java, ContactGroup::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `getting contact groups without query params calls requests with the correct params`() { + val grantId = "abc-123-grant-id" + + contacts.listGroups(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/contacts/groups", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, ContactGroup::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) } } } From 009e8a8a4233f66a6ddf1a7c2f4c85e78d2af6c6 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:52:55 -0500 Subject: [PATCH 02/16] add null inits to optional fields --- src/main/kotlin/com/nylas/models/Connector.kt | 4 ++-- .../kotlin/com/nylas/models/CreateConnectorRequest.kt | 4 ++-- src/main/kotlin/com/nylas/models/Credential.kt | 8 ++++---- src/main/kotlin/com/nylas/models/ScheduledMessage.kt | 2 +- .../kotlin/com/nylas/models/UpdateCredentialRequest.kt | 4 ++-- src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/nylas/models/Connector.kt b/src/main/kotlin/com/nylas/models/Connector.kt index 1597d7cd..20ee20d0 100644 --- a/src/main/kotlin/com/nylas/models/Connector.kt +++ b/src/main/kotlin/com/nylas/models/Connector.kt @@ -26,7 +26,7 @@ sealed class Connector( * The Google OAuth scopes */ @Json(name = "scope") - val scope: List?, + val scope: List? = null, ) : Connector(AuthProvider.GOOGLE) /** @@ -42,7 +42,7 @@ sealed class Connector( * The Microsoft OAuth scopes */ @Json(name = "scope") - val scope: List?, + val scope: List? = null, ) : Connector(AuthProvider.MICROSOFT) /** diff --git a/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt b/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt index ab3e5256..5a4eb416 100644 --- a/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt @@ -26,7 +26,7 @@ sealed class CreateConnectorRequest( * The Google OAuth scopes */ @Json(name = "scope") - val scope: List?, + val scope: List? = null, ) : CreateConnectorRequest(AuthProvider.GOOGLE) { /** * Builder for Google connector creation requests. @@ -65,7 +65,7 @@ sealed class CreateConnectorRequest( * The Microsoft OAuth scopes */ @Json(name = "scope") - val scope: List?, + val scope: List? = null, ) : CreateConnectorRequest(AuthProvider.MICROSOFT) { /** * Builder for Microsoft connector creation requests. diff --git a/src/main/kotlin/com/nylas/models/Credential.kt b/src/main/kotlin/com/nylas/models/Credential.kt index 9071234c..0515c2bc 100644 --- a/src/main/kotlin/com/nylas/models/Credential.kt +++ b/src/main/kotlin/com/nylas/models/Credential.kt @@ -17,20 +17,20 @@ data class Credential( * The type of credential */ @Json(name = "credential_type") - val credentialType: CredentialType?, + val credentialType: CredentialType? = null, /** * Hashed value of the credential that you created */ @Json(name = "hashed_data") - val hashedData: String?, + val hashedData: String? = null, /** * Timestamp of when the credential was created */ @Json(name = "created_at") - val createdAt: Long?, + val createdAt: Long? = null, /** * Timestamp of when the credential was updated */ @Json(name = "updated_at") - val updatedAt: Long?, + val updatedAt: Long? = null, ) diff --git a/src/main/kotlin/com/nylas/models/ScheduledMessage.kt b/src/main/kotlin/com/nylas/models/ScheduledMessage.kt index 4e1e5cb9..a6abf7bc 100644 --- a/src/main/kotlin/com/nylas/models/ScheduledMessage.kt +++ b/src/main/kotlin/com/nylas/models/ScheduledMessage.kt @@ -20,5 +20,5 @@ data class ScheduledMessage( * The time the message was sent or failed to send, in epoch time. */ @Json(name = "close_time") - val closeTime: Int?, + val closeTime: Int? = null, ) diff --git a/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt b/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt index 61f3491d..1866f967 100644 --- a/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt @@ -10,12 +10,12 @@ data class UpdateCredentialRequest( * Unique name of this credential */ @Json(name = "name") - val name: String?, + val name: String? = null, /** * Data that specifies some special data required for this credential */ @Json(name = "credential_data") - val credentialData: CredentialData?, + val credentialData: CredentialData? = null, ) { /** * Builder for [UpdateCredentialRequest] diff --git a/src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt b/src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt index d45e2a9c..25690392 100644 --- a/src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt @@ -10,17 +10,17 @@ data class UpdateThreadRequest( * Sets all messages in the thread as starred or unstarred. */ @Json(name = "starred") - val starred: Boolean?, + val starred: Boolean? = null, /** * Sets all messages in the thread as read or unread. */ @Json(name = "unread") - val unread: Boolean?, + val unread: Boolean? = null, /** * The IDs of the folders to apply, overwriting all previous folders for all messages in the thread. */ @Json(name = "folders") - val folders: List?, + val folders: List? = null, ) { /** * Builder for [UpdateThreadRequest]. From c2418491426ad0161c7d1da5a369dc79d845fb1b Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:55:14 -0500 Subject: [PATCH 03/16] additional credential tests --- .../com/nylas/resources/CredentialsTests.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/kotlin/com/nylas/resources/CredentialsTests.kt b/src/test/kotlin/com/nylas/resources/CredentialsTests.kt index cf56651f..9dd255d8 100644 --- a/src/test/kotlin/com/nylas/resources/CredentialsTests.kt +++ b/src/test/kotlin/com/nylas/resources/CredentialsTests.kt @@ -19,6 +19,7 @@ import org.mockito.kotlin.whenever import java.lang.reflect.Type import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNull class CredentialsTests { private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) @@ -93,6 +94,25 @@ class CredentialsTests { assertEquals("v3/connectors/google/creds", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(ListResponse::class.java, Credential::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `listing credentials without query params requests with the correct params`() { + credentials.list(AuthProvider.GOOGLE) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/connectors/google/creds", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Credential::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) } @Test From b048444a988b48d486ed24c714c4dae1e036056d Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:55:19 -0500 Subject: [PATCH 04/16] draft tests --- .../kotlin/com/nylas/resources/DraftsTests.kt | 363 ++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 src/test/kotlin/com/nylas/resources/DraftsTests.kt diff --git a/src/test/kotlin/com/nylas/resources/DraftsTests.kt b/src/test/kotlin/com/nylas/resources/DraftsTests.kt new file mode 100644 index 00000000..098fc423 --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/DraftsTests.kt @@ -0,0 +1,363 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.models.Response +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types +import okhttp3.* +import okio.Buffer +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import java.lang.reflect.Type +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNull + +class DraftsTests { + private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) + private val mockCall: Call = Mockito.mock(Call::class.java) + private val mockResponse: okhttp3.Response = Mockito.mock(okhttp3.Response::class.java) + private val mockResponseBody: ResponseBody = Mockito.mock(ResponseBody::class.java) + private val mockOkHttpClientBuilder: OkHttpClient.Builder = Mockito.mock() + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(mockOkHttpClientBuilder.addInterceptor(any())).thenReturn(mockOkHttpClientBuilder) + whenever(mockOkHttpClientBuilder.build()).thenReturn(mockHttpClient) + whenever(mockHttpClient.newCall(any())).thenReturn(mockCall) + whenever(mockCall.execute()).thenReturn(mockResponse) + whenever(mockResponse.isSuccessful).thenReturn(true) + whenever(mockResponse.body()).thenReturn(mockResponseBody) + } + + @Nested + inner class SerializationTests { + @Test + fun `Draft serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Draft::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "body": "Hello, I just sent a message using Nylas!", + "cc": [ + { + "email": "arya.stark@example.com" + } + ], + "attachments": [ + { + "content_type": "text/calendar", + "id": "4kj2jrcoj9ve5j9yxqz5cuv98", + "size": 1708 + } + ], + "folders": [ + "8l6c4d11y1p4dm4fxj52whyr9", + "d9zkcr2tljpu3m4qpj7l2hbr0" + ], + "from": [ + { + "name": "Daenerys Targaryen", + "email": "daenerys.t@example.com" + } + ], + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "id": "5d3qmne77v32r8l4phyuksl2x", + "object": "draft", + "reply_to": [ + { + "name": "Daenerys Targaryen", + "email": "daenerys.t@example.com" + } + ], + "snippet": "Hello, I just sent a message using Nylas!", + "starred": true, + "subject": "Hello from Nylas!", + "thread_id": "1t8tv3890q4vgmwq6pmdwm8qgsaer", + "to": [ + { + "name": "Jon Snow", + "email": "j.snow@example.com" + } + ], + "date": 1705084742, + "created_at": 1705084926 + } + } + """.trimIndent(), + ) + + val draft = adapter.fromJson(jsonBuffer)!! + assertIs(draft) + assertEquals("Hello, I just sent a message using Nylas!", draft.body) + assertEquals(1, draft.cc?.size) + assertEquals("arya.stark@example.com", draft.cc?.first()?.email) + assertEquals(1, draft.attachments?.size) + assertEquals("text/calendar", draft.attachments?.first()?.contentType) + assertEquals("4kj2jrcoj9ve5j9yxqz5cuv98", draft.attachments?.first()?.id) + assertEquals(1708, draft.attachments?.first()?.size) + assertEquals(2, draft.folders?.size) + assertEquals("8l6c4d11y1p4dm4fxj52whyr9", draft.folders?.first()) + assertEquals("d9zkcr2tljpu3m4qpj7l2hbr0", draft.folders?.last()) + assertEquals(1, draft.from.size) + assertEquals("Daenerys Targaryen", draft.from.first().name) + assertEquals("daenerys.t@example.com", draft.from.first().email) + assertEquals("41009df5-bf11-4c97-aa18-b285b5f2e386", draft.grantId) + assertEquals("5d3qmne77v32r8l4phyuksl2x", draft.id) + assertEquals("draft", draft.getObject()) + assertEquals(1, draft.replyTo?.size) + assertEquals("Daenerys Targaryen", draft.replyTo?.first()?.name) + assertEquals("daenerys.t@example.com", draft.replyTo?.first()?.email) + assertEquals("Hello, I just sent a message using Nylas!", draft.snippet) + assertEquals(true, draft.starred) + assertEquals("Hello from Nylas!", draft.subject) + assertEquals("1t8tv3890q4vgmwq6pmdwm8qgsaer", draft.threadId) + assertEquals(1, draft.to?.size) + assertEquals("Jon Snow", draft.to?.first()?.name) + assertEquals("j.snow@example.com", draft.to?.first()?.email) + assertEquals(1705084742, draft.date) + assertEquals(1705084926, draft.createdAt) + } + } + + @Nested + inner class CrudTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var drafts: Drafts + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + drafts = Drafts(mockNylasClient) + } + + @Test + fun `listing drafts calls requests with the correct params`() { + val queryParams = ListDraftsQueryParams( + limit = 10, + threadId = "thread-123", + subject = "subject", + to = listOf("to"), + cc = listOf("cc"), + bcc = listOf("bcc"), + unread = true, + starred = true, + hasAttachment = true, + ) + + drafts.list(grantId, queryParams) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/drafts", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Draft::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `listing drafts without query params calls requests with the correct params`() { + drafts.list(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/drafts", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Draft::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `finding a draft calls requests with the correct params`() { + val draftId = "draft-123" + + drafts.find(grantId, draftId) + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/drafts/$draftId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Draft::class.java), typeCaptor.firstValue) + } + + @Test + fun `creating a draft calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(CreateDraftRequest::class.java) + val createDraftRequest = CreateDraftRequest( + body = "Hello, I just sent a message using Nylas!", + cc = listOf( + EmailName( + email = "test@gmail.com", + name = "Test", + ), + ), + bcc = listOf( + EmailName( + email = "bcc@gmail.com", + name = "BCC", + ), + ), + snippet = "Hello, I just sent a message using Nylas!", + threadId = "thread-123", + subject = "Hello from Nylas!", + unread = false, + starred = true, + sendAt = 1620000000, + replyToMessageId = "reply-to-message-id", + trackingOptions = TrackingOptions( + label = "label", + links = true, + opens = true, + threadReplies = true, + ), + ) + + drafts.create(grantId, createDraftRequest) + + val pathCaptor = argumentCaptor() + val methodCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeFormRequest>( + pathCaptor.capture(), + methodCaptor.capture(), + requestBodyCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/drafts", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Draft::class.java), typeCaptor.firstValue) + assertEquals(NylasClient.HttpMethod.POST, methodCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + val multipart = requestBodyCaptor.firstValue as MultipartBody + assertEquals(1, multipart.size()) + val buffer = Buffer() + multipart.part(0).body().writeTo(buffer) + assertEquals(adapter.toJson(createDraftRequest), buffer.readUtf8()) + } + + @Test + fun `updating a draft calls requests with the correct params`() { + val draftId = "draft-123" + val adapter = JsonHelper.moshi().adapter(UpdateDraftRequest::class.java) + val updateDraftRequest = UpdateDraftRequest( + body = "Hello, I just sent a message using Nylas!", + subject = "Hello from Nylas!", + unread = false, + starred = true, + ) + + drafts.update(grantId, draftId, updateDraftRequest) + + val pathCaptor = argumentCaptor() + val methodCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeFormRequest>( + pathCaptor.capture(), + methodCaptor.capture(), + requestBodyCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/drafts/$draftId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Draft::class.java), typeCaptor.firstValue) + assertEquals(NylasClient.HttpMethod.PUT, methodCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + val multipart = requestBodyCaptor.firstValue as MultipartBody + assertEquals(1, multipart.size()) + val buffer = Buffer() + multipart.part(0).body().writeTo(buffer) + assertEquals(adapter.toJson(updateDraftRequest), buffer.readUtf8()) + } + + @Test + fun `destroying a draft calls requests with the correct params`() { + val draftId = "draft-123" + + drafts.destroy(grantId, draftId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/drafts/$draftId", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } + + @Nested + inner class OtherMethodTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var drafts: Drafts + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + drafts = Drafts(mockNylasClient) + } + + @Test + fun `send calls requests with the correct params`() { + val grantId = "abc-123-grant-id" + val draftId = "draft-123" + + drafts.send(grantId, draftId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePost>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/drafts/$draftId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Message::class.java), typeCaptor.firstValue) + assertNull(requestBodyCaptor.firstValue) + } + } +} From 13a7e9b0609bbd37282c4c826b23e9eb8a719669 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:20:29 -0500 Subject: [PATCH 05/16] Tests for Folders --- .../com/nylas/resources/FoldersTests.kt | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/test/kotlin/com/nylas/resources/FoldersTests.kt diff --git a/src/test/kotlin/com/nylas/resources/FoldersTests.kt b/src/test/kotlin/com/nylas/resources/FoldersTests.kt new file mode 100644 index 00000000..a91f902a --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/FoldersTests.kt @@ -0,0 +1,208 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.models.Response +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types +import okhttp3.* +import okio.Buffer +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.* +import java.lang.reflect.Type +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNull + +class FoldersTests { + private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) + private val mockCall: Call = Mockito.mock(Call::class.java) + private val mockResponse: okhttp3.Response = Mockito.mock(okhttp3.Response::class.java) + private val mockResponseBody: ResponseBody = Mockito.mock(ResponseBody::class.java) + private val mockOkHttpClientBuilder: OkHttpClient.Builder = Mockito.mock() + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(mockOkHttpClientBuilder.addInterceptor(any())).thenReturn(mockOkHttpClientBuilder) + whenever(mockOkHttpClientBuilder.build()).thenReturn(mockHttpClient) + whenever(mockHttpClient.newCall(any())).thenReturn(mockCall) + whenever(mockCall.execute()).thenReturn(mockResponse) + whenever(mockResponse.isSuccessful).thenReturn(true) + whenever(mockResponse.body()).thenReturn(mockResponseBody) + } + + @Nested + inner class SerializationTests { + @Test + fun `Folder serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Folder::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "id": "SENT", + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "name": "SENT", + "system_folder": true, + "object": "folder", + "unread_count": 0, + "child_count": 0, + "parent_id": "ascsf21412", + "background_color": "#039BE5", + "text_color": "#039BE5", + "total_count": 0 + } + """.trimIndent(), + ) + + val folder = adapter.fromJson(jsonBuffer)!! + assertIs(folder) + assertEquals("SENT", folder.id) + assertEquals("41009df5-bf11-4c97-aa18-b285b5f2e386", folder.grantId) + assertEquals("SENT", folder.name) + assertEquals(true, folder.systemFolder) + assertEquals("folder", folder.getObject()) + assertEquals(0, folder.unreadCount) + assertEquals(0, folder.childCount) + assertEquals("ascsf21412", folder.parentId) + assertEquals("#039BE5", folder.backgroundColor) + assertEquals("#039BE5", folder.textColor) + assertEquals(0, folder.totalCount) + } + } + + @Nested + inner class CrudTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var folders: Folders + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + folders = Folders(mockNylasClient) + } + + @Test + fun `listing folders calls requests with the correct params`() { + folders.list(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/folders", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Folder::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `finding a folder calls requests with the correct params`() { + val folderId = "folder-123" + + folders.find(grantId, folderId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/folders/$folderId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Folder::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `creating a folder calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(CreateFolderRequest::class.java) + val createFolderRequest = CreateFolderRequest( + name = "My New Folder", + parentId = "parent-folder-id", + backgroundColor = "#039BE5", + textColor = "#039BE5", + ) + + folders.create(grantId, createFolderRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePost>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/folders", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Folder::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(createFolderRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `updating a folder calls requests with the correct params`() { + val folderId = "folder-123" + val adapter = JsonHelper.moshi().adapter(UpdateFolderRequest::class.java) + val updateFolderRequest = UpdateFolderRequest( + name = "My New Folder", + parentId = "parent-folder-id", + backgroundColor = "#039BE5", + textColor = "#039BE5", + ) + + folders.update(grantId, folderId, updateFolderRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePut>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/folders/$folderId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Folder::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateFolderRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `destroying a folder calls requests with the correct params`() { + val folderId = "folder-123" + + folders.destroy(grantId, folderId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/folders/$folderId", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } +} From e2694ba342b21d680a02b72463a75e10f4a82db4 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 12:50:04 -0500 Subject: [PATCH 06/16] Threads tests --- .../com/nylas/resources/ThreadsTests.kt | 311 ++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 src/test/kotlin/com/nylas/resources/ThreadsTests.kt diff --git a/src/test/kotlin/com/nylas/resources/ThreadsTests.kt b/src/test/kotlin/com/nylas/resources/ThreadsTests.kt new file mode 100644 index 00000000..5f06fe6c --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/ThreadsTests.kt @@ -0,0 +1,311 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.models.Response +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types +import okhttp3.* +import okio.Buffer +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.* +import java.lang.reflect.Type +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNull + +class ThreadsTests { + private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) + private val mockCall: Call = Mockito.mock(Call::class.java) + private val mockResponse: okhttp3.Response = Mockito.mock(okhttp3.Response::class.java) + private val mockResponseBody: ResponseBody = Mockito.mock(ResponseBody::class.java) + private val mockOkHttpClientBuilder: OkHttpClient.Builder = Mockito.mock() + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(mockOkHttpClientBuilder.addInterceptor(any())).thenReturn(mockOkHttpClientBuilder) + whenever(mockOkHttpClientBuilder.build()).thenReturn(mockHttpClient) + whenever(mockHttpClient.newCall(any())).thenReturn(mockCall) + whenever(mockCall.execute()).thenReturn(mockResponse) + whenever(mockResponse.isSuccessful).thenReturn(true) + whenever(mockResponse.body()).thenReturn(mockResponseBody) + } + + @Nested + inner class SerializationTests { + @Test + fun `Thread serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Thread::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "grant_id": "ca8f1733-6063-40cc-a2e3-ec7274abef11", + "id": "7ml84jdmfnw20sq59f30hirhe", + "object": "thread", + "has_attachments": false, + "has_drafts": false, + "earliest_message_date": 1634149514, + "latest_message_received_date": 1634832749, + "latest_message_sent_date": 1635174399, + "participants": [ + { + "email": "daenerys.t@example.com", + "name": "Daenerys Targaryen" + } + ], + "snippet": "jnlnnn --Sent with Nylas", + "starred": false, + "subject": "Dinner Wednesday?", + "unread": false, + "message_ids": [ + "njeb79kFFzli09", + "998abue3mGH4sk" + ], + "draft_ids": [ + "a809kmmoW90Dx" + ], + "folders": [ + "8l6c4d11y1p4dm4fxj52whyr9", + "d9zkcr2tljpu3m4qpj7l2hbr0" + ], + "latest_draft_or_message": { + "body": "Hello, I just sent a message using Nylas!", + "cc": [ + { + "name": "Arya Stark", + "email": "arya.stark@example.com" + } + ], + "date": 1635355739, + "attachments": [ + { + "content": "YXR0YWNoDQoNCi0tLS0tLS0tLS0gRm9yd2FyZGVkIG1lc3NhZ2UgL=", + "content_type": "text/calendar", + "id": "4kj2jrcoj9ve5j9yxqz5cuv98", + "size": 1708 + } + ], + "folders": [ + "8l6c4d11y1p4dm4fxj52whyr9", + "d9zkcr2tljpu3m4qpj7l2hbr0" + ], + "from": [ + { + "name": "Daenerys Targaryen", + "email": "daenerys.t@example.com" + } + ], + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "id": "njeb79kFFzli09", + "object": "message", + "reply_to": [ + { + "name": "Daenerys Targaryen", + "email": "daenerys.t@example.com" + } + ], + "snippet": "Hello, I just sent a message using Nylas!", + "starred": true, + "subject": "Hello from Nylas!", + "thread_id": "1t8tv3890q4vgmwq6pmdwm8qgsaer", + "to": [ + { + "name": "Jon Snow", + "email": "j.snow@example.com" + } + ], + "unread": true + } + } + """.trimIndent(), + ) + + val thread = adapter.fromJson(jsonBuffer)!! + assertIs(thread) + assertEquals("ca8f1733-6063-40cc-a2e3-ec7274abef11", thread.grantId) + assertEquals("7ml84jdmfnw20sq59f30hirhe", thread.id) + assertEquals("thread", thread.getObject()) + assertEquals(false, thread.hasAttachments) + assertEquals(false, thread.hasDrafts) + assertEquals(1634149514, thread.earliestMessageDate) + assertEquals(1634832749, thread.latestMessageReceivedDate) + assertEquals(1635174399, thread.latestMessageSentDate) + assertEquals( + listOf( + EmailName( + email = "daenerys.t@example.com", + name = "Daenerys Targaryen", + ), + ), + thread.participants, + ) + assertEquals("jnlnnn --Sent with Nylas", thread.snippet) + assertEquals(false, thread.starred) + assertEquals("Dinner Wednesday?", thread.subject) + assertEquals(false, thread.unread) + assertEquals( + listOf( + "njeb79kFFzli09", + "998abue3mGH4sk", + ), + thread.messageIds, + ) + assertEquals( + listOf( + "a809kmmoW90Dx", + ), + thread.draftIds, + ) + assertEquals( + listOf( + "8l6c4d11y1p4dm4fxj52whyr9", + "d9zkcr2tljpu3m4qpj7l2hbr0", + ), + thread.folders, + ) + assertIs(thread.latestDraftOrMessage) + } + } + + @Nested + inner class CrudTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var threads: Threads + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + threads = Threads(mockNylasClient) + } + + @Test + fun `listing threads calls requests with the correct params`() { + val queryParams = ListThreadsQueryParams( + limit = 10, + pageToken = "abc-123", + subject = "Hello from Nylas!", + anyEmail = listOf("test@gmail.com"), + to = listOf("rec@gmail.com"), + cc = listOf("cc@gmail.com"), + bcc = listOf("bcc@gmail.com"), + from = listOf("from@gmail.com"), + inFolder = listOf("inbox"), + unread = true, + starred = true, + hasAttachment = true, + latestMessageBefore = 1634832749, + latestMessageAfter = 1634149514, + searchQueryNative = "1634832749", + ) + + threads.list(grantId, queryParams) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/threads", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Thread::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `listing threads without query params calls requests with the correct params`() { + threads.list(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/threads", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Thread::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `finding a thread calls requests with the correct params`() { + val threadId = "thread-123" + + threads.find(grantId, threadId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/threads/$threadId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Thread::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `updating a thread calls requests with the correct params`() { + val threadId = "thread-123" + val adapter = JsonHelper.moshi().adapter(UpdateThreadRequest::class.java) + val updateThreadRequest = UpdateThreadRequest( + starred = true, + unread = true, + folders = listOf("folder-1", "folder-2"), + ) + + threads.update(grantId, threadId, updateThreadRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePut>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/threads/$threadId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Thread::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateThreadRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `destroying a thread calls requests with the correct params`() { + val threadId = "thread-123" + + threads.destroy(grantId, threadId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/threads/$threadId", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } +} From 4f1b293490db9f79086395c4d39e74492b7cd065 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:22:09 -0500 Subject: [PATCH 07/16] Add Grants enums --- src/main/kotlin/com/nylas/models/Grant.kt | 4 ++-- src/main/kotlin/com/nylas/models/GrantStatus.kt | 10 ++++++++++ src/main/kotlin/com/nylas/resources/Grants.kt | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/com/nylas/models/GrantStatus.kt diff --git a/src/main/kotlin/com/nylas/models/Grant.kt b/src/main/kotlin/com/nylas/models/Grant.kt index 8e1d70f2..62635cbd 100644 --- a/src/main/kotlin/com/nylas/models/Grant.kt +++ b/src/main/kotlin/com/nylas/models/Grant.kt @@ -15,7 +15,7 @@ data class Grant( * OAuth provider that the user authenticated with. */ @Json(name = "provider") - val provider: String, + val provider: AuthProvider, /** * Scopes specified for the grant. */ @@ -30,7 +30,7 @@ data class Grant( * Status of the grant, if it is still valid or if the user needs to re-authenticate. */ @Json(name = "grant_status") - val grantStatus: String? = null, + val grantStatus: GrantStatus? = null, /** * Email address associated with the grant. */ diff --git a/src/main/kotlin/com/nylas/models/GrantStatus.kt b/src/main/kotlin/com/nylas/models/GrantStatus.kt new file mode 100644 index 00000000..dc28654c --- /dev/null +++ b/src/main/kotlin/com/nylas/models/GrantStatus.kt @@ -0,0 +1,10 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +enum class GrantStatus { + @Json(name = "valid") + VALID, + @Json(name = "invalid") + INVALID, +} \ No newline at end of file diff --git a/src/main/kotlin/com/nylas/resources/Grants.kt b/src/main/kotlin/com/nylas/resources/Grants.kt index f520fa42..e70cbbdd 100644 --- a/src/main/kotlin/com/nylas/resources/Grants.kt +++ b/src/main/kotlin/com/nylas/resources/Grants.kt @@ -48,7 +48,7 @@ class Grants(client: NylasClient) : Resource(client, Grant::class.java) { .adapter(UpdateGrantRequest::class.java) .toJson(requestBody) - return updateResource(path, serializedRequestBody) + return patchResource(path, serializedRequestBody) } /** From c49a4ab5e20cfe59df9ca8fdd47146b6ea93792a Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:22:19 -0500 Subject: [PATCH 08/16] Add Grants tests --- .../kotlin/com/nylas/resources/GrantsTests.kt | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/test/kotlin/com/nylas/resources/GrantsTests.kt diff --git a/src/test/kotlin/com/nylas/resources/GrantsTests.kt b/src/test/kotlin/com/nylas/resources/GrantsTests.kt new file mode 100644 index 00000000..62b87c39 --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/GrantsTests.kt @@ -0,0 +1,209 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.ResponseBody +import okio.Buffer +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import java.lang.reflect.Type +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNull + +class GrantsTests { + private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) + private val mockCall: Call = Mockito.mock(Call::class.java) + private val mockResponse: okhttp3.Response = Mockito.mock(okhttp3.Response::class.java) + private val mockResponseBody: ResponseBody = Mockito.mock(ResponseBody::class.java) + private val mockOkHttpClientBuilder: OkHttpClient.Builder = Mockito.mock() + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(mockOkHttpClientBuilder.addInterceptor(any())).thenReturn(mockOkHttpClientBuilder) + whenever(mockOkHttpClientBuilder.build()).thenReturn(mockHttpClient) + whenever(mockHttpClient.newCall(any())).thenReturn(mockCall) + whenever(mockCall.execute()).thenReturn(mockResponse) + whenever(mockResponse.isSuccessful).thenReturn(true) + whenever(mockResponse.body()).thenReturn(mockResponseBody) + } + + @Nested + inner class SerializationTests { + @Test + fun `Grant serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Grant::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "id": "e19f8e1a-eb1c-41c0-b6a6-d2e59daf7f47", + "provider": "google", + "grant_status": "valid", + "email": "email@example.com", + "scope": [ + "Mail.Read", + "User.Read", + "offline_access" + ], + "user_agent": "string", + "ip": "string", + "state": "my-state", + "created_at": 1617817109, + "updated_at": 1617817109 + } + """.trimIndent(), + ) + + val grant = adapter.fromJson(jsonBuffer)!! + assertEquals("e19f8e1a-eb1c-41c0-b6a6-d2e59daf7f47", grant.id) + assertEquals(AuthProvider.GOOGLE, grant.provider) + assertEquals(GrantStatus.VALID, grant.grantStatus) + assertEquals("email@example.com", grant.email) + assertEquals(listOf("Mail.Read", "User.Read", "offline_access"), grant.scope) + assertEquals("string", grant.userAgent) + assertEquals("string", grant.ip) + assertEquals("my-state", grant.state) + assertEquals(1617817109, grant.createdAt) + assertEquals(1617817109, grant.updatedAt) + } + } + + @Nested + inner class CrudTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var grants: Grants + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + grants = Grants(mockNylasClient) + } + + @Test + fun `listing grants calls requests with the correct params`() { + grants.list() + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Grant::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `listing grants with queryParams calls requests with the correct params`() { + val queryParams = ListGrantsQueryParams( + limit = 10, + offset = 10, + ) + + grants.list(queryParams) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Grant::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `finding a grant calls requests with the correct params`() { + val grantId = "abc123" + + grants.find(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Grant::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `updating a grant calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(UpdateGrantRequest::class.java) + val updateGrantRequest = UpdateGrantRequest( + settings = mapOf( + "key" to "value", + ), + scopes = listOf( + "Mail.Read", + "User.Read", + "offline_access", + ), + ) + val grantId = "abc123" + + grants.update(grantId, updateGrantRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePatch>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Grant::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateGrantRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `destroying a grant calls requests with the correct params`() { + val grantId = "abc123" + + grants.destroy(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } +} From a691e37f154a1e1326b23888e90c82d7a90233ca Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:02:21 -0500 Subject: [PATCH 09/16] Tests for messages --- .../com/nylas/resources/MessagesTests.kt | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 src/test/kotlin/com/nylas/resources/MessagesTests.kt diff --git a/src/test/kotlin/com/nylas/resources/MessagesTests.kt b/src/test/kotlin/com/nylas/resources/MessagesTests.kt new file mode 100644 index 00000000..d8804a0b --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/MessagesTests.kt @@ -0,0 +1,437 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.models.Response +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types +import okhttp3.* +import okio.Buffer +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.* +import java.lang.reflect.Type +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNull + +class MessagesTests { + private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) + private val mockCall: Call = Mockito.mock(Call::class.java) + private val mockResponse: okhttp3.Response = Mockito.mock(okhttp3.Response::class.java) + private val mockResponseBody: ResponseBody = Mockito.mock(ResponseBody::class.java) + private val mockOkHttpClientBuilder: OkHttpClient.Builder = Mockito.mock() + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(mockOkHttpClientBuilder.addInterceptor(any())).thenReturn(mockOkHttpClientBuilder) + whenever(mockOkHttpClientBuilder.build()).thenReturn(mockHttpClient) + whenever(mockHttpClient.newCall(any())).thenReturn(mockCall) + whenever(mockCall.execute()).thenReturn(mockResponse) + whenever(mockResponse.isSuccessful).thenReturn(true) + whenever(mockResponse.body()).thenReturn(mockResponseBody) + } + + @Nested + inner class SerializationTests { + @Test + fun `Message serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Message::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "body": "Hello, I just sent a message using Nylas!", + "cc": [ + { + "name": "Arya Stark", + "email": "arya.stark@example.com" + } + ], + "date": 1635355739, + "attachments": [ + { + "content_type": "text/calendar", + "id": "4kj2jrcoj9ve5j9yxqz5cuv98", + "size": 1708 + } + ], + "folders": [ + "8l6c4d11y1p4dm4fxj52whyr9", + "d9zkcr2tljpu3m4qpj7l2hbr0" + ], + "from": [ + { + "name": "Daenerys Targaryen", + "email": "daenerys.t@example.com" + } + ], + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "id": "5d3qmne77v32r8l4phyuksl2x", + "object": "message", + "reply_to": [ + { + "name": "Daenerys Targaryen", + "email": "daenerys.t@example.com" + } + ], + "snippet": "Hello, I just sent a message using Nylas!", + "starred": true, + "subject": "Hello from Nylas!", + "thread_id": "1t8tv3890q4vgmwq6pmdwm8qgsaer", + "to": [ + { + "name": "Jon Snow", + "email": "j.snow@example.com" + } + ], + "unread": true + } + """.trimIndent(), + ) + + val message = adapter.fromJson(jsonBuffer)!! + assertIs(message) + assertEquals("Hello, I just sent a message using Nylas!", message.body) + assertEquals(1635355739, message.date) + assertEquals(1, message.cc?.size) + assertEquals("Arya Stark", message.cc?.get(0)?.name) + assertEquals("arya.stark@example.com", message.cc?.get(0)?.email) + assertEquals(1, message.attachments?.size) + assertEquals("text/calendar", message.attachments?.get(0)?.contentType) + assertEquals("4kj2jrcoj9ve5j9yxqz5cuv98", message.attachments?.get(0)?.id) + assertEquals(1708, message.attachments?.get(0)?.size) + assertEquals(2, message.folders?.size) + assertEquals("8l6c4d11y1p4dm4fxj52whyr9", message.folders?.get(0)) + assertEquals("d9zkcr2tljpu3m4qpj7l2hbr0", message.folders?.get(1)) + assertEquals(1, message.from.size) + assertEquals("Daenerys Targaryen", message.from[0].name) + assertEquals("daenerys.t@example.com", message.from[0].email) + assertEquals("41009df5-bf11-4c97-aa18-b285b5f2e386", message.grantId) + assertEquals("5d3qmne77v32r8l4phyuksl2x", message.id) + assertEquals("message", message.getObject()) + assertEquals(1, message.replyTo?.size) + assertEquals("Daenerys Targaryen", message.replyTo?.get(0)?.name) + assertEquals("daenerys.t@example.com", message.replyTo?.get(0)?.email) + assertEquals("Hello, I just sent a message using Nylas!", message.snippet) + assertEquals(true, message.starred) + assertEquals("Hello from Nylas!", message.subject) + assertEquals("1t8tv3890q4vgmwq6pmdwm8qgsaer", message.threadId) + assertEquals(1, message.to?.size) + assertEquals("Jon Snow", message.to?.get(0)?.name) + assertEquals("j.snow@example.com", message.to?.get(0)?.email) + assertEquals(true, message.unread) + } + } + + @Nested + inner class CrudTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var messages: Messages + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + messages = Messages(mockNylasClient) + } + + @Test + fun `listing messages calls requests with the correct params`() { + val queryParams = ListMessagesQueryParams( + limit = 10, + pageToken = "abc-123", + subject = "Hello from Nylas!", + anyEmail = listOf("test@gmail.com"), + to = listOf("rec@gmail.com"), + cc = listOf("cc@gmail.com"), + bcc = listOf("bcc@gmail.com"), + from = listOf("from@gmail.com"), + inFolder = listOf("inbox"), + unread = true, + starred = true, + hasAttachment = true, + searchQueryNative = "1634832749", + receivedBefore = 1634832749, + receivedAfter = 1634832749, + fields = MessageFields.INCLUDE_HEADERS + ) + + messages.list(grantId, queryParams) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Message::class.java), typeCaptor.firstValue) + assertEquals(queryParams, queryParamCaptor.firstValue) + } + + @Test + fun `listing messages without query params calls requests with the correct params`() { + messages.list(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Message::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `finding a message calls requests with the correct params`() { + val messageId = "message-123" + + messages.find(grantId, messageId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/$messageId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Message::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `updating a message calls requests with the correct params`() { + val messageId = "message-123" + val adapter = JsonHelper.moshi().adapter(UpdateMessageRequest::class.java) + val updateMessageRequest = UpdateMessageRequest( + starred = true, + unread = true, + folders = listOf("folder-1", "folder-2"), + ) + + messages.update(grantId, messageId, updateMessageRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePut>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/$messageId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Message::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateMessageRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `destroying a message calls requests with the correct params`() { + val messageId = "message-123" + + messages.destroy(grantId, messageId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/$messageId", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } + + @Nested + inner class ScheduledMessagesTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var messages: Messages + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + messages = Messages(mockNylasClient) + } + + @Test + fun `listing scheduled messages calls requests with the correct params`() { + messages.listScheduledMessages(grantId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/schedules", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, ScheduledMessagesList::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `finding a scheduled message calls requests with the correct params`() { + val scheduledMessageId = "scheduled-message-123" + + messages.findScheduledMessage(grantId, scheduledMessageId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/schedules/$scheduledMessageId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, ScheduledMessage::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `stopping a scheduled message calls requests with the correct params`() { + val scheduledMessageId = "scheduled-message-123" + + messages.stopScheduledMessage(grantId, scheduledMessageId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/schedules/$scheduledMessageId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, StopScheduledMessageResponse::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } + + @Nested + inner class SendTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var messages: Messages + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + messages = Messages(mockNylasClient) + } + + @Test + fun `sending a message calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(SendMessageRequest::class.java) + val sendMessageRequest = SendMessageRequest( + to = listOf( + EmailName( + email = "test@gmail.com", + name = "Test", + ), + ), + body = "Hello, I just sent a message using Nylas!", + cc = listOf( + EmailName( + email = "test@gmail.com", + name = "Test", + ), + ), + bcc = listOf( + EmailName( + email = "bcc@gmail.com", + name = "BCC", + ), + ), + snippet = "Hello, I just sent a message using Nylas!", + threadId = "thread-123", + subject = "Hello from Nylas!", + unread = false, + starred = true, + sendAt = 1620000000, + replyToMessageId = "reply-to-message-id", + trackingOptions = TrackingOptions( + label = "label", + links = true, + opens = true, + threadReplies = true, + ), + ) + + messages.send(grantId, sendMessageRequest) + + val pathCaptor = argumentCaptor() + val methodCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeFormRequest>( + pathCaptor.capture(), + methodCaptor.capture(), + requestBodyCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/send", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Message::class.java), typeCaptor.firstValue) + assertEquals(NylasClient.HttpMethod.POST, methodCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + val multipart = requestBodyCaptor.firstValue as MultipartBody + assertEquals(1, multipart.size()) + val buffer = Buffer() + multipart.part(0).body().writeTo(buffer) + assertEquals(adapter.toJson(sendMessageRequest), buffer.readUtf8()) + } + } + + @Nested + inner class ResourceTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var messages: Messages + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + messages = Messages(mockNylasClient) + } + + @Test + fun `credentials returns a smart compose instance`() { + val smartCompose = messages.smartCompose() + assertIs(smartCompose) + } + } +} From bf014f9a6bf7ddd9a5c3ea6b62bdbf340b2771f6 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:13:18 -0500 Subject: [PATCH 10/16] Fix smart compose return type --- src/main/kotlin/com/nylas/resources/SmartCompose.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/nylas/resources/SmartCompose.kt b/src/main/kotlin/com/nylas/resources/SmartCompose.kt index 72787c37..c12ac3c3 100644 --- a/src/main/kotlin/com/nylas/resources/SmartCompose.kt +++ b/src/main/kotlin/com/nylas/resources/SmartCompose.kt @@ -3,8 +3,10 @@ package com.nylas.resources import com.nylas.NylasClient import com.nylas.models.ComposeMessageRequest import com.nylas.models.ComposeMessageResponse +import com.nylas.models.Draft import com.nylas.models.Response import com.nylas.util.JsonHelper +import com.squareup.moshi.Types /** * A collection of Smart Compose related API endpoints. @@ -26,8 +28,9 @@ class SmartCompose(private val client: NylasClient) { val serializedRequestBody = JsonHelper.moshi() .adapter(ComposeMessageRequest::class.java) .toJson(requestBody) + val responseType = Types.newParameterizedType(Response::class.java, ComposeMessageResponse::class.java) - return client.executePost(path, ComposeMessageResponse::class.java, serializedRequestBody) + return client.executePost(path, responseType, serializedRequestBody) } /** @@ -43,7 +46,8 @@ class SmartCompose(private val client: NylasClient) { val serializedRequestBody = JsonHelper.moshi() .adapter(ComposeMessageRequest::class.java) .toJson(requestBody) + val responseType = Types.newParameterizedType(Response::class.java, ComposeMessageResponse::class.java) - return client.executePost(path, ComposeMessageResponse::class.java, serializedRequestBody) + return client.executePost(path, responseType, serializedRequestBody) } } From ec7c559374ecd7e8c3711f01805366921db169fc Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:13:26 -0500 Subject: [PATCH 11/16] Add tests for smart compose --- .../com/nylas/resources/SmartComposeTests.kt | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/test/kotlin/com/nylas/resources/SmartComposeTests.kt diff --git a/src/test/kotlin/com/nylas/resources/SmartComposeTests.kt b/src/test/kotlin/com/nylas/resources/SmartComposeTests.kt new file mode 100644 index 00000000..785470af --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/SmartComposeTests.kt @@ -0,0 +1,128 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.ResponseBody +import okio.Buffer +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import java.lang.reflect.Type +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNull + +class SmartComposeTests { + private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) + private val mockCall: Call = Mockito.mock(Call::class.java) + private val mockResponse: okhttp3.Response = Mockito.mock(okhttp3.Response::class.java) + private val mockResponseBody: ResponseBody = Mockito.mock(ResponseBody::class.java) + private val mockOkHttpClientBuilder: OkHttpClient.Builder = Mockito.mock() + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(mockOkHttpClientBuilder.addInterceptor(any())).thenReturn(mockOkHttpClientBuilder) + whenever(mockOkHttpClientBuilder.build()).thenReturn(mockHttpClient) + whenever(mockHttpClient.newCall(any())).thenReturn(mockCall) + whenever(mockCall.execute()).thenReturn(mockResponse) + whenever(mockResponse.isSuccessful).thenReturn(true) + whenever(mockResponse.body()).thenReturn(mockResponseBody) + } + + @Nested + inner class SerializationTests { + @Test + fun `SmartCompose serializes properly`() { + val adapter = JsonHelper.moshi().adapter(ComposeMessageResponse::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "suggestion": "Hello world" + } + """.trimIndent(), + ) + + val smartCompose = adapter.fromJson(jsonBuffer)!! + assertIs(smartCompose) + assertEquals("Hello world", smartCompose.suggestion) + } + } + + @Nested + inner class ComposeMessageTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var smartCompose: SmartCompose + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + smartCompose = SmartCompose(mockNylasClient) + } + + @Test + fun `composing a message calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(ComposeMessageRequest::class.java) + val composeMessageRequest = ComposeMessageRequest( + prompt = "Hello world", + ) + + smartCompose.composeMessage(grantId, composeMessageRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePost>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/smart-compose", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, ComposeMessageResponse::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(composeMessageRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `composing a reply calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(ComposeMessageRequest::class.java) + val composeMessageRequest = ComposeMessageRequest( + prompt = "Hello world", + ) + val messageId = "abc-123-message-id" + + smartCompose.composeMessageReply(grantId, messageId, composeMessageRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePost>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/messages/$messageId/smart-compose", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, ComposeMessageResponse::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(composeMessageRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } +} \ No newline at end of file From bdc4fa779660d22e05c84e4e9a39a01e4d532fb8 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:49:57 -0500 Subject: [PATCH 12/16] Webhook fixes --- .../com/nylas/models/CreateWebhookRequest.kt | 20 +++++++++---------- .../com/nylas/models/UpdateWebhookRequest.kt | 20 +++++++++---------- src/main/kotlin/com/nylas/models/Webhook.kt | 10 +++++----- .../com/nylas/models/WebhookWithSecret.kt | 10 +++++----- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt b/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt index 3e4c7b8c..77566cba 100644 --- a/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt @@ -14,8 +14,8 @@ data class CreateWebhookRequest( /** * The url to send webhooks to. */ - @Json(name = "callback_url") - val callbackUrl: String, + @Json(name = "webhook_url") + val webhookUrl: String, /** * A human-readable description of the webhook destination. */ @@ -24,20 +24,20 @@ data class CreateWebhookRequest( /** * The email addresses that Nylas notifies when a webhook is down for a while. */ - @Json(name = "notification_email_address") - val notificationEmailAddress: String? = null, + @Json(name = "notification_email_addresses") + val notificationEmailAddresses: List? = null, ) { /** * A builder for creating a [CreateWebhookRequest]. * @param triggerTypes Select the event that triggers the webhook. - * @param callbackUrl The url to send webhooks to. + * @param webhookUrl The url to send webhooks to. */ data class Builder( private val triggerTypes: List, - private val callbackUrl: String, + private val webhookUrl: String, ) { private var description: String? = null - private var notificationEmailAddress: String? = null + private var notificationEmailAddresses: List? = null /** * Set a human-readable description of the webhook destination. @@ -48,15 +48,15 @@ data class CreateWebhookRequest( /** * Set the email addresses that Nylas notifies when a webhook is down for a while. - * @param notificationEmailAddress The email addresses that Nylas notifies when a webhook is down for a while. + * @param notificationEmailAddresses The email addresses that Nylas notifies when a webhook is down for a while. * @return The builder. */ - fun notificationEmailAddress(notificationEmailAddress: String?) = apply { this.notificationEmailAddress = notificationEmailAddress } + fun notificationEmailAddresses(notificationEmailAddresses: List?) = apply { this.notificationEmailAddresses = notificationEmailAddresses } /** * Build the [CreateWebhookRequest]. * @return The created [CreateWebhookRequest]. */ - fun build() = CreateWebhookRequest(triggerTypes, callbackUrl, description, notificationEmailAddress) + fun build() = CreateWebhookRequest(triggerTypes, webhookUrl, description, notificationEmailAddresses) } } diff --git a/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt b/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt index 2464230b..566cdfa5 100644 --- a/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt @@ -14,8 +14,8 @@ data class UpdateWebhookRequest( /** * The url to send webhooks to. */ - @Json(name = "callback_url") - val callbackUrl: String? = null, + @Json(name = "webhook_url") + val webhookUrl: String? = null, /** * A human-readable description of the webhook destination. */ @@ -24,17 +24,17 @@ data class UpdateWebhookRequest( /** * The email addresses that Nylas notifies when a webhook is down for a while. */ - @Json(name = "notification_email_address") - val notificationEmailAddress: String? = null, + @Json(name = "notification_email_addresses") + val notificationEmailAddresses: List? = null, ) { /** * A builder for creating a [UpdateWebhookRequest]. */ class Builder { private var triggerTypes: List? = null - private var callbackUrl: String? = null + private var webhookUrl: String? = null private var description: String? = null - private var notificationEmailAddress: String? = null + private var notificationEmailAddresses: List? = null /** * Set the event that triggers the webhook. @@ -48,7 +48,7 @@ data class UpdateWebhookRequest( * @param callbackUrl The url to send webhooks to. * @return The builder. */ - fun callbackUrl(callbackUrl: String?) = apply { this.callbackUrl = callbackUrl } + fun webhookUrl(webhookUrl: String?) = apply { this.webhookUrl = webhookUrl } /** * Set a human-readable description of the webhook destination. @@ -59,15 +59,15 @@ data class UpdateWebhookRequest( /** * Set the email addresses that Nylas notifies when a webhook is down for a while. - * @param notificationEmailAddress The email addresses that Nylas notifies when a webhook is down for a while. + * @param notificationEmailAddresses The email addresses that Nylas notifies when a webhook is down for a while. * @return The builder. */ - fun notificationEmailAddress(notificationEmailAddress: String?) = apply { this.notificationEmailAddress = notificationEmailAddress } + fun notificationEmailAddresses(notificationEmailAddresses: List?) = apply { this.notificationEmailAddresses = notificationEmailAddresses } /** * Build the [UpdateWebhookRequest]. * @return The created [UpdateWebhookRequest]. */ - fun build() = UpdateWebhookRequest(triggerTypes, callbackUrl, description, notificationEmailAddress) + fun build() = UpdateWebhookRequest(triggerTypes, webhookUrl, description, notificationEmailAddresses) } } diff --git a/src/main/kotlin/com/nylas/models/Webhook.kt b/src/main/kotlin/com/nylas/models/Webhook.kt index 3d47c722..e267ce2c 100644 --- a/src/main/kotlin/com/nylas/models/Webhook.kt +++ b/src/main/kotlin/com/nylas/models/Webhook.kt @@ -14,8 +14,8 @@ data class Webhook( /** * The url to send webhooks to. */ - @Json(name = "callback_url") - val callbackUrl: String, + @Json(name = "webhook_url") + val webhookUrl: String, /** * Select the event that triggers the webhook. */ @@ -40,7 +40,7 @@ data class Webhook( * The time the status field was last updated, represented as a Unix timestamp in seconds. */ @Json(name = "updated_at") - val updatedAt: String, + val updatedAt: Long, /** * A human-readable description of the webhook destination. */ @@ -49,6 +49,6 @@ data class Webhook( /** * The email addresses that Nylas notifies when a webhook is down for a while. */ - @Json(name = "notification_email_address") - val notificationEmailAddress: String? = null, + @Json(name = "notification_email_addresses") + val notificationEmailAddresses: List? = null, ) diff --git a/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt b/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt index 3b7a3260..998d547b 100644 --- a/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt +++ b/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt @@ -14,8 +14,8 @@ data class WebhookWithSecret( /** * The url to send webhooks to. */ - @Json(name = "callback_url") - val callbackUrl: String, + @Json(name = "webhook_url") + val webhookUrl: String, /** * A secret value used to encode the X-Nylas-Signature header on webhook requests. */ @@ -45,7 +45,7 @@ data class WebhookWithSecret( * The time the status field was last updated, represented as a Unix timestamp in seconds. */ @Json(name = "updated_at") - val updatedAt: String, + val updatedAt: Long, /** * A human-readable description of the webhook destination. */ @@ -54,6 +54,6 @@ data class WebhookWithSecret( /** * The email addresses that Nylas notifies when a webhook is down for a while. */ - @Json(name = "notification_email_address") - val notificationEmailAddress: String? = null, + @Json(name = "notification_email_addresses") + val notificationEmailAddresses: List? = null, ) From 942fd5b14ce1511939eaf2297c2578f6549835cc Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:50:02 -0500 Subject: [PATCH 13/16] Webhook tests --- .../com/nylas/resources/WebhooksTests.kt | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 src/test/kotlin/com/nylas/resources/WebhooksTests.kt diff --git a/src/test/kotlin/com/nylas/resources/WebhooksTests.kt b/src/test/kotlin/com/nylas/resources/WebhooksTests.kt new file mode 100644 index 00000000..5090f9a9 --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/WebhooksTests.kt @@ -0,0 +1,260 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.ResponseBody +import okio.Buffer +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import java.lang.reflect.Type +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs + +class WebhooksTests { + private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java) + private val mockCall: Call = Mockito.mock(Call::class.java) + private val mockResponse: okhttp3.Response = Mockito.mock(okhttp3.Response::class.java) + private val mockResponseBody: ResponseBody = Mockito.mock(ResponseBody::class.java) + private val mockOkHttpClientBuilder: OkHttpClient.Builder = Mockito.mock() + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + whenever(mockOkHttpClientBuilder.addInterceptor(any())).thenReturn(mockOkHttpClientBuilder) + whenever(mockOkHttpClientBuilder.build()).thenReturn(mockHttpClient) + whenever(mockHttpClient.newCall(any())).thenReturn(mockCall) + whenever(mockCall.execute()).thenReturn(mockResponse) + whenever(mockResponse.isSuccessful).thenReturn(true) + whenever(mockResponse.body()).thenReturn(mockResponseBody) + } + + @Nested + inner class SerializationTests { + @Test + fun `Webhook serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Webhook::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "id": "UMWjAjMeWQ4D8gYF2moonK4486", + "description": "Production webhook destination", + "trigger_types": [ + "calendar.created" + ], + "webhook_url": "https://example.com/webhooks", + "status": "active", + "notification_email_addresses": [ + "jane@example.com", + "joe@example.com" + ], + "status_updated_at": 1234567890, + "created_at": 1234567890, + "updated_at": 1234567890 + } + """.trimIndent(), + ) + + val webhook = adapter.fromJson(jsonBuffer)!! + assertEquals("UMWjAjMeWQ4D8gYF2moonK4486", webhook.id) + assertEquals("Production webhook destination", webhook.description) + assertEquals(listOf(WebhookTriggers.CALENDAR_CREATED), webhook.triggerTypes) + assertEquals("https://example.com/webhooks", webhook.webhookUrl) + assertEquals(WebhookStatus.ACTIVE, webhook.status) + assertEquals(listOf("jane@example.com", "joe@example.com"), webhook.notificationEmailAddresses) + assertEquals(1234567890, webhook.statusUpdatedAt) + assertEquals(1234567890, webhook.createdAt) + assertEquals(1234567890, webhook.updatedAt) + } + } + + @Nested + inner class CrudTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var webhooks: Webhooks + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + webhooks = Webhooks(mockNylasClient) + } + + @Test + fun `listing webhooks calls requests with the correct params`() { + webhooks.list() + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/webhooks", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Webhook::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `finding a webhook calls requests with the correct params`() { + val webhookId = "WEBHOOK_ID" + webhooks.find(webhookId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/webhooks/$webhookId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Webhook::class.java), typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `creating a webhook calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(CreateWebhookRequest::class.java) + val createWebhookRequest = CreateWebhookRequest( + description = "Production webhook destination", + triggerTypes = listOf(WebhookTriggers.CALENDAR_CREATED), + webhookUrl = "https://example.com/webhooks", + notificationEmailAddresses = listOf("jane@example.com") + ) + + webhooks.create(createWebhookRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePost>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/webhooks", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, WebhookWithSecret::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(createWebhookRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `updating a webhook calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(UpdateWebhookRequest::class.java) + val updateWebhookRequest = UpdateWebhookRequest( + description = "Production webhook destination", + triggerTypes = listOf(WebhookTriggers.CALENDAR_CREATED), + webhookUrl = "https://example.com/webhooks", + notificationEmailAddresses = listOf("jane@example.com") + ) + val webhookId = "WEBHOOK_ID" + + webhooks.update(webhookId, updateWebhookRequest) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePut>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/webhooks/$webhookId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Webhook::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateWebhookRequest), requestBodyCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + + @Test + fun `destroying a webhook calls requests with the correct params`() { + val webhookId = "WEBHOOK_ID" + + webhooks.destroy(webhookId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/webhooks/$webhookId", pathCaptor.firstValue) + assertEquals(WebhookDeleteResponse::class.java, typeCaptor.firstValue) + assertNull(queryParamCaptor.firstValue) + } + } + + @Nested + inner class OtherMethodTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var webhooks: Webhooks + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + webhooks = Webhooks(mockNylasClient) + } + + @Test + fun `rotating secret calls requests with the correct params`() { + val webhookId = "webhook-123" + + webhooks.rotateSecret(webhookId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val requestBodyCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executePost>( + pathCaptor.capture(), + typeCaptor.capture(), + requestBodyCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/webhooks/$webhookId/rotate-secret", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, WebhookWithSecret::class.java), typeCaptor.firstValue) + assertNull(requestBodyCaptor.firstValue) + } + + @Test + fun `getting ip addresses calls requests with the correct params`() { + webhooks.ipAddresses() + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/webhooks/ip-addresses", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, WebhookIpAddressesResponse::class.java), typeCaptor.firstValue) + } + } +} From 7c6d6920b8127a1e034bfd281326c66d04716c76 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:50:22 -0500 Subject: [PATCH 14/16] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4eea5f0..54af486d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Added support for sending drafts * Added support for the contacts API * Added enum support for OAuth prompt +* Added additional enum support for Grants ### Changed * Fixed issue with sending scheduled messages @@ -17,6 +18,9 @@ * Fixed provider detect endpoint path * Fixed scope encoding for OAuth URL * Fixed typo in 'EventVisibility' enum +* Fixed various issues with the connector API +* Fixed response type for smart compose methods +* Fixed inaccuracies with webhook related models ## [2.0.0-beta.3] - Released 2023-12-18 From e5a60075c327102d7995b496205659a9638ed66c Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:28:58 -0500 Subject: [PATCH 15/16] Add tests for extracting challenge parameter --- .../kotlin/com/nylas/resources/WebhooksTests.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/test/kotlin/com/nylas/resources/WebhooksTests.kt b/src/test/kotlin/com/nylas/resources/WebhooksTests.kt index 5090f9a9..cb4d34c4 100644 --- a/src/test/kotlin/com/nylas/resources/WebhooksTests.kt +++ b/src/test/kotlin/com/nylas/resources/WebhooksTests.kt @@ -256,5 +256,22 @@ class WebhooksTests { assertEquals("v3/webhooks/ip-addresses", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(ListResponse::class.java, WebhookIpAddressesResponse::class.java), typeCaptor.firstValue) } + + @Test + fun `extracting challenge parameter from url succeeds if present`() { + val url = "https://example.com?challenge=12345" + val challenge = webhooks.extractChallengeParameter(url) + assertEquals("12345", challenge) + } + + @Test + fun `extracting challenge parameter from url throws an error if not present`() { + val url = "https://example.com?foo=bar" + try { + webhooks.extractChallengeParameter(url) + } catch (e: IllegalArgumentException) { + assertIs(e) + } + } } } From 631e686f5a1912811a73531abaf27d3480f799c9 Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:32:25 -0500 Subject: [PATCH 16/16] linting --- src/main/kotlin/com/nylas/models/GrantStatus.kt | 3 ++- src/main/kotlin/com/nylas/resources/SmartCompose.kt | 1 - src/test/kotlin/com/nylas/resources/GrantsTests.kt | 1 + src/test/kotlin/com/nylas/resources/MessagesTests.kt | 2 +- src/test/kotlin/com/nylas/resources/SmartComposeTests.kt | 2 +- src/test/kotlin/com/nylas/resources/WebhooksTests.kt | 4 ++-- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/nylas/models/GrantStatus.kt b/src/main/kotlin/com/nylas/models/GrantStatus.kt index dc28654c..242825b2 100644 --- a/src/main/kotlin/com/nylas/models/GrantStatus.kt +++ b/src/main/kotlin/com/nylas/models/GrantStatus.kt @@ -5,6 +5,7 @@ import com.squareup.moshi.Json enum class GrantStatus { @Json(name = "valid") VALID, + @Json(name = "invalid") INVALID, -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/nylas/resources/SmartCompose.kt b/src/main/kotlin/com/nylas/resources/SmartCompose.kt index c12ac3c3..036c35bf 100644 --- a/src/main/kotlin/com/nylas/resources/SmartCompose.kt +++ b/src/main/kotlin/com/nylas/resources/SmartCompose.kt @@ -3,7 +3,6 @@ package com.nylas.resources import com.nylas.NylasClient import com.nylas.models.ComposeMessageRequest import com.nylas.models.ComposeMessageResponse -import com.nylas.models.Draft import com.nylas.models.Response import com.nylas.util.JsonHelper import com.squareup.moshi.Types diff --git a/src/test/kotlin/com/nylas/resources/GrantsTests.kt b/src/test/kotlin/com/nylas/resources/GrantsTests.kt index 62b87c39..9819488f 100644 --- a/src/test/kotlin/com/nylas/resources/GrantsTests.kt +++ b/src/test/kotlin/com/nylas/resources/GrantsTests.kt @@ -67,6 +67,7 @@ class GrantsTests { ) val grant = adapter.fromJson(jsonBuffer)!! + assertIs(grant) assertEquals("e19f8e1a-eb1c-41c0-b6a6-d2e59daf7f47", grant.id) assertEquals(AuthProvider.GOOGLE, grant.provider) assertEquals(GrantStatus.VALID, grant.grantStatus) diff --git a/src/test/kotlin/com/nylas/resources/MessagesTests.kt b/src/test/kotlin/com/nylas/resources/MessagesTests.kt index d8804a0b..d7f15ad8 100644 --- a/src/test/kotlin/com/nylas/resources/MessagesTests.kt +++ b/src/test/kotlin/com/nylas/resources/MessagesTests.kt @@ -158,7 +158,7 @@ class MessagesTests { searchQueryNative = "1634832749", receivedBefore = 1634832749, receivedAfter = 1634832749, - fields = MessageFields.INCLUDE_HEADERS + fields = MessageFields.INCLUDE_HEADERS, ) messages.list(grantId, queryParams) diff --git a/src/test/kotlin/com/nylas/resources/SmartComposeTests.kt b/src/test/kotlin/com/nylas/resources/SmartComposeTests.kt index 785470af..980021b7 100644 --- a/src/test/kotlin/com/nylas/resources/SmartComposeTests.kt +++ b/src/test/kotlin/com/nylas/resources/SmartComposeTests.kt @@ -125,4 +125,4 @@ class SmartComposeTests { assertNull(queryParamCaptor.firstValue) } } -} \ No newline at end of file +} diff --git a/src/test/kotlin/com/nylas/resources/WebhooksTests.kt b/src/test/kotlin/com/nylas/resources/WebhooksTests.kt index cb4d34c4..ed45decc 100644 --- a/src/test/kotlin/com/nylas/resources/WebhooksTests.kt +++ b/src/test/kotlin/com/nylas/resources/WebhooksTests.kt @@ -134,7 +134,7 @@ class WebhooksTests { description = "Production webhook destination", triggerTypes = listOf(WebhookTriggers.CALENDAR_CREATED), webhookUrl = "https://example.com/webhooks", - notificationEmailAddresses = listOf("jane@example.com") + notificationEmailAddresses = listOf("jane@example.com"), ) webhooks.create(createWebhookRequest) @@ -163,7 +163,7 @@ class WebhooksTests { description = "Production webhook destination", triggerTypes = listOf(WebhookTriggers.CALENDAR_CREATED), webhookUrl = "https://example.com/webhooks", - notificationEmailAddresses = listOf("jane@example.com") + notificationEmailAddresses = listOf("jane@example.com"), ) val webhookId = "WEBHOOK_ID"