summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEirik Nygaard <eirik.nygaard@yahooinc.com>2022-11-24 13:01:43 +0100
committerGitHub <noreply@github.com>2022-11-24 13:01:43 +0100
commit2edd631a79e96791d9638f7b3a80b15c9f94d1f7 (patch)
tree32a42254d55bbf91fb0274b23dec084d2a489084
parent10c2f711e234065c193aee3b413081ac7f86290e (diff)
parent5a6b8f759f5d41ee9c7801f57e205d1499df84fd (diff)
Merge pull request #24971 from vespa-engine/ogronnesby/notification-format
Ogronnesby/notification format
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Mailer.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMailer.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java23
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java44
-rw-r--r--controller-server/src/main/resources/mail/mail-notification.tmpl647
-rw-r--r--controller-server/src/main/resources/mail/mail-verification.tmpl494
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java19
-rw-r--r--controller-server/src/test/resources/mail/notification.txt647
10 files changed, 1844 insertions, 45 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Mailer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Mailer.java
index 27eec34cf4e..187f140f646 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Mailer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/organization/Mailer.java
@@ -19,6 +19,4 @@ public interface Mailer {
/** Returns the domain this is configured to use. */
String domain();
- void sendVerificationMail(PendingMailVerification pendingMailVerification);
-
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMailer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMailer.java
index dcfa9b7ea07..c2f53e3457d 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMailer.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/stubs/MockMailer.java
@@ -48,11 +48,6 @@ public class MockMailer implements Mailer {
return "domain";
}
- @Override
- public void sendVerificationMail(PendingMailVerification pendingMailVerification) {
- send(new Mail(List.of(pendingMailVerification.getMailAddress()), "subject", "message"));
- }
-
/** Returns the list of mails sent to the given recipient. Modifications affect the set of mails stored in this. */
public List<Mail> inbox(String recipient) {
return mails.getOrDefault(recipient, List.of());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
index b40db745fd7..9a7fffd2a9e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
@@ -126,7 +126,7 @@ public class Controller extends AbstractComponent {
notifier = new Notifier(curator, serviceRegistry.zoneRegistry(), serviceRegistry.mailer(), flagSource);
notificationsDb = new NotificationsDb(this);
supportAccessControl = new SupportAccessControl(this);
- mailVerifier = new MailVerifier(tenantController, serviceRegistry.mailer(), curator, clock);
+ mailVerifier = new MailVerifier(serviceRegistry.zoneRegistry().dashboardUrl(), tenantController, serviceRegistry.mailer(), curator, clock);
// Record the version of this controller
curator().writeControllerVersion(this.hostname(), serviceRegistry.controllerVersion());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java
index a7f3a3ca3b2..902343d5acf 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/MailVerifier.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.api.integration.organization.Mail;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Mailer;
import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
@@ -10,11 +11,15 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantContacts;
import com.yahoo.vespa.hosted.controller.tenant.TenantInfo;
import com.yahoo.vespa.hosted.controller.tenant.PendingMailVerification;
+import java.net.URI;
import java.time.Clock;
import java.time.Duration;
+import java.util.List;
import java.util.Optional;
import java.util.UUID;
+import static com.yahoo.yolean.Exceptions.uncheck;
+
/**
* @author olaa
@@ -25,13 +30,16 @@ public class MailVerifier {
private final Mailer mailer;
private final CuratorDb curatorDb;
private final Clock clock;
+ private final URI dashboardUri;
private static final Duration VERIFICATION_DEADLINE = Duration.ofDays(7);
- public MailVerifier(TenantController tenantController, Mailer mailer, CuratorDb curatorDb, Clock clock) {
+
+ public MailVerifier(URI dashboardUri, TenantController tenantController, Mailer mailer, CuratorDb curatorDb, Clock clock) {
this.tenantController = tenantController;
this.mailer = mailer;
this.curatorDb = curatorDb;
this.clock = clock;
+ this.dashboardUri = dashboardUri;
}
public PendingMailVerification sendMailVerification(TenantName tenantName, String email, PendingMailVerification.MailType mailType) {
@@ -43,7 +51,7 @@ public class MailVerifier {
var verificationDeadline = clock.instant().plus(VERIFICATION_DEADLINE);
var pendingMailVerification = new PendingMailVerification(tenantName, email, verificationCode, verificationDeadline, mailType);
writePendingVerification(pendingMailVerification);
- mailer.sendVerificationMail(pendingMailVerification);
+ mailer.send(mailOf(pendingMailVerification));
return pendingMailVerification;
}
@@ -113,4 +121,15 @@ public class MailVerifier {
.map(CloudTenant.class::cast)
.orElseThrow(() -> new IllegalStateException("Mail verification is only applicable for cloud tenants"));
}
+
+ private Mail mailOf(PendingMailVerification pendingMailVerification) {
+ var classLoader = this.getClass().getClassLoader();
+ var template = uncheck(() -> classLoader.getResourceAsStream("mail/mail-verification.tmpl").readAllBytes());
+ var message = new String(template)
+ .replaceAll("%\\{consoleUrl}", dashboardUri.getHost())
+ .replaceAll("%\\{email}", pendingMailVerification.getMailAddress())
+ .replaceAll("%\\{code}", pendingMailVerification.getVerificationCode());
+ return new Mail(List.of(pendingMailVerification.getMailAddress()), "Please verify your email", "", message);
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java
index 1074649ca7a..526bf0c53b3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/notification/Notifier.java
@@ -3,6 +3,8 @@ package com.yahoo.vespa.hosted.controller.notification;
import com.google.common.annotations.VisibleForTesting;
import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.restapi.UriBuilder;
import com.yahoo.text.Text;
import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.FlagSource;
@@ -15,6 +17,7 @@ import com.yahoo.vespa.hosted.controller.persistence.CuratorDb;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
import com.yahoo.vespa.hosted.controller.tenant.TenantContacts;
+import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
@@ -23,6 +26,8 @@ import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import static com.yahoo.yolean.Exceptions.uncheck;
+
/**
* Notifier is responsible for dispatching user notifications to their chosen Contact points.
*
@@ -43,6 +48,7 @@ public class Notifier {
private final Mailer mailer;
private final FlagSource flagSource;
private final NotificationFormatter formatter;
+ private final URI dashboardUri;
private static final Logger log = Logger.getLogger(Notifier.class.getName());
@@ -54,6 +60,7 @@ public class Notifier {
this.mailer = Objects.requireNonNull(mailer);
this.flagSource = Objects.requireNonNull(flagSource);
this.formatter = new NotificationFormatter(zoneRegistry);
+ this.dashboardUri = zoneRegistry.dashboardUrl();
}
public void dispatch(List<Notification> notifications, NotificationSource source) {
@@ -124,24 +131,18 @@ public class Notifier {
public Mail mailOf(FormattedNotification content, Collection<String> recipients) {
var notification = content.notification();
var subject = Text.format("[%s] %s Vespa Notification for %s", notification.level().toString().toUpperCase(), content.prettyType(), applicationIdSource(notification.source()));
- String body = new StringBuilder()
- .append(content.messagePrefix()).append("\n")
- .append(notification.messages().stream().map(m -> " * " + m).collect(Collectors.joining("\n"))).append("\n")
- .append("\n")
- .append("Vespa Console link:\n")
- .append(content.uri().toString()).toString();
- String html = new StringBuilder()
- .append(header)
- .append(content.messagePrefix()).append("<br>\n")
- .append("<ul>\n")
- .append(notification.messages().stream()
+ var template = uncheck(() -> Notifier.class.getResourceAsStream("/mail/mail-notification.tmpl").readAllBytes());
+ var html = new String(template)
+ .replace("[[NOTIFICATION_HEADER]]", content.messagePrefix())
+ .replace("[[NOTIFICATION_ITEMS]]", notification.messages().stream()
.map(Notifier::linkify)
.map(m -> "<li>" + m + "</li>")
- .collect(Collectors.joining("<br>\n")))
- .append("</ul>\n")
- .append("<br>\n")
- .append("<a href=\"" + content.uri() + "\">Vespa Console</a>").toString();
- return new Mail(recipients, subject, body, html);
+ .collect(Collectors.joining()))
+ .replace("[[LINK_TO_ACCOUNT_NOTIFICATIONS]]", accountNotificationsUri(content.notification().source().tenant()))
+ .replace("[[LINK_TO_PRIVACY_POLICY]]", "https://legal.yahoo.com/xw/en/yahoo/privacy/topic/b2bprivacypolicy/index.html")
+ .replace("[[LINK_TO_TERMS_OF_SERVICE]]", consoleUri("terms-of-service-trial.html"))
+ .replace("[[LINK_TO_SUPPORT]]", consoleUri("support"));
+ return new Mail(recipients, subject, "", html);
}
@VisibleForTesting
@@ -157,5 +158,16 @@ public class Notifier {
return sb.toString();
}
+ private String accountNotificationsUri(TenantName tenant) {
+ return new UriBuilder(dashboardUri)
+ .append("tenant/")
+ .append(tenant.value())
+ .append("account/notifications")
+ .toString();
+ }
+
+ private String consoleUri(String path) {
+ return new UriBuilder(dashboardUri).append(path).toString();
+ }
}
diff --git a/controller-server/src/main/resources/mail/mail-notification.tmpl b/controller-server/src/main/resources/mail/mail-notification.tmpl
new file mode 100644
index 00000000000..868a3a76bab
--- /dev/null
+++ b/controller-server/src/main/resources/mail/mail-notification.tmpl
@@ -0,0 +1,647 @@
+<!DOCTYPE html>
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:v="urn:schemas-microsoft-com:vml"
+ xmlns:o="urn:schemas-microsoft-com:office:office"
+>
+ <head>
+ <title></title>
+ <!--[if !mso]><!-->
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <!--<![endif]-->
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <style type="text/css">
+ #outlook a {
+ padding: 0;
+ }
+
+ body {
+ margin: 0;
+ padding: 0;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ }
+
+ table,
+ td {
+ border-collapse: collapse;
+ mso-table-lspace: 0pt;
+ mso-table-rspace: 0pt;
+ }
+
+ img {
+ border: 0;
+ height: auto;
+ line-height: 100%;
+ outline: none;
+ text-decoration: none;
+ -ms-interpolation-mode: bicubic;
+ }
+
+ p {
+ display: block;
+ margin: 13px 0;
+ }
+ </style>
+ <!--[if mso]>
+ <noscript>
+ <xml>
+ <o:OfficeDocumentSettings>
+ <o:AllowPNG />
+ <o:PixelsPerInch>96</o:PixelsPerInch>
+ </o:OfficeDocumentSettings>
+ </xml>
+ </noscript>
+ <![endif]-->
+ <!--[if lte mso 11]>
+ <style type="text/css">
+ .mj-outlook-group-fix {
+ width: 100% !important;
+ }
+ </style>
+ <![endif]-->
+ <!--[if !mso]><!-->
+ <link
+ href="https://fonts.googleapis.com/css?family=Open Sans"
+ rel="stylesheet"
+ type="text/css"
+ />
+ <style type="text/css">
+ @import url(https://fonts.googleapis.com/css?family=Open Sans);
+ </style>
+ <!--<![endif]-->
+ <style type="text/css">
+ @media only screen and (min-width: 480px) {
+ .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ }
+ </style>
+ <style media="screen and (min-width:480px)">
+ .moz-text-html .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ </style>
+ <style type="text/css">
+ [owa] .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ </style>
+ <style type="text/css">
+ @media only screen and (max-width: 480px) {
+ table.mj-full-width-mobile {
+ width: 100% !important;
+ }
+
+ td.mj-full-width-mobile {
+ width: auto !important;
+ }
+ }
+ </style>
+ </head>
+
+ <body style="word-spacing: normal; background-color: #f2f7fa">
+ <div style="background-color: #f2f7fa">
+ <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div style="margin: 0px auto; max-width: 600px">
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0px 20px 0px;
+ padding-bottom: 0px;
+ padding-top: 0px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 0px 0px 25px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 11px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <br />
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div
+ style="
+ background: #ffffff;
+ background-color: #ffffff;
+ margin: 0px auto;
+ max-width: 600px;
+ "
+ >
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="background: #ffffff; background-color: #ffffff; width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0;
+ padding-bottom: 0px;
+ padding-left: 0px;
+ padding-right: 0px;
+ padding-top: 0px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 0px;
+ padding-right: 0px;
+ padding-bottom: 40px;
+ padding-left: 0px;
+ word-break: break-word;
+ "
+ >
+ <p
+ style="
+ border-top: solid 8px #005a8e;
+ font-size: 1px;
+ margin: 0px auto;
+ width: 100%;
+ "
+ ></p>
+ <!--[if mso | IE
+ ]><table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ style="
+ border-top: solid 8px #005a8e;
+ font-size: 1px;
+ margin: 0px auto;
+ width: 600px;
+ "
+ role="presentation"
+ width="600px"
+ >
+ <tr>
+ <td style="height: 0; line-height: 0">
+ &nbsp;
+ </td>
+ </tr>
+ </table><!
+ [endif]-->
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ word-break: break-word;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="
+ border-collapse: collapse;
+ border-spacing: 0px;
+ "
+ >
+ <tbody>
+ <tr>
+ <td style="width: 121px">
+ <img
+ alt=""
+ height="auto"
+ src="https://data.vespa.oath.cloud/assets/vespa-cloud-logo.png"
+ style="
+ border: none;
+ display: block;
+ outline: none;
+ text-decoration: none;
+ height: auto;
+ width: 100%;
+ font-size: 13px;
+ "
+ width="121"
+ />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div
+ style="
+ background: #ffffff;
+ background-color: #ffffff;
+ margin: 0px auto;
+ max-width: 600px;
+ "
+ >
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="background: #ffffff; background-color: #ffffff; width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0px 20px 0px;
+ padding-bottom: 70px;
+ padding-top: 30px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 25px 0px 25px;
+ padding-top: 0px;
+ padding-right: 50px;
+ padding-bottom: 0px;
+ padding-left: 50px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <h1
+ style="
+ text-align: center;
+ color: #000000;
+ line-height: 32px;
+ "
+ >
+ Vespa cloud notifications
+ </h1>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 25px 0px 25px;
+ padding-top: 0px;
+ padding-right: 50px;
+ padding-bottom: 0px;
+ padding-left: 50px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <p>
+ [[NOTIFICATION_HEADER]]
+ </p>
+ <ul>
+ [[NOTIFICATION_ITEMS]]
+ </ul>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="center"
+ vertical-align="middle"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ word-break: break-word;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="border-collapse: separate; line-height: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ bgcolor="#005A8E"
+ role="presentation"
+ style="
+ border: none;
+ border-radius: 100px;
+ cursor: auto;
+ mso-padding-alt: 15px 25px 15px 25px;
+ background: #005a8e;
+ "
+ valign="middle"
+ >
+ <a
+ href="#"
+ style="
+ display: inline-block;
+ background: #005a8e;
+ color: #ffffff;
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 120%;
+ margin: 0;
+ text-decoration: none;
+ text-transform: none;
+ padding: 15px 25px 15px 25px;
+ mso-padding-alt: 0px;
+ border-radius: 100px;
+ "
+ target="_blank"
+ ><b style="font-weight: 700"
+ ><b style="font-weight: 700"
+ >Go to Console</b
+ ></b
+ ></a
+ >
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div style="margin: 0px auto; max-width: 600px">
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0px 20px 0px;
+ padding-bottom: 0px;
+ padding-top: 20px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 0px 20px 0px 20px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 11px;
+ line-height: 22px;
+ text-align: center;
+ color: #797e82;
+ "
+ >
+ <p style="margin: 10px 0">
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: #005a8e"
+ href="[[LINK_TO_PRIVACY_POLICY]]"
+ ><span style="color: #005a8e"
+ >Yahoo Privacy Policy</span
+ ></a
+ ><span style="color: #797e82"
+ >&nbsp; &nbsp;|&nbsp; &nbsp;</span
+ ><a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: #005a8e"
+ href="[[LINK_TO_TERMS_OF_SERVICE]]"
+ ><span style="color: #005a8e"
+ >Terms of Service</span
+ ></a
+ ><span style="color: #797e82"
+ >&nbsp; &nbsp;|&nbsp; &nbsp;</span
+ ><a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: #005a8e"
+ href="[[LINK_TO_SUPPORT]]"
+ ><span style="color: #005a8e">Support</span></a
+ >
+ </p>
+ <p style="margin: 10px 0">
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: inherit; text-decoration: none"
+ href="[[LINK_TO_ACCOUNT_NOTIFICATIONS]]"
+ >Click
+ <span style="color: #005a8e"><u>here</u></span>
+ to manage your notifications setting.</a
+ ><br />
+ </p>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </div>
+ </body>
+</html>
diff --git a/controller-server/src/main/resources/mail/mail-verification.tmpl b/controller-server/src/main/resources/mail/mail-verification.tmpl
new file mode 100644
index 00000000000..8a473e74755
--- /dev/null
+++ b/controller-server/src/main/resources/mail/mail-verification.tmpl
@@ -0,0 +1,494 @@
+<!DOCTYPE html>
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:v="urn:schemas-microsoft-com:vml"
+ xmlns:o="urn:schemas-microsoft-com:office:office"
+>
+ <head>
+ <title></title>
+ <!--[if !mso]><!-->
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <!--<![endif]-->
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <style type="text/css">
+ #outlook a {
+ padding: 0;
+ }
+
+ body {
+ margin: 0;
+ padding: 0;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ }
+
+ table,
+ td {
+ border-collapse: collapse;
+ mso-table-lspace: 0pt;
+ mso-table-rspace: 0pt;
+ }
+
+ img {
+ border: 0;
+ height: auto;
+ line-height: 100%;
+ outline: none;
+ text-decoration: none;
+ -ms-interpolation-mode: bicubic;
+ }
+
+ p {
+ display: block;
+ margin: 13px 0;
+ }
+ </style>
+ <!--[if mso]>
+ <noscript>
+ <xml>
+ <o:OfficeDocumentSettings>
+ <o:AllowPNG />
+ <o:PixelsPerInch>96</o:PixelsPerInch>
+ </o:OfficeDocumentSettings>
+ </xml>
+ </noscript>
+ <![endif]-->
+ <!--[if lte mso 11]>
+ <style type="text/css">
+ .mj-outlook-group-fix {
+ width: 100% !important;
+ }
+ </style>
+ <![endif]-->
+ <!--[if !mso]><!-->
+ <link
+ href="https://fonts.googleapis.com/css?family=Open Sans"
+ rel="stylesheet"
+ type="text/css"
+ />
+ <style type="text/css">
+ @import url(https://fonts.googleapis.com/css?family=Open Sans);
+ </style>
+ <!--<![endif]-->
+ <style type="text/css">
+ @media only screen and (min-width: 480px) {
+ .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ }
+ </style>
+ <style media="screen and (min-width:480px)">
+ .moz-text-html .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ </style>
+ <style type="text/css">
+ [owa] .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ </style>
+ <style type="text/css">
+ @media only screen and (max-width: 480px) {
+ table.mj-full-width-mobile {
+ width: 100% !important;
+ }
+
+ td.mj-full-width-mobile {
+ width: auto !important;
+ }
+ }
+ </style>
+ </head>
+
+ <body style="word-spacing: normal; background-color: #f8f8f8">
+ <div style="background-color: #f8f8f8">
+ <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div
+ style="
+ background: #ffffff;
+ background-color: #ffffff;
+ margin: 0px auto;
+ max-width: 600px;
+ "
+ >
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="background: #ffffff; background-color: #ffffff; width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0;
+ padding-bottom: 0px;
+ padding-left: 0px;
+ padding-right: 0px;
+ padding-top: 0px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 0px;
+ padding-right: 0px;
+ padding-bottom: 40px;
+ padding-left: 0px;
+ word-break: break-word;
+ "
+ >
+ <p
+ style="
+ border-top: solid 7px #3b9fde;
+ font-size: 1px;
+ margin: 0px auto;
+ width: 100%;
+ "
+ ></p>
+ <!--[if mso | IE
+ ]><table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ style="
+ border-top: solid 7px #3b9fde;
+ font-size: 1px;
+ margin: 0px auto;
+ width: 600px;
+ "
+ role="presentation"
+ width="600px"
+ >
+ <tr>
+ <td style="height: 0; line-height: 0">
+ &nbsp;
+ </td>
+ </tr>
+ </table><!
+ [endif]-->
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ word-break: break-word;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="
+ border-collapse: collapse;
+ border-spacing: 0px;
+ "
+ >
+ <tbody>
+ <tr>
+ <td style="width: 110px">
+ <img
+ alt=""
+ height="auto"
+ src="https://data.vespa.oath.cloud/assets/vespa-icon.png"
+ style="
+ border: none;
+ display: block;
+ outline: none;
+ text-decoration: none;
+ height: auto;
+ width: 100%;
+ font-size: 13px;
+ "
+ width="110"
+ />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div
+ style="
+ background: #ffffff;
+ background-color: #ffffff;
+ margin: 0px auto;
+ max-width: 600px;
+ "
+ >
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="background: #ffffff; background-color: #ffffff; width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0px 20px 0px;
+ padding-bottom: 70px;
+ padding-top: 30px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 25px 0px 25px;
+ padding-top: 0px;
+ padding-right: 50px;
+ padding-bottom: 0px;
+ padding-left: 50px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <h1
+ style="
+ text-align: center;
+ color: #000000;
+ line-height: 32px;
+ "
+ >
+ Verify your email address
+ </h1>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 25px 0px 25px;
+ padding-top: 0px;
+ padding-right: 50px;
+ padding-bottom: 0px;
+ padding-left: 50px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <p style="margin: 10px 0; text-align: center">
+ You have entered the email address <b>%{email}</b> in
+ Vespa Cloud.&nbsp;
+ </p>
+ <p style="margin: 10px 0; text-align: center">
+ Please verify this email address by clicking the
+ button below.
+ </p>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="center"
+ vertical-align="middle"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ word-break: break-word;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="border-collapse: separate; line-height: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ bgcolor="#3B9FDE"
+ role="presentation"
+ style="
+ border: none;
+ border-radius: 100px;
+ cursor: auto;
+ mso-padding-alt: 15px 25px 15px 25px;
+ background: #3b9fde;
+ "
+ valign="middle"
+ >
+ <a
+ href="https://%{consoleUrl}/verify?code=%{code}"
+ style="
+ display: inline-block;
+ background: #3b9fde;
+ color: #ffffff;
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 120%;
+ margin: 0;
+ text-decoration: none;
+ text-transform: none;
+ padding: 15px 25px 15px 25px;
+ mso-padding-alt: 0px;
+ border-radius: 100px;
+ "
+ target="_blank"
+ ><b style="font-weight: 700"
+ ><b style="font-weight: 700"
+ >Verify your email</b
+ ></b
+ ></a
+ >
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 25px 0px 25px;
+ padding-top: 0px;
+ padding-right: 50px;
+ padding-bottom: 0px;
+ padding-left: 50px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <p style="margin: 10px 0; text-align: center">
+ Or copy and paste this link into your browser
+ </p>
+ <p style="margin: 10px 0; text-align: center">
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ href="https://%{consoleUrl}/verify?code=%{code}"
+ style="color: #3b9fde"
+ >https://%{consoleUrl}/verify?code=%{code}</a
+ >
+ </p>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </div>
+ </body>
+</html>
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
index 873ab435444..edea07e205c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MailVerifierTest.java
@@ -12,9 +12,11 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantContacts;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.net.URI;
import java.time.Duration;
import java.util.List;
+import static com.yahoo.yolean.Exceptions.uncheck;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -27,7 +29,7 @@ class MailVerifierTest {
private final ControllerTester tester = new ControllerTester(SystemName.Public);
private final MockMailer mailer = tester.serviceRegistry().mailer();
- private final MailVerifier mailVerifier = new MailVerifier(tester.controller().tenants(), mailer, tester.curator(), tester.clock());
+ private final MailVerifier mailVerifier = new MailVerifier(URI.create("https://dashboard.uri.example.com"), tester.controller().tenants(), mailer, tester.curator(), tester.clock());
private static final TenantName tenantName = TenantName.from("scoober");
private static final String mail = "unverified@bar.com";
@@ -53,9 +55,7 @@ class MailVerifierTest {
mailVerifier.sendMailVerification(tenantName, mail, PendingMailVerification.MailType.NOTIFICATIONS);
// Verify mail is sent
- var expectedMail = "message";
assertEquals(1, mailer.inbox(mail).size());
- assertEquals(expectedMail, mailer.inbox(mail).get(0).message());
// Verify ZK data is updated
var writtenMailVerification = tester.curator().listPendingMailVerifications().get(0);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java
index c0501206494..ecf42979b40 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/notification/NotifierTest.java
@@ -20,6 +20,7 @@ import com.yahoo.vespa.hosted.controller.tenant.TenantInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Map;
@@ -54,7 +55,7 @@ public class NotifierTest {
}
@Test
- void dispatch() {
+ void dispatch() throws IOException {
var mailer = new MockMailer();
var flagSource = new InMemoryFlagSource().withBooleanFlag(Flags.NOTIFICATION_DISPATCH_FLAG.id(), true);
var notifier = new Notifier(curatorDb, new ZoneRegistryMock(SystemName.cd), mailer, flagSource);
@@ -68,21 +69,7 @@ public class NotifierTest {
var mail = mailer.inbox(email.getEmailAddress()).get(0);
assertEquals("[WARNING] Test package Vespa Notification for tenant1.default.default", mail.subject());
- assertEquals("""
- <div style="background: #00598c; height: 55px; width: 100%">
- <img
- src="https://vespa.ai/assets/vespa-logo.png"
- style="width: auto; height: 34px; margin: 10px"
- />
- </div>
- <br>
- There are problems with tests for default.default<br>
- <ul>
- <li>test package has production tests, but no production tests are declared in deployment.xml</li><br>
- <li>see <a href="https://docs.vespa.ai/en/testing.html">https://docs.vespa.ai/en/testing.html</a> for details on how to write system tests for Vespa</li></ul>
- <br>
- <a href="https://dashboard.tld/tenant1/default">Vespa Console</a>""",
- mail.htmlMessage().get());
+ assertEquals(new String(NotifierTest.class.getResourceAsStream("/mail/notification.txt").readAllBytes()), mail.htmlMessage().get());
}
@Test
diff --git a/controller-server/src/test/resources/mail/notification.txt b/controller-server/src/test/resources/mail/notification.txt
new file mode 100644
index 00000000000..5ca26cb4753
--- /dev/null
+++ b/controller-server/src/test/resources/mail/notification.txt
@@ -0,0 +1,647 @@
+<!DOCTYPE html>
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:v="urn:schemas-microsoft-com:vml"
+ xmlns:o="urn:schemas-microsoft-com:office:office"
+>
+ <head>
+ <title></title>
+ <!--[if !mso]><!-->
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <!--<![endif]-->
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
+ <style type="text/css">
+ #outlook a {
+ padding: 0;
+ }
+
+ body {
+ margin: 0;
+ padding: 0;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ }
+
+ table,
+ td {
+ border-collapse: collapse;
+ mso-table-lspace: 0pt;
+ mso-table-rspace: 0pt;
+ }
+
+ img {
+ border: 0;
+ height: auto;
+ line-height: 100%;
+ outline: none;
+ text-decoration: none;
+ -ms-interpolation-mode: bicubic;
+ }
+
+ p {
+ display: block;
+ margin: 13px 0;
+ }
+ </style>
+ <!--[if mso]>
+ <noscript>
+ <xml>
+ <o:OfficeDocumentSettings>
+ <o:AllowPNG />
+ <o:PixelsPerInch>96</o:PixelsPerInch>
+ </o:OfficeDocumentSettings>
+ </xml>
+ </noscript>
+ <![endif]-->
+ <!--[if lte mso 11]>
+ <style type="text/css">
+ .mj-outlook-group-fix {
+ width: 100% !important;
+ }
+ </style>
+ <![endif]-->
+ <!--[if !mso]><!-->
+ <link
+ href="https://fonts.googleapis.com/css?family=Open Sans"
+ rel="stylesheet"
+ type="text/css"
+ />
+ <style type="text/css">
+ @import url(https://fonts.googleapis.com/css?family=Open Sans);
+ </style>
+ <!--<![endif]-->
+ <style type="text/css">
+ @media only screen and (min-width: 480px) {
+ .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ }
+ </style>
+ <style media="screen and (min-width:480px)">
+ .moz-text-html .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ </style>
+ <style type="text/css">
+ [owa] .mj-column-per-100 {
+ width: 100% !important;
+ max-width: 100%;
+ }
+ </style>
+ <style type="text/css">
+ @media only screen and (max-width: 480px) {
+ table.mj-full-width-mobile {
+ width: 100% !important;
+ }
+
+ td.mj-full-width-mobile {
+ width: auto !important;
+ }
+ }
+ </style>
+ </head>
+
+ <body style="word-spacing: normal; background-color: #f2f7fa">
+ <div style="background-color: #f2f7fa">
+ <!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div style="margin: 0px auto; max-width: 600px">
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0px 20px 0px;
+ padding-bottom: 0px;
+ padding-top: 0px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 0px 0px 25px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 11px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <br />
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div
+ style="
+ background: #ffffff;
+ background-color: #ffffff;
+ margin: 0px auto;
+ max-width: 600px;
+ "
+ >
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="background: #ffffff; background-color: #ffffff; width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0;
+ padding-bottom: 0px;
+ padding-left: 0px;
+ padding-right: 0px;
+ padding-top: 0px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 0px;
+ padding-right: 0px;
+ padding-bottom: 40px;
+ padding-left: 0px;
+ word-break: break-word;
+ "
+ >
+ <p
+ style="
+ border-top: solid 8px #005a8e;
+ font-size: 1px;
+ margin: 0px auto;
+ width: 100%;
+ "
+ ></p>
+ <!--[if mso | IE
+ ]><table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ style="
+ border-top: solid 8px #005a8e;
+ font-size: 1px;
+ margin: 0px auto;
+ width: 600px;
+ "
+ role="presentation"
+ width="600px"
+ >
+ <tr>
+ <td style="height: 0; line-height: 0">
+ &nbsp;
+ </td>
+ </tr>
+ </table><!
+ [endif]-->
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ word-break: break-word;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="
+ border-collapse: collapse;
+ border-spacing: 0px;
+ "
+ >
+ <tbody>
+ <tr>
+ <td style="width: 121px">
+ <img
+ alt=""
+ height="auto"
+ src="https://data.vespa.oath.cloud/assets/vespa-cloud-logo.png"
+ style="
+ border: none;
+ display: block;
+ outline: none;
+ text-decoration: none;
+ height: auto;
+ width: 100%;
+ font-size: 13px;
+ "
+ width="121"
+ />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div
+ style="
+ background: #ffffff;
+ background-color: #ffffff;
+ margin: 0px auto;
+ max-width: 600px;
+ "
+ >
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="background: #ffffff; background-color: #ffffff; width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0px 20px 0px;
+ padding-bottom: 70px;
+ padding-top: 30px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 25px 0px 25px;
+ padding-top: 0px;
+ padding-right: 50px;
+ padding-bottom: 0px;
+ padding-left: 50px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <h1
+ style="
+ text-align: center;
+ color: #000000;
+ line-height: 32px;
+ "
+ >
+ Vespa cloud notifications
+ </h1>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="left"
+ style="
+ font-size: 0px;
+ padding: 0px 25px 0px 25px;
+ padding-top: 0px;
+ padding-right: 50px;
+ padding-bottom: 0px;
+ padding-left: 50px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ line-height: 22px;
+ text-align: left;
+ color: #797e82;
+ "
+ >
+ <p>
+ There are problems with tests for default.default
+ </p>
+ <ul>
+ <li>test package has production tests, but no production tests are declared in deployment.xml</li><li>see <a href="https://docs.vespa.ai/en/testing.html">https://docs.vespa.ai/en/testing.html</a> for details on how to write system tests for Vespa</li>
+ </ul>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td
+ align="center"
+ vertical-align="middle"
+ style="
+ font-size: 0px;
+ padding: 10px 25px;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ word-break: break-word;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="border-collapse: separate; line-height: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ bgcolor="#005A8E"
+ role="presentation"
+ style="
+ border: none;
+ border-radius: 100px;
+ cursor: auto;
+ mso-padding-alt: 15px 25px 15px 25px;
+ background: #005a8e;
+ "
+ valign="middle"
+ >
+ <a
+ href="#"
+ style="
+ display: inline-block;
+ background: #005a8e;
+ color: #ffffff;
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 120%;
+ margin: 0;
+ text-decoration: none;
+ text-transform: none;
+ padding: 15px 25px 15px 25px;
+ mso-padding-alt: 0px;
+ border-radius: 100px;
+ "
+ target="_blank"
+ ><b style="font-weight: 700"
+ ><b style="font-weight: 700"
+ >Go to Console</b
+ ></b
+ ></a
+ >
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
+ <div style="margin: 0px auto; max-width: 600px">
+ <table
+ align="center"
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="width: 100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ style="
+ direction: ltr;
+ font-size: 0px;
+ padding: 20px 0px 20px 0px;
+ padding-bottom: 0px;
+ padding-top: 20px;
+ text-align: center;
+ "
+ >
+ <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
+ <div
+ class="mj-column-per-100 mj-outlook-group-fix"
+ style="
+ font-size: 0px;
+ text-align: left;
+ direction: ltr;
+ display: inline-block;
+ vertical-align: top;
+ width: 100%;
+ "
+ >
+ <table
+ border="0"
+ cellpadding="0"
+ cellspacing="0"
+ role="presentation"
+ style="vertical-align: top"
+ width="100%"
+ >
+ <tbody>
+ <tr>
+ <td
+ align="center"
+ style="
+ font-size: 0px;
+ padding: 0px 20px 0px 20px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ word-break: break-word;
+ "
+ >
+ <div
+ style="
+ font-family: Open Sans, Helvetica, Arial,
+ sans-serif;
+ font-size: 11px;
+ line-height: 22px;
+ text-align: center;
+ color: #797e82;
+ "
+ >
+ <p style="margin: 10px 0">
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: #005a8e"
+ href="https://legal.yahoo.com/xw/en/yahoo/privacy/topic/b2bprivacypolicy/index.html"
+ ><span style="color: #005a8e"
+ >Yahoo Privacy Policy</span
+ ></a
+ ><span style="color: #797e82"
+ >&nbsp; &nbsp;|&nbsp; &nbsp;</span
+ ><a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: #005a8e"
+ href="https://dashboard.tld/terms-of-service-trial.html"
+ ><span style="color: #005a8e"
+ >Terms of Service</span
+ ></a
+ ><span style="color: #797e82"
+ >&nbsp; &nbsp;|&nbsp; &nbsp;</span
+ ><a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: #005a8e"
+ href="https://dashboard.tld/support"
+ ><span style="color: #005a8e">Support</span></a
+ >
+ </p>
+ <p style="margin: 10px 0">
+ <a
+ target="_blank"
+ rel="noopener noreferrer"
+ style="color: inherit; text-decoration: none"
+ href="https://dashboard.tld/tenant/tenant1/account/notifications"
+ >Click
+ <span style="color: #005a8e"><u>here</u></span>
+ to manage your notifications setting.</a
+ ><br />
+ </p>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <!--[if mso | IE]></td></tr></table><![endif]-->
+ </div>
+ </body>
+</html>