Skip to content

Commit

Permalink
HAI-1526 Send hanke invitation emails
Browse files Browse the repository at this point in the history
Include the email sending feature to creation of new hanke users.

Note: this commit does not include sending of application invitations. It will be implemented on another commit.
  • Loading branch information
pitkni committed Sep 7, 2023
1 parent f98dfdc commit 4090bed
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import fi.hel.haitaton.hanke.factory.AlluDataFactory
import fi.hel.haitaton.hanke.factory.DateFactory
import fi.hel.haitaton.hanke.factory.HankeFactory
import fi.hel.haitaton.hanke.factory.HankeFactory.Companion.withGeneratedOmistaja
import fi.hel.haitaton.hanke.factory.HankeFactory.Companion.withYhteystiedot
import fi.hel.haitaton.hanke.geometria.Geometriat
import fi.hel.haitaton.hanke.logging.DisclosureLogService
import fi.hel.haitaton.hanke.permissions.PermissionCode
Expand Down Expand Up @@ -329,14 +330,19 @@ class HankeControllerITests(@Autowired override val mockMvc: MockMvc) : Controll

@Test
fun `Sanitize hanke input and return 200`() {
val hanke = HankeFactory.create().apply { generated = true }
every { hankeService.createHanke(hanke.copy(id = null, generated = false)) } returns
hanke.copy(generated = false)
val hanke = HankeFactory.create().withYhteystiedot().apply { generated = true }
val expectedServiceArgument =
hanke.apply {
generated = false
id = null
}
every { hankeService.createHanke(expectedServiceArgument) } returns
expectedServiceArgument

post(url, hanke).andExpect(status().isOk)

verifySequence {
hankeService.createHanke(any())
hankeService.createHanke(expectedServiceArgument)
disclosureLogService.saveDisclosureLogsForHanke(any(), any())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,6 @@ class EmailSenderServiceITest : DatabaseTest() {
contains("""<a href="http://localhost:3001/${data.invitationToken}">""")
}
}

@Test
fun `sendHankeInvitationEmail handles input without inviter name`() {
val data = hankeInvitationData(inviterName = null)

emailSenderService.sendHankeInvitationEmail(data)

val email = greenMail.firstReceivedMessage()
val (textBody, htmlBody) = getBodiesFromHybridEmail(email)
assertThat(textBody).contains("Asioija ${data.inviterEmail}")
assertThat(htmlBody).contains("Asioija ${data.inviterEmail}")
}
}

@Nested
Expand Down Expand Up @@ -229,18 +217,6 @@ class EmailSenderServiceITest : DatabaseTest() {
contains("""<a href="http://localhost:3001">""")
}
}

@Test
fun `sendApplicationInvitationEmail handles input without inviter name`() {
val data = applicationInvitationData(inviterName = null)

emailSenderService.sendApplicationInvitationEmail(data)

val email = greenMail.firstReceivedMessage()
val (textBody, htmlBody) = getBodiesFromHybridEmail(email)
assertThat(textBody).contains("Asioija ${data.inviterEmail} on tehnyt")
assertThat(htmlBody).contains("Asioija ${data.inviterEmail} on tehnyt")
}
}

/** Returns a (text body, HTML body) pair. */
Expand Down Expand Up @@ -270,7 +246,7 @@ class EmailSenderServiceITest : DatabaseTest() {
return Pair(bodies[0], bodies[1])
}

private fun hankeInvitationData(inviterName: String? = DEFAULT_INVITER_NAME) =
private fun hankeInvitationData(inviterName: String = DEFAULT_INVITER_NAME) =
HankeInvitationData(
inviterName = inviterName,
inviterEmail = "[email protected]",
Expand All @@ -280,7 +256,7 @@ class EmailSenderServiceITest : DatabaseTest() {
invitationToken = "MgtzRbcPsvoKQamnaSxCnmW7",
)

private fun applicationInvitationData(inviterName: String? = DEFAULT_INVITER_NAME) =
private fun applicationInvitationData(inviterName: String = DEFAULT_INVITER_NAME) =
ApplicationInvitationData(
inviterName = inviterName,
inviterEmail = "[email protected]",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,40 @@ import assertk.assertions.hasSize
import assertk.assertions.isEmpty
import assertk.assertions.isEqualTo
import assertk.assertions.isIn
import assertk.assertions.isNotEmpty
import assertk.assertions.isNotNull
import assertk.assertions.isNull
import assertk.assertions.isTrue
import assertk.assertions.matches
import assertk.assertions.messageContains
import com.fasterxml.jackson.databind.util.ClassUtil.hasClass
import com.ninjasquad.springmockk.MockkBean
import fi.hel.haitaton.hanke.DatabaseTest
import fi.hel.haitaton.hanke.email.EmailSenderService
import fi.hel.haitaton.hanke.email.HankeInvitationData
import fi.hel.haitaton.hanke.factory.AlluDataFactory
import fi.hel.haitaton.hanke.factory.AlluDataFactory.Companion.defaultApplicationName
import fi.hel.haitaton.hanke.factory.AlluDataFactory.Companion.teppoEmail
import fi.hel.haitaton.hanke.factory.AlluDataFactory.Companion.withContacts
import fi.hel.haitaton.hanke.factory.HankeFactory
import fi.hel.haitaton.hanke.factory.HankeFactory.Companion.withGeneratedOmistaja
import fi.hel.haitaton.hanke.factory.HankeFactory.Companion.withYhteystiedot
import fi.hel.haitaton.hanke.factory.HankeYhteystietoFactory
import fi.hel.haitaton.hanke.factory.TEPPO_TESTI
import fi.hel.haitaton.hanke.logging.AuditLogRepository
import fi.hel.haitaton.hanke.logging.ObjectType
import fi.hel.haitaton.hanke.logging.Operation
import fi.hel.haitaton.hanke.logging.UserRole
import fi.hel.haitaton.hanke.test.Asserts.isRecent
import fi.hel.haitaton.hanke.toChangeLogJsonString
import io.mockk.checkUnnecessaryStub
import io.mockk.clearAllMocks
import io.mockk.confirmVerified
import io.mockk.justRun
import io.mockk.verify
import java.time.OffsetDateTime
import java.util.UUID
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
Expand All @@ -51,6 +65,7 @@ const val kayttajaTunnistePattern = "[a-zA-z0-9]{24}"
class HankeKayttajaServiceITest : DatabaseTest() {

@Autowired private lateinit var hankeKayttajaService: HankeKayttajaService
@Autowired private lateinit var permissionService: PermissionService

@Autowired private lateinit var hankeFactory: HankeFactory

Expand All @@ -59,7 +74,18 @@ class HankeKayttajaServiceITest : DatabaseTest() {
@Autowired private lateinit var permissionRepository: PermissionRepository
@Autowired private lateinit var auditLogRepository: AuditLogRepository

@Autowired private lateinit var permissionService: PermissionService
@MockkBean private lateinit var emailSenderService: EmailSenderService

@BeforeEach
fun setup() {
clearAllMocks()
}

@AfterEach
fun tearDown() {
checkUnnecessaryStub()
confirmVerified(emailSenderService)
}

@Nested
inner class GetKayttajatByHankeId {
Expand Down Expand Up @@ -95,6 +121,61 @@ class HankeKayttajaServiceITest : DatabaseTest() {
}
}

@Nested
inner class GetKayttajaByCurrentUser {

@Test
fun `When user exists should return current hanke user`() {
val hankeWithApplications = hankeFactory.saveGenerated(userId = USERNAME)

val result: HankeKayttajaDto? =
hankeKayttajaService.getKayttajaByCurrentUser(
hankeId = hankeWithApplications.hanke.id!!
)

assertThat(result).isNotNull()
with(result!!) {
assertThat(id).isNotNull()
assertThat(sahkoposti).isEqualTo(teppoEmail)
assertThat(nimi).isEqualTo(TEPPO_TESTI)
assertThat(kayttooikeustaso).isEqualTo(Kayttooikeustaso.KAIKKI_OIKEUDET)
assertThat(tunnistautunut).isTrue()
}
}

@Test
fun `When no hanke should return null`() {
val result: HankeKayttajaDto? =
hankeKayttajaService.getKayttajaByCurrentUser(hankeId = 123)

assertThat(result).isNull()
}

@Test
fun `When no related permission should return null`() {
val hanke = hankeFactory.save()
permissionRepository.deleteAll()

val result: HankeKayttajaDto? =
hankeKayttajaService.getKayttajaByCurrentUser(hankeId = hanke.id!!)

assertThat(result).isNull()
}

@Test
fun `When no kayttaja should return null`() {
val hankeWithApplications = hankeFactory.saveGenerated(userId = USERNAME)
val hankeId = hankeWithApplications.hanke.id!!
val jou = hankeKayttajaService.getKayttajaByCurrentUser(hankeId)!!
hankeKayttajaRepository.deleteById(jou.id)

val result: HankeKayttajaDto? =
hankeKayttajaService.getKayttajaByCurrentUser(hankeId = hankeId)

assertThat(result).isNull()
}
}

@Nested
inner class AddHankeFounder {
private val perustaja = HankeFactory.defaultPerustaja
Expand Down Expand Up @@ -371,6 +452,27 @@ class HankeKayttajaServiceITest : DatabaseTest() {
"[email protected]",
)
}

@Test
fun `Sends emails for new hanke users`() {
val hanke = hankeFactory.saveGenerated(userId = USERNAME).hanke
val hankeWithYhteystiedot = hanke.withYhteystiedot() // 4 sub contacts
val capturedEmails = mutableListOf<HankeInvitationData>()
justRun { emailSenderService.sendHankeInvitationEmail(capture(capturedEmails)) }

hankeKayttajaService.saveNewTokensFromHanke(hankeWithYhteystiedot)

verify(exactly = 4) { emailSenderService.sendHankeInvitationEmail(any()) }
assertThat(capturedEmails).each { inv ->
inv.transform { it.inviterName }.isEqualTo(TEPPO_TESTI)
inv.transform { it.inviterEmail }.isEqualTo(teppoEmail)
inv.transform { it.recipientEmail }
.isIn("yhteys-email1", "yhteys-email2", "yhteys-email3", "yhteys-email4")
inv.transform { it.hankeTunnus }.isEqualTo(hanke.hankeTunnus!!)
inv.transform { it.hankeNimi }.isEqualTo(defaultApplicationName)
inv.transform { it.invitationToken }.isNotEmpty()
}
}
}

@Nested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,11 @@ When Hanke is created:
if (hanke == null) {
throw HankeArgumentException("No hanke given when creating hanke")
}
val sanitizedHanke = hanke.copy(id = null, generated = false)
val sanitizedHanke =
hanke.apply {
id = null
generated = false
}

val userId = currentUserId()
logger.info { "Creating Hanke for user $userId: ${hanke.toLogString()} " }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ data class EmailFilterProperties(
)

data class ApplicationInvitationData(
val inviterName: String?,
val inviterName: String,
val inviterEmail: String,
val recipientEmail: String,
val applicationType: ApplicationType,
Expand All @@ -37,7 +37,7 @@ data class ApplicationInvitationData(
)

data class HankeInvitationData(
val inviterName: String?,
val inviterName: String,
val inviterEmail: String,
val recipientEmail: String,
val hankeTunnus: String,
Expand Down Expand Up @@ -80,7 +80,7 @@ class EmailSenderService(
val templateData =
mapOf(
"baseUrl" to emailConfig.baseUrl,
"inviterInfo" to defineInviterInfo(data.inviterName, data.inviterEmail),
"inviterInfo" to inviterInfo(data.inviterName, data.inviterEmail),
"hankeTunnus" to data.hankeTunnus,
"hankeNimi" to data.hankeNimi,
"invitationToken" to data.invitationToken,
Expand All @@ -97,7 +97,7 @@ class EmailSenderService(
val templateData =
mapOf(
"baseUrl" to emailConfig.baseUrl,
"inviterInfo" to defineInviterInfo(data.inviterName, data.inviterEmail),
"inviterInfo" to inviterInfo(data.inviterName, data.inviterEmail),
"applicationType" to applicationTypeText,
"applicationIdentifier" to data.applicationIdentifier,
"hankeTunnus" to data.hankeTunnus,
Expand Down Expand Up @@ -127,8 +127,7 @@ class EmailSenderService(
mailSender.send(mimeMessage)
}

private fun defineInviterInfo(name: String?, email: String): String =
if (name.isNullOrBlank()) "Asioija $email" else "$name ($email)"
private fun inviterInfo(name: String, email: String): String = "$name ($email)"

private fun convertApplicationTypeFinnish(type: ApplicationType): String =
when (type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ interface HankeKayttajaRepository : JpaRepository<HankeKayttajaEntity, UUID> {
hankeId: Int,
sahkopostit: List<String>
): List<HankeKayttajaEntity>

fun findByPermissionId(permissionId: Int): HankeKayttajaEntity?
}
Loading

0 comments on commit 4090bed

Please sign in to comment.