summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2025-11-21 12:29:05 +0100
committerGitHub2025-11-21 12:29:05 +0100
commit2f8ccf33d108537ea7cfe398085a25a7bc926406 (patch)
tree731fe307bccd37d8f20c46485ece8902f0c60e21
parent3e5949197c0a0a738321234746ab8e742389444c (diff)
feat: add alternative endpoints for request (#196)
This allows for requests to (with optional path-prefix "/api"): * POST /{usecase} * POST /{usecase}/etl/patient-record => as DNPM:DIP * DELETE /{usecase}/{ID} * DELETE /{usecase}/etl/patient-record/{ID} * DELETE /{usecase}/etl/patient/{ID} => as DNPM:DIP Where {usecase} is one of: * mtbfile * mtb => as DNPM:DIP
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt165
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt4
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt12
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt216
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt69
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt68
6 files changed, 380 insertions, 154 deletions
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
index e966898..35551a9 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -33,8 +33,9 @@ import java.time.Instant
import java.util.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
-import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import org.springframework.beans.factory.annotation.Autowired
@@ -92,10 +93,23 @@ class MtbFileRestControllerTest {
.check(any())
}
- @Test
- fun testShouldGrantPermissionToSendMtbFile() {
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile",
+ "/mtbfile/etl/patient-record",
+ "/mtb",
+ "/mtb/etl/patient-record",
+ "/api/mtbfile",
+ "/api/mtbfile/etl/patient-record",
+ "/api/mtb",
+ "/api/mtb/etl/patient-record",
+ ]
+ )
+ fun testShouldGrantPermissionToSendMtbFile(url: String) {
mockMvc
- .post("/mtbfile") {
+ .post(url) {
with(user("onkostarserver").roles("MTBFILE"))
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
@@ -105,10 +119,23 @@ class MtbFileRestControllerTest {
verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
- @Test
- fun testShouldGrantPermissionToSendMtbFileToAdminUser() {
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile",
+ "/mtbfile/etl/patient-record",
+ "/mtb",
+ "/mtb/etl/patient-record",
+ "/api/mtbfile",
+ "/api/mtbfile/etl/patient-record",
+ "/api/mtb",
+ "/api/mtb/etl/patient-record",
+ ]
+ )
+ fun testShouldGrantPermissionToSendMtbFileToAdminUser(url: String) {
mockMvc
- .post("/mtbfile") {
+ .post(url) {
with(user("onkostarserver").roles("ADMIN"))
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
@@ -118,10 +145,23 @@ class MtbFileRestControllerTest {
verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
- @Test
- fun testShouldDenyPermissionToSendMtbFile() {
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile",
+ "/mtbfile/etl/patient-record",
+ "/mtb",
+ "/mtb/etl/patient-record",
+ "/api/mtbfile",
+ "/api/mtbfile/etl/patient-record",
+ "/api/mtb",
+ "/api/mtb/etl/patient-record",
+ ]
+ )
+ fun testShouldDenyPermissionToSendMtbFile(url: String) {
mockMvc
- .post("/mtbfile") {
+ .post(url) {
with(anonymous())
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
@@ -131,10 +171,23 @@ class MtbFileRestControllerTest {
verify(requestProcessor, never()).processMtbFile(any<Mtb>())
}
- @Test
- fun testShouldDenyPermissionToSendMtbFileForUser() {
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile",
+ "/mtbfile/etl/patient-record",
+ "/mtb",
+ "/mtb/etl/patient-record",
+ "/api/mtbfile",
+ "/api/mtbfile/etl/patient-record",
+ "/api/mtb",
+ "/api/mtb/etl/patient-record",
+ ]
+ )
+ fun testShouldDenyPermissionToSendMtbFileForUser(url: String) {
mockMvc
- .post("/mtbfile") {
+ .post(url) {
with(user("fakeuser").roles("USER"))
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
@@ -144,21 +197,53 @@ class MtbFileRestControllerTest {
verify(requestProcessor, never()).processMtbFile(any<Mtb>())
}
- @Test
- fun testShouldGrantPermissionToDeletePatientData() {
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile/TEST_12345678",
+ "/mtbfile/etl/patient-record/TEST_12345678",
+ "/mtbfile/etl/patient/TEST_12345678",
+ "/mtb/TEST_12345678",
+ "/mtb/etl/patient-record/TEST_12345678",
+ "/mtb/etl/patient/TEST_12345678",
+ "/api/mtbfile/TEST_12345678",
+ "/api/mtbfile/etl/patient-record/TEST_12345678",
+ "/api/mtbfile/etl/patient/TEST_12345678",
+ "/api/mtb/TEST_12345678",
+ "/api/mtb/etl/patient-record/TEST_12345678",
+ "/api/mtb/etl/patient/TEST_12345678",
+ ]
+ )
+ fun testShouldGrantPermissionToDeletePatientData(url: String) {
mockMvc
- .delete("/mtbfile/12345678") { with(user("onkostarserver").roles("MTBFILE")) }
+ .delete(url) { with(user("onkostarserver").roles("MTBFILE")) }
.andExpect { status { isAccepted() } }
verify(requestProcessor, times(1))
.processDeletion(anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE))
}
- @Test
- fun testShouldDenyPermissionToDeletePatientData() {
- mockMvc
- .delete("/mtbfile/12345678") { with(anonymous()) }
- .andExpect { status { isUnauthorized() } }
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile/TEST_12345678",
+ "/mtbfile/etl/patient-record/TEST_12345678",
+ "/mtbfile/etl/patient/TEST_12345678",
+ "/mtb/TEST_12345678",
+ "/mtb/etl/patient-record/TEST_12345678",
+ "/mtb/etl/patient/TEST_12345678",
+ "/api/mtbfile/TEST_12345678",
+ "/api/mtbfile/etl/patient-record/TEST_12345678",
+ "/api/mtbfile/etl/patient/TEST_12345678",
+ "/api/mtb/TEST_12345678",
+ "/api/mtb/etl/patient-record/TEST_12345678",
+ "/api/mtb/etl/patient/TEST_12345678",
+ ]
+ )
+ fun testShouldDenyPermissionToDeletePatientData(url: String) {
+ mockMvc.delete(url) { with(anonymous()) }.andExpect { status { isUnauthorized() } }
verify(requestProcessor, never()).processDeletion(anyValueClass(), any())
}
@@ -176,10 +261,23 @@ class MtbFileRestControllerTest {
]
)
inner class WithOidcEnabled {
- @Test
- fun testShouldGrantPermissionToSendMtbFileToAdminUser() {
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile",
+ "/mtbfile/etl/patient-record",
+ "/mtb",
+ "/mtb/etl/patient-record",
+ "/api/mtbfile",
+ "/api/mtbfile/etl/patient-record",
+ "/api/mtb",
+ "/api/mtb/etl/patient-record",
+ ]
+ )
+ fun testShouldGrantPermissionToSendMtbFileToAdminUser(url: String) {
mockMvc
- .post("/mtbfile") {
+ .post(url) {
with(user("onkostarserver").roles("ADMIN"))
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
@@ -189,10 +287,23 @@ class MtbFileRestControllerTest {
verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
- @Test
- fun testShouldGrantPermissionToSendMtbFileToUser() {
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile",
+ "/mtbfile/etl/patient-record",
+ "/mtb",
+ "/mtb/etl/patient-record",
+ "/api/mtbfile",
+ "/api/mtbfile/etl/patient-record",
+ "/api/mtb",
+ "/api/mtb/etl/patient-record",
+ ]
+ )
+ fun testShouldGrantPermissionToSendMtbFileToUser(url: String) {
mockMvc
- .post("/mtbfile") {
+ .post(url) {
with(user("onkostarserver").roles("USER"))
contentType = MediaType.APPLICATION_JSON
content = ObjectMapper().writeValueAsString(mtbFile)
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
index d44303b..6b7cab8 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
@@ -89,6 +89,8 @@ class AppSecurityConfiguration(private val securityConfigProperties: SecurityCon
http {
authorizeHttpRequests {
authorize("/configs/**", hasRole("ADMIN"))
+ authorize("/api/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
+ authorize("/api/mtb/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
authorize("/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
authorize("/mtb/**", hasAnyRole("MTBFILE", "ADMIN", "USER"))
authorize("/report/**", hasAnyRole("ADMIN", "USER"))
@@ -154,6 +156,8 @@ class AppSecurityConfiguration(private val securityConfigProperties: SecurityCon
http {
authorizeHttpRequests {
authorize("/configs/**", hasRole("ADMIN"))
+ authorize("/api/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN"))
+ authorize("/api/mtb/**", hasAnyRole("MTBFILE", "ADMIN"))
authorize("/mtbfile/**", hasAnyRole("MTBFILE", "ADMIN"))
authorize("/mtb/**", hasAnyRole("MTBFILE", "ADMIN"))
authorize("/report/**", hasRole("ADMIN"))
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
index 5a43242..071e3cd 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
@@ -31,7 +31,7 @@ import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
@RestController
-@RequestMapping(path = ["mtbfile", "mtb"])
+@RequestMapping(path = ["mtbfile", "mtb", "api/mtbfile", "api/mtb"])
class MtbFileRestController(
private val requestProcessor: RequestProcessor,
private val consentEvaluator: ConsentEvaluator,
@@ -44,8 +44,12 @@ class MtbFileRestController(
}
@PostMapping(
+ path = ["", "etl/patient-record"],
consumes =
- [MediaType.APPLICATION_JSON_VALUE, CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE]
+ [
+ MediaType.APPLICATION_JSON_VALUE,
+ CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE,
+ ],
)
fun mtbFile(@RequestBody mtbFile: Mtb): ResponseEntity<Unit> {
val consentEvaluation = consentEvaluator.check(mtbFile)
@@ -60,7 +64,9 @@ class MtbFileRestController(
return ResponseEntity.accepted().build()
}
- @DeleteMapping(path = ["{patientId}"])
+ @DeleteMapping(
+ path = ["{patientId}", "etl/patient-record/{patientId}", "etl/patient/{patientId}"]
+ )
fun deleteData(@PathVariable patientId: String): ResponseEntity<Unit> {
logger.debug("Accepted patient ID to process deletion")
requestProcessor.processDeletion(PatientId(patientId), TtpConsentStatus.UNKNOWN_CHECK_FILE)
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
index 387119f..23032e8 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
@@ -37,13 +37,21 @@ infix fun Mtb.pseudonymizeWith(pseudonymizeService: PseudonymizeService) {
this.episodesOfCare?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
this.carePlans?.filterNotNull()?.forEach { carePlan ->
- carePlan.patient.id = patientPseudonym
- carePlan.rebiopsyRequests?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- carePlan.histologyReevaluationRequests?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- carePlan.medicationRecommendations?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- carePlan.studyEnrollmentRecommendations?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- carePlan.procedureRecommendations?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- carePlan.geneticCounselingRecommendation?.patient?.id = patientPseudonym
+ carePlan.patient.id = patientPseudonym
+ carePlan.rebiopsyRequests?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
+ carePlan.histologyReevaluationRequests?.filterNotNull()?.forEach {
+ it.patient?.id = patientPseudonym
+ }
+ carePlan.medicationRecommendations?.filterNotNull()?.forEach {
+ it.patient?.id = patientPseudonym
+ }
+ carePlan.studyEnrollmentRecommendations?.filterNotNull()?.forEach {
+ it.patient?.id = patientPseudonym
+ }
+ carePlan.procedureRecommendations?.filterNotNull()?.forEach {
+ it.patient?.id = patientPseudonym
+ }
+ carePlan.geneticCounselingRecommendation?.patient?.id = patientPseudonym
}
this.diagnoses?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
this.guidelineTherapies?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
@@ -58,26 +66,34 @@ infix fun Mtb.pseudonymizeWith(pseudonymizeService: PseudonymizeService) {
it.results.tumorCellContent?.patient?.id = patientPseudonym
}
this.ngsReports?.filterNotNull()?.forEach { ngsReport ->
- ngsReport.patient?.id = patientPseudonym
- ngsReport.results?.simpleVariants?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- ngsReport.results?.copyNumberVariants?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- ngsReport.results?.dnaFusions?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- ngsReport.results?.rnaFusions?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- ngsReport.results?.tumorCellContent?.patient?.id = patientPseudonym
- ngsReport.results?.brcaness?.patient?.id = patientPseudonym
- ngsReport.results?.tmb?.patient?.id = patientPseudonym
- ngsReport.results?.hrdScore?.patient?.id = patientPseudonym
+ ngsReport.patient?.id = patientPseudonym
+ ngsReport.results?.simpleVariants?.filterNotNull()?.forEach {
+ it.patient?.id = patientPseudonym
+ }
+ ngsReport.results?.copyNumberVariants?.filterNotNull()?.forEach {
+ it.patient?.id = patientPseudonym
+ }
+ ngsReport.results?.dnaFusions?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
+ ngsReport.results?.rnaFusions?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
+ ngsReport.results?.tumorCellContent?.patient?.id = patientPseudonym
+ ngsReport.results?.brcaness?.patient?.id = patientPseudonym
+ ngsReport.results?.tmb?.patient?.id = patientPseudonym
+ ngsReport.results?.hrdScore?.patient?.id = patientPseudonym
}
this.ihcReports?.filterNotNull()?.forEach { ihcReports ->
- ihcReports.patient?.id = patientPseudonym
- ihcReports.results?.msiMmr?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- ihcReports.results?.proteinExpression?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
+ ihcReports.patient?.id = patientPseudonym
+ ihcReports.results?.msiMmr?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
+ ihcReports.results?.proteinExpression?.filterNotNull()?.forEach {
+ it.patient?.id = patientPseudonym
+ }
}
this.responses?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
this.specimens?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
this.priorDiagnosticReports?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
this.performanceStatus?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
- this.systemicTherapies?.filterNotNull()?.forEach { systemicTherapy -> systemicTherapy.history?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym } }
+ this.systemicTherapies?.filterNotNull()?.forEach { systemicTherapy ->
+ systemicTherapy.history?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
+ }
this.followUps?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
this.msiFindings?.filterNotNull()?.forEach { it.patient?.id = patientPseudonym }
@@ -86,8 +102,8 @@ infix fun Mtb.pseudonymizeWith(pseudonymizeService: PseudonymizeService) {
if (researchConsent.contains("patient")) {
// here we expect only a patient reference any other data like display
// need to be removed, since may contain unsecure data
- researchConsent.remove("patient")
- researchConsent["patient"] = mapOf("reference" to "Patient/$patientPseudonym")
+ researchConsent.remove("patient")
+ researchConsent["patient"] = mapOf("reference" to "Patient/$patientPseudonym")
}
}
}
@@ -108,8 +124,8 @@ infix fun Mtb.anonymizeContentWith(pseudonymizeService: PseudonymizeService) {
}
this.episodesOfCare?.filterNotNull()?.forEach { episodeOfCare ->
- episodeOfCare.apply { id = id?.let(::anonymize) }
- episodeOfCare.diagnoses?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
+ episodeOfCare.apply { id = id?.let(::anonymize) }
+ episodeOfCare.diagnoses?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
}
this.carePlans?.onEach { carePlan ->
@@ -118,127 +134,147 @@ infix fun Mtb.anonymizeContentWith(pseudonymizeService: PseudonymizeService) {
this.geneticCounselingRecommendation?.apply { this.id = this.id?.let(::anonymize) }
this.rebiopsyRequests?.filterNotNull()?.forEach { rebiopsyRequest ->
- rebiopsyRequest.id = rebiopsyRequest.id?.let(::anonymize)
- rebiopsyRequest.tumorEntity?.id = rebiopsyRequest.tumorEntity?.id?.let(::anonymize)
+ rebiopsyRequest.id = rebiopsyRequest.id?.let(::anonymize)
+ rebiopsyRequest.tumorEntity?.id = rebiopsyRequest.tumorEntity?.id?.let(::anonymize)
}
this.histologyReevaluationRequests?.filterNotNull()?.forEach { histologyReevaluationRequest ->
- histologyReevaluationRequest.id = histologyReevaluationRequest.id?.let(::anonymize)
- histologyReevaluationRequest.specimen?.id = histologyReevaluationRequest.specimen?.id?.let(::anonymize)
+ histologyReevaluationRequest.id = histologyReevaluationRequest.id?.let(::anonymize)
+ histologyReevaluationRequest.specimen?.id =
+ histologyReevaluationRequest.specimen?.id?.let(::anonymize)
}
this.medicationRecommendations?.filterNotNull()?.forEach { medicationRecommendations ->
- medicationRecommendations.id = medicationRecommendations.id?.let(::anonymize)
- medicationRecommendations.supportingVariants?.filterNotNull()?.forEach { it.variant?.id = it.variant?.id?.let(::anonymize) }
- medicationRecommendations.reason?.id = medicationRecommendations.reason?.id?.let(::anonymize)
+ medicationRecommendations.id = medicationRecommendations.id?.let(::anonymize)
+ medicationRecommendations.supportingVariants?.filterNotNull()?.forEach {
+ it.variant?.id = it.variant?.id?.let(::anonymize)
+ }
+ medicationRecommendations.reason?.id =
+ medicationRecommendations.reason?.id?.let(::anonymize)
}
this.reason?.id = this.reason?.id?.let(::anonymize)
- this.studyEnrollmentRecommendations?.filterNotNull()?.forEach { studyEnrollmentRecommendation ->
- studyEnrollmentRecommendation.reason?.id = studyEnrollmentRecommendation.reason?.id?.let(::anonymize)
+ this.studyEnrollmentRecommendations?.filterNotNull()?.forEach { studyEnrollmentRecommendation
+ ->
+ studyEnrollmentRecommendation.reason?.id =
+ studyEnrollmentRecommendation.reason?.id?.let(::anonymize)
}
this.procedureRecommendations?.filterNotNull()?.forEach { procedureRecommendation ->
- procedureRecommendation.id = procedureRecommendation.id?.let(::anonymize)
- procedureRecommendation.supportingVariants?.filterNotNull()?.forEach { it.variant?.id = it.variant?.id?.let(::anonymize) }
- procedureRecommendation.reason?.id = procedureRecommendation.reason?.id?.let(::anonymize)
+ procedureRecommendation.id = procedureRecommendation.id?.let(::anonymize)
+ procedureRecommendation.supportingVariants?.filterNotNull()?.forEach {
+ it.variant?.id = it.variant?.id?.let(::anonymize)
+ }
+ procedureRecommendation.reason?.id = procedureRecommendation.reason?.id?.let(::anonymize)
}
- this.studyEnrollmentRecommendations?.filterNotNull()?.forEach { studyEnrollmentRecommendation ->
- studyEnrollmentRecommendation.id = studyEnrollmentRecommendation.id?.let(::anonymize)
- studyEnrollmentRecommendation.supportingVariants.forEach { it.variant?.id = it?.variant?.id?.let(::anonymize) }
+ this.studyEnrollmentRecommendations?.filterNotNull()?.forEach { studyEnrollmentRecommendation
+ ->
+ studyEnrollmentRecommendation.id = studyEnrollmentRecommendation.id?.let(::anonymize)
+ studyEnrollmentRecommendation.supportingVariants.forEach {
+ it.variant?.id = it?.variant?.id?.let(::anonymize)
+ }
}
}
}
this.responses?.filterNotNull()?.forEach { response ->
- response.id = response.id?.let(::anonymize)
- response.therapy?.id = response.therapy?.id?.let(::anonymize)
+ response.id = response.id?.let(::anonymize)
+ response.therapy?.id = response.therapy?.id?.let(::anonymize)
}
this.diagnoses?.filterNotNull()?.forEach { diagnose ->
- diagnose.id = diagnose.id?.let(::anonymize)
- diagnose.histology?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
+ diagnose.id = diagnose.id?.let(::anonymize)
+ diagnose.histology?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
}
this.ngsReports?.filterNotNull()?.forEach { ngsReport ->
- ngsReport.id = ngsReport.id?.let(::anonymize)
- ngsReport.results?.tumorCellContent?.id = ngsReport.results.tumorCellContent?.id?.let(::anonymize)
- ngsReport.results?.tumorCellContent?.specimen?.id =
- ngsReport.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
- ngsReport.results?.rnaFusions?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
- ngsReport.results?.simpleVariants?.filterNotNull()?.forEach {
+ ngsReport.id = ngsReport.id?.let(::anonymize)
+ ngsReport.results?.tumorCellContent?.id =
+ ngsReport.results.tumorCellContent?.id?.let(::anonymize)
+ ngsReport.results?.tumorCellContent?.specimen?.id =
+ ngsReport.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
+ ngsReport.results?.rnaFusions?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
+ ngsReport.results?.simpleVariants?.filterNotNull()?.forEach {
it.id = it.id?.let(::anonymize)
it.transcriptId?.value = it.transcriptId?.value?.let(::anonymize)
}
- ngsReport.results?.tmb?.id = ngsReport.results?.tmb?.id?.let(::anonymize)
- ngsReport.results?.tmb?.specimen?.id = ngsReport.results?.tmb?.specimen?.id?.let(::anonymize)
+ ngsReport.results?.tmb?.id = ngsReport.results?.tmb?.id?.let(::anonymize)
+ ngsReport.results?.tmb?.specimen?.id = ngsReport.results?.tmb?.specimen?.id?.let(::anonymize)
- ngsReport.results?.brcaness?.id = ngsReport.results?.brcaness?.id?.let(::anonymize)
- ngsReport.results?.brcaness?.specimen?.id = ngsReport.results?.brcaness?.specimen?.id?.let(::anonymize)
- ngsReport.results?.copyNumberVariants?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
- ngsReport.results?.hrdScore?.id = ngsReport.results?.hrdScore?.id?.let(::anonymize)
- ngsReport.results?.hrdScore?.specimen?.id = ngsReport.results?.hrdScore?.specimen?.id?.let(::anonymize)
- ngsReport.results?.rnaSeqs?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
- ngsReport.results?.dnaFusions?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
- ngsReport.specimen?.id = ngsReport.specimen?.id?.let(::anonymize)
+ ngsReport.results?.brcaness?.id = ngsReport.results?.brcaness?.id?.let(::anonymize)
+ ngsReport.results?.brcaness?.specimen?.id =
+ ngsReport.results?.brcaness?.specimen?.id?.let(::anonymize)
+ ngsReport.results?.copyNumberVariants?.filterNotNull()?.forEach {
+ it.id = it.id?.let(::anonymize)
+ }
+ ngsReport.results?.hrdScore?.id = ngsReport.results?.hrdScore?.id?.let(::anonymize)
+ ngsReport.results?.hrdScore?.specimen?.id =
+ ngsReport.results?.hrdScore?.specimen?.id?.let(::anonymize)
+ ngsReport.results?.rnaSeqs?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
+ ngsReport.results?.dnaFusions?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
+ ngsReport.specimen?.id = ngsReport.specimen?.id?.let(::anonymize)
}
this.histologyReports?.filterNotNull()?.forEach { histologyReport ->
- histologyReport.id = histologyReport.id?.let(::anonymize)
- histologyReport.results?.tumorCellContent?.id = histologyReport.results?.tumorCellContent?.id?.let(::anonymize)
- histologyReport.results?.tumorCellContent?.specimen?.id =
- histologyReport.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
+ histologyReport.id = histologyReport.id?.let(::anonymize)
+ histologyReport.results?.tumorCellContent?.id =
+ histologyReport.results?.tumorCellContent?.id?.let(::anonymize)
+ histologyReport.results?.tumorCellContent?.specimen?.id =
+ histologyReport.results?.tumorCellContent?.specimen?.id?.let(::anonymize)
- histologyReport.results?.tumorMorphology?.id = histologyReport.results?.tumorMorphology?.id?.let(::anonymize)
- histologyReport.results?.tumorMorphology?.specimen?.id =
- histologyReport.results?.tumorMorphology?.specimen?.id?.let(::anonymize)
- histologyReport.specimen?.id = histologyReport.specimen?.id?.let(::anonymize)
+ histologyReport.results?.tumorMorphology?.id =
+ histologyReport.results?.tumorMorphology?.id?.let(::anonymize)
+ histologyReport.results?.tumorMorphology?.specimen?.id =
+ histologyReport.results?.tumorMorphology?.specimen?.id?.let(::anonymize)
+ histologyReport.specimen?.id = histologyReport.specimen?.id?.let(::anonymize)
}
this.claimResponses?.filterNotNull()?.forEach { claimResponse ->
- claimResponse.id = claimResponse.id?.let(::anonymize)
- claimResponse.claim?.id = claimResponse.claim?.id?.let(::anonymize)
+ claimResponse.id = claimResponse.id?.let(::anonymize)
+ claimResponse.claim?.id = claimResponse.claim?.id?.let(::anonymize)
}
this.claims?.filterNotNull()?.forEach { claim ->
- claim.id = claim.id?.let(::anonymize)
- claim.recommendation?.id = claim.recommendation?.id?.let(::anonymize)
+ claim.id = claim.id?.let(::anonymize)
+ claim.recommendation?.id = claim.recommendation?.id?.let(::anonymize)
}
this.familyMemberHistories?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
this.guidelineProcedures?.filterNotNull()?.forEach { guidelineProcedure ->
- guidelineProcedure.id = guidelineProcedure.id?.let(::anonymize)
- guidelineProcedure.reason?.id = guidelineProcedure.reason?.id?.let(::anonymize)
- guidelineProcedure.basedOn?.id = guidelineProcedure.basedOn?.id?.let(::anonymize)
+ guidelineProcedure.id = guidelineProcedure.id?.let(::anonymize)
+ guidelineProcedure.reason?.id = guidelineProcedure.reason?.id?.let(::anonymize)
+ guidelineProcedure.basedOn?.id = guidelineProcedure.basedOn?.id?.let(::anonymize)
}
this.guidelineTherapies?.filterNotNull()?.forEach { guidelineTherapy ->
- guidelineTherapy.id = guidelineTherapy.id?.let(::anonymize)
- guidelineTherapy.reason?.id = guidelineTherapy.reason?.id?.let(::anonymize)
- guidelineTherapy.basedOn?.id = guidelineTherapy.basedOn?.id?.let(::anonymize)
+ guidelineTherapy.id = guidelineTherapy.id?.let(::anonymize)
+ guidelineTherapy.reason?.id = guidelineTherapy.reason?.id?.let(::anonymize)
+ guidelineTherapy.basedOn?.id = guidelineTherapy.basedOn?.id?.let(::anonymize)
}
this.ihcReports?.filterNotNull()?.forEach { ihcReport ->
- ihcReport.id = ihcReport.id?.let(::anonymize)
- ihcReport.specimen?.id = ihcReport.specimen?.id?.let(::anonymize)
- ihcReport.results?.proteinExpression?.filterNotNull()?.forEach { it.id = it.id.let(::anonymize) }
+ ihcReport.id = ihcReport.id?.let(::anonymize)
+ ihcReport.specimen?.id = ihcReport.specimen?.id?.let(::anonymize)
+ ihcReport.results?.proteinExpression?.filterNotNull()?.forEach {
+ it.id = it.id.let(::anonymize)
+ }
}
this.msiFindings?.filterNotNull()?.forEach { msiFinding ->
- msiFinding.id = msiFinding.id?.let(::anonymize)
- msiFinding.specimen?.id = msiFinding.specimen?.id?.let(::anonymize)
+ msiFinding.id = msiFinding.id?.let(::anonymize)
+ msiFinding.specimen?.id = msiFinding.specimen?.id?.let(::anonymize)
}
this.performanceStatus?.filterNotNull()?.forEach { it.id = it.id?.let(::anonymize) }
this.priorDiagnosticReports?.filterNotNull()?.forEach { priorDiagnosticReport ->
- priorDiagnosticReport.id = priorDiagnosticReport.id?.let(::anonymize)
- priorDiagnosticReport.specimen?.id = priorDiagnosticReport.specimen?.id?.let(::anonymize)
+ priorDiagnosticReport.id = priorDiagnosticReport.id?.let(::anonymize)
+ priorDiagnosticReport.specimen?.id = priorDiagnosticReport.specimen?.id?.let(::anonymize)
}
this.specimens?.filterNotNull()?.forEach { specimen ->
- specimen.id = specimen.id?.let(::anonymize)
- specimen.diagnosis?.id = specimen.diagnosis?.id?.let(::anonymize)
+ specimen.id = specimen.id?.let(::anonymize)
+ specimen.diagnosis?.id = specimen.diagnosis?.id?.let(::anonymize)
}
this.systemicTherapies?.filterNotNull()?.forEach { systemicTherapy ->
- systemicTherapy.history?.filterNotNull()?.forEach { history ->
- history.id = history.id?.let(::anonymize)
- history.reason?.id = history.reason?.id?.let(::anonymize)
- history.basedOn?.id = history.basedOn?.id?.let(::anonymize)
+ systemicTherapy.history?.filterNotNull()?.forEach { history ->
+ history.id = history.id?.let(::anonymize)
+ history.reason?.id = history.reason?.id?.let(::anonymize)
+ history.basedOn?.id = history.basedOn?.id?.let(::anonymize)
}
}
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
index 95858f4..ce72ba3 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -25,6 +25,7 @@ import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.consent.ConsentEvaluation
import dev.dnpm.etl.processor.consent.ConsentEvaluator
import dev.dnpm.etl.processor.consent.TtpConsentStatus
+import dev.dnpm.etl.processor.input.Dnpm21MtbFile.Companion.buildMtb
import dev.dnpm.etl.processor.services.RequestProcessor
import dev.pcvolkmer.mv64e.mtb.*
import java.time.Instant
@@ -36,6 +37,7 @@ import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.ArgumentsSource
+import org.junit.jupiter.params.provider.ValueSource
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -112,6 +114,51 @@ class MtbFileRestControllerTest {
}
}
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile",
+ "/mtbfile/etl/patient-record",
+ "/mtb",
+ "/mtb/etl/patient-record",
+ "/api/mtbfile",
+ "/api/mtbfile/etl/patient-record",
+ "/api/mtb",
+ "/api/mtb/etl/patient-record",
+ ]
+ )
+ fun shouldAcceptPostRequests(url: String) {
+ whenever(consentEvaluator.check(any<Mtb>()))
+ .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true))
+
+ val mtb =
+ buildMtb(
+ MvhMetadata.builder()
+ .modelProjectConsent(
+ ModelProjectConsent.builder()
+ .provisions(
+ listOf(
+ Provision.builder()
+ .date(Date())
+ .type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .build()
+ )
+ )
+ .build()
+ )
+ .build()
+ )
+
+ mockMvc
+ .post(url) {
+ content = objectMapper.writeValueAsString(mtb)
+ contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
+ }
+ .andExpect { status { isAccepted() } }
+ }
+
@Test
fun shouldProcessDeleteRequest() {
mockMvc.delete("/mtbfile/TEST_12345678").andExpect { status { isAccepted() } }
@@ -123,6 +170,28 @@ class MtbFileRestControllerTest {
)
verify(consentEvaluator, times(0)).check(any<Mtb>())
}
+
+ @ParameterizedTest
+ @ValueSource(
+ strings =
+ [
+ "/mtbfile/TEST_12345678",
+ "/mtbfile/etl/patient-record/TEST_12345678",
+ "/mtbfile/etl/patient/TEST_12345678",
+ "/mtb/TEST_12345678",
+ "/mtb/etl/patient-record/TEST_12345678",
+ "/mtb/etl/patient/TEST_12345678",
+ "/api/mtbfile/TEST_12345678",
+ "/api/mtbfile/etl/patient-record/TEST_12345678",
+ "/api/mtbfile/etl/patient/TEST_12345678",
+ "/api/mtb/TEST_12345678",
+ "/api/mtb/etl/patient-record/TEST_12345678",
+ "/api/mtb/etl/patient/TEST_12345678",
+ ]
+ )
+ fun shouldAcceptDeleteRequests(url: String) {
+ mockMvc.delete(url).andExpect { status { isAccepted() } }
+ }
}
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
index 2b4cd34..84b081a 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
@@ -251,44 +251,44 @@ class ExtensionsTest {
assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.specimens.first().diagnosis.id)
}
- @Test
- fun shouldNotThrowAnyExceptionOnMissingMsiId(
- @Mock pseudonymizeService: PseudonymizeService
- ) {
+ @Test
+ fun shouldNotThrowAnyExceptionOnMissingMsiId(@Mock pseudonymizeService: PseudonymizeService) {
- doAnswer {
- it.arguments[0]
- "PSEUDO-ID"
+ doAnswer {
+ it.arguments[0]
+ "PSEUDO-ID"
}
- .whenever(pseudonymizeService)
- .patientPseudonym(anyValueClass())
-
- doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix()
-
- val mtbFile =
- Mtb().apply {
- this.patient =
- Patient().apply {
- this.id = "PID"
- this.birthDate = Date.from(Instant.now())
- this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE }
- }
- this.msiFindings = listOf(
- null,
- Msi.builder().id("1").build(),
- Msi.builder(). build(),
- Msi.builder().specimen(null).build(),
- Msi.builder().specimen(Reference.builder().build()).build()
- )
- }
+ .whenever(pseudonymizeService)
+ .patientPseudonym(anyValueClass())
- mtbFile.pseudonymizeWith(pseudonymizeService)
- mtbFile.anonymizeContentWith(pseudonymizeService)
+ doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix()
- assertThat( mtbFile.msiFindings ).isNotNull
- assertThat(mtbFile.msiFindings[1]).satisfiesAnyOf(
+ val mtbFile =
+ Mtb().apply {
+ this.patient =
+ Patient().apply {
+ this.id = "PID"
+ this.birthDate = Date.from(Instant.now())
+ this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE }
+ }
+ this.msiFindings =
+ listOf(
+ null,
+ Msi.builder().id("1").build(),
+ Msi.builder().build(),
+ Msi.builder().specimen(null).build(),
+ Msi.builder().specimen(Reference.builder().build()).build(),
+ )
+ }
+
+ mtbFile.pseudonymizeWith(pseudonymizeService)
+ mtbFile.anonymizeContentWith(pseudonymizeService)
+
+ assertThat(mtbFile.msiFindings).isNotNull
+ assertThat(mtbFile.msiFindings[1])
+ .satisfiesAnyOf(
{ assertThat(it.id).isNull() },
- { assertThat(it.id).isEqualTo("TESTDOMAIN44e20a53bbbf9f3ae39626d05df7014dcd77d6098")}
+ { assertThat(it.id).isEqualTo("TESTDOMAIN44e20a53bbbf9f3ae39626d05df7014dcd77d6098") },
)
- }
+ }
}