From d5532efac0eac93e52eb5976fa36f53e3978fe6c Mon Sep 17 00:00:00 2001 From: Mostafa Rashed <17770919+mrashed-dev@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:16:35 -0500 Subject: [PATCH] Add Messages, Drafts, and Smart Compose APIs support (#166) # Description This PR adds support for messages, drafts, and smart compose APIs. # License I confirm that this contribution is made under the terms of the MIT license and that I have the authority necessary to make this contribution on behalf of its copyright owner. --- CHANGELOG.md | 1 + src/main/kotlin/com/nylas/NylasClient.kt | 45 +++ .../kotlin/com/nylas/models/Attachment.kt | 49 ++++ .../com/nylas/models/ComposeMessageRequest.kt | 11 + .../nylas/models/ComposeMessageResponse.kt | 11 + .../nylas/models/CreateAttachmentRequest.kt | 97 +++++++ .../nylas/models/CreateCredentialRequest.kt | 6 +- .../com/nylas/models/CreateDraftRequest.kt | 224 +++++++++++++++ src/main/kotlin/com/nylas/models/Draft.kt | 109 ++++++++ .../nylas/models/FindMessageQueryParams.kt | 11 + src/main/kotlin/com/nylas/models/IMessage.kt | 3 + .../nylas/models/IMessageAttachmentRequest.kt | 11 + .../com/nylas/models/ListDraftsQueryParams.kt | 180 ++++++++++++ .../nylas/models/ListMessagesQueryParams.kt | 263 ++++++++++++++++++ .../nylas/models/ListThreadsQueryParams.kt | 251 +++++++++++++++++ src/main/kotlin/com/nylas/models/Message.kt | 120 ++++++++ .../kotlin/com/nylas/models/MessageFields.kt | 11 + .../kotlin/com/nylas/models/MessageHeaders.kt | 15 + .../com/nylas/models/ScheduledMessage.kt | 19 ++ .../nylas/models/ScheduledMessageStatus.kt | 15 + .../com/nylas/models/ScheduledMessagesList.kt | 11 + .../com/nylas/models/SendMessageRequest.kt | 218 +++++++++++++++ .../models/StopScheduledMessageResponse.kt | 11 + src/main/kotlin/com/nylas/models/Thread.kt | 98 +++++++ .../com/nylas/models/TrackingOptions.kt | 23 ++ .../com/nylas/models/UpdateDraftRequest.kt | 174 ++++++++++++ .../com/nylas/models/UpdateMessageRequest.kt | 78 ++++++ .../com/nylas/models/UpdateThreadRequest.kt | 65 +++++ src/main/kotlin/com/nylas/resources/Drafts.kt | 86 ++++++ .../kotlin/com/nylas/resources/Messages.kt | 126 +++++++++ .../com/nylas/resources/SmartCompose.kt | 49 ++++ .../kotlin/com/nylas/resources/Threads.kt | 59 ++++ src/main/kotlin/com/nylas/util/FileUtils.kt | 88 ++++++ .../kotlin/com/nylas/util/IMessageAdapter.kt | 39 +++ src/main/kotlin/com/nylas/util/JsonHelper.kt | 2 + 35 files changed, 2576 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/com/nylas/models/Attachment.kt create mode 100644 src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt create mode 100644 src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt create mode 100644 src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt create mode 100644 src/main/kotlin/com/nylas/models/CreateDraftRequest.kt create mode 100644 src/main/kotlin/com/nylas/models/Draft.kt create mode 100644 src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt create mode 100644 src/main/kotlin/com/nylas/models/IMessage.kt create mode 100644 src/main/kotlin/com/nylas/models/IMessageAttachmentRequest.kt create mode 100644 src/main/kotlin/com/nylas/models/ListDraftsQueryParams.kt create mode 100644 src/main/kotlin/com/nylas/models/ListMessagesQueryParams.kt create mode 100644 src/main/kotlin/com/nylas/models/ListThreadsQueryParams.kt create mode 100644 src/main/kotlin/com/nylas/models/Message.kt create mode 100644 src/main/kotlin/com/nylas/models/MessageFields.kt create mode 100644 src/main/kotlin/com/nylas/models/MessageHeaders.kt create mode 100644 src/main/kotlin/com/nylas/models/ScheduledMessage.kt create mode 100644 src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt create mode 100644 src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt create mode 100644 src/main/kotlin/com/nylas/models/SendMessageRequest.kt create mode 100644 src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt create mode 100644 src/main/kotlin/com/nylas/models/Thread.kt create mode 100644 src/main/kotlin/com/nylas/models/TrackingOptions.kt create mode 100644 src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt create mode 100644 src/main/kotlin/com/nylas/models/UpdateMessageRequest.kt create mode 100644 src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt create mode 100644 src/main/kotlin/com/nylas/resources/Drafts.kt create mode 100644 src/main/kotlin/com/nylas/resources/Messages.kt create mode 100644 src/main/kotlin/com/nylas/resources/SmartCompose.kt create mode 100644 src/main/kotlin/com/nylas/resources/Threads.kt create mode 100644 src/main/kotlin/com/nylas/util/FileUtils.kt create mode 100644 src/main/kotlin/com/nylas/util/IMessageAdapter.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c4a6e5..ae615843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added * Added support for the free-busy endpoint +* Added Messages, Drafts, and Smart Compose APIs support * Added support for custom authentication, connectors, and credentials APIs ### Changed diff --git a/src/main/kotlin/com/nylas/NylasClient.kt b/src/main/kotlin/com/nylas/NylasClient.kt index 60e87920..af503730 100644 --- a/src/main/kotlin/com/nylas/NylasClient.kt +++ b/src/main/kotlin/com/nylas/NylasClient.kt @@ -103,6 +103,14 @@ class NylasClient( return Connectors(this) } + /** + * Access the Drafts API + * @return The Drafts API + */ + fun drafts(): Drafts { + return Drafts(this) + } + /** * Access the Events API * @return The Events API @@ -111,6 +119,22 @@ class NylasClient( return Events(this) } + /** + * Access the Messages API + * @return The Messages API + */ + fun messages(): Messages { + return Messages(this) + } + + /** + * Access the Threads API + * @return The Threads API + */ + fun threads(): Threads { + return Threads(this) + } + /** * Access the Webhooks API * @return The Webhooks API @@ -223,6 +247,27 @@ class NylasClient( return executeRequest(url, HttpMethod.DELETE, null, resultType) } + /** + * Execute a request with a form-body payload to the Nylas API. + * @param path The path to request. + * @param method The HTTP method to use. + * @param requestBody The form-data request body. + * @param resultType The type of the response body. + * @param queryParams The query parameters. + * @suppress Not for public use. + */ + @Throws(AbstractNylasApiError::class, NylasSdkTimeoutError::class) + fun executeFormRequest( + path: String, + method: HttpMethod, + requestBody: RequestBody, + resultType: Type? = null, + queryParams: IQueryParams? = null, + ): T { + val url = buildUrl(path, queryParams) + return executeRequest(url, method, requestBody, resultType) + } + private fun buildRequest( url: HttpUrl.Builder, method: HttpMethod, diff --git a/src/main/kotlin/com/nylas/models/Attachment.kt b/src/main/kotlin/com/nylas/models/Attachment.kt new file mode 100644 index 00000000..a4966bbf --- /dev/null +++ b/src/main/kotlin/com/nylas/models/Attachment.kt @@ -0,0 +1,49 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representation of a Nylas attachment object + */ +data class Attachment( + /** + * Globally unique object identifier. + */ + @Json(name = "id") + val id: String? = null, + /** + * Nylas grant ID that is now successfully created. + */ + @Json(name = "grant_id") + val grantId: String? = null, + /** + * The size of the attachment in bytes. + */ + @Json(name = "size") + val size: Int? = null, + /** + * The filename of the attachment. + */ + @Json(name = "filename") + val filename: String? = null, + /** + * The content type of the attachment. + */ + @Json(name = "content_type") + val contentType: String? = null, + /** + * If it's an inline attachment. + */ + @Json(name = "is_inline") + val isInline: Boolean? = null, + /** + * The content ID of the attachment. + */ + @Json(name = "content_id") + val contentId: String? = null, + /** + * The content disposition if the attachment is located inline. + */ + @Json(name = "content_disposition") + val contentDisposition: String? = null, +) diff --git a/src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt b/src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt new file mode 100644 index 00000000..625f18f5 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ComposeMessageRequest.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +/** + * Class representing a request to compose a message. + */ +data class ComposeMessageRequest( + /** + * The prompt that smart compose will use to generate a message suggestion. + */ + val prompt: String, +) diff --git a/src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt b/src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt new file mode 100644 index 00000000..e94e7aad --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ComposeMessageResponse.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +/** + * Class representing a response to a message composition request. + */ +data class ComposeMessageResponse( + /** + * The message suggestion generated by smart compose. + */ + val suggestion: String, +) diff --git a/src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt b/src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt new file mode 100644 index 00000000..dd6d570e --- /dev/null +++ b/src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt @@ -0,0 +1,97 @@ +package com.nylas.models + +import com.squareup.moshi.Json +import java.io.InputStream + +/** + * Class representing a Nylas attachment object. + */ +class CreateAttachmentRequest( + /** + * The filename of the attachment. + */ + @Json(name = "filename") + val filename: String, + /** + * The content type of the attachment. + */ + @Json(name = "content_type") + val contentType: String, + /** + * The content of the attachment. + */ + @Transient + val content: InputStream, + /** + * The size of the attachment in bytes. + */ + @Json(name = "size") + val size: Int, + /** + * If it's an inline attachment. + */ + @Json(name = "is_inline") + val isInline: Boolean? = null, + /** + * The content ID of the attachment. + */ + @Json(name = "content_id") + val contentId: String? = null, + /** + * The content disposition if the attachment is located inline. + */ + @Json(name = "content_disposition") + val contentDisposition: String? = null, +) { + /** + * Builder for [CreateAttachmentRequest]. + * @property filename The filename of the attachment. + * @property contentType The content type of the attachment. + * @property size The size of the attachment in bytes. + */ + data class Builder( + private val filename: String, + private val contentType: String, + private val content: InputStream, + private val size: Int, + ) { + private var isInline: Boolean? = null + private var contentId: String? = null + private var contentDisposition: String? = null + + /** + * Set if the attachment is inline. + * @param isInline If the attachment is inline. + * @return The builder. + */ + fun isInline(isInline: Boolean) = apply { this.isInline = isInline } + + /** + * Set the content ID of the attachment. + * @param contentId The content ID of the attachment. + * @return The builder. + */ + fun contentId(contentId: String) = apply { this.contentId = contentId } + + /** + * Set the content disposition of the attachment. + * @param contentDisposition The content disposition of the attachment. + * @return The builder. + */ + fun contentDisposition(contentDisposition: String) = apply { this.contentDisposition = contentDisposition } + + /** + * Build the [CreateAttachmentRequest]. + * @return The [CreateAttachmentRequest]. + */ + fun build() = CreateAttachmentRequest( + filename = filename, + contentType = contentType, + content = content, + size = size, + isInline = isInline, + contentId = contentId, + contentDisposition = contentDisposition, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt b/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt index 227db925..b99cd659 100644 --- a/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateCredentialRequest.kt @@ -35,7 +35,7 @@ sealed class CreateCredentialRequest( * Data that specifies some special data required for a Microsoft credential */ @Json(name = "credential_data") - override val credentialData: MicrosoftAdminConsentCredentialData, + override val credentialData: CredentialData.MicrosoftAdminConsent, ) : CreateCredentialRequest(name, credentialData, CredentialType.ADMINCONSENT) /** @@ -51,7 +51,7 @@ sealed class CreateCredentialRequest( * Data that specifies some special data required for a Google credential */ @Json(name = "credential_data") - override val credentialData: GoogleServiceAccountCredentialData, + override val credentialData: CredentialData.GoogleServiceAccount, ) : CreateCredentialRequest(name, credentialData, CredentialType.SERVICEACCOUNT) /** @@ -67,6 +67,6 @@ sealed class CreateCredentialRequest( * Data that specifies some special data required for an override credential */ @Json(name = "credential_data") - override val credentialData: ConnectorOverrideCredentialData, + override val credentialData: CredentialData.ConnectorOverride, ) : CreateCredentialRequest(name, credentialData, CredentialType.CONNECTOR) } diff --git a/src/main/kotlin/com/nylas/models/CreateDraftRequest.kt b/src/main/kotlin/com/nylas/models/CreateDraftRequest.kt new file mode 100644 index 00000000..a977cae1 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/CreateDraftRequest.kt @@ -0,0 +1,224 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing a request to create a draft. + */ +data class CreateDraftRequest( + /** + * An array of message recipients. + */ + @Json(name = "to") + val to: List? = null, + /** + * An array of bcc recipients. + */ + @Json(name = "bcc") + val bcc: List? = null, + /** + * An array of cc recipients. + */ + @Json(name = "cc") + val cc: List? = null, + /** + * An array of name and email pairs that override the sent reply-to headers. + */ + @Json(name = "reply_to") + val replyTo: List? = null, + /** + * An array of files to attach to the message. + */ + @Transient + override val attachments: List? = null, + /** + * A short snippet of the message body. + * This is the first 100 characters of the message body, with any HTML tags removed. + */ + @Json(name = "snippet") + val snippet: String? = null, + /** + * The message subject. + */ + @Json(name = "subject") + val subject: String? = null, + /** + * A reference to the parent thread object. + * If this is a new draft, the thread will be empty. + */ + @Json(name = "thread_id") + val threadId: String? = null, + /** + * The full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + */ + @Json(name = "body") + val body: String? = null, + /** + * Whether or not the message has been starred by the user. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * Whether or not the message has been read by the user. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * Unix timestamp to send the message at. + */ + @Json(name = "send_at") + val sendAt: Int? = null, + /** + * The ID of the message that you are replying to. + */ + @Json(name = "reply_to_message_id") + val replyToMessageId: String? = null, + /** + * Options for tracking opens, links, and thread replies. + */ + @Json(name = "tracking_options") + val trackingOptions: TrackingOptions? = null, +) : IMessageAttachmentRequest { + /** + * Builder for [CreateDraftRequest]. + */ + class Builder { + private var to: List? = null + private var bcc: List? = null + private var cc: List? = null + private var replyTo: List? = null + private var attachments: List? = null + private var snippet: String? = null + private var subject: String? = null + private var threadId: String? = null + private var body: String? = null + private var starred: Boolean? = null + private var unread: Boolean? = null + private var sendAt: Int? = null + private var replyToMessageId: String? = null + private var trackingOptions: TrackingOptions? = null + + /** + * Sets the recipients. + * @param to The recipients. + * @return The builder. + */ + fun to(to: List?) = apply { this.to = to } + + /** + * Sets the bcc recipients. + * @param bcc The bcc recipients. + * @return The builder. + */ + fun bcc(bcc: List?) = apply { this.bcc = bcc } + + /** + * Sets the cc recipients. + * @param cc The cc recipients. + * @return The builder. + */ + fun cc(cc: List?) = apply { this.cc = cc } + + /** + * Sets the reply-to recipients. + * @param replyTo The reply-to recipients. + * @return The builder. + */ + fun replyTo(replyTo: List?) = apply { this.replyTo = replyTo } + + /** + * Sets the files to attach to the message. + * @param attachments The files to attach to the message. + * @return The builder. + */ + fun attachments(attachments: List?) = apply { this.attachments = attachments } + + /** + * Sets the snippet of the message body. + * This is the first 100 characters of the message body, with any HTML tags removed. + * @param snippet The snippet of the message body. + * @return The builder. + */ + fun snippet(snippet: String?) = apply { this.snippet = snippet } + + /** + * Sets the message subject. + * @param subject The message subject. + * @return The builder. + */ + fun subject(subject: String?) = apply { this.subject = subject } + + /** + * Sets the reference to the parent thread object. + * If this is a new draft, the thread will be empty. + * @param threadId The reference to the parent thread object. + * @return The builder. + */ + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** + * Sets the full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + * @param body The full HTML message body. + * @return The builder. + */ + fun body(body: String?) = apply { this.body = body } + + /** + * Sets whether or not the message has been starred by the user. + * @param starred Whether or not the message has been starred by the user. + * @return The builder. + */ + fun starred(starred: Boolean?) = apply { this.starred = starred } + + /** + * Sets whether or not the message has been read by the user. + * @param unread Whether or not the message has been read by the user. + * @return The builder. + */ + fun unread(unread: Boolean?) = apply { this.unread = unread } + + /** + * Sets the unix timestamp to send the message at. + * @param sendAt The unix timestamp to send the message at. + * @return The builder. + */ + fun sendAt(sendAt: Int?) = apply { this.sendAt = sendAt } + + /** + * Sets the ID of the message that you are replying to. + * @param replyToMessageId The ID of the message that you are replying to. + * @return The builder. + */ + fun replyToMessageId(replyToMessageId: String?) = apply { this.replyToMessageId = replyToMessageId } + + /** + * Sets the options for tracking opens, links, and thread replies. + * @param trackingOptions The options for tracking opens, links, and thread replies. + * @return The builder. + */ + fun trackingOptions(trackingOptions: TrackingOptions?) = apply { this.trackingOptions = trackingOptions } + + /** + * Builds a [SendMessageRequest] instance. + * @return The [SendMessageRequest] instance. + */ + fun build() = CreateDraftRequest( + to, + bcc, + cc, + replyTo, + attachments, + snippet, + subject, + threadId, + body, + starred, + unread, + sendAt, + replyToMessageId, + trackingOptions, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/Draft.kt b/src/main/kotlin/com/nylas/models/Draft.kt new file mode 100644 index 00000000..18220af7 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/Draft.kt @@ -0,0 +1,109 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing a Nylas Draft object. + */ +class Draft( + /** + * The unique identifier for the message. + */ + @Json(name = "id") + val id: String, + /** + * Grant ID of the Nylas account. + */ + @Json(name = "grant_id") + val grantId: String, + /** + * An array of message senders. + */ + @Json(name = "from") + val from: List, + /** + * Unix timestamp of when the message was received by the mail server. + * This may be different from the unverified Date header in raw message object. + */ + @Json(name = "date") + val date: Long, + /** + * The type of object. + */ + @Json(name = "object") + private val obj: String = "draft", + /** + * An array of bcc recipients. + */ + @Json(name = "bcc") + val bcc: List? = null, + /** + * An array of cc recipients. + */ + @Json(name = "cc") + val cc: List? = null, + /** + * An array of name and email pairs that override the sent reply-to headers. + */ + @Json(name = "reply_to") + val replyTo: List? = null, + /** + * A short snippet of the message body. + * This is the first 100 characters of the message body, with any HTML tags removed. + */ + @Json(name = "snippet") + val snippet: String? = null, + /** + * The message subject. + */ + @Json(name = "subject") + val subject: String? = null, + /** + * A reference to the parent thread object. + * If this is a new draft, the thread will be empty. + */ + @Json(name = "thread_id") + val threadId: String? = null, + /** + * The full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + */ + @Json(name = "body") + val body: String? = null, + /** + * Whether or not the message has been starred by the user. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * Whether or not the message has been read by the user. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * The ID of the folder(s) the message appears in. + */ + @Json(name = "folders") + val folders: List? = null, + /** + * An array of message recipients. + */ + @Json(name = "to") + val to: List? = null, + /** + * Unix timestamp of when the message was created. + */ + @Json(name = "created_at") + val createdAt: Long? = null, + /** + * An array of files attached to the message. + */ + @Json(name = "attachments") + val attachments: List? = null, +) : IMessage { + /** + * Get the type of object. + * @return The type of object. + */ + fun getObject(): String = obj +} diff --git a/src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt b/src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt new file mode 100644 index 00000000..b830c079 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/FindMessageQueryParams.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +/** + * Class representing the query parameters for finding a message. + */ +data class FindMessageQueryParams( + /** + * Allows you to specify to the message with headers included. + */ + val fields: MessageFields? = null, +) diff --git a/src/main/kotlin/com/nylas/models/IMessage.kt b/src/main/kotlin/com/nylas/models/IMessage.kt new file mode 100644 index 00000000..76658855 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/IMessage.kt @@ -0,0 +1,3 @@ +package com.nylas.models + +interface IMessage diff --git a/src/main/kotlin/com/nylas/models/IMessageAttachmentRequest.kt b/src/main/kotlin/com/nylas/models/IMessageAttachmentRequest.kt new file mode 100644 index 00000000..dd3b73f0 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/IMessageAttachmentRequest.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +/** + * Represents a message-related request that uses an attachment + */ +interface IMessageAttachmentRequest { + /** + * An array of files to attach to the message. + */ + val attachments: List? +} diff --git a/src/main/kotlin/com/nylas/models/ListDraftsQueryParams.kt b/src/main/kotlin/com/nylas/models/ListDraftsQueryParams.kt new file mode 100644 index 00000000..714e12c6 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ListDraftsQueryParams.kt @@ -0,0 +1,180 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing the query parameters for listing drafts. + */ +data class ListDraftsQueryParams( + /** + * The maximum number of objects to return. + * This field defaults to 50. The maximum allowed value is 200. + */ + @Json(name = "limit") + val limit: Int? = null, + /** + * An identifier that specifies which page of data to return. + * This value should be taken from the [ListResponse.nextCursor] response field. + */ + @Json(name = "page_token") + val pageToken: String? = null, + /** + * Return items with a matching literal subject. + */ + @Json(name = "subject") + val subject: String? = null, + /** + * Return emails that have been sent or received from this list of email addresses. + */ + @Json(name = "any_email") + val anyEmail: List? = null, + /** + * Return items containing drafts to be sent these email address. + */ + @Json(name = "to") + val to: List? = null, + /** + * Return items containing drafts cc'ing these email address. + */ + @Json(name = "cc") + val cc: List? = null, + /** + * Return items containing drafts bcc'ing these email address. + */ + @Json(name = "bcc") + val bcc: List? = null, + /** + * Return drafts that are unread. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * Return drafts that are starred. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * Return drafts that belong to this thread. + */ + @Json(name = "thread_id") + val threadId: String? = null, + /** + * Return drafts that contain attachments. + */ + @Json(name = "has_attachment") + val hasAttachment: Boolean? = null, +) : IQueryParams { + /** + * Builder for [ListDraftsQueryParams]. + */ + class Builder { + private var limit: Int? = null + private var pageToken: String? = null + private var subject: String? = null + private var anyEmail: List? = null + private var to: List? = null + private var cc: List? = null + private var bcc: List? = null + private var unread: Boolean? = null + private var starred: Boolean? = null + private var threadId: String? = null + private var hasAttachment: Boolean? = null + + /** + * Sets the maximum number of objects to return. + * This field defaults to 50. The maximum allowed value is 200. + * @param limit The maximum number of objects to return. + * @return The builder. + */ + fun limit(limit: Int?) = apply { this.limit = limit } + + /** + * Sets the identifier that specifies which page of data to return. + * This value should be taken from the [ListResponse.nextCursor] response field. + * @param pageToken The identifier that specifies which page of data to return. + * @return The builder. + */ + fun pageToken(pageToken: String?) = apply { this.pageToken = pageToken } + + /** + * Sets the subject. + * @param subject The subject. + * @return The builder. + */ + fun subject(subject: String?) = apply { this.subject = subject } + + /** + * Sets the list of email addresses. + * @param anyEmail The list of email addresses. + * @return The builder. + */ + fun anyEmail(anyEmail: List?) = apply { this.anyEmail = anyEmail } + + /** + * Sets the list of email addresses. + * @param to The list of email addresses. + * @return The builder. + */ + fun to(to: List?) = apply { this.to = to } + + /** + * Sets the list of email addresses. + * @param cc The list of email addresses. + * @return The builder. + */ + fun cc(cc: List?) = apply { this.cc = cc } + + /** + * Sets the list of email addresses. + * @param bcc The list of email addresses. + * @return The builder. + */ + fun bcc(bcc: List?) = apply { this.bcc = bcc } + + /** + * Sets the unread flag. + * @param unread The unread flag. + * @return The builder. + */ + fun unread(unread: Boolean?) = apply { this.unread = unread } + + /** + * Sets the starred flag. + * @param starred The starred flag. + * @return The builder. + */ + fun starred(starred: Boolean?) = apply { this.starred = starred } + + /** + * Sets the thread id. + * @param threadId The thread id. + * @return The builder. + */ + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** + * Sets the has attachment flag. + * @param hasAttachment The has attachment flag. + * @return The builder. + */ + fun hasAttachment(hasAttachment: Boolean?) = apply { this.hasAttachment = hasAttachment } + + /** + * Builds a [ListDraftsQueryParams] instance. + * @return The [ListDraftsQueryParams] instance. + */ + fun build() = ListDraftsQueryParams( + limit = limit, + pageToken = pageToken, + subject = subject, + anyEmail = anyEmail, + to = to, + cc = cc, + bcc = bcc, + unread = unread, + starred = starred, + threadId = threadId, + hasAttachment = hasAttachment, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/ListMessagesQueryParams.kt b/src/main/kotlin/com/nylas/models/ListMessagesQueryParams.kt new file mode 100644 index 00000000..2f1f75df --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ListMessagesQueryParams.kt @@ -0,0 +1,263 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing the query parameters for listing messages. + */ +data class ListMessagesQueryParams( + /** + * The maximum number of objects to return. + * This field defaults to 50. The maximum allowed value is 200. + */ + @Json(name = "limit") + val limit: Int? = null, + /** + * An identifier that specifies which page of data to return. + * This value should be taken from the [ListResponse.nextCursor] response field. + */ + @Json(name = "page_token") + val pageToken: String? = null, + /** + * Return items with a matching literal subject. + */ + @Json(name = "subject") + val subject: String? = null, + /** + * Return emails that have been sent or received from this list of email addresses. + */ + @Json(name = "any_email") + val anyEmail: List? = null, + /** + * Return items containing messages sent to these email address. + */ + @Json(name = "to") + val to: List? = null, + /** + * Return items containing messages sent from these email address. + */ + @Json(name = "from") + val from: List? = null, + /** + * Return items containing messages cc'd on these email address. + */ + @Json(name = "cc") + val cc: List? = null, + /** + * Return items containing messages bcc'd on these email address. + */ + @Json(name = "bcc") + val bcc: List? = null, + /** + * Return emails that are in these folder IDs. + */ + @Json(name = "in") + val inFolder: List? = null, + /** + * Return emails that are unread. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * Return emails that are starred. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * Return emails that belong to this thread. + */ + @Json(name = "thread_id") + val threadId: String? = null, + /** + * Return emails that have been received before this timestamp. + */ + @Json(name = "received_before") + val receivedBefore: Int? = null, + /** + * Return emails that have been received after this timestamp. + */ + @Json(name = "received_after") + val receivedAfter: Int? = null, + /** + * Return emails that contain attachments. + */ + @Json(name = "has_attachment") + val hasAttachment: Boolean? = null, + /** + * Allows you to specify to return messages with headers included. + */ + @Json(name = "fields") + val fields: MessageFields? = null, + /** + * The provider-specific query string used to search messages. + * Available for Google and Microsoft Graph only. + */ + @Json(name = "search_query_native") + val searchQueryNative: String? = null, +) : IQueryParams { + class Builder { + private var limit: Int? = null + private var pageToken: String? = null + private var subject: String? = null + private var anyEmail: List? = null + private var to: List? = null + private var from: List? = null + private var cc: List? = null + private var bcc: List? = null + private var inFolder: List? = null + private var unread: Boolean? = null + private var starred: Boolean? = null + private var threadId: String? = null + private var receivedBefore: Int? = null + private var receivedAfter: Int? = null + private var hasAttachment: Boolean? = null + private var fields: MessageFields? = null + private var searchQueryNative: String? = null + + /** + * Sets the maximum number of objects to return. + * This field defaults to 10. The maximum allowed value is 200. + * @param limit The maximum number of objects to return. + * @return The builder. + */ + fun limit(limit: Int?) = apply { this.limit = limit } + + /** + * Sets the identifier that specifies which page of data to return. + * This value should be taken from the next_cursor response field. + * @param pageToken The identifier that specifies which page of data to return. + * @return The builder. + */ + fun pageToken(pageToken: String?) = apply { this.pageToken = pageToken } + + /** + * Sets the subject to match. + * @param subject The subject to match. + * @return The builder. + */ + fun subject(subject: String?) = apply { this.subject = subject } + + /** + * Set the list of email addresses to match. + * @param anyEmail The list of email addresses to match. + * @return The builder + */ + fun anyEmail(anyEmail: List?) = apply { this.anyEmail = anyEmail } + + /** + * Set the list of email addresses to match in the to field. + * @param to The list of email addresses to match in the to field. + * @return The builder + */ + fun to(to: List?) = apply { this.to = to } + + /** + * Set the list of email addresses to match in the from field. + * @param from The list of email addresses to match in the from field. + * @return The builder + */ + fun from(from: List?) = apply { this.from = from } + + /** + * Set the list of email addresses to match in the cc field. + * @param cc The list of email addresses to match in the cc field. + * @return The builder + */ + fun cc(cc: List?) = apply { this.cc = cc } + + /** + * Set the list of email addresses to match in the bcc field. + * @param bcc The list of email addresses to match in the bcc field. + * @return The builder + */ + fun bcc(bcc: List?) = apply { this.bcc = bcc } + + /** + * Set the list of folder IDs to match. + * @param inFolder The list of folder IDs to match. + * @return The builder + */ + fun inFolder(inFolder: List?) = apply { this.inFolder = inFolder } + + /** + * Set to true to return emails that are unread. + * @param unread True to return emails that are unread. + * @return The builder + */ + fun unread(unread: Boolean?) = apply { this.unread = unread } + + /** + * Set to true to return emails that are starred. + * @param starred True to return emails that are starred. + * @return The builder + */ + fun starred(starred: Boolean?) = apply { this.starred = starred } + + /** + * Set the thread ID to match. + * @param threadId The thread ID to match. + * @return The builder + */ + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** + * Set the timestamp to match for emails received before. + * @param receivedBefore The timestamp to match for emails received before. + * @return The builder + */ + fun receivedBefore(receivedBefore: Int?) = apply { this.receivedBefore = receivedBefore } + + /** + * Set the timestamp to match for emails received after. + * @param receivedAfter The timestamp to match for emails received after. + * @return The builder + */ + fun receivedAfter(receivedAfter: Int?) = apply { this.receivedAfter = receivedAfter } + + /** + * Set to true to return emails that contain attachments. + * @param hasAttachment True to return emails that contain attachments. + * @return The builder + */ + fun hasAttachment(hasAttachment: Boolean?) = apply { this.hasAttachment = hasAttachment } + + /** + * Set to true to return emails that contain attachments. + * @param fields The fields to include in the response. + * @return The builder + */ + fun fields(fields: MessageFields?) = apply { this.fields = fields } + + /** + * Set the provider-specific query string used to search messages. + * Available for Google and Microsoft Graph only. + * @param searchQueryNative The provider-specific query string used to search messages. + * @return The builder + */ + fun searchQueryNative(searchQueryNative: String?) = apply { this.searchQueryNative = searchQueryNative } + + /** + * Builds the [ListMessagesQueryParams] object. + * @return The [ListMessagesQueryParams] object. + */ + fun build() = ListMessagesQueryParams( + limit = limit, + pageToken = pageToken, + subject = subject, + anyEmail = anyEmail, + to = to, + from = from, + cc = cc, + bcc = bcc, + inFolder = inFolder, + unread = unread, + starred = starred, + threadId = threadId, + receivedBefore = receivedBefore, + receivedAfter = receivedAfter, + hasAttachment = hasAttachment, + fields = fields, + searchQueryNative = searchQueryNative, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/ListThreadsQueryParams.kt b/src/main/kotlin/com/nylas/models/ListThreadsQueryParams.kt new file mode 100644 index 00000000..0e775d97 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ListThreadsQueryParams.kt @@ -0,0 +1,251 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +data class ListThreadsQueryParams( + /** + * The maximum number of objects to return. + * This field defaults to 50. The maximum allowed value is 200. + */ + @Json(name = "limit") + val limit: Int? = null, + /** + * An identifier that specifies which page of data to return. + * This value should be taken from the [ListResponse.nextCursor] response field. + */ + @Json(name = "page_token") + val pageToken: String? = null, + /** + * Return items with a matching literal subject. + */ + @Json(name = "subject") + val subject: String? = null, + /** + * Return emails that have been sent or received from this list of email addresses. + */ + @Json(name = "any_email") + val anyEmail: List? = null, + /** + * Return items containing messages sent to these email address. + */ + @Json(name = "to") + val to: List? = null, + /** + * Return items containing messages sent from these email address. + */ + @Json(name = "from") + val from: List? = null, + /** + * Return items containing messages cc'd on these email address. + */ + @Json(name = "cc") + val cc: List? = null, + /** + * Return items containing messages bcc'd on these email address. + */ + @Json(name = "bcc") + val bcc: List? = null, + /** + * Return emails that are in these folder IDs. + */ + @Json(name = "in") + val inFolder: List? = null, + /** + * Return emails that are unread. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * Return emails that are starred. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * Return threads whose most recent message was received before this Unix timestamp. + */ + @Json(name = "latest_message_before") + val latestMessageBefore: Long? = null, + /** + * Return threads whose most recent message was received after this Unix timestamp. + */ + @Json(name = "latest_message_after") + val latestMessageAfter: Long? = null, + /** + * Return emails that contain attachments. + */ + @Json(name = "has_attachment") + val hasAttachment: Boolean? = null, + /** + * The provider-specific query string used to search messages. + * Available for Google and Microsoft Graph only. + */ + @Json(name = "search_query_native") + val searchQueryNative: String? = null, +) : IQueryParams { + /** + * Builder for [ListThreadsQueryParams]. + */ + class Builder { + private var limit: Int? = null + private var pageToken: String? = null + private var subject: String? = null + private var anyEmail: List? = null + private var to: List? = null + private var from: List? = null + private var cc: List? = null + private var bcc: List? = null + private var inFolder: List? = null + private var unread: Boolean? = null + private var starred: Boolean? = null + private var threadId: String? = null + private var latestMessageBefore: Long? = null + private var latestMessageAfter: Long? = null + private var hasAttachment: Boolean? = null + private var fields: MessageFields? = null + private var searchQueryNative: String? = null + + /** + * Sets the maximum number of objects to return. + * This field defaults to 10. The maximum allowed value is 200. + * @param limit The maximum number of objects to return. + * @return The builder. + */ + fun limit(limit: Int?) = apply { this.limit = limit } + + /** + * Sets the identifier that specifies which page of data to return. + * This value should be taken from the next_cursor response field. + * @param pageToken The identifier that specifies which page of data to return. + * @return The builder. + */ + fun pageToken(pageToken: String?) = apply { this.pageToken = pageToken } + + /** + * Sets the subject to match. + * @param subject The subject to match. + * @return The builder. + */ + fun subject(subject: String?) = apply { this.subject = subject } + + /** + * Set the list of email addresses to match. + * @param anyEmail The list of email addresses to match. + * @return The builder + */ + fun anyEmail(anyEmail: List?) = apply { this.anyEmail = anyEmail } + + /** + * Set the list of email addresses to match in the to field. + * @param to The list of email addresses to match in the to field. + * @return The builder + */ + fun to(to: List?) = apply { this.to = to } + + /** + * Set the list of email addresses to match in the from field. + * @param from The list of email addresses to match in the from field. + * @return The builder + */ + fun from(from: List?) = apply { this.from = from } + + /** + * Set the list of email addresses to match in the cc field. + * @param cc The list of email addresses to match in the cc field. + * @return The builder + */ + fun cc(cc: List?) = apply { this.cc = cc } + + /** + * Set the list of email addresses to match in the bcc field. + * @param bcc The list of email addresses to match in the bcc field. + * @return The builder + */ + fun bcc(bcc: List?) = apply { this.bcc = bcc } + + /** + * Set the list of folder IDs to match. + * @param inFolder The list of folder IDs to match. + * @return The builder + */ + fun inFolder(inFolder: List?) = apply { this.inFolder = inFolder } + + /** + * Set to true to return emails that are unread. + * @param unread True to return emails that are unread. + * @return The builder + */ + fun unread(unread: Boolean?) = apply { this.unread = unread } + + /** + * Set to true to return emails that are starred. + * @param starred True to return emails that are starred. + * @return The builder + */ + fun starred(starred: Boolean?) = apply { this.starred = starred } + + /** + * Set the thread ID to match. + * @param threadId The thread ID to match. + * @return The builder + */ + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** + * Set the timestamp to match for emails received before. + * @param receivedBefore The timestamp to match for emails received before. + * @return The builder + */ + fun latestMessageBefore(latestMessageBefore: Long?) = apply { this.latestMessageBefore = latestMessageBefore } + + /** + * Set the timestamp to match for emails received after. + * @param receivedAfter The timestamp to match for emails received after. + * @return The builder + */ + fun latestMessageAfter(latestMessageAfter: Long?) = apply { this.latestMessageAfter = latestMessageAfter } + + /** + * Set to true to return emails that contain attachments. + * @param hasAttachment True to return emails that contain attachments. + * @return The builder + */ + fun hasAttachment(hasAttachment: Boolean?) = apply { this.hasAttachment = hasAttachment } + + /** + * Set to true to return emails that contain attachments. + * @param fields The fields to include in the response. + * @return The builder + */ + fun fields(fields: MessageFields?) = apply { this.fields = fields } + + /** + * Set the provider-specific query string used to search messages. + * Available for Google and Microsoft Graph only. + * @param searchQueryNative The provider-specific query string used to search messages. + * @return The builder + */ + fun searchQueryNative(searchQueryNative: String?) = apply { this.searchQueryNative = searchQueryNative } + + /** + * Builds the [ListThreadsQueryParams] object. + * @return The [ListThreadsQueryParams] object. + */ + fun build() = ListThreadsQueryParams( + limit = limit, + pageToken = pageToken, + subject = subject, + anyEmail = anyEmail, + to = to, + from = from, + cc = cc, + bcc = bcc, + inFolder = inFolder, + unread = unread, + starred = starred, + latestMessageBefore = latestMessageBefore, + latestMessageAfter = latestMessageAfter, + hasAttachment = hasAttachment, + searchQueryNative = searchQueryNative, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/Message.kt b/src/main/kotlin/com/nylas/models/Message.kt new file mode 100644 index 00000000..0a445d57 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/Message.kt @@ -0,0 +1,120 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing a Nylas Message object. + */ +data class Message( + /** + * The unique identifier for the message. + */ + @Json(name = "id") + val id: String, + /** + * Grant ID of the Nylas account. + */ + @Json(name = "grant_id") + val grantId: String, + /** + * An array of message senders. + */ + @Json(name = "from") + val from: List, + /** + * Unix timestamp of when the message was received by the mail server. + * This may be different from the unverified Date header in raw message object. + */ + @Json(name = "date") + val date: Long, + /** + * The type of object. + */ + @Json(name = "object") + private val obj: String = "message", + /** + * An array of bcc recipients. + */ + @Json(name = "bcc") + val bcc: List? = null, + /** + * An array of cc recipients. + */ + @Json(name = "cc") + val cc: List? = null, + /** + * An array of name and email pairs that override the sent reply-to headers. + */ + @Json(name = "reply_to") + val replyTo: List? = null, + /** + * A short snippet of the message body. + * This is the first 100 characters of the message body, with any HTML tags removed. + */ + @Json(name = "snippet") + val snippet: String? = null, + /** + * The message subject. + */ + @Json(name = "subject") + val subject: String? = null, + /** + * A reference to the parent thread object. + * If this is a new draft, the thread will be empty. + */ + @Json(name = "thread_id") + val threadId: String? = null, + /** + * The full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + */ + @Json(name = "body") + val body: String? = null, + /** + * Whether or not the message has been starred by the user. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * Whether or not the message has been read by the user. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * The ID of the folder(s) the message appears in. + */ + @Json(name = "folders") + val folders: List? = null, + /** + * An array of message recipients. + */ + @Json(name = "to") + val to: List? = null, + /** + * An array of files attached to the message. + */ + @Json(name = "attachments") + val attachments: List? = null, + /** + * The message headers. + * Only present if the 'fields' query parameter is set to includeHeaders. + */ + @Json(name = "headers") + val headers: List? = null, + /** + * Unix timestamp of when the message was created. + */ + @Json(name = "created_at") + val createdAt: Long? = null, + /** + * A list of key-value pairs storing additional data. + */ + @Json(name = "metadata") + val metadata: Map? = null, +) : IMessage { + /** + * Get the type of object. + * @return The type of object. + */ + fun getObject(): String = obj +} diff --git a/src/main/kotlin/com/nylas/models/MessageFields.kt b/src/main/kotlin/com/nylas/models/MessageFields.kt new file mode 100644 index 00000000..741ca17d --- /dev/null +++ b/src/main/kotlin/com/nylas/models/MessageFields.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +enum class MessageFields { + @Json(name = "standard") + STANDARD, + + @Json(name = "include_headers") + INCLUDE_HEADERS, +} diff --git a/src/main/kotlin/com/nylas/models/MessageHeaders.kt b/src/main/kotlin/com/nylas/models/MessageHeaders.kt new file mode 100644 index 00000000..5789123b --- /dev/null +++ b/src/main/kotlin/com/nylas/models/MessageHeaders.kt @@ -0,0 +1,15 @@ +package com.nylas.models + +/** + * Class representing a message header. + */ +data class MessageHeaders( + /** + * The header name. + */ + val name: String, + /** + * The header value. + */ + val value: String, +) diff --git a/src/main/kotlin/com/nylas/models/ScheduledMessage.kt b/src/main/kotlin/com/nylas/models/ScheduledMessage.kt new file mode 100644 index 00000000..0e1d8a3c --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ScheduledMessage.kt @@ -0,0 +1,19 @@ +package com.nylas.models + +/** + * Class representing information about a scheduled message. + */ +data class ScheduledMessage( + /** + * The unique identifier for the scheduled message. + */ + val scheduleId: Int, + /** + * The status of the scheduled message. + */ + val status: ScheduledMessageStatus, + /** + * The time the message was sent or failed to send, in epoch time. + */ + val closeTime: Int?, +) diff --git a/src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt b/src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt new file mode 100644 index 00000000..c4ebaaca --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ScheduledMessageStatus.kt @@ -0,0 +1,15 @@ +package com.nylas.models + +/** + * Class representing a scheduled message status. + */ +data class ScheduledMessageStatus( + /** + * The status code the describes the state of the scheduled message + */ + val code: String, + /** + * A description of the status of the scheduled message + */ + val description: String, +) diff --git a/src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt b/src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt new file mode 100644 index 00000000..03d7137c --- /dev/null +++ b/src/main/kotlin/com/nylas/models/ScheduledMessagesList.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +/** + * Class representing a list of scheduled messages. + */ +data class ScheduledMessagesList( + /** + * The list of scheduled messages. + */ + val schedules: List, +) diff --git a/src/main/kotlin/com/nylas/models/SendMessageRequest.kt b/src/main/kotlin/com/nylas/models/SendMessageRequest.kt new file mode 100644 index 00000000..1fb07c0b --- /dev/null +++ b/src/main/kotlin/com/nylas/models/SendMessageRequest.kt @@ -0,0 +1,218 @@ +package com.nylas.models + +/** + * Class representing a request to send a message. + */ +data class SendMessageRequest( + /** + * An array of message recipients. + */ + val to: List, + /** + * An array of bcc recipients. + */ + val bcc: List? = null, + /** + * An array of cc recipients. + */ + val cc: List? = null, + /** + * An array of name and email pairs that override the sent reply-to headers. + */ + val replyTo: List? = null, + /** + * An array of files to attach to the message. + */ + override val attachments: List? = null, + /** + * A short snippet of the message body. + * This is the first 100 characters of the message body, with any HTML tags removed. + */ + val snippet: String? = null, + /** + * The message subject. + */ + val subject: String? = null, + /** + * A reference to the parent thread object. + * If this is a new draft, the thread will be empty. + */ + val threadId: String? = null, + /** + * The full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + */ + val body: String? = null, + /** + * Whether or not the message has been starred by the user. + */ + val starred: Boolean? = null, + /** + * Whether or not the message has been read by the user. + */ + val unread: Boolean? = null, + /** + * Unix timestamp to send the message at. + */ + val sendAt: Int? = null, + /** + * The ID of the message that you are replying to. + */ + val replyToMessageId: String? = null, + /** + * Options for tracking opens, links, and thread replies. + */ + val trackingOptions: TrackingOptions? = null, + /** + * Whether or not to use draft support. + * This is primarily used when dealing with large attachments. + */ + val useDraft: Boolean? = null, +) : IMessageAttachmentRequest { + /** + * Builder for [SendMessageRequest]. + * @property to An array of message recipients. + */ + data class Builder( + private val to: List, + ) { + private var bcc: List? = null + private var cc: List? = null + private var replyTo: List? = null + private var attachments: List? = null + private var snippet: String? = null + private var subject: String? = null + private var threadId: String? = null + private var body: String? = null + private var starred: Boolean? = null + private var unread: Boolean? = null + private var sendAt: Int? = null + private var replyToMessageId: String? = null + private var trackingOptions: TrackingOptions? = null + private var useDraft: Boolean? = null + + /** + * Sets the bcc recipients. + * @param bcc The bcc recipients. + * @return The builder. + */ + fun bcc(bcc: List?) = apply { this.bcc = bcc } + + /** + * Sets the cc recipients. + * @param cc The cc recipients. + * @return The builder. + */ + fun cc(cc: List?) = apply { this.cc = cc } + + /** + * Sets the reply-to recipients. + * @param replyTo The reply-to recipients. + * @return The builder. + */ + fun replyTo(replyTo: List?) = apply { this.replyTo = replyTo } + + /** + * Sets the files to attach to the message. + * @param attachments The files to attach to the message. + * @return The builder. + */ + fun attachments(attachments: List?) = apply { this.attachments = attachments } + + /** + * Sets the snippet of the message body. + * This is the first 100 characters of the message body, with any HTML tags removed. + * @param snippet The snippet of the message body. + * @return The builder. + */ + fun snippet(snippet: String?) = apply { this.snippet = snippet } + + /** + * Sets the message subject. + * @param subject The message subject. + * @return The builder. + */ + fun subject(subject: String?) = apply { this.subject = subject } + + /** + * Sets the reference to the parent thread object. + * If this is a new draft, the thread will be empty. + * @param threadId The reference to the parent thread object. + * @return The builder. + */ + fun threadId(threadId: String?) = apply { this.threadId = threadId } + + /** + * Sets the full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + * @param body The full HTML message body. + * @return The builder. + */ + fun body(body: String?) = apply { this.body = body } + + /** + * Sets whether or not the message has been starred by the user. + * @param starred Whether or not the message has been starred by the user. + * @return The builder. + */ + fun starred(starred: Boolean?) = apply { this.starred = starred } + + /** + * Sets whether or not the message has been read by the user. + * @param unread Whether or not the message has been read by the user. + * @return The builder. + */ + fun unread(unread: Boolean?) = apply { this.unread = unread } + + /** + * Sets the unix timestamp to send the message at. + * @param sendAt The unix timestamp to send the message at. + * @return The builder. + */ + fun sendAt(sendAt: Int?) = apply { this.sendAt = sendAt } + + /** + * Sets the ID of the message that you are replying to. + * @param replyToMessageId The ID of the message that you are replying to. + * @return The builder. + */ + fun replyToMessageId(replyToMessageId: String?) = apply { this.replyToMessageId = replyToMessageId } + + /** + * Sets the options for tracking opens, links, and thread replies. + * @param trackingOptions The options for tracking opens, links, and thread replies. + * @return The builder. + */ + fun trackingOptions(trackingOptions: TrackingOptions?) = apply { this.trackingOptions = trackingOptions } + + /** + * Sets whether or not to use draft support. + * This is primarily used when dealing with large attachments. + * @param useDraft Whether or not to use draft support. + * @return The builder. + */ + fun useDraft(useDraft: Boolean?) = apply { this.useDraft = useDraft } + + /** + * Builds a [SendMessageRequest] instance. + * @return The [SendMessageRequest] instance. + */ + fun build() = SendMessageRequest( + to, + bcc, + cc, + replyTo, + attachments, + snippet, + subject, + threadId, + body, + starred, + unread, + sendAt, + replyToMessageId, + trackingOptions, + useDraft, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt b/src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt new file mode 100644 index 00000000..71ba4bb2 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/StopScheduledMessageResponse.kt @@ -0,0 +1,11 @@ +package com.nylas.models + +/** + * Class representing a response after stopping a scheduled message. + */ +data class StopScheduledMessageResponse( + /** + * A message describing the result of the request. + */ + val message: String, +) diff --git a/src/main/kotlin/com/nylas/models/Thread.kt b/src/main/kotlin/com/nylas/models/Thread.kt new file mode 100644 index 00000000..f6add67d --- /dev/null +++ b/src/main/kotlin/com/nylas/models/Thread.kt @@ -0,0 +1,98 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +data class Thread( + /** + * The unique identifier for the thread. + */ + @Json(name = "id") + val id: String, + /** + * Grant ID of the Nylas account. + */ + @Json(name = "grant_id") + val grantId: String, + /** + * The type of object. + */ + @Json(name = "object") + val obj: String = "thread", + /** + * The latest message or draft in the thread. + */ + @Json(name = "latest_draft_or_message") + val latestDraftOrMessage: IMessage? = null, + /** + * Whether or not a message in a thread has attachments. + */ + @Json(name = "has_attachments") + val hasAttachments: Boolean? = null, + /** + * Whether or not a message in a thread has drafts. + */ + @Json(name = "has_drafts") + val hasDrafts: Boolean? = null, + /** + * A boolean indicating whether the thread is starred or not. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * A boolean indicating if all messages within the thread are read or not. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * Unix timestamp of the earliest or first message in the thread. + */ + @Json(name = "earliest_message_date") + val earliestMessageDate: Long? = null, + /** + * Unix timestamp of the most recent message received in the thread. + */ + @Json(name = "latest_message_received_date") + val latestMessageReceivedDate: Long? = null, + /** + * Unix timestamp of the most recent message sent in the thread. + */ + @Json(name = "latest_message_sent_date") + val latestMessageSentDate: Long? = null, + /** + * An array of participants in the thread. + */ + @Json(name = "participants") + val participants: List? = null, + /** + * An array of message IDs in the thread. + */ + @Json(name = "message_ids") + val messageIds: List? = null, + /** + * An array of draft IDs in the thread. + */ + @Json(name = "draft_ids") + val draftIds: List? = null, + /** + * An array of folder IDs the thread appears in. + */ + @Json(name = "folders") + val folders: List? = null, + /** + * A short snippet of the last received message/draft body. + * This is the first 100 characters of the message body, with any HTML tags removed. + */ + @Json(name = "snippet") + val snippet: String? = null, + /** + * The subject line of the thread. + */ + @Json(name = "subject") + val subject: String? = null, +) { + /** + * Get the type of object. + * @return The type of object. + */ + fun getObject() = obj +} diff --git a/src/main/kotlin/com/nylas/models/TrackingOptions.kt b/src/main/kotlin/com/nylas/models/TrackingOptions.kt new file mode 100644 index 00000000..b99cd4d5 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/TrackingOptions.kt @@ -0,0 +1,23 @@ +package com.nylas.models + +/** + * Class representing the different tracking options for when a message is sent. + */ +data class TrackingOptions( + /** + * The label to apply to tracked messages. + */ + val label: String? = null, + /** + * Whether to track links. + */ + val links: Boolean? = null, + /** + * Whether to track opens. + */ + val opens: Boolean? = null, + /** + * Whether to track thread replies. + */ + val threadReplies: Boolean? = null, +) diff --git a/src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt b/src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt new file mode 100644 index 00000000..7c6e6173 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt @@ -0,0 +1,174 @@ +package com.nylas.models + +/** + * Class representing a request to update a draft. + */ +data class UpdateDraftRequest( + /** + * An array of message recipients. + */ + val to: List? = null, + /** + * An array of bcc recipients. + */ + val bcc: List? = null, + /** + * An array of cc recipients. + */ + val cc: List? = null, + /** + * An array of name and email pairs that override the sent reply-to headers. + */ + val replyTo: List? = null, + /** + * An array of files to attach to the message. + */ + override val attachments: List? = null, + /** + * The message subject. + */ + val subject: String? = null, + /** + * The full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + */ + val body: String? = null, + /** + * Whether or not the message has been starred by the user. + */ + val starred: Boolean? = null, + /** + * Whether or not the message has been read by the user. + */ + val unread: Boolean? = null, + /** + * Unix timestamp to send the message at. + */ + val sendAt: Int? = null, + /** + * The ID of the message that you are replying to. + */ + val replyToMessageId: String? = null, + /** + * Options for tracking opens, links, and thread replies. + */ + val trackingOptions: TrackingOptions? = null, +) : IMessageAttachmentRequest { + /** + * Builder for [UpdateDraftRequest]. + */ + class Builder { + private var to: List? = null + private var bcc: List? = null + private var cc: List? = null + private var replyTo: List? = null + private var attachments: List? = null + private var subject: String? = null + private var body: String? = null + private var starred: Boolean? = null + private var unread: Boolean? = null + private var sendAt: Int? = null + private var replyToMessageId: String? = null + private var trackingOptions: TrackingOptions? = null + + /** + * Set an array of message recipients. + * @param to An array of message recipients. + * @return The builder. + */ + fun to(to: List) = apply { this.to = to } + + /** + * Set an array of bcc recipients. + * @param bcc An array of bcc recipients. + * @return The builder. + */ + fun bcc(bcc: List) = apply { this.bcc = bcc } + + /** + * Set an array of cc recipients. + * @param cc An array of cc recipients. + * @return The builder. + */ + fun cc(cc: List) = apply { this.cc = cc } + + /** + * Set an array of name and email pairs that override the sent reply-to headers. + * @param replyTo An array of name and email pairs that override the sent reply-to headers. + * @return The builder. + */ + fun replyTo(replyTo: List) = apply { this.replyTo = replyTo } + + /** + * Set an array of files to attach to the draft. + * @param attachments An array of files to attach to the message. + * @return The builder. + */ + fun attachments(attachments: List) = apply { this.attachments = attachments } + + /** + * Set the message subject. + * @param subject The message subject. + * @return The builder. + */ + fun subject(subject: String) = apply { this.subject = subject } + + /** + * Set the full HTML message body. + * Messages with only plain-text representations are up-converted to HTML. + * @param body The full HTML message body. + * @return The builder. + */ + fun body(body: String) = apply { this.body = body } + + /** + * Set whether or not the message has been starred by the user. + * @param starred Whether or not the message has been starred by the user. + * @return The builder. + */ + fun starred(starred: Boolean) = apply { this.starred = starred } + + /** + * Set whether or not the message has been read by the user. + * @param unread Whether or not the message has been read by the user. + * @return The builder. + */ + fun unread(unread: Boolean) = apply { this.unread = unread } + + /** + * Set the unix timestamp to send the message at. + * @param sendAt The unix timestamp to send the message at. + * @return The builder. + */ + fun sendAt(sendAt: Int) = apply { this.sendAt = sendAt } + + /** + * Set the ID of the message that you are replying to. + * @param replyToMessageId The ID of the message that you are replying to. + * @return The builder. + */ + fun replyToMessageId(replyToMessageId: String) = apply { this.replyToMessageId = replyToMessageId } + + /** + * Set options for tracking opens, links, and thread replies. + * @param trackingOptions Options for tracking opens, links, and thread replies. + * @return The builder. + */ + fun trackingOptions(trackingOptions: TrackingOptions) = apply { this.trackingOptions = trackingOptions } + + fun build() = UpdateDraftRequest( + to = to, + bcc = bcc, + cc = cc, + replyTo = replyTo, + attachments = attachments, + subject = subject, + body = body, + starred = starred, + unread = unread, + sendAt = sendAt, + replyToMessageId = replyToMessageId, + trackingOptions = trackingOptions, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/UpdateMessageRequest.kt b/src/main/kotlin/com/nylas/models/UpdateMessageRequest.kt new file mode 100644 index 00000000..76b98c06 --- /dev/null +++ b/src/main/kotlin/com/nylas/models/UpdateMessageRequest.kt @@ -0,0 +1,78 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing a request to update a message. + */ +data class UpdateMessageRequest( + /** + * Sets the message as starred or unstarred. + */ + @Json(name = "starred") + val starred: Boolean? = null, + /** + * Sets the message as read or unread. + */ + @Json(name = "unread") + val unread: Boolean? = null, + /** + * The IDs of the folders the message should appear in. + */ + @Json(name = "folders") + val folders: List? = null, + /** + * A list of key-value pairs storing additional data. + */ + @Json(name = "metadata") + val metadata: Map? = null, +) { + /** + * Builder for [UpdateMessageRequest]. + */ + class Builder { + private var starred: Boolean? = null + private var unread: Boolean? = null + private var folders: List? = null + private var metadata: Map? = null + + /** + * Set if the message is starred. + * @param starred If the message is starred. + * @return The builder. + */ + fun starred(starred: Boolean) = apply { this.starred = starred } + + /** + * Set if the message is unread. + * @param unread If the message is unread. + * @return The builder. + */ + fun unread(unread: Boolean) = apply { this.unread = unread } + + /** + * Set the folders the message should appear in. + * @param folders The folders the message should appear in. + * @return The builder. + */ + fun folders(folders: List) = apply { this.folders = folders } + + /** + * Set the metadata. + * @param metadata The metadata. + * @return The builder. + */ + fun metadata(metadata: Map) = apply { this.metadata = metadata } + + /** + * Build the [UpdateMessageRequest]. + * @return The [UpdateMessageRequest]. + */ + fun build() = UpdateMessageRequest( + starred = starred, + unread = unread, + folders = folders, + metadata = metadata, + ) + } +} diff --git a/src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt b/src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt new file mode 100644 index 00000000..d45e2a9c --- /dev/null +++ b/src/main/kotlin/com/nylas/models/UpdateThreadRequest.kt @@ -0,0 +1,65 @@ +package com.nylas.models + +import com.squareup.moshi.Json + +/** + * Class representing a request to update a thread. + */ +data class UpdateThreadRequest( + /** + * Sets all messages in the thread as starred or unstarred. + */ + @Json(name = "starred") + val starred: Boolean?, + /** + * Sets all messages in the thread as read or unread. + */ + @Json(name = "unread") + val unread: Boolean?, + /** + * The IDs of the folders to apply, overwriting all previous folders for all messages in the thread. + */ + @Json(name = "folders") + val folders: List?, +) { + /** + * Builder for [UpdateThreadRequest]. + */ + class Builder { + private var starred: Boolean? = null + private var unread: Boolean? = null + private var folders: List? = null + private var metadata: Map? = null + + /** + * Sets if all messages in the thread as starred or unstarred. + * @param starred If the thread is starred. + * @return The builder. + */ + fun starred(starred: Boolean) = apply { this.starred = starred } + + /** + * Sets if all messages in the thread as read or unread. + * @param unread If the thread is unread. + * @return The builder. + */ + fun unread(unread: Boolean) = apply { this.unread = unread } + + /** + * The IDs of the folders to apply, overwriting all previous folders for all messages in the thread. + * @param folders The folders the thread should appear in. + * @return The builder. + */ + fun folders(folders: List) = apply { this.folders = folders } + + /** + * Build the [UpdateThreadRequest]. + * @return The [UpdateThreadRequest]. + */ + fun build() = UpdateThreadRequest( + starred = starred, + unread = unread, + folders = folders, + ) + } +} diff --git a/src/main/kotlin/com/nylas/resources/Drafts.kt b/src/main/kotlin/com/nylas/resources/Drafts.kt new file mode 100644 index 00000000..4b90f87d --- /dev/null +++ b/src/main/kotlin/com/nylas/resources/Drafts.kt @@ -0,0 +1,86 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.util.FileUtils +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types + +class Drafts(client: NylasClient) : Resource(client, Draft::class.java) { + /** + * Return all Drafts + * @param identifier The identifier of the grant to act upon + * @param queryParams The query parameters to include in the request + * @return The list of Drafts + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + @JvmOverloads + fun list(identifier: String, queryParams: ListDraftsQueryParams? = null): ListResponse { + val path = String.format("v3/grants/%s/drafts", identifier) + return listResource(path, queryParams) + } + + /** + * Return a Draft + * @param identifier The identifier of the grant to act upon + * @param draftId The id of the Draft to retrieve. + * @return The Draft + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun find(identifier: String, draftId: String): Response { + val path = String.format("v3/grants/%s/drafts/%s", identifier, draftId) + return findResource(path) + } + + /** + * Create a Draft + * @param identifier The identifier of the grant to act upon + * @param requestBody The values to create the Draft with + * @return The updated Draft + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun create(identifier: String, requestBody: CreateDraftRequest): Response { + val path = String.format("v3/grants/%s/drafts", identifier) + + val attachmentLessPayload = requestBody.copy(attachments = null) + val serializedRequestBody = JsonHelper.moshi() + .adapter(CreateDraftRequest::class.java) + .toJson(attachmentLessPayload) + val multipart = FileUtils.buildFormRequest(requestBody, serializedRequestBody) + val responseType = Types.newParameterizedType(Response::class.java, Draft::class.java) + + return client.executeFormRequest(path, NylasClient.HttpMethod.POST, multipart, responseType) + } + + /** + * Update a Draft + * @param identifier The identifier of the grant to act upon + * @param draftId The id of the Draft to update. + * @param requestBody The values to update the Draft with + * @return The updated Draft + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun update(identifier: String, draftId: String, requestBody: UpdateDraftRequest): Response { + val path = String.format("v3/grants/%s/drafts/%s", identifier, draftId) + + val attachmentLessPayload = requestBody.copy(attachments = null) + val serializedRequestBody = JsonHelper.moshi() + .adapter(UpdateDraftRequest::class.java) + .toJson(attachmentLessPayload) + val multipart = FileUtils.buildFormRequest(requestBody, serializedRequestBody) + + return client.executeFormRequest(path, NylasClient.HttpMethod.PUT, multipart, Draft::class.java) + } + + /** + * Delete a Draft + * @param identifier The identifier of the grant to act upon + * @param draftId The id of the Draft to delete. + * @return The deletion response + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun destroy(identifier: String, draftId: String): DeleteResponse { + val path = String.format("v3/grants/%s/drafts/%s", identifier, draftId) + return destroyResource(path) + } +} diff --git a/src/main/kotlin/com/nylas/resources/Messages.kt b/src/main/kotlin/com/nylas/resources/Messages.kt new file mode 100644 index 00000000..46f3cac1 --- /dev/null +++ b/src/main/kotlin/com/nylas/resources/Messages.kt @@ -0,0 +1,126 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.util.FileUtils +import com.nylas.util.JsonHelper +import com.squareup.moshi.Types + +class Messages(client: NylasClient) : Resource(client, Message::class.java) { + /** + * Access the Smart Compose collection of endpoints + * @return The Smart Compose collection of endpoints + */ + fun smartCompose(): SmartCompose { + return SmartCompose(client) + } + + /** + * Return all Messages + * @param identifier The identifier of the grant to act upon + * @param queryParams The query parameters to include in the request + * @return The list of Messages + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + @JvmOverloads + fun list(identifier: String, queryParams: ListMessagesQueryParams? = null): ListResponse { + val path = String.format("v3/grants/%s/messages", identifier) + return listResource(path, queryParams) + } + + /** + * Return a Message + * @param identifier The identifier of the grant to act upon + * @param messageId The id of the Message to retrieve. + * @return The Message + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun find(identifier: String, messageId: String): Response { + val path = String.format("v3/grants/%s/messages/%s", identifier, messageId) + return findResource(path) + } + + /** + * Update a Message + * @param identifier The identifier of the grant to act upon + * @param messageId The id of the Message to update. + * @param requestBody The values to update the Message with + * @return The updated Message + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun update(identifier: String, messageId: String, requestBody: UpdateMessageRequest): Response { + val path = String.format("v3/grants/%s/messages/%s", identifier, messageId) + val adapter = JsonHelper.moshi().adapter(UpdateMessageRequest::class.java) + val serializedRequestBody = adapter.toJson(requestBody) + return updateResource(path, serializedRequestBody) + } + + /** + * Delete a Message + * @param identifier The identifier of the grant to act upon + * @param messageId The id of the Message to delete. + * @return The deletion response + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun destroy(identifier: String, messageId: String): DeleteResponse { + val path = String.format("v3/grants/%s/messages/%s", identifier, messageId) + return destroyResource(path) + } + + /** + * Send an email + * @param identifier The identifier of the grant to act upon + * @param requestBody The values to send the email with + * @return The sent email + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun send(identifier: String, requestBody: SendMessageRequest): Response { + val path = String.format("v3/grants/%s/messages/send", identifier) + + val attachmentLessPayload = requestBody.copy(attachments = null) + val serializedRequestBody = JsonHelper.moshi() + .adapter(SendMessageRequest::class.java) + .toJson(attachmentLessPayload) + val multipart = FileUtils.buildFormRequest(requestBody, serializedRequestBody) + + return client.executeFormRequest(path, NylasClient.HttpMethod.POST, multipart, Message::class.java) + } + + /** + * Retrieve your scheduled messages + * @param identifier The identifier of the grant to act upon + * @return The list of scheduled messages + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun listScheduledMessages(identifier: String): Response { + val path = String.format("v3/grants/%s/messages/schedules", identifier) + val responseType = Types.newParameterizedType(Response::class.java, ScheduledMessagesList::class.java) + return client.executeGet(path, responseType) + } + + /** + * Retrieve a scheduled message + * @param identifier The identifier of the grant to act upon + * @param scheduleId The id of the scheduled message to retrieve + * @return The scheduled message + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun findScheduledMessage(identifier: String, scheduleId: String): Response { + val path = String.format("v3/grants/%s/messages/schedules/%s", identifier, scheduleId) + val responseType = Types.newParameterizedType(Response::class.java, ScheduledMessage::class.java) + return client.executeGet(path, responseType) + } + + /** + * Stop a scheduled message + * @param identifier The identifier of the grant to act upon + * @param scheduleId The id of the scheduled message to stop + * @return The confirmation of the stopped scheduled message + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun stopScheduledMessage(identifier: String, scheduleId: String): Response { + val path = String.format("v3/grants/%s/messages/schedules/%s", identifier, scheduleId) + val responseType = Types.newParameterizedType(Response::class.java, StopScheduledMessageResponse::class.java) + return client.executeDelete(path, responseType) + } +} diff --git a/src/main/kotlin/com/nylas/resources/SmartCompose.kt b/src/main/kotlin/com/nylas/resources/SmartCompose.kt new file mode 100644 index 00000000..72787c37 --- /dev/null +++ b/src/main/kotlin/com/nylas/resources/SmartCompose.kt @@ -0,0 +1,49 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.ComposeMessageRequest +import com.nylas.models.ComposeMessageResponse +import com.nylas.models.Response +import com.nylas.util.JsonHelper + +/** + * A collection of Smart Compose related API endpoints. + * + * These endpoints allow for the generation of message suggestions. + * + * @param client The configured Nylas API client + */ +class SmartCompose(private val client: NylasClient) { + /** + * Compose a message + * @property identifier The identifier of the grant to act upon + * @property requestBody The prompt that smart compose will use to generate a message suggestion + * @return The generated message + */ + fun composeMessage(identifier: String, requestBody: ComposeMessageRequest): Response { + val path = "v3/grants/$identifier/messages/smart-compose" + + val serializedRequestBody = JsonHelper.moshi() + .adapter(ComposeMessageRequest::class.java) + .toJson(requestBody) + + return client.executePost(path, ComposeMessageResponse::class.java, serializedRequestBody) + } + + /** + * Compose a message reply + * @property identifier The identifier of the grant to act upon + * @property messageId The id of the message to reply to + * @property requestBody The prompt that smart compose will use to generate a reply suggestion + * @return The generated message reply + */ + fun composeMessageReply(identifier: String, messageId: String, requestBody: ComposeMessageRequest): Response { + val path = "v3/grants/$identifier/messages/$messageId/smart-compose" + + val serializedRequestBody = JsonHelper.moshi() + .adapter(ComposeMessageRequest::class.java) + .toJson(requestBody) + + return client.executePost(path, ComposeMessageResponse::class.java, serializedRequestBody) + } +} diff --git a/src/main/kotlin/com/nylas/resources/Threads.kt b/src/main/kotlin/com/nylas/resources/Threads.kt new file mode 100644 index 00000000..857fb1f1 --- /dev/null +++ b/src/main/kotlin/com/nylas/resources/Threads.kt @@ -0,0 +1,59 @@ +package com.nylas.resources + +import com.nylas.NylasClient +import com.nylas.models.* +import com.nylas.util.JsonHelper + +class Threads(client: NylasClient) : Resource(client, Thread::class.java) { + /** + * Return all Threads + * @param identifier The identifier of the grant to act upon + * @param queryParams The query parameters to include in the request + * @return The list of Threads + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + @JvmOverloads + fun list(identifier: String, queryParams: ListThreadsQueryParams? = null): ListResponse { + val path = String.format("v3/grants/%s/threads", identifier) + return listResource(path, queryParams) + } + + /** + * Return a Thread + * @param identifier The identifier of the grant to act upon + * @param threadId The id of the Thread to retrieve. + * @return The Thread + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun find(identifier: String, threadId: String): Response { + val path = String.format("v3/grants/%s/threads/%s", identifier, threadId) + return findResource(path) + } + + /** + * Update a Thread + * @param identifier The identifier of the grant to act upon + * @param threadId The id of the Thread to update. + * @param requestBody The values to update the Thread with + * @return The updated Thread + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun update(identifier: String, threadId: String, requestBody: UpdateThreadRequest): Response { + val path = String.format("v3/grants/%s/threads/%s", identifier, threadId) + val adapter = JsonHelper.moshi().adapter(UpdateThreadRequest::class.java) + val serializedRequestBody = adapter.toJson(requestBody) + return updateResource(path, serializedRequestBody) + } + + /** + * Delete a Thread + * @param identifier The identifier of the grant to act upon + * @param threadId The id of the Thread to delete. + * @return The deletion response + */ + @Throws(NylasApiError::class, NylasSdkTimeoutError::class) + fun destroy(identifier: String, threadId: String): DeleteResponse { + val path = String.format("v3/grants/%s/threads/%s", identifier, threadId) + return destroyResource(path) + } +} diff --git a/src/main/kotlin/com/nylas/util/FileUtils.kt b/src/main/kotlin/com/nylas/util/FileUtils.kt new file mode 100644 index 00000000..0c1c1d1b --- /dev/null +++ b/src/main/kotlin/com/nylas/util/FileUtils.kt @@ -0,0 +1,88 @@ +package com.nylas.util + +import com.nylas.models.CreateAttachmentRequest +import com.nylas.models.IMessageAttachmentRequest +import okhttp3.MediaType +import okhttp3.MultipartBody +import okhttp3.RequestBody +import okio.BufferedSink +import okio.source +import java.io.IOException +import java.io.InputStream +import java.nio.file.Files +import java.nio.file.Paths + +class FileUtils { + companion object { + /** + * Converts an [InputStream] into a streaming [RequestBody] for use with [okhttp3] requests. + * + * This method is optimized for memory efficiency by streaming data directly + * from the source [InputStream] to OkHttp's internal handling, without + * loading the entire content into memory first. + * + * @param contentType The media type of the request body. Defaults to `null`. + * @return A [RequestBody] that streams content directly from the provided [InputStream]. + * + * @throws IOException if there's an error while trying to access the content length. + * + * @suppress Not for public use. + */ + @JvmStatic + fun InputStream.toStreamingRequestBody(contentType: MediaType? = null): RequestBody { + return object : RequestBody() { + override fun contentType() = contentType + + override fun writeTo(sink: BufferedSink) { + source().use { source -> + sink.writeAll(source) + } + } + + override fun contentLength(): Long { + return try { + this@toStreamingRequestBody.available().toLong() + } catch (e: IOException) { + -1 + } + } + } + } + + /** + * Build the request to attach a file to a message/draft object. + * @param filePath The path to the file to attach. + * @return A [CreateAttachmentRequest] that will attach the file to the message/draft. + */ + @JvmStatic + fun attachFileRequestBuilder(filePath: String): CreateAttachmentRequest { + val path = Paths.get(filePath) + val filename = path.fileName.toString() + val contentType = Files.probeContentType(path) ?: "application/octet-stream" + val content = Files.newInputStream(path) + val size = Files.size(path) + + return CreateAttachmentRequest( + filename = filename, + contentType = contentType, + size = size.toInt(), + content = content, + ) + } + + @JvmStatic + fun buildFormRequest(requestBody: T, attachmentLessPayload: String): MultipartBody { + val multipartBuilder = MultipartBody.Builder().setType(MultipartBody.FORM) + multipartBuilder.addFormDataPart("message", attachmentLessPayload) + + // Add a separate form field for each attachment + requestBody.attachments?.forEachIndexed { index, attachment -> + val contentType = MediaType.parse(attachment.contentType) + val contentBody = attachment.content.toStreamingRequestBody(contentType) + multipartBuilder.addFormDataPart("file$index", attachment.filename, contentBody) + } + + return multipartBuilder.build() + } + } +} diff --git a/src/main/kotlin/com/nylas/util/IMessageAdapter.kt b/src/main/kotlin/com/nylas/util/IMessageAdapter.kt new file mode 100644 index 00000000..e051c976 --- /dev/null +++ b/src/main/kotlin/com/nylas/util/IMessageAdapter.kt @@ -0,0 +1,39 @@ +package com.nylas.util + +import com.nylas.models.Draft +import com.nylas.models.IMessage +import com.nylas.models.Message +import com.squareup.moshi.* + +/** + * This class is used to serialize and deserialize the IMessage interface. + * @suppress Not for public use. + */ +class IMessageAdapter { + @FromJson + fun fromJson( + reader: JsonReader, + messageAdapter: JsonAdapter, + draftAdapter: JsonAdapter, + ): IMessage? { + val map = reader.readJsonValue() as? Map<*, *> ?: return null + return when { + map.containsKey("object") && map["object"]?.equals("drafts") == true -> draftAdapter.fromJsonValue(map) + else -> messageAdapter.fromJsonValue(map) + } + } + + @ToJson + fun toJson( + writer: JsonWriter, + value: IMessage?, + messageAdapter: JsonAdapter, + draftAdapter: JsonAdapter, + ) { + when (value) { + is Message -> messageAdapter.toJson(writer, value) + is Draft -> draftAdapter.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 7ed1ece9..fc10060d 100644 --- a/src/main/kotlin/com/nylas/util/JsonHelper.kt +++ b/src/main/kotlin/com/nylas/util/JsonHelper.kt @@ -28,6 +28,8 @@ class JsonHelper { .add(UpdateConferencingAdapter()) .add(CreateWhenAdapter()) .add(UpdateWhenAdapter()) + .add(CreateWhenAdapter()) + .add(IMessageAdapter()) .add(CreateConnectorAdapter()) .add(CredentialDataAdapter()) .add(MicrosoftAdminConsentCredentialDataAdapter())