Enviando emails con SendGrid y Kotlin

0
2457

Índice de contenidos

  1. Introducción
  2. Registrarse en SendGrid
  3. Inicialización del proyecto y envío de nuestro primer email
  4. Envío de email con datos adjuntos
  5. Envío de emails usando plantillas dinámicas
  6. Envío de emails personalizados
  7. Añadir ajustes a nuestro email
  8. Envío automático de emails
  9. Conclusiones

Introducción

En muchas ocasiones, tenemos la necesidad de poder enviar emails a nuestros usuarios. 

Y no sólo enviar emails, automatizarlos, personalizarlos para cada usuario, tener diferentes plantillas de email para cada ocasión, campañas de marketing, etc. 

Y si estamos hablando de una aplicación con 1.000, 10.000 o 100.000 usuarios, este proceso se complica bastante, y sería imposible hacerlo a mano, por lo que tenemos que automatizarlo en código, y aquí es donde entra en juego SendGrid, que nos ofrece una API, con todo lo necesario para implementar nuestro propio servicio de email personalizado. En este tutorial vamos a ver un ejemplo de implementación en Kotlin, así que vamos allá.

Registrarse en SendGrid

En primer lugar para poder utilizar el servicio es necesario crear una cuenta en www.sendgrid.com, a pesar de que este servicio es parte de la empresa Twillio, no nos servirá tener una cuenta de Twillio para acceder, por lo que tendremos que crearnos una nueva.

No es necesario pagar para empezar a realizar pruebas, el plan gratuito incluye 100 email al día, y no os preocupéis por las pruebas que hagáis, dado que SendGrid, nos ofrece un Sandbox Mode para poder realizar todas las pruebas necesarias para verificar el funcionamiento del envío del email sin gastar nuestros 100 emails diarios.

Una vez tengamos nuestra cuenta creada lo primero que veremos será la consola web:

Consola web Sendgrid

Inicialización del proyecto y envío de nuestro primer email

Para este ejemplo de implementación vamos a utilizar Kotlin y Maven e IntelliJ. Y la V3 de SendGrid.

Pero antes de comenzar, necesitaremos crear una API KEY, para poder usar los servicios. Para ello nos dirigimos a la consola y en el menú lateral de la izquierda veremos una pestaña llamada Settings y dentro de ella hacemos clic en API Keys:

Una vez dentro hacemos clic en Create API Key y se nos desplegará un formulario como el siguiente:

Aquí podremos crear la key con su nombre y los permisos que queramos darle, por defecto están todos los permisos, pero si hacemos clic en la opción Restricted Access podremos modificar los permisos necesarios.

También debemos añadir como mínimo una dirección de correo desde la que se enviarán los emails, para ello vamos a Settings y luego a Sender Authentication, si es la primera vez que accedemos aparecerá un botón azul en la esquina superior derecha para crear un nuevo remitente, si no es así, aparecerá un botón blanco en la parte izquierda con el texto Verify Single Sender, ese nos llevará a donde se encuentra el botón azul para crear un nuevo remitente, hacemos clic en él y se nos abrirá un formulario, lo rellenamos, verificamos el email, con el correo que nos envían, y ya tenemos nuestro primer Sender añadido. 

Una vez hecho esto, vamos al código, en primer lugar, vamos a añadir la dependencia de SendGrid a Maven, para ello añadimos en nuestro pom.xml lo siguiente:

<dependencies>

  <dependency>
    <groupId>com.sendgrid</groupId>
    <artifactId>sendgrid-java</artifactId>
    <version>4.8.2</version>
  </dependency>

…

</dependencies>

Y ahora crearemos una clase MailService en la que tendremos la lógica de nuestro servicio:

import com.sendgrid.SendGrid
import com.sendgrid.Request
import com.sendgrid.Response
import com.sendgrid.Method
import com.sendgrid.helpers.mail.Mail

private const val MAIL_SEND_ENDPOINT = "mail/send"

class MailService {
  
  private val sendgrid = SendGrid("<<YOUR API KEY>>")
  private val request = Request()

  fun sendEmail (mail: Mail): Response {
    request.method = Method.POST
    request.endpoint = MAIL_SEND_ENDPOINT
    request.body = mail.build()

    return sendgrid.api(request)
  }
}


La url base de la petición siempre será
https://api.sendgrid.com/v3/ que ya va implícita en la petición, por lo que tan sólo tendremos que pasarle a la request el tipo de petición, el endpoint y el body, que en nuestro caso será un objeto Mail proporcionado por la librería de SendGrid, lo mínimo que requiere este objeto es un email desde el que se envía el mensaje, un email de destino, un asunto y un contenido, para ello hemos creado una clase para realizar las distintas pruebas de nuestro servicio.

import com.sendgrid.Response
import com.sendgrid.helpers.mail.Mail
import com.sendgrid.helpers.mail.objects.Content
import com.sendgrid.helpers.mail.objects.Email
import motherobjects.ContentMother
import motherobjects.EmailMother
import org.junit.jupiter.api.Test

class MailServiceTest {

private val sut: MailService = MailService()
private lateinit var mail: Mail
private val subject: String = "Subject Test"
private val emailTo: Email = EmailMother.createEmail("test@test.com", "Test Test")
private val emailFrom: Email = EmailMother.createEmail("juan@test.com", "Juan Test")
private val contentEmail: Content = ContentMother.createContentEmail("text/html", "<h1>Test email</h1>")

    @Test
    fun sendSimpleEmail() {
        mail = Mail(emailFrom, subject, emailTo, contentEmail)

        val response = sut.sendEmail(mail)

        showResponseResult(response)
    }
}

Lo primero que haremos es crear nuestro email de envío y de destino, para ello nos apoyamos en una clase que hemos creado, con un método createEmail() que nos devolverá un objeto Email que pertenece a la librería de SendGrid, al que le tendremos que pasar el email y nombre de la persona asociada a dicho email, siendo el único parámetro obligatorio, el email de la persona.

class EmailMother {

    companion object {
        fun createEmail(emailAddress: String, recipientName: String): Email {
            val email = Email(emailAddress)
            email.name = recipientName

            return email
        }
    }
}

Una vez tenemos el email, lo siguiente es pasarle un asunto, este es de tipo String, no hace falta crearlo mediante ningún objeto, si no queremos especificar ningún asunto, también podremos pasarle un null. Y por último necesitaremos un contenido del mensaje, este será el mensaje en sí. Al igual que para el email, nos apoyaremos en una clase que nos construya el objeto:

import com.sendgrid.helpers.mail.objects.Content

class ContentMother {

    companion object {
        fun createContentEmail(contentType: String, contentValue: String): Content {
            val emailContent = Content()
            emailContent.type = contentType
            emailContent.value = contentValue

            return emailContent
        }
    }
}

Ningún atributo es obligatorio, podremos enviar un email sin contenido, pero en caso de querer pasarle un valor deberemos pasarle el tipo de contenido, en nuestro caso queremos pasarle un mensaje formateado con HTML, por lo que en el type le indicaremos que es “text/html”.

private val contentEmail: Content = ContentMother.createContentEmail("text/html", "<h1>Test email</h1>")

Bien, una vez tenemos todos estos datos, construimos el objeto Mail y se lo pasamos a la función que hemos creado:

mail = Mail(emailFrom, subject, emailTo, contentEmail)
val response = sut.sendEmail(mail)
fun sendEmail (mail: Mail): Response {
    request.method = Method.POST
    request.endpoint = MAIL_SEND_ENDPOINT
    request.body = mail.build()

    return sendgrid.api(request)
}

Aquí simplemente tendremos que añadirle como body, nuestro objeto construido y llamar a nuestra api, está devolverá un código 202 si todo ha ido correctamente, y nos llegará nuestro correo, como se muestra en la siguiente imagen:


Envío de email con datos adjuntos

Muchas veces necesitamos enviar junto con nuestro email, un PDF o una imagen, SendGrid nos ofrece esta posibilidad mediante los Attachments que son los objetos que se pueden añadir al Mail para incluir este tipo de archivos, hay que tener en cuenta que el peso máximo en un email permitido, incluyendo asunto, texto, y archivos adjuntos es de 30MB.

Para este ejemplo, seguiremos usando la misma función de nuestro servicio de email, lo único que le añadiremos al objeto Mail los archivos adjuntos, en este caso un PDF y una imagen.

El contenido sea un documento o una imagen deberá estar en base64, le deberemos indicar el nombre del archivo y el tipo del archivo, para ello utilizaremos nuevamente una clase que nos ayude con la creación de estos objetos:

import com.sendgrid.helpers.mail.objects.Attachments

class AttachmentMother {

    companion object {
        fun createAttachments(content: String, fileName: String, type: String): Attachments {
            val attachments = Attachments()
            attachments.content = content
            attachments.filename = fileName
            attachments.type = type

            return attachments
        }
    }
}

Una vez tengamos los objetos creados, tan sólo tendremos que añadirlos a nuestro Mail, mediante el método addAttachments(…), y listo, ya podemos enviar nuestro mensaje con archivos adjuntos.

@Test
fun sendEmailWithAttachments() {
    mail = Mail(emailFrom, subject, emailTo, contentEmail)

    val pdfContent = "JVBERi0xLjQKJdPr6eEK..."

    val pdf: Attachments = AttachmentMother.createAttachments(pdfContent, "test.pdf", "text/html");
    mail.addAttachments(pdf)

    val imageContent = "iVBORw0KGgoAAAANSUhEU..."

    val image: Attachments = AttachmentMother.createAttachments(imageContent, "image.png", "image/png");
    mail.addAttachments(image)

    val response = sut.sendEmail(mail)

    showResponseResult(response)
}

Envío de emails usando plantillas dinámicas

En muchas ocasiones nos encontramos ante la situación de querer enviar un email con un bonito diseño a cada usuario, y seguramente con sus datos personalizados, para esto SendGrid nos ofrece una variedad de plantillas hechas por ellos mismos, o bien, las podemos crear nosotros mismos con su herramienta de creación de plantillas, es tan sencillo como arrastrar y soltar, y también si lo preferimos podremos crearlas mediante código HTML, a su vez, dentro de las plantillas podemos definir variables para luego sustituirlas por datos propios, como por ejemplo, podemos definir una variable username, que desde el código se cambiará por el nombre de nuestro cliente.

Para poder hacer esto, debemos definir antes que nada la variable username, para poder usarla en la plantilla, para ello vamos al menú y hacemos clic en Marketing y dentro en Custom Fields.

Una vez dentro podremos añadir nuestro campo personalizado, pero si nos fijamos ya existen varios creados por SendGrid, estos los podemos usar ya en nuestras plantillas, y son los básicos como nombre, apellidos, dirección, etc.

Por lo que antes de crear un nuevo campo, debemos fijarnos si ya existe. Para crear el campo hacemos clic en New Custom Field y escribimos el nombre del campo y el tipo, que puede ser texto, número o fecha.

Bien, una vez tengamos las variables que necesitamos creados, vamos a diseñar nuestra template, para ello nos vamos al menú en la consola web y hacemos clic en Design Library:

Una vez dentro, tendremos la opción de crear nuestra propia plantilla, o seleccionar entre las tantas opciones que nos ofrecen:

 

Si hacemos clic en crear nuestra propia plantilla, primero nos preguntará si queremos usar el editor HTML, o el editor de arrastrar y soltar componentes para crear la plantilla:

Hacemos clic en Design Editor, y una vez dentro podremos arrastrar y soltar los componentes que queramos para crear la plantilla, incluso existen componentes para incrustar código HTML, es obligatorio como mínimo indicar la versión de la plantilla y el asunto, y es que cuando usamos templates, no podremos especificar el asunto desde nuestro código, ya estará por defecto en la plantilla.

Si os fijáis en esta última imagen, veréis que en el pie de página hay algo de texto entre llaves como por ejemplo {{Sender_Name}}, estas son variables dinámicas, es decir, que podemos sustituirlas por el valor que queramos desde el código, por ejemplo si añadimos algo de texto como el siguiente a nuestra plantilla:

Podremos sustituir la variable username, por el nombre del usuario o cliente, de esta forma aunque sean 10 o 1000 o 1 millón de mensajes, los enviaremos de forma personalizada a cada usuario.

Pero no solo basta con crear la variable entre llaves en la plantilla para poderla usar, hay que definir la existencia de esta en SendGrid (lo veremos más adelante). Una vez tengamos creada nuestra plantilla, o hayamos seleccionado una de SendGrid para editarla, vamos nuevamente al menú de la consola web, y hacemos clic en Email API y dentro en Dynamic Templates.

Una vez dentro se nos desplegará un menú con las plantillas que tengamos creadas, nosotros vamos a crear una, así que hacemos clic en Create a Dynamic Template.

Se nos abrirá un formulario para colocar el nombre de nuestra plantilla, este nombre no lo verá el usuario cuando se envíe el email, es solo para diferenciarlas internamente, cuando la tengamos creada aparecerá como una plantilla más, hacemos clic y nos dirá que tenemos que añadir una versión nueva:

Al añadir la versión, tendremos que seleccionar entre las plantillas que vimos anteriormente, entre estás estará la que acabamos de crear, la seleccionamos, y ya tenemos creada nuestra plantilla, si os fijáis en la anterior imagen, debajo del nombre de la plantilla hay un Template ID, este nos servirá para especificar la plantilla que queramos usar desde nuestra aplicación.

Bien, una vez tenemos copiado el Template ID, es hora de volver al código y enviar mensajes con nuestra nueva plantilla, para ello tan sólo tenemos que añadirle a nuestro objeto Mail, los datos de intercambiar y el Template ID, así que añadimos un caso de prueba nuevo:

@Test
fun sendEmailWithTemplate() {
    mail = Mail(emailFrom, subject, emailTo, contentEmail)

    mail.personalization[0].addDynamicTemplateData("username", "Juanma")
    mail.templateId = "d-6409e016383c405383cc9f57b"

    val response = sut.sendEmail(mail)

    showResponseResult(response)
}

Como veis tan solo hemos añadido el atributo templateId y en el array de objetos de personalización, en la primera posición (ya que por defecto hay 1 creado), añadimos los datos a intercambiar, el primer argumento es la variable que se encuentra en la plantilla, y el segundo el dato por el que lo queremos modificar, no hace falta especificar las {{ }}.

Bien, ¿por qué se añade en el array de personalización? Hablaremos más adelante de como usar esta parte del Mail, pero, por lo pronto esto es así, porque estamos personalizando el email para un usuario o cliente en concreto.

Y ya veremos que si tenemos 4 o 5 usuarios diferentes, se puede personalizar para esos usuarios, por lo que los datos del template deberán ir asociados a cada uno de estos y no al email en general, ejemplo:

mail.personalization[0].addDynamicTemplateData("username", "User1")
mail.personalization[1].addDynamicTemplateData("username", "User2")
mail.personalization[2].addDynamicTemplateData("username", "User3")
mail.personalization[3].addDynamicTemplateData("username", "User4")
mail.personalization[4].addDynamicTemplateData("username", "User5")

Una vez tenemos estos 2 nuevos atributos, es tan sencillo como volver a enviar el email y ahora se enviará con nuestra plantilla:

Como vemos, la variable se ha sustituido, y las de pie página que no hemos especificado no se han rellenado con nada, fijaros también como el asunto del email ha cambiado, ahora ya no es el que habíamos especificado en el código sino que es el especificado en la plantilla.

NOTA: Si usamos la V2 de SendGrid, deberemos usar esta línea de código en su lugar:

​​mail.personalization[0].addSubstitution("username", "Juanma")

Envío de emails personalizados

Antes hemos mencionado al array de objetos de personalización que podemos incluir en nuestro Mail, vamos a profundizar más en esto, tal cuál lo define SendGrid en su documentación, las personalizaciones te permiten sobreescribir varios metadatos para cada email en la solicitud.

A nuestro objeto Mail, podemos añadirle tantas personalizaciones como queramos, pero ten en cuenta que esto va a sobreescribir los atributos de asunto, a quién lo enviamos o desde donde que hayamos especificado antes.

Para nuestro ejemplo vamos a crear otra prueba más, en este caso tan solo tendremos que añadirle el objeto personalizado a mail, nos apoyaremos en una clase para la creación de estos objetos:

@Test
fun sendEmailWithPersonalization() {
    mail = Mail(emailFrom, subject, emailTo, contentEmail)

    val personalization0 = PersonalizationMother.createPersonalization("test@test.com",
    "pepito@test.com", "Pepito", "Asunto 1")

    mail.addPersonalization(personalization0)

    val personalization1 = PersonalizationMother.createPersonalization("pepito@test.com",
    "juan@test.com", "Juanma", "Asunto 2")

    mail.addPersonalization(personalization1)

    mail.templateId = "d-6409e016383c405383cc9f57b"

    val response = sut.sendEmail(mail)

    showResponseResult(response)
}

Ten en cuenta que aunque le pasemos un asunto diferente a cada objeto de personalización, al usar una plantilla este no tendrá efecto y será sobrescrito por el de la plantilla.

En nuestra clase auxiliar, le pasamos el email al que lo queremos enviar, a donde queremos mandar copias, el nombre de usuario para sustituirlo en la plantilla y el asunto, la copia oculta la ponemos directamente.

import com.sendgrid.helpers.mail.objects.Email
import com.sendgrid.helpers.mail.objects.Personalization

class PersonalizationMother {

    companion object {
        fun createPersonalization(emailTo: String, emailCc: String, usernameTemplateData: String, subject: String): Personalization {
            val personalization = Personalization()

            personalization.addTo(Email(emailTo))
            personalization.addBcc(Email("test@email.com"))
            personalization.addCc(Email(emailCc))
            personalization.addDynamicTemplateData("username", usernameTemplateData)
            personalization.subject = subject

            return personalization
        }
    }
}

Ahora si mandamos estos emails, 

Vemos cómo ha llegado a diferentes servidores de correo, y con la variable intercambiada y personalizada para cada usuario.

Añadir ajustes a nuestro email

La librería de SendGrid nos ofrece muchas opciones para añadir herramientas o personalizar nuestros emails desde el código a través del objeto MailSettings, al que podremos añadir diferentes objetos de ajustes como para el pie de página, para seguimiento de apertura de emails, o de clic en enlaces, o incluso especificar que estamos usando el modo de pruebas para no gastar nuestro límite de emails.

@Test
fun sendEmailWithSettings() {
    mail = Mail(emailFrom, subject, emailTo, contentEmail)

    val footerSetting = FooterSettingMother.createFooterSetting()
    val sandboxSetting = SandboxSettingMother.createSandboxSetting()

    val mailSettings = MailSettingsMother.createMailSettings(footerSetting, sandboxSetting)

    mail.setMailSettings(mailSettings)

    val response = sut.sendEmail(mail)

    showResponseResult(response)
}

Como vemos, creamos primero cada objeto de ajuste necesario, existen más que podemos consultar en la documentación: Mail Settings | Twilio (sendgrid.com) 

import com.sendgrid.helpers.mail.objects.FooterSetting

class FooterSettingMother {

    companion object {
        fun createFooterSetting(): FooterSetting {
            val footerSetting = FooterSetting()
            footerSetting.enable = true
            footerSetting.text = "Mi pie de página personalizado"

            return footerSetting
        }
    }
}

En el Footer Setting podemos definir si se va a mostrar o no el pie de página, y en caso afirmativo el texto que contendrá.

Y en el caso del sandbox, si lo marcamos como activado, cuando hagamos una petición, no se enviará ningún mensaje, pero nos validará que la estructura de la petición se ha hecho correctamente.

Por tanto mientras estamos desarrollando podemos activarlo y cuando veamos que la petición se va a realizar correctamente podemos desactivarlo para ver cómo llega el email.

import com.sendgrid.helpers.mail.objects.Setting

class SandboxSettingMother {

    companion object {
        fun createSandboxSetting(): Setting {
            val sandboxSetting = Setting()
            sandboxSetting.enable = false

            return sandboxSetting
        }
    }
}

A parte de los ajustes mencionados en el ejemplo y en la documentación, existen una serie de ajustes especiales, que son los de seguimiento o TrackingSettings, éstos, nos van a permitir obtener estadísticas relevantes del uso de nuestros emails por parte de los usuarios.

Por ejemplo, el número de veces que han hecho clic en enlaces dentro del mensaje (hasta 1000 enlaces por email), el número de veces que se ha abierto el correo, la cantidad de personas que se han dado de baja de nuestra lista de email, o incluso conectarlo con Google Analytics, para más info: Tracking Settings | Twilio (sendgrid.com)

Para este ejemplo vamos a añadir los ajustes arriba mencionados (excepto Google Analytics), y siguiendo el esquema hasta ahora, nuestro caso de prueba, en el que le añadimos estos ajustes al Mail:

@Test
fun sendEmailWithTrackingSettings() {
    mail = Mail(emailFrom, subject, emailTo, contentEmail)

    val clickTrackingSetting = ClickTrackingSettingMother.createClickTrackingSetting()

    val openTrackingSetting = OpenTrackingSettingMother.createOpenTrackingSetting()

    val subscriptionTrackingSetting = SubscriptionTrackingSettingMother.createSubscriptionTrackingSetting()

    val trackingSettings = TrackingSettingsMother.createTrackingSettings(clickTrackingSetting, openTrackingSetting, subscriptionTrackingSetting)

    mail.trackingSettings = trackingSettings

    val response = sut.sendEmail(mail)

    showResponseResult(response)
}

Vamos a ver cómo se construye cada uno de estos ajustes de seguimiento, empecemos por el de seguimiento del número de clics:

import com.sendgrid.helpers.mail.objects.ClickTrackingSetting

class ClickTrackingSettingMother {

    companion object {
        fun createClickTrackingSetting(): ClickTrackingSetting {
            val clickTrackingSetting = ClickTrackingSetting()

            clickTrackingSetting.enable = true

            return clickTrackingSetting
        }
    }
}

Este puede estar activado o desactivado, pero puede activarse por defecto si realizamos alguna campaña de marketing.

El de seguimiento de apertura, funciona colocando una imagen de 1 píxel al final del email, si no queremos que esté en esa zona, podemos seleccionar otra mediante el atributo substitutionTag.

import com.sendgrid.helpers.mail.objects.OpenTrackingSetting

class OpenTrackingSettingMother {

    companion object {
        fun createOpenTrackingSetting(): OpenTrackingSetting {
            val openTrackingSetting = OpenTrackingSetting()

            openTrackingSetting.enable = true
            openTrackingSetting.substitutionTag = "%open-tracking"

            return openTrackingSetting
        }
    }
}

Por último, si queremos saber estadísticas sobre los usuarios que se han dado de baja de nuestros emails, deberemos usar los ajustes de seguimiento de suscripción.

import com.sendgrid.helpers.mail.objects.SubscriptionTrackingSetting

class SubscriptionTrackingSettingMother {

    companion object {
        fun createSubscriptionTrackingSetting(): SubscriptionTrackingSetting {
            val subscriptionTrackingSetting = SubscriptionTrackingSetting()

            subscriptionTrackingSetting.enable = true
            subscriptionTrackingSetting.text = "Thank you, see you soon"

            return subscriptionTrackingSetting
        }
    }
}

Ahora crearemos nuestro objeto TrackingSettings con los objetos que hemos creado anteriormente:

import com.sendgrid.helpers.mail.objects.ClickTrackingSetting
import com.sendgrid.helpers.mail.objects.OpenTrackingSetting
import com.sendgrid.helpers.mail.objects.SubscriptionTrackingSetting
import com.sendgrid.helpers.mail.objects.TrackingSettings

class TrackingSettingsMother {

    companion object {
        fun createTrackingSettings(
            clickTrackingSetting: ClickTrackingSetting,
            openTrackingSetting: OpenTrackingSetting,
            subscriptionTrackingSetting: SubscriptionTrackingSetting
            ): TrackingSettings {
              val trackingSettings = TrackingSettings()

              trackingSettings.clickTrackingSetting = clickTrackingSetting
              trackingSettings.openTrackingSetting = openTrackingSetting
              trackingSettings.subscriptionTrackingSetting = subscriptionTrackingSetting

              return trackingSettings
        }
    }
}

Y esto se lo añadimos a nuestro objeto Mail, como siempre:

mail.trackingSettings = trackingSettings

Envío automático de emails

Bien, hasta ahora hemos visto muchas opciones de SendGrid, pero una de las más habituales es programar un email para que se envíe a una hora determinada en el futuro, esto lo podemos hacer con el atributo sendAt, simplemente le tenemos que pasar la fecha en formato UNIX Timestamp, por ejemplo si queremos enviar el email el Sat, 20 May 2023 10:00:32 GMT tenemos que pasarle el dato 1684576832, ejemplo:

mail.sendAt = 1684576832

Para convertir una fecha a timestamp, podemos hacer lo siguiente: 

val zonedDateTime = ZonedDateTime.of(2022, 1, 26, 7, 36, 24, 0, ZoneId.of("UTC"))
val toEpochSecond = zonedDateTime.toEpochSecond()

En este caso, estamos pasando a timestamp, la fecha de Miércoles 26 de Enero de 2022, 07:36:24, para construir la fecha con ZonedDateTime, le pasamos como argumentos en este orden: año, mes, dia, hora, minuto y segundo, los 2 últimos son la milésima del segundo (si queremos ser muy precisos) y la zona.

Luego llamamos al método toEpochSecond(), y lo que nos devuelve se lo asignamos a nuestro mail.

mail.sendAt = toEpochSecond

Este atributo sendAt puede estar a nivel de los objetos de personalizaciones, por lo que puedes por ejemplo crear 3 objetos de personalizaciones, con diferente momento de envío y a los mismos usuarios, además si quieres puede añadir tantos destinatarios como desees dentro de los objetos de personalización, en vez de pasarle un solo email de destino, le pasamos una lista con los emails de destino.

fun createPersonalizationWithMultipleRecipients(emailsTo: List<String>, emailCc: String, usernameTemplateData: String, subject: String): Personalization {
    val personalization = Personalization()

    for (email in emailsTo) {
      personalization.addTo(Email(email))
    }

    personalization.addBcc(Email("test@email.com"))
    personalization.addCc(Email(emailCc))
    personalization.addDynamicTemplateData("username", usernameTemplateData)
    personalization.subject = subject

    return personalization
}

Ahora supongamos que queremos pausar, o cancelar el envío programado de varios emails, para ello existe 1 endpoint dentro de la API de SendGrid:

POST /user/scheduled_sends

Este endpoint, nos permite pasarle en el body de la petición, el estado al que queremos pasar el envío de emails, pausado o cancelado, y el batchId al que están asociados, para saber cual cancelar o pausar en caso de existir varios, pero, ¿qué es el batchId? 

El batchId, es un identificador que asociaremos a los emails desde nuestro código antes de enviarlos, de esta forma sabremos a qué conjunto de emails dirigirnos para pausarlos o cancelarlos, o incluso saber si ya se han enviado.

Para ello primero tendremos que realizar una petición a nuestra API, para que nos devuelva un nuevo batchId, que asociaremos al email, nuestro Batch Service luciría así:

import com.sendgrid.Method
import com.sendgrid.Response

private const val MAIL_BATCH_ENDPOINT = "mail/batch"

class BatchService: Service() {

    fun createBatchId(): Response {
        request.method = Method.POST
        request.endpoint = MAIL_BATCH_ENDPOINT

        return sendgrid.api(request)
    }
}

Ya que ahora existen 2 servicios que usan la misma API, he creado una clase Service para los elementos comunes, como son la API y la request.

import com.sendgrid.Request
import com.sendgrid.SendGrid

open class Service {
    protected val sendgrid = SendGrid("<<API KEY>>")
    protected val request = Request()
}

 

import com.sendgrid.Method
import com.sendgrid.Response
import com.sendgrid.helpers.mail.Mail

private const val MAIL_SEND_ENDPOINT = "mail/send"

class MailService: Service() {

    fun sendEmail(mail: Mail): Response {
        request.method = Method.POST
        request.endpoint = MAIL_SEND_ENDPOINT
        request.body = mail.build()

        return sendgrid.api(request)
    }
}

Ahora lo único que tenemos que hacer, es llamar al endpoint del BatchService, y el id que nos devuelva, asignarlo a nuestro objeto Mail:

@Test
fun sendEmailWithBatchId() {
    mail = Mail(emailFrom, "Test with batch id", emailTo, contentEmail)

    val responseBatch = batchService.createBatchId()
    mail.batchId = getBatchIdFromResponse(responseBatch)
    mail.sendAt = 1643011933 // Timestamp in the future

    val response = sut.sendEmail(mail)

    showResponseResult(response)
}

private fun getBatchIdFromResponse(response: Response): String {
    val startIndex = response.body.indexOf(":") + 1
    val endIndex = response.body.indexOf("}")

    return response.body.substring(startIndex, endIndex).replace("\"", " ").trim()
}

La función que hemos creado getBatchIdFromResponse es debido a que la petición al servicio del batch, nos devuelve lo siguiente en el body:

{
"batch_id": "asldfjh"
}

Por lo que si queremos obtener el dato que nos interesa debemos extraerlo.

Podemos también usar alguna librería para parsear el JSON desde una String, en este caso vamos a utilizar Jackson:

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-kotlin</artifactId>
    <version>2.13.1</version>
</dependency>
@Test
fun sendEmailWithBatchId() {
    mail = Mail(emailFrom, "Test with batch id", emailTo, contentEmail)

    val responseBatch = batchService.createBatchId()
    val responseToObject: BatchResponse = objectMapper.readValue(responseBatch.body)

    mail.batchId = responseToObject.batch_id
    mail.sendAt = toEpochSecond

    val response = sut.sendEmail(mail)

    showResponseResult(response)
}
package model

data class BatchResponse (var batch_id: String) { }

Una vez tenemos, nuestro batchId, asociado a nuestros emails, podemos cancelar o pausar el envío de estos, siempre y cuando queden más de 10 minutos hasta el envío, en caso contrario no será posible.

Para este ejemplo, vamos a cancelar un email justo después de enviarlo:

@Test
fun cancelEmailWithBatchId() {
    mail = Mail(emailFrom, "Test with batch id", emailTo, contentEmail)

    val responseBatch = batchService.createBatchId()
    val responseToObject: BatchResponse = objectMapper.readValue(responseBatch.body)

    mail.batchId = responseToObject.batch_id
    mail.sendAt = toEpochSecond

    val response = sut.sendEmail(mail)

    showResponseResult(response)

    val scheduleResponse = scheduleService.cancelSchedule(responseToObject.batch_id)

    showResponseResult(scheduleResponse)
}

Y nuestro ScheduleService:

import com.sendgrid.Method
import com.sendgrid.Response

private const val SCHEDULES_ENDPOINT = "user/scheduled_sends"

class ScheduleService: Service() {

fun cancelSchedule(batchId: String): Response {
    request.method = Method.POST
    request.endpoint = SCHEDULES_ENDPOINT
    request.body = "{\"batch_id\": \"${batchId}\", \"status\": \"cancel\"}"

    return sendgrid.api(request)
}

Si realizamos esta petición cuando queden menos de 10 minutos para el envío, no se realizará, a pesar de que nos devuelva un status code correcto el email será enviado de todas formas.

Pero si lo hacemos antes de esos 10 minutos, todos los emails asociados a ese batchId serán cancelados. Si por el contrario queremos pausar el envío, tendremos que realizar la misma petición pero modificando el status:

fun pauseSchedule(batchId: String): Response {
    request.method = Method.POST
    request.endpoint = SCHEDULES_ENDPOINT
    request.body = "{\"batch_id\": \"${batchId}\", \"status\": \"pause\"}"

    return sendgrid.api(request)
}

Ten en cuenta que si dejas un email pausado por más de 72 horas, este será descartado automáticamente.

Si queremos eliminar la pausa o cancelación de un email deberemos llamar al siguiente endpoint en nuestro ScheduleService, pasándole el batchId, de los emails que queremos eliminar la cancelación o la pausa:

private const val SCHEDULES_ENDPOINT = "user/scheduled_sends"
fun deleteCancelOrPauseSchedule(batchId: String): Response {
    request.method = Method.DELETE
    request.endpoint = SCHEDULES_ENDPOINT + "/${batchId}"

    return sendgrid.api(request)
}

Si desconocemos el batchId, podemos llamar a un endpoint para recuperar todos los que han sido cancelados o pausados:

fun retrieveAllSchedules(): Response {
    request.method = Method.GET
    request.endpoint = SCHEDULES_ENDPOINT

    return sendgrid.api(request)
}

Conclusiones

Como vemos, es bastante sencillo utilizar la API de SendGrid, y nos ofrece bastantes soluciones para nuestro envío de emails, este ejemplo está en Kotlin, pero en el Github de SendGrid, tienes varios ejemplos en otros lenguajes como PHP o Javascript, si tienes alguna duda, te dejo el enlace al repositorio de Github, donde está todo el código realizado en este tutorial.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad