Skip to content

Commit d174f6c

Browse files
committed
feat: add is_plaintext support to SendMessageRequest and CreateDraftRequest
1 parent 72dada5 commit d174f6c

File tree

7 files changed

+288
-2
lines changed

7 files changed

+288
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## [Unreleased]
44

5+
### Added
6+
* `is_plaintext` support in `SendMessageRequest` and `CreateDraftRequest` to control plain text vs HTML message formatting
7+
58
### Changed
69
* `SendMessageRequest.sendAt` field changed from `Int?` to `Long?` to support Unix timestamps beyond 2038. Maintains backward compatibility through method overloading - existing `Int` parameters are automatically converted to `Long`. **Note:** Kotlin users passing integer literals may need to specify type explicitly (e.g., `1620000000L` or `1620000000 as Int`).
710

examples/src/main/java/com/nylas/examples/MessagesExample.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.nylas.examples;
22

33
import com.nylas.NylasClient;
4+
import com.nylas.models.EmailName;
45
import com.nylas.models.FindMessageQueryParams;
56
import com.nylas.models.ListMessagesQueryParams;
67
import com.nylas.models.ListResponse;
@@ -9,6 +10,7 @@
910
import com.nylas.models.NylasApiError;
1011
import com.nylas.models.NylasSdkTimeoutError;
1112
import com.nylas.models.Response;
13+
import com.nylas.models.SendMessageRequest;
1214
import com.nylas.models.TrackingOptions;
1315
import okhttp3.OkHttpClient;
1416

@@ -134,6 +136,9 @@ private static void runMessagesExample(NylasClient nylas, Map<String, String> co
134136

135137
// 5. Find a specific message with different field options
136138
demonstrateMessageFinding(nylas, grantId);
139+
140+
// 6. Demonstrate is_plaintext feature for sending messages
141+
demonstratePlaintextFeature(nylas, grantId, config);
137142
}
138143

139144
private static void demonstrateStandardMessageListing(NylasClient nylas, String grantId) throws NylasApiError, NylasSdkTimeoutError {
@@ -301,4 +306,60 @@ private static void printMessageDetails(Message message, String requestType) {
301306
System.out.println(" Raw MIME length: " + message.getRawMime().length() + " characters");
302307
}
303308
}
309+
310+
private static void demonstratePlaintextFeature(NylasClient nylas, String grantId, Map<String, String> config) throws NylasApiError, NylasSdkTimeoutError {
311+
System.out.println("📝 6. Demonstrating is_plaintext feature for sending messages (NEW FEATURE):");
312+
313+
String recipientEmail = config.get("NYLAS_RECIPIENT_EMAIL");
314+
if (recipientEmail == null) {
315+
System.out.println(" ⚠️ Skipping send examples - NYLAS_RECIPIENT_EMAIL not configured");
316+
System.out.println(" To enable this demo, set NYLAS_RECIPIENT_EMAIL in your .env file");
317+
return;
318+
}
319+
320+
System.out.println(" Sending test messages to: " + recipientEmail);
321+
322+
// 1. Send HTML message (default behavior)
323+
System.out.println("\n 📧 Sending HTML message (is_plaintext = false or not specified):");
324+
325+
SendMessageRequest htmlRequest = new SendMessageRequest.Builder(
326+
Arrays.asList(new EmailName(recipientEmail, "Test Recipient"))
327+
)
328+
.subject("HTML Message Test - Nylas SDK")
329+
.body("<html><body><h1>Hello from Nylas!</h1><p>This is an <b>HTML</b> message with <i>formatting</i>.</p></body></html>")
330+
.isPlaintext(false) // Explicitly set to false (this is also the default)
331+
.build();
332+
333+
try {
334+
Response<Message> htmlResponse = nylas.messages().send(grantId, htmlRequest);
335+
System.out.println(" ✅ HTML message sent successfully");
336+
System.out.println(" Message ID: " + htmlResponse.getData().getId());
337+
} catch (Exception e) {
338+
System.out.println(" ❌ Failed to send HTML message: " + e.getMessage());
339+
}
340+
341+
// 2. Send plain text message using is_plaintext feature
342+
System.out.println("\n 📄 Sending plain text message (is_plaintext = true):");
343+
344+
SendMessageRequest plaintextRequest = new SendMessageRequest.Builder(
345+
Arrays.asList(new EmailName(recipientEmail, "Test Recipient"))
346+
)
347+
.subject("Plain Text Message Test - Nylas SDK")
348+
.body("Hello from Nylas!\n\nThis is a PLAIN TEXT message.\nNo HTML formatting will be applied.\n\nBest regards,\nNylas SDK")
349+
.isPlaintext(true) // NEW FEATURE: Force plain text mode
350+
.build();
351+
352+
try {
353+
Response<Message> plaintextResponse = nylas.messages().send(grantId, plaintextRequest);
354+
System.out.println(" ✅ Plain text message sent successfully");
355+
System.out.println(" Message ID: " + plaintextResponse.getData().getId());
356+
} catch (Exception e) {
357+
System.out.println(" ❌ Failed to send plain text message: " + e.getMessage());
358+
}
359+
360+
System.out.println("\n 💡 Key differences:");
361+
System.out.println(" - HTML message (is_plaintext=false): Message body is sent as HTML with MIME formatting");
362+
System.out.println(" - Plain text message (is_plaintext=true): Message body is sent as plain text, no HTML in MIME data");
363+
System.out.println(" - Default behavior: is_plaintext=false (HTML formatting)");
364+
}
304365
}

examples/src/main/kotlin/com/nylas/examples/KotlinMessagesExample.kt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ private fun runMessagesExample(nylas: NylasClient, config: Map<String, String>)
117117

118118
// 5. Find a specific message with different field options
119119
demonstrateMessageFinding(nylas, grantId)
120+
121+
// 6. Demonstrate is_plaintext feature for sending messages
122+
demonstratePlaintextFeature(nylas, grantId, config)
120123
}
121124

122125
private fun demonstrateStandardMessageListing(nylas: NylasClient, grantId: String) {
@@ -277,4 +280,60 @@ private fun printMessageDetails(message: Message, requestType: String) {
277280
message.rawMime?.let { rawMime ->
278281
println(" Raw MIME length: ${rawMime.length} characters")
279282
}
283+
}
284+
285+
private fun demonstratePlaintextFeature(nylas: NylasClient, grantId: String, config: Map<String, String>) {
286+
println("📝 6. Demonstrating is_plaintext feature for sending messages (NEW FEATURE):")
287+
288+
val recipientEmail = config["NYLAS_RECIPIENT_EMAIL"]
289+
if (recipientEmail == null) {
290+
println(" ⚠️ Skipping send examples - NYLAS_RECIPIENT_EMAIL not configured")
291+
println(" To enable this demo, set NYLAS_RECIPIENT_EMAIL in your .env file")
292+
return
293+
}
294+
295+
println(" Sending test messages to: $recipientEmail")
296+
297+
// 1. Send HTML message (default behavior)
298+
println("\n 📧 Sending HTML message (is_plaintext = false or not specified):")
299+
300+
val htmlRequest = SendMessageRequest.Builder(
301+
listOf(EmailName(recipientEmail, "Test Recipient"))
302+
)
303+
.subject("HTML Message Test - Nylas SDK")
304+
.body("<html><body><h1>Hello from Nylas!</h1><p>This is an <b>HTML</b> message with <i>formatting</i>.</p></body></html>")
305+
.isPlaintext(false) // Explicitly set to false (this is also the default)
306+
.build()
307+
308+
try {
309+
val htmlResponse = nylas.messages().send(grantId, htmlRequest)
310+
println(" ✅ HTML message sent successfully")
311+
println(" Message ID: ${htmlResponse.data.id}")
312+
} catch (e: Exception) {
313+
println(" ❌ Failed to send HTML message: ${e.message}")
314+
}
315+
316+
// 2. Send plain text message using is_plaintext feature
317+
println("\n 📄 Sending plain text message (is_plaintext = true):")
318+
319+
val plaintextRequest = SendMessageRequest.Builder(
320+
listOf(EmailName(recipientEmail, "Test Recipient"))
321+
)
322+
.subject("Plain Text Message Test - Nylas SDK")
323+
.body("Hello from Nylas!\n\nThis is a PLAIN TEXT message.\nNo HTML formatting will be applied.\n\nBest regards,\nNylas SDK")
324+
.isPlaintext(true) // NEW FEATURE: Force plain text mode
325+
.build()
326+
327+
try {
328+
val plaintextResponse = nylas.messages().send(grantId, plaintextRequest)
329+
println(" ✅ Plain text message sent successfully")
330+
println(" Message ID: ${plaintextResponse.data.id}")
331+
} catch (e: Exception) {
332+
println(" ❌ Failed to send plain text message: ${e.message}")
333+
}
334+
335+
println("\n 💡 Key differences:")
336+
println(" - HTML message (is_plaintext=false): Message body is sent as HTML with MIME formatting")
337+
println(" - Plain text message (is_plaintext=true): Message body is sent as plain text, no HTML in MIME data")
338+
println(" - Default behavior: is_plaintext=false (HTML formatting)")
280339
}

src/main/kotlin/com/nylas/models/CreateDraftRequest.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ data class CreateDraftRequest(
7272
*/
7373
@Json(name = "custom_headers")
7474
val customHeaders: List<CustomHeader>? = null,
75+
/**
76+
* When true, the message body is sent as plain text and the MIME data doesn't include the HTML version of the message.
77+
* When false, the message body is sent as HTML.
78+
*/
79+
@Json(name = "is_plaintext")
80+
val isPlaintext: Boolean? = null,
7581
) : IMessageAttachmentRequest {
7682
/**
7783
* Builder for [CreateDraftRequest].
@@ -90,6 +96,7 @@ data class CreateDraftRequest(
9096
private var replyToMessageId: String? = null
9197
private var trackingOptions: TrackingOptions? = null
9298
private var customHeaders: List<CustomHeader>? = null
99+
private var isPlaintext: Boolean? = null
93100

94101
/**
95102
* Sets the from address.
@@ -184,8 +191,17 @@ data class CreateDraftRequest(
184191
fun customHeaders(customHeaders: List<CustomHeader>?) = apply { this.customHeaders = customHeaders }
185192

186193
/**
187-
* Builds a [SendMessageRequest] instance.
188-
* @return The [SendMessageRequest] instance.
194+
* Sets whether the message body is sent as plain text.
195+
* When true, the message body is sent as plain text and the MIME data doesn't include the HTML version of the message.
196+
* When false, the message body is sent as HTML.
197+
* @param isPlaintext Whether the message body is sent as plain text.
198+
* @return The builder.
199+
*/
200+
fun isPlaintext(isPlaintext: Boolean?) = apply { this.isPlaintext = isPlaintext }
201+
202+
/**
203+
* Builds a [CreateDraftRequest] instance.
204+
* @return The [CreateDraftRequest] instance.
189205
*/
190206
fun build() =
191207
CreateDraftRequest(
@@ -202,6 +218,7 @@ data class CreateDraftRequest(
202218
replyToMessageId,
203219
trackingOptions,
204220
customHeaders,
221+
isPlaintext,
205222
)
206223
}
207224
}

src/main/kotlin/com/nylas/models/SendMessageRequest.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ data class SendMessageRequest(
7878
*/
7979
@Json(name = "custom_headers")
8080
val customHeaders: List<CustomHeader>? = null,
81+
/**
82+
* When true, the message body is sent as plain text and the MIME data doesn't include the HTML version of the message.
83+
* When false, the message body is sent as HTML.
84+
*/
85+
@Json(name = "is_plaintext")
86+
val isPlaintext: Boolean? = null,
8187
) : IMessageAttachmentRequest {
8288
/**
8389
* Builder for [SendMessageRequest].
@@ -99,6 +105,7 @@ data class SendMessageRequest(
99105
private var trackingOptions: TrackingOptions? = null
100106
private var useDraft: Boolean? = null
101107
private var customHeaders: List<CustomHeader>? = null
108+
private var isPlaintext: Boolean? = null
102109

103110
/**
104111
* Sets the bcc recipients.
@@ -200,6 +207,15 @@ data class SendMessageRequest(
200207
*/
201208
fun customHeaders(customHeaders: List<CustomHeader>?) = apply { this.customHeaders = customHeaders }
202209

210+
/**
211+
* Sets whether the message body is sent as plain text.
212+
* When true, the message body is sent as plain text and the MIME data doesn't include the HTML version of the message.
213+
* When false, the message body is sent as HTML.
214+
* @param isPlaintext Whether the message body is sent as plain text.
215+
* @return The builder.
216+
*/
217+
fun isPlaintext(isPlaintext: Boolean?) = apply { this.isPlaintext = isPlaintext }
218+
203219
/**
204220
* Builds a [SendMessageRequest] instance.
205221
* @return The [SendMessageRequest] instance.
@@ -220,6 +236,7 @@ data class SendMessageRequest(
220236
trackingOptions,
221237
useDraft,
222238
customHeaders,
239+
isPlaintext,
223240
)
224241
}
225242
}

src/test/kotlin/com/nylas/resources/DraftsTests.kt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ import java.io.ByteArrayInputStream
1919
import java.lang.reflect.Type
2020
import kotlin.test.Test
2121
import kotlin.test.assertEquals
22+
import kotlin.test.assertFalse
2223
import kotlin.test.assertIs
2324
import kotlin.test.assertNull
25+
import kotlin.test.assertTrue
2426

2527
class DraftsTests {
2628
private val mockHttpClient: OkHttpClient = Mockito.mock(OkHttpClient::class.java)
@@ -254,6 +256,67 @@ class DraftsTests {
254256
assertNull(queryParamCaptor.firstValue)
255257
}
256258

259+
@Test
260+
fun `creating a draft with is_plaintext true serializes correctly`() {
261+
val adapter = JsonHelper.moshi().adapter(CreateDraftRequest::class.java)
262+
val createDraftRequest =
263+
CreateDraftRequest(
264+
body = "Hello, I just created a draft using Nylas!",
265+
subject = "Hello from Nylas!",
266+
isPlaintext = true,
267+
)
268+
269+
val serializedRequest = adapter.toJson(createDraftRequest)
270+
assertTrue(serializedRequest.contains("\"is_plaintext\":true"))
271+
272+
drafts.create(grantId, createDraftRequest)
273+
274+
val pathCaptor = argumentCaptor<String>()
275+
val typeCaptor = argumentCaptor<Type>()
276+
val requestBodyCaptor = argumentCaptor<String>()
277+
val queryParamCaptor = argumentCaptor<IQueryParams>()
278+
val overrideParamCaptor = argumentCaptor<RequestOverrides>()
279+
verify(mockNylasClient).executePost<Response<Draft>>(
280+
pathCaptor.capture(),
281+
typeCaptor.capture(),
282+
requestBodyCaptor.capture(),
283+
queryParamCaptor.capture(),
284+
overrideParamCaptor.capture(),
285+
)
286+
287+
assertEquals("v3/grants/$grantId/drafts", pathCaptor.firstValue)
288+
assertEquals(Types.newParameterizedType(Response::class.java, Draft::class.java), typeCaptor.firstValue)
289+
assertEquals(adapter.toJson(createDraftRequest), requestBodyCaptor.firstValue)
290+
assertNull(queryParamCaptor.firstValue)
291+
}
292+
293+
@Test
294+
fun `creating a draft with is_plaintext false or not specified defaults correctly`() {
295+
val adapter = JsonHelper.moshi().adapter(CreateDraftRequest::class.java)
296+
297+
// Test with explicit false
298+
val createDraftRequestFalse =
299+
CreateDraftRequest(
300+
body = "Hello, I just created a draft using Nylas!",
301+
subject = "Hello from Nylas!",
302+
isPlaintext = false,
303+
)
304+
305+
val serializedRequestFalse = adapter.toJson(createDraftRequestFalse)
306+
assertTrue(serializedRequestFalse.contains("\"is_plaintext\":false"))
307+
308+
// Test with not specified (should default to false)
309+
val createDraftRequestDefault =
310+
CreateDraftRequest(
311+
body = "Hello, I just created a draft using Nylas!",
312+
subject = "Hello from Nylas!",
313+
)
314+
315+
val serializedRequestDefault = adapter.toJson(createDraftRequestDefault)
316+
// When null/not specified, the field should not be included in JSON or be false
317+
assertFalse(serializedRequestDefault.contains("\"is_plaintext\":true"))
318+
}
319+
257320
@Test
258321
fun `creating a draft with small attachment calls requests with the correct params`() {
259322
val adapter = JsonHelper.moshi().adapter(CreateDraftRequest::class.java)

0 commit comments

Comments
 (0)