diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c40e76d..b74cb725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,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 @@ -22,6 +23,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 diff --git a/src/main/kotlin/com/nylas/models/ApplicationDetails.kt b/src/main/kotlin/com/nylas/models/ApplicationDetails.kt index d3133d64..4f768bfb 100644 --- a/src/main/kotlin/com/nylas/models/ApplicationDetails.kt +++ b/src/main/kotlin/com/nylas/models/ApplicationDetails.kt @@ -37,10 +37,10 @@ data class ApplicationDetails( @Json(name = "hosted_authentication") val hostedAuthentication: HostedAuthentication? = null, /** - * List of redirect URIs + * List of callback URIs */ - @Json(name = "redirect_uris") - val redirectUris: List? = null, + @Json(name = "callback_uris") + val callbackUris: List? = null, ) { /** * Class representation of branding details for the application diff --git a/src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt b/src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt index 625f18f5..f4c17feb 100644 --- a/src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt +++ b/src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a request to compose a message. */ @@ -7,5 +9,6 @@ data class ComposeMessageRequest( /** * The prompt that smart compose will use to generate a message suggestion. */ + @Json(name = "prompt") val prompt: String, ) diff --git a/src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt b/src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt index e94e7aad..f3c0304a 100644 --- a/src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt +++ b/src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a response to a message composition request. */ @@ -7,5 +9,6 @@ data class ComposeMessageResponse( /** * The message suggestion generated by smart compose. */ + @Json(name = "suggestion") val suggestion: String, ) diff --git a/src/main/kotlin/com/nylas/models/Connector.kt b/src/main/kotlin/com/nylas/models/Connector.kt index cf5f0cda..20ee20d0 100644 --- a/src/main/kotlin/com/nylas/models/Connector.kt +++ b/src/main/kotlin/com/nylas/models/Connector.kt @@ -1,29 +1,66 @@ package com.nylas.models import com.squareup.moshi.Json +import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory /** * Class representation of the Nylas connector response. */ -data class Connector( - /** - * Custom name of the connector - */ - @Json(name = "name") - val name: String, +sealed class Connector( /** * The provider type */ @Json(name = "provider") val provider: AuthProvider, +) { + /** + * Class representing a Google connector creation request. + */ + data class Google( + /** + * The Google OAuth provider credentials and settings + */ + @Json(name = "settings") + val settings: GoogleConnectorSettings, + /** + * The Google OAuth scopes + */ + @Json(name = "scope") + val scope: List? = null, + ) : Connector(AuthProvider.GOOGLE) + /** - * Optional settings from provider + * Class representing a Microsoft connector creation request. */ - @Json(name = "settings") - val settings: Map? = null, + data class Microsoft( + /** + * The Microsoft OAuth provider credentials and settings + */ + @Json(name = "settings") + val settings: MicrosoftConnectorSettings, + /** + * The Microsoft OAuth scopes + */ + @Json(name = "scope") + val scope: List? = null, + ) : Connector(AuthProvider.MICROSOFT) + /** - * Default scopes for the connector + * Class representing an IMAP connector creation request. */ - @Json(name = "scope") - val scope: List? = null, -) + class Imap : Connector(AuthProvider.IMAP) + + /** + * Class representing a virtual calendar connector creation request. + */ + class VirtualCalendar : Connector(AuthProvider.VIRTUAL_CALENDAR) + + companion object { + @JvmStatic + val CONNECTOR_JSON_ADAPTER_FACTORY: PolymorphicJsonAdapterFactory = PolymorphicJsonAdapterFactory.of(Connector::class.java, "provider") + .withSubtype(Google::class.java, AuthProvider.GOOGLE.value) + .withSubtype(Microsoft::class.java, AuthProvider.MICROSOFT.value) + .withSubtype(Imap::class.java, AuthProvider.IMAP.value) + .withSubtype(VirtualCalendar::class.java, AuthProvider.VIRTUAL_CALENDAR.value) + } +} diff --git a/src/main/kotlin/com/nylas/models/ContactEmail.kt b/src/main/kotlin/com/nylas/models/ContactEmail.kt index 1cf4188f..6106ac01 100644 --- a/src/main/kotlin/com/nylas/models/ContactEmail.kt +++ b/src/main/kotlin/com/nylas/models/ContactEmail.kt @@ -1,10 +1,14 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Interface for email addresses in a contact. */ data class ContactEmail( + @Json(name = "email") val email: String? = null, + @Json(name = "type") val type: ContactType? = null, ) { class Builder { diff --git a/src/main/kotlin/com/nylas/models/ContactGroup.kt b/src/main/kotlin/com/nylas/models/ContactGroup.kt index b4fc571d..67cab6c8 100644 --- a/src/main/kotlin/com/nylas/models/ContactGroup.kt +++ b/src/main/kotlin/com/nylas/models/ContactGroup.kt @@ -1,14 +1,22 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a contact group. */ data class ContactGroup( + @Json(name = "id") val id: String, + @Json(name = "grant_id") val grantId: String? = null, + @Json(name = "group_type") val groupType: GroupType? = null, + @Json(name = "name") val name: String? = null, + @Json(name = "path") val path: String? = null, + @Json(name = "object") private val obj: String = "contact_group", ) { fun getObject() = obj diff --git a/src/main/kotlin/com/nylas/models/ContactGroupId.kt b/src/main/kotlin/com/nylas/models/ContactGroupId.kt index 4db33a7a..85e050e7 100644 --- a/src/main/kotlin/com/nylas/models/ContactGroupId.kt +++ b/src/main/kotlin/com/nylas/models/ContactGroupId.kt @@ -1,8 +1,14 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing an object that points to a contact group ID. */ data class ContactGroupId( + /** + * The ID of the contact group. + */ + @Json(name = "id") val id: String, ) diff --git a/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt b/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt index b485c5d6..5a4eb416 100644 --- a/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateConnectorRequest.kt @@ -1,16 +1,12 @@ package com.nylas.models import com.squareup.moshi.Json +import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory /** * This sealed class represents the base Nylas connector creation request. */ sealed class CreateConnectorRequest( - /** - * Custom name of the connector - */ - @Json(name = "name") - open val name: String, /** * The provider type */ @@ -21,11 +17,6 @@ sealed class CreateConnectorRequest( * Class representing a Google connector creation request. */ data class Google( - /** - * Custom name of the connector - */ - @Json(name = "name") - override val name: String, /** * The Google OAuth provider credentials and settings */ @@ -35,18 +26,36 @@ sealed class CreateConnectorRequest( * The Google OAuth scopes */ @Json(name = "scope") - val scope: List?, - ) : CreateConnectorRequest(name, AuthProvider.GOOGLE) + val scope: List? = null, + ) : CreateConnectorRequest(AuthProvider.GOOGLE) { + /** + * Builder for Google connector creation requests. + * @param settings The Google OAuth provider credentials and settings + */ + data class Builder( + private val settings: GoogleCreateConnectorSettings, + ) { + private var scope: List? = null + + /** + * Set the Google OAuth scopes + * @param scope The Google OAuth scopes + * @return The builder + */ + fun scope(scope: List) = apply { this.scope = scope } + + /** + * Build the Google connector creation request + * @return The Google connector creation request + */ + fun build() = Google(settings, scope) + } + } /** * Class representing a Microsoft connector creation request. */ data class Microsoft( - /** - * Custom name of the connector - */ - @Json(name = "name") - override val name: String, /** * The Microsoft OAuth provider credentials and settings */ @@ -56,28 +65,48 @@ sealed class CreateConnectorRequest( * The Microsoft OAuth scopes */ @Json(name = "scope") - val scope: List?, - ) : CreateConnectorRequest(name, AuthProvider.MICROSOFT) + val scope: List? = null, + ) : CreateConnectorRequest(AuthProvider.MICROSOFT) { + /** + * Builder for Microsoft connector creation requests. + * @param settings The Microsoft OAuth provider credentials and settings + */ + data class Builder( + private val settings: MicrosoftCreateConnectorSettings, + ) { + private var scope: List? = null + + /** + * Set the Microsoft OAuth scopes + * @param scope The Microsoft OAuth scopes + * @return The builder + */ + fun scope(scope: List) = apply { this.scope = scope } + + /** + * Build the Microsoft connector creation request + * @return The Microsoft connector creation request + */ + fun build() = Microsoft(settings, scope) + } + } /** * Class representing an IMAP connector creation request. */ - data class Imap( - /** - * Custom name of the connector - */ - @Json(name = "name") - override val name: String, - ) : CreateConnectorRequest(name, AuthProvider.IMAP) + class Imap : CreateConnectorRequest(AuthProvider.IMAP) /** * Class representing a virtual calendar connector creation request. */ - data class VirtualCalendar( - /** - * Custom name of the connector - */ - @Json(name = "name") - override val name: String, - ) : CreateConnectorRequest(name, AuthProvider.VIRTUAL_CALENDAR) + class VirtualCalendar : CreateConnectorRequest(AuthProvider.VIRTUAL_CALENDAR) + + companion object { + @JvmStatic + val CREATE_CONNECTOR_JSON_ADAPTER_FACTORY: PolymorphicJsonAdapterFactory = PolymorphicJsonAdapterFactory.of(CreateConnectorRequest::class.java, "provider") + .withSubtype(Google::class.java, AuthProvider.GOOGLE.value) + .withSubtype(Microsoft::class.java, AuthProvider.MICROSOFT.value) + .withSubtype(Imap::class.java, AuthProvider.IMAP.value) + .withSubtype(VirtualCalendar::class.java, AuthProvider.VIRTUAL_CALENDAR.value) + } } diff --git a/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt b/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt index b99cd659..4dc0727a 100644 --- a/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt @@ -1,6 +1,7 @@ package com.nylas.models import com.squareup.moshi.Json +import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory /** * Class representing a request to create a credential @@ -69,4 +70,12 @@ sealed class CreateCredentialRequest( @Json(name = "credential_data") override val credentialData: CredentialData.ConnectorOverride, ) : CreateCredentialRequest(name, credentialData, CredentialType.CONNECTOR) + + companion object { + @JvmStatic + val CREATE_CREDENTIAL_JSON_ADAPTER_FACTORY: PolymorphicJsonAdapterFactory = PolymorphicJsonAdapterFactory.of(CreateCredentialRequest::class.java, "credential_type") + .withSubtype(Microsoft::class.java, CredentialType.ADMINCONSENT.value) + .withSubtype(Google::class.java, CredentialType.SERVICEACCOUNT.value) + .withSubtype(Override::class.java, CredentialType.CONNECTOR.value) + } } diff --git a/src/main/kotlin/com/nylas/models/CreateFolderRequest.kt b/src/main/kotlin/com/nylas/models/CreateFolderRequest.kt index 5f7a5644..f5e41226 100644 --- a/src/main/kotlin/com/nylas/models/CreateFolderRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateFolderRequest.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representation of the Nylas folder creation request. */ @@ -7,18 +9,22 @@ data class CreateFolderRequest( /** * The name of the folder. */ + @Json(name = "name") val name: String, /** * The parent ID of the folder. (Microsoft only) */ + @Json(name = "parent_id") val parentId: String? = null, /** * The background color of the folder. (Google only) */ + @Json(name = "background_color") val backgroundColor: String? = null, /** * The text color of the folder. (Google only) */ + @Json(name = "text_color") val textColor: String? = null, ) { /** diff --git a/src/main/kotlin/com/nylas/models/CreateGrantRequest.kt b/src/main/kotlin/com/nylas/models/CreateGrantRequest.kt index aefcf121..20d9c1a4 100644 --- a/src/main/kotlin/com/nylas/models/CreateGrantRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateGrantRequest.kt @@ -24,8 +24,8 @@ data class CreateGrantRequest( /** * Optional list of scopes to request. If not specified it will use the integration default scopes. */ - @Json(name = "scopes") - val scopes: List? = null, + @Json(name = "scope") + val scope: List? = null, ) { /** * Builder for [CreateGrantRequest]. diff --git a/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt b/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt index 943da6dc..77566cba 100644 --- a/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateWebhookRequest.kt @@ -24,8 +24,8 @@ 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]. @@ -37,7 +37,7 @@ data class CreateWebhookRequest( 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, webhookUrl, description, notificationEmailAddress) + fun build() = CreateWebhookRequest(triggerTypes, webhookUrl, description, notificationEmailAddresses) } } diff --git a/src/main/kotlin/com/nylas/models/Credential.kt b/src/main/kotlin/com/nylas/models/Credential.kt index 1d05ebca..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: Int?, + val createdAt: Long? = null, /** * Timestamp of when the credential was updated */ @Json(name = "updated_at") - val updatedAt: Int?, + val updatedAt: Long? = null, ) diff --git a/src/main/kotlin/com/nylas/models/CredentialType.kt b/src/main/kotlin/com/nylas/models/CredentialType.kt index 1a1f10d1..5e0ae278 100644 --- a/src/main/kotlin/com/nylas/models/CredentialType.kt +++ b/src/main/kotlin/com/nylas/models/CredentialType.kt @@ -2,13 +2,13 @@ package com.nylas.models import com.squareup.moshi.Json -enum class CredentialType { +enum class CredentialType(val value: String) { @Json(name = "adminconsent") - ADMINCONSENT, + ADMINCONSENT("adminconsent"), @Json(name = "serviceaccount") - SERVICEACCOUNT, + SERVICEACCOUNT("serviceaccount"), @Json(name = "connector") - CONNECTOR, + CONNECTOR("connector"), } diff --git a/src/main/kotlin/com/nylas/models/EmailName.kt b/src/main/kotlin/com/nylas/models/EmailName.kt index 6c4c87ae..f4eb280d 100644 --- a/src/main/kotlin/com/nylas/models/EmailName.kt +++ b/src/main/kotlin/com/nylas/models/EmailName.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing an email address and optional name. */ @@ -7,9 +9,11 @@ data class EmailName( /** * Email address. */ + @Json(name = "email") val email: String, /** * Full name. */ + @Json(name = "name") val name: String? = null, ) diff --git a/src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt b/src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt index b830c079..5b1e0e0d 100644 --- a/src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt +++ b/src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing the query parameters for finding a message. */ @@ -7,5 +9,6 @@ data class FindMessageQueryParams( /** * Allows you to specify to the message with headers included. */ + @Json(name = "fields") val fields: MessageFields? = null, ) diff --git a/src/main/kotlin/com/nylas/models/GoogleConnectorSettings.kt b/src/main/kotlin/com/nylas/models/GoogleConnectorSettings.kt new file mode 100644 index 00000000..01061968 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/GoogleConnectorSettings.kt @@ -0,0 +1,14 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing Google connector setting. + */ +data class GoogleConnectorSettings( + /** + * The Google Pub/Sub topic name + */ + @Json(name = "topic_name") + val topicName: String? = null, +) diff --git a/src/main/kotlin/com/nylas/models/GoogleCreateConnectorSettings.kt b/src/main/kotlin/com/nylas/models/GoogleCreateConnectorSettings.kt index 9a260bf1..fb8b86f1 100644 --- a/src/main/kotlin/com/nylas/models/GoogleCreateConnectorSettings.kt +++ b/src/main/kotlin/com/nylas/models/GoogleCreateConnectorSettings.kt @@ -21,4 +21,33 @@ data class GoogleCreateConnectorSettings( */ @Json(name = "topic_name") val topicName: String? = null, -) +) { + /** + * Builder for [GoogleCreateConnectorSettings]. + * @property clientId The Google Client ID + * @property clientSecret The Google Client Secret + */ + data class Builder( + private val clientId: String, + private var clientSecret: String, + ) { + private var topicName: String? = null + + /** + * Set the Google Pub/Sub topic name + * @param topicName The Google Pub/Sub topic name + * @return The builder + */ + fun topicName(topicName: String) = apply { this.topicName = topicName } + + /** + * Build the [GoogleCreateConnectorSettings] object + * @return The [GoogleCreateConnectorSettings] object + */ + fun build() = GoogleCreateConnectorSettings( + clientId = clientId, + clientSecret = clientSecret, + topicName = topicName, + ) + } +} 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..242825b2 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/GrantStatus.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +enum class GrantStatus { + @Json(name = "valid") + VALID, + + @Json(name = "invalid") + INVALID, +} diff --git a/src/main/kotlin/com/nylas/models/InstantMessagingAddress.kt b/src/main/kotlin/com/nylas/models/InstantMessagingAddress.kt index 6db3b788..f05d9711 100644 --- a/src/main/kotlin/com/nylas/models/InstantMessagingAddress.kt +++ b/src/main/kotlin/com/nylas/models/InstantMessagingAddress.kt @@ -1,10 +1,14 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representation for an IM address in a contact. */ data class InstantMessagingAddress( + @Json(name = "im_address") val imAddress: String? = null, + @Json(name = "type") val type: ContactType? = null, ) { class Builder { diff --git a/src/main/kotlin/com/nylas/models/ListCredentialsQueryParams.kt b/src/main/kotlin/com/nylas/models/ListCredentialsQueryParams.kt index 4ed68fb3..ec3092f5 100644 --- a/src/main/kotlin/com/nylas/models/ListCredentialsQueryParams.kt +++ b/src/main/kotlin/com/nylas/models/ListCredentialsQueryParams.kt @@ -10,22 +10,22 @@ data class ListCredentialsQueryParams( * Limit the number of results */ @Json(name = "limit") - val limit: Int?, + val limit: Int? = null, /** * Offset the results by this number */ @Json(name = "offset") - val offset: Int?, + val offset: Int? = null, /** * Sort the results by field name */ @Json(name = "sort_by") - val sortBy: SortBy?, + val sortBy: SortBy? = null, /** * Order the results by ascending or descending */ @Json(name = "order_by") - val orderBy: OrderBy?, + val orderBy: OrderBy? = null, ) : IQueryParams { /** * Builder for [ListCredentialsQueryParams]. diff --git a/src/main/kotlin/com/nylas/models/MessageHeaders.kt b/src/main/kotlin/com/nylas/models/MessageHeaders.kt index 5789123b..375543b7 100644 --- a/src/main/kotlin/com/nylas/models/MessageHeaders.kt +++ b/src/main/kotlin/com/nylas/models/MessageHeaders.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a message header. */ @@ -7,9 +9,11 @@ data class MessageHeaders( /** * The header name. */ + @Json(name = "name") val name: String, /** * The header value. */ + @Json(name = "value") val value: String, ) diff --git a/src/main/kotlin/com/nylas/models/MicrosoftConnectorSettings.kt b/src/main/kotlin/com/nylas/models/MicrosoftConnectorSettings.kt new file mode 100644 index 00000000..4e172ac1 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/MicrosoftConnectorSettings.kt @@ -0,0 +1,14 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing Microsoft credentials and settings. + */ +data class MicrosoftConnectorSettings( + /** + * The Microsoft tenant ID + */ + @Json(name = "tenant") + val tenant: String? = null, +) diff --git a/src/main/kotlin/com/nylas/models/PKCEAuthURL.kt b/src/main/kotlin/com/nylas/models/PKCEAuthURL.kt index 05ffd57b..62ac617b 100644 --- a/src/main/kotlin/com/nylas/models/PKCEAuthURL.kt +++ b/src/main/kotlin/com/nylas/models/PKCEAuthURL.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing the object containing the OAuth 2.0 URL as well as the hashed secret. */ @@ -7,13 +9,16 @@ data class PKCEAuthURL( /** * The URL for hosted authentication */ + @Json(name = "url") val url: String, /** * Server-side challenge used in the OAuth 2.0 flow */ + @Json(name = "secret") val secret: String, /** * SHA-256 hash of the secret */ + @Json(name = "secret_hash") val secretHash: String, ) diff --git a/src/main/kotlin/com/nylas/models/PhoneNumber.kt b/src/main/kotlin/com/nylas/models/PhoneNumber.kt index bd4d8719..845dc7a4 100644 --- a/src/main/kotlin/com/nylas/models/PhoneNumber.kt +++ b/src/main/kotlin/com/nylas/models/PhoneNumber.kt @@ -1,10 +1,14 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representation for phone numbers in a contact. */ data class PhoneNumber( + @Json(name = "number") val number: String? = null, + @Json(name = "type") val type: ContactType? = null, ) { class Builder { diff --git a/src/main/kotlin/com/nylas/models/PhysicalAddress.kt b/src/main/kotlin/com/nylas/models/PhysicalAddress.kt index 3479e47c..891f017b 100644 --- a/src/main/kotlin/com/nylas/models/PhysicalAddress.kt +++ b/src/main/kotlin/com/nylas/models/PhysicalAddress.kt @@ -1,15 +1,24 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representation for a physical address in a contact. */ data class PhysicalAddress( + @Json(name = "format") val format: String? = null, + @Json(name = "street_address") val streetAddress: String? = null, + @Json(name = "city") val city: String? = null, + @Json(name = "postal_code") val postalCode: String? = null, + @Json(name = "state") val state: String? = null, + @Json(name = "country") val country: String? = null, + @Json(name = "type") val type: ContactType? = null, ) { class Builder { diff --git a/src/main/kotlin/com/nylas/models/ScheduledMessage.kt b/src/main/kotlin/com/nylas/models/ScheduledMessage.kt index 0e1d8a3c..a6abf7bc 100644 --- a/src/main/kotlin/com/nylas/models/ScheduledMessage.kt +++ b/src/main/kotlin/com/nylas/models/ScheduledMessage.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing information about a scheduled message. */ @@ -7,13 +9,16 @@ data class ScheduledMessage( /** * The unique identifier for the scheduled message. */ - val scheduleId: Int, + @Json(name = "schedule_id") + val scheduleId: String, /** * The status of the scheduled message. */ + @Json(name = "status") val status: ScheduledMessageStatus, /** * The time the message was sent or failed to send, in epoch time. */ - val closeTime: Int?, + @Json(name = "close_time") + val closeTime: Int? = null, ) diff --git a/src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt b/src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt index c4ebaaca..591da80e 100644 --- a/src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt +++ b/src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a scheduled message status. */ @@ -7,9 +9,11 @@ data class ScheduledMessageStatus( /** * The status code the describes the state of the scheduled message */ + @Json(name = "code") val code: String, /** * A description of the status of the scheduled message */ + @Json(name = "description") val description: String, ) diff --git a/src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt b/src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt index 03d7137c..730e6aa4 100644 --- a/src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt +++ b/src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a list of scheduled messages. */ @@ -7,5 +9,6 @@ data class ScheduledMessagesList( /** * The list of scheduled messages. */ + @Json(name = "schedules") val schedules: List, ) diff --git a/src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt b/src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt index 71ba4bb2..5f02b90a 100644 --- a/src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt +++ b/src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a response after stopping a scheduled message. */ @@ -7,5 +9,6 @@ data class StopScheduledMessageResponse( /** * A message describing the result of the request. */ + @Json(name = "message") val message: String, ) diff --git a/src/main/kotlin/com/nylas/models/UpdateConnectorRequest.kt b/src/main/kotlin/com/nylas/models/UpdateConnectorRequest.kt index 91838b90..12c026b9 100644 --- a/src/main/kotlin/com/nylas/models/UpdateConnectorRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateConnectorRequest.kt @@ -1,19 +1,90 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a Nylas update connector request. */ -data class UpdateConnectorRequest( - /** - * Custom name of the connector - */ - val name: String?, +sealed class UpdateConnectorRequest { /** - * The OAuth provider credentials and settings + * Class representing a Google connector creation request. */ - val settings: Map?, + data class Google( + /** + * The Google OAuth provider credentials and settings + */ + @Json(name = "settings") + val settings: GoogleConnectorSettings? = null, + /** + * The Google OAuth scopes + */ + @Json(name = "scope") + val scope: List? = null, + ) : UpdateConnectorRequest() { + class Builder { + private var settings: GoogleConnectorSettings? = null + private var scope: List? = null + + /** + * Set the Google OAuth provider credentials and settings + * @param settings The Google OAuth provider credentials and settings + * @return The builder + */ + fun settings(settings: GoogleConnectorSettings) = apply { this.settings = settings } + + /** + * Set the Google OAuth scopes + * @param scope The Google OAuth scopes + * @return The builder + */ + fun scope(scope: List) = apply { this.scope = scope } + + /** + * Build the Google connector creation request + * @return The Google connector creation request + */ + fun build() = Google(settings, scope) + } + } + /** - * The OAuth scopes + * Class representing a Microsoft connector creation request. */ - val scope: List?, -) + data class Microsoft( + /** + * The Microsoft OAuth provider credentials and settings + */ + @Json(name = "settings") + val settings: MicrosoftConnectorSettings? = null, + /** + * The Microsoft OAuth scopes + */ + @Json(name = "scope") + val scope: List? = null, + ) : UpdateConnectorRequest() { + class Builder { + private var settings: MicrosoftConnectorSettings? = null + private var scope: List? = null + + /** + * Set the Microsoft OAuth provider credentials and settings + * @param settings The Microsoft OAuth provider credentials and settings + * @return The builder + */ + fun settings(settings: MicrosoftConnectorSettings) = apply { this.settings = settings } + + /** + * Set the Microsoft OAuth scopes + * @param scope The Microsoft OAuth scopes + * @return The builder + */ + fun scope(scope: List) = apply { this.scope = scope } + + /** + * Build the Microsoft connector creation request + * @return The Microsoft connector creation request + */ + fun build() = Microsoft(settings, scope) + } + } +} diff --git a/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt b/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt index 8ea98948..1866f967 100644 --- a/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateCredentialRequest.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a request to update a credential */ @@ -7,11 +9,13 @@ data class UpdateCredentialRequest( /** * Unique name of this credential */ - val name: String?, + @Json(name = "name") + val name: String? = null, /** * Data that specifies some special data required for this credential */ - val credentialData: CredentialData?, + @Json(name = "credential_data") + val credentialData: CredentialData? = null, ) { /** * Builder for [UpdateCredentialRequest] diff --git a/src/main/kotlin/com/nylas/models/UpdateFolderRequest.kt b/src/main/kotlin/com/nylas/models/UpdateFolderRequest.kt index 29fe7bc1..dec9f246 100644 --- a/src/main/kotlin/com/nylas/models/UpdateFolderRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateFolderRequest.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representation of the Nylas folder update request. */ @@ -7,18 +9,22 @@ data class UpdateFolderRequest( /** * The name of the folder. */ + @Json(name = "name") val name: String? = null, /** * The parent ID of the folder. (Microsoft only) */ + @Json(name = "parent_id") val parentId: String? = null, /** * The background color of the folder. (Google only) */ + @Json(name = "background_color") val backgroundColor: String? = null, /** * The text color of the folder. (Google only) */ + @Json(name = "text_color") val textColor: String? = null, ) { /** 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]. diff --git a/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt b/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt index 3b72d0af..fbbc16ff 100644 --- a/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateWebhookRequest.kt @@ -24,8 +24,8 @@ 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]. @@ -34,7 +34,7 @@ data class UpdateWebhookRequest( private var triggerTypes: List? = 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. @@ -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, webhookUrl, 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 7371edcb..e267ce2c 100644 --- a/src/main/kotlin/com/nylas/models/Webhook.kt +++ b/src/main/kotlin/com/nylas/models/Webhook.kt @@ -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/WebhookDeleteResponse.kt b/src/main/kotlin/com/nylas/models/WebhookDeleteResponse.kt index 15e330e6..6e7adff3 100644 --- a/src/main/kotlin/com/nylas/models/WebhookDeleteResponse.kt +++ b/src/main/kotlin/com/nylas/models/WebhookDeleteResponse.kt @@ -1,5 +1,7 @@ package com.nylas.models +import com.squareup.moshi.Json + /** * Class representing a Nylas webhook delete response. */ @@ -7,10 +9,12 @@ data class WebhookDeleteResponse( /** * ID of the request. */ + @Json(name = "request_id") val requestId: String, /** * Object containing the webhook deletion status. */ + @Json(name = "data") val data: DataWebhookDeleteData? = null, ) { /** @@ -20,6 +24,7 @@ data class WebhookDeleteResponse( /** * The status of the webhook deletion. */ - val status: Boolean, + @Json(name = "status") + val status: String, ) } diff --git a/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt b/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt index a94402cb..998d547b 100644 --- a/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt +++ b/src/main/kotlin/com/nylas/models/WebhookWithSecret.kt @@ -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, ) diff --git a/src/main/kotlin/com/nylas/resources/Connectors.kt b/src/main/kotlin/com/nylas/resources/Connectors.kt index b9be492d..b07ac823 100644 --- a/src/main/kotlin/com/nylas/resources/Connectors.kt +++ b/src/main/kotlin/com/nylas/resources/Connectors.kt @@ -64,7 +64,7 @@ class Connectors(client: NylasClient) : Resource(client, Connector::c .adapter(UpdateConnectorRequest::class.java) .toJson(requestBody) - return updateResource(path, serializedRequestBody) + return patchResource(path, serializedRequestBody) } /** diff --git a/src/main/kotlin/com/nylas/resources/Contacts.kt b/src/main/kotlin/com/nylas/resources/Contacts.kt index e5c7870f..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) } /** @@ -83,7 +83,7 @@ class Contacts(client: NylasClient) : Resource(client, Contact::class.j @Throws(NylasApiError::class, NylasSdkTimeoutError::class) @JvmOverloads fun listGroups(identifier: String, queryParams: ListContactGroupsQueryParams? = null): ListResponse { - val path = String.format("v3/grants/%s/messages/contacts/groups", identifier) + val path = String.format("v3/grants/%s/contacts/groups", identifier) val responseType = Types.newParameterizedType(ListResponse::class.java, ContactGroup::class.java) return client.executeGet(path, responseType, queryParams) } diff --git a/src/main/kotlin/com/nylas/resources/Credentials.kt b/src/main/kotlin/com/nylas/resources/Credentials.kt index a19c346b..e4d2328c 100644 --- a/src/main/kotlin/com/nylas/resources/Credentials.kt +++ b/src/main/kotlin/com/nylas/resources/Credentials.kt @@ -60,7 +60,7 @@ class Credentials(client: NylasClient) : Resource(client, Credential .adapter(UpdateCredentialRequest::class.java) .toJson(requestBody) - return updateResource(path, serializedRequestBody) + return patchResource(path, serializedRequestBody) } /** 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) } /** diff --git a/src/main/kotlin/com/nylas/resources/SmartCompose.kt b/src/main/kotlin/com/nylas/resources/SmartCompose.kt index 72787c37..036c35bf 100644 --- a/src/main/kotlin/com/nylas/resources/SmartCompose.kt +++ b/src/main/kotlin/com/nylas/resources/SmartCompose.kt @@ -5,6 +5,7 @@ import com.nylas.models.ComposeMessageRequest import com.nylas.models.ComposeMessageResponse 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 +27,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 +45,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) } } diff --git a/src/main/kotlin/com/nylas/util/CreateConnectorAdapter.kt b/src/main/kotlin/com/nylas/util/CreateConnectorAdapter.kt deleted file mode 100644 index 9c59ac07..00000000 --- a/src/main/kotlin/com/nylas/util/CreateConnectorAdapter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.nylas.util - -import com.nylas.models.* -import com.squareup.moshi.* - -/** - * This class is used to serialize and deserialize the CreateConnectorRequest object. - * @suppress Not for public use. - */ -class CreateConnectorAdapter { - @FromJson - @Throws(UnsupportedOperationException::class) - fun fromJson(reader: JsonReader): CreateEventRequest.Conferencing? { - throw UnsupportedOperationException("CreateConnectorRequest is only used for serialization") - } - - @ToJson - fun toJson( - writer: JsonWriter, - value: CreateConnectorRequest?, - delegateGoogle: JsonAdapter, - delegateMicrosoft: JsonAdapter, - delegateImap: JsonAdapter, - delegateVirtualCalendar: JsonAdapter, - ) { - when (value) { - is CreateConnectorRequest.Google -> delegateGoogle.toJson(writer, value) - is CreateConnectorRequest.Microsoft -> delegateMicrosoft.toJson(writer, value) - is CreateConnectorRequest.Imap -> delegateImap.toJson(writer, value) - is CreateConnectorRequest.VirtualCalendar -> delegateVirtualCalendar.toJson(writer, value) - else -> writer.nullValue() - } - } -} diff --git a/src/main/kotlin/com/nylas/util/JsonHelper.kt b/src/main/kotlin/com/nylas/util/JsonHelper.kt index d2f9aadb..4f3646b5 100644 --- a/src/main/kotlin/com/nylas/util/JsonHelper.kt +++ b/src/main/kotlin/com/nylas/util/JsonHelper.kt @@ -1,5 +1,8 @@ package com.nylas.util +import com.nylas.models.Connector.Companion.CONNECTOR_JSON_ADAPTER_FACTORY +import com.nylas.models.CreateConnectorRequest.Companion.CREATE_CONNECTOR_JSON_ADAPTER_FACTORY +import com.nylas.models.CreateCredentialRequest.Companion.CREATE_CREDENTIAL_JSON_ADAPTER_FACTORY import com.nylas.models.GetFreeBusyResponse.Companion.FREE_BUSY_JSON_FACTORY import com.nylas.models.When.Companion.WHEN_JSON_FACTORY import com.squareup.moshi.JsonAdapter @@ -30,7 +33,7 @@ class JsonHelper { .add(UpdateWhenAdapter()) .add(CreateWhenAdapter()) .add(IMessageAdapter()) - .add(CreateConnectorAdapter()) + .add(UpdateConnectorAdapter()) .add(CredentialDataAdapter()) .add(MicrosoftAdminConsentCredentialDataAdapter()) .add(GoogleServiceAccountCredentialDataAdapter()) @@ -38,6 +41,9 @@ class JsonHelper { // Polymorphic adapters .add(WHEN_JSON_FACTORY) .add(FREE_BUSY_JSON_FACTORY) + .add(CONNECTOR_JSON_ADAPTER_FACTORY) + .add(CREATE_CONNECTOR_JSON_ADAPTER_FACTORY) + .add(CREATE_CREDENTIAL_JSON_ADAPTER_FACTORY) .add(KotlinJsonAdapterFactory()) .build() diff --git a/src/main/kotlin/com/nylas/util/UpdateConnectorAdapter.kt b/src/main/kotlin/com/nylas/util/UpdateConnectorAdapter.kt new file mode 100644 index 00000000..7c967899 --- /dev/null +++ b/src/main/kotlin/com/nylas/util/UpdateConnectorAdapter.kt @@ -0,0 +1,30 @@ +package com.nylas.util + +import com.nylas.models.* +import com.squareup.moshi.* + +/** + * This class is used to serialize and deserialize the UpdateConnectorRequest object. + * @suppress Not for public use. + */ +class UpdateConnectorAdapter { + @FromJson + @Throws(UnsupportedOperationException::class) + fun fromJson(reader: JsonReader): UpdateConnectorRequest? { + throw UnsupportedOperationException("UpdateConnectorRequest is only used for serialization") + } + + @ToJson + fun toJson( + writer: JsonWriter, + value: UpdateConnectorRequest?, + delegateGoogle: JsonAdapter, + delegateMicrosoft: JsonAdapter, + ) { + when (value) { + is UpdateConnectorRequest.Google -> delegateGoogle.toJson(writer, value) + is UpdateConnectorRequest.Microsoft -> delegateMicrosoft.toJson(writer, value) + else -> writer.nullValue() + } + } +} diff --git a/src/test/kotlin/com/nylas/resources/ApplicationsTests.kt b/src/test/kotlin/com/nylas/resources/ApplicationsTests.kt new file mode 100644 index 00000000..15202463 --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/ApplicationsTests.kt @@ -0,0 +1,167 @@ +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 + +class ApplicationsTests { + 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 `ApplicationDetails serializes properly`() { + val adapter = JsonHelper.moshi().adapter(ApplicationDetails::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "application_id": "ad410018-d306-43f9-8361-fa5d7b2172e0", + "organization_id": "f5db4482-dbbe-4b32-b347-61c260d803ce", + "region": "us", + "environment": "production", + "branding": { + "name": "My application", + "icon_url": "https://my-app.com/my-icon.png", + "website_url": "https://my-app.com", + "description": "Online banking application." + }, + "hosted_authentication": { + "background_image_url": "https://my-app.com/bg.jpg", + "alignment": "left", + "color_primary": "#dc0000", + "color_secondary": "#000056", + "title": "string", + "subtitle": "string", + "background_color": "#003400", + "spacing": 5 + }, + "callback_uris": [ + { + "id": "0556d035-6cb6-4262-a035-6b77e11cf8fc", + "url": "string", + "platform": "web", + "settings": { + "origin": "string", + "bundle_id": "string", + "package_name": "string", + "sha1_certificate_fingerprint": "string" + } + } + ] + } + """.trimIndent(), + ) + + val app = adapter.fromJson(jsonBuffer)!! + assertIs(app) + assertEquals("ad410018-d306-43f9-8361-fa5d7b2172e0", app.applicationId) + assertEquals("f5db4482-dbbe-4b32-b347-61c260d803ce", app.organizationId) + assertEquals(Region.US, app.region) + assertEquals(Environment.PRODUCTION, app.environment) + assertIs(app.branding) + assertEquals("My application", app.branding.name) + assertEquals("https://my-app.com/my-icon.png", app.branding.iconUrl) + assertEquals("https://my-app.com", app.branding.websiteUrl) + assertEquals("Online banking application.", app.branding.description) + assertIs(app.hostedAuthentication) + assertEquals("https://my-app.com/bg.jpg", app.hostedAuthentication?.backgroundImageUrl) + assertEquals("left", app.hostedAuthentication?.alignment) + assertEquals("#dc0000", app.hostedAuthentication?.colorPrimary) + assertEquals("#000056", app.hostedAuthentication?.colorSecondary) + assertEquals("string", app.hostedAuthentication?.title) + assertEquals("string", app.hostedAuthentication?.subtitle) + assertEquals("#003400", app.hostedAuthentication?.backgroundColor) + assertEquals(5, app.hostedAuthentication?.spacing) + assertEquals(1, app.callbackUris?.size) + assertIs(app.callbackUris?.get(0)) + assertEquals("0556d035-6cb6-4262-a035-6b77e11cf8fc", app.callbackUris?.get(0)?.id) + assertEquals("string", app.callbackUris?.get(0)?.url) + assertEquals(Platform.WEB, app.callbackUris?.get(0)?.platform) + assertIs(app.callbackUris?.get(0)?.settings) + assertEquals("string", app.callbackUris?.get(0)?.settings?.origin) + assertEquals("string", app.callbackUris?.get(0)?.settings?.bundleId) + assertEquals("string", app.callbackUris?.get(0)?.settings?.packageName) + assertEquals("string", app.callbackUris?.get(0)?.settings?.sha1CertificateFingerprint) + } + } + + @Nested + inner class GetDetailsTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var mockApplications: Applications + + @BeforeEach + fun setup() { + grantId = "grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + mockApplications = Applications(mockNylasClient) + } + + @Test + fun `getting application details calls requests with the correct params`() { + mockApplications.getDetails() + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/applications", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, ApplicationDetails::class.java), typeCaptor.firstValue) + } + } + + @Nested + inner class ResourcesTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var mockApplications: Applications + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + mockApplications = Applications(mockNylasClient) + } + + @Test + fun `redirectUris returns a RedirectUris instance`() { + val redirectUris = mockApplications.redirectUris() + assertIs(redirectUris) + } + } +} diff --git a/src/test/kotlin/com/nylas/resources/AttachmentsTests.kt b/src/test/kotlin/com/nylas/resources/AttachmentsTests.kt new file mode 100644 index 00000000..98d28222 --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/AttachmentsTests.kt @@ -0,0 +1,140 @@ +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 + +class AttachmentsTests { + 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 `Attachment serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Attachment::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "content_type": "image/png", + "filename": "pic.png", + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "id": "185e56cb50e12e82", + "is_inline": true, + "size": 13068, + "content_id": "" + } + """.trimIndent(), + ) + + val attachment = adapter.fromJson(jsonBuffer)!! + assertEquals("image/png", attachment.contentType) + assertEquals("pic.png", attachment.filename) + assertEquals("41009df5-bf11-4c97-aa18-b285b5f2e386", attachment.grantId) + assertEquals("185e56cb50e12e82", attachment.id) + assertEquals(true, attachment.isInline) + assertEquals(13068, attachment.size) + assertEquals("", attachment.contentId) + } + } + + @Nested + inner class RequestTests { + private lateinit var grantId: String + private lateinit var attachmentId: String + private lateinit var queryParams: FindAttachmentQueryParams + private lateinit var mockNylasClient: NylasClient + private lateinit var attachments: Attachments + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + queryParams = FindAttachmentQueryParams( + messageId = "message-id", + ) + attachmentId = "attachment-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + attachments = Attachments(mockNylasClient) + } + + @Test + fun `find makes a GET request to the correct path`() { + attachments.find(grantId, attachmentId, queryParams) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/attachments/$attachmentId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Attachment::class.java), typeCaptor.firstValue) + } + + @Test + fun `download makes a GET request to the correct path`() { + attachments.download(grantId, attachmentId, queryParams) + + val pathCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).downloadResponse( + pathCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/attachments/$attachmentId/download", pathCaptor.firstValue) + } + + @Test + fun `downloadBytes makes a GET request to the correct path`() { + val byteArray = byteArrayOf(0b00000100, 0b00000010, 0b00000011) + whenever(mockResponseBody.bytes()).thenReturn(byteArray) + whenever(mockNylasClient.downloadResponse(any(), any())).thenReturn(mockResponseBody) + + val bytes = attachments.downloadBytes(grantId, attachmentId, queryParams) + + val pathCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).downloadResponse( + pathCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/attachments/$attachmentId/download", pathCaptor.firstValue) + assertEquals(byteArray, bytes) + } + } +} diff --git a/src/test/kotlin/com/nylas/resources/AuthTests.kt b/src/test/kotlin/com/nylas/resources/AuthTests.kt index 059a1083..f1774f1f 100644 --- a/src/test/kotlin/com/nylas/resources/AuthTests.kt +++ b/src/test/kotlin/com/nylas/resources/AuthTests.kt @@ -250,7 +250,7 @@ class AuthTests { provider = AuthProvider.GOOGLE, settings = mapOf("email" to "test@nylas.com"), state = "abc-123-state", - scopes = listOf("email.read_only", "calendar", "contacts"), + scope = listOf("email.read_only", "calendar", "contacts"), ) auth.customAuthentication(request) diff --git a/src/test/kotlin/com/nylas/resources/ConnectorsTests.kt b/src/test/kotlin/com/nylas/resources/ConnectorsTests.kt new file mode 100644 index 00000000..5a9e1b5d --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/ConnectorsTests.kt @@ -0,0 +1,215 @@ +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 + +class ConnectorsTests { + 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 `Connector serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Connector::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "provider": "google", + "settings": { + "topic_name": "abc123" + }, + "scope": [ + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile" + ] + } + """.trimIndent(), + ) + + val connector = adapter.fromJson(jsonBuffer)!! + assertIs(connector) + assertEquals(AuthProvider.GOOGLE, connector.provider) + assertEquals("abc123", connector.settings.topicName) + assertEquals( + listOf( + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + ), + connector.scope, + ) + } + } + + @Nested + inner class CrudTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var connectors: Connectors + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + connectors = Connectors(mockNylasClient) + } + + @Test + fun `listing connectors calls requests with the correct params`() { + connectors.list() + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/connectors", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, Connector::class.java), typeCaptor.firstValue) + } + + @Test + fun `finding a connector calls requests with the correct params`() { + connectors.find(AuthProvider.GOOGLE) + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/connectors/google", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Connector::class.java), typeCaptor.firstValue) + } + + @Test + fun `creating a connector calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(CreateConnectorRequest::class.java) + val createConnectorRequest = CreateConnectorRequest.Google( + settings = GoogleCreateConnectorSettings( + clientId = "CLIENT_ID", + clientSecret = "CLIENT_SECRET", + topicName = "abc123", + ), + scope = listOf( + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + ), + ) + + connectors.create(createConnectorRequest) + 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/connectors", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Connector::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(createConnectorRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `updating a connector calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(UpdateConnectorRequest::class.java) + val updateConnectorRequest = UpdateConnectorRequest.Google( + settings = GoogleConnectorSettings( + topicName = "abc123", + ), + scope = listOf( + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + ), + ) + + connectors.update(AuthProvider.GOOGLE, updateConnectorRequest) + 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/connectors/google", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Connector::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateConnectorRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `destroying a connector calls requests with the correct params`() { + connectors.destroy(AuthProvider.IMAP) + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/connectors/imap", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + } + } + + @Nested + inner class ResourcesTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var connectors: Connectors + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + connectors = Connectors(mockNylasClient) + } + + @Test + fun `credentials returns a Credentials instance`() { + val credentials = connectors.credentials() + assertIs(credentials) + } + } +} diff --git a/src/test/kotlin/com/nylas/resources/ContactsTests.kt b/src/test/kotlin/com/nylas/resources/ContactsTests.kt new file mode 100644 index 00000000..54460c67 --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/ContactsTests.kt @@ -0,0 +1,501 @@ +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 ContactsTests { + 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 `Contact serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Contact::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "birthday": "1960-12-31", + "company_name": "Nylas", + "emails": [ + { + "type": "work", + "email": "john-work@example.com" + } + ], + "given_name": "John", + "grant_id": "41009df5-bf11-4c97-aa18-b285b5f2e386", + "groups": [ + { + "id": "starred" + } + ], + "id": "5d3qmne77v32r8l4phyuksl2x", + "im_addresses": [ + { + "type": "other", + "im_address": "myjabberaddress" + } + ], + "job_title": "Software Engineer", + "manager_name": "Bill", + "middle_name": "Jacob", + "nickname": "JD", + "notes": "Loves ramen", + "object": "contact", + "office_location": "123 Main Street", + "phone_numbers": [ + { + "type": "work", + "number": "+1-555-555-5555" + } + ], + "physical_addresses": [ + { + "type": "work", + "street_address": "123 Main Street", + "postal_code": 94107, + "state": "CA", + "country": "US", + "city": "San Francisco" + } + ], + "picture_url": "https://example.com/picture.jpg", + "suffix": "Jr.", + "surname": "Doe", + "web_pages": [ + { + "type": "work", + "url": "http://www.linkedin.com/in/johndoe" + } + ] + } + """.trimIndent(), + ) + + val contact = adapter.fromJson(jsonBuffer)!! + assertIs(contact) + assertEquals("1960-12-31", contact.birthday) + assertEquals("Nylas", contact.companyName) + assertEquals("John", contact.givenName) + assertEquals("41009df5-bf11-4c97-aa18-b285b5f2e386", contact.grantId) + assertEquals("5d3qmne77v32r8l4phyuksl2x", contact.id) + assertEquals("Software Engineer", contact.jobTitle) + assertEquals("Bill", contact.managerName) + assertEquals("Jacob", contact.middleName) + assertEquals("JD", contact.nickname) + assertEquals("Loves ramen", contact.notes) + assertEquals("contact", contact.getObject()) + assertEquals("123 Main Street", contact.officeLocation) + assertEquals("Jr.", contact.suffix) + assertEquals("Doe", contact.surname) + assertEquals("https://example.com/picture.jpg", contact.pictureUrl) + assertEquals( + listOf( + ContactEmail( + type = ContactType.WORK, + email = "john-work@example.com", + ), + ), + contact.emails, + ) + assertEquals( + listOf( + ContactGroupId( + id = "starred", + ), + ), + contact.groups, + ) + assertEquals( + listOf( + InstantMessagingAddress( + type = ContactType.OTHER, + imAddress = "myjabberaddress", + ), + ), + contact.imAddresses, + ) + assertEquals( + listOf( + PhoneNumber( + type = ContactType.WORK, + number = "+1-555-555-5555", + ), + ), + contact.phoneNumbers, + ) + assertEquals( + listOf( + WebPage( + type = ContactType.WORK, + url = "http://www.linkedin.com/in/johndoe", + ), + ), + contact.webPages, + ) + assertEquals( + listOf( + PhysicalAddress( + type = ContactType.WORK, + streetAddress = "123 Main Street", + postalCode = "94107", + state = "CA", + country = "US", + city = "San Francisco", + ), + ), + contact.physicalAddresses, + ) + } + } + + @Nested + inner class CrudTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var contacts: Contacts + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + contacts = Contacts(mockNylasClient) + } + + @Test + fun `listing contacts calls requests with the correct params`() { + val queryParams = ListContactsQueryParams( + limit = 10, + email = "test@gmail.com", + ) + + contacts.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/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 + fun `finding a contact calls requests with the correct params`() { + val contactId = "contact-123" + val queryParams = FindContactQueryParams( + profilePicture = true, + ) + + contacts.find(grantId, contactId, queryParams) + + 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) + 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 + fun `creating a contact calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(CreateContactRequest::class.java) + val createContactRequest = CreateContactRequest( + birthday = "1960-12-31", + companyName = "Nylas", + emails = listOf( + ContactEmail( + type = ContactType.WORK, + email = "test@gmail.com", + ), + ), + givenName = "John", + groups = listOf( + ContactGroupId( + id = "starred", + ), + ), + imAddresses = listOf( + InstantMessagingAddress( + type = ContactType.OTHER, + imAddress = "myjabberaddress", + ), + ), + jobTitle = "Software Engineer", + managerName = "Bill", + middleName = "Jacob", + nickname = "JD", + notes = "Loves ramen", + officeLocation = "123 Main Street", + phoneNumbers = listOf( + PhoneNumber( + type = ContactType.WORK, + number = "+1-555-555-5555", + ), + ), + physicalAddresses = listOf( + PhysicalAddress( + type = ContactType.WORK, + streetAddress = "123 Main Street", + postalCode = "94107", + state = "CA", + country = "US", + city = "San Francisco", + ), + ), + suffix = "Jr.", + surname = "Doe", + webPages = listOf( + WebPage( + type = ContactType.WORK, + url = "http://www.linkedin.com/in/johndoe", + ), + ), + ) + + contacts.create(grantId, createContactRequest) + 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/contacts", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Contact::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(createContactRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `updating a contact calls requests with the correct params`() { + val contactId = "contact-123" + val adapter = JsonHelper.moshi().adapter(UpdateContactRequest::class.java) + val updateContactRequest = UpdateContactRequest( + birthday = "1960-12-31", + companyName = "Nylas", + emails = listOf( + ContactEmail( + type = ContactType.WORK, + email = "test@gmail.com", + ), + ), + givenName = "John", + groups = listOf( + ContactGroupId( + id = "starred", + ), + ), + imAddresses = listOf( + InstantMessagingAddress( + type = ContactType.OTHER, + imAddress = "myjabberaddress", + ), + ), + jobTitle = "Software Engineer", + managerName = "Bill", + middleName = "Jacob", + nickname = "JD", + notes = "Loves ramen", + officeLocation = "123 Main Street", + phoneNumbers = listOf( + PhoneNumber( + type = ContactType.WORK, + number = "+1-555-555-5555", + ), + ), + physicalAddresses = listOf( + PhysicalAddress( + type = ContactType.WORK, + streetAddress = "123 Main Street", + postalCode = "94107", + state = "CA", + country = "US", + city = "San Francisco", + ), + ), + suffix = "Jr.", + surname = "Doe", + webPages = listOf( + WebPage( + type = ContactType.WORK, + url = "http://www.linkedin.com/in/johndoe", + ), + ), + ) + + contacts.update(grantId, contactId, updateContactRequest) + 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/contacts/$contactId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Contact::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateContactRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `destroying a contact calls requests with the correct params`() { + val contactId = "contact-123" + + contacts.destroy(grantId, contactId) + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/grants/$grantId/contacts/$contactId", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + } + } + + @Nested + inner class OtherMethodTests { + private lateinit var grantId: String + private lateinit var mockNylasClient: NylasClient + private lateinit var contacts: Contacts + + @BeforeEach + fun setup() { + grantId = "abc-123-grant-id" + mockNylasClient = Mockito.mock(NylasClient::class.java) + contacts = Contacts(mockNylasClient) + } + + @Test + fun `getting contact groups calls requests with the correct params`() { + val grantId = "abc-123-grant-id" + val queryParams = ListContactGroupsQueryParams( + limit = 10, + ) + + contacts.listGroups(grantId, queryParams) + + 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) + 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) + } + } +} diff --git a/src/test/kotlin/com/nylas/resources/CredentialsTests.kt b/src/test/kotlin/com/nylas/resources/CredentialsTests.kt new file mode 100644 index 00000000..9dd255d8 --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/CredentialsTests.kt @@ -0,0 +1,216 @@ +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.assertNull + +class CredentialsTests { + 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 `Credential serializes properly`() { + val adapter = JsonHelper.moshi().adapter(Credential::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "id": "e19f8e1a-eb1c-41c0-b6a6-d2e59daf7f47", + "name": "My first Google credential", + "created_at": 1617817109, + "updated_at": 1617817109 + } + """.trimIndent(), + ) + + val credential = adapter.fromJson(jsonBuffer)!! + assertEquals("e19f8e1a-eb1c-41c0-b6a6-d2e59daf7f47", credential.id) + assertEquals("My first Google credential", credential.name) + assertEquals(1617817109L, credential.createdAt) + assertEquals(1617817109L, credential.updatedAt) + } + } + + @Nested + inner class CrudTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var credentials: Credentials + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + credentials = Credentials(mockNylasClient) + } + + @Test + fun `listing credentials calls requests with the correct params`() { + val queryParams = ListCredentialsQueryParams( + limit = 10, + offset = 10, + ) + + credentials.list(AuthProvider.GOOGLE, queryParams) + + 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) + 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 + fun `finding a credential calls requests with the correct params`() { + val credentialId = "abc-123" + + credentials.find(AuthProvider.GOOGLE, credentialId) + + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/connectors/google/creds/abc-123", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Credential::class.java), typeCaptor.firstValue) + } + + @Test + fun `creating a credential calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(CreateCredentialRequest::class.java) + val createCredentialRequest = CreateCredentialRequest.Google( + name = "My first Google credential", + credentialData = CredentialData.GoogleServiceAccount( + privateKeyId = "abc", + privateKey = "123", + clientEmail = "abc@gmail.com", + ), + ) + + credentials.create(AuthProvider.GOOGLE, createCredentialRequest) + + 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/connectors/google/creds", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Credential::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(createCredentialRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `updating a credential calls requests with the correct params`() { + val credentialId = "abc-123" + val adapter = JsonHelper.moshi().adapter(UpdateCredentialRequest::class.java) + val updateCredentialRequest = UpdateCredentialRequest( + name = "My first Google credential", + credentialData = CredentialData.GoogleServiceAccount( + privateKeyId = "abc", + privateKey = "123", + clientEmail = "", + ), + ) + + credentials.update(AuthProvider.GOOGLE, credentialId, updateCredentialRequest) + + 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/connectors/google/creds/abc-123", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, Credential::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateCredentialRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `destroying a credential calls requests with the correct params`() { + val credentialId = "abc-123" + + credentials.destroy(AuthProvider.IMAP, credentialId) + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/connectors/imap/creds/abc-123", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + } + } +} 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) + } + } +} 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) + } + } +} 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..9819488f --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/GrantsTests.kt @@ -0,0 +1,210 @@ +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)!! + assertIs(grant) + 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) + } + } +} 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..d7f15ad8 --- /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) + } + } +} diff --git a/src/test/kotlin/com/nylas/resources/RedirectUriTests.kt b/src/test/kotlin/com/nylas/resources/RedirectUriTests.kt new file mode 100644 index 00000000..00375f8b --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/RedirectUriTests.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 + +class RedirectUriTests { + 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 `RedirectUri serializes properly`() { + val adapter = JsonHelper.moshi().adapter(RedirectUri::class.java) + val jsonBuffer = Buffer().writeUtf8( + """ + { + "id": "0556d035-6cb6-4262-a035-6b77e11cf8fc", + "url": "http://localhost/abc", + "platform": "web", + "settings": { + "origin": "string", + "bundle_id": "string", + "app_store_id": "string", + "team_id": "string", + "package_name": "string", + "sha1_certificate_fingerprint": "string" + } + } + """.trimIndent(), + ) + + val redirectUri = adapter.fromJson(jsonBuffer)!! + assertEquals("0556d035-6cb6-4262-a035-6b77e11cf8fc", redirectUri.id) + assertEquals("http://localhost/abc", redirectUri.url) + assertEquals(Platform.WEB, redirectUri.platform) + assertIs(redirectUri.settings) + assertEquals("string", redirectUri.settings?.origin) + assertEquals("string", redirectUri.settings?.bundleId) + assertEquals("string", redirectUri.settings?.appStoreId) + assertEquals("string", redirectUri.settings?.teamId) + assertEquals("string", redirectUri.settings?.packageName) + assertEquals("string", redirectUri.settings?.sha1CertificateFingerprint) + } + } + + @Nested + inner class CrudTests { + private lateinit var mockNylasClient: NylasClient + private lateinit var redirectUris: RedirectUris + + @BeforeEach + fun setup() { + mockNylasClient = Mockito.mock(NylasClient::class.java) + redirectUris = RedirectUris(mockNylasClient) + } + + @Test + fun `listing redirectUris calls requests with the correct params`() { + redirectUris.list() + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/applications/redirect-uris", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(ListResponse::class.java, RedirectUri::class.java), typeCaptor.firstValue) + } + + @Test + fun `finding a redirectUri calls requests with the correct params`() { + val redirectUriId = "redirectUri-123" + + redirectUris.find(redirectUriId) + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeGet>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/applications/redirect-uris/$redirectUriId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, RedirectUri::class.java), typeCaptor.firstValue) + } + + @Test + fun `creating a redirectUri calls requests with the correct params`() { + val adapter = JsonHelper.moshi().adapter(CreateRedirectUriRequest::class.java) + val createRedirectUriRequest = CreateRedirectUriRequest( + url = "http://localhost/abc", + platform = Platform.WEB, + settings = RedirectUriSettings( + origin = "string", + bundleId = "string", + appStoreId = "string", + teamId = "string", + packageName = "string", + sha1CertificateFingerprint = "string", + ), + ) + + redirectUris.create(createRedirectUriRequest) + 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/applications/redirect-uris", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, RedirectUri::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(createRedirectUriRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `updating a redirectUri calls requests with the correct params`() { + val redirectUriId = "redirectUri-123" + val adapter = JsonHelper.moshi().adapter(UpdateRedirectUriRequest::class.java) + val updateRedirectUriRequest = UpdateRedirectUriRequest( + url = "http://localhost/abc", + platform = Platform.WEB, + settings = RedirectUriSettings( + origin = "string", + bundleId = "string", + appStoreId = "string", + teamId = "string", + packageName = "string", + sha1CertificateFingerprint = "string", + ), + ) + + redirectUris.update(redirectUriId, updateRedirectUriRequest) + 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/applications/redirect-uris/$redirectUriId", pathCaptor.firstValue) + assertEquals(Types.newParameterizedType(Response::class.java, RedirectUri::class.java), typeCaptor.firstValue) + assertEquals(adapter.toJson(updateRedirectUriRequest), requestBodyCaptor.firstValue) + } + + @Test + fun `destroying a redirectUri calls requests with the correct params`() { + val redirectUriId = "redirectUri-123" + + redirectUris.destroy(redirectUriId) + val pathCaptor = argumentCaptor() + val typeCaptor = argumentCaptor() + val queryParamCaptor = argumentCaptor() + verify(mockNylasClient).executeDelete>( + pathCaptor.capture(), + typeCaptor.capture(), + queryParamCaptor.capture(), + ) + + assertEquals("v3/applications/redirect-uris/$redirectUriId", pathCaptor.firstValue) + assertEquals(DeleteResponse::class.java, typeCaptor.firstValue) + } + } +} 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..980021b7 --- /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) + } + } +} 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) + } + } +} 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..ed45decc --- /dev/null +++ b/src/test/kotlin/com/nylas/resources/WebhooksTests.kt @@ -0,0 +1,277 @@ +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) + } + + @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) + } + } + } +}