Skip to content

Commit b21b27c

Browse files
committed
add support for email notification attachment as an array of bytes
1 parent 9a81457 commit b21b27c

File tree

10 files changed

+117
-6
lines changed

10 files changed

+117
-6
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ ThisBuild / organization := "app.softnetwork"
3131

3232
name := "notification"
3333

34-
ThisBuild / version := "0.2.5"
34+
ThisBuild / version := "0.2.6"
3535

3636
ThisBuild / scalaVersion := "2.12.15"
3737

common/src/main/protobuf/model/notifications.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,12 @@ message BasicDevice{
5656

5757
message Attachment{
5858
option (scalapb.message).extends = "ProtobufDomainObject";
59+
option (scalapb.message).extends = "AttachmentDecorator";
60+
option (scalapb.message).companion_extends = "AttachmentCompanion";
5961
required string name = 1;
6062
required string path = 2;
6163
optional string description = 3;
64+
optional bytes bytes = 4;
6265
}
6366

6467
enum MailType{
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package app.softnetwork.notification.model
2+
3+
trait AttachmentCompanion {
4+
def apply(name: String, path: String, bytes: Array[Byte]): Attachment = {
5+
Attachment.defaultInstance.withName(name).withPath(path).copyFrom(bytes)
6+
}
7+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package app.softnetwork.notification.model
2+
3+
import app.softnetwork.utils.MimeTypeTools
4+
import com.google.protobuf.ByteString
5+
6+
import javax.activation.{DataSource, FileDataSource}
7+
import javax.mail.util.ByteArrayDataSource
8+
9+
trait AttachmentDecorator { _: Attachment =>
10+
11+
def copyFrom(bytes: Array[Byte]): Attachment = this.withBytes(ByteString.copyFrom(bytes))
12+
13+
lazy val arrayOfBytes: Option[Array[Byte]] = bytes.map(_.toByteArray)
14+
15+
lazy val `type`: Option[String] = arrayOfBytes.flatMap(MimeTypeTools.detectMimeType)
16+
17+
lazy val dataSource: DataSource = arrayOfBytes
18+
.map { b =>
19+
val ds = new ByteArrayDataSource(b, `type`.getOrElse("application/octet-stream"))
20+
ds.setName(name)
21+
ds
22+
}
23+
.getOrElse(new FileDataSource(path))
24+
25+
}
104 KB
Loading
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package app.softnetwork.notification.model
2+
3+
import app.softnetwork.utils.HashTools
4+
import org.scalatest.flatspec.AnyFlatSpec
5+
import org.scalatest.matchers.should.Matchers
6+
7+
import java.io.ByteArrayInputStream
8+
import java.nio.file.{Files, Paths}
9+
import javax.mail.util.ByteArrayDataSource
10+
11+
class AttachmentSpec extends AnyFlatSpec with Matchers {
12+
13+
"png attachment" should "work" in {
14+
val name = "avatar.png"
15+
val path =
16+
Paths.get(Thread.currentThread().getContextClassLoader.getResource(name).getPath)
17+
val bytes = Files.readAllBytes(path)
18+
val attachment = Attachment(name, path.toString, bytes)
19+
assert(attachment.name == name)
20+
assert(attachment.dataSource.isInstanceOf[ByteArrayDataSource])
21+
assert(attachment.`type`.isDefined)
22+
assert(attachment.`type`.getOrElse("") == "image/png")
23+
val md5 = HashTools
24+
.hashStream(
25+
new ByteArrayInputStream(bytes)
26+
)
27+
.getOrElse("")
28+
assert(
29+
HashTools
30+
.hashStream(
31+
new ByteArrayInputStream(attachment.bytes.map(_.toByteArray).getOrElse(Array.empty))
32+
)
33+
.getOrElse("") == md5
34+
)
35+
}
36+
37+
}

core/src/main/scala/app/softnetwork/notification/spi/SimpleMailProvider.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import app.softnetwork.notification.model.MailType._
1111
import app.softnetwork.notification.model._
1212

1313
import java.util.Date
14-
import javax.activation.FileDataSource
1514
import scala.util.{Failure, Success, Try}
1615

1716
/** From https://gist.github.com/mariussoutier/3436111
@@ -43,7 +42,7 @@ trait SimpleMailProvider extends MailProvider with MailSettings with StrictLoggi
4342
}
4443
attachments.foreach(attachment => {
4544
multipart.attach(
46-
new FileDataSource(attachment.path),
45+
attachment.dataSource,
4746
attachment.name,
4847
attachment.description.orNull,
4948
EmailAttachment.ATTACHMENT
104 KB
Loading

testkit/src/main/scala/app/softnetwork/notification/scalatest/NotificationTestKit.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import app.softnetwork.notification.config.MailSettings
88
import app.softnetwork.notification.config.NotificationSettings.NotificationConfig
99
import app.softnetwork.notification.launch.NotificationGuardian
1010
import app.softnetwork.notification.message.Schedule4NotificationTriggered
11-
import app.softnetwork.notification.model.Notification
11+
import app.softnetwork.notification.model.{Attachment, BasicDevice, From, Mail, Notification, Platform, Push, SMS}
1212
import app.softnetwork.scheduler.scalatest.SchedulerTestKit
1313
import com.typesafe.config.Config
1414
import org.scalatest.Suite
15-
import app.softnetwork.notification.model.{BasicDevice, From, Mail, Platform, Push, SMS}
1615
import app.softnetwork.persistence.launch.PersistentEntity
1716
import app.softnetwork.persistence.query.EventProcessorStream
1817

1918
import java.net.ServerSocket
19+
import java.nio.file.{Files, Path}
2020

2121
trait NotificationTestKit[T <: Notification]
2222
extends SchedulerTestKit
@@ -43,13 +43,19 @@ trait NotificationTestKit[T <: Notification]
4343
val subject = "test"
4444
val message = "message"
4545

46-
protected def generateMail(uuid: String): Mail =
46+
protected def generateAttachment(name: String, path: Path): Attachment = {
47+
val bytes = Files.readAllBytes(path)
48+
Attachment(name, path.toString, bytes)
49+
}
50+
51+
protected def generateMail(uuid: String, attachments: Seq[Attachment] = Seq.empty): Mail =
4752
Mail.defaultInstance
4853
.withUuid(uuid)
4954
.withFrom(From(MailSettings.MailConfig.username, None))
5055
.withTo(Seq("nobody@gmail.com"))
5156
.withSubject(subject)
5257
.withMessage(message)
58+
.withAttachments(attachments)
5359

5460
protected def generateSMS(uuid: String): SMS =
5561
SMS.defaultInstance

testkit/src/test/scala/app/softnetwork/notification/handlers/SimpleMailNotificationsHandlerSpec.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
package app.softnetwork.notification.handlers
22

33
import app.softnetwork.notification.message._
4+
import app.softnetwork.notification.model.Attachment
45
import app.softnetwork.notification.scalatest.SimpleMailNotificationsTestKit
56
import org.scalatest.wordspec.AnyWordSpecLike
67

8+
import java.nio.file.Paths
9+
710
/** Created by smanciot on 14/04/2020.
811
*/
912
class SimpleMailNotificationsHandlerSpec
1013
extends SimpleMailNotificationsHandler
1114
with AnyWordSpecLike
1215
with SimpleMailNotificationsTestKit {
1316

17+
var attachment: Attachment = _
18+
19+
override def beforeAll(): Unit = {
20+
super.beforeAll()
21+
attachment = generateAttachment(
22+
"avatar.png",
23+
Paths.get(Thread.currentThread().getContextClassLoader.getResource("avatar.png").getPath)
24+
)
25+
}
26+
1427
"Simple Mail Notification handler" must {
1528

1629
"add notification" in {
@@ -25,6 +38,18 @@ class SimpleMailNotificationsHandlerSpec
2538
}
2639
}
2740

41+
"add notification with attachment(s)" in {
42+
val uuid = "addWithAttachments"
43+
this ? (uuid, AddNotification(generateMail(uuid, Seq(attachment)))) await {
44+
case n: NotificationAdded =>
45+
n.uuid shouldBe uuid
46+
assert(
47+
probe.receiveMessage().schedule.uuid == s"MailNotification#$uuid#NotificationTimerKey"
48+
)
49+
case _ => fail()
50+
}
51+
}
52+
2853
"remove notification" in {
2954
val uuid = "remove"
3055
this ? (uuid, AddNotification(generateMail(uuid))) await {
@@ -50,6 +75,15 @@ class SimpleMailNotificationsHandlerSpec
5075
}
5176
}
5277

78+
"send notification with attachment(s)" in {
79+
val uuid = "sendWithAttachments"
80+
this ? (uuid, SendNotification(generateMail(uuid, Seq(attachment)))) await {
81+
case n: NotificationSent =>
82+
n.uuid shouldBe uuid
83+
case _ => fail()
84+
}
85+
}
86+
5387
"resend notification" in {
5488
val uuid = "resend"
5589
this ? (uuid, SendNotification(generateMail(uuid))) await {

0 commit comments

Comments
 (0)