Skip to content

Commit 61a4659

Browse files
OV-471 - Telemetry metrics for Visitor Create/Remove and TimeSLot created (#149)
* OV-471 - Telemetry metrics for Visitor Create/Remove and TimeSLot created * OV-448 audit sync of visit creation and visit visitor addition. (#148) * OV-471 - Telemetry metrics for Visitor Create/Remove and TimeSLot created * OV-471 - Resolve Conflicts * OV-471 - Resolve Conflicts --------- Co-authored-by: Matt Crowson <60104344+matt-crowson-moj@users.noreply.github.com>
1 parent 83d91cc commit 61a4659

File tree

11 files changed

+387
-22
lines changed

11 files changed

+387
-22
lines changed

src/main/kotlin/uk/gov/justice/digital/hmpps/officialvisitsapi/service/OfficialVisitCreateService.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.PrisonVisitSlot
2121
import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.PrisonerVisitedRepository
2222
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.auditing.AuditingService
2323
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.auditing.auditVisitCreateEvent
24+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.Source
2425
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsEvents
2526
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsService
2627
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.VisitMetricInfo
28+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.VisitorMetricInfo
2729
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.slotavailability.AvailableSlotService
2830
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.slotavailability.AvailableSlotSpecification
2931
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.slotavailability.AvailableSlotSpecificationFactory
@@ -106,6 +108,20 @@ class OfficialVisitCreateService(
106108
)
107109
},
108110
)
111+
}.also {
112+
it.visitorAndContactIds.forEach { value ->
113+
metricsService.send(
114+
MetricsEvents.ADD_VISITOR,
115+
VisitorMetricInfo(
116+
source = Source.DPS,
117+
username = user.username,
118+
prisonCode = prisonCode,
119+
officialVisitId = it.officialVisitId,
120+
contactId = value.second!!,
121+
officialVisitorId = value.first,
122+
),
123+
)
124+
}
109125
}
110126
}
111127

src/main/kotlin/uk/gov/justice/digital/hmpps/officialvisitsapi/service/OfficialVisitUpdateService.kt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.OfficialVisitor
2323
import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.PrisonVisitSlotRepository
2424
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.auditing.AuditingService
2525
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.auditing.auditVisitChangeEvent
26+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.Source
2627
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsEvents
2728
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsService
2829
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.VisitMetricInfo
30+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.VisitorMetricInfo
2931
import java.time.LocalDateTime
3032

3133
@Service
@@ -34,7 +36,7 @@ class OfficialVisitUpdateService(
3436
private val officialVisitorRepository: OfficialVisitorRepository,
3537
private val prisonVisitSlotRepository: PrisonVisitSlotRepository,
3638
private val contactsService: ContactsService,
37-
val metricsService: MetricsService,
39+
private val metricsService: MetricsService,
3840
private val auditingService: AuditingService,
3941
) {
4042
/**
@@ -210,6 +212,34 @@ class OfficialVisitUpdateService(
210212
}
211213
},
212214
)
215+
}.also {
216+
it.visitorsAdded.forEach { value ->
217+
metricsService.send(
218+
MetricsEvents.ADD_VISITOR,
219+
VisitorMetricInfo(
220+
source = Source.DPS,
221+
username = user.username,
222+
prisonCode = prisonCode,
223+
officialVisitId = officialVisitId,
224+
contactId = value.contactId,
225+
officialVisitorId = value.officialVisitorId,
226+
),
227+
)
228+
}
229+
}.also {
230+
it.visitorsDeleted.forEach { value ->
231+
metricsService.send(
232+
MetricsEvents.REMOVE_VISITOR,
233+
VisitorMetricInfo(
234+
source = Source.DPS,
235+
username = user.username,
236+
prisonCode = prisonCode,
237+
officialVisitId = officialVisitId,
238+
contactId = value.contactId,
239+
officialVisitorId = value.officialVisitorId,
240+
),
241+
)
242+
}
213243
}
214244
}
215245

src/main/kotlin/uk/gov/justice/digital/hmpps/officialvisitsapi/service/admin/PrisonTimeSlotService.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.PrisonTimeSlotR
2121
import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.PrisonVisitSlotRepository
2222
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.LocationsService
2323
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.User
24+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.Source
25+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsEvents
26+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsService
27+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.TimeSlotInfo
2428
import java.time.LocalDate
2529
import java.time.LocalDateTime
2630

@@ -31,6 +35,7 @@ class PrisonTimeSlotService(
3135
private val prisonerSearchClient: PrisonerSearchClient,
3236
private val locationService: LocationsService,
3337
private val officialVisitRepository: OfficialVisitRepository,
38+
private val metricsService: MetricsService,
3439
) {
3540
@Transactional(readOnly = true)
3641
fun getPrisonTimeSlotById(prisonTimeSlotId: Long): TimeSlot {
@@ -42,7 +47,17 @@ class PrisonTimeSlotService(
4247
@Transactional
4348
fun create(request: CreateTimeSlotRequest, user: User): TimeSlot {
4449
request.validate()
45-
return prisonTimeSlotRepository.saveAndFlush(request.toEntity(user.username)).toModel()
50+
return prisonTimeSlotRepository.saveAndFlush(request.toEntity(user.username)).toModel().also {
51+
metricsService.send(
52+
eventType = MetricsEvents.TIMESLOT_ADDED,
53+
info = TimeSlotInfo(
54+
source = Source.DPS,
55+
username = user.username,
56+
prisonCode = request.prisonCode,
57+
dayCode = request.dayCode.toString(),
58+
),
59+
)
60+
}
4661
}
4762

4863
@Transactional

src/main/kotlin/uk/gov/justice/digital/hmpps/officialvisitsapi/service/metrics/MetricsEvents.kt

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ enum class MetricsEvents(val eventType: String) {
4141
additionalInformation = additionalInformation,
4242
)
4343
},
44+
ADD_VISITOR("OfficialVisitorAdded") {
45+
override fun event(additionalInformation: MetricInfo) = OfficialVisitMetricTelemetry(
46+
eventType = eventType,
47+
additionalInformation = additionalInformation,
48+
)
49+
},
50+
REMOVE_VISITOR("OfficialVisitorRemoved") {
51+
override fun event(additionalInformation: MetricInfo) = OfficialVisitMetricTelemetry(
52+
eventType = eventType,
53+
additionalInformation = additionalInformation,
54+
)
55+
},
56+
TIMESLOT_ADDED("TimeSlotAdded") {
57+
override fun event(additionalInformation: MetricInfo) = OfficialVisitMetricTelemetry(
58+
eventType = eventType,
59+
additionalInformation = additionalInformation,
60+
)
61+
},
4462
;
4563

4664
abstract fun event(
@@ -57,6 +75,7 @@ data class OfficialVisitMetricTelemetry(
5775
val baseMap = mapOf(
5876
"prison_code" to additionalInformation.prisonCode,
5977
"source" to additionalInformation.source.toString(),
78+
"username" to additionalInformation.username,
6079
)
6180
return when (additionalInformation) {
6281
is VisitMetricInfo -> {
@@ -65,6 +84,14 @@ data class OfficialVisitMetricTelemetry(
6584
is SearchInfo -> {
6685
baseMap + additionalInformation.searchAdditionalInfo()
6786
}
87+
88+
is VisitorMetricInfo -> {
89+
baseMap + additionalInformation.visitorAdditionalInfo()
90+
}
91+
92+
is TimeSlotInfo -> {
93+
baseMap + additionalInformation.timeSLotInfo()
94+
}
6895
}
6996
}
7097

@@ -77,6 +104,9 @@ data class OfficialVisitMetricTelemetry(
77104
"number_of_results" to additionalInformation.numberOfResults.toDouble(),
78105
)
79106
}
107+
else -> {
108+
emptyMap<String, Double>()
109+
}
80110
}
81111

82112
fun VisitMetricInfo.hoursBeforeStartTimeMetric(): Pair<String, Double> = "hoursBeforeStartTime" to ChronoUnit.HOURS.between(
@@ -91,9 +121,17 @@ data class OfficialVisitMetricTelemetry(
91121

92122
private fun VisitMetricInfo.visitAdditionalInfo(): Map<String, String> = mapOf(
93123
"prisoner_number" to prisonerNumber,
94-
"username" to username,
95124
)
96125

126+
private fun VisitorMetricInfo.visitorAdditionalInfo(): Map<String, String> = mapOf(
127+
"official_visit_id" to "$officialVisitId",
128+
"official_visitor_id" to "$officialVisitorId",
129+
"contact_id" to "$contactId",
130+
)
131+
132+
private fun TimeSlotInfo.timeSLotInfo(): Map<String, String> = mapOf(
133+
"day_code" to dayCode,
134+
)
97135
private fun OfficialVisitMetricTelemetry.visitMetrics(
98136
additionalInformation: VisitMetricInfo,
99137
): Map<String, Double> = listOfNotNull(
@@ -141,3 +179,19 @@ data class SearchInfo(
141179
val visitStatuses: List<VisitStatusType>?,
142180
val numberOfResults: Int = 0,
143181
) : MetricInfo(username = username, prisonCode = prisonCode)
182+
183+
data class VisitorMetricInfo(
184+
override val source: Source = Source.DPS,
185+
override val username: String,
186+
override val prisonCode: String,
187+
val officialVisitId: Long,
188+
val contactId: Long,
189+
val officialVisitorId: Long,
190+
) : MetricInfo(source = source, username = username, prisonCode = prisonCode)
191+
192+
data class TimeSlotInfo(
193+
override val source: Source = Source.DPS,
194+
override val username: String,
195+
override val prisonCode: String,
196+
val dayCode: String,
197+
) : MetricInfo(source = source, username = username, prisonCode = prisonCode)

src/main/kotlin/uk/gov/justice/digital/hmpps/officialvisitsapi/service/sync/SyncOfficialVisitorService.kt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import uk.gov.justice.digital.hmpps.officialvisitsapi.service.ContactsService
1919
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.UserService
2020
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.auditing.AuditingService
2121
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.auditing.auditVisitCreateEvent
22+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.Source
23+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsEvents
24+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsService
25+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.VisitorMetricInfo
2226
import java.time.LocalDateTime
2327
import kotlin.jvm.optionals.getOrNull
2428

@@ -29,6 +33,7 @@ class SyncOfficialVisitorService(
2933
private val officialVisitorRepository: OfficialVisitorRepository,
3034
private val contactsService: ContactsService,
3135
private val visitorEquipmentRepository: VisitorEquipmentRepository,
36+
private val metricsService: MetricsService,
3237
private val auditingService: AuditingService,
3338
) {
3439
companion object {
@@ -74,7 +79,19 @@ class SyncOfficialVisitorService(
7479
createdBy = request.createUsername ?: "SYNC",
7580
createdTime = request.createDateTime ?: LocalDateTime.now(),
7681
),
77-
)
82+
).also {
83+
metricsService.send(
84+
MetricsEvents.ADD_VISITOR,
85+
info = VisitorMetricInfo(
86+
source = Source.NOMIS,
87+
username = it.createdBy,
88+
prisonCode = visit.prisonCode,
89+
officialVisitId = visit.officialVisitId,
90+
contactId = it.contactId!!,
91+
officialVisitorId = it.officialVisitorId,
92+
),
93+
)
94+
}
7895

7996
return SyncAddVisitorResponse(
8097
officialVisitId = visit.officialVisitId,
@@ -109,7 +126,19 @@ class SyncOfficialVisitorService(
109126
visitorEquipmentRepository.deleteAllByOfficialVisitor(visitor)
110127
}
111128

112-
visit.removeVisitor(visitor)
129+
visit.removeVisitor(visitor).also {
130+
metricsService.send(
131+
MetricsEvents.REMOVE_VISITOR,
132+
info = VisitorMetricInfo(
133+
source = Source.NOMIS,
134+
username = UserService.getClientAsUser("NOMIS").username,
135+
prisonCode = visit.prisonCode,
136+
officialVisitId = visit.officialVisitId,
137+
contactId = visitor.contactId!!,
138+
officialVisitorId = visitor.officialVisitorId,
139+
),
140+
)
141+
}
113142

114143
return SyncRemoveVisitorResponse(
115144
officialVisitId = visit.officialVisitId,

src/main/kotlin/uk/gov/justice/digital/hmpps/officialvisitsapi/service/sync/SyncTimeSlotService.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,36 @@ import uk.gov.justice.digital.hmpps.officialvisitsapi.model.request.sync.SyncUpd
1212
import uk.gov.justice.digital.hmpps.officialvisitsapi.model.response.sync.SyncTimeSlot
1313
import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.PrisonTimeSlotRepository
1414
import uk.gov.justice.digital.hmpps.officialvisitsapi.repository.PrisonVisitSlotRepository
15+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.Source
16+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsEvents
17+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsService
18+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.TimeSlotInfo
1519

1620
@Service
1721
@Transactional
18-
class SyncTimeSlotService(val prisonTimeSlotRepository: PrisonTimeSlotRepository, val prisonVisitSlotRepository: PrisonVisitSlotRepository) {
22+
class SyncTimeSlotService(
23+
val prisonTimeSlotRepository: PrisonTimeSlotRepository,
24+
val prisonVisitSlotRepository: PrisonVisitSlotRepository,
25+
val metricsService: MetricsService,
26+
) {
1927
@Transactional(readOnly = true)
2028
fun getPrisonTimeSlotById(prisonTimeSlotId: Long): SyncTimeSlot {
2129
val prisonTimeSlotEntity = prisonTimeSlotRepository.findById(prisonTimeSlotId)
2230
.orElseThrow { EntityNotFoundException("Prison time slot with ID $prisonTimeSlotId was not found") }
2331
return prisonTimeSlotEntity.toSyncModel()
2432
}
2533

26-
fun createPrisonTimeSlot(request: SyncCreateTimeSlotRequest) = prisonTimeSlotRepository.saveAndFlush(request.toEntity()).toSyncModel()
34+
fun createPrisonTimeSlot(request: SyncCreateTimeSlotRequest) = prisonTimeSlotRepository.saveAndFlush(request.toEntity()).toSyncModel().also {
35+
metricsService.send(
36+
eventType = MetricsEvents.TIMESLOT_ADDED,
37+
info = TimeSlotInfo(
38+
source = Source.NOMIS,
39+
username = request.createdBy,
40+
prisonCode = request.prisonCode,
41+
dayCode = request.dayCode.toString(),
42+
),
43+
)
44+
}
2745

2846
fun updatePrisonTimeSlot(prisonTimeSlotId: Long, request: SyncUpdateTimeSlotRequest): SyncTimeSlot {
2947
val timeSlotEntity = prisonTimeSlotRepository.findById(prisonTimeSlotId)

src/test/kotlin/uk/gov/justice/digital/hmpps/officialvisitsapi/integration/resource/CreateOfficialVisitIntegrationTest.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package uk.gov.justice.digital.hmpps.officialvisitsapi.integration.resource
33
import org.junit.jupiter.api.AfterEach
44
import org.junit.jupiter.api.BeforeEach
55
import org.junit.jupiter.api.Test
6+
import org.mockito.kotlin.eq
7+
import org.mockito.kotlin.verify
68
import org.springframework.http.MediaType
9+
import org.springframework.test.context.bean.override.mockito.MockitoBean
710
import org.springframework.test.web.reactive.server.WebTestClient
811
import org.springframework.test.web.reactive.server.expectBody
912
import org.springframework.transaction.annotation.Transactional
@@ -36,11 +39,17 @@ import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.Pe
3639
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.Source
3740
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.VisitInfo
3841
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.events.outbound.VisitorInfo
42+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsEvents
43+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.MetricsService
44+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.VisitMetricInfo
45+
import uk.gov.justice.digital.hmpps.officialvisitsapi.service.metrics.VisitorMetricInfo
3946
import java.time.DayOfWeek
4047
import java.time.LocalTime
4148
import java.util.UUID
4249

4350
class CreateOfficialVisitIntegrationTest : IntegrationTestBase() {
51+
@MockitoBean
52+
private lateinit var metricsService: MetricsService
4453

4554
private val officialVisitor = OfficialVisitor(
4655
visitorTypeCode = VisitorType.CONTACT,
@@ -110,6 +119,38 @@ class CreateOfficialVisitIntegrationTest : IntegrationTestBase() {
110119
auditedEventRepository.findAll() hasSize 0
111120

112121
val officialVisitResponse = webTestClient.create(nextMondayAt9)
122+
verify(metricsService).send(
123+
eventType = eq(
124+
MetricsEvents.CREATE,
125+
),
126+
info = eq(
127+
VisitMetricInfo(
128+
officialVisitId = officialVisitResponse.officialVisitId,
129+
source = Source.DPS,
130+
username = MOORLAND_PRISON_USER.username,
131+
prisonCode = MOORLAND,
132+
prisonerNumber = MOORLAND_PRISONER.number,
133+
numberOfVisitors = nextMondayAt9.officialVisitors.size.toLong(),
134+
locationType = null,
135+
startTime = nextMondayAt9.startTime,
136+
),
137+
),
138+
)
139+
verify(metricsService).send(
140+
eventType = eq(
141+
MetricsEvents.ADD_VISITOR,
142+
),
143+
info = eq(
144+
VisitorMetricInfo(
145+
officialVisitId = officialVisitResponse.officialVisitId,
146+
source = Source.DPS,
147+
username = MOORLAND_PRISON_USER.username,
148+
prisonCode = MOORLAND,
149+
contactId = officialVisitResponse.visitorAndContactIds.first().second!!,
150+
officialVisitorId = officialVisitResponse.visitorAndContactIds.first().first,
151+
),
152+
),
153+
)
113154

114155
val persistedOfficialVisit = officialVisitRepository.findById(officialVisitResponse.officialVisitId).get()
115156

0 commit comments

Comments
 (0)