Skip to content

Commit

Permalink
Create a dedicated EmailAddress class
Browse files Browse the repository at this point in the history
  • Loading branch information
awelless committed Feb 17, 2024
1 parent 9bd3769 commit 6e230f7
Show file tree
Hide file tree
Showing 29 changed files with 163 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class MailMessageResource(

private fun MailMessage.toDto() = AdminSlicedMailDto(
id = id.toString(),
emailFrom = emailFrom,
emailTo = emailTo,
emailFrom = emailFrom?.email,
emailTo = emailTo.email,
type = IdNameDto(type.id.toString(), type.name),
createdAt = createdAt,
sendingStartedAt = sendingStartedAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.mailit.core.model.MailMessageTemplate
import io.mailit.core.model.MailMessageType
import io.mailit.core.model.PlainTextMailMessageType
import io.mailit.template.api.TemplateEngine
import io.mailit.value.EmailAddress.Companion.toEmailAddress
import java.util.concurrent.atomic.AtomicLong

private val counter = AtomicLong()
Expand Down Expand Up @@ -47,8 +48,8 @@ fun createMailMessage(messageType: MailMessageType): MailMessage {
text = "text",
data = emptyMap(),
subject = null,
emailFrom = "email@from.com",
emailTo = "email@to.com",
emailFrom = "email@from.com".toEmailAddress(),
emailTo = "email@to.com".toEmailAddress(),
type = messageType,
createdAt = nowWithoutNanos(),
status = MailMessageStatus.PENDING,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.mailit.connector.http.web

data class CreateMailDto(
val text: String?,
val data: Map<String, Any>?,
val subject: String?,
val emailFrom: String?,
val emailTo: String,
val mailType: String,
val deduplicationId: String?,
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package io.mailit.connector.http.web

import io.mailit.connector.http.security.Roles.APPLICATION
import io.mailit.core.external.api.CreateMailCommand
import io.mailit.core.external.api.CreateMailRequest
import io.mailit.core.external.api.MailMessageService
import io.mailit.value.EmailAddress.Companion.toEmailAddress
import jakarta.annotation.security.RolesAllowed
import jakarta.ws.rs.POST
import jakarta.ws.rs.Path
Expand All @@ -17,8 +18,18 @@ class HttpConnector(

@ResponseStatus(ACCEPTED)
@POST
suspend fun sendMail(command: CreateMailCommand): IdDto {
val savedMail = mailMessageService.createNewMail(command)
suspend fun sendMail(dto: CreateMailDto): IdDto {
val savedMail = mailMessageService.createNewMail(dto.toRequest())
return IdDto(savedMail.id.toString())
}

private fun CreateMailDto.toRequest() = CreateMailRequest(
text = text,
data = data,
subject = subject,
emailFrom = emailFrom?.toEmailAddress(),
emailTo = emailTo.toEmailAddress(),
mailTypeName = mailType,
deduplicationId = deduplicationId,
)
}
1 change: 1 addition & 0 deletions core/connector-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dependencies {
api(project(":core:exception"))
api(project(":core:model"))
api(project(":value-classes"))
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package io.mailit.core.external.api

import io.mailit.core.model.MailMessage
import io.mailit.value.EmailAddress

interface MailMessageService {

suspend fun createNewMail(command: CreateMailCommand): MailMessage
suspend fun createNewMail(command: CreateMailRequest): MailMessage
}

data class CreateMailCommand(
data class CreateMailRequest(
val text: String?,
val data: Map<String, Any>?,
val subject: String?,
val emailFrom: String?,
val emailTo: String,
val mailType: String,
val emailFrom: EmailAddress?,
val emailTo: EmailAddress,
val mailTypeName: String,
val deduplicationId: String?,
)
1 change: 1 addition & 0 deletions core/model/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dependencies {
api(project(":value-classes"))
implementation(project(":template:template-api"))

testImplementation(project(":common-test"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.mailit.core.model

import io.mailit.value.EmailAddress
import java.time.Instant

data class MailMessage(
Expand All @@ -21,9 +22,9 @@ data class MailMessage(
/**
* Overrides default sender email address
*/
val emailFrom: String?,
val emailFrom: EmailAddress?,

val emailTo: String,
val emailTo: EmailAddress,

val type: MailMessageType,

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package io.mailit.core.service.mail
import io.mailit.core.admin.api.mail.MailMessageService
import io.mailit.core.exception.DuplicateUniqueKeyException
import io.mailit.core.exception.ValidationException
import io.mailit.core.external.api.CreateMailCommand
import io.mailit.core.external.api.CreateMailRequest
import io.mailit.core.external.api.MailMessageService as ConnectorMailMessageService
import io.mailit.core.model.MailMessage
import io.mailit.core.model.MailMessageStatus
import io.mailit.core.model.Slice
import io.mailit.core.service.isEmail
import io.mailit.core.spi.MailMessageRepository
import io.mailit.core.spi.MailMessageTypeRepository
import io.mailit.idgenerator.api.IdGenerator
Expand All @@ -24,11 +23,9 @@ class MailMessageServiceImpl(
override suspend fun getAllSliced(page: Int, size: Int): Slice<MailMessage> =
mailMessageRepository.findAllSlicedDescendingIdSorted(page, size)

override suspend fun createNewMail(command: CreateMailCommand): MailMessage {
val messageType = mailMessageTypeRepository.findByName(command.mailType)
?: throw ValidationException("Invalid type: ${command.mailType} is passed")

validateBeforeCreate(command.emailFrom, command.emailTo)
override suspend fun createNewMail(command: CreateMailRequest): MailMessage {
val messageType = mailMessageTypeRepository.findByName(command.mailTypeName)
?: throw ValidationException("Invalid type: ${command.mailTypeName} is passed")

val message = MailMessage(
id = idGenerator.generateId(),
Expand All @@ -54,19 +51,5 @@ class MailMessageServiceImpl(
return message
}

private fun validateBeforeCreate(emailFrom: String?, emailTo: String) {
if (emailFrom?.isEmail() == false) {
throw ValidationException("emailFrom is incorrect")
}

if (emailTo.isBlank()) {
throw ValidationException("emailTo shouldn't be blank")
}

if (!emailTo.isEmail()) {
throw ValidationException("emailTo is incorrect")
}
}

companion object : KLogging()
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class MailFactory(
is HtmlMailMessageType -> htmlMessage(mailMessage, type)
}

if (!mailMessage.emailFrom.isNullOrBlank()) {
mail.from = mailMessage.emailFrom
mailMessage.emailFrom?.let {
mail.from = it.email
}

// todo allow to configure domain
Expand All @@ -30,7 +30,7 @@ class MailFactory(
}

private fun plainTextMessage(mailMessage: MailMessage) =
Mail.withText(mailMessage.emailTo, mailMessage.subject, mailMessage.text)
Mail.withText(mailMessage.emailTo.email, mailMessage.subject, mailMessage.text)

private suspend fun htmlMessage(mailMessage: MailMessage, mailMessageType: HtmlMailMessageType): Mail {
val htmlMessage = templateProcessor.process(
Expand All @@ -39,6 +39,6 @@ class MailFactory(
data = mailMessage.data.orEmpty(),
)

return Mail.withHtml(mailMessage.emailTo, mailMessage.subject, htmlMessage.getOrThrow())
return Mail.withHtml(mailMessage.emailTo.email, mailMessage.subject, htmlMessage.getOrThrow())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.mailit.core.service.mail

import io.mailit.core.exception.DuplicateUniqueKeyException
import io.mailit.core.exception.ValidationException
import io.mailit.core.external.api.CreateMailCommand
import io.mailit.core.external.api.CreateMailRequest
import io.mailit.core.model.MailMessage
import io.mailit.core.model.MailMessageStatus.PENDING
import io.mailit.core.model.MailMessageType
Expand All @@ -11,6 +11,7 @@ import io.mailit.core.spi.MailMessageTypeRepository
import io.mailit.idgenerator.test.ConstantIdGenerator
import io.mailit.test.createMailMessage
import io.mailit.test.createPlainMailMessageType
import io.mailit.value.EmailAddress.Companion.toEmailAddress
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.impl.annotations.InjectMockKs
Expand All @@ -25,10 +26,6 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.Arguments.arguments
import org.junit.jupiter.params.provider.MethodSource

@ExtendWith(MockKExtension::class)
class MailMessageServiceImplTest {
Expand Down Expand Up @@ -59,13 +56,13 @@ class MailMessageServiceImplTest {
@Test
fun `createNewMail - when everything is correct - creates`() = runTest {
// given
val command = CreateMailCommand(
val command = CreateMailRequest(
text = "Some message",
data = mapOf("name" to "john"),
subject = "subject",
emailFrom = "from@gmail.com",
emailTo = "to@mail.com",
mailType = mailType.name,
emailFrom = "from@gmail.com".toEmailAddress(),
emailTo = "to@mail.com".toEmailAddress(),
mailTypeName = mailType.name,
deduplicationId = "deduplication",
)

Expand All @@ -92,13 +89,13 @@ class MailMessageServiceImplTest {
@Test
fun `createNewMail - when mail is duplicate - does nothing`() = runTest {
// given
val command = CreateMailCommand(
val command = CreateMailRequest(
text = "Some message",
data = mapOf("name" to "john"),
subject = "subject",
emailFrom = "from@gmail.com",
emailTo = "to@mail.com",
mailType = mailType.name,
emailFrom = "from@gmail.com".toEmailAddress(),
emailTo = "to@mail.com".toEmailAddress(),
mailTypeName = mailType.name,
deduplicationId = "deduplication",
)

Expand All @@ -114,56 +111,20 @@ class MailMessageServiceImplTest {

@Test
fun `createNewMail - when message type is invalid - throws exception`() = runTest {
val command = CreateMailCommand(
val command = CreateMailRequest(
text = "Some message",
data = mapOf("name" to "john"),
subject = "subject",
emailFrom = "from@gmail.com",
emailTo = "to@mail.com",
mailType = "invalid",
emailFrom = "from@gmail.com".toEmailAddress(),
emailTo = "to@mail.com".toEmailAddress(),
mailTypeName = "invalid",
deduplicationId = "deduplication",
)

coEvery { mailMessageTypeRepository.findByName(command.mailType) } returns null
coEvery { mailMessageTypeRepository.findByName(command.mailTypeName) } returns null

assertThrows<ValidationException> { mailMessageService.createNewMail(command) }

coVerify(exactly = 0) { mailMessageRepository.create(any()) }
}

@ParameterizedTest
@MethodSource("invalidDataForCreation")
fun `createNewMail - with invalid data - throws exception`(
subject: String?,
emailFrom: String?,
emailTo: String,
expectedMessage: String,
) = runTest {
val command = CreateMailCommand(
text = "123",
data = emptyMap(),
subject = subject,
emailFrom = emailFrom,
emailTo = emailTo,
mailType = "123",
deduplicationId = "deduplication",
)

val exception = assertThrows<ValidationException> {
mailMessageService.createNewMail(command)
}

assertEquals(expectedMessage, exception.message)

coVerify(exactly = 0) { mailMessageRepository.create(any()) }
}

companion object {
@JvmStatic
private fun invalidDataForCreation(): List<Arguments> = listOf(
arguments("subject", "email.email.com", "email@gmail.com", "emailFrom is incorrect"),
arguments("subject", "email@email.com", "", "emailTo shouldn't be blank"),
arguments("subject", null, "email.email.com", "emailTo is incorrect"),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ import org.junit.jupiter.api.Test

class MailFactoryTest {

companion object {
private const val MESSAGE_ID_HEADER = "Message-ID"
}

private val templateProcessor = StubTemplateProcessor("some html")
private val mailFactory = MailFactory(templateProcessor)

Expand All @@ -29,8 +25,8 @@ class MailFactoryTest {
// then
assertEquals(message.text, actual.text)
assertEquals(message.subject, actual.subject)
assertEquals(listOf(message.emailTo), actual.to)
assertEquals(message.emailFrom, actual.from)
assertEquals(listOf(message.emailTo.email), actual.to)
assertEquals(message.emailFrom?.email, actual.from)
assertEquals(listOf("${message.id}@mail-it.io"), actual.headers[MESSAGE_ID_HEADER])
}

Expand All @@ -46,8 +42,12 @@ class MailFactoryTest {
// then
assertEquals(templateProcessor.html, actual.html)
assertEquals(message.subject, actual.subject)
assertEquals(listOf(message.emailTo), actual.to)
assertEquals(message.emailFrom, actual.from)
assertEquals(listOf(message.emailTo.email), actual.to)
assertEquals(message.emailFrom?.email, actual.from)
assertEquals(listOf("${message.id}@mail-it.io"), actual.headers[MESSAGE_ID_HEADER])
}

companion object {
private const val MESSAGE_ID_HEADER = "Message-ID"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,16 @@ class MailMessageResourceTest {
"content.size()" equalTo 2,

"content[0].id" equalTo mail2.id.toString(),
"content[0].emailFrom" equalTo mail2.emailFrom,
"content[0].emailTo" equalTo mail2.emailTo,
"content[0].emailFrom" equalTo mail2.emailFrom?.email,
"content[0].emailTo" equalTo mail2.emailTo.email,
"content[0].type.id" equalTo mail2.type.id.toString(),
"content[0].type.name" equalTo mail2.type.name,
"content[0].status" equalTo mail2.status.name,
"content[0].failedCount" equalTo mail2.failedCount,

"content[1].id" equalTo mail1.id.toString(),
"content[1].emailFrom" equalTo mail1.emailFrom,
"content[1].emailTo" equalTo mail1.emailTo,
"content[1].emailFrom" equalTo mail1.emailFrom?.email,
"content[1].emailTo" equalTo mail1.emailTo.email,
"content[1].type.id" equalTo mail1.type.id.toString(),
"content[1].type.name" equalTo mail1.type.name,
"content[1].status" equalTo mail1.status.name,
Expand Down
Loading

0 comments on commit 6e230f7

Please sign in to comment.