Show Navigation

Send Email and Spock Spring

Learn how to send emails with AWS SES and SendGrid from a Grails app and leverage Spock Spring integration to verify interaction.

Authors: Sergio del Amo

Grails Version: 4

1 Getting Started

1.1 What you will need

To complete this guide, you will need the following:

  • Some time on your hands

  • A decent text editor or IDE

  • JDK 11 or greater installed with JAVA_HOME configured appropriately

1.2 How to complete the guide

To get started do the following:

or

The Grails guides repositories contain two folders:

  • initial Initial project. Often a simple Grails app with some additional code to give you a head-start.

  • complete A completed example. It is the result of working through the steps presented by the guide and applying those changes to the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/grails-email/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/grails-email/complete

If you want to start from scratch, create a new Grails 3 application using Grails Application Forge.

forgeDefault

2 Writing the App

Create an app with the rest-api profile

grails create-app example --profile=rest-api

2.1 Controller

Add an entry to UrlMappings:

grails-app/controllers/example/grails/UrlMappings.groovy
package example.grails

class UrlMappings {

    static mappings = {
    ...
    ..
    .
link:../../snippets/grails-app/controllers/example/grails/UrlMappings.groovy[role=include]
    }
}

Create MailController which use a collaborator, emailService to send and email.

grails-app/controllers/example/grails/MailController.groovy
link:../../snippets/grails-app/controllers/example/grails/MailController.groovy[role=include]

The previous controller uses a command object:

grails-app/controllers/example/grails/EmailCmd.groovy
link:../../snippets/grails-app/controllers/example/grails/EmailCmd.groovy[role=include]
1 recipient is required
2 subject is required
3 You must specify either textBody or htmlBody

2.2 Email Service

Create an interface - EmailService. Any email integration present in the application should implement it.

src/groovy/main/example/grails/EmailService.groovy
link:../../snippets/src/main/groovy/example/grails/EmailService.groovy[role=include]
src/groovy/main/example/grails/Email.groovy
link:../../snippets/src/main/groovy/example/grails/Email.groovy[role=include]

2.2.1 AWS SES

Amazon Simple Email Service (Amazon SES) is a cloud-based email sending service designed to help digital marketers and application developers send marketing, notification, and transactional emails. It is a reliable, cost-effective service for businesses of all sizes that use email to keep in contact with their customers.

There is a AWS SDK SES Grails plugin. However, in this guide we are going to integrate AWS SDK SES directly.

Add a dependency to AWS SES SDK:

build.gradle
link:../../../snippets/build.gradle[role=include]

Also, add configuration properties which can be passed via system properties / command line arguments:

grails-app/conf/application.yml
link:../../../snippets/grails-app/conf/application.yml[role=include]

Create one service which encapsulates the integration with SES. There are several way to provide programmatic credentials.

The client searches for credentials using the default credentials provider chain, in the following order:

In the Java system properties: aws.accessKeyId and aws.secretKey.

In system environment variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.

In the default credentials file (the location of this file varies by platform).

In the Amazon ECS environment variable: AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.

In the instance profile credentials, which exist within the instance metadata associated with the IAM role for the EC2 instance.

grails-app/services/example/grails/AwsSesMailService.groovy
link:../../../snippets/grails-app/services/example/grails/AwsSesMailService.groovy[role=include]
1 Retrieve configuration values with GrailsConfigurationAware

2.2.2 SendGrid

SendGrid is a transactional email service.

SendGrid is responsible for sending billions of emails for some of the best and brightest companies in the world.

There is a SendGrid Grails Plugin. However, in this guide we are going to integrate AWS SDK SES directly.

Add a dependency to SendGrid SDK:

build.gradle
link:../../../snippets/build.gradle[role=include]

Add configuration properties which can be passed via system properties / command line arguments:

grails-app/conf/application.yml
link:../../../snippets/grails-app/conf/application.yml[role=include]

Create a service which encapsulates the integration with SendGrid:

grails-app/services/example/grails/SendGridEmailService.groovy
link:../../../snippets/grails-app/services/example/grails/SendGridEmailService.groovy[role=include]
1 Retrieve configuration values with GrailsConfigurationAware

2.3 Resources

Grails integrates with and builds on the Spring Framework.

You can easily register new (or override existing) beans by configuring them in grails-app/conf/spring/resources.groovy which uses the Grails Spring DSL.

Depending on the presence of the required system properties we are going to enable SendGrid or AWS SES integration.

grails-app/conf/spring/resources.groovy
link:../../snippets/grails-app/conf/spring/resources.groovy[role=include]

Add a logger to get more visibility:

grails-app/conf/logback.groovy
...
..
.
link:../../snippets/grails-app/conf/logback.groovy[role=include]

To use SendGrid, start the app with the necessary system properties:

$ ./gradlew -DSENDGRID_FROM_EMAIL=email@email.com -DSENDGRID_APIKEY=XXXXXX bootRun

To use AWS SES, start the app with the necessary system properties:

$ export AWS_ACCESS_KEY_ID=XXXXXXXX
$ export AWS_SECRET_ACCESS_KEY=XXXXXXXX
$ ./gradlew -DAWS_REGION=eu-west-1 -DAWS_SOURCE=email@email.com  bootRun

2.4 Test

In our acceptance test we don’t want bean emailService to be SendGridEmailService or AwsSesMailService. Instead we want it to be a Mock which we can verify interactions against.

The spock-spring module provides support for defining Spock mocks and stubs as Spring beans.

Add a dependency to spock-spring:

build.gradle
link:../../snippets/build.gradle[role=include]

First, you need to annotate Application.groovy with @ComponentScan.

grails-app/init/example/grails/Application.groovy
link:../../snippets/grails-app/init/example/grails/Application.groovy[role=include]

In the next test, we use an embedded config annotated with @TestConfiguration. We create an EmailService mock using a DetachedMockFactory.

src/integration-test/groovy/example/grails/MailControllerSpec.groovy
link:../../snippets/src/integration-test/groovy/example/grails/MailControllerSpec.groovy[role=include]
1 emailService.send method is invoked once.

Learn more about Spring Spock integration in Spock documentation.

3 Do you need help with Grails?

Help with Apache Grails

Apache Grails is supported by an active community of contributors and the Apache Software Foundation. If you need help working through a guide, want to discuss the framework, or have run into something that looks like a bug, the channels below are the right place to start.

For Grails plugins, see the matching project on the apache org or the plugin’s own GitHub repository.