diff --git a/frontend-react/src/pages/error/legacy-content/ErrorNoPage.tsx b/frontend-react/src/pages/error/legacy-content/ErrorNoPage.tsx
index a076f75d3f4..aceb33d7c51 100644
--- a/frontend-react/src/pages/error/legacy-content/ErrorNoPage.tsx
+++ b/frontend-react/src/pages/error/legacy-content/ErrorNoPage.tsx
@@ -2,6 +2,7 @@ import { Button } from "@trussworks/react-uswds";
import { Helmet } from "react-helmet-async";
import { useNavigate } from "react-router-dom";
+import { pageNotFound } from "../../../content/error/ErrorMessages";
import site from "../../../content/site.json";
export const ErrorNoPage = () => {
@@ -9,42 +10,31 @@ export const ErrorNoPage = () => {
return (
<>
- Page Not Found | {import.meta.env.VITE_TITLE}
+
+ {pageNotFound} | {import.meta.env.VITE_TITLE}
+
-
+
-
Page not found
+
{pageNotFound}
- We’re sorry, we can’t find the page you're
- looking for. It might have been removed, changed
- names, or is otherwise unavailable.
+ We’re sorry, we can’t find the page you're looking for. It might have been removed,
+ changed names, or is otherwise unavailable.
- If you typed the URL directly, check your
- spelling and capitalization. Our URLs look like
- this:{" "}
-
- reportstream.cdc.gov/example-one
-
- .
+ If you typed the URL directly, check your spelling and capitalization. Our URLs look
+ like this: reportstream.cdc.gov/example-one.
- Visit our homepage or contact us at{" "}
- {site.orgs.RS.email} and we’ll point you in the
+ Visit our homepage or contact us at {site.orgs.RS.email} and we’ll point you in the
right direction.{" "}
-
@@ -52,11 +42,7 @@ export const ErrorNoPage = () => {
- window.open(
- `mailto:${site.orgs.RS.email}`,
- )
- }
+ onClick={() => window.open(`mailto:${site.orgs.RS.email}`)}
>
Contact us
diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock
index 262a13d07be..c22b4708f09 100644
--- a/frontend-react/yarn.lock
+++ b/frontend-react/yarn.lock
@@ -11429,6 +11429,15 @@ __metadata:
languageName: node
linkType: hard
+"p-limit@npm:^6.1.0":
+ version: 6.1.0
+ resolution: "p-limit@npm:6.1.0"
+ dependencies:
+ yocto-queue: ^1.1.1
+ checksum: 0c98d8fc1006b70fc7423232a47e8d026dc69279b06fe7ff8b4c0cc8023de2b6bb8991b609d93c3dec691a7a362ab0f0157df521d931a01fec192a5e404b9ee5
+ languageName: node
+ linkType: hard
+
"p-locate@npm:^3.0.0":
version: 3.0.0
resolution: "p-locate@npm:3.0.0"
@@ -12268,6 +12277,7 @@ __metadata:
msw-storybook-addon: beta
npm-run-all: ^4.1.5
otpauth: ^9.3.2
+ p-limit: ^6.1.0
patch-package: ^8.0.0
postcss: ^8.4.45
prettier: ^3.3.3
@@ -15612,6 +15622,13 @@ __metadata:
languageName: node
linkType: hard
+"yocto-queue@npm:^1.1.1":
+ version: 1.1.1
+ resolution: "yocto-queue@npm:1.1.1"
+ checksum: f2e05b767ed3141e6372a80af9caa4715d60969227f38b1a4370d60bffe153c9c5b33a862905609afc9b375ec57cd40999810d20e5e10229a204e8bde7ef255c
+ languageName: node
+ linkType: hard
+
"zwitch@npm:^2.0.0":
version: 2.0.2
resolution: "zwitch@npm:2.0.2"
diff --git a/gradle.properties b/gradle.properties
index a0d1f026d36..aeb4a2f63d6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,3 @@
org.gradle.jvmargs=-Dfile.encoding=UTF8 -Xmx2048M
# Set Kotlin version for all peices of the build environment
-systemProp.kotlinVersion=1.9.23
+systemProp.kotlinVersion=1.9.25
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e6441136f3d..a4b76b9530d 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a4413138c96..df97d72b8b9 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index b740cf13397..f5feea6d6b1 100755
--- a/gradlew
+++ b/gradlew
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
diff --git a/gradlew.bat b/gradlew.bat
index 7101f8e4676..9b42019c791 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
diff --git a/prime-router/Dockerfile.build b/prime-router/Dockerfile.build
index 923fc6572ef..07610edd1d9 100644
--- a/prime-router/Dockerfile.build
+++ b/prime-router/Dockerfile.build
@@ -3,7 +3,7 @@
# To build it, you must specify at least the following --build-arg values
# with some suggested default shown here
#
-# --build-arg GRADLE_VERSION=8.8 - The version of Gradle you want to build with
+# --build-arg GRADLE_VERSION=8.10.2 - The version of Gradle you want to build with
# --build-arg AFCT_VERSION=4.0.5198 - The version of the Azure Functions Core Tools
# --build-arg JAVA_VERSION=17 - The version of the JDK (and thus JRE) you want to build against/with
#
@@ -14,7 +14,7 @@
# a tad bit easier when you are rebuilding the builder image
#
FROM alpine:3.20 AS downloader
-ARG GRADLE_VERSION=8.8
+ARG GRADLE_VERSION=8.10.2
ARG AFCT_VERSION=4.0.5198
RUN apk update && apk add wget --no-cache
diff --git a/prime-router/build.gradle.kts b/prime-router/build.gradle.kts
index 16afe126e90..65ca5665f3f 100644
--- a/prime-router/build.gradle.kts
+++ b/prime-router/build.gradle.kts
@@ -35,17 +35,27 @@ apply(from = rootProject.file("buildSrc/shared.gradle.kts"))
plugins {
val kotlinVersion by System.getProperties()
id("reportstream.project-conventions")
- id("org.flywaydb.flyway") version "10.17.0"
+ id("org.flywaydb.flyway") version "10.18.0"
id("nu.studer.jooq") version "9.0"
id("com.github.johnrengelman.shadow") version "8.1.1"
id("com.microsoft.azure.azurefunctions") version "1.16.1"
id("com.adarshr.test-logger") version "4.0.0"
id("jacoco")
id("org.jetbrains.dokka") version "1.8.20"
- id("com.avast.gradle.docker-compose") version "0.17.7"
+ id("com.avast.gradle.docker-compose") version "0.17.8"
id("org.jetbrains.kotlin.plugin.serialization") version "$kotlinVersion"
id("com.nocwriter.runsql") version ("1.0.3")
- id("io.swagger.core.v3.swagger-gradle-plugin") version "2.2.22"
+ id("io.swagger.core.v3.swagger-gradle-plugin") version "2.2.23"
+}
+
+// retrieve the current commit hash
+val commitId by lazy {
+ val stdout = ByteArrayOutputStream()
+ exec {
+ commandLine("git", "rev-parse", "--short", "HEAD")
+ standardOutput = stdout
+ }
+ stdout.toString(StandardCharsets.UTF_8).trim()
}
group = "gov.cdc.prime.reportstream"
@@ -65,7 +75,7 @@ val javaVersion = when (appJvmTarget) {
}
val ktorVersion = "2.3.12"
val kotlinVersion by System.getProperties()
-val jacksonVersion = "2.17.1"
+val jacksonVersion = "2.17.2"
jacoco.toolVersion = "0.8.12"
// Local database information, first one wins:
@@ -261,6 +271,9 @@ sourceSets.create("testIntegration") {
runtimeClasspath += sourceSets["main"].output
}
+// Add generated version object
+sourceSets["main"].java.srcDir("$buildDir/generated-src/version")
+
val compileTestIntegrationKotlin: KotlinCompile by tasks
compileTestIntegrationKotlin.kotlinOptions.jvmTarget = appJvmTarget
@@ -340,6 +353,7 @@ tasks.withType().configureEach {
}
tasks.processResources {
+ dependsOn("generateVersionObject")
// Set the proper build values in the build.properties file
filesMatching("build.properties") {
val dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd")
@@ -500,18 +514,32 @@ tasks.azureFunctionsPackage {
finalizedBy("copyAzureScripts")
}
+// TODO: remove after implementation of health check endpoint
tasks.register("generateVersionFile") {
doLast {
- val stdout = ByteArrayOutputStream()
- exec {
- commandLine("git", "rev-parse", "--short", "HEAD")
- standardOutput = stdout
- }
- val currentCommit = stdout.toString(StandardCharsets.UTF_8).trim()
- File(buildDir, "$azureFunctionsDir/$azureAppName/version.json").writeText("{\"commitId\": \"$currentCommit\"}")
+ file("$buildDir/$azureFunctionsDir/$azureAppName/version.json").writeText("{\"commitId\": \"$commitId\"}")
}
}
+tasks.register("generateVersionObject") {
+ val sourceDir = file("$buildDir/generated-src/version")
+ val sourceFile = file("$sourceDir/Version.kt")
+ sourceDir.mkdirs()
+ sourceFile.writeText(
+ """
+ package gov.cdc.prime.router.version
+
+ /**
+ * Supplies information for the current build.
+ * This file is generated via Gradle task prior to compile time and should always contain the current commit hash.
+ */
+ object Version {
+ const val commitId = "$commitId"
+ }
+ """.trimIndent()
+ )
+}
+
val azureResourcesTmpDir = File(buildDir, "$azureFunctionsDir-resources/$azureAppName")
val azureResourcesFinalDir = File(buildDir, "$azureFunctionsDir/$azureAppName")
tasks.register("gatherAzureResources") {
@@ -805,7 +833,7 @@ buildscript {
// will need to be removed once this issue is resolved in Maven.
classpath("net.minidev:json-smart:2.5.1")
// as per flyway v10 docs the postgres flyway module must be on the project buildpath
- classpath("org.flywaydb:flyway-database-postgresql:10.17.0")
+ classpath("org.flywaydb:flyway-database-postgresql:10.18.0")
}
}
@@ -818,37 +846,37 @@ configurations {
}
dependencies {
- jooqGenerator("org.postgresql:postgresql:42.7.3")
+ jooqGenerator("org.postgresql:postgresql:42.7.4")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
implementation("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlinVersion")
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
implementation("com.microsoft.azure.functions:azure-functions-java-library:3.1.0")
- implementation("com.azure:azure-core:1.51.0")
- implementation("com.azure:azure-core-http-netty:1.15.3")
+ implementation("com.azure:azure-core:1.52.0")
+ implementation("com.azure:azure-core-http-netty:1.15.4")
implementation("com.azure:azure-storage-blob:12.27.0") {
exclude(group = "com.azure", module = "azure-core")
}
implementation("com.azure:azure-storage-queue:12.22.0") {
exclude(group = "com.azure", module = "azure-core")
}
- implementation("com.azure:azure-security-keyvault-secrets:4.8.5") {
+ implementation("com.azure:azure-security-keyvault-secrets:4.8.6") {
exclude(group = "com.azure", module = "azure-core")
exclude(group = "com.azure", module = "azure-core-http-netty")
}
- implementation("com.azure:azure-identity:1.13.2") {
+ implementation("com.azure:azure-identity:1.13.3") {
exclude(group = "com.azure", module = "azure-core")
exclude(group = "com.azure", module = "azure-core-http-netty")
}
- implementation("com.nimbusds:nimbus-jose-jwt:9.40")
- implementation("org.apache.logging.log4j:log4j-api:2.23.1")
- implementation("org.apache.logging.log4j:log4j-core:2.23.1")
- implementation("org.apache.logging.log4j:log4j-slf4j2-impl:2.23.1")
- implementation("org.apache.logging.log4j:log4j-layout-template-json:2.23.1")
- implementation("org.apache.logging.log4j:log4j-api-kotlin:1.4.0")
+ implementation("com.nimbusds:nimbus-jose-jwt:9.41.1")
+ implementation("org.apache.logging.log4j:log4j-api:2.24.0")
+ implementation("org.apache.logging.log4j:log4j-core:2.24.0")
+ implementation("org.apache.logging.log4j:log4j-slf4j2-impl:2.24.0")
+ implementation("org.apache.logging.log4j:log4j-layout-template-json:2.24.0")
+ implementation("org.apache.logging.log4j:log4j-api-kotlin:1.5.0")
implementation("io.github.oshai:kotlin-logging-jvm:7.0.0")
- implementation("com.github.doyaaaaaken:kotlin-csv-jvm:1.9.3")
+ implementation("com.github.doyaaaaaken:kotlin-csv-jvm:1.10.0")
implementation("tech.tablesaw:tablesaw-core:0.43.1")
implementation("com.github.ajalt.clikt:clikt-jvm:4.4.0")
@@ -857,7 +885,7 @@ dependencies {
implementation("com.github.javafaker:javafaker:1.0.2") {
exclude(group = "org.yaml", module = "snakeyaml")
}
- implementation("org.yaml:snakeyaml:2.2")
+ implementation("org.yaml:snakeyaml:2.3")
implementation("io.github.linuxforhealth:hl7v2-fhir-converter") {
version {
branch = "master"
@@ -873,7 +901,7 @@ dependencies {
implementation("ca.uhn.hapi:hapi-base:2.5.1")
implementation("ca.uhn.hapi:hapi-structures-v251:2.5.1")
implementation("ca.uhn.hapi:hapi-structures-v27:2.5.1")
- implementation("com.googlecode.libphonenumber:libphonenumber:8.13.42")
+ implementation("com.googlecode.libphonenumber:libphonenumber:8.13.46")
implementation("org.thymeleaf:thymeleaf:3.1.2.RELEASE")
implementation("com.sendgrid:sendgrid-java:4.10.2")
implementation("com.okta.jwt:okta-jwt-verifier:0.5.7")
@@ -887,12 +915,12 @@ dependencies {
implementation("org.apache.commons:commons-text:1.12.0")
implementation("commons-codec:commons-codec:1.17.1")
implementation("commons-io:commons-io:2.16.1")
- implementation("org.postgresql:postgresql:42.7.3")
+ implementation("org.postgresql:postgresql:42.7.4")
implementation("com.zaxxer:HikariCP:5.1.0")
- implementation("org.flywaydb:flyway-core:10.17.0")
- implementation("org.flywaydb:flyway-database-postgresql:10.17.0")
+ implementation("org.flywaydb:flyway-core:10.18.0")
+ implementation("org.flywaydb:flyway-database-postgresql:10.18.0")
implementation("org.commonmark:commonmark:0.22.0")
- implementation("com.google.guava:guava:33.2.1-jre")
+ implementation("com.google.guava:guava:33.3.0-jre")
implementation("com.helger.as2:as2-lib:5.1.2")
implementation("org.bouncycastle:bcprov-jdk15to18:1.78.1")
implementation("org.bouncycastle:bcprov-jdk18on:1.78.1")
@@ -915,14 +943,14 @@ dependencies {
implementation("it.skrape:skrapeit-http-fetcher:1.3.0-alpha.2")
implementation("org.apache.poi:poi:5.3.0")
implementation("org.apache.poi:poi-ooxml:5.3.0")
- implementation("org.apache.commons:commons-compress:1.26.2")
+ implementation("org.apache.commons:commons-compress:1.27.1")
implementation("commons-io:commons-io:2.16.1")
implementation("com.anyascii:anyascii:0.3.2")
// force jsoup since skrapeit-html-parser@1.2.1+ has not updated
implementation("org.jsoup:jsoup:1.18.1")
// https://mvnrepository.com/artifact/io.swagger/swagger-annotations
implementation("io.swagger:swagger-annotations:1.6.14")
- implementation("io.swagger.core.v3:swagger-jaxrs2:2.2.22")
+ implementation("io.swagger.core.v3:swagger-jaxrs2:2.2.23")
// https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api
implementation("javax.ws.rs:javax.ws.rs-api:2.1.1")
// https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api
@@ -948,7 +976,7 @@ dependencies {
implementation("io.konform:konform-jvm:0.4.0")
runtimeOnly("com.okta.jwt:okta-jwt-verifier-impl:0.5.7")
- runtimeOnly("com.squareup.okio:okio:3.9.0")
+ runtimeOnly("com.squareup.okio:okio:3.9.1")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
diff --git a/prime-router/docs/docs-deprecated/chatops-provisioning.md b/prime-router/docs/docs-deprecated/chatops-provisioning.md
index be53b2b7e44..2d43ba96f49 100644
--- a/prime-router/docs/docs-deprecated/chatops-provisioning.md
+++ b/prime-router/docs/docs-deprecated/chatops-provisioning.md
@@ -2,7 +2,7 @@
## Application
-[slack-boltjs-app](https://github.com/JosiahSiegel/slack-boltjs-app) is installed as a submodule under [operations/slack-boltjs-app](../../operations/slack-boltjs-app/).
+[slack-boltjs-app](https://github.com/focusconsulting/slack-boltjs-app) is installed as a submodule under [operations/slack-boltjs-app](../../operations/slack-boltjs-app/).
To update the submodule, navigate to the subdirectory and `git pull`.
diff --git a/prime-router/docs/docs-deprecated/getting-started/install-gradle.md b/prime-router/docs/docs-deprecated/getting-started/install-gradle.md
index 955f9e71ced..90fc7b3cf64 100644
--- a/prime-router/docs/docs-deprecated/getting-started/install-gradle.md
+++ b/prime-router/docs/docs-deprecated/getting-started/install-gradle.md
@@ -20,7 +20,7 @@ If your package manager contains a gradle version >=8.0.0, feel free to install
```bash
mkdir -p ${HOME?}/bin/gradle-bins/
cd ${HOME?}/bin/gradle-bins/
-VERSION=8.8
+VERSION=8.10.2
wget https://services.gradle.org/distributions/gradle-${VERSION?}-bin.zip
unzip "gradle-${VERSION?}-bin.zip"
rm "gradle-${VERSION?}-bin.zip"
diff --git a/prime-router/docs/docs-deprecated/playbooks/dependabot-updates.md b/prime-router/docs/docs-deprecated/playbooks/dependabot-updates.md
index d470d812472..1c84a92132e 100644
--- a/prime-router/docs/docs-deprecated/playbooks/dependabot-updates.md
+++ b/prime-router/docs/docs-deprecated/playbooks/dependabot-updates.md
@@ -18,7 +18,7 @@ Please pay attention to [security related dependency PRs](https://github.com/CDC
## Steps
1. Open the dependabot's pull request to be merged and identify if the PR is out of date from master. If so, create a new comment in the PR with the text
-`@dependabot rebase` to let dependabot rebase the branch for you. If you use any other method, dependabot will not be able to keep track of the PR. If the PR has conflicts (and no manual commits were added), use `@dpendabot recreate` to recreate the PR from scratch.
+`@dependabot rebase` to let dependabot rebase the branch for you. If you use any other method, dependabot will not be able to keep track of the PR. If the PR has conflicts (and no manual commits were added), use `@dependabot recreate` to recreate the PR from scratch.
1. Verify that the build for the PR is successful. Note that the unit, integration and smoke tests are run as part of the build.
1. Read the updated library's changelog and identify and communicate any risks you find. When in doubt ask! Library changes can affect many parts of the system.
1. Identify any library version conflicts for the updated library. This may happen when other libraries are dependent on a different version of the same library. See
diff --git a/prime-router/docs/observability/azure-events.md b/prime-router/docs/observability/azure-events.md
index 07fcbaaeb6a..d7b6b6a9bbb 100644
--- a/prime-router/docs/observability/azure-events.md
+++ b/prime-router/docs/observability/azure-events.md
@@ -45,115 +45,6 @@ class MyService(
}
```
-Under the hood, it will serialize your event class and push the event
-to the configured Microsoft AppInsights instance.
-
-## Event Glossery
-
-### ReportCreatedEvent
-This event is emitted by the convert step when a report is successfully translated into a FHIR bundle.
-- reportId
- - The ID assigned to the created report
-- topic
- - The topic of the created report
-
-
-### ReportAcceptedEvent
-This event is emitted by the destination filter step, _before_ any filters are evaluated
-- reportId
- - The report ID from the preceding function (convert step)
-- submittedReportId
- - The report ID submitted by the sender
-- topic
- - The topic of the report
-- sender
- - The full sender name
-- observations
- - A list of observations each containing a list of its mapped conditions
-- bundleSize
- - Length of the bundle JSON string
-- messageId
- - From the bundle.identifier value and system. If ingested as HL7 this comes from MSH-10
-
-
-### ReportNotRoutedEvent
-This is event is emitted by the destination filter step if a bundle not routed to any receivers.
-
-- reportId
- - The ID of the empty report that terminated this lineage
-- parentReportId
- - The report ID from the preceding function (convert step)
-- submittedReportId
- - The report ID submitted by the sender
-- topic
- - The topic of the report
-- sender
- - The full sender name
-- bundleSize
- - Length of the bundle JSON string
-- failingFilters
- - A list of all the filters that failed causing this report not the be routed
-- messageId
- - From the bundle.identifier value and system. If ingested as HL7 this comes from MSH-10
-
-
-### ReportRouteEvent
-This event is emitted by the receiver filter step, _after_ all filters have passed and a report has been
-routed to a receiver. Many `ReportRouteEvent` can correspond to a `ReportAcceptedEvent` and can be "joined" on:
-
-`ReportAcceptedEvent.reportId == ReportRouteEvent.parentReportId`
-
-- reportId
- - The ID of the report routed to the receiver
-- parentReportId
- - The report ID from the preceding function (destination filter step)
-- submittedReportId
- - The report ID submitted by the sender
-- topic
- - The topic of the report
-- sender
- - The full sender name
-- receiver
- - The full receiver name. (deprecated: When a report does not get routed to a receiver this value will be `"null"`)
-- observations
- - A list of observations each containing a list of its mapped conditions
-- (deprecated) originalObservations
- - (deprecated) A list of observations in the originally submitted report, before any filters were run
-- filteredObservations
- - A list of observations that were filtered from the bundle during filtering
-- bundleSize
- - Length of the bundle JSON string
-- messageId
- - From the bundle.identifier value and system. If ingested as HL7 this comes from MSH-10
-
-
-### ReceiverFilterFailedEvent
-This event is emitted by the receiver filter step if a bundle fails a receiver filter.
-
-- reportId
- - The ID of the empty report that terminated this lineage
-- parentReportId
- - The report ID from the preceding function (destination filter step)
-- submittedReportId
- - The report ID submitted by the sender
-- topic
- - The topic of the report
-- sender
- - The full sender name
-- receiver
- - The full receiver name.
-- observations
- - A list of observations each containing a list of its mapped conditions
-- failingFilters
- - A list of all the filters that failed for this report
-- failingFilterType
- - The type of filter that failed this report
-- bundleSize
- - Length of the bundle JSON string
-- messageId
- - From the bundle.identifier value and system. If ingested as HL7 this comes from MSH-10
-
-
## How to query for events
Events that are pushed to Azure can be found in the `customEvents` table in the log explorer. The properties defined in
@@ -176,24 +67,24 @@ customEvents
### Distinct senders
```
customEvents
-| where name == "ReportAcceptedEvent"
-| extend sender = tostring(customDimensions.sender)
-| distinct sender
+| where name == "REPORT_RECEIVED"
+| extend senderName = tostring(parse_json(tostring(customDimensions.params)).senderName)
+| distinct senderName
```
### Get report count sent by sender
```
customEvents
-| where name == "ReportAcceptedEvent"
-| extend sender = tostring(customDimensions.sender)
-| summarize count() by sender
+| where name == "REPORT_RECEIVED"
+| extend senderName = tostring(parse_json(tostring(customDimensions.params)).senderName)
+| summarize count() by senderName
| order by count_
```
### Get report count sent by topic
```
customEvents
-| where name == "ReportAcceptedEvent"
+| where name == "REPORT_RECEIVED"
| extend topic = tostring(customDimensions.topic)
| summarize count() by topic
| order by count_
@@ -202,12 +93,8 @@ customEvents
### Get reportable conditions count for all reports sent to Report Stream
```
customEvents
-| where name == "ReportAcceptedEvent"
-| extend observations = parse_json(tostring(customDimensions.observations))
-| mv-expand observations
-| extend conditions = parse_json(tostring(observations.conditions))
-| mv-expand conditions
-| extend conditionDisplay = tostring(conditions.display)
+| where name == "ITEM_ACCEPTED"
+| extend conditionDisplay = tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(customDimensions.params)).bundleDigest)).observationSummaries))[0].testSummary))[0].conditions))[0].display)
| summarize count() by conditionDisplay
| order by count_
```
@@ -215,28 +102,25 @@ customEvents
### Distinct receivers
```
customEvents
-| where name == "ReportRouteEvent"
-| extend receiver = tostring(customDimensions.receiver)
-| where receiver != "null"
-| distinct receiver
+| where name == "ITEM_ROUTED"
+| extend receiverName = tostring(parse_json(tostring(customDimensions.params)).receiverName)
+| distinct receiverName
```
### Get report count routed to a receiver
```
customEvents
-| where name == "ReportRouteEvent"
-| extend receiver = tostring(customDimensions.receiver)
-| where receiver != "null"
-| summarize count() by receiver
+| where name == "ITEM_ROUTED"
+| extend receiverName = tostring(parse_json(tostring(customDimensions.params)).receiverName)
+| summarize count() by receiverName
| order by count_
```
### Get report count routed by topic
```
customEvents
-| where name == "ReportRouteEvent"
-| extend topic = tostring(customDimensions.topic), receiver = tostring(customDimensions.receiver)
-| where receiver != "null"
+| where name == "ITEM_ROUTED"
+| extend topic = tostring(customDimensions.topic)
| summarize count() by topic
| order by count_
```
@@ -244,13 +128,8 @@ customEvents
### Get reportable conditions count for all reports routed to receivers
```
customEvents
-| where name == "ReportRouteEvent"
-| extend observations = parse_json(tostring(customDimensions.observations)), receiver = tostring(customDimensions.receiver)
-| where receiver != "null"
-| mv-expand observations
-| extend conditions = parse_json(tostring(observations.conditions))
-| mv-expand conditions
-| extend conditionDisplay = tostring(conditions.display)
+| where name == "ITEM_ROUTED"
+| extend conditionDisplay = tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(customDimensions.params)).bundleDigest)).observationSummaries))[0].testSummary))[0].conditions))[0].display)
| summarize count() by conditionDisplay
| order by count_
```
diff --git a/prime-router/docs/universal-pipeline/destination-filter.md b/prime-router/docs/universal-pipeline/destination-filter.md
index fd9162c4d38..21fbe27e878 100644
--- a/prime-router/docs/universal-pipeline/destination-filter.md
+++ b/prime-router/docs/universal-pipeline/destination-filter.md
@@ -186,10 +186,10 @@ This filter will log messages to the console when:
This step emits one of two events below _once_ each time it runs.
-| Event | Trigger |
-|-------------------------------------------------------------------------------|------------------------------------------------------------------|
-| [ReportAcceptedEvent](../observability/azure-events.md#reportacceptedevent) | when a report is received by this step (precludes all filtering) |
-| [ReportNotRoutedEvent](../observability/azure-events.md#reportnotroutedevent) | when a report is not valid for any receivers |
+| Event | Trigger |
+|-----------------|------------------------------------------------------------------|
+| ITEM_ROUTED | when a report is received by this step (precludes all filtering) |
+| ITEM_NOT_ROUTED | when a report is not valid for any receivers |
## Retries
diff --git a/prime-router/docs/universal-pipeline/receiver-filter.md b/prime-router/docs/universal-pipeline/receiver-filter.md
index 3c0de1e403d..e98957961f0 100644
--- a/prime-router/docs/universal-pipeline/receiver-filter.md
+++ b/prime-router/docs/universal-pipeline/receiver-filter.md
@@ -294,8 +294,7 @@ This step emits one of the below events _per receiver_ each time it runs.
| Event | Trigger |
|-----------------------------------------------------------------------------------------|------------------------------------------------------------|
-| [ReportRouteEvent](../observability/azure-events.md#reportrouteevent) | When a report is routed to a receiver (all filters passed) |
-| [ReceiverFilterFailedEvent](../observability/azure-events.md#receiverfilterfailedevent) | When a report fails receiver filters |
+| ITEM_FILTER_FAILED | When a report fails receiver filters |
## Retries
diff --git a/prime-router/gradle.properties b/prime-router/gradle.properties
index 99176a42c51..2e777795522 100644
--- a/prime-router/gradle.properties
+++ b/prime-router/gradle.properties
@@ -1,3 +1,3 @@
# This setting makes flyway fall back to session locks as concurrent index creation cannot be done
# within a transaction. This setting is needed as of flyway 9.19.4.
-flyway.postgresql.transactional.lock=false
+flyway.postgresql.transactional.lock=false
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/azure/ActionHistory.kt b/prime-router/src/main/kotlin/azure/ActionHistory.kt
index de7a0f0a053..53c139c788b 100644
--- a/prime-router/src/main/kotlin/azure/ActionHistory.kt
+++ b/prime-router/src/main/kotlin/azure/ActionHistory.kt
@@ -6,6 +6,8 @@ import com.microsoft.azure.functions.HttpRequestMessage
import com.microsoft.azure.functions.HttpResponseMessage
import com.microsoft.azure.functions.HttpStatusType
import com.networknt.org.apache.commons.validator.routines.InetAddressValidator
+import fhirengine.engine.CustomFhirPathFunctions
+import gov.cdc.prime.reportstream.shared.BlobUtils
import gov.cdc.prime.router.ActionLog
import gov.cdc.prime.router.ActionLogLevel
import gov.cdc.prime.router.ClientSource
@@ -25,11 +27,16 @@ import gov.cdc.prime.router.azure.db.tables.pojos.ItemLineage
import gov.cdc.prime.router.azure.db.tables.pojos.ReportFile
import gov.cdc.prime.router.azure.db.tables.pojos.ReportLineage
import gov.cdc.prime.router.azure.db.tables.pojos.Task
+import gov.cdc.prime.router.azure.observability.bundleDigest.BundleDigestExtractor
+import gov.cdc.prime.router.azure.observability.bundleDigest.FhirPathBundleDigestLabResultExtractorStrategy
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
import gov.cdc.prime.router.azure.observability.event.ReportStreamEventName
import gov.cdc.prime.router.azure.observability.event.ReportStreamEventProperties
import gov.cdc.prime.router.common.AzureHttpUtils.getSenderIP
import gov.cdc.prime.router.common.JacksonMapperUtilities
+import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext
+import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder
+import gov.cdc.prime.router.report.ReportService
import io.ktor.http.HttpStatusCode
import org.apache.logging.log4j.kotlin.Logging
import org.jooq.impl.SQLDataType
@@ -565,6 +572,7 @@ class ActionHistory(
result: String,
header: WorkflowEngine.Header,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
transportType: String,
) {
if (isReportAlreadyTracked(sentReportId)) {
@@ -616,6 +624,45 @@ class ActionHistory(
)
}
+ val lineages = Report.createItemLineagesFromDb(header, sentReportId)
+ lineages?.forEach { itemLineage ->
+ val receiverFilterReportFile = reportService.getReportForItemAtTask(
+ itemLineage.parentReportId,
+ itemLineage.parentIndex,
+ TaskAction.receiver_filter
+ )
+ if (receiverFilterReportFile != null) {
+ val blob = BlobAccess.downloadBlob(
+ receiverFilterReportFile.bodyUrl,
+ BlobUtils.digestToString(receiverFilterReportFile.blobDigest)
+ )
+ val bundle = FhirTranscoder.decode(blob)
+ val bundleDigestExtractor = BundleDigestExtractor(
+ FhirPathBundleDigestLabResultExtractorStrategy(
+ CustomContext(
+ bundle,
+ bundle,
+ mutableMapOf(),
+ CustomFhirPathFunctions()
+ )
+ )
+ )
+ reportEventService.sendItemEvent(ReportStreamEventName.ITEM_SENT, reportFile, TaskAction.send) {
+ trackingId(bundle)
+ parentReportId(header.reportFile.reportId)
+ childItemIndex(itemLineage.childIndex)
+ params(
+ mapOf(
+ ReportStreamEventProperties.BUNDLE_DIGEST
+ to bundleDigestExtractor.generateDigest(bundle),
+ ReportStreamEventProperties.RECEIVER_NAME to receiver.fullName,
+ )
+ )
+ }
+ } else {
+ logger.error("No translate report found for sent item.")
+ }
+ }
reportsOut[reportFile.reportId] = reportFile
}
diff --git a/prime-router/src/main/kotlin/azure/SendFunction.kt b/prime-router/src/main/kotlin/azure/SendFunction.kt
index dc77ddbe883..e258e641c8e 100644
--- a/prime-router/src/main/kotlin/azure/SendFunction.kt
+++ b/prime-router/src/main/kotlin/azure/SendFunction.kt
@@ -117,7 +117,8 @@ class SendFunction(
retryItems,
context,
actionHistory,
- reportEventService
+ reportEventService,
+ workflowEngine.reportService
)
if (nextRetry != null) {
nextRetryItems += nextRetry
diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReceiverFilterFailedEvent.kt b/prime-router/src/main/kotlin/azure/observability/event/ReceiverFilterFailedEvent.kt
deleted file mode 100644
index 6f915d64bdf..00000000000
--- a/prime-router/src/main/kotlin/azure/observability/event/ReceiverFilterFailedEvent.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package gov.cdc.prime.router.azure.observability.event
-
-import gov.cdc.prime.router.ReportId
-import gov.cdc.prime.router.ReportStreamFilterType
-import gov.cdc.prime.router.Topic
-
-/**
- * Event definition for when a report fails a receiver's filters
- */
-
-data class ReceiverFilterFailedEvent(
- val reportId: ReportId,
- val parentReportId: ReportId,
- val submittedReportId: ReportId,
- val topic: Topic,
- val sender: String,
- val receiver: String,
- val observations: List,
- val failingFilters: List,
- val failingFilterType: ReportStreamFilterType,
- val bundleSize: Int,
- val messageId: AzureEventUtils.MessageID,
-) : AzureCustomEvent
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReportAcceptedEvent.kt b/prime-router/src/main/kotlin/azure/observability/event/ReportAcceptedEvent.kt
deleted file mode 100644
index 0eed89d94aa..00000000000
--- a/prime-router/src/main/kotlin/azure/observability/event/ReportAcceptedEvent.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package gov.cdc.prime.router.azure.observability.event
-
-import gov.cdc.prime.router.ReportId
-import gov.cdc.prime.router.Topic
-
-/**
- * Event definition for when a report is ready to be processed per receiver
- *
- * This event should contain all observations sent by the sender since no
- * receiver specific filters have been run
- */
-data class ReportAcceptedEvent(
- val reportId: ReportId,
- val submittedReportId: ReportId,
- val topic: Topic,
- val sender: String,
- val observations: List,
- val bundleSize: Int,
- val messageId: AzureEventUtils.MessageID,
-) : AzureCustomEvent
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReportCreatedEvent.kt b/prime-router/src/main/kotlin/azure/observability/event/ReportCreatedEvent.kt
deleted file mode 100644
index f6453dad064..00000000000
--- a/prime-router/src/main/kotlin/azure/observability/event/ReportCreatedEvent.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package gov.cdc.prime.router.azure.observability.event
-
-import gov.cdc.prime.router.ReportId
-import gov.cdc.prime.router.Topic
-
-/**
- * An event emitted during every report created
- */
-data class ReportCreatedEvent(
- val reportId: ReportId,
- val topic: Topic,
-) : AzureCustomEvent
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReportNotRoutedEvent.kt b/prime-router/src/main/kotlin/azure/observability/event/ReportNotRoutedEvent.kt
deleted file mode 100644
index 0280e12d912..00000000000
--- a/prime-router/src/main/kotlin/azure/observability/event/ReportNotRoutedEvent.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package gov.cdc.prime.router.azure.observability.event
-
-import gov.cdc.prime.router.ReportId
-import gov.cdc.prime.router.Topic
-
-/**
- * Event definition for when a report does not get routed to any receivers
- */
-
-data class ReportNotRoutedEvent(
- val reportId: ReportId,
- val parentReportId: ReportId,
- val submittedReportId: ReportId,
- val topic: Topic,
- val sender: String,
- val bundleSize: Int,
- val messageId: AzureEventUtils.MessageID,
-) : AzureCustomEvent
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReportRouteEvent.kt b/prime-router/src/main/kotlin/azure/observability/event/ReportRouteEvent.kt
deleted file mode 100644
index 4479c753fa7..00000000000
--- a/prime-router/src/main/kotlin/azure/observability/event/ReportRouteEvent.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package gov.cdc.prime.router.azure.observability.event
-
-import gov.cdc.prime.router.ReportId
-import gov.cdc.prime.router.Topic
-
-/**
- * Event definition for when a report gets routed to a receiver
- */
-data class ReportRouteEvent(
- val reportId: ReportId,
- val parentReportId: ReportId,
- val submittedReportId: ReportId,
- val topic: Topic,
- val sender: String,
- val receiver: String?, // TODO: this should not be nullable anymore after #14450
- val observations: List,
- val filteredObservations: List,
- val bundleSize: Int,
- val messageId: AzureEventUtils.MessageID,
-) : AzureCustomEvent
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventData.kt b/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventData.kt
index b527e8a1466..99556776b23 100644
--- a/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventData.kt
+++ b/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventData.kt
@@ -27,6 +27,7 @@ data class ReportEventData(
val blobUrl: String,
val pipelineStepName: TaskAction,
val timestamp: OffsetDateTime,
+ val commitId: String,
)
/**
@@ -87,6 +88,7 @@ enum class ReportStreamEventName {
ITEM_ROUTED,
REPORT_LAST_MILE_FAILURE,
REPORT_NOT_PROCESSABLE,
+ ITEM_SENT,
}
/**
diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventService.kt b/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventService.kt
index d8c37ac8ddb..42509ce0748 100644
--- a/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventService.kt
+++ b/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventService.kt
@@ -7,6 +7,7 @@ import gov.cdc.prime.router.azure.DatabaseAccess
import gov.cdc.prime.router.azure.db.enums.TaskAction
import gov.cdc.prime.router.azure.db.tables.pojos.ReportFile
import gov.cdc.prime.router.report.ReportService
+import gov.cdc.prime.router.version.Version
import java.time.OffsetDateTime
import java.util.UUID
@@ -412,7 +413,8 @@ class ReportStreamEventService(
topic,
childBodyUrl,
pipelineStepName,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
)
}
diff --git a/prime-router/src/main/kotlin/history/db/ReportGraph.kt b/prime-router/src/main/kotlin/history/db/ReportGraph.kt
index b9c425a07f9..103b9831eb3 100644
--- a/prime-router/src/main/kotlin/history/db/ReportGraph.kt
+++ b/prime-router/src/main/kotlin/history/db/ReportGraph.kt
@@ -19,7 +19,7 @@ import org.jooq.CommonTableExpression
import org.jooq.DSLContext
import org.jooq.Record
import org.jooq.Record1
-import org.jooq.Record2
+import org.jooq.SelectConditionStep
import org.jooq.SelectOnConditionStep
import org.jooq.impl.CustomRecord
import org.jooq.impl.CustomTable
@@ -213,6 +213,25 @@ class ReportGraph(
return descendantReportRecords(txn, cte, searchedForTaskActions).fetchInto(ReportFile::class.java)
}
+ /**
+ * Retrieves ancestor report from a [TaskAction] for a particular item.
+ *
+ * @param txn the transaction to run the DB access under
+ * @param childReportId the reportId to search for ancestors of
+ * @param childIndex the index of the child
+ * @param searchedForTaskAction the task action associated with the desired ancestor report
+ * @return The ancestor report for that particular action
+ */
+ fun getAncestorReport(
+ txn: DataAccessTransaction,
+ childReportId: UUID,
+ childIndex: Int,
+ searchedForTaskAction: TaskAction,
+ ): ReportFile? {
+ val cte = itemAncestorGraphCommonTableExpression(childReportId, childIndex)
+ return ancestorReportRecords(txn, cte, searchedForTaskAction).fetchOneInto(ReportFile::class.java)
+ }
+
/**
* Returns all the metadata rows associated with the passed in [ItemGraphRecord]
*
@@ -421,19 +440,15 @@ class ReportGraph(
*/
fun reportAncestorGraphCommonTableExpression(childReportIds: List) =
DSL.name(lineageCteName).fields(
- PARENT_REPORT_ID_FIELD,
- PATH_FIELD
+ PARENT_REPORT_ID_FIELD
).`as`(
DSL.select(
- REPORT_LINEAGE.PARENT_REPORT_ID,
- REPORT_LINEAGE.CHILD_REPORT_ID.cast(SQLDataType.VARCHAR),
+ REPORT_LINEAGE.PARENT_REPORT_ID
).from(REPORT_LINEAGE)
.where(REPORT_LINEAGE.CHILD_REPORT_ID.`in`(childReportIds))
.unionAll(
DSL.select(
- REPORT_LINEAGE.PARENT_REPORT_ID,
- DSL.field("$lineageCteName.$PATH_FIELD", SQLDataType.VARCHAR)
- .concat(REPORT_LINEAGE.PARENT_REPORT_ID)
+ REPORT_LINEAGE.PARENT_REPORT_ID
)
.from(REPORT_LINEAGE)
.join(DSL.table(DSL.name(lineageCteName)))
@@ -454,7 +469,7 @@ class ReportGraph(
*/
private fun rootReportRecords(
txn: DataAccessTransaction,
- cte: CommonTableExpression>,
+ cte: CommonTableExpression>,
) = DSL.using(txn)
.withRecursive(cte)
.select(REPORT_FILE.asterisk())
@@ -520,4 +535,30 @@ class ReportGraph(
return select
}
+
+ /**
+ * Fetches all ancestor report records in a recursive manner.
+ *
+ * @param txn the data access transaction
+ * @param cte the common table expression for report lineage
+ * @return the descendant report records
+ */
+ private fun ancestorReportRecords(
+ txn: DataAccessTransaction,
+ cte: CommonTableExpression,
+ searchedForTaskAction: TaskAction,
+ ): SelectConditionStep {
+ val select = DSL.using(txn)
+ .withRecursive(cte)
+ .select(REPORT_FILE.asterisk())
+ .distinctOn(REPORT_FILE.REPORT_ID)
+ .from(cte)
+ .join(REPORT_FILE)
+ .on(REPORT_FILE.REPORT_ID.eq(ItemGraphTable.ITEM_GRAPH.PARENT_REPORT_ID))
+ .join(ACTION)
+ .on(ACTION.ACTION_ID.eq(REPORT_FILE.ACTION_ID))
+ .where(ACTION.ACTION_NAME.eq(searchedForTaskAction))
+
+ return select
+ }
}
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/report/ReportService.kt b/prime-router/src/main/kotlin/report/ReportService.kt
index 7fb5481b8ef..1997c5a63f7 100644
--- a/prime-router/src/main/kotlin/report/ReportService.kt
+++ b/prime-router/src/main/kotlin/report/ReportService.kt
@@ -2,6 +2,7 @@ package gov.cdc.prime.router.report
import gov.cdc.prime.router.ReportId
import gov.cdc.prime.router.azure.DatabaseAccess
+import gov.cdc.prime.router.azure.db.enums.TaskAction
import gov.cdc.prime.router.azure.db.tables.pojos.ReportFile
import gov.cdc.prime.router.common.BaseEngine
import gov.cdc.prime.router.history.db.ReportGraph
@@ -47,7 +48,23 @@ class ReportService(
* @return List of ReportFile objects of the root reports
*/
fun getRootReports(childReportId: ReportId): List {
- return reportGraph.getRootReports(childReportId)
+ return reportGraph.getRootReports(childReportId).distinctBy { it.reportId }
+ }
+
+ /**
+ * Accepts a descendant item (report id and index) and finds the ancestor report associated with the
+ * passed [TaskAction]
+ *
+ * @param childReportId the descendant child report
+ * @param childIndex the index of the item
+ * @param task the particular task to find the ancestor report for
+ *
+ * @return the [ReportFile] ancestor at the passed [TaskAction]
+ */
+ fun getReportForItemAtTask(childReportId: ReportId, childIndex: Int, task: TaskAction): ReportFile? {
+ return db.transactReturning { txn ->
+ reportGraph.getAncestorReport(txn, childReportId, childIndex, task)
+ }
}
/**
diff --git a/prime-router/src/main/kotlin/transport/AS2Transport.kt b/prime-router/src/main/kotlin/transport/AS2Transport.kt
index 6817e065307..b79e2f2d444 100644
--- a/prime-router/src/main/kotlin/transport/AS2Transport.kt
+++ b/prime-router/src/main/kotlin/transport/AS2Transport.kt
@@ -22,6 +22,7 @@ import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
import gov.cdc.prime.router.credentials.CredentialHelper
import gov.cdc.prime.router.credentials.CredentialRequestReason
import gov.cdc.prime.router.credentials.UserJksCredential
+import gov.cdc.prime.router.report.ReportService
import org.apache.hc.core5.util.Timeout
import org.apache.http.conn.ConnectTimeoutException
import org.apache.logging.log4j.kotlin.Logging
@@ -50,6 +51,7 @@ class AS2Transport(val metadata: Metadata? = null) : ITransport, Logging {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
// DevNote: This code is similar to the SFTP code in structure
//
@@ -78,6 +80,7 @@ class AS2Transport(val metadata: Metadata? = null) : ITransport, Logging {
msg,
header,
reportEventService,
+ reportService,
this::class.java.simpleName
)
actionHistory.trackItemLineages(Report.createItemLineagesFromDb(header, sentReportId))
diff --git a/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt b/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt
index 0c1a47ddcce..ea372653d59 100644
--- a/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt
+++ b/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt
@@ -10,6 +10,7 @@ import gov.cdc.prime.router.azure.BlobAccess
import gov.cdc.prime.router.azure.WorkflowEngine
import gov.cdc.prime.router.azure.db.enums.TaskAction
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
+import gov.cdc.prime.router.report.ReportService
class BlobStoreTransport : ITransport {
override fun send(
@@ -21,6 +22,7 @@ class BlobStoreTransport : ITransport {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
val blobTransportType = transportType as BlobStoreTransportType
val envVar: String = blobTransportType.containerName
@@ -41,6 +43,7 @@ class BlobStoreTransport : ITransport {
msg,
header,
reportEventService,
+ reportService,
this::class.java.simpleName
)
actionHistory.trackItemLineages(Report.createItemLineagesFromDb(header, sentReportId))
diff --git a/prime-router/src/main/kotlin/transport/EmailTransport.kt b/prime-router/src/main/kotlin/transport/EmailTransport.kt
index c4e152fe49f..a7e75f090ee 100644
--- a/prime-router/src/main/kotlin/transport/EmailTransport.kt
+++ b/prime-router/src/main/kotlin/transport/EmailTransport.kt
@@ -14,6 +14,7 @@ import gov.cdc.prime.router.TransportType
import gov.cdc.prime.router.azure.ActionHistory
import gov.cdc.prime.router.azure.WorkflowEngine
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
+import gov.cdc.prime.router.report.ReportService
import org.thymeleaf.TemplateEngine
import org.thymeleaf.context.Context
import org.thymeleaf.templateresolver.StringTemplateResolver
@@ -33,6 +34,7 @@ class EmailTransport : ITransport {
context: ExecutionContext,
actionHistory: ActionHistory, // not used by emailer
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
val emailTransport = transportType as EmailTransportType
val content = buildContent(header)
diff --git a/prime-router/src/main/kotlin/transport/GAENTransport.kt b/prime-router/src/main/kotlin/transport/GAENTransport.kt
index 1a81e40312e..a5abfbdd829 100644
--- a/prime-router/src/main/kotlin/transport/GAENTransport.kt
+++ b/prime-router/src/main/kotlin/transport/GAENTransport.kt
@@ -20,6 +20,7 @@ import gov.cdc.prime.router.common.HttpClientUtils
import gov.cdc.prime.router.credentials.CredentialHelper
import gov.cdc.prime.router.credentials.CredentialRequestReason
import gov.cdc.prime.router.credentials.UserApiKeyCredential
+import gov.cdc.prime.router.report.ReportService
import io.ktor.client.HttpClient
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
@@ -78,6 +79,7 @@ class GAENTransport(val httpClient: HttpClient? = null) : ITransport, Logging {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
val gaenTransportInfo = transportType as GAENTransportType
val reportId = header.reportFile.reportId
@@ -106,7 +108,7 @@ class GAENTransport(val httpClient: HttpClient? = null) : ITransport, Logging {
// Record the work in history and logs
when (postResult) {
- PostResult.SUCCESS -> recordFullSuccess(params, reportEventService)
+ PostResult.SUCCESS -> recordFullSuccess(params, reportEventService, reportService)
PostResult.RETRY -> recordFailureWithRetry(params)
PostResult.FAIL -> recordFailure(params)
}
@@ -123,7 +125,11 @@ class GAENTransport(val httpClient: HttpClient? = null) : ITransport, Logging {
/**
* Record in [ActionHistory] the full success of this notification. Log an info message as well.
*/
- private fun recordFullSuccess(params: SendParams, reportEventService: IReportStreamEventService) {
+ private fun recordFullSuccess(
+ params: SendParams,
+ reportEventService: IReportStreamEventService,
+ reportService: ReportService,
+ ) {
val msg = "${params.receiver.fullName}: Successful exposure notifications of ${params.comboId}"
val history = params.actionHistory
params.context.logger.info(msg)
@@ -137,6 +143,7 @@ class GAENTransport(val httpClient: HttpClient? = null) : ITransport, Logging {
msg,
params.header,
reportEventService,
+ reportService,
this::class.java.simpleName
)
history.trackItemLineages(Report.createItemLineagesFromDb(params.header, params.sentReportId))
diff --git a/prime-router/src/main/kotlin/transport/ITransport.kt b/prime-router/src/main/kotlin/transport/ITransport.kt
index 3f05de5fd74..169ec7191f1 100644
--- a/prime-router/src/main/kotlin/transport/ITransport.kt
+++ b/prime-router/src/main/kotlin/transport/ITransport.kt
@@ -6,6 +6,7 @@ import gov.cdc.prime.router.TransportType
import gov.cdc.prime.router.azure.ActionHistory
import gov.cdc.prime.router.azure.WorkflowEngine
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
+import gov.cdc.prime.router.report.ReportService
interface ITransport {
/**
@@ -26,5 +27,6 @@ interface ITransport {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems?
}
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/transport/NullTransport.kt b/prime-router/src/main/kotlin/transport/NullTransport.kt
index 9c3f90f867b..2bb329c9acc 100644
--- a/prime-router/src/main/kotlin/transport/NullTransport.kt
+++ b/prime-router/src/main/kotlin/transport/NullTransport.kt
@@ -7,6 +7,7 @@ import gov.cdc.prime.router.TransportType
import gov.cdc.prime.router.azure.ActionHistory
import gov.cdc.prime.router.azure.WorkflowEngine
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
+import gov.cdc.prime.router.report.ReportService
/**
* The Null transport is intended for testing and benchmarking purposes.
@@ -21,6 +22,7 @@ class NullTransport : ITransport {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
if (header.content == null) error("No content for report ${header.reportFile.reportId}")
val receiver = header.receiver ?: error("No receiver defined for report ${header.reportFile.reportId}")
@@ -34,6 +36,7 @@ class NullTransport : ITransport {
msg,
header,
reportEventService,
+ reportService,
this::class.java.simpleName
)
actionHistory.trackItemLineages(Report.createItemLineagesFromDb(header, sentReportId))
diff --git a/prime-router/src/main/kotlin/transport/RESTTransport.kt b/prime-router/src/main/kotlin/transport/RESTTransport.kt
index 6b0758c9ccb..c134ad04e0b 100644
--- a/prime-router/src/main/kotlin/transport/RESTTransport.kt
+++ b/prime-router/src/main/kotlin/transport/RESTTransport.kt
@@ -20,6 +20,7 @@ import gov.cdc.prime.router.credentials.UserApiKeyCredential
import gov.cdc.prime.router.credentials.UserAssertionCredential
import gov.cdc.prime.router.credentials.UserJksCredential
import gov.cdc.prime.router.credentials.UserPassCredential
+import gov.cdc.prime.router.report.ReportService
import gov.cdc.prime.router.tokens.AuthUtils
import io.ktor.client.HttpClient
import io.ktor.client.call.body
@@ -93,6 +94,7 @@ class RESTTransport(private val httpClient: HttpClient? = null) : ITransport {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
val logger: Logger = context.logger
@@ -157,6 +159,7 @@ class RESTTransport(private val httpClient: HttpClient? = null) : ITransport {
msg,
header,
reportEventService,
+ reportService,
this::class.java.simpleName
)
actionHistory.trackItemLineages(Report.createItemLineagesFromDb(header, sentReportId))
diff --git a/prime-router/src/main/kotlin/transport/SftpTransport.kt b/prime-router/src/main/kotlin/transport/SftpTransport.kt
index 9acfb9ea322..266960e2dd9 100644
--- a/prime-router/src/main/kotlin/transport/SftpTransport.kt
+++ b/prime-router/src/main/kotlin/transport/SftpTransport.kt
@@ -19,6 +19,7 @@ import gov.cdc.prime.router.credentials.SftpCredential
import gov.cdc.prime.router.credentials.UserPassCredential
import gov.cdc.prime.router.credentials.UserPemCredential
import gov.cdc.prime.router.credentials.UserPpkCredential
+import gov.cdc.prime.router.report.ReportService
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.sftp.RemoteResourceFilter
@@ -48,6 +49,7 @@ class SftpTransport : ITransport, Logging {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
val sftpTransportType = transportType as SFTPTransportType
@@ -72,6 +74,7 @@ class SftpTransport : ITransport, Logging {
msg,
header,
reportEventService,
+ reportService,
this::class.java.simpleName
)
actionHistory.trackItemLineages(Report.createItemLineagesFromDb(header, sentReportId))
diff --git a/prime-router/src/main/kotlin/transport/SoapTransport.kt b/prime-router/src/main/kotlin/transport/SoapTransport.kt
index aba6e6113d3..ec771d556e5 100644
--- a/prime-router/src/main/kotlin/transport/SoapTransport.kt
+++ b/prime-router/src/main/kotlin/transport/SoapTransport.kt
@@ -15,6 +15,7 @@ import gov.cdc.prime.router.credentials.CredentialHelper
import gov.cdc.prime.router.credentials.CredentialRequestReason
import gov.cdc.prime.router.credentials.SoapCredential
import gov.cdc.prime.router.credentials.UserJksCredential
+import gov.cdc.prime.router.report.ReportService
import gov.cdc.prime.router.serializers.SoapEnvelope
import gov.cdc.prime.router.serializers.SoapObjectService
import io.ktor.client.HttpClient
@@ -154,6 +155,7 @@ class SoapTransport(private val httpClient: HttpClient? = null) : ITransport {
context: ExecutionContext,
actionHistory: ActionHistory,
reportEventService: IReportStreamEventService,
+ reportService: ReportService,
): RetryItems? {
// verify that we have a SOAP transport type for our parameters. I think if we ever fell
// into this scenario with different parameters there's something seriously wrong in the system,
@@ -211,6 +213,7 @@ class SoapTransport(private val httpClient: HttpClient? = null) : ITransport {
msg,
header,
reportEventService,
+ reportService,
this::class.java.simpleName
)
actionHistory.trackItemLineages(Report.createItemLineagesFromDb(header, sentReportId))
diff --git a/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/MT/MT-receiver-transform.yml b/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/MT/MT-receiver-transform.yml
index 1fb4e963ae5..dc2b9295545 100644
--- a/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/MT/MT-receiver-transform.yml
+++ b/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/MT/MT-receiver-transform.yml
@@ -16,3 +16,8 @@ elements:
condition: '%resource.code.exists()'
value: [ '%resource.system.getCodingSystemMapping()' ]
hl7Spec: [ '/PATIENT_RESULT/PATIENT/PID-10-3' ]
+
+ - name: mt-patient-county-codes
+ condition: 'Bundle.entry.resource.ofType(Patient).address.district.empty().not() and Bundle.entry.resource.ofType(Patient).address.state.empty().not()'
+ hl7Spec: [ '/PATIENT_RESULT/PATIENT/PID-11-9' ]
+ value: [ "FIPSCountyLookup(Bundle.entry.resource.ofType(Patient).address.district,Bundle.entry.resource.ofType(Patient).address.state)[0]" ]
diff --git a/prime-router/src/test/kotlin/azure/ActionHistoryTests.kt b/prime-router/src/test/kotlin/azure/ActionHistoryTests.kt
index 1d23f1c0356..c6b6c5389a6 100644
--- a/prime-router/src/test/kotlin/azure/ActionHistoryTests.kt
+++ b/prime-router/src/test/kotlin/azure/ActionHistoryTests.kt
@@ -23,18 +23,27 @@ import gov.cdc.prime.router.Report
import gov.cdc.prime.router.Schema
import gov.cdc.prime.router.Topic
import gov.cdc.prime.router.azure.db.enums.TaskAction
+import gov.cdc.prime.router.azure.db.tables.pojos.ItemLineage
import gov.cdc.prime.router.azure.db.tables.pojos.ReportFile
+import gov.cdc.prime.router.azure.observability.bundleDigest.BundleDigest
+import gov.cdc.prime.router.azure.observability.bundleDigest.BundleDigestExtractor
import gov.cdc.prime.router.azure.observability.event.AzureEventService
import gov.cdc.prime.router.azure.observability.event.ReportEventData
import gov.cdc.prime.router.azure.observability.event.ReportStreamEventService
import gov.cdc.prime.router.common.JacksonMapperUtilities
+import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder
+import gov.cdc.prime.router.report.ReportService
import gov.cdc.prime.router.unittest.UnitTestUtils
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkClass
+import io.mockk.mockkConstructor
import io.mockk.mockkObject
import io.mockk.spyk
+import io.mockk.unmockkAll
import io.mockk.verify
+import org.hl7.fhir.r4.model.Bundle
+import org.junit.jupiter.api.AfterEach
import java.time.OffsetDateTime
import java.util.UUID
import kotlin.test.Test
@@ -43,6 +52,12 @@ import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
class ActionHistoryTests {
+
+ @AfterEach
+ fun afterEach() {
+ unmockkAll()
+ }
+
@Test
fun `test trackActionReceiverInfo`() {
val actionHistory = ActionHistory(TaskAction.translate)
@@ -334,9 +349,14 @@ class ActionHistoryTests {
"http://blobUrl",
"".toByteArray()
)
+ every { BlobAccess.downloadBlob(any(), any()) } returns ""
val mockAzureEventService = mockk()
every { mockAzureEventService.trackEvent(any()) } returns Unit
val mockReportEventService = mockk()
+ val mockReportService = mockk()
+ every {
+ mockReportService.getReportForItemAtTask(any(), any(), any())
+ } returns mockk(relaxed = true)
every {
mockReportEventService.getReportEventData(
any(),
@@ -352,17 +372,37 @@ class ActionHistoryTests {
Topic.TEST,
"http://blobUrl",
TaskAction.send,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ ""
)
every {
mockReportEventService.sendReportEvent(any(), any(), any(), any())
} returns Unit
+ every { mockReportEventService.sendItemEvent(any(), any(), any(), any()) } returns Unit
+ mockkObject(Report)
+ mockkObject(FhirTranscoder)
+ every { FhirTranscoder.decode(any(), any()) } returns mockk()
+ mockkConstructor(BundleDigestExtractor::class)
+ every { anyConstructed().generateDigest(any()) } returns mockk()
val header = mockk()
val inReportFile = mockk()
every { header.reportFile } returns inReportFile
every { header.content } returns "".toByteArray()
every { inReportFile.itemCount } returns 15
every { inReportFile.reportId } returns uuid
+ every { Report.createItemLineagesFromDb(any(), any()) } returns listOf(
+ ItemLineage(
+ 1,
+ header.reportFile.reportId,
+ 1,
+ uuid,
+ 1,
+ "",
+ "",
+ OffsetDateTime.now(),
+ ""
+ )
+ )
val orgReceiver = org.receivers[0]
val actionHistory1 = ActionHistory(TaskAction.receive)
actionHistory1.action
@@ -374,6 +414,7 @@ class ActionHistoryTests {
"result1",
header,
mockReportEventService,
+ mockReportService,
""
)
assertThat(actionHistory1.reportsOut[uuid]).isNotNull()
@@ -393,6 +434,7 @@ class ActionHistoryTests {
assertThat(actionHistory1.action.externalName).isEqualTo("filename1")
verify(exactly = 1) {
mockReportEventService.sendReportEvent(any(), any(), any(), any())
+ mockReportEventService.sendItemEvent(any(), any(), any(), any())
}
// not allowed to track the same report twice.
assertFailure {
@@ -404,6 +446,7 @@ class ActionHistoryTests {
"result1",
header,
mockReportEventService,
+ mockReportService,
""
)
}
@@ -437,6 +480,10 @@ class ActionHistoryTests {
val mockAzureEventService = mockk()
every { mockAzureEventService.trackEvent(any()) } returns Unit
val mockReportEventService = mockk()
+ val mockReportService = mockk()
+ every {
+ mockReportService.getReportForItemAtTask(any(), any(), any())
+ } returns mockk(relaxed = true)
every {
mockReportEventService.getReportEventData(
any(),
@@ -452,7 +499,8 @@ class ActionHistoryTests {
Topic.TEST,
"http://blobUrl",
TaskAction.send,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ ""
)
mockkObject(BlobAccess.Companion)
mockkObject(BlobUtils)
@@ -460,15 +508,35 @@ class ActionHistoryTests {
every { BlobAccess.uploadBlob(capture(blobUrls), any()) } returns "http://blobUrl"
every { BlobUtils.sha256Digest(any()) } returns byteArrayOf()
every { BlobAccess.uploadBody(any(), any(), any(), any(), Event.EventAction.NONE) } answers { callOriginal() }
+ every { BlobAccess.downloadBlob(any(), any()) } returns ""
+ mockkObject(Report)
+ mockkObject(FhirTranscoder)
+ every { FhirTranscoder.decode(any(), any()) } returns mockk()
+ mockkConstructor(BundleDigestExtractor::class)
+ every { anyConstructed().generateDigest(any()) } returns mockk()
val header = mockk()
every {
mockReportEventService.sendReportEvent(any(), any(), any(), any())
} returns Unit
+ every { mockReportEventService.sendItemEvent(any(), any(), any(), any()) } returns Unit
val inReportFile = mockk()
every { header.reportFile } returns inReportFile
every { header.content } returns "".toByteArray()
every { inReportFile.itemCount } returns 15
every { inReportFile.reportId } returns uuid
+ every { Report.createItemLineagesFromDb(any(), any()) } returns listOf(
+ ItemLineage(
+ 1,
+ header.reportFile.reportId,
+ 1,
+ uuid,
+ 1,
+ "",
+ "",
+ OffsetDateTime.now(),
+ ""
+ )
+ )
val actionHistory1 = ActionHistory(TaskAction.receive)
actionHistory1.trackSentReport(
@@ -479,6 +547,7 @@ class ActionHistoryTests {
"result1",
header,
mockReportEventService,
+ mockReportService,
""
)
assertThat(actionHistory1.reportsOut[uuid]).isNotNull()
@@ -495,6 +564,7 @@ class ActionHistoryTests {
"result1",
header,
mockReportEventService,
+ mockReportService,
""
)
assertThat(actionHistory2.reportsOut[uuid]).isNotNull()
@@ -502,6 +572,7 @@ class ActionHistoryTests {
.isEqualTo("STED/NESTED/STLTs/REALLY_LONG_STATE_NAME/REALLY_LONG_STATE_NAME")
verify(exactly = 2) {
mockReportEventService.sendReportEvent(any(), any(), any(), any())
+ mockReportEventService.sendItemEvent(any(), any(), any(), any())
}
}
@@ -677,6 +748,10 @@ class ActionHistoryTests {
val mockAzureEventService = mockk()
every { mockAzureEventService.trackEvent(any()) } returns Unit
val mockReportEventService = mockk()
+ val mockReportService = mockk()
+ every {
+ mockReportService.getReportForItemAtTask(any(), any(), any())
+ } returns mockk(relaxed = true)
every {
mockReportEventService.getReportEventData(
any(),
@@ -692,23 +767,44 @@ class ActionHistoryTests {
Topic.TEST,
"http://blobUrl",
TaskAction.send,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ ""
)
mockkObject(BlobAccess.Companion)
mockkObject(BlobUtils)
+ mockkObject(Report)
val blobUrls = mutableListOf()
every { BlobAccess.uploadBlob(capture(blobUrls), any()) } returns "http://blobUrl"
every { BlobUtils.sha256Digest(any()) } returns byteArrayOf()
every { BlobAccess.uploadBody(any(), any(), any(), any(), Event.EventAction.NONE) } answers { callOriginal() }
+ every { BlobAccess.downloadBlob(any(), any()) } returns ""
+ mockkObject(FhirTranscoder)
+ every { FhirTranscoder.decode(any(), any()) } returns mockk()
+ mockkConstructor(BundleDigestExtractor::class)
+ every { anyConstructed().generateDigest(any()) } returns mockk()
val header = mockk()
every {
mockReportEventService.sendReportEvent(any(), any(), any(), any())
} returns Unit
+ every { mockReportEventService.sendItemEvent(any(), any(), any(), any()) } returns Unit
val inReportFile = mockk()
every { header.reportFile } returns inReportFile
every { header.content } returns "".toByteArray()
every { inReportFile.itemCount } returns 15
every { inReportFile.reportId } returns uuid
+ every { Report.createItemLineagesFromDb(any(), any()) } returns listOf(
+ ItemLineage(
+ 1,
+ header.reportFile.reportId,
+ 1,
+ uuid,
+ 1,
+ "",
+ "",
+ OffsetDateTime.now(),
+ ""
+ )
+ )
val actionHistory1 = ActionHistory(TaskAction.receive)
actionHistory1.action
actionHistory1.trackSentReport(
@@ -719,6 +815,7 @@ class ActionHistoryTests {
"result1",
header,
mockReportEventService,
+ mockReportService,
""
)
assertThat(actionHistory1.reportsOut[uuid]).isNotNull()
@@ -732,6 +829,7 @@ class ActionHistoryTests {
"result1",
header,
mockReportEventService,
+ mockReportService,
""
)
assertThat(actionHistory2.reportsOut[uuid2]).isNotNull()
@@ -740,6 +838,7 @@ class ActionHistoryTests {
assertContains(blobUrls[1], org.receivers[1].fullName)
verify(exactly = 2) {
mockReportEventService.sendReportEvent(any(), any(), any(), any())
+ mockReportEventService.sendItemEvent(any(), any(), any(), any())
}
}
diff --git a/prime-router/src/test/kotlin/azure/SendFunctionTests.kt b/prime-router/src/test/kotlin/azure/SendFunctionTests.kt
index e94e5d0ae87..9c6695494aa 100644
--- a/prime-router/src/test/kotlin/azure/SendFunctionTests.kt
+++ b/prime-router/src/test/kotlin/azure/SendFunctionTests.kt
@@ -116,7 +116,7 @@ class SendFunctionTests {
val header = makeHeader()
nextEvent = block(header, null, null)
}
- every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any()) }.returns(null)
+ every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any(), any(),) }.returns(null)
every { workflowEngine.recordAction(any()) }.returns(Unit)
every { workflowEngine.azureEventService.trackEvent(any()) }.returns(Unit)
every { workflowEngine.reportService.getRootReports(any()) } returns reportList
@@ -148,7 +148,7 @@ class SendFunctionTests {
nextEvent = block(header, null, null)
}
setupWorkflow()
- every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any()) }
+ every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any(), any(),) }
.returns(RetryToken.allItems)
every { workflowEngine.recordAction(any()) }.returns(Unit)
every { workflowEngine.db } returns mockk()
@@ -181,7 +181,7 @@ class SendFunctionTests {
)
}
setupWorkflow()
- every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any()) }
+ every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any(), any(),) }
.returns(RetryToken.allItems)
every { workflowEngine.recordAction(any()) }.returns(Unit)
every { workflowEngine.db } returns mockk()
@@ -218,7 +218,7 @@ class SendFunctionTests {
)
}
setupWorkflow()
- every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any()) }
+ every { sftpTransport.send(any(), any(), any(), any(), any(), any(), any(), any(), any(),) }
.returns(RetryToken.allItems)
every { workflowEngine.recordAction(any()) }.returns(Unit)
every { workflowEngine.db } returns mockk(relaxed = true)
diff --git a/prime-router/src/test/kotlin/azure/observability/context/MDCUtilsTest.kt b/prime-router/src/test/kotlin/azure/observability/context/MDCUtilsTest.kt
index 5c8cc249993..3d51e2ffac2 100644
--- a/prime-router/src/test/kotlin/azure/observability/context/MDCUtilsTest.kt
+++ b/prime-router/src/test/kotlin/azure/observability/context/MDCUtilsTest.kt
@@ -94,7 +94,8 @@ class MDCUtilsTest {
Topic.FULL_ELR,
"",
TaskAction.send,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ ""
),
mapOf(
ReportStreamEventProperties.FILENAME to "filename"
@@ -121,7 +122,8 @@ class MDCUtilsTest {
Topic.FULL_ELR,
"",
TaskAction.send,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ ""
),
ItemEventData(
1,
diff --git a/prime-router/src/test/kotlin/azure/observability/event/ReportEventServiceTest.kt b/prime-router/src/test/kotlin/azure/observability/event/ReportEventServiceTest.kt
index a625b6c6f55..526c409cc1b 100644
--- a/prime-router/src/test/kotlin/azure/observability/event/ReportEventServiceTest.kt
+++ b/prime-router/src/test/kotlin/azure/observability/event/ReportEventServiceTest.kt
@@ -12,6 +12,7 @@ import gov.cdc.prime.router.db.ReportStreamTestDatabaseContainer
import gov.cdc.prime.router.db.ReportStreamTestDatabaseSetupExtension
import gov.cdc.prime.router.history.db.ReportGraph
import gov.cdc.prime.router.report.ReportService
+import gov.cdc.prime.router.version.Version
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.testcontainers.junit.jupiter.Testcontainers
@@ -93,7 +94,8 @@ class ReportEventServiceTest {
Topic.FULL_ELR,
"",
TaskAction.send,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -136,7 +138,8 @@ class ReportEventServiceTest {
Topic.FULL_ELR,
"",
TaskAction.send,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt
index a7e9795e1cf..69b6dbe7182 100644
--- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt
@@ -69,6 +69,7 @@ import gov.cdc.prime.router.history.DetailedActionLog
import gov.cdc.prime.router.metadata.LookupTable
import gov.cdc.prime.router.metadata.ObservationMappingConstants
import gov.cdc.prime.router.unittest.UnitTestUtils
+import gov.cdc.prime.router.version.Version
import io.mockk.every
import io.mockk.mockkConstructor
import io.mockk.mockkObject
@@ -357,7 +358,8 @@ class FHIRConverterIntegrationTests {
Topic.FULL_ELR,
routedReports[1].bodyUrl,
TaskAction.convert,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -529,7 +531,8 @@ class FHIRConverterIntegrationTests {
Topic.FULL_ELR,
routedReports[1].bodyUrl,
TaskAction.convert,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -664,7 +667,8 @@ class FHIRConverterIntegrationTests {
Topic.MARS_OTC_ELR,
"",
TaskAction.convert,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt
index 0d7aa55ebed..76d52f0b353 100644
--- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt
@@ -47,6 +47,7 @@ import gov.cdc.prime.router.history.db.ReportGraph
import gov.cdc.prime.router.metadata.LookupTable
import gov.cdc.prime.router.report.ReportService
import gov.cdc.prime.router.unittest.UnitTestUtils
+import gov.cdc.prime.router.version.Version
import io.mockk.every
import io.mockk.mockkConstructor
import io.mockk.mockkObject
@@ -349,7 +350,8 @@ class FHIRDestinationFilterIntegrationTests : Logging {
Topic.FULL_ELR,
routedReport.bodyUrl,
TaskAction.destination_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -450,7 +452,8 @@ class FHIRDestinationFilterIntegrationTests : Logging {
Topic.FULL_ELR,
"",
TaskAction.destination_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp,
ReportEventData::childReportId
diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt
index 7580e6d67bb..6092e53516f 100644
--- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt
@@ -56,6 +56,7 @@ import gov.cdc.prime.router.metadata.LookupTable
import gov.cdc.prime.router.metadata.ObservationMappingConstants
import gov.cdc.prime.router.report.ReportService
import gov.cdc.prime.router.unittest.UnitTestUtils
+import gov.cdc.prime.router.version.Version
import io.mockk.every
import io.mockk.mockkConstructor
import io.mockk.mockkObject
@@ -388,7 +389,8 @@ class FHIRReceiverFilterIntegrationTests : Logging {
Topic.FULL_ELR,
"",
TaskAction.receiver_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp,
)
@@ -562,7 +564,8 @@ class FHIRReceiverFilterIntegrationTests : Logging {
Topic.FULL_ELR,
"",
TaskAction.receiver_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp,
)
@@ -749,7 +752,8 @@ class FHIRReceiverFilterIntegrationTests : Logging {
Topic.FULL_ELR,
"",
TaskAction.receiver_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp,
)
@@ -880,7 +884,8 @@ class FHIRReceiverFilterIntegrationTests : Logging {
Topic.FULL_ELR,
"",
TaskAction.receiver_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp,
)
@@ -1163,7 +1168,8 @@ class FHIRReceiverFilterIntegrationTests : Logging {
Topic.FULL_ELR,
"",
TaskAction.receiver_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp,
)
diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverIntegrationTests.kt
index 763a1f03a7b..df8c56b8e7b 100644
--- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverIntegrationTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverIntegrationTests.kt
@@ -42,6 +42,7 @@ import gov.cdc.prime.router.fhirengine.engine.FHIRReceiver
import gov.cdc.prime.router.history.DetailedActionLog
import gov.cdc.prime.router.history.DetailedReport
import gov.cdc.prime.router.unittest.UnitTestUtils
+import gov.cdc.prime.router.version.Version
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockkObject
@@ -228,7 +229,8 @@ class FHIRReceiverIntegrationTests {
Topic.FULL_ELR,
receiveBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -254,7 +256,8 @@ class FHIRReceiverIntegrationTests {
Topic.FULL_ELR,
receiveBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -339,7 +342,8 @@ class FHIRReceiverIntegrationTests {
null,
submissionBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -441,7 +445,8 @@ class FHIRReceiverIntegrationTests {
Topic.FULL_ELR,
receiveBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -544,7 +549,8 @@ class FHIRReceiverIntegrationTests {
Topic.FULL_ELR,
receiveBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -649,7 +655,8 @@ class FHIRReceiverIntegrationTests {
Topic.FULL_ELR,
receiveBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -753,7 +760,8 @@ class FHIRReceiverIntegrationTests {
Topic.FULL_ELR,
receiveBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
@@ -854,7 +862,8 @@ class FHIRReceiverIntegrationTests {
null,
receiveBlobUrl,
TaskAction.receive,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
diff --git a/prime-router/src/test/kotlin/fhirengine/engine/FhirDestinationFilterTests.kt b/prime-router/src/test/kotlin/fhirengine/engine/FhirDestinationFilterTests.kt
index cac3f90e718..d6546d3eb8f 100644
--- a/prime-router/src/test/kotlin/fhirengine/engine/FhirDestinationFilterTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/engine/FhirDestinationFilterTests.kt
@@ -39,6 +39,7 @@ import gov.cdc.prime.router.azure.observability.event.TestSummary
import gov.cdc.prime.router.metadata.LookupTable
import gov.cdc.prime.router.report.ReportService
import gov.cdc.prime.router.unittest.UnitTestUtils
+import gov.cdc.prime.router.version.Version
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockk
@@ -338,7 +339,8 @@ class FhirDestinationFilterTests {
Topic.FULL_ELR,
"test",
TaskAction.destination_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp,
)
@@ -502,7 +504,8 @@ class FhirDestinationFilterTests {
Topic.FULL_ELR,
"",
TaskAction.destination_filter,
- OffsetDateTime.now()
+ OffsetDateTime.now(),
+ Version.commitId
),
ReportEventData::timestamp
)
diff --git a/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt
index 2587c0399da..eb8032f034b 100644
--- a/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt
+++ b/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt
@@ -15,6 +15,7 @@ import gov.cdc.prime.router.azure.db.tables.pojos.ReportFile
import gov.cdc.prime.router.azure.db.tables.pojos.Task
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
import gov.cdc.prime.router.credentials.UserJksCredential
+import gov.cdc.prime.router.report.ReportService
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockk
@@ -124,7 +125,8 @@ class AS2TransportIntegrationTests {
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
@@ -150,7 +152,8 @@ class AS2TransportIntegrationTests {
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isSameInstanceAs(RetryToken.allItems)
diff --git a/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt
index 60896850940..c7d7682a6b4 100644
--- a/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt
+++ b/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt
@@ -16,6 +16,7 @@ import gov.cdc.prime.router.azure.db.enums.TaskAction
import gov.cdc.prime.router.azure.db.tables.pojos.Task
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
import gov.cdc.prime.router.credentials.UserApiKeyCredential
+import gov.cdc.prime.router.report.ReportService
import io.ktor.http.HttpStatusCode
import io.mockk.every
import io.mockk.mockk
@@ -143,7 +144,8 @@ class GAENTransportIntegrationTests : TransportIntegrationTests() {
retryItems = null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
@@ -175,7 +177,8 @@ class GAENTransportIntegrationTests : TransportIntegrationTests() {
retryItems = null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(RetryToken.isAllItems(retryItems)).isTrue()
@@ -207,7 +210,8 @@ class GAENTransportIntegrationTests : TransportIntegrationTests() {
retryItems = null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
@@ -240,7 +244,8 @@ class GAENTransportIntegrationTests : TransportIntegrationTests() {
retryItems = null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
diff --git a/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt
index a78b3729a53..918356e5d9d 100644
--- a/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt
+++ b/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt
@@ -18,6 +18,7 @@ import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
import gov.cdc.prime.router.credentials.UserApiKeyCredential
import gov.cdc.prime.router.credentials.UserAssertionCredential
import gov.cdc.prime.router.credentials.UserPassCredential
+import gov.cdc.prime.router.report.ReportService
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
@@ -329,7 +330,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -355,7 +357,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -379,7 +382,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -403,7 +407,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -427,7 +432,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNotNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -451,7 +457,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -475,7 +482,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -499,7 +507,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNotNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -523,7 +532,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNotNull()
assertThat(actionHistory.action.httpStatus).isNotNull()
@@ -544,7 +554,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNotNull()
}
@@ -568,7 +579,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
// RESTTransport is called WITH transport.parameters empty
val retryItems = mockRestTransport.send(
transportType, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
// Then:
@@ -601,7 +613,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
// RESTTransport is called WITH flexionRestTransportType which has transport.parameters
val retryItems = mockRestTransport.send(
flexionRestTransportType, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
// Then:
@@ -628,7 +641,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
val retryItems = mockRestTransport.send(
flexionRestTransportType, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
}
@@ -646,7 +660,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
val retryItems = mockRestTransport.send(
flexionRestTransportTypeWithJwtParams, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
}
@@ -690,7 +705,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
// RESTTransport is called WITH transport.parameters empty
val retryItems = mockRestTransport.send(
nbsRestTransportTypeLive, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
// Then:
@@ -716,7 +732,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
)
val retryItems = mockRestTransport.send(
nbsRestTransportTypeLive, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
}
@@ -752,7 +769,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
// RESTTransport is called WITH transport.parameters empty
val retryItems = mockRestTransport.send(
natusRestTransportTypeLive, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
// Then:
@@ -801,7 +819,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
// RESTTransport is called WITH transport.parameters empty
val retryItems = mockRestTransport.send(
natusRestTransportTypeLiveEncrypt, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
@@ -836,7 +855,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// Then:
@@ -869,7 +889,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
)
val retryItems = mockRestTransport.send(
natusRestTransportTypeLive, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
}
@@ -922,7 +943,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
val retryItems = mockRestTransport.send(
okRestTransportTypeLive, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNull()
}
@@ -969,7 +991,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
// RESTTransport is called WITH transport.parameters empty
val retryItems = mockRestTransport.send(
epicRestTransportTypeLive, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
// Then:
@@ -1014,7 +1037,8 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A==
// RESTTransport is called WITH flexionRestTransportType which has transport.parameters
val retryItems = mockRestTransport.send(
oracleRlNRestTransport, header, reportId, "test", null,
- context, actionHistory, mockk(relaxed = true)
+ context, actionHistory, mockk(relaxed = true),
+ mockk(relaxed = true)
)
// Then:
diff --git a/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt
index 542fe512e1c..92cfbb6b48d 100644
--- a/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt
+++ b/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt
@@ -175,7 +175,8 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
f.actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// successful SFTP upload
@@ -216,7 +217,8 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
f.actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// successful SFTP upload
@@ -257,7 +259,8 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
f.actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// successful SFTP upload
@@ -281,7 +284,8 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
f.actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// asserts that the initial null check works
@@ -312,7 +316,8 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
f.actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// asserts that missing credentials will fail SFTP
@@ -351,7 +356,8 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
f.actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// asserts that authentication error will result in error
@@ -383,7 +389,8 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
f.actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
// asserts that invalid credential types will result in error
diff --git a/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt
index a947cb66ae3..36bbf5221ed 100644
--- a/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt
+++ b/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt
@@ -14,6 +14,7 @@ import gov.cdc.prime.router.azure.db.enums.TaskAction
import gov.cdc.prime.router.azure.db.tables.pojos.Task
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
import gov.cdc.prime.router.credentials.UserPassCredential
+import gov.cdc.prime.router.report.ReportService
import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.respond
@@ -121,7 +122,7 @@ class SoapTransportIntegrationTests : TransportIntegrationTests() {
)
val retryItems = mockSoapTransport.send(
transportType, header, reportId, "test", null, context, actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true), mockk(relaxed = true)
)
assertThat(retryItems).isNull()
}
@@ -141,7 +142,8 @@ class SoapTransportIntegrationTests : TransportIntegrationTests() {
null,
context,
actionHistory,
- mockk(relaxed = true)
+ mockk(relaxed = true),
+ mockk(relaxed = true)
)
assertThat(retryItems).isNotNull()
}
diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts
index c166e033acd..d372ead6770 100644
--- a/shared/build.gradle.kts
+++ b/shared/build.gradle.kts
@@ -12,10 +12,10 @@ repositories {
}
dependencies {
- implementation("org.apache.commons:commons-lang3:3.15.0")
+ implementation("org.apache.commons:commons-lang3:3.17.0")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.28.1")
- testImplementation("org.apache.commons:commons-compress:1.26.2")
+ testImplementation("org.apache.commons:commons-compress:1.27.1")
}
tasks.test {
diff --git a/submissions/build.gradle.kts b/submissions/build.gradle.kts
index 4f3f4c9294e..8c96e222de5 100644
--- a/submissions/build.gradle.kts
+++ b/submissions/build.gradle.kts
@@ -1,10 +1,10 @@
apply(from = rootProject.file("buildSrc/shared.gradle.kts"))
plugins {
- id("org.springframework.boot") version "3.3.2"
+ id("org.springframework.boot") version "3.3.4"
id("io.spring.dependency-management") version "1.1.6"
id("reportstream.project-conventions")
- kotlin("plugin.spring") version "2.0.0"
+ kotlin("plugin.spring") version "2.0.20"
}
group = "gov.cdc.prime"
@@ -29,9 +29,10 @@ dependencies {
testImplementation("org.xmlunit:xmlunit-core:2.10.0")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.4.0")
- testImplementation("org.apache.commons:commons-compress:1.26.2")
+ testImplementation("org.apache.commons:commons-compress:1.27.1")
+ testImplementation("org.springframework.security:spring-security-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.8.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.9.0")
implementation(project(":shared"))
}
diff --git a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/SubmissionReceivedEvent.kt b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/SubmissionReceivedEvent.kt
index 61c348c0aa6..b82c0993a31 100644
--- a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/SubmissionReceivedEvent.kt
+++ b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/SubmissionReceivedEvent.kt
@@ -8,9 +8,17 @@ data class SubmissionReceivedEvent(
val reportId: UUID,
val parentReportId: UUID,
val rootReportId: UUID,
- val headers: Map,
- val sender: String,
- val senderIP: String,
- val fileSize: String,
+ val requestParameters: SubmissionDetails,
+ val method: String,
+ val url: String,
+ val senderName: String,
+ val senderIp: String,
+ val fileLength: String,
val blobUrl: String,
+ val pipelineStepName: String,
+)
+
+data class SubmissionDetails(
+ val headers: Map,
+ val queryParameters: Map>,
)
\ No newline at end of file
diff --git a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AllowedParametersConfig.kt b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AllowedParametersConfig.kt
new file mode 100644
index 00000000000..d3dab4cdccf
--- /dev/null
+++ b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AllowedParametersConfig.kt
@@ -0,0 +1,41 @@
+package gov.cdc.prime.reportstream.submissions.config
+
+import org.springframework.boot.context.properties.ConfigurationProperties
+import org.springframework.stereotype.Component
+
+/**
+ * Configuration class to load allowed headers and query parameters from the application properties.
+ *
+ * This class is used to read properties prefixed with "allowed" from the `application.properties` or `application.yml`
+ * file and bind them to the `headers` and `queryParameters` fields.
+ *
+ * Example of properties in the `application.properties` file:
+ *
+ * ```
+ * allowed.headers.client_id=client_id
+ * allowed.headers.content_type=content-type
+ * allowed.queryParameters.processing=processing
+ * allowed.queryParameters.another_param=another
+ * ```
+ *
+ * These properties will be automatically injected into the `headers` and `queryParameters` lists when the
+ * Spring application context is initialized.
+ *
+ * @property headers A list of allowed HTTP header names that are expected in incoming requests.
+ * @property queryParameters A list of allowed query parameter names that can be used in incoming requests.
+ */
+@Component
+@ConfigurationProperties(prefix = "allowed")
+class AllowedParametersConfig {
+ /**
+ * A list of allowed HTTP headers that can be accepted by the API.
+ * Each entry in the list represents a header name expected in the incoming request.
+ */
+ var headers: List = emptyList()
+
+ /**
+ * A list of allowed query parameters that the API can accept in incoming requests.
+ * Each entry in the list represents a query parameter name expected in the incoming request.
+ */
+ var queryParameters: List = emptyList()
+}
\ No newline at end of file
diff --git a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/SubmissionController.kt b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/SubmissionController.kt
index c92e98354eb..cec852bc193 100644
--- a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/SubmissionController.kt
+++ b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/SubmissionController.kt
@@ -8,8 +8,11 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import gov.cdc.prime.reportstream.shared.BlobUtils
import gov.cdc.prime.reportstream.shared.QueueMessage
import gov.cdc.prime.reportstream.shared.Submission
+import gov.cdc.prime.reportstream.submissions.SubmissionDetails
import gov.cdc.prime.reportstream.submissions.SubmissionReceivedEvent
import gov.cdc.prime.reportstream.submissions.TelemetryService
+import gov.cdc.prime.reportstream.submissions.config.AllowedParametersConfig
+import jakarta.servlet.http.HttpServletRequest
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
@@ -42,6 +45,7 @@ class SubmissionController(
private val queueClient: QueueClient,
private val tableClient: TableClient,
private val telemetryService: TelemetryService,
+ private val allowedParametersConfig: AllowedParametersConfig,
) {
/**
* Submits a report.
@@ -54,6 +58,7 @@ class SubmissionController(
* @param contentType the content type of the report (must be "application/hl7-v2" or "application/fhir+ndjson")
* @param clientId the ID of the client submitting the report. Should represent org.senderName
* @param data the report data
+ * @param request gives access to request details
* @return a ResponseEntity containing the reportID, status, and timestamp
*/
@PostMapping("/api/v1/reports", consumes = ["application/hl7-v2", "application/fhir+ndjson"])
@@ -65,12 +70,19 @@ class SubmissionController(
@RequestHeader("x-azure-clientip") senderIp: String,
@RequestHeader(value = "payloadName", required = false) payloadName: String?,
@RequestBody data: String,
+ request: HttpServletRequest,
): ResponseEntity<*> {
val reportId = UUID.randomUUID()
val reportReceivedTime = Instant.now()
val contentTypeMime = contentType.substringBefore(';')
val status = "Received"
val objectMapper = jacksonObjectMapper().registerModule(JavaTimeModule())
+ // Filter request headers based on the allowed list
+ val filteredHeaders = filterHeaders(headers)
+
+ // Filter query parameters based on the allowed list (only keep 'processing' or others defined in application.properties)
+ val filteredQueryParameters = filterQueryParameters(request)
+
logger.info(
"Received report submission: reportId=$reportId, contentType=$contentTypeMime" +
", clientId=$clientId${payloadName?.let { ", payloadName=$it" } ?: ""}}"
@@ -98,11 +110,17 @@ class SubmissionController(
reportId = reportId,
parentReportId = reportId,
rootReportId = reportId,
- headers = filterHeaders(headers),
- sender = clientId,
- senderIP = senderIp,
- fileSize = contentLength,
- blobUrl = blobClient.blobUrl
+ requestParameters = SubmissionDetails(
+ filteredHeaders,
+ filteredQueryParameters
+ ),
+ method = request.method,
+ url = request.requestURL.toString(),
+ senderName = clientId,
+ senderIp = senderIp,
+ fileLength = contentLength,
+ blobUrl = blobClient.blobUrl,
+ pipelineStepName = "submission"
)
logger.debug("Created SUBMISSION_RECEIVED")
@@ -121,7 +139,7 @@ class SubmissionController(
BlobUtils.digestToString(digest),
clientId.lowercase(),
reportId,
- filterHeaders(headers).toMap(),
+ filterHeaders(headers),
).serialize()
logger.debug("Created message for queue")
@@ -231,10 +249,38 @@ class SubmissionController(
}
}
+ /**
+ * Filters the request headers based on the allowed headers configured in the application.yml.
+ * Handles the case where allowed headers are defined as a list.
+ */
private fun filterHeaders(headers: Map): Map {
- val headersToInclude =
- listOf("client_id", "content-type", "payloadname", "x-azure-clientip", "content-length")
- return headers.filter { it.key.lowercase() in headersToInclude }
+ val allowedHeaders = allowedParametersConfig.headers
+
+ // Filter the request headers to only include allowed headers
+ return headers.filterKeys { key ->
+ allowedHeaders.map { it.lowercase() }.contains(key.lowercase())
+ }
+ }
+
+ /**
+ * Filters the query parameters based on the allowed query parameters configured in the application.yml.
+ * Handles multiple values for the same query parameter from HttpServletRequest.
+ */
+ private fun filterQueryParameters(request: HttpServletRequest): Map> {
+ val allowedQueryParams = allowedParametersConfig.queryParameters
+
+ // Create a map to hold the filtered query parameters
+ val filteredParams = mutableMapOf>()
+
+ // Loop over allowed parameters and get their values from the request
+ allowedQueryParams.forEach { paramName ->
+ val values = request.getParameterValues(paramName)
+ if (values != null) {
+ filteredParams[paramName] = values.toList() // Convert array to List
+ }
+ }
+
+ return filteredParams
}
private fun formBlobName(
diff --git a/submissions/src/main/resources/application.properties b/submissions/src/main/resources/application.properties
deleted file mode 100644
index a8750014f5d..00000000000
--- a/submissions/src/main/resources/application.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-spring.application.name=submissions
-server.port=8880
-azure.storage.connection-string=${AZURE_STORAGE_CONNECTION_STRING:DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;QueueEndpoint=http://localhost:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;}
-azure.storage.container-name=${AZURE_STORAGE_CONTAINER_NAME:reports}
-azure.storage.queue-name=${AZURE_STORAGE_QUEUE_NAME:elr-fhir-receive}
-azure.storage.table-name=${AZURE_STORAGE_TABLE_NAME:submission}
-spring.security.oauth2.resourceserver.jwt.issuer-uri=https://reportstream.oktapreview.com/oauth2/ausekaai7gUuUtHda1d7
\ No newline at end of file
diff --git a/submissions/src/main/resources/application.yml b/submissions/src/main/resources/application.yml
new file mode 100644
index 00000000000..c75d070b3fa
--- /dev/null
+++ b/submissions/src/main/resources/application.yml
@@ -0,0 +1,27 @@
+spring:
+ application:
+ name: submissions
+ security:
+ oauth2:
+ resourceserver:
+ jwt:
+ issuer-uri: https://reportstream.oktapreview.com/oauth2/ausekaai7gUuUtHda1d7
+ server:
+ port: 8880
+
+azure:
+ storage:
+ connection-string: ${AZURE_STORAGE_CONNECTION_STRING:DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;QueueEndpoint=http://localhost:10001/devstoreaccount1;TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;}
+ container-name: ${AZURE_STORAGE_CONTAINER_NAME:reports}
+ queue-name: ${AZURE_STORAGE_QUEUE_NAME:elr-fhir-receive}
+ table-name: ${AZURE_STORAGE_TABLE_NAME:submission}
+
+allowed:
+ headers:
+ - client_id
+ - content-type
+ - payloadname
+ - x-azure-clientip
+ - content-length
+# - queryParameters:
+# - param
\ No newline at end of file
diff --git a/submissions/src/test/kotlin/SubmissionControllerIntegrationTest.kt b/submissions/src/test/kotlin/SubmissionControllerIntegrationTest.kt
index 50255bb05e6..dbfcd9934dd 100644
--- a/submissions/src/test/kotlin/SubmissionControllerIntegrationTest.kt
+++ b/submissions/src/test/kotlin/SubmissionControllerIntegrationTest.kt
@@ -10,6 +10,8 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import gov.cdc.prime.reportstream.shared.QueueMessage
import gov.cdc.prime.reportstream.shared.QueueMessage.ObjectMapperProvider
+import gov.cdc.prime.reportstream.submissions.config.AzureConfig
+import gov.cdc.prime.reportstream.submissions.config.SecurityConfig
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
@@ -17,7 +19,9 @@ import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.context.annotation.Import
import org.springframework.http.MediaType
+import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
@@ -31,6 +35,7 @@ import java.util.Base64
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureMockMvc
+@Import(AzureConfig::class, SecurityConfig::class)
class SubmissionControllerIntegrationTest {
@Autowired
@@ -92,6 +97,7 @@ class SubmissionControllerIntegrationTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.valueOf("application/hl7-v2"))
.header("client_id", "testClient")
diff --git a/submissions/src/test/kotlin/SubmissionControllerTest.kt b/submissions/src/test/kotlin/SubmissionControllerTest.kt
index ab1dfd84120..2c552872834 100644
--- a/submissions/src/test/kotlin/SubmissionControllerTest.kt
+++ b/submissions/src/test/kotlin/SubmissionControllerTest.kt
@@ -11,7 +11,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import gov.cdc.prime.reportstream.shared.QueueMessage
import gov.cdc.prime.reportstream.shared.QueueMessage.ObjectMapperProvider
import gov.cdc.prime.reportstream.submissions.TelemetryService
+import gov.cdc.prime.reportstream.submissions.config.AllowedParametersConfig
import gov.cdc.prime.reportstream.submissions.config.AzureConfig
+import gov.cdc.prime.reportstream.submissions.config.SecurityConfig
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -33,6 +35,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.context.annotation.Import
import org.springframework.http.MediaType
+import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
@@ -42,7 +45,7 @@ import java.util.Base64
import java.util.UUID
@WebMvcTest(SubmissionController::class)
-@Import(AzureConfig::class)
+@Import(AzureConfig::class, SecurityConfig::class, AllowedParametersConfig::class)
class SubmissionControllerTest {
@Autowired
@@ -126,6 +129,7 @@ class SubmissionControllerTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.valueOf("application/hl7-v2"))
.header("client_id", "testClient")
@@ -172,6 +176,7 @@ class SubmissionControllerTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.valueOf("application/fhir+ndjson"))
.header("client_id", "testClient")
@@ -203,6 +208,7 @@ class SubmissionControllerTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.APPLICATION_JSON)
.header("client_id", "testClient")
@@ -219,6 +225,7 @@ class SubmissionControllerTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.valueOf("application/hl7-v2"))
.header("payloadname", "testPayload")
@@ -241,6 +248,7 @@ class SubmissionControllerTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.parseMediaType("application/hl7-v2"))
.header("client_id", "testClient")
@@ -263,6 +271,7 @@ class SubmissionControllerTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.parseMediaType("application/hl7-v2"))
.header("client_id", "testClient")
@@ -301,11 +310,14 @@ class SubmissionControllerTest {
mockMvc.perform(
MockMvcRequestBuilders.post("/api/v1/reports")
+ .with(csrf())
.content(requestBody)
.contentType(MediaType.valueOf("application/hl7-v2"))
.header("client_id", "testClient")
.header("payloadname", "testPayload")
.header("x-azure-clientip", "127.0.0.1")
+ .queryParam("processing", "test1", "test2")
+ .queryParam("test", "test2")
)
.andExpect(MockMvcResultMatchers.status().isCreated)
@@ -321,12 +333,19 @@ class SubmissionControllerTest {
val eventDetails = objectMapper.readValue(capturedProperties["event"], Map::class.java)
assert(eventDetails["reportId"] == reportId.toString())
assert(eventDetails["blobUrl"] == expectedBlobUrl)
- assert(eventDetails["senderIP"] == "127.0.0.1")
- val headers = eventDetails["headers"] as Map<*, *>
+ assert(eventDetails["senderIp"] == "127.0.0.1")
+ assert(eventDetails["method"] == "POST")
+ assert(eventDetails["senderName"] == "testClient")
+ assert(eventDetails["pipelineStepName"] == "submission")
+ assert(eventDetails["url"] == "http://localhost/api/v1/reports")
+ val requestParameters = eventDetails["requestParameters"] as Map<*, *>
+ val headers = requestParameters["headers"] as Map<*, *>
assert(headers["client_id"] == "testClient")
assert(headers["Content-Type"] == "application/hl7-v2;charset=UTF-8")
assert(headers["payloadname"] == "testPayload")
assert(headers["x-azure-clientip"] == "127.0.0.1")
+ val queryParameters = requestParameters["queryParameters"] as Map<*, *>
+ assert(queryParameters.isEmpty())
uuidMockedStatic.close()
}