Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-britton-moj committed Jan 9, 2025
1 parent dfceab5 commit a4b8366
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package uk.gov.justice.digital.hmpps

import com.microsoft.graph.serviceclient.GraphServiceClient
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
import org.springframework.boot.test.mock.mockito.SpyBean
import uk.gov.justice.digital.hmpps.data.generator.Data
import uk.gov.justice.digital.hmpps.entity.Contact
import uk.gov.justice.digital.hmpps.entity.ContactRepository
Expand All @@ -37,7 +33,7 @@ internal class IntegrationTest {
@MockBean
lateinit var telemetryService: TelemetryService

@SpyBean
@MockBean
lateinit var mailBoxService: MailboxService

@Test
Expand Down Expand Up @@ -119,8 +115,7 @@ 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())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ package uk.gov.justice.digital.hmpps
import org.openfolder.kotlinasyncapi.springweb.EnableAsyncApi
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableAsync
import org.springframework.scheduling.annotation.EnableScheduling

@EnableAsync
@EnableAsyncApi
@EnableScheduling
@SpringBootApplication
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,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 @@ -41,48 +42,50 @@ class Handler(
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()
Expand All @@ -92,7 +95,7 @@ class Handler(
eventPublisher.publishEvent(
UnableToCreateContactFromEmail(this, "Unable to parse CRN from message subject")
)
throw IllegalArgumentException("No CRN in 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
Expand Up @@ -10,16 +10,13 @@ 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.scheduling.annotation.Async
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.retry.retry
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService
import java.time.Duration.ofMillis

@Service
class MailboxService(
Expand All @@ -40,16 +37,19 @@ class MailboxService(
}
}

@Async
@EventListener(UnableToCreateContactFromEmail::class)
fun onUnableToCreateContactFromEmail(event: UnableToCreateContactFromEmail) = retry(3, delay = ofMillis(250)) {
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
Expand Down

0 comments on commit a4b8366

Please sign in to comment.