From a501553b3b411bd3f0b70f973472e71b09f1a2d5 Mon Sep 17 00:00:00 2001 From: Tom Winter Date: Thu, 21 Nov 2024 12:47:36 +0100 Subject: [PATCH] feat: support named argumentes in sql v1 queries --- .../DefaultReportCalculationUseCase.kt | 15 +- .../DefaultReportCalculationUseCaseTest.kt | 151 +++++++++++++++++- .../start-report-calculation.feature | 19 +++ .../documents/ReportCalculation:5.json | 13 ++ .../database/documents/ReportConfig:5.json | 11 ++ 5 files changed, 201 insertions(+), 8 deletions(-) create mode 100644 application/aam-backend-service/src/test/resources/database/documents/ReportCalculation:5.json create mode 100644 application/aam-backend-service/src/test/resources/database/documents/ReportConfig:5.json diff --git a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/usecase/DefaultReportCalculationUseCase.kt b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/usecase/DefaultReportCalculationUseCase.kt index 4cfe305..d8881a9 100644 --- a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/usecase/DefaultReportCalculationUseCase.kt +++ b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/usecase/DefaultReportCalculationUseCase.kt @@ -97,7 +97,8 @@ class DefaultReportCalculationUseCase( @Throws(AamException::class) private fun handleSqlReport( - report: Report, reportCalculation: ReportCalculation + report: Report, + reportCalculation: ReportCalculation, ): UseCaseOutcome { for ((argKey: String, transformationKeys: List) in report.transformations) { handleTransformations(argKey, transformationKeys, reportCalculation.args) @@ -180,10 +181,14 @@ class DefaultReportCalculationUseCase( ): QueryRequest { return when (report.version) { 1 -> { - QueryRequest( - query = query.sql, - args = reportCalculation.args.values.toList() - ) + if (query.sql.contains("$")) { + getQueryRequest(query, reportCalculation.args) + } else { + QueryRequest( + query = query.sql, + args = reportCalculation.args.values.toList() + ) + } } 2 -> { diff --git a/application/aam-backend-service/src/test/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/core/DefaultReportCalculationUseCaseTest.kt b/application/aam-backend-service/src/test/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/core/DefaultReportCalculationUseCaseTest.kt index d1eb693..52f6ccf 100644 --- a/application/aam-backend-service/src/test/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/core/DefaultReportCalculationUseCaseTest.kt +++ b/application/aam-backend-service/src/test/kotlin/com/aamdigital/aambackendservice/reporting/reportcalculation/core/DefaultReportCalculationUseCaseTest.kt @@ -16,8 +16,6 @@ import com.aamdigital.aambackendservice.reporting.transformation.SqlFromDateTran import com.aamdigital.aambackendservice.reporting.transformation.SqlFromDateTransformation.Companion.DEFAULT_FROM_DATE import com.aamdigital.aambackendservice.reporting.transformation.SqlToDateTransformation import com.aamdigital.aambackendservice.reporting.transformation.SqlToDateTransformation.Companion.DEFAULT_TO_DATE -import com.fasterxml.jackson.core.JsonFactory -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -35,7 +33,7 @@ import org.mockito.kotlin.whenever class DefaultReportCalculationUseCaseTest { private lateinit var service: DefaultReportCalculationUseCase - + @Mock lateinit var reportCalculationStorage: ReportCalculationStorage @@ -208,6 +206,153 @@ class DefaultReportCalculationUseCaseTest { ) } + @Test + fun `should apply named argument transformations before sending to query service (v1)`() { + // given + val report = Report( + id = "Report:1", + title = "Report", + version = 1, + items = listOf( + ReportItem.ReportQuery( + sql = "SELECT * FROM foo WHERE time BETWEEN \$from and \$to", + ) + ), + transformations = mapOf( + "to" to listOf("SQL_TO_DATE"), + "from" to listOf("SQL_FROM_DATE"), + ), + ) + + val reportCalculation = ReportCalculation( + id = "ReportCalculation:1", + report = DomainReference("Report:1"), + status = ReportCalculationStatus.PENDING, + args = mutableMapOf( + Pair("from", "2010-01-15T00:00:00.000Z"), + Pair("to", "2010-01-16"), + ) + ) + + whenever( + reportCalculationStorage.fetchReportCalculation( + eq(DomainReference("ReportCalculation:1")) + ) + ).thenReturn(reportCalculation) + + whenever( + reportStorage.fetchReport( + eq(DomainReference("Report:1")) + ) + ).thenReturn(report) + + whenever(queryStorage.executeQuery(any())).thenReturn("[{}]".byteInputStream()) + + whenever(reportCalculationStorage.addReportCalculationData(any(), any())) + .thenReturn(reportCalculation) + + // when + val response = service.run( + ReportCalculationRequest( + reportCalculationId = reportCalculation.id + ) + ) + + // then + assertThat(response).isInstanceOf(UseCaseOutcome.Success::class.java) + + assertEquals( + reportCalculation, + (response as UseCaseOutcome.Success).data.reportCalculation + ) + + verify(queryStorage).executeQuery( + eq( + QueryRequest( + query = "SELECT * FROM foo WHERE time BETWEEN ? and ?", + args = listOf( + "2010-01-15", "2010-01-16T23:59:59.999Z" + ) + ) + ) + ) + } + + @Test + fun `should apply multiple named argument transformations before sending to query service (v1)`() { + // given + val report = Report( + id = "Report:1", + title = "Report", + version = 1, + items = listOf( + ReportItem.ReportQuery( + sql = "SELECT * FROM foo WHERE time BETWEEN \$from and \$to AND date BETWEEN \$from AND \$to", + ) + ), + transformations = mapOf( + "to" to listOf("SQL_TO_DATE"), + "from" to listOf("SQL_FROM_DATE"), + ), + ) + + val reportCalculation = ReportCalculation( + id = "ReportCalculation:1", + report = DomainReference("Report:1"), + status = ReportCalculationStatus.PENDING, + args = mutableMapOf( + Pair("from", "2010-01-15T00:00:00.000Z"), + Pair("to", "2010-01-16"), + ) + ) + + whenever( + reportCalculationStorage.fetchReportCalculation( + eq(DomainReference("ReportCalculation:1")) + ) + ).thenReturn(reportCalculation) + + whenever( + reportStorage.fetchReport( + eq(DomainReference("Report:1")) + ) + ).thenReturn(report) + + whenever(queryStorage.executeQuery(any())).thenReturn("[{}]".byteInputStream()) + + whenever(reportCalculationStorage.addReportCalculationData(any(), any())) + .thenReturn(reportCalculation) + + // when + val response = service.run( + ReportCalculationRequest( + reportCalculationId = reportCalculation.id + ) + ) + + // then + assertThat(response).isInstanceOf(UseCaseOutcome.Success::class.java) + + assertEquals( + reportCalculation, + (response as UseCaseOutcome.Success).data.reportCalculation + ) + + verify(queryStorage).executeQuery( + eq( + QueryRequest( + query = "SELECT * FROM foo WHERE time BETWEEN ? and ? AND date BETWEEN ? AND ?", + args = listOf( + "2010-01-15", + "2010-01-16T23:59:59.999Z", + "2010-01-15", + "2010-01-16T23:59:59.999Z", + ) + ) + ) + ) + } + @Test fun `should apply argument transformations before sending to query service (v2)`() { // given diff --git a/application/aam-backend-service/src/test/resources/cucumber/features/reporting/reportcalculation/start-report-calculation.feature b/application/aam-backend-service/src/test/resources/cucumber/features/reporting/reportcalculation/start-report-calculation.feature index 2b04b27..3dc39c5 100644 --- a/application/aam-backend-service/src/test/resources/cucumber/features/reporting/reportcalculation/start-report-calculation.feature +++ b/application/aam-backend-service/src/test/resources/cucumber/features/reporting/reportcalculation/start-report-calculation.feature @@ -97,3 +97,22 @@ Feature: the report calculation endpoint persist to database When the client calls GET /v1/reporting/report-calculation/ReportCalculation:4/data Then the client receives an json object Then the client receives status code of 200 + + Scenario: ReportCalculation for v1 ReportConfig with named arguments is processed within 5 seconds + Given document ReportConfig:5 is stored in database app + Given document Config:CONFIG_ENTITY is stored in database app + Given document ReportCalculation:5 is stored in database report-calculation + Given signed in as client dummy-client with secret client-secret in realm dummy-realm + When the client calls GET /v1/reporting/report-calculation/ReportCalculation:5 + Then the client receives an json object + Then the client receives status code of 200 + Then the client receives value PENDING for property status + Given emit ReportCalculationEvent for ReportCalculation:5 in tenant local-spring + Then the client waits for 5000 milliseconds + When the client calls GET /v1/reporting/report-calculation/ReportCalculation:5 + Then the client receives an json object + Then the client receives status code of 200 + Then the client receives value FINISHED_SUCCESS for property status + When the client calls GET /v1/reporting/report-calculation/ReportCalculation:5/data + Then the client receives an json object + Then the client receives status code of 200 diff --git a/application/aam-backend-service/src/test/resources/database/documents/ReportCalculation:5.json b/application/aam-backend-service/src/test/resources/database/documents/ReportCalculation:5.json new file mode 100644 index 0000000..742f090 --- /dev/null +++ b/application/aam-backend-service/src/test/resources/database/documents/ReportCalculation:5.json @@ -0,0 +1,13 @@ +{ + "_id": "ReportCalculation:5", + "report": { + "id": "ReportConfig:5" + }, + "args": { + "from": "2024-01-01T00:00:00Z", + "to": "2024-04-30T00:00:00Z" + }, + "status": "PENDING", + "startDate": null, + "endDate": null +} diff --git a/application/aam-backend-service/src/test/resources/database/documents/ReportConfig:5.json b/application/aam-backend-service/src/test/resources/database/documents/ReportConfig:5.json new file mode 100644 index 0000000..923ffee --- /dev/null +++ b/application/aam-backend-service/src/test/resources/database/documents/ReportConfig:5.json @@ -0,0 +1,11 @@ +{ + "_id": "ReportConfig:5", + "title": "Version 1 with placeholder", + "mode": "sql", + "version": 1, + "neededArgs": [ + "from", + "to" + ], + "aggregationDefinition": "SELECT s.name as name, s.privateSchool as privateSchool FROM School as s WHERE s.date BETWEEN $from AND $to AND s.date BETWEEN $from AND $to" +}