Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BTD-585] Audit service changes #115

Merged
merged 1 commit into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package uk.gov.justice.digital.hmpps.learnerrecordsapi.config

import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.AuditEvent.MATCH_LE
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.AuditEvent.MATCH_ULN
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.AuditEvent.createAuditEvent
import uk.gov.justice.hmpps.sqs.audit.HmppsAuditEvent
import uk.gov.justice.hmpps.sqs.audit.HmppsAuditService
import java.time.Instant
import java.util.UUID

object AuditEvent {
const val MATCH_ULN = "match-uln"
const val MATCH_LE = "match-learner-events"

fun createAuditEvent(whatEvent: String, userName: String, requestParams: String): HmppsAuditEvent = HmppsAuditEvent(
what = whatEvent,
correlationId = UUID.randomUUID().toString(),
`when` = Instant.now(),
who = userName,
service = "hmpps-learner-records-api",
details = requestParams,
)
}

class AuditHelper(private val auditService: HmppsAuditService) {

private suspend fun publishAuditEvent(whatEvent: String, userName: String, requestParams: String) {
val hmppsLRSEvent = createAuditEvent(
whatEvent,
userName,
requestParams,
)
auditService.publishEvent(hmppsLRSEvent)
}

suspend fun publishMatchAuditEvent(userName: String, nomisId: String) {
publishAuditEvent(MATCH_ULN, userName, nomisId)
}

suspend fun publishLearnerEventsAuditEvent(userName: String, nomisId: String) {
publishAuditEvent(MATCH_LE, userName, nomisId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.AuditEvent.createAuditEvent
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.AuditHelper
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.Roles.ROLE_LEARNERS_RO
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.Roles.ROLE_LEARNERS_UI
import uk.gov.justice.digital.hmpps.learnerrecordsapi.logging.LoggerUtil
Expand All @@ -27,6 +27,7 @@ import uk.gov.justice.digital.hmpps.learnerrecordsapi.openapi.MatchCheckApi
import uk.gov.justice.digital.hmpps.learnerrecordsapi.openapi.MatchConfirmApi
import uk.gov.justice.digital.hmpps.learnerrecordsapi.service.LearnerEventsService
import uk.gov.justice.digital.hmpps.learnerrecordsapi.service.MatchService
import uk.gov.justice.hmpps.kotlin.auth.HmppsAuthenticationHolder
import uk.gov.justice.hmpps.sqs.audit.HmppsAuditService
import java.net.URI

Expand All @@ -35,20 +36,25 @@ import java.net.URI
class MatchResource(
private val matchService: MatchService,
private val learnerEventsService: LearnerEventsService,
private val auditService: HmppsAuditService,
auditService: HmppsAuditService,
private val authHolder: HmppsAuthenticationHolder,
) {

val logger = LoggerUtil.getLogger<MatchResource>()
val searchLearnerEventsByNomisId = "SEARCH_LEARNER_EVENTS_BY_NOMISID"
private val auditHelper = AuditHelper(auditService)

@PreAuthorize("hasAnyRole('$ROLE_LEARNERS_UI', '$ROLE_LEARNERS_RO')")
@GetMapping("/{nomisId}")
@Tag(name = "Match")
@MatchCheckApi
fun findMatch(
suspend fun findMatch(
@PathVariable(name = "nomisId", required = true) nomisId: String,
@RequestHeader("X-Username", required = true) userName: String,
): ResponseEntity<CheckMatchResponse> {
val roles = authHolder.roles.map { it?.authority ?: "" }
if (roles.contains(ROLE_LEARNERS_RO)) {
auditHelper.publishMatchAuditEvent(userName, nomisId)
}
logger.log("Received a get request to match endpoint", nomisId)
return matchService.findMatch(nomisId)?.let {
ResponseEntity.status(HttpStatus.OK).body(
Expand Down Expand Up @@ -83,7 +89,7 @@ class MatchResource(
@PathVariable(name = "nomisId", required = true) nomisId: String,
@RequestHeader("X-Username", required = true) userName: String,
): ResponseEntity<LearnerEventsResponse> {
auditService.publishEvent(createAuditEvent(searchLearnerEventsByNomisId, userName, nomisId))
auditHelper.publishLearnerEventsAuditEvent(userName, nomisId)
logger.log("Received a post request to learner events by Nomis ID endpoint", nomisId)
val checkMatchResponse: CheckMatchResponse? = learnerEventsService.getMatchEntityForNomisId(nomisId)
if (checkMatchResponse == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.AuditEvent.createAuditEvent
import uk.gov.justice.digital.hmpps.learnerrecordsapi.models.request.Gender
import uk.gov.justice.digital.hmpps.learnerrecordsapi.models.request.LearnersRequest
import java.time.Instant
Expand All @@ -20,7 +21,7 @@ class AuditEventTest {
lastKnownPostCode = "NE2 2AS",
emailAddress = "test@example.com",
)
val hmppsAuditEvent = AuditEvent.createAuditEvent("searchLearnersByDemographics", "User", request.toString())
val hmppsAuditEvent = createAuditEvent("searchLearnersByDemographics", "User", request.toString())
assertEquals("hmpps-learner-records-api", hmppsAuditEvent.service)
assertEquals("searchLearnersByDemographics", hmppsAuditEvent.what)
assertEquals("User", hmppsAuditEvent.who)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.springframework.http.MediaType
import org.springframework.test.web.reactive.server.WebTestClient
import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest
import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.AuditEvent.MATCH_LE
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.HmppsBoldLrsExceptionHandler
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.Roles.ROLE_LEARNERS_RO
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.Roles.ROLE_LEARNERS_UI
Expand Down Expand Up @@ -266,7 +267,7 @@ class MatchResourceIntTest : IntegrationTestBase() {
HmppsAuditEvent::class.java,
)

assertThat(receivedEvent.what).isEqualTo("SEARCH_LEARNER_EVENTS_BY_NOMISID")
assertThat(receivedEvent.what).isEqualTo(MATCH_LE)
assertThat(receivedEvent.who).isEqualTo("TestUser")
assertThat(receivedEvent.service).isEqualTo("hmpps-learner-records-api")
assertThat(receivedEvent.`when`).isBeforeOrEqualTo(Instant.now())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.springframework.http.HttpStatus
import org.springframework.security.core.GrantedAuthority
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.Roles.ROLE_LEARNERS_RO
import uk.gov.justice.digital.hmpps.learnerrecordsapi.config.Roles.ROLE_LEARNERS_UI
import uk.gov.justice.digital.hmpps.learnerrecordsapi.models.lrsapi.response.exceptions.MatchNotFoundException
import uk.gov.justice.digital.hmpps.learnerrecordsapi.models.response.CheckMatchResponse
import uk.gov.justice.digital.hmpps.learnerrecordsapi.models.response.CheckMatchStatus
import uk.gov.justice.digital.hmpps.learnerrecordsapi.service.LearnerEventsService
import uk.gov.justice.digital.hmpps.learnerrecordsapi.service.MatchService
import uk.gov.justice.hmpps.kotlin.auth.HmppsAuthenticationHolder
import uk.gov.justice.hmpps.sqs.audit.HmppsAuditService
import java.time.LocalDate

Expand All @@ -33,50 +39,60 @@ class MatchResourceTest {
private lateinit var learnerEventsResource: LearnerEventsResource
private lateinit var mockAuditService: HmppsAuditService
private lateinit var mockLearnerEventsService: LearnerEventsService
private lateinit var mockAuthHolder: HmppsAuthenticationHolder

@BeforeEach
fun setup() {
mockMatchService = mock(MatchService::class.java)
mockLearnerEventsService = mock(LearnerEventsService::class.java)
mockAuditService = mock(HmppsAuditService::class.java)
matchResource = MatchResource(mockMatchService, mockLearnerEventsService, mockAuditService)
mockAuthHolder = mock(HmppsAuthenticationHolder::class.java)
matchResource = MatchResource(mockMatchService, mockLearnerEventsService, mockAuditService, mockAuthHolder)
learnerEventsResource = LearnerEventsResource(mockLearnerEventsService)
}

private fun setRoleAndUln(role: String, uln: String?) {
`when`(mockAuthHolder.roles).thenReturn(
listOf(GrantedAuthority { role }),
)
`when`(mockMatchService.findMatch(any())).thenReturn(
uln?.let {
CheckMatchResponse(
matchedUln = it,
)
},
)
}

@Test
fun `should return NOT_FOUND if no record found`() {
`when`(mockMatchService.findMatch(any())).thenReturn(null)
fun `should return NOT_FOUND if no record found`() = runTest {
setRoleAndUln(ROLE_LEARNERS_UI, null)

val actual = matchResource.findMatch(nomisId, "")
assertThat(actual.statusCode).isEqualTo(HttpStatus.NOT_FOUND)
assertThat(actual.body?.status ?: "").isEqualTo(CheckMatchStatus.NotFound)
verify(mockAuditService, times(0)).publishEvent(any())
}

@Test
fun `should return entity if record found`() {
`when`(mockMatchService.findMatch(any())).thenReturn(
CheckMatchResponse(
matchedUln = matchedUln,
),
)
fun `should return entity if record found`() = runTest {
setRoleAndUln(ROLE_LEARNERS_RO, matchedUln)

val actual = matchResource.findMatch(nomisId, "")
assertThat(actual.statusCode).isEqualTo(HttpStatus.OK)
assertThat(actual.body?.matchedUln ?: "").isEqualTo(matchedUln)
assertThat(actual.body?.status ?: "").isEqualTo(CheckMatchStatus.Found)
verify(mockAuditService, times(1)).publishEvent(any())
}

@Test
fun `should return NO_MATCH if id cannot be matched`() {
`when`(mockMatchService.findMatch(any())).thenReturn(
CheckMatchResponse(
matchedUln = "",
),
)
fun `should return NO_MATCH if id cannot be matched`() = runTest {
setRoleAndUln(ROLE_LEARNERS_UI, "")

val actual = matchResource.findMatch(nomisId, "")
assertThat(actual.statusCode).isEqualTo(HttpStatus.OK)
assertThat(actual.body?.status ?: "").isEqualTo(CheckMatchStatus.NoMatch)
verify(mockAuditService, times(0)).publishEvent(any())
}

@Test
Expand All @@ -86,6 +102,7 @@ class MatchResourceTest {
matchResource.findLearnerEventsByNomisId(nomisId, "")
}
assertThat(exception.message).isEqualTo(nomisId)
verify(mockAuditService, times(1)).publishEvent(any())
}

@Test
Expand Down