Skip to content

Commit

Permalink
MAN-319 PI-2749: MPOP Activity Search API
Browse files Browse the repository at this point in the history
  • Loading branch information
pmcphee77 committed Jan 22, 2025
1 parent f9d6fe6 commit eef79fd
Show file tree
Hide file tree
Showing 14 changed files with 242 additions and 9 deletions.
5 changes: 3 additions & 2 deletions projects/manage-supervision-and-delius/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ apply(plugin = "com.google.cloud.tools.jib")
dependencies {
implementation(project(":libs:audit"))
implementation(project(":libs:commons"))
implementation(project(":libs:oauth-server"))
implementation(project(":libs:limited-access"))
implementation(project(":libs:document-management"))
implementation(project(":libs:limited-access"))
implementation(project(":libs:oauth-client"))
implementation(project(":libs:oauth-server"))
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-security")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ generic-service:
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: https://sign-in-dev.hmpps.service.justice.gov.uk/auth/.well-known/jwks.json
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: https://sign-in-dev.hmpps.service.justice.gov.uk/auth/issuer
INTEGRATIONS_ALFRESCO_URL: https://hmpps-delius-alfresco-test.apps.live.cloud-platform.service.justice.gov.uk/alfresco/service/noms-spg
INTEGRATIONS_PROBATION-SEARCH_URL: https://probation-offender-search-dev.hmpps.service.justice.gov.uk

generic-prometheus-alerts:
businessHoursOnly: true
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ generic-service:
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: https://sign-in-preprod.hmpps.service.justice.gov.uk/auth/.well-known/jwks.json
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: https://sign-in-preprod.hmpps.service.justice.gov.uk/auth/issuer
INTEGRATIONS_ALFRESCO_URL: https://alfresco.pre-prod.delius.probation.hmpps.dsd.io/alfresco/service/noms-spg
INTEGRATIONS_PROBATION-SEARCH_URL: https://probation-offender-search-preprod.hmpps.service.justice.gov.uk

generic-prometheus-alerts:
businessHoursOnly: true
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ generic-service:
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: https://sign-in.hmpps.service.justice.gov.uk/auth/.well-known/jwks.json
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: https://sign-in.hmpps.service.justice.gov.uk/auth/issuer
INTEGRATIONS_ALFRESCO_URL: https://alfresco.probation.service.justice.gov.uk/alfresco/service/noms-spg
INTEGRATIONS_PROBATION-SEARCH_URL: https://probation-offender-search.hmpps.service.justice.gov.uk
3 changes: 3 additions & 0 deletions projects/manage-supervision-and-delius/deploy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ generic-service:
SPRING_DATASOURCE_PASSWORD: DB_PASSWORD
manage-supervision-and-delius-sentry:
SENTRY_DSN: SENTRY_DSN
manage-supervision-and-delius-client-credentials:
OAUTH2_CLIENT-ID: CLIENT_ID
OAUTH2_CLIENT-SECRET: CLIENT_SECRET

generic-prometheus-alerts:
targetApplication: manage-supervision-and-delius
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"request": {
"method": "POST",
"urlPath": "/auth/oauth/token"
},
"response": {
"headers": {
"Content-Type": "application/json"
},
"status": 200,
"jsonBody": {
"access_token": "token",
"token_type": "bearer",
"expires_in": 9999999999,
"scope": "read write",
"sub": "probation-integration-dev",
"auth_source": "none",
"iss": "https://sign-in-dev.hmpps.service.justice.gov.uk/auth/issuer"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package uk.gov.justice.digital.hmpps

import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.client.WireMock
import com.github.tomakehurst.wiremock.client.WireMock.aResponse
import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
Expand All @@ -9,12 +13,18 @@ import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import uk.gov.justice.digital.hmpps.api.model.activity.PersonActivity
import uk.gov.justice.digital.hmpps.client.ActivitySearchRequest
import uk.gov.justice.digital.hmpps.client.ContactSearchResponse
import uk.gov.justice.digital.hmpps.client.ContactSearchResult
import uk.gov.justice.digital.hmpps.data.generator.ContactGenerator
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator.OVERVIEW
import uk.gov.justice.digital.hmpps.service.toActivity
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.contentAsJson
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.objectMapper
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withJson
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withToken

@AutoConfigureMockMvc
Expand All @@ -23,6 +33,61 @@ internal class ActivityIntegrationTest {
@Autowired
lateinit var mockMvc: MockMvc

@Autowired
lateinit var wireMockServer: WireMockServer

@Test
fun `activity search calls probation offender activity search`() {

val person = OVERVIEW
val searchResponse = ContactSearchResponse(
page = 0, totalResults = 10, totalPages = 11, size = 3,
results = listOf(
ContactSearchResult(
crn = person.crn,
id = ContactGenerator.FIRST_APPT_CONTACT.id
),
ContactSearchResult(
crn = person.crn,
id = ContactGenerator.FIRST_NON_APPT_CONTACT.id
),
ContactSearchResult(
crn = person.crn,
id = ContactGenerator.NEXT_APPT_CONTACT.id
)
)
)
wireMockServer.stubFor(
WireMock.post(urlPathEqualTo("/probation-search/search/activity"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody(objectMapper.writeValueAsString(searchResponse))
)
)

val res = mockMvc
.perform(
post("/activity/${person.crn}").withToken()
.withJson(
ActivitySearchRequest(crn = person.crn)
)
)
.andExpect(status().isOk)
.andReturn().response.contentAsJson<PersonActivity>()

assertThat(res.personSummary.crn, equalTo(person.crn))
assertThat(res.activities.size, equalTo(3))
assertThat(res.activities[0].id, equalTo(ContactGenerator.FIRST_APPT_CONTACT.id))
assertThat(
res.activities[0].location?.officeName,
equalTo(ContactGenerator.FIRST_APPT_CONTACT.toActivity().location?.officeName)
)
assertThat(res.activities[1].id, equalTo(ContactGenerator.FIRST_NON_APPT_CONTACT.id))
assertThat(res.activities[2].id, equalTo(ContactGenerator.NEXT_APPT_CONTACT.id))
}

@Test
fun `all person activity is returned`() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package uk.gov.justice.digital.hmpps.api.controller

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.data.domain.PageRequest
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import uk.gov.justice.digital.hmpps.api.model.activity.PersonActivitySearchRequest
import uk.gov.justice.digital.hmpps.service.ActivityService

@RestController
Expand All @@ -18,4 +17,13 @@ class ActivityController(private val activityService: ActivityService) {
@GetMapping
@Operation(summary = "Gets all activity for a person’ ")
fun getPersonActivity(@PathVariable crn: String) = activityService.getPersonActivity(crn)
}

@PostMapping
@Operation(summary = "Activity Log Search’ ")
fun activitySearch(
@PathVariable crn: String,
@RequestBody searchRequest: PersonActivitySearchRequest,
@RequestParam(required = false, defaultValue = "0") page: Int,
@RequestParam(required = false, defaultValue = "10") size: Int
) = activityService.activitySearch(crn, searchRequest, PageRequest.of(page, size))
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
package uk.gov.justice.digital.hmpps.api.model.activity

import com.fasterxml.jackson.annotation.JsonFormat
import uk.gov.justice.digital.hmpps.api.model.PersonSummary
import java.time.LocalDate

data class PersonActivity(
val personSummary: PersonSummary,
val activities: List<Activity>
)
)

data class PersonActivitySearchResponse(
val size: Int,
val page: Int,
val totalResults: Long,
val totalPages: Int,
val personSummary: PersonSummary,
val activities: List<Activity>
)

data class PersonActivitySearchRequest(
val keywords: String? = "",
@JsonFormat(pattern = "yyyy-MM-dd")
val dateFrom: LocalDate? = null,
@JsonFormat(pattern = "yyyy-MM-dd")
val dateTo: LocalDate? = null,
val filters: List<String> = emptyList(),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package uk.gov.justice.digital.hmpps.client

import com.fasterxml.jackson.annotation.JsonFormat
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.service.annotation.PostExchange
import java.time.LocalDate

interface ProbationSearchClient {
@PostExchange(url = "/search/activity")
fun contactSearch(
@RequestBody body: ActivitySearchRequest,
@RequestParam page: Int = 0,
@RequestParam size: Int = 10): ContactSearchResponse
}

data class ActivitySearchRequest(
val crn: String,
val keywords: String? = "",
@JsonFormat(pattern = "yyyy-MM-dd")
val dateFrom: LocalDate? = null,
@JsonFormat(pattern = "yyyy-MM-dd")
val dateTo: LocalDate? = null,
val filters: List<String> = emptyList(),
)


data class ContactSearchResponse(
val size: Int,
val page: Int,
val totalResults: Long,
val totalPages: Int,
val results: List<ContactSearchResult>,
)

data class ContactSearchResult(
val crn: String,
val id: Long,
val highlights: Map<String, List<String>> = mapOf()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package uk.gov.justice.digital.hmpps.config

import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.client.RestClient
import uk.gov.justice.digital.hmpps.client.ProbationSearchClient
import uk.gov.justice.digital.hmpps.config.security.createClient

@Configuration
class RestClientConfig(private val oauth2Client: RestClient) {

@Bean
fun probationSearchClient(@Value("\${integrations.probation-search.url}") apiBaseUrl: String): ProbationSearchClient =
createClient(oauth2Client.mutate().baseUrl(apiBaseUrl).build())
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ interface ContactRepository : JpaRepository<Contact, Long> {
)
fun findByPersonId(personId: Long): List<Contact>

fun findByPersonIdAndIdIn(personId: Long, ids: List<Long>): List<Contact>

fun findByPersonIdAndEventIdIn(personId: Long, eventId: List<Long>): List<Contact>

fun findByPersonIdAndId(personId: Long, id: Long): Contact?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,56 @@
package uk.gov.justice.digital.hmpps.service

import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import uk.gov.justice.digital.hmpps.api.model.activity.Activity
import uk.gov.justice.digital.hmpps.api.model.activity.PersonActivity
import uk.gov.justice.digital.hmpps.api.model.activity.PersonActivitySearchRequest
import uk.gov.justice.digital.hmpps.api.model.activity.PersonActivitySearchResponse
import uk.gov.justice.digital.hmpps.client.ActivitySearchRequest
import uk.gov.justice.digital.hmpps.client.ProbationSearchClient
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.ContactRepository
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.PersonRepository
import uk.gov.justice.digital.hmpps.integrations.delius.overview.entity.getSummary

@Service
class ActivityService(
private val personRepository: PersonRepository,
private val contactRepository: ContactRepository
private val contactRepository: ContactRepository,
private val probationSearchClient: ProbationSearchClient
) {

@Transactional
fun activitySearch(
crn: String,
searchRequest: PersonActivitySearchRequest,
pageable: Pageable
): PersonActivitySearchResponse {
val summary = personRepository.getSummary(crn)
val probationSearchRequest = ActivitySearchRequest(
crn = crn,
keywords = searchRequest.keywords,
dateFrom = searchRequest.dateFrom,
dateTo = searchRequest.dateTo,
filters = searchRequest.filters
)
val response =
probationSearchClient.contactSearch(probationSearchRequest, pageable.pageNumber, pageable.pageSize)
val ids = response.results.map { it.id }
val contacts = ids.map { contactId ->
contactRepository.findByPersonIdAndIdIn(summary.id, ids).associateBy { it.id }[contactId]!!
}

return PersonActivitySearchResponse(
size = response.size,
page = response.page,
totalResults = response.totalResults,
totalPages = response.totalPages,
personSummary = summary.toPersonSummary(),
activities = contacts.map { it.toActivity() }
)
}

@Transactional
fun getPersonActivity(crn: String): PersonActivity {
val summary = personRepository.getSummary(crn)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ spring:
query.mutation_strategy.global_temporary:
create_tables: false
drop_tables: false
security.oauth2.client:
registration:
default:
provider: hmpps-auth
authorization-grant-type: client_credentials
client-id: ${oauth2.client-id}
client-secret: ${oauth2.client-secret}
provider:
hmpps-auth:
token-uri: http://localhost:${wiremock.port}/auth/oauth/token
threads.virtual.enabled: true
ldap:
base: ou=Users,dc=moj,dc=com
Expand Down Expand Up @@ -54,6 +64,13 @@ seed.database: true

wiremock.enabled: true

integrations:
probation-search.url: http://localhost:${wiremock.port}/probation-search

oauth2:
client-id: manage-supervision-and-delius
client-secret: manage-supervision-and-delius

logging.level:
uk.gov.justice.digital.hmpps: DEBUG
org.hibernate.tool.schema: ERROR
Expand Down

0 comments on commit eef79fd

Please sign in to comment.