-
Notifications
You must be signed in to change notification settings - Fork 97
Add Grails 8 email guide (v8 prose, snippets, guides.yml) #524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sanjana2505006
wants to merge
5
commits into
apache:master
Choose a base branch
from
sanjana2505006:grails-email-v8-guide
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
882e33b
Add Grails 8 email guide (v8 prose, snippets, guides.yml)
sanjana2505006 52ffc53
Address review feedback on Grails 8 email guide.
sanjana2505006 efc870f
Refresh PR after review fixes
sanjana2505006 611b7d5
Sync email guide snippets with companion review fixes.
sanjana2505006 7c086fd
Re-vendor email snippets from companion b07505d.
sanjana2505006 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| AWS SES is the third provider option: | ||
|
|
||
| [source,groovy] | ||
| .src/main/groovy/example/AwsSesEmailService.groovy | ||
| ---- | ||
| include::../snippets/src/main/groovy/example/AwsSesEmailService.groovy[] | ||
| ---- | ||
|
|
||
| Set `EMAIL_PROVIDER=ses` and configure `AWS_REGION` and `AWS_SOURCE` plus standard AWS credentials for the SDK. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| Accept JSON payloads with a command object that implements `Email` and validates required fields: | ||
|
|
||
| [source,groovy] | ||
| .grails-app/controllers/example/EmailCmd.groovy | ||
| ---- | ||
| include::../snippets/grails-app/controllers/example/EmailCmd.groovy[] | ||
| ---- | ||
|
|
||
| The controller delegates to `EmailService`: | ||
|
|
||
| [source,groovy] | ||
| .grails-app/controllers/example/MailController.groovy | ||
| ---- | ||
| include::../snippets/grails-app/controllers/example/MailController.groovy[] | ||
| ---- | ||
|
|
||
| [IMPORTANT] | ||
| ==== | ||
| This endpoint is demonstration-only. It accepts arbitrary recipients and message content with no authentication, so a deployed copy can become an outbound mail relay through the configured provider. Before any public or production exposure, add authentication, authorization, recipient controls, and abuse/rate-limit protection. | ||
| ==== | ||
|
|
||
| Example request: | ||
|
jamesfredley marked this conversation as resolved.
|
||
|
|
||
| [source,bash] | ||
| ---- | ||
| curl -X POST http://localhost:8080/mail/send \ | ||
| -H 'Content-Type: application/json' \ | ||
| -d '{"recipient":"user@example.com","subject":"Hello","textBody":"Hi there"}' | ||
| ---- | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| Define a small `Email` contract and a single `EmailService` boundary that every provider implements: | ||
|
|
||
| [source,groovy] | ||
| .src/main/groovy/example/Email.groovy | ||
| ---- | ||
| include::../snippets/src/main/groovy/example/Email.groovy[] | ||
| ---- | ||
|
|
||
| [source,groovy] | ||
| .src/main/groovy/example/EmailService.groovy | ||
| ---- | ||
| include::../snippets/src/main/groovy/example/EmailService.groovy[] | ||
| ---- | ||
|
|
||
| Controllers and tests depend on `EmailService`, not on SMTP, SendGrid, or SES directly. Swap providers by changing Spring wiring and configuration. | ||
|
|
||
| Add the mail dependencies in `build.gradle`: | ||
|
|
||
| [source,groovy] | ||
| .build.gradle | ||
| ---- | ||
| include::../snippets/build.gradle[] | ||
| ---- | ||
|
|
||
| Grails 8 uses `org.grails.plugins:grails-mail` 5.x from the Grails plugin repository, compatible with Apache Grails 8. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| Clone the repository and verify tests pass: | ||
|
|
||
| [source,bash] | ||
| ---- | ||
| git clone -b grails8 https://github.com/grails-guides/grails-email.git | ||
| cd grails-email/initial | ||
| ./gradlew test | ||
| ---- | ||
|
|
||
| The `initial/` project is a Grails 8 REST API starter with no mail wiring yet. | ||
|
|
||
| To skip ahead, `cd ../complete` and run the same commands; that tree contains the finished mail services, controller, and integration spec. | ||
|
|
||
| Both `initial` and `complete` must pass in CI (see `.github/workflows/grails8.yml`). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| include::{commondir}/common-helpWithGrails.adoc[] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| Learn how to send mail from a Grails 8 application over SMTP (Grails Mail plugin) and through a provider (SendGrid / AWS SES), externalize configuration in `application.yml`, and verify send interactions with Spock Spring integration tests, without dispatching real email in CI. | ||
|
|
||
| This guide parallels the HTTP client guide in shape: an external integration behind a mockable service boundary, with tests that assert the contract. | ||
|
|
||
| This guide follows the standard Grails guides layout: work in the `initial/` project and compare your progress with `complete/`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| Wire the active provider in `grails-app/conf/spring/resources.groovy`: | ||
|
|
||
| [source,groovy] | ||
| .grails-app/conf/spring/resources.groovy | ||
| ---- | ||
| include::../snippets/grails-app/conf/spring/resources.groovy[] | ||
| ---- | ||
|
|
||
| The switch reads `email.provider` from configuration. Provider wiring is skipped in the `test` environment so integration tests can register a mock `EmailService` bean. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| * Approximately 45 minutes | ||
| * JDK 21 (Apache Grails 8 requires Java 21) | ||
| * A https://www.grails.org/[Grails] installation or the bundled Gradle wrapper in `initial/` and `complete/` | ||
| * Optional: local SMTP catcher on port **1025** for manual `bootRun` verification (for example https://github.com/mailhog/MailHog[MailHog]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| SendGrid implements the same `EmailService` contract: | ||
|
|
||
| [source,groovy] | ||
| .src/main/groovy/example/SendGridEmailService.groovy | ||
| ---- | ||
| include::../snippets/src/main/groovy/example/SendGridEmailService.groovy[] | ||
| ---- | ||
|
|
||
| Set `EMAIL_PROVIDER=sendgrid` and provide `SENDGRID_APIKEY` and `SENDGRID_FROM_EMAIL` (see the `sendgrid` block in `application.yml`). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| The default provider delegates to the Grails Mail plugin: | ||
|
|
||
| [source,groovy] | ||
| .grails-app/services/example/SmtpEmailService.groovy | ||
| ---- | ||
| include::../snippets/grails-app/services/example/SmtpEmailService.groovy[] | ||
| ---- | ||
|
|
||
| Configure SMTP settings in `application.yml`: | ||
|
|
||
| [source,yaml] | ||
| .grails-app/conf/application.yml | ||
| ---- | ||
| include::../snippets/grails-app/conf/application.yml[] | ||
| ---- | ||
|
|
||
| For local development, point `SMTP_HOST` and `SMTP_PORT` at a mail catcher on `localhost:1025`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| You added outbound email to a Grails 8 application: | ||
|
|
||
| * An `EmailService` boundary with SMTP, SendGrid, and AWS SES implementations | ||
| * Externalized provider and mail settings in `application.yml` | ||
| * A REST controller with command-object validation | ||
| * Spock Spring integration tests that mock the sender, with no live email in CI | ||
|
|
||
| Next steps: add HTML templates, queue outbound mail asynchronously, or wire email into domain events. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| Verify the HTTP endpoint builds the message and invokes `EmailService` without sending live email. Register a Spock mock as the primary `EmailService` bean: | ||
|
|
||
| [source,groovy] | ||
| .src/integration-test/groovy/example/MailControllerSpec.groovy | ||
| ---- | ||
| include::../snippets/src/integration-test/groovy/example/MailControllerSpec.groovy[] | ||
| ---- | ||
|
|
||
| The spec posts to `/mail/send` and asserts `emailService.send` is called with the expected recipient, subject, and body. | ||
|
|
||
| Run tests: | ||
|
|
||
| [source,bash] | ||
| ---- | ||
| cd ../complete | ||
| ./gradlew test integrationTest | ||
| ---- |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| implementation "org.grails.plugins:grails-mail:5.0.3" | ||
| implementation "com.sendgrid:sendgrid-java:4.9.3" | ||
| implementation "software.amazon.awssdk:ses:2.29.45" |
28 changes: 28 additions & 0 deletions
28
guides/grails-email/v8/snippets/grails-app/conf/application.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| #tag::mailConfig[] | ||
| grails: | ||
| mail: | ||
|
jamesfredley marked this conversation as resolved.
|
||
| host: ${SMTP_HOST:localhost} | ||
| port: ${SMTP_PORT:1025} | ||
| username: ${SMTP_USERNAME:} | ||
| password: ${SMTP_PASSWORD:} | ||
| default: | ||
| from: ${SMTP_FROM:no-reply@example.com} | ||
| props: | ||
| mail.smtp.auth: ${SMTP_AUTH:false} | ||
| mail.smtp.starttls.enable: ${SMTP_STARTTLS:false} | ||
| #end::mailConfig[] | ||
| #tag::emailProvider[] | ||
| email: | ||
| provider: ${EMAIL_PROVIDER:smtp} | ||
| #end::emailProvider[] | ||
| #tag::sendgrid[] | ||
| sendgrid: | ||
| api: ${SENDGRID_APIKEY:} | ||
| from: ${SENDGRID_FROM_EMAIL:} | ||
| #end::sendgrid[] | ||
| #tag::awsses[] | ||
| aws: | ||
| ses: | ||
| source: ${AWS_SOURCE:} | ||
| region: ${AWS_REGION:} | ||
| #end::awsses[] | ||
26 changes: 26 additions & 0 deletions
26
guides/grails-email/v8/snippets/grails-app/conf/spring/resources.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import example.AwsSesEmailService | ||
| import example.SendGridEmailService | ||
| import example.SmtpEmailService | ||
| import grails.util.Environment | ||
|
|
||
| beans = { | ||
| if (Environment.current == Environment.TEST) { | ||
| return | ||
| } | ||
| //tag::emailServiceBeans[] | ||
| def provider = application.config.getProperty('email.provider', String, 'smtp') | ||
| switch (provider) { | ||
| case 'sendgrid': | ||
| emailService(SendGridEmailService) | ||
| break | ||
| case 'ses': | ||
| emailService(AwsSesEmailService) | ||
| break | ||
| case 'smtp': | ||
| emailService(SmtpEmailService) | ||
| break | ||
| default: | ||
| throw new IllegalArgumentException("Unsupported email.provider: ${provider}") | ||
| } | ||
| //end::emailServiceBeans[] | ||
| } |
27 changes: 27 additions & 0 deletions
27
guides/grails-email/v8/snippets/grails-app/controllers/example/EmailCmd.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package example | ||
|
|
||
| import grails.compiler.GrailsCompileStatic | ||
| import grails.validation.Validateable | ||
| import groovy.transform.ToString | ||
|
|
||
| @ToString | ||
| @GrailsCompileStatic | ||
| class EmailCmd implements Validateable, Email { | ||
| String recipient | ||
| List<String> cc = [] | ||
| List<String> bcc = [] | ||
| String subject | ||
| String htmlBody | ||
| String textBody | ||
| String replyTo | ||
|
|
||
| static constraints = { | ||
| recipient nullable: false // <1> | ||
| subject nullable: false // <2> | ||
| htmlBody nullable: true | ||
| textBody nullable: true, validator: { String val, EmailCmd obj -> // <3> | ||
| !(!obj.htmlBody && !val) | ||
| } | ||
| replyTo nullable: true | ||
| } | ||
| } |
25 changes: 25 additions & 0 deletions
25
guides/grails-email/v8/snippets/grails-app/controllers/example/MailController.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package example | ||
|
|
||
| import groovy.transform.CompileStatic | ||
| import groovy.util.logging.Slf4j | ||
|
|
||
| import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY | ||
|
|
||
| @Slf4j | ||
| @CompileStatic | ||
| class MailController { | ||
|
|
||
| EmailService emailService | ||
|
|
||
| static allowedMethods = [send: 'POST'] | ||
|
|
||
| def send(EmailCmd cmd) { | ||
| if (cmd.hasErrors()) { | ||
| respond cmd.errors, view: '/application/errors', status: UNPROCESSABLE_ENTITY | ||
| return | ||
| } | ||
| log.info 'Sending mail to {} with subject {}', cmd.recipient, cmd.subject | ||
| emailService.send(cmd) | ||
| render status: 200 | ||
| } | ||
| } |
36 changes: 36 additions & 0 deletions
36
guides/grails-email/v8/snippets/grails-app/services/example/SmtpEmailService.groovy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package example | ||
|
|
||
| import grails.plugins.mail.MailService | ||
| import groovy.transform.CompileStatic | ||
| import groovy.util.logging.Slf4j | ||
|
|
||
| @Slf4j | ||
| @CompileStatic | ||
| class SmtpEmailService implements EmailService { // <1> | ||
|
|
||
| MailService mailService | ||
|
|
||
| @Override | ||
| void send(Email email) { | ||
| mailService.sendMail { | ||
| to email.recipient | ||
| subject email.subject | ||
| if (email.textBody) { | ||
| text email.textBody | ||
| } | ||
| if (email.htmlBody) { | ||
| html email.htmlBody | ||
| } | ||
| if (email.replyTo) { | ||
| replyTo email.replyTo | ||
| } | ||
| if (email.cc) { | ||
| cc email.cc as String[] | ||
| } | ||
| if (email.bcc) { | ||
| bcc email.bcc as String[] | ||
| } | ||
| } | ||
| log.debug('SMTP message handed off to MailService for {}', email.recipient) | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.