Skip to content

Commit

Permalink
Fixed issue where attachments < 3mb were not being encoded correctly (#…
Browse files Browse the repository at this point in the history
…227)

# Description
This PR fixes an issue where we were not encoding attachments less than
3mb properly.

# License
<!-- Your PR comment must contain the following line for us to merge the
PR. -->
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.
  • Loading branch information
mrashed-dev authored Apr 30, 2024
1 parent 6d4f58d commit 867ac11
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/nylas/models/CreateDraftRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ data class CreateDraftRequest(
/**
* An array of files to attach to the message.
*/
@Transient
@Json(name = "attachments")
override val attachments: List<CreateAttachmentRequest>? = null,
/**
* The message subject.
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/nylas/models/SendMessageRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ data class SendMessageRequest(
/**
* An array of files to attach to the message.
*/
@Transient
@Json(name = "attachments")
override val attachments: List<CreateAttachmentRequest>? = null,
/**
* The message subject.
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/nylas/models/UpdateDraftRequest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ data class UpdateDraftRequest(
/**
* An array of files to attach to the message.
*/
@Transient
@Json(name = "attachments")
override val attachments: List<CreateAttachmentRequest>? = null,
/**
* The message subject.
Expand Down
40 changes: 40 additions & 0 deletions src/main/kotlin/com/nylas/util/CreateAttachmentRequestAdapter.kt
Original file line number Diff line number Diff line change
@@ -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")
}
}
13 changes: 13 additions & 0 deletions src/main/kotlin/com/nylas/util/FileUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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())
}
}
}
1 change: 1 addition & 0 deletions src/main/kotlin/com/nylas/util/JsonHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class JsonHelper {
.add(IMessageAdapter())
.add(UpdateConnectorAdapter())
.add(CredentialDataAdapter())
.add(CreateAttachmentRequestAdapter())
.add(MicrosoftAdminConsentCredentialDataAdapter())
.add(GoogleServiceAccountCredentialDataAdapter())
.add(ConnectorOverrideCredentialDataAdapter())
Expand Down
32 changes: 28 additions & 4 deletions src/test/kotlin/com/nylas/resources/DraftsTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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!",
Expand All @@ -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)

Expand All @@ -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)
}

Expand Down Expand Up @@ -324,6 +335,7 @@ class DraftsTests {
),
customHeaders = listOf(CustomHeader(name = "header", value = "value")),
)
val attachmentLessRequest = createDraftRequest.copy(attachments = null)

drafts.create(grantId, createDraftRequest)

Expand Down Expand Up @@ -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())
}

Expand Down Expand Up @@ -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!",
Expand All @@ -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)

Expand All @@ -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)
}

Expand All @@ -451,6 +474,7 @@ class DraftsTests {
),
),
)
val attachmentLessRequest = updateDraftRequest.copy(attachments = null)

drafts.update(grantId, draftId, updateDraftRequest)

Expand Down Expand Up @@ -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())
}

Expand Down
16 changes: 14 additions & 2 deletions src/test/kotlin/com/nylas/resources/MessagesTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "[email protected]", name = "Test")),
Expand All @@ -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)

Expand All @@ -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)
}

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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())
}
}
Expand Down

0 comments on commit 867ac11

Please sign in to comment.