diff --git a/src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt b/src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt index dd6d570e..383de3a8 100644 --- a/src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateAttachmentRequest.kt @@ -20,7 +20,7 @@ class CreateAttachmentRequest( /** * The content of the attachment. */ - @Transient + @Json(name = "content") val content: InputStream, /** * The size of the attachment in bytes. diff --git a/src/main/kotlin/com/nylas/models/CreateDraftRequest.kt b/src/main/kotlin/com/nylas/models/CreateDraftRequest.kt index 4eea7194..bd0e8daa 100644 --- a/src/main/kotlin/com/nylas/models/CreateDraftRequest.kt +++ b/src/main/kotlin/com/nylas/models/CreateDraftRequest.kt @@ -29,7 +29,7 @@ data class CreateDraftRequest( /** * An array of files to attach to the message. */ - @Transient + @Json(name = "attachments") override val attachments: List? = null, /** * The message subject. diff --git a/src/main/kotlin/com/nylas/models/SendMessageRequest.kt b/src/main/kotlin/com/nylas/models/SendMessageRequest.kt index fee41f45..7c326830 100644 --- a/src/main/kotlin/com/nylas/models/SendMessageRequest.kt +++ b/src/main/kotlin/com/nylas/models/SendMessageRequest.kt @@ -29,7 +29,7 @@ data class SendMessageRequest( /** * An array of files to attach to the message. */ - @Transient + @Json(name = "attachments") override val attachments: List? = null, /** * The message subject. diff --git a/src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt b/src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt index 17cd495d..e49a4ef2 100644 --- a/src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt +++ b/src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt @@ -29,7 +29,7 @@ data class UpdateDraftRequest( /** * An array of files to attach to the message. */ - @Transient + @Json(name = "attachments") override val attachments: List? = null, /** * The message subject. diff --git a/src/main/kotlin/com/nylas/util/CreateAttachmentRequestAdapter.kt b/src/main/kotlin/com/nylas/util/CreateAttachmentRequestAdapter.kt new file mode 100644 index 00000000..03be7eb8 --- /dev/null +++ b/src/main/kotlin/com/nylas/util/CreateAttachmentRequestAdapter.kt @@ -0,0 +1,40 @@ +package com.nylas.util + +import com.nylas.models.CreateAttachmentRequest +import com.squareup.moshi.FromJson +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.ToJson + +/** + * This class is used to serialize and deserialize the CreateAttachmentRequest class. + * @suppress Not for public use. + */ +class CreateAttachmentRequestAdapter { + @ToJson + @Throws(UnsupportedOperationException::class) + fun toJson(writer: JsonWriter, value: CreateAttachmentRequest?) { + writer.beginObject() + writer.name("filename").value(value?.filename) + writer.name("content_type").value(value?.contentType) + writer.name("size").value(value?.size) + value?.isInline?.let { writer.name("is_inline").value(it) } + value?.contentId?.let { writer.name("content_id").value(it) } + value?.contentDisposition?.let { writer.name("content_disposition").value(it) } + + // Encode the file stream to base64 + value?.content?.let { + val base64Content = it.use { stream -> + FileUtils.encodeStreamToBase64(stream) + } + writer.name("content").value(base64Content) + } + + writer.endObject() + } + + @FromJson + @Throws(UnsupportedOperationException::class) + fun fromJson(reader: com.squareup.moshi.JsonReader): CreateAttachmentRequest? { + throw UnsupportedOperationException("Deserialization not supported") + } +} diff --git a/src/main/kotlin/com/nylas/util/FileUtils.kt b/src/main/kotlin/com/nylas/util/FileUtils.kt index 485c2299..d59f06f1 100644 --- a/src/main/kotlin/com/nylas/util/FileUtils.kt +++ b/src/main/kotlin/com/nylas/util/FileUtils.kt @@ -7,10 +7,12 @@ import okhttp3.MultipartBody import okhttp3.RequestBody import okio.BufferedSink import okio.source +import java.io.ByteArrayOutputStream import java.io.IOException import java.io.InputStream import java.nio.file.Files import java.nio.file.Paths +import java.util.* class FileUtils { companion object { @@ -90,5 +92,16 @@ class FileUtils { return multipartBuilder.build() } + + /** + * Encode an [InputStream] to a base64 string. + * @param inputStream The input stream to encode. + * @return The base64 encoded string. + */ + fun encodeStreamToBase64(inputStream: InputStream): String { + val buffer = ByteArrayOutputStream() + inputStream.copyTo(buffer) + return Base64.getEncoder().encodeToString(buffer.toByteArray()) + } } } diff --git a/src/main/kotlin/com/nylas/util/JsonHelper.kt b/src/main/kotlin/com/nylas/util/JsonHelper.kt index 4f3646b5..cf49c358 100644 --- a/src/main/kotlin/com/nylas/util/JsonHelper.kt +++ b/src/main/kotlin/com/nylas/util/JsonHelper.kt @@ -35,6 +35,7 @@ class JsonHelper { .add(IMessageAdapter()) .add(UpdateConnectorAdapter()) .add(CredentialDataAdapter()) + .add(CreateAttachmentRequestAdapter()) .add(MicrosoftAdminConsentCredentialDataAdapter()) .add(GoogleServiceAccountCredentialDataAdapter()) .add(ConnectorOverrideCredentialDataAdapter()) diff --git a/src/test/kotlin/com/nylas/resources/DraftsTests.kt b/src/test/kotlin/com/nylas/resources/DraftsTests.kt index eb774273..1f440116 100644 --- a/src/test/kotlin/com/nylas/resources/DraftsTests.kt +++ b/src/test/kotlin/com/nylas/resources/DraftsTests.kt @@ -258,6 +258,7 @@ class DraftsTests { fun `creating a draft with small attachment calls requests with the correct params`() { val adapter = JsonHelper.moshi().adapter(CreateDraftRequest::class.java) val testInputStream = ByteArrayInputStream("test data".toByteArray()) + val testInputStreamCopy = ByteArrayInputStream("test data".toByteArray()) val createDraftRequest = CreateDraftRequest( body = "Hello, I just sent a message using Nylas!", @@ -278,6 +279,16 @@ class DraftsTests { ), customHeaders = listOf(CustomHeader(name = "header", value = "value")), ) + val expectedRequest = createDraftRequest.copy( + attachments = listOf( + CreateAttachmentRequest( + content = testInputStreamCopy, + contentType = "text/plain", + filename = "attachment.txt", + size = 100, + ), + ), + ) drafts.create(grantId, createDraftRequest) @@ -296,7 +307,7 @@ class DraftsTests { assertEquals("v3/grants/$grantId/drafts", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(Response::class.java, Draft::class.java), typeCaptor.firstValue) - assertEquals(adapter.toJson(createDraftRequest), requestBodyCaptor.firstValue) + assertEquals(adapter.toJson(expectedRequest), requestBodyCaptor.firstValue) assertNull(queryParamCaptor.firstValue) } @@ -324,6 +335,7 @@ class DraftsTests { ), customHeaders = listOf(CustomHeader(name = "header", value = "value")), ) + val attachmentLessRequest = createDraftRequest.copy(attachments = null) drafts.create(grantId, createDraftRequest) @@ -352,7 +364,7 @@ class DraftsTests { val fileBuffer = Buffer() multipart.part(0).body().writeTo(buffer) multipart.part(1).body().writeTo(fileBuffer) - assertEquals(adapter.toJson(createDraftRequest), buffer.readUtf8()) + assertEquals(adapter.toJson(attachmentLessRequest), buffer.readUtf8()) assertEquals("test data", fileBuffer.readUtf8()) } @@ -394,6 +406,7 @@ class DraftsTests { val draftId = "draft-123" val adapter = JsonHelper.moshi().adapter(UpdateDraftRequest::class.java) val testInputStream = ByteArrayInputStream("test data".toByteArray()) + val testInputStreamCopy = ByteArrayInputStream("test data".toByteArray()) val updateDraftRequest = UpdateDraftRequest( body = "Hello, I just sent a message using Nylas!", @@ -409,6 +422,16 @@ class DraftsTests { ), ), ) + val expectedRequest = updateDraftRequest.copy( + attachments = listOf( + CreateAttachmentRequest( + content = testInputStreamCopy, + contentType = "text/plain", + filename = "attachment.txt", + size = 100, + ), + ), + ) drafts.update(grantId, draftId, updateDraftRequest) @@ -427,7 +450,7 @@ class DraftsTests { assertEquals("v3/grants/$grantId/drafts/$draftId", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(Response::class.java, Draft::class.java), typeCaptor.firstValue) - assertEquals(adapter.toJson(updateDraftRequest), requestBodyCaptor.firstValue) + assertEquals(adapter.toJson(expectedRequest), requestBodyCaptor.firstValue) assertNull(queryParamCaptor.firstValue) } @@ -451,6 +474,7 @@ class DraftsTests { ), ), ) + val attachmentLessRequest = updateDraftRequest.copy(attachments = null) drafts.update(grantId, draftId, updateDraftRequest) @@ -479,7 +503,7 @@ class DraftsTests { val fileBuffer = Buffer() multipart.part(0).body().writeTo(buffer) multipart.part(1).body().writeTo(fileBuffer) - assertEquals(adapter.toJson(updateDraftRequest), buffer.readUtf8()) + assertEquals(adapter.toJson(attachmentLessRequest), buffer.readUtf8()) assertEquals("test data", fileBuffer.readUtf8()) } diff --git a/src/test/kotlin/com/nylas/resources/MessagesTests.kt b/src/test/kotlin/com/nylas/resources/MessagesTests.kt index f98e27dc..1250e611 100644 --- a/src/test/kotlin/com/nylas/resources/MessagesTests.kt +++ b/src/test/kotlin/com/nylas/resources/MessagesTests.kt @@ -412,6 +412,7 @@ class MessagesTests { fun `sending a message with a small attachment calls requests with the correct params`() { val adapter = JsonHelper.moshi().adapter(SendMessageRequest::class.java) val testInputStream = ByteArrayInputStream("test data".toByteArray()) + val testInputStreamCopy = ByteArrayInputStream("test data".toByteArray()) val sendMessageRequest = SendMessageRequest( to = listOf(EmailName(email = "test@gmail.com", name = "Test")), @@ -433,6 +434,16 @@ class MessagesTests { ), customHeaders = listOf(CustomHeader(name = "header-name", value = "header-value")), ) + val expectedRequestBody = sendMessageRequest.copy( + attachments = listOf( + CreateAttachmentRequest( + content = testInputStreamCopy, + contentType = "text/plain", + filename = "attachment.txt", + size = 100, + ), + ), + ) messages.send(grantId, sendMessageRequest) @@ -451,7 +462,7 @@ class MessagesTests { assertEquals("v3/grants/$grantId/messages/send", pathCaptor.firstValue) assertEquals(Types.newParameterizedType(Response::class.java, Message::class.java), typeCaptor.firstValue) - assertEquals(adapter.toJson(sendMessageRequest), requestBodyCaptor.firstValue) + assertEquals(adapter.toJson(expectedRequestBody), requestBodyCaptor.firstValue) assertNull(queryParamCaptor.firstValue) } @@ -480,6 +491,7 @@ class MessagesTests { ), customHeaders = listOf(CustomHeader(name = "header-name", value = "header-value")), ) + val attachmentLessRequest = sendMessageRequest.copy(attachments = null) messages.send(grantId, sendMessageRequest) @@ -508,7 +520,7 @@ class MessagesTests { val fileBuffer = Buffer() multipart.part(0).body().writeTo(buffer) multipart.part(1).body().writeTo(fileBuffer) - assertEquals(adapter.toJson(sendMessageRequest), buffer.readUtf8()) + assertEquals(adapter.toJson(attachmentLessRequest), buffer.readUtf8()) assertEquals("test data", fileBuffer.readUtf8()) } }