Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 6.1.0-RELEASE
* Add a `sanitiseContentFor` parameter to `sendEmail`, a list of placeholders, the content of which you want Notify to sanitise.

## 6.0.0-RELEASE
* Remove Java 8, 9, and 10 from supported versions. This library only supports Java 11+ from now on.

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>uk.gov.service.notify</groupId>
<artifactId>notifications-java-client</artifactId>
<version>6.0.1-RELEASE</version>
<version>6.1.0-RELEASE</version>
<packaging>jar</packaging>

<name>GOV.UK Notify Java client</name>
Expand Down
22 changes: 20 additions & 2 deletions src/main/java/uk/gov/service/notify/NotificationClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Objects;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -138,7 +139,7 @@ public SendEmailResponse sendEmail(String templateId,
String emailAddress,
Map<String, ?> personalisation,
String reference) throws NotificationClientException {
return sendEmail(templateId, emailAddress, personalisation, reference, "", null);
return sendEmail(templateId, emailAddress, personalisation, reference, "", null, null);
}

@Override
Expand All @@ -147,7 +148,7 @@ public SendEmailResponse sendEmail(String templateId,
Map<String, ?> personalisation,
String reference,
String emailReplyToId) throws NotificationClientException {
return sendEmail(templateId, emailAddress, personalisation, reference, emailReplyToId, null);
return sendEmail(templateId, emailAddress, personalisation, reference, emailReplyToId, null, null);
}

@Override
Expand All @@ -157,6 +158,18 @@ public SendEmailResponse sendEmail(String templateId,
String reference,
String emailReplyToId,
URI oneClickUnsubscribeURL) throws NotificationClientException {
return sendEmail(templateId, emailAddress, personalisation, reference, emailReplyToId, oneClickUnsubscribeURL, null);
}

@Override
public SendEmailResponse sendEmail(String templateId,
String emailAddress,
Map<String, ?> personalisation,
String reference,
String emailReplyToId,
URI oneClickUnsubscribeURL,
List<String> sanitiseContentFor) throws NotificationClientException {


JSONObject body = createBodyForPostRequest(templateId,
null,
Expand All @@ -176,6 +189,11 @@ public SendEmailResponse sendEmail(String templateId,
body.put("one_click_unsubscribe_url", oneClickUnsubscribeURL);
}

if(sanitiseContentFor != null)
{
body.put("sanitise_content_for", sanitiseContentFor);
}

HttpURLConnection conn = createConnectionAndSetHeaders(baseUrl + "/v2/notifications/email", "POST");
String response = performPostRequest(conn, body, HttpsURLConnection.HTTP_CREATED);
return new SendEmailResponse(response);
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/uk/gov/service/notify/NotificationClientApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import java.util.Map;

public interface NotificationClientApi {
Expand Down Expand Up @@ -61,6 +62,27 @@ public interface NotificationClientApi {
*/
SendEmailResponse sendEmail(String templateId, String emailAddress, Map<String, ?> personalisation, String reference, String emailReplyToId, URI oneClickUnsubscribeURL) throws NotificationClientException;

/**
* The sendEmail method will create an HTTPS POST request. A JWT token will be created and added as an Authorization header to the request.
*
* @param templateId The template id is visible on the template page in the application.
* @param emailAddress The email address
* @param personalisation Map representing the placeholders for the template if any. For example, key=name value=Bob
* Can be an empty map or null when the template does not require placeholders.
* @param reference A reference specified by the service for the notification. Get all notifications can be filtered by this reference.
* This reference can be unique or used used to refer to a batch of notifications.
* Can be an empty string or null, when you do not require a reference for the notifications.
* @param emailReplyToId An optional identifier for a reply to email address for the notification, rather than use the service default.
* Service emailReplyToIds can be accessed via the service settings / manage email reply to addresses page.
* Omit this argument to use the default service email reply to address.
* @param oneClickUnsubscribeURL A link so users can unsubscribe, see https://www.notifications.service.gov.uk/using-notify/unsubscribe-links
* @param sanitiseContentFor A list of placeholders, the content of which should be sanitised
* by Notify. Notify will remove links and escape Markdown characters for the selected content.
* @return <code>SendEmailResponse</code>
* @throws NotificationClientException see https://docs.notifications.service.gov.uk/java.html#send-an-email-error-codes
*/
SendEmailResponse sendEmail(String templateId, String emailAddress, Map<String, ?> personalisation, String reference, String emailReplyToId, URI oneClickUnsubscribeURL, List<String> sanitiseContentFor) throws NotificationClientException;

/**
* The sendSms method will create an HTTPS POST request. A JWT token will be created and added as an Authorization header to the request.
*
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/uk/gov/service/notify/SendEmailResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class SendEmailResponse {
private final String subject;
private final String fromEmail;
private final URI oneClickUnsubscribeURL;
private final JSONObject sanitisedContent;

public SendEmailResponse(String response) {
JSONObject data = new JSONObject(response);
Expand All @@ -30,6 +31,7 @@ public SendEmailResponse(String response) {
templateVersion = template.getInt("version");
templateUri = template.getString("uri");
oneClickUnsubscribeURL = data.isNull("one_click_unsubscribe_url") ? null : URI.create(data.getString("one_click_unsubscribe_url"));
sanitisedContent = data.isNull("sanitised_content") ? null : data.getJSONObject("sanitised_content");
}

public UUID getNotificationId() {
Expand Down Expand Up @@ -68,6 +70,10 @@ public Optional<URI> getOneClickUnsubscribeURL() {
return Optional.ofNullable(oneClickUnsubscribeURL);
}

public Optional<JSONObject> getSanitisedContent() {
return Optional.ofNullable(sanitisedContent);
}

@Override
public String toString() {
return "SendEmailResponse{" +
Expand All @@ -80,6 +86,7 @@ public String toString() {
", subject='" + subject + '\'' +
", fromEmail='" + fromEmail + '\'' +
", oneClickUnsubscribeURL=" + oneClickUnsubscribeURL +
", sanitisedContent=" + sanitisedContent +
'}';
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
# - PATCH version when you make backwards-compatible bug fixes.
#
# -- http://semver.org/
project.version=6.0.1-RELEASE
project.version=6.1.0-RELEASE
39 changes: 39 additions & 0 deletions src/test/java/uk/gov/service/notify/NotificationClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -392,6 +393,44 @@ public void testSendEmailWithUnsubscribeURL() throws NotificationClientException
assertEquals(oneClickUnsubscribeURL, requestReceivedByNotifyApi.getOneClickUnsubscribeURL());
}

@Test
public void testSendEmailWithsanitiseContentFor() throws NotificationClientException, IOException {
NotifyEmailResponse expected = objectMapper.readValue(this.getClass().getClassLoader().getResourceAsStream("v2_notifications_email_sanitise_personalisation_response.json"), NotifyEmailResponse.class);
wireMockRule.stubFor(post("/v2/notifications/email")
.willReturn(created()
.withResponseBody(new Body(objectMapper.writeValueAsString(expected)))));
NotificationClient client = new NotificationClient(COMBINED_API_KEY, BASE_URL);
// setting up this map can be replaced with Map.of() in later Java versions
Map<String, Object> personalisation = new HashMap<>();
personalisation.put("name", "Anne Example, now [click this evil link](https://evil.link)");
URI oneClickUnsubscribeURL = URI.create("http://localhost/unsubscribe");
String[] placeholdersToSanitise = {"name"};
List<String> sanitiseContentFor = Arrays.asList(placeholdersToSanitise);

SendEmailResponse actual = client.sendEmail("aTemplateId", "anEmailAddress", personalisation, "aReference", "an emailReplyToId", oneClickUnsubscribeURL, sanitiseContentFor);

assertEquals(expected.getNotificationId(), actual.getNotificationId());
assertEquals(expected.getReference(), actual.getReference().get());
assertEquals(expected.getContent().getBody(), actual.getBody());
assertEquals(expected.getContent().getSubject(), actual.getSubject());
assertEquals(expected.getContent().getFromEmail(), actual.getFromEmail().get());
// No notification uri in SendEmailResponse?
// assertEquals(expected.getUri(), actual.getUri());
assertEquals(expected.getTemplate().getId(), actual.getTemplateId());
assertEquals(expected.getTemplate().getVersion(), actual.getTemplateVersion());
assertEquals(expected.getTemplate().getUri().toString(), actual.getTemplateUri());
assertEquals(objectMapper.writeValueAsString(expected.getSanitisedContent()), actual.getSanitisedContent().orElseGet(JSONObject::new).toString());

LoggedRequest request = validateRequest();
NotifyEmailRequest requestReceivedByNotifyApi = objectMapper.readValue(request.getBodyAsString(), NotifyEmailRequest.class);
assertEquals("anEmailAddress", requestReceivedByNotifyApi.getEmailAddress());
assertEquals("aTemplateId", requestReceivedByNotifyApi.getTemplateId());
assertEquals(personalisation, requestReceivedByNotifyApi.getPersonalisation());
assertEquals("aReference", requestReceivedByNotifyApi.getReference());
assertEquals("an emailReplyToId", requestReceivedByNotifyApi.getEmailReplyToId());
assertEquals(sanitiseContentFor, requestReceivedByNotifyApi.getSanitiseContentFor());
}

@Test
public void testSendSmsHandlesErrors() {
wireMockRule.stubFor(post("/v2/notifications/sms")
Expand Down
30 changes: 22 additions & 8 deletions src/test/java/uk/gov/service/notify/domain/NotifyEmailRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Objects;

Expand All @@ -14,19 +15,24 @@ public class NotifyEmailRequest {
private final String reference;
private final String emailReplyToId;
private final URI oneClickUnsubscribeURL;
private final List<String> sanitiseContentFor;

public NotifyEmailRequest(@JsonProperty("email_address") String emailAddress,
@JsonProperty("template_id") String templateId,
@JsonProperty("personalisation") Map<String, ?> personalisation,
@JsonProperty("reference") String reference,
@JsonProperty("email_reply_to_id") String emailReplyToId,
@JsonProperty("one_click_unsubscribe_url") URI oneClickUnsubscribeURL) {
public NotifyEmailRequest(
@JsonProperty("email_address") String emailAddress,
@JsonProperty("template_id") String templateId,
@JsonProperty("personalisation") Map<String, ?> personalisation,
@JsonProperty("reference") String reference,
@JsonProperty("email_reply_to_id") String emailReplyToId,
@JsonProperty("one_click_unsubscribe_url") URI oneClickUnsubscribeURL,
@JsonProperty("sanitise_content_for") List<String> sanitiseContentFor
) {
this.emailAddress = emailAddress;
this.templateId = templateId;
this.personalisation = personalisation;
this.reference = reference;
this.emailReplyToId = emailReplyToId;
this.oneClickUnsubscribeURL = oneClickUnsubscribeURL;
this.sanitiseContentFor = sanitiseContentFor;
}

@JsonProperty("email_address")
Expand Down Expand Up @@ -59,17 +65,24 @@ public URI getOneClickUnsubscribeURL() {
return oneClickUnsubscribeURL;
}

@JsonProperty("sanitise_content_for")
public List<String> getSanitiseContentFor() {
return sanitiseContentFor;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NotifyEmailRequest request = (NotifyEmailRequest) o;
return Objects.equals(emailAddress, request.emailAddress) && Objects.equals(templateId, request.templateId) && Objects.equals(personalisation, request.personalisation) && Objects.equals(reference, request.reference) && Objects.equals(emailReplyToId, request.emailReplyToId) && Objects.equals(oneClickUnsubscribeURL, request.oneClickUnsubscribeURL);
return Objects.equals(emailAddress, request.emailAddress) && Objects.equals(templateId, request.templateId) && Objects.equals(personalisation, request.personalisation) && Objects.equals(reference, request.reference) && Objects.equals(emailReplyToId, request.emailReplyToId) && Objects.equals(oneClickUnsubscribeURL, request.oneClickUnsubscribeURL) && Objects.equals(sanitiseContentFor, request.sanitiseContentFor);
}

@Override
public int hashCode() {
return Objects.hash(emailAddress, templateId, personalisation, reference, emailReplyToId, oneClickUnsubscribeURL);
return Objects.hash(
emailAddress, templateId, personalisation, reference, emailReplyToId, oneClickUnsubscribeURL, sanitiseContentFor
);
}

@Override
Expand All @@ -81,6 +94,7 @@ public String toString() {
", reference='" + reference + '\'' +
", emailReplyToId='" + emailReplyToId + '\'' +
", oneClickUnsubscribeURL=" + oneClickUnsubscribeURL +
", sanitiseContentFor=" + sanitiseContentFor +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;

import java.net.URI;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

Expand Down Expand Up @@ -116,19 +117,24 @@ public String toString() {
private final URI uri;
private final Template template;
private final URI oneClickUnsubscribeURL;

public NotifyEmailResponse(@JsonProperty("id") UUID notificationId,
@JsonProperty("reference") String reference,
@JsonProperty("content") Content content,
@JsonProperty("uri") URI uri,
@JsonProperty("template") Template template,
@JsonProperty("one_click_unsubscribe_url") URI oneClickUnsubscribeURL) {
private final Map<String, Map<String, String>> sanitisedContent;

public NotifyEmailResponse(
@JsonProperty("id") UUID notificationId,
@JsonProperty("reference") String reference,
@JsonProperty("content") Content content,
@JsonProperty("uri") URI uri,
@JsonProperty("template") Template template,
@JsonProperty("one_click_unsubscribe_url") URI oneClickUnsubscribeURL,
@JsonProperty("sanitised_content") Map<String, Map<String, String>> sanitisedContent
) {
this.notificationId = notificationId;
this.reference = reference;
this.content = content;
this.uri = uri;
this.template = template;
this.oneClickUnsubscribeURL = oneClickUnsubscribeURL;
this.sanitisedContent = sanitisedContent;
}

@JsonProperty("id")
Expand Down Expand Up @@ -161,17 +167,22 @@ public URI getOneClickUnsubscribeURL() {
return oneClickUnsubscribeURL;
}

@JsonProperty("sanitised_content")
public Map<String, Map<String, String>> getSanitisedContent() {
return sanitisedContent;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NotifyEmailResponse that = (NotifyEmailResponse) o;
return Objects.equals(notificationId, that.notificationId) && Objects.equals(reference, that.reference) && Objects.equals(content, that.content) && Objects.equals(uri, that.uri) && Objects.equals(template, that.template) && Objects.equals(oneClickUnsubscribeURL, that.oneClickUnsubscribeURL);
return Objects.equals(notificationId, that.notificationId) && Objects.equals(reference, that.reference) && Objects.equals(content, that.content) && Objects.equals(uri, that.uri) && Objects.equals(template, that.template) && Objects.equals(oneClickUnsubscribeURL, that.oneClickUnsubscribeURL) && Objects.equals(sanitisedContent, that.sanitisedContent);
}

@Override
public int hashCode() {
return Objects.hash(notificationId, reference, content, uri, template, oneClickUnsubscribeURL);
return Objects.hash(notificationId, reference, content, uri, template, oneClickUnsubscribeURL, sanitisedContent);
}

@Override
Expand All @@ -183,6 +194,7 @@ public String toString() {
", uri=" + uri +
", template=" + template +
", oneClickUnsubscribeURL=" + oneClickUnsubscribeURL +
", sanitisedContent=" + sanitisedContent +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"id": "7dd0b55b-fca1-496f-b52a-787692045f71",
"reference": "a reference",
"content": {
"body": "a content`body",
"subject": "a subject",
"from_email": "a from email"
},
"uri": null,
"template": {
"id": "cc378915-2e6c-4e04-86a7-5f71949582b7",
"version": -1,
"uri": "http://localhost"
},
"sanitised_content": {
"name": {
"unsanitised": "Anne Example, now [click this evil link](https://evil.link)",
"sanitised": "Anne Example, now \\[click this evil link\\]\\(\\)"
}
}
}