Skip to content

Commit

Permalink
PI-2649 Reply by email on error (#4529)
Browse files Browse the repository at this point in the history
* PI-2649

---------

Co-authored-by: probation-integration-bot[bot] <177347787+probation-integration-bot[bot]@users.noreply.github.com>
  • Loading branch information
1 parent b25fa04 commit fd67b0a
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ package uk.gov.justice.digital.hmpps
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
import org.mockito.Mockito.atLeastOnce
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.verify
import org.mockito.kotlin.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
Expand All @@ -20,6 +18,7 @@ import uk.gov.justice.digital.hmpps.message.Notification
import uk.gov.justice.digital.hmpps.messaging.EmailMessage
import uk.gov.justice.digital.hmpps.messaging.Handler
import uk.gov.justice.digital.hmpps.resourceloader.ResourceLoader.get
import uk.gov.justice.digital.hmpps.service.MailboxService
import uk.gov.justice.digital.hmpps.telemetry.TelemetryMessagingExtensions.notificationReceived
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService

Expand All @@ -34,6 +33,9 @@ internal class IntegrationTest {
@MockBean
lateinit var telemetryService: TelemetryService

@MockBean
lateinit var mailBoxService: MailboxService

@Test
fun `contact is created`() {
val notification = Notification(get<EmailMessage>("successful-message"))
Expand Down Expand Up @@ -113,8 +115,8 @@ internal class IntegrationTest {
@Test
fun `error when missing crn`() {
val notification = Notification(get<EmailMessage>("no-crn"))
val exception = assertThrows<IllegalArgumentException> { handler.handle(notification) }
assertThat(exception.message, equalTo("No CRN in message subject"))
assertDoesNotThrow { handler.handle(notification) }
verify(mailBoxService).onUnableToCreateContactFromEmail(any())
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.openfolder.kotlinasyncapi.annotation.Schema
import org.openfolder.kotlinasyncapi.annotation.channel.Channel
import org.openfolder.kotlinasyncapi.annotation.channel.Message
import org.openfolder.kotlinasyncapi.annotation.channel.Publish
import org.springframework.context.ApplicationEventPublisher
import org.springframework.ldap.NameNotFoundException
import org.springframework.ldap.core.AttributesMapper
import org.springframework.ldap.core.LdapTemplate
Expand All @@ -19,6 +20,7 @@ import uk.gov.justice.digital.hmpps.datetime.DeliusDateFormatter
import uk.gov.justice.digital.hmpps.entity.*
import uk.gov.justice.digital.hmpps.entity.ContactType.Code.EMAIL
import uk.gov.justice.digital.hmpps.entity.Person.Companion.CRN_REGEX
import uk.gov.justice.digital.hmpps.exception.IgnorableMessageException
import uk.gov.justice.digital.hmpps.message.Notification
import uk.gov.justice.digital.hmpps.telemetry.TelemetryMessagingExtensions.notificationReceived
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService
Expand All @@ -37,56 +39,66 @@ class Handler(
private val personManagerRepository: PersonManagerRepository,
private val staffRepository: StaffRepository,
private val ldapTemplate: LdapTemplate,
private val eventPublisher: ApplicationEventPublisher,
) : NotificationHandler<EmailMessage>, AuditableService(auditedInteractionService) {
@Publish(messages = [Message(title = "email-message", payload = Schema(EmailMessage::class))])
override fun handle(notification: Notification<EmailMessage>) = audit(ADD_CONTACT) { audit ->
telemetryService.notificationReceived(notification)
val message = notification.message
override fun handle(notification: Notification<EmailMessage>) = try {
audit(ADD_CONTACT) { audit ->
telemetryService.notificationReceived(notification)
val message = notification.message

val crn = message.extractCrn()
val emailAddress =
message.fromEmailAddress.takeIf { it.endsWith("@justice.gov.uk") || it.endsWith("@digital.justice.gov.uk") }
?: throw IllegalArgumentException("Email address does not end with @justice.gov.uk or @digital.justice.gov.uk")
val person = personRepository.getByCrn(crn)
val manager = personManagerRepository.getManager(person.id)
val staffId = findStaffIdForEmailAddress(emailAddress) ?: manager.staffId
val fullNotes = """
val crn = message.extractCrn()
val emailAddress =
message.fromEmailAddress.takeIf { it.endsWith("@justice.gov.uk") || it.endsWith("@digital.justice.gov.uk") }
?: throw IllegalArgumentException("Email address does not end with @justice.gov.uk or @digital.justice.gov.uk")
val person = personRepository.getByCrn(crn)
val manager = personManagerRepository.getManager(person.id)
val staffId = findStaffIdForEmailAddress(emailAddress) ?: manager.staffId
val fullNotes = """
|This contact was created automatically from a forwarded email sent by ${message.fromEmailAddress} ${message.onAt}.
|Subject: ${message.subject}
|
|${htmlToMarkdownConverter.convert(message.bodyContent)}
""".trimMargin()
val contact = contactRepository.save(
Contact(
personId = person.id,
externalReference = "urn:uk:gov:hmpps:justice-email:${message.id}",
type = contactTypeRepository.getByCode(EMAIL),
date = message.receivedDateTime,
startTime = message.receivedDateTime,
description = "Email - ${message.subject.replace(CRN_REGEX.toRegex(), "").trim()}".truncated(),
notes = fullNotes,
staffId = staffId,
teamId = manager.teamId,
providerId = manager.providerId,
val contact = contactRepository.save(
Contact(
personId = person.id,
externalReference = "urn:uk:gov:hmpps:justice-email:${message.id}",
type = contactTypeRepository.getByCode(EMAIL),
date = message.receivedDateTime,
startTime = message.receivedDateTime,
description = "Email - ${message.subject.replace(CRN_REGEX.toRegex(), "").trim()}".truncated(),
notes = fullNotes,
staffId = staffId,
teamId = manager.teamId,
providerId = manager.providerId,
)
)
)
audit["contactId"] = contact.id
audit["contactId"] = contact.id

telemetryService.trackEvent(
"CreatedContact", mapOf(
"crn" to crn,
"staffId" to staffId.toString(),
"contactId" to contact.id.toString(),
"messageId" to message.id,
telemetryService.trackEvent(
"CreatedContact", mapOf(
"crn" to crn,
"staffId" to staffId.toString(),
"contactId" to contact.id.toString(),
"messageId" to message.id,
)
)
)
}
} catch (_: IgnorableMessageException) {
}

private fun EmailMessage.extractCrn(): String {
val crns = CRN_REGEX.toRegex().findAll(subject).map { it.value }.distinct()
return when (crns.count()) {
1 -> crns.single().uppercase()
0 -> throw IllegalArgumentException("No CRN in message subject")
0 -> {
eventPublisher.publishEvent(
UnableToCreateContactFromEmail(this, "Unable to parse CRN from message subject")
)
throw IgnorableMessageException("No CRN in message subject")
}

else -> throw IllegalArgumentException("Multiple CRNs in message subject")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package uk.gov.justice.digital.hmpps.messaging

data class UnableToCreateContactFromEmail(val email: EmailMessage, val reason: String)
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package uk.gov.justice.digital.hmpps.service

import com.microsoft.graph.models.EmailAddress
import com.microsoft.graph.models.ItemBody
import com.microsoft.graph.models.Message
import com.microsoft.graph.models.Recipient
import com.microsoft.graph.serviceclient.GraphServiceClient
import com.microsoft.graph.users.item.sendmail.SendMailPostRequestBody
import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.instrumentation.annotations.WithSpan
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.event.EventListener
import org.springframework.stereotype.Service
import uk.gov.justice.digital.hmpps.message.MessageAttributes
import uk.gov.justice.digital.hmpps.message.Notification
import uk.gov.justice.digital.hmpps.messaging.EmailMessage
import uk.gov.justice.digital.hmpps.messaging.UnableToCreateContactFromEmail
import uk.gov.justice.digital.hmpps.publisher.NotificationPublisher
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService

Expand All @@ -31,6 +37,21 @@ class MailboxService(
}
}

@EventListener(UnableToCreateContactFromEmail::class)
fun onUnableToCreateContactFromEmail(event: UnableToCreateContactFromEmail) {
val toEmailAddress = EmailAddress().apply { address = event.email.fromEmailAddress }
val message = Message().apply {
subject = "Unable to create contact from email"
body = ItemBody().apply { content = "Reason for the contact not being created: ${event.reason}" }
toRecipients = listOf(Recipient().apply { emailAddress = toEmailAddress })
}
graphServiceClient.me().sendMail().post(SendMailPostRequestBody().apply { setMessage(message) })
telemetryService.trackEvent(
"UnableToCreateContactFromEmail",
mapOf("emailId" to event.email.id, "reason" to event.reason)
)
}

private fun getUnreadMessages() = graphServiceClient
.users()
.byUserId(emailAddress)
Expand Down

0 comments on commit fd67b0a

Please sign in to comment.