mirror of
https://github.com/VinylDNS/vinyldns
synced 2025-08-22 10:10:12 +00:00
Add email notifier (#674)
* Add email notifier Provide email on batch change to the requesting user * Test email notifier Add unit tests for email notifier * Address EmailNotifier comments Add integration test for Email Notifier Log unparseable emails Add detail to email
This commit is contained in:
parent
c880b07145
commit
3074e503fa
@ -32,3 +32,10 @@ services:
|
||||
- "9324:9324"
|
||||
volumes:
|
||||
- ./elasticmq/custom.conf:/etc/elasticmq/elasticmq.conf
|
||||
|
||||
mail:
|
||||
image: flaviovs/mock-smtp
|
||||
ports:
|
||||
- "19025:25"
|
||||
volumes:
|
||||
- ../target:/var/lib/mock-smtp
|
||||
|
@ -93,6 +93,10 @@ vinyldns {
|
||||
crypto {
|
||||
type = "vinyldns.core.crypto.NoOpCrypto"
|
||||
}
|
||||
|
||||
email.settings.smtp {
|
||||
port = 19025
|
||||
}
|
||||
}
|
||||
|
||||
# Global settings
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2018 Comcast Cable Communications Management, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package vinyldns.api.notifier.email
|
||||
|
||||
import com.typesafe.config.{Config, ConfigFactory}
|
||||
import vinyldns.core.notifier._
|
||||
import vinyldns.api.MySqlApiIntegrationSpec
|
||||
import vinyldns.mysql.MySqlIntegrationSpec
|
||||
import org.scalatest.{Matchers, WordSpecLike}
|
||||
import vinyldns.core.domain.batch._
|
||||
import vinyldns.core.domain.record.RecordType
|
||||
import vinyldns.core.domain.record.AData
|
||||
import org.joda.time.DateTime
|
||||
import vinyldns.core.TestMembershipData._
|
||||
import java.nio.file.{Files, Path, Paths}
|
||||
import cats.effect.{IO, Resource}
|
||||
import scala.collection.JavaConverters._
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import cats.implicits._
|
||||
|
||||
class EmailNotifierIntegrationSpec
|
||||
extends MySqlApiIntegrationSpec
|
||||
with MySqlIntegrationSpec
|
||||
with Matchers
|
||||
with WordSpecLike
|
||||
with BeforeAndAfterEach {
|
||||
|
||||
import vinyldns.api.domain.DomainValidations._
|
||||
|
||||
val emailConfig: Config = ConfigFactory.load().getConfig("vinyldns.email.settings")
|
||||
|
||||
val targetDirectory = Paths.get("../../target")
|
||||
|
||||
override def beforeEach: Unit =
|
||||
deleteEmailFiles(targetDirectory).unsafeRunSync()
|
||||
|
||||
override def afterEach: Unit =
|
||||
deleteEmailFiles(targetDirectory).unsafeRunSync()
|
||||
|
||||
"Email Notifier" should {
|
||||
|
||||
"send an email" in {
|
||||
val batchChange = BatchChange(
|
||||
okUser.id,
|
||||
okUser.userName,
|
||||
None,
|
||||
DateTime.now,
|
||||
List(
|
||||
SingleAddChange(
|
||||
"some-zone-id",
|
||||
"zone-name",
|
||||
"record-name",
|
||||
"a" * HOST_MAX_LENGTH,
|
||||
RecordType.A,
|
||||
300,
|
||||
AData("1.1.1.1"),
|
||||
SingleChangeStatus.Complete,
|
||||
None,
|
||||
None,
|
||||
None)),
|
||||
approvalStatus = BatchChangeApprovalStatus.AutoApproved
|
||||
)
|
||||
|
||||
val program = for {
|
||||
_ <- userRepository.save(okUser)
|
||||
notifier <- new EmailNotifierProvider()
|
||||
.load(NotifierConfig("", emailConfig), userRepository)
|
||||
_ <- notifier.notify(Notification(batchChange))
|
||||
emailFiles <- retrieveEmailFiles(targetDirectory)
|
||||
} yield emailFiles
|
||||
|
||||
val files = program.unsafeRunSync()
|
||||
|
||||
files.length should be(1)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def deleteEmailFiles(path: Path): IO[Unit] =
|
||||
for {
|
||||
files <- retrieveEmailFiles(path)
|
||||
_ <- files.traverse { file =>
|
||||
IO(Files.delete(file))
|
||||
}
|
||||
} yield ()
|
||||
|
||||
def retrieveEmailFiles(path: Path): IO[List[Path]] =
|
||||
Resource.fromAutoCloseable(IO(Files.newDirectoryStream(path, "*.eml"))).use { s =>
|
||||
IO {
|
||||
s.iterator.asScala.toList
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -91,6 +91,13 @@ vinyldns {
|
||||
|
||||
notifiers = []
|
||||
|
||||
email = {
|
||||
class-name = "vinyldns.api.notifier.email.EmailNotifierProvider"
|
||||
settings = {
|
||||
from = "VinylDNS <do-not-reply@vinyldns.io>"
|
||||
}
|
||||
}
|
||||
|
||||
defaultZoneConnection {
|
||||
name = "vinyldns."
|
||||
keyName = "vinyldns."
|
||||
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2018 Comcast Cable Communications Management, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package vinyldns.api.notifier.email
|
||||
|
||||
import vinyldns.core.notifier.{Notification, Notifier}
|
||||
import cats.effect.IO
|
||||
import vinyldns.core.domain.batch.BatchChange
|
||||
import vinyldns.core.domain.membership.UserRepository
|
||||
import vinyldns.core.domain.membership.User
|
||||
import org.slf4j.LoggerFactory
|
||||
import javax.mail.internet.{InternetAddress, MimeMessage}
|
||||
import javax.mail.{Address, Message, Session}
|
||||
import scala.util.Try
|
||||
import vinyldns.core.domain.batch.SingleChange
|
||||
import vinyldns.core.domain.batch.SingleAddChange
|
||||
import vinyldns.core.domain.batch.SingleDeleteChange
|
||||
import vinyldns.core.domain.record.AData
|
||||
import vinyldns.core.domain.record.AAAAData
|
||||
import vinyldns.core.domain.record.CNAMEData
|
||||
import vinyldns.core.domain.record.MXData
|
||||
import vinyldns.core.domain.record.TXTData
|
||||
import vinyldns.core.domain.record.PTRData
|
||||
import vinyldns.core.domain.record.RecordData
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
import vinyldns.core.domain.batch.BatchChangeStatus._
|
||||
import vinyldns.core.domain.batch.BatchChangeApprovalStatus._
|
||||
|
||||
class EmailNotifier(config: EmailNotifierConfig, session: Session, userRepository: UserRepository)
|
||||
extends Notifier {
|
||||
|
||||
private val logger = LoggerFactory.getLogger("EmailNotifier")
|
||||
|
||||
def notify(notification: Notification[_]): IO[Unit] =
|
||||
notification.change match {
|
||||
case bc: BatchChange => sendBatchChangeNotification(bc)
|
||||
case _ => IO.unit
|
||||
}
|
||||
|
||||
def send(addresses: Address*)(buildMessage: Message => Message): IO[Unit] = IO {
|
||||
val message = new MimeMessage(session)
|
||||
message.setRecipients(Message.RecipientType.TO, addresses.toArray)
|
||||
message.setFrom(config.from)
|
||||
buildMessage(message)
|
||||
message.saveChanges()
|
||||
val transport = session.getTransport("smtp")
|
||||
transport.connect()
|
||||
transport.sendMessage(message, message.getAllRecipients())
|
||||
transport.close()
|
||||
}
|
||||
|
||||
def sendBatchChangeNotification(bc: BatchChange): IO[Unit] =
|
||||
userRepository.getUser(bc.userId).flatMap {
|
||||
case Some(UserWithEmail(email)) =>
|
||||
send(email) { message =>
|
||||
message.setSubject(s"VinylDNS Batch change ${bc.id} results")
|
||||
message.setContent(formatBatchChange(bc), "text/html")
|
||||
message
|
||||
}
|
||||
case Some(user: User) if user.email.isDefined =>
|
||||
IO {
|
||||
logger.warn(
|
||||
s"Unable to properly parse email for ${user.id}: ${user.email.getOrElse("<none>")}")
|
||||
}
|
||||
case None => IO { logger.warn(s"Unable to find user: ${bc.userId}") }
|
||||
case _ => IO.unit
|
||||
}
|
||||
|
||||
def formatBatchChange(bc: BatchChange): String =
|
||||
s"""<h1>Batch Change Results</h1>
|
||||
| <b>Submitter:</b> ${bc.userName} <br/>
|
||||
| ${bc.comments.map(comments => s"<b>Description:</b> ${comments}</br>").getOrElse("")}
|
||||
| <b>Created:</b> ${bc.createdTimestamp.toString(DateTimeFormat.fullDateTime)} <br/>
|
||||
| <b>Id:</b> ${bc.id}<br/>
|
||||
| <b>Status:</b> ${formatStatus(bc.approvalStatus, bc.status)}<br/>
|
||||
| <table border = "1">
|
||||
| <tr><th>#</th><th>Change Type</th><th>Record Type</th><th>Input Name</th>
|
||||
| <th>TTL</th><th>Record Data</th><th>Status</th><th>Message</th></tr>
|
||||
| ${bc.changes.zipWithIndex.map((formatSingleChange _).tupled).mkString("\n")}
|
||||
| </table>
|
||||
""".stripMargin
|
||||
|
||||
def formatStatus(approval: BatchChangeApprovalStatus, status: BatchChangeStatus): String =
|
||||
(approval, status) match {
|
||||
case (ManuallyRejected, _) => "Rejected"
|
||||
case (PendingApproval, _) => "Pending Approval"
|
||||
case (_, PartialFailure) => "Partially Failed"
|
||||
case (_, status) => status.toString
|
||||
}
|
||||
|
||||
def formatSingleChange(sc: SingleChange, index: Int): String = sc match {
|
||||
case SingleAddChange(
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
inputName,
|
||||
typ,
|
||||
ttl,
|
||||
recordData,
|
||||
status,
|
||||
systemMessage,
|
||||
_,
|
||||
_,
|
||||
_) =>
|
||||
s"""<tr><td>${index + 1}</td><td>Add</td><td>$typ</td><td>$inputName</td>
|
||||
| <td>$ttl</td><td>${formatRecordData(recordData)}</td><td>$status</td>
|
||||
| <td>${systemMessage.getOrElse("")}</td></tr>"""
|
||||
case SingleDeleteChange(_, _, _, inputName, typ, status, systemMessage, _, _, _) =>
|
||||
s"""<tr><td>${index + 1}</td><td>Delete</td><td>$typ</td><td>$inputName</td>
|
||||
| <td></td><td></td><td>$status</td><td>${systemMessage.getOrElse("")}</td></tr>"""
|
||||
}
|
||||
|
||||
def formatRecordData(rd: RecordData): String = rd match {
|
||||
case AData(address) => address
|
||||
case AAAAData(address) => address
|
||||
case CNAMEData(cname) => cname
|
||||
case MXData(preference, exchange) => s"Preference: $preference Exchange: $exchange"
|
||||
case PTRData(name) => name
|
||||
case TXTData(text) => text
|
||||
case _ => rd.toString
|
||||
}
|
||||
|
||||
object UserWithEmail {
|
||||
def unapply(user: User): Option[Address] =
|
||||
for {
|
||||
email <- user.email
|
||||
address <- Try(new InternetAddress(email)).toOption
|
||||
} yield address
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2018 Comcast Cable Communications Management, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package vinyldns.api.notifier.email
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import javax.mail.Address
|
||||
import javax.mail.internet.InternetAddress
|
||||
import pureconfig.ConfigReader
|
||||
import scala.util.Try
|
||||
import pureconfig.error.CannotConvert
|
||||
import java.util.Properties
|
||||
import com.typesafe.config.{ConfigObject, ConfigValue}
|
||||
import com.typesafe.config.ConfigValueType
|
||||
|
||||
object EmailNotifierConfig {
|
||||
|
||||
implicit val smtpPropertiesReader: ConfigReader[Properties] = {
|
||||
ConfigReader[ConfigObject].map { config =>
|
||||
val props = new Properties()
|
||||
|
||||
def convertToProperties(baseKey: String, config: ConfigObject): Unit =
|
||||
config.keySet().asScala.foreach {
|
||||
case key =>
|
||||
config.get(key) match {
|
||||
case value: ConfigObject =>
|
||||
convertToProperties(s"${baseKey}.${key}", value)
|
||||
case value: ConfigValue if value.valueType != ConfigValueType.NULL =>
|
||||
props.put(s"${baseKey}.${key}", value.unwrapped())
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
convertToProperties("mail.smtp", config)
|
||||
props
|
||||
}
|
||||
}
|
||||
|
||||
implicit val addressReader: ConfigReader[Address] = ConfigReader[String].emap { s =>
|
||||
Try(new InternetAddress(s)).toEither.left.map { exc =>
|
||||
CannotConvert(s, "InternetAddress", exc.getMessage)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class EmailNotifierConfig(from: Address, smtp: Properties = new Properties())
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2018 Comcast Cable Communications Management, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package vinyldns.api.notifier.email
|
||||
|
||||
import vinyldns.core.notifier.{Notifier, NotifierConfig, NotifierProvider}
|
||||
import vinyldns.core.domain.membership.UserRepository
|
||||
import pureconfig.module.catseffect.loadConfigF
|
||||
import cats.effect.IO
|
||||
import javax.mail.Session
|
||||
|
||||
class EmailNotifierProvider extends NotifierProvider {
|
||||
|
||||
import EmailNotifierConfig._
|
||||
|
||||
def load(config: NotifierConfig, userRepository: UserRepository): IO[Notifier] =
|
||||
for {
|
||||
emailConfig <- loadConfigF[IO, EmailNotifierConfig](config.settings)
|
||||
session <- createSession(emailConfig)
|
||||
} yield new EmailNotifier(emailConfig, session, userRepository)
|
||||
|
||||
def createSession(config: EmailNotifierConfig): IO[Session] = IO {
|
||||
Session.getInstance(config.smtp)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2018 Comcast Cable Communications Management, LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package vinyldns.api.notifier.email
|
||||
import org.scalatest.{BeforeAndAfterEach, Matchers, WordSpec}
|
||||
import org.scalatest.mockito.MockitoSugar
|
||||
import vinyldns.api.CatsHelpers
|
||||
import javax.mail.{Provider, Session, Transport, URLName}
|
||||
import java.util.Properties
|
||||
import vinyldns.core.domain.membership.UserRepository
|
||||
import vinyldns.core.notifier.Notification
|
||||
import javax.mail.internet.InternetAddress
|
||||
import org.mockito.Matchers.{eq => eqArg, _}
|
||||
import org.mockito.Mockito._
|
||||
import org.mockito.ArgumentCaptor
|
||||
import cats.effect.IO
|
||||
import javax.mail.{Address, Message}
|
||||
import vinyldns.core.domain.membership.User
|
||||
import vinyldns.core.domain.batch.BatchChange
|
||||
import org.joda.time.DateTime
|
||||
import vinyldns.core.domain.batch.BatchChangeApprovalStatus
|
||||
import vinyldns.core.domain.batch.SingleChange
|
||||
import vinyldns.core.domain.batch.SingleAddChange
|
||||
import vinyldns.core.domain.batch.SingleDeleteChange
|
||||
import vinyldns.core.domain.record.RecordType
|
||||
import vinyldns.core.domain.record.AData
|
||||
import _root_.vinyldns.core.domain.batch.SingleChangeStatus
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.collection.JavaConverters._
|
||||
import vinyldns.core.notifier.NotifierConfig
|
||||
|
||||
object MockTransport extends MockitoSugar {
|
||||
val mockTransport = mock[Transport]
|
||||
}
|
||||
|
||||
class MockTransport(session: Session, urlname: URLName) extends Transport(session, urlname) {
|
||||
import MockTransport._
|
||||
|
||||
override def connect(): Unit = mockTransport.connect()
|
||||
|
||||
override def close(): Unit = mockTransport.close()
|
||||
|
||||
def sendMessage(msg: Message, addresses: Array[Address]): Unit =
|
||||
mockTransport.sendMessage(msg, addresses)
|
||||
}
|
||||
|
||||
class EmailNotifierSpec
|
||||
extends WordSpec
|
||||
with Matchers
|
||||
with MockitoSugar
|
||||
with BeforeAndAfterEach
|
||||
with CatsHelpers {
|
||||
|
||||
import MockTransport._
|
||||
|
||||
val mockUserRepository = mock[UserRepository]
|
||||
val session = Session.getInstance(new Properties())
|
||||
session.setProvider(
|
||||
new Provider(
|
||||
Provider.Type.TRANSPORT,
|
||||
"smtp",
|
||||
"vinyldns.api.notifier.email.MockTransport",
|
||||
"vinyl",
|
||||
"1.0"))
|
||||
|
||||
override protected def beforeEach(): Unit =
|
||||
reset(mockUserRepository, mockTransport)
|
||||
|
||||
def batchChange(
|
||||
description: Option[String] = None,
|
||||
changes: List[SingleChange] = List.empty): BatchChange =
|
||||
BatchChange(
|
||||
"test",
|
||||
"testUser",
|
||||
description,
|
||||
DateTime.now(),
|
||||
changes,
|
||||
None,
|
||||
BatchChangeApprovalStatus.AutoApproved,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
"testBatch")
|
||||
|
||||
"Email Notifier" should {
|
||||
"do nothing for unsupported Notifications" in {
|
||||
val emailConfig: Config = ConfigFactory.parseMap(
|
||||
Map[String, Any](
|
||||
"from" -> "Testing <test@test.com>",
|
||||
"smtp.host" -> "wouldfail.mail.com",
|
||||
"smtp.auth.mechanisms" -> "PLAIN"
|
||||
).asJava)
|
||||
val notifier = new EmailNotifierProvider()
|
||||
.load(NotifierConfig("", emailConfig), mockUserRepository)
|
||||
.unsafeRunSync()
|
||||
|
||||
notifier.notify(new Notification("this won't be supported ever")) should be(IO.unit)
|
||||
}
|
||||
|
||||
"do nothing for user without email" in {
|
||||
val notifier = new EmailNotifier(
|
||||
EmailNotifierConfig(new InternetAddress("test@test.com"), new Properties()),
|
||||
session,
|
||||
mockUserRepository
|
||||
)
|
||||
|
||||
doReturn(IO.pure(Some(User("testUser", "access", "secret"))))
|
||||
.when(mockUserRepository)
|
||||
.getUser("test")
|
||||
|
||||
notifier.notify(Notification(batchChange())).unsafeRunSync()
|
||||
|
||||
verify(mockUserRepository).getUser("test")
|
||||
}
|
||||
|
||||
"do nothing when user not found" in {
|
||||
val notifier = new EmailNotifier(
|
||||
EmailNotifierConfig(new InternetAddress("test@test.com"), new Properties()),
|
||||
session,
|
||||
mockUserRepository
|
||||
)
|
||||
|
||||
doReturn(IO.pure(None))
|
||||
.when(mockUserRepository)
|
||||
.getUser("test")
|
||||
|
||||
notifier.notify(Notification(batchChange())).unsafeRunSync()
|
||||
|
||||
verify(mockUserRepository).getUser("test")
|
||||
}
|
||||
|
||||
"send an email to a user" in {
|
||||
val fromAddress = new InternetAddress("test@test.com")
|
||||
val notifier = new EmailNotifier(
|
||||
EmailNotifierConfig(fromAddress, new Properties()),
|
||||
session,
|
||||
mockUserRepository
|
||||
)
|
||||
|
||||
doReturn(
|
||||
IO.pure(Some(User("testUser", "access", "secret", None, None, Some("testuser@test.com")))))
|
||||
.when(mockUserRepository)
|
||||
.getUser("test")
|
||||
|
||||
val expectedAddresses = Array[Address](new InternetAddress("testuser@test.com"))
|
||||
val messageArgument = ArgumentCaptor.forClass(classOf[Message])
|
||||
|
||||
doNothing().when(mockTransport).connect()
|
||||
doNothing()
|
||||
.when(mockTransport)
|
||||
.sendMessage(messageArgument.capture(), eqArg(expectedAddresses))
|
||||
doNothing().when(mockTransport).close()
|
||||
|
||||
val description = "notes"
|
||||
val singleChanges: List[SingleChange] = List(
|
||||
SingleAddChange(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"www.test.com",
|
||||
RecordType.A,
|
||||
200,
|
||||
AData("1.2.3.4"),
|
||||
SingleChangeStatus.Complete,
|
||||
None,
|
||||
None,
|
||||
None),
|
||||
SingleDeleteChange(
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"deleteme.test.com",
|
||||
RecordType.A,
|
||||
SingleChangeStatus.Failed,
|
||||
Some("message for you"),
|
||||
None,
|
||||
None)
|
||||
)
|
||||
val change = batchChange(Some(description), singleChanges)
|
||||
|
||||
notifier.notify(Notification(change)).unsafeRunSync()
|
||||
|
||||
val message = messageArgument.getValue()
|
||||
|
||||
message.getFrom() should be(Array(fromAddress))
|
||||
message.getContentType() should be("text/html; charset=us-ascii")
|
||||
message.getAllRecipients() should be(expectedAddresses)
|
||||
message.getSubject() should be("VinylDNS Batch change testBatch results")
|
||||
val content = message.getContent().asInstanceOf[String]
|
||||
|
||||
content.contains(change.id) should be(true)
|
||||
content.contains(description) should be(true)
|
||||
|
||||
val regex = raw"<tr>((.|\s)+?)<\/tr>".r
|
||||
val rows = (for (m <- regex.findAllMatchIn(content)) yield m.group(0)).toList
|
||||
|
||||
def assertSingleChangeCaptured(row: String, sc: SingleChange): Unit = {
|
||||
row.contains(sc.inputName) should be(true)
|
||||
row.contains(sc.typ.toString) should be(true)
|
||||
row.contains(sc.status.toString) should be(true)
|
||||
sc.systemMessage.foreach(row.contains(_) should be(true))
|
||||
sc match {
|
||||
case ac: SingleAddChange =>
|
||||
row.contains("Add") should be(true)
|
||||
ac.recordData match {
|
||||
case AData(address) => row.contains(address) should be(true)
|
||||
case _ => row.contains(ac.recordData) should be(true)
|
||||
}
|
||||
row.contains(ac.ttl.toString) should be(true)
|
||||
case _: SingleDeleteChange =>
|
||||
row.contains("Delete") should be(true)
|
||||
}
|
||||
}
|
||||
|
||||
rows.tail.zip(singleChanges).foreach((assertSingleChangeCaptured _).tupled)
|
||||
|
||||
verify(mockUserRepository).getUser("test")
|
||||
verify(mockTransport).sendMessage(any[Message], eqArg(expectedAddresses))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -49,7 +49,9 @@ object Dependencies {
|
||||
"com.47deg" %% "github4s" % "0.18.6",
|
||||
"com.comcast" %% "ip4s-core" % ip4sV,
|
||||
"com.comcast" %% "ip4s-cats" % ip4sV,
|
||||
"com.iheart" %% "ficus" % "1.4.3"
|
||||
"com.iheart" %% "ficus" % "1.4.3",
|
||||
"com.sun.mail" % "javax.mail" % "1.6.2",
|
||||
"javax.mail" % "javax.mail-api" % "1.6.2"
|
||||
)
|
||||
|
||||
lazy val coreDependencies = Seq(
|
||||
|
Loading…
x
Reference in New Issue
Block a user