Java Quarkus

Integración servicio de correos Sendgrid

Este tutorial requiere una cuenta activa en el servicio de envio de correos electrónicos Sendgrid. La configuración básica de este servicio implica asociar un correo electrónico válido en el apartado 'Sender Authentication -> Single Sender Verification' y generar una 'API Key' para la implementación.

Utilizaremos el siguiente comando Maven para generar el aplicativo java:

mvn io.quarkus.platform:quarkus-maven-plugin:2.2.3.Final:create \
-DprojectGroupId=dev.failapp \
-DprojectArtifactId=mail-lab \
-DclassName="dev.failapp.lab.resource.EmailRest" \
-Dpath="/api/v1/email"

En el archivo 'pom.xml' se definen las dependencias de servicio sendgrid, servicios REST Json, ORM Panache y driver de base de datos de prueba (H2):

..
...
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
    <groupId>com.sendgrid</groupId>
    <artifactId>sendgrid-java</artifactId>
    <version>4.7.4</version>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-junit5</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <scope>test</scope>
</dependency>
..
...

En el archivo 'application.properties' se configuran los parámetros básicos del ORM y la base de datos H2, junto al 'API Key' y correo asociado a servicio Sendgrid:

..
...
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:file:~/mail;AUTO_SERVER=true;DB_CLOSE_DELAY=-1
quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect
quarkus.hibernate-orm.database.generation=update

config.mail.service.api.key=AQUI_TU_API_KEY_SENDGRID
config.mail.service.from=contacto@failapp.dev
...

Creamos una clase para obtener los parámetros de configuración de nuestra aplicación, anotandola con 'ApplicationScoped' para poder injectarla a la capa de servicios:

@ApplicationScoped
public class EmailConfig {

    @ConfigProperty(name = "config.mail.service.api.key", defaultValue = "")
    private String mailApiKey;

    @ConfigProperty(name = "config.mail.service.from")
    private String mailFrom;

    @Produces
    public SendGrid sendGrid() {
        return new SendGrid(mailApiKey);
    }

    public String getMailFrom() {
        return mailFrom;
    }
}

Agregamos una clase entidad sencilla y una clase 'repositorio' para temas de persistencia:

@Entity
public class EmailRequest {

    @Id
    @GeneratedValue
    private Long id;
    private String to;
    private String subject;
    private String body;
    private String timestamp;

    public EmailRequest() {
    }

    public EmailRequest(String to, String subject, String body) {
        this.to = to;
        this.subject = subject;
        this.body = body;
    }

    // getters ..
    // setters ..

}
@ApplicationScoped
public class EmailRepository implements PanacheRepository<EmailRequest> {

    public Optional<EmailRequest> findByTo(String to) {
        return find("to", to).firstResultOptional();
    }

}

La clase encargada de implementar el servicio de correo tiene el siguiente aspecto:

@ApplicationScoped
public class EmailService {

    private static final Logger log = Logger.getLogger(EmailService.class);

    @Inject
    private EmailRepository emailRepository;

    @Inject
    private SendGrid sendGrid;

    @Inject
    private EmailConfig emailConfig;

    @Transactional
    public boolean sendEmail(EmailRequest emailRequest) {

        if (Optional.ofNullable(emailRequest).isEmpty())
            return false;

        try {

            Email from = new Email(emailConfig.getMailFrom());
            Email to = new Email(emailRequest.getTo());
            String subject = emailRequest.getSubject();
            Content content = new Content("text/plain", emailRequest.getBody());

            Mail mail = new Mail(from, subject, to, content);

            Request request = new Request();
            request.setMethod(Method.POST);
            request.setEndpoint("mail/send");
            request.setBody(mail.build());

            Response response = sendGrid.api(request);

            log.infof("[x] response status code: %s", response.getStatusCode());
            log.infof("[x] response body: %s", response.getBody());
            log.infof("[x] response headers: %s", response.getHeaders());

            if (response.getStatusCode() == 202) {
                emailRequest.setTimestamp(LocalDateTime.now().format(Util.formatDateTime));
                emailRepository.persist(emailRequest);
                return true;
            }

        } catch (Exception ex) {
            log.infof("[x] error: %s", ex.getMessage());
        }

        return false;
    }
}

Por último, la clase que recibe el mensaje a través de metodo http post (/api/v1/email) e implementa el servicio:

@Path("/")
public class EmailRest {

    @Inject
    private EmailService emailService;

    @POST
    @Path("/api/v1/email")
    public Response sendEmail(EmailRequest emailRequest) {
        boolean success = emailService.sendEmail(emailRequest);
        if (success) {
            Map<String, Object> map = new HashMap<>();
            map.put("mail to", emailRequest.getTo());
            map.put("status", "send");
            return Response.ok(map).build();
        } else {
            return Response.status(404).build();
        }
    }
}

Prueba rápida con curl en la consola:

curl -i -H "Content-Type: application/json" -X POST \
        -d '{"to":"jrodriguez@failapp.dev","subject":"mail test", "body": "mensaje test.."}' \
        http://localhost:8080/api/v1/email

Puedes descargar el código fuente completo de este ejemplo en el siguiente enlace.



¿Tienes alguna consulta?
Puedes contactarme enviándome un mensaje desde aquí.