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

Debugging and unit tests #59

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 0 additions & 10 deletions .run/Dev.run.xml

This file was deleted.

22 changes: 22 additions & 0 deletions .run/local.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="local" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot">
<option name="ACTIVE_PROFILES" value="local" />
<option name="ALTERNATIVE_JRE_PATH" value="openjdk-23" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<module name="hmpps-electronic-monitoring-datastore-api.main" />
<option name="SPRING_BOOT_MAIN_CLASS" value="uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.HmppsElectronicMonitoringDatastoreApi" />
<extension name="net.ashald.envfile">
<option name="IS_ENABLED" value="false" />
<option name="IS_SUBST" value="false" />
<option name="IS_PATH_MACRO_SUPPORTED" value="false" />
<option name="IS_IGNORE_MISSING_FILES" value="false" />
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
<ENTRIES>
<ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false" />
</ENTRIES>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.json.JSONObject
import org.springframework.stereotype.Service
import software.amazon.awssdk.services.athena.model.ColumnInfo
import software.amazon.awssdk.services.athena.model.ColumnNullable
import software.amazon.awssdk.services.athena.model.Datum
Expand All @@ -13,6 +14,7 @@ import software.amazon.awssdk.services.athena.model.Row
import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties

@Service
class AthenaHelper {
companion object {
fun resultSetFromJson(string: String): ResultSet = resultSetFromJson(JSONObject(string))
Expand Down Expand Up @@ -83,7 +85,7 @@ class AthenaHelper {
return allPresent
}

inline fun <reified T> mapTo(resultSet: ResultSet): List<T> {
inline fun <reified T> mapToStatic(resultSet: ResultSet): List<T> {
val mapper = jacksonObjectMapper()
.registerKotlinModule()
.apply {
Expand All @@ -102,5 +104,7 @@ class AthenaHelper {
mapper.convertValue(row, T::class.java)
}
}

inline fun <reified T> mapTo(resultSet: ResultSet): List<T> = Companion.mapToStatic<T>(resultSet)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.service.Ath
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.service.AthenaService

@Service
class OrderInformationRepository {
class OrderInformationRepository(
private val athenaService: AthenaService,
private val athenaHelper: AthenaHelper = AthenaHelper(),
) {

companion object {
private val fakeOrders = listOf(
OrderInformation(
Expand Down Expand Up @@ -46,6 +50,7 @@ class OrderInformationRepository {
),

)

private fun generateFakeDocuments(): List<Document> = listOf(
Document(name = "Document 1", url = "#", createdOn = "01-02-2020", time = "0100", notes = "Order 1 documents xxxxxx xxxxxxx NAME HDC New.msg application/octet-stream NAME - TOLD TO IGNORE AS SUBJECT STILL IN CUSTODY ABCD 12 345678 xx-xxxxxx"),
Document(name = "Document 2", url = "#", createdOn = "21-09-2017", time = "0200", notes = "Order 2 documents xxxxxx xxxxxxx NAME HDC New.msg application/octet-stream NAME - TOLD TO IGNORE AS SUBJECT STILL IN CUSTODY ABCD 12 345678 xx-xxxxxx"),
Expand Down Expand Up @@ -81,16 +86,13 @@ class OrderInformationRepository {
WHERE legacy_subject_id = $orderId
""".trimIndent(),
)

fun parseKeyOrderResponse(resultSet: ResultSet): KeyOrderInformation {
var dtoOrders: List<AthenaKeyOrderDTO> = AthenaHelper.mapTo<AthenaKeyOrderDTO>(resultSet)

var orders: List<KeyOrderInformation> = dtoOrders.map { dto -> KeyOrderInformation(dto) }
return orders[0]
}
}

private val athenaService = AthenaService()
fun parseKeyOrderResponse(resultSet: ResultSet): KeyOrderInformation {
var dtoOrders: List<AthenaKeyOrderDTO> = AthenaHelper.mapToStatic<AthenaKeyOrderDTO>(resultSet)
var orders: List<KeyOrderInformation> = dtoOrders.map { dto -> KeyOrderInformation(dto) }
return orders[0]
}

fun getKeyOrderInformation(orderId: String): AthenaQueryResponse<KeyOrderInformation> {
val athenaQuery: AthenaQuery = getKeyOrderQuery(orderId)
Expand All @@ -111,9 +113,9 @@ class OrderInformationRepository {
// TODO: remove this "mock" test once plumbing is complete
fun getMockOrderInformation(orderId: String): OrderInformation {
// Always return the first order in the list but modify the legacyOrderId
return fakeOrders.first().apply {
return fakeOrders.first().copy().apply {
keyOrderInformation = keyOrderInformation.copy(
legacyOrderId = "${keyOrderInformation.legacyOrderId}-$orderId",
legacyOrderId = "$orderId",
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class OrderRepository {
}

fun parseOrders(resultSet: ResultSet): List<OrderSearchResult> {
var dtoOrders: List<AthenaOrderSearchResultDTO> = AthenaHelper.mapTo<AthenaOrderSearchResultDTO>(resultSet)
var dtoOrders: List<AthenaOrderSearchResultDTO> = AthenaHelper.mapToStatic<AthenaOrderSearchResultDTO>(resultSet)

var orderSearchResults: List<OrderSearchResult> = dtoOrders.map { dto -> OrderSearchResult(dto) }
// return getFakeOrders() // TODO: Early return, testing/demo early.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.resource

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
Expand All @@ -9,26 +8,28 @@ 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 uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.helpers.AthenaHelper
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.KeyOrderInformation
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.OrderInformation
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.athena.AthenaQueryResponse
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.repository.OrderInformationRepository
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.service.AthenaService
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.service.internal.AuditService

@RestController
@PreAuthorize("hasAnyAuthority('ROLE_EM_DATASTORE_GENERAL_RO', 'ROLE_EM_DATASTORE_RESTRICTED_RO')")
@RequestMapping(value = ["/orders"], produces = [MediaType.APPLICATION_JSON_VALUE])
class OrderController(
@Autowired val repository: OrderInformationRepository,
@Autowired val auditService: AuditService,
val repository: OrderInformationRepository = OrderInformationRepository(AthenaService(), AthenaHelper()),
val auditService: AuditService,
) {

@GetMapping("/getMockOrderSummary/{orderId}")
fun getMockOrderSummary(
authentication: Authentication,
@PathVariable orderId: String,
): ResponseEntity<OrderInformation> {
val repository = OrderInformationRepository()
// val repository = OrderInformationRepository()
val orderInfo: OrderInformation = repository.getMockOrderInformation(orderId)

// TODO: Re-enable audit once Cloud Platform in place
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ class SearchController(
@Autowired val auditService: AuditService,
) {

@GetMapping("/confirmConnection")
fun confirmConnection(
authentication: Authentication,
): ResponseEntity<Map<String, String>> {
// TODO: Re-enable audit once Cloud Platform in place
// auditService.createEvent(
// authentication.principal.toString(),
// "CONFIRM_CONNECTION",
// mapOf("confirmConnection" to "true"),
// )

return ResponseEntity(
mapOf("message" to "Connection successful"),
HttpStatus.OK,
)
}

// @GetMapping("/cases/{caseID}")
// fun getCases(
// @PathVariable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.service

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.springframework.stereotype.Service
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.athena.AthenaClient
import software.amazon.awssdk.services.athena.model.AthenaException
Expand All @@ -17,6 +18,7 @@ import software.amazon.awssdk.services.athena.model.StartQueryExecutionRequest
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.config.AthenaClientException

// We will instantiate as new for now
@Service
class AthenaService {
private val stsService = AssumeRoleService()
private val outputBucket: String = "s3://emds-dev-athena-query-results-20240917144028307600000004"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ class AthenaHelperTest {
),
)

val result = AthenaHelper.mapTo<AthenaOrderSearchResultDTO>(resultSet)
val result = AthenaHelper.mapToStatic<AthenaOrderSearchResultDTO>(resultSet)

Assertions.assertThat(result).isEqualTo(expected)
}
Expand All @@ -445,7 +445,7 @@ class AthenaHelperTest {
),
)

val result = AthenaHelper.mapTo<MiniOrder>(resultSet)
val result = AthenaHelper.mapToStatic<MiniOrder>(resultSet)

Assertions.assertThat(result).isEqualTo(expected)
}
Expand All @@ -461,7 +461,7 @@ class AthenaHelperTest {
)

Assertions.assertThatExceptionOfType(IllegalArgumentException::class.java)
.isThrownBy { AthenaHelper.mapTo<FakeObject>(resultSet) }
.isThrownBy { AthenaHelper.mapToStatic<FakeObject>(resultSet) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.repository

import org.assertj.core.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
import org.springframework.test.context.ActiveProfiles
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.helpers.AthenaHelper
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.Document
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.DocumentList
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.KeyOrderInformation
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.OrderInformation
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.model.SubjectHistoryReport
import uk.gov.justice.digital.hmpps.electronicmonitoringdatastoreapi.service.AthenaService

@ActiveProfiles("test")
class OrderInformationRepositoryTest {
private lateinit var repository: OrderInformationRepository
private lateinit var athenaService: AthenaService
private lateinit var athenaHelper: AthenaHelper

@BeforeEach
fun setup() {
athenaService = mock()
athenaHelper = mock()
repository = OrderInformationRepository(athenaService, athenaHelper)
}

@Nested
inner class GetKeyOrderQuery

@Nested
inner class ParseKeyOrdeResponse

@Nested
inner class GetKeyOrderInformation {

// @Test
// fun `calls athena and returns data`() {
// val orderId = "test-id"
//
// val expected = AthenaQueryResponse<KeyOrderInformation>(
// queryString = "test-id",
// athenaRole = "DEV",
// queryResponse = KeyOrderInformation(
// specials = "fake",
// legacySubjectId = "fake",
// legacyOrderId = "fake",
// name = "fake",
// alias = "fake-and-nullable",
// dateOfBirth = "fake",
// address1 = "fake",
// address2 = "fake",
// address3 = "fake",
// postcode = "fake",
// orderStartDate = "fake",
// orderEndDate = "fake",
// ),
// )
//
// val resultSet: ResultSet = AthenaHelper.Companion.resultSetFromJson(
// AthenaHelperTest().defaultResultSet,
// )
//
// `when`(athenaService.getQueryResult(any<AthenaRole>(), any<String>()))
// .thenReturn(resultSet)
//
// // TODO: fix errors caused by AthenaHelper parsing
// // My AthenaHelper isn't parsing the fake ResultSet as the object I want.
// // This is designed to mock that return, but it's not quite working yet.
// `when`(athenaHelper.mapTo<AthenaKeyOrderDTO>(resultSet))
// .thenReturn(listOf(AthenaKeyOrderDTO(legacySubjectId = "fake",
// legacyOrderId = "fake",
// name = "fake",
// alias = "fake-and-nullable",
// dateOfBirth = "fake",
// address1 = "fake",
// address2 = "fake",
// address3 = "fake",
// postcode = "fake",
// orderStartDate = "fake",
// orderEndDate = "fake",
// )))
//
// val result = repository.getKeyOrderInformation(orderId)
//
//
// // Assertions.assertThat(result.queryString).isEqualTo(expected.queryString)
// // Assertions.assertThat(result.athenaRole).isEqualTo(expected.athenaRole)
// // Assertions.assertThat(result.queryResponse).isEqualTo(expected.queryResponse)
// // Assertions.assertThat(result).isEqualTo(expected)
// Assertions.assertThat(true).isEqualTo(true)
// }
}

@Nested
inner class GetMockOrderInformation {
@Test
fun `Returns mock order information`() {
val orderId = "I am the test legacy subject Id"

val expected: OrderInformation = OrderInformation(
keyOrderInformation = KeyOrderInformation(
specials = "No",
legacySubjectId = "1234567",
legacyOrderId = orderId,
name = "John Smith",
alias = "Zeno",
dateOfBirth = "01-02-1980",
address1 = "1 Primary Street",
address2 = "Sutton",
address3 = "London",
postcode = "ABC 123",
orderStartDate = "01-02-2012",
orderEndDate = "03-04-2013",
),
subjectHistoryReport = SubjectHistoryReport(
reportUrl = "#",
name = "1234567",
createdOn = "01-02-2020",
time = "0900",
),
documents = DocumentList(
pageSize = 14,
orderDocuments = listOf(
Document(name = "Document 1", url = "#", createdOn = "01-02-2020", time = "0100", notes = "Order 1 documents xxxxxx xxxxxxx NAME HDC New.msg application/octet-stream NAME - TOLD TO IGNORE AS SUBJECT STILL IN CUSTODY ABCD 12 345678 xx-xxxxxx"),
Document(name = "Document 2", url = "#", createdOn = "21-09-2017", time = "0200", notes = "Order 2 documents xxxxxx xxxxxxx NAME HDC New.msg application/octet-stream NAME - TOLD TO IGNORE AS SUBJECT STILL IN CUSTODY ABCD 12 345678 xx-xxxxxx"),
// There are more documents in here but there is no real utility in testing they are returned
),
),
)

val result: OrderInformation = repository.getMockOrderInformation(orderId)

Assertions.assertThat(result.keyOrderInformation).isEqualTo(expected.keyOrderInformation)
Assertions.assertThat(result.subjectHistoryReport).isEqualTo(expected.subjectHistoryReport)
Assertions.assertThat(result.documents.orderDocuments[1]).isEqualTo(expected.documents.orderDocuments[1])
}

// Test added to confirm solution of a bug in OrderInformationRepostory.getMockOrderInformation
// Bug resulted in appending legacySubjectId in the mock data rather than replacing it, and using the same object reference for all occurrences
@Test
fun `Returns correct order summary when called multiple times`() {
val orderId1 = "I am test data"
val result1 = repository.getMockOrderInformation(orderId1)

Assertions.assertThat(result1.keyOrderInformation.legacyOrderId).isEqualTo(orderId1)

val orderId2 = "I am different test data"
val result2 = repository.getMockOrderInformation(orderId2)

Assertions.assertThat(result1.keyOrderInformation.legacyOrderId).isEqualTo(orderId1)
Assertions.assertThat(result2.keyOrderInformation.legacyOrderId).isEqualTo(orderId2)
}
}
}
Loading