diff --git a/.github/ISSUE_TEMPLATE/devops_request.md b/.github/ISSUE_TEMPLATE/devops_request.md deleted file mode 100644 index c6c6a0115c4..00000000000 --- a/.github/ISSUE_TEMPLATE/devops_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: DevOps request -about: Request infrastructure from PRIME DevOps -title: '' -labels: DevOps -assignees: '' - ---- - -**Description** -A clear and concise description of the infrastructure you are requesting - -**Specific Requested resources** -A list of exact resources needed and recommended names - -**Existing Resources?** -Are there any existing resources that we will need to delete or modify for this? - -**Additional Context** -Any additional necessary context? diff --git a/.github/workflows/log_management.yml b/.github/workflows/log_management.yml index 8774b3dadda..5253b8dfb88 100644 --- a/.github/workflows/log_management.yml +++ b/.github/workflows/log_management.yml @@ -13,8 +13,7 @@ jobs: - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - name: Workflow Housekeeper - workflows NOT in default branch - uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 - ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/workflow-housekeeper + uses: ./.github/actions/workflow-housekeeper env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} @@ -25,8 +24,7 @@ jobs: dry-run: false - name: Workflow Housekeeper - workflows in default branch - uses: JosiahSiegel/workflow-housekeeper@731cc20bb613208b34efb6ac74aab4ba147abb50 - ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/workflow-housekeeper + uses: ./.github/actions/workflow-housekeeper env: GITHUB_TOKEN: ${{ secrets.LOG_MANAGEMENT_TOKEN }} diff --git a/.github/workflows/prepare_deployment_branch.yaml b/.github/workflows/prepare_deployment_branch.yaml index 01c35749047..9b23ba63805 100644 --- a/.github/workflows/prepare_deployment_branch.yaml +++ b/.github/workflows/prepare_deployment_branch.yaml @@ -28,8 +28,7 @@ jobs: - name: "Create branch '${{ env.BRANCH_NAME }}' to contain the changes for the deployment on ${{ env.DEPLOYMENT_DATE }}" - uses: JosiahSiegel/remote-branch-action@dbe7a2138eb064fbfdb980abee918091a7501fbe - ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/remote-branch-action + uses: ./.github/actions/remote-branch with: branch: "${{ env.BRANCH_NAME }}" @@ -37,8 +36,7 @@ jobs: - name: "Prepare a Pull Request from ${{ env.BRANCH_NAME }} into production branch" id: pr - uses: JosiahSiegel/reliable-pull-request-action@ae8d0c88126329ee363a35392793d0bc94cb82e7 - ## DevSecOps - Aquia (Replace) - uses: ./.github/actions/reliable-pull-request-action + uses: ./.github/actions/reliable-pull-request env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/client/OktaGroupsClient.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/client/OktaGroupsClient.kt index 84f9b1530db..3fa92c8adb8 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/client/OktaGroupsClient.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/client/OktaGroupsClient.kt @@ -7,9 +7,7 @@ import org.apache.logging.log4j.kotlin.Logging import org.springframework.stereotype.Service @Service -class OktaGroupsClient( - private val applicationGroupsApi: ApplicationGroupsApi, -) : Logging { +class OktaGroupsClient(private val applicationGroupsApi: ApplicationGroupsApi) : Logging { /** * Get all application groups from the Okta Admin API @@ -18,8 +16,7 @@ class OktaGroupsClient( * * @see https://developer.okta.com/docs/api/openapi/okta-management/management/tag/ApplicationGroups/#tag/ApplicationGroups/operation/listApplicationGroupAssignments */ - suspend fun getApplicationGroups(appId: String): List { - return withContext(Dispatchers.IO) { + suspend fun getApplicationGroups(appId: String): List = withContext(Dispatchers.IO) { try { val groups = applicationGroupsApi .listApplicationGroupAssignments(appId, null, null, null, "group") @@ -33,5 +30,4 @@ class OktaGroupsClient( throw ex } } - } } \ No newline at end of file diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/ApplicationConfig.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/ApplicationConfig.kt index a55c87d6184..f9a098d5407 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/ApplicationConfig.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/ApplicationConfig.kt @@ -10,17 +10,11 @@ import kotlin.time.TimeSource * Simple class to automatically read configuration from application.yml (or environment variable overrides) */ @ConfigurationProperties(prefix = "app") -data class ApplicationConfig( - val environment: Environment, -) { +data class ApplicationConfig(val environment: Environment) { @Bean - fun timeSource(): TimeSource { - return TimeSource.Monotonic - } + fun timeSource(): TimeSource = TimeSource.Monotonic @Bean - fun clock(): Clock { - return Clock.systemUTC() - } + fun clock(): Clock = Clock.systemUTC() } \ No newline at end of file diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/OktaClientConfig.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/OktaClientConfig.kt index 66085dbb9c7..4f9531ae392 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/OktaClientConfig.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/OktaClientConfig.kt @@ -12,13 +12,10 @@ import org.springframework.context.annotation.Profile @Configuration @Profile("!test") -class OktaClientConfig( - private val oktaClientProperties: OktaClientProperties, -) { +class OktaClientConfig(private val oktaClientProperties: OktaClientProperties) { @Bean - fun apiClient(): ApiClient { - return Clients.builder() + fun apiClient(): ApiClient = Clients.builder() .setOrgUrl(oktaClientProperties.orgUrl) .setAuthorizationMode(AuthorizationMode.PRIVATE_KEY) .setClientId(oktaClientProperties.clientId) @@ -26,12 +23,9 @@ class OktaClientConfig( .setPrivateKey(oktaClientProperties.apiPrivateKey) // .setCacheManager(...) TODO: investigate caching since groups don't often change .build() - } @Bean - fun applicationGroupsApi(): ApplicationGroupsApi { - return ApplicationGroupsApi(apiClient()) - } + fun applicationGroupsApi(): ApplicationGroupsApi = ApplicationGroupsApi(apiClient()) @ConfigurationProperties(prefix = "okta.admin-client") data class OktaClientProperties( diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/SecurityConfig.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/SecurityConfig.kt index 5e3a97f12dc..63d77b71726 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/SecurityConfig.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/config/SecurityConfig.kt @@ -16,9 +16,7 @@ import org.springframework.security.web.server.SecurityWebFilterChain */ @Configuration @EnableWebFluxSecurity -class SecurityConfig( - private val applicationConfig: ApplicationConfig, -) : Logging { +class SecurityConfig(private val applicationConfig: ApplicationConfig) : Logging { @Bean fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthController.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthController.kt index 98979e5f801..4413e6553b6 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthController.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthController.kt @@ -8,9 +8,7 @@ import org.springframework.web.bind.annotation.RestController import kotlin.time.TimeSource @RestController -class HealthController( - timeSource: TimeSource, -) { +class HealthController(timeSource: TimeSource) { private val applicationStart = timeSource.markNow() diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactory.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactory.kt index e618da63f76..35a516cc60e 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactory.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactory.kt @@ -15,19 +15,15 @@ import reactor.core.publisher.Mono * defined in spring-cloud-gateway and is instantiated via configuration under a route's filters. */ @Component -class AppendOktaGroupsGatewayFilterFactory( - private val oktaGroupsService: OktaGroupsService, -) : AbstractGatewayFilterFactory() { +class AppendOktaGroupsGatewayFilterFactory(private val oktaGroupsService: OktaGroupsService) : + AbstractGatewayFilterFactory() { /** * function used only in testing to create our filter without any configuration */ - fun apply(): GatewayFilter { - return apply { _: Any? -> } - } + fun apply(): GatewayFilter = apply { _: Any? -> } - override fun apply(config: Any?): GatewayFilter { - return GatewayFilter { exchange, chain -> + override fun apply(config: Any?): GatewayFilter = GatewayFilter { exchange, chain -> exchange .getPrincipal() .flatMap { oktaAccessTokenJWT -> @@ -57,5 +53,4 @@ class AppendOktaGroupsGatewayFilterFactory( chain.filter(exchange.mutate().request(request).build()) } } - } } \ No newline at end of file diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/model/ApplicationStatus.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/model/ApplicationStatus.kt index da9a90b2fa0..26022f9b6f9 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/model/ApplicationStatus.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/model/ApplicationStatus.kt @@ -3,8 +3,4 @@ package gov.cdc.prime.reportstream.auth.model /** * Simple json response model for application status */ -data class ApplicationStatus( - val application: String, - val status: String, - val uptime: String, -) \ No newline at end of file +data class ApplicationStatus(val application: String, val status: String, val uptime: String) \ No newline at end of file diff --git a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/service/OktaGroupsJWTWriter.kt b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/service/OktaGroupsJWTWriter.kt index 0533882ed21..65c47dd7cd6 100644 --- a/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/service/OktaGroupsJWTWriter.kt +++ b/auth/src/main/kotlin/gov/cdc/prime/reportstream/auth/service/OktaGroupsJWTWriter.kt @@ -17,10 +17,7 @@ import java.util.Date import java.util.UUID @Service -class OktaGroupsJWTWriter( - private val jwtConfig: OktaGroupsJWTConfig, - private val clock: Clock, -) { +class OktaGroupsJWTWriter(private val jwtConfig: OktaGroupsJWTConfig, private val clock: Clock) { /** * generate and sign our custom JWT containing Okta group information for a particular application diff --git a/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/config/TestOktaClientConfig.kt b/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/config/TestOktaClientConfig.kt index f5446568763..fc04e99e25e 100644 --- a/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/config/TestOktaClientConfig.kt +++ b/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/config/TestOktaClientConfig.kt @@ -15,12 +15,8 @@ import org.springframework.context.annotation.Profile class TestOktaClientConfig { @Bean - fun apiClient(): ApiClient { - return mockk() - } + fun apiClient(): ApiClient = mockk() @Bean - fun applicationGroupsApi(): ApplicationGroupsApi { - return mockk() - } + fun applicationGroupsApi(): ApplicationGroupsApi = mockk() } \ No newline at end of file diff --git a/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthControllerTest.kt b/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthControllerTest.kt index 46fef5e71b0..79bb8825b27 100644 --- a/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthControllerTest.kt +++ b/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/controller/HealthControllerTest.kt @@ -16,9 +16,7 @@ import kotlin.test.Test @SpringBootTest @AutoConfigureWebTestClient @Import(TestOktaClientConfig::class) -class HealthControllerTest @Autowired constructor( - private val webTestClient: WebTestClient, -) { +class HealthControllerTest @Autowired constructor(private val webTestClient: WebTestClient) { @Test fun `successful healthcheck`() { diff --git a/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactoryTest.kt b/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactoryTest.kt index 384ed4852ae..6b2e62503b6 100644 --- a/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactoryTest.kt +++ b/auth/src/test/kotlin/gov/cdc/prime/reportstream/auth/filter/AppendOktaGroupsGatewayFilterFactoryTest.kt @@ -42,19 +42,14 @@ class AppendOktaGroupsGatewayFilterFactoryTest @Autowired constructor( ) { @TestConfiguration - class Config( - @Value("\${wiremock.server.port}") val port: Int, - ) { + class Config(@Value("\${wiremock.server.port}") val port: Int) { @Bean - fun oktaGroupsService(): OktaGroupsService { - return mockk() - } + fun oktaGroupsService(): OktaGroupsService = mockk() @Bean - fun appendOktaGroupsGatewayFilterFactory(): AppendOktaGroupsGatewayFilterFactory { - return AppendOktaGroupsGatewayFilterFactory(oktaGroupsService()) - } + fun appendOktaGroupsGatewayFilterFactory(): + AppendOktaGroupsGatewayFilterFactory = AppendOktaGroupsGatewayFilterFactory(oktaGroupsService()) @Bean fun testRouteLocator(builder: RouteLocatorBuilder): RouteLocator { diff --git a/buildSrc/shared.gradle.kts b/buildSrc/shared.gradle.kts index 876558bdf15..0bc8eb06267 100644 --- a/buildSrc/shared.gradle.kts +++ b/buildSrc/shared.gradle.kts @@ -5,4 +5,3 @@ repositories { } maven { url = uri("https://repo.spring.io/snapshot") } } - diff --git a/buildSrc/src/main/kotlin/reportstream.project-conventions.gradle.kts b/buildSrc/src/main/kotlin/reportstream.project-conventions.gradle.kts index f8f9a6bd43d..dfd8d01a8ed 100644 --- a/buildSrc/src/main/kotlin/reportstream.project-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/reportstream.project-conventions.gradle.kts @@ -1,41 +1,40 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jlleitschuh.gradle.ktlint.KtlintExtension +apply(from = rootProject.file("buildSrc/shared.gradle.kts")) + plugins { kotlin("jvm") id("org.jlleitschuh.gradle.ktlint") } +ktlint { + version = "1.5.0" +} + group = "gov.cdc.prime.reportstream" version = "0.0.1-SNAPSHOT" -repositories { - mavenCentral() -} +val jvmTarget = JvmTarget.JVM_17 kotlin { - jvmToolchain(17) + jvmToolchain(jvmTarget.target.toInt()) } -val majorJavaVersion = 17 java { - sourceCompatibility = JavaVersion.toVersion(majorJavaVersion) - targetCompatibility = JavaVersion.toVersion(majorJavaVersion) + sourceCompatibility = JavaVersion.toVersion(jvmTarget.target) + targetCompatibility = JavaVersion.toVersion(jvmTarget.target) toolchain { - languageVersion = JavaLanguageVersion.of(majorJavaVersion) + languageVersion = JavaLanguageVersion.of(jvmTarget.target) } } val compileKotlin: KotlinCompile by tasks val compileTestKotlin: KotlinCompile by tasks -compileKotlin.kotlinOptions.jvmTarget = "$majorJavaVersion" -compileKotlin.kotlinOptions.allWarningsAsErrors = true -compileTestKotlin.kotlinOptions.jvmTarget = "$majorJavaVersion" -compileTestKotlin.kotlinOptions.allWarningsAsErrors = true - -configure { - // See ktlint versions at https://github.com/pinterest/ktlint/releases - version.set("1.1.1") -} +compileKotlin.compilerOptions.jvmTarget.set(jvmTarget) +compileKotlin.compilerOptions.allWarningsAsErrors = true +compileTestKotlin.compilerOptions.jvmTarget.set(jvmTarget) +compileTestKotlin.compilerOptions.allWarningsAsErrors = true tasks.withType { useJUnitPlatform() diff --git a/frontend-react/e2e/spec/chromium-only/authenticated/receiver-status-page-user-flow.spec.ts b/frontend-react/e2e/spec/chromium-only/authenticated/receiver-status-page-user-flow.spec.ts index 152996d902e..907b031702c 100644 --- a/frontend-react/e2e/spec/chromium-only/authenticated/receiver-status-page-user-flow.spec.ts +++ b/frontend-react/e2e/spec/chromium-only/authenticated/receiver-status-page-user-flow.spec.ts @@ -193,6 +193,8 @@ test.describe( }); test("result message", async ({ adminReceiverStatusPage }) => { + await adminReceiverStatusPage.statusContainer.waitFor({ state: "visible" }); + // get first entry's result from all-fail receiver's first day -> third time period const receiverI = 0; const dayI = 0; diff --git a/frontend-react/package.json b/frontend-react/package.json index 43420f40242..4ecb495332f 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -9,6 +9,7 @@ "@microsoft/applicationinsights-web": "^3.3.4", "@okta/okta-react": "^6.9.0", "@okta/okta-signin-widget": "^7.27.2", + "@react-pdf/renderer": "^4.1.6", "@rest-hooks/rest": "^3.0.3", "@tanstack/react-query": "^5.64.0", "@tanstack/react-query-devtools": "^5.64.0", diff --git a/frontend-react/src/components/Admin/MessageTesting/MessageTestingAccordion.tsx b/frontend-react/src/components/Admin/MessageTesting/MessageTestingAccordion.tsx index 89e5d673059..89aa1a14e89 100644 --- a/frontend-react/src/components/Admin/MessageTesting/MessageTestingAccordion.tsx +++ b/frontend-react/src/components/Admin/MessageTesting/MessageTestingAccordion.tsx @@ -1,23 +1,18 @@ import { Accordion, Icon, Tag } from "@trussworks/react-uswds"; -import { RSMessageResult } from "../../../config/endpoints/reports"; export const MessageTestingAccordion = ({ accordionTitle, priority, - resultData, - fieldsToRender, + fieldData, }: { accordionTitle: string; priority: "error" | "warning"; - resultData: RSMessageResult; - fieldsToRender: (keyof RSMessageResult)[]; + fieldData: (string | boolean | undefined)[]; }) => { const fieldID = accordionTitle.toLowerCase().split(" ").join("-"); - const existingFields = fieldsToRender.filter((field) => Object.keys(resultData).includes(field)); - const combinedFieldData = existingFields.flatMap((field) => resultData[field]); // Immediately return if there's no warning/error data to display - if (combinedFieldData.length === 0) return; + if (fieldData.length === 0) return; return (
@@ -37,20 +32,20 @@ export const MessageTestingAccordion = ({ {accordionTitle} {priority === "error" && ( - {combinedFieldData.length} + {fieldData.length} )} {priority === "warning" && ( - {combinedFieldData.length} + {fieldData.length} )} ), content: (
- {combinedFieldData.map((item, index) => ( + {fieldData.map((item, index) => (
{item}
- {index < combinedFieldData.length - 1 &&
} + {index < fieldData.length - 1 &&
}
))}
diff --git a/frontend-react/src/components/Admin/MessageTesting/MessageTestingPDF.tsx b/frontend-react/src/components/Admin/MessageTesting/MessageTestingPDF.tsx new file mode 100644 index 00000000000..83b97082118 --- /dev/null +++ b/frontend-react/src/components/Admin/MessageTesting/MessageTestingPDF.tsx @@ -0,0 +1,187 @@ +import { Document, Font, Page, StyleSheet, Text, View } from "@react-pdf/renderer"; +import PublicSansBold from "@uswds/uswds/fonts/public-sans/PublicSans-Bold.ttf"; +import PublicSansRegular from "@uswds/uswds/fonts/public-sans/PublicSans-Regular.ttf"; +import language from "./language.json"; +import { prettifyJSON } from "../../../utils/misc"; + +interface MessageTestingPDFProps { + orgName: string; + receiverName: string; + testStatus: string; + filterFieldData: (string | boolean | undefined)[]; + transformFieldData: (string | boolean | undefined)[]; + warningFieldData: (string | boolean | undefined)[]; + testMessage: string; + outputMessage: string; + isPassed: boolean; +} + +Font.register({ + family: "Public Sans Web", + fonts: [{ src: PublicSansRegular }, { src: PublicSansBold, fontWeight: "bold" }], +}); + +const styles = StyleSheet.create({ + page: { + padding: 30, + fontSize: 12, + fontFamily: "Public Sans Web", + }, + section: { + marginBottom: 20, + }, + sectionTitle: { + fontSize: 14, + marginBottom: 6, + fontWeight: "bold", + }, + text: { + marginBottom: 4, + }, + bannerContainer: { + padding: 10, + marginBottom: 20, + }, + bannerText: { + color: "black", + fontWeight: "bold", + }, + bannerSuccess: { + backgroundColor: "#ecf3ec", + borderLeft: "4px solid #00a91c", + }, + bannerWarning: { + backgroundColor: "#faf3d1", + borderLeft: "4px solid #ffbe2e", + }, + bannerError: { + backgroundColor: "#f4e3db", + borderLeft: "4px solid #d54309", + }, + codeBlock: { + backgroundColor: "#f5f5f5", + padding: 10, + fontFamily: "Courier", // or 'Roboto Mono' if you registered it + marginBottom: 10, + }, + hr: { + borderBottomWidth: 1, + borderBottomColor: "#000", + marginVertical: 5, + }, +}); + +const statusConfig = { + success: { + text: language.successAlertHeading, + className: styles.bannerSuccess, + }, + warning: { + text: language.warningAlertHeading, + className: styles.bannerWarning, + }, + error: { + text: language.errorAlertHeading, + className: styles.bannerError, + }, +}; + +const MessageTestingPDF = ({ + orgName, + receiverName, + testStatus, + filterFieldData, + transformFieldData, + warningFieldData, + testMessage, + outputMessage, + isPassed, +}: MessageTestingPDFProps) => { + const { text: bannerText, className: bannerClass } = statusConfig[testStatus as "success" | "warning" | "error"]; + const lines = prettifyJSON(testMessage) + .split("\n") + .map((line) => { + return line.replace(/ /g, "\u00A0"); + }); + return ( + + + {/* Section 1 */} + + Message testing result + Org name: {orgName} + Receiver name: {receiverName} + + + {/* Section 2 - Banner */} + + {bannerText} + + + {/* Section 3 - Filters triggered */} + {filterFieldData.length && ( + + Filters triggered + {filterFieldData.map((line, index) => ( + + + {line} + + + ))} + + )} + + {/* Section 4 - Transforms triggered */} + {transformFieldData.length && ( + + Transform errors + {transformFieldData.map((line, index) => ( + + + {line} + + + ))} + + )} + + {/* Section 5 - Warnings triggered */} + {warningFieldData.length && ( + + Transform warnings + {warningFieldData.map((line, index) => ( + + + {line} + + + ))} + + )} + + {/* Section 6 - Output message (HL7) */} + {isPassed && ( + + Output message + + {outputMessage} + + + )} + + {/* Section 7 - Test message (JSON) */} + + Test message + + {lines.map((line, idx) => ( + {line} + ))} + + + + + ); +}; + +export default MessageTestingPDF; diff --git a/frontend-react/src/components/Admin/MessageTesting/MessageTestingResult.tsx b/frontend-react/src/components/Admin/MessageTesting/MessageTestingResult.tsx index fd0ef022530..b623893d209 100644 --- a/frontend-react/src/components/Admin/MessageTesting/MessageTestingResult.tsx +++ b/frontend-react/src/components/Admin/MessageTesting/MessageTestingResult.tsx @@ -1,8 +1,10 @@ +import { usePDF } from "@react-pdf/renderer"; import { QueryObserverResult } from "@tanstack/react-query"; import { Accordion, Button, Icon } from "@trussworks/react-uswds"; import { type PropsWithChildren } from "react"; import language from "./language.json"; import { MessageTestingAccordion } from "./MessageTestingAccordion"; +import MessageTestingPDF from "./MessageTestingPDF"; import type { RSMessage, RSMessageResult } from "../../../config/endpoints/reports"; import Alert, { type AlertProps } from "../../../shared/Alert/Alert"; import HL7Message from "../../../shared/HL7Message/HL7Message"; @@ -14,10 +16,11 @@ export interface MessageTestingResultProps extends PropsWithChildren { resultData: RSMessageResult; handleGoBack: () => void; refetch: () => Promise>; + orgname: string; + receivername: string; } const filterFields: (keyof RSMessageResult)[] = ["filterErrors"]; - const transformFields: (keyof RSMessageResult)[] = [ "senderTransformErrors", "enrichmentSchemaErrors", @@ -35,6 +38,8 @@ const MessageTestingResult = ({ submittedMessage, handleGoBack, refetch, + orgname, + receivername, ...props }: MessageTestingResultProps) => { const isPassed = @@ -43,9 +48,12 @@ const MessageTestingResult = ({ resultData.enrichmentSchemaErrors.length === 0 && resultData.receiverTransformErrors.length === 0; const isWarned = - resultData.senderTransformWarnings.length > 0 && - resultData.enrichmentSchemaWarnings.length > 0 && + resultData.senderTransformWarnings.length > 0 || + resultData.enrichmentSchemaWarnings.length > 0 || resultData.receiverTransformWarnings.length > 0; + const filterFieldData = filterFields.flatMap((key) => resultData[key]); + const transformFieldData = transformFields.flatMap((key) => resultData[key]); + const warningFieldData = warningFields.flatMap((key) => resultData[key]); const alertType: AlertProps["type"] = !isPassed ? "error" : isWarned ? "warning" : "success"; const alertHeading = language[`${alertType}AlertHeading`]; @@ -60,14 +68,38 @@ const MessageTestingResult = ({ hour12: true, }; + const MessageTestingPDFRef = ( + + ); + const [instance] = usePDF({ document: MessageTestingPDFRef }); return (

Test results: {submittedMessage?.fileName}

+
+ + {instance.loading ? "Loading..." : "Download PDF"} + - + +
Select new message @@ -88,25 +120,18 @@ const MessageTestingResult = ({
- + {resultData.message && isPassed && ( @@ -132,7 +157,7 @@ const MessageTestingResult = ({ )}
-

Last updated: October 29, 2024

+

Last updated: January 28, 2025

## Objectives for FY25 Learn about our goals and how we are measuring success for this fiscal year (FY), which runs from October 2024 through September 2025. @@ -32,8 +32,6 @@ Learn about our goals and how we are measuring success for this fiscal year (FY) * Build basic functionality to allow ReportStream to ingest CSV via an API for conditions beyond COVID. ### Facilitate simple, efficient reporting for additional core public health data sources and public health systems. -* Route hospitalization data from at least one healthcare organization to the CDC National Syndromic Surveillance Program (NSSP). -* Route hospitalization data from at least one healthcare organization to one STLT. * Connect ReportStream to one federal healthcare system outside of the CDC. * Participate in CDC One Health activities and/or CDC Laboratory Response Network. * Connect ReportStream to three third-party standards/services. @@ -75,25 +73,6 @@ Learn about our goals and how we are measuring success for this fiscal year (FY)

Onboard 3 senders to ReportStream sending all of their ELR reportable conditions.

), }, - { - title:

Route live hospitalization data from a healthcare system to a STLT and the CDC National Syndromic Surveillance Program (NSSP).

, - tag: "recently-completed", - body: ( -

Design and implement an SFTP bucket to allow for submissions to meet submission needs of NSSP.

- ), - }, - { - tag: "working-on-now", - body: ( -

Align on the technical requirements needed to route admission, discharge, and transfer (ADT) data from a hospital system to CDC NSSP with the Oklahoma State Department of Health.

- ), - }, - { - tag: "next", - body: ( -

Connect a hospital system and CDC NSSP to ReportStream to enable routing of ADT data.

- ), - }, { title:

Connect ReportStream to one federal healthcare system outside of the CDC.

, tag: "working-on-now", diff --git a/frontend-react/src/pages/admin/AdminMessageTestingPage/AdminMessageTestingPage.tsx b/frontend-react/src/pages/admin/AdminMessageTestingPage/AdminMessageTestingPage.tsx index c32c1bb8ec7..352c6373e98 100644 --- a/frontend-react/src/pages/admin/AdminMessageTestingPage/AdminMessageTestingPage.tsx +++ b/frontend-react/src/pages/admin/AdminMessageTestingPage/AdminMessageTestingPage.tsx @@ -108,6 +108,8 @@ const AdminMessageTestingPage = () => { setCurrentMessageTestStep(MessageTestingSteps.StepOne); }} refetch={refetch} + orgname={orgname ?? "N/A"} + receivername={receivername ?? "N/A"} /> )} diff --git a/frontend-react/src/pages/error/Generic.tsx b/frontend-react/src/pages/error/Generic.tsx index 7567dee32c9..6a076f9a125 100644 --- a/frontend-react/src/pages/error/Generic.tsx +++ b/frontend-react/src/pages/error/Generic.tsx @@ -12,7 +12,7 @@ import { export const StringErrorDisplay = ({ message }: { message?: string }) => { return ( - {message ? message : GENERIC_ERROR_STRING} + {message ?? GENERIC_ERROR_STRING} ); }; @@ -28,10 +28,7 @@ export const ParagraphErrorDisplay = ({ Error -
+
@@ -40,10 +37,7 @@ export const ParagraphErrorDisplay = ({
  • -
  • @@ -61,18 +55,11 @@ export interface GenericErrorProps { displayAsPage?: boolean; displayConfig?: ErrorDisplayMessage; } -export const BasicErrorDisplay = ({ - displayAsPage, - displayConfig, -}: GenericErrorProps) => { +export const BasicErrorDisplay = ({ displayAsPage, displayConfig }: GenericErrorProps) => { if (!displayConfig) { // For back-compat with older uses // TODO: Remove when we stop using GenericError outside of RSErrorBoundary - return displayAsPage ? ( - - ) : ( - - ); + return displayAsPage ? : ; } else { // For use with RSNetworkError // Error message/page configs are designed in `/src/content/error/ErrorMessages.ts` diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index 0410a2c7ac7..48201bc80da 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -212,12 +212,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.0, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6": - version: 7.24.1 - resolution: "@babel/runtime@npm:7.24.1" +"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.0, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6": + version: 7.26.7 + resolution: "@babel/runtime@npm:7.26.7" dependencies: regenerator-runtime: ^0.14.0 - checksum: 5c8f3b912ba949865f03b3cf8395c60e1f4ebd1033fbd835bdfe81b6cac8a87d85bc3c7aded5fcdf07be044c9ab8c818f467abe0deca50020c72496782639572 + checksum: a1664a08f3f4854b895b540cca2f5f5c6c1993b5fb788c9615d70fc201e16bb254df8e0550c83eaf2749a14d87775e11a7c9ded6161203e9da7a4a323d546925 languageName: node linkType: hard @@ -1738,6 +1738,175 @@ __metadata: languageName: node linkType: hard +"@react-pdf/fns@npm:3.0.0": + version: 3.0.0 + resolution: "@react-pdf/fns@npm:3.0.0" + dependencies: + "@babel/runtime": ^7.20.13 + checksum: e9f3dd0288f1af59968de6d78a75c15cc8b30be8572e78e66e4bfdd7e1abf5b7c76cfc3633992bfe3d98b5e0c72d54bc7bff04c0ae097804d576e0ea9468d09f + languageName: node + linkType: hard + +"@react-pdf/font@npm:^3.0.1": + version: 3.0.1 + resolution: "@react-pdf/font@npm:3.0.1" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/types": ^2.7.0 + fontkit: ^2.0.2 + is-url: ^1.2.4 + checksum: 97cfcbe2e9a8a604618663b60b884c5ae1e755c69ac3c1258769f7f4b1b0564df52e366751eaaf0242273095a8455956d9b47300a03bd8a21592c61acccc6797 + languageName: node + linkType: hard + +"@react-pdf/image@npm:^3.0.1": + version: 3.0.1 + resolution: "@react-pdf/image@npm:3.0.1" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/png-js": ^3.0.0 + jay-peg: ^1.1.0 + checksum: b9a30834a8e80c72995fabae430f92cc11e2449e3276817df0403797542786d75983f2b8f0fead024c22a4521a9c76853acc60540280044d31f972bed885c344 + languageName: node + linkType: hard + +"@react-pdf/layout@npm:^4.2.0": + version: 4.2.0 + resolution: "@react-pdf/layout@npm:4.2.0" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/fns": 3.0.0 + "@react-pdf/image": ^3.0.1 + "@react-pdf/pdfkit": ^4.0.0 + "@react-pdf/primitives": ^4.0.0 + "@react-pdf/stylesheet": ^5.2.0 + "@react-pdf/textkit": ^5.0.1 + "@react-pdf/types": ^2.7.0 + emoji-regex: ^10.3.0 + queue: ^6.0.1 + yoga-layout: ^3.1.0 + checksum: 7b7c0ce505110eb477c390f4d6054c53b76193bac470310405952252a0f00be5068b3b59fef033ae03bc201275343c2a7cd93992aae03605d93ee69d8aa4b69a + languageName: node + linkType: hard + +"@react-pdf/pdfkit@npm:^4.0.0": + version: 4.0.0 + resolution: "@react-pdf/pdfkit@npm:4.0.0" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/png-js": ^3.0.0 + browserify-zlib: ^0.2.0 + crypto-js: ^4.2.0 + fontkit: ^2.0.2 + jay-peg: ^1.1.0 + vite-compatible-readable-stream: ^3.6.1 + checksum: d08b67e768932c20f702c0dd642954a9695158ccacabbea46409abcdbbf943a5409924444ddff6d3db6d1477ca35b2568ff8cdcce1381cd90c4ca147e1eee546 + languageName: node + linkType: hard + +"@react-pdf/png-js@npm:^3.0.0": + version: 3.0.0 + resolution: "@react-pdf/png-js@npm:3.0.0" + dependencies: + browserify-zlib: ^0.2.0 + checksum: 90c1612d7576d83eaf3fef9d5714b7fedb8f3e83a9af199763c4d68f73652aecec7d9fa90f185224c412500849f44beb4a4c49828c097e93d2ad42336bb019d1 + languageName: node + linkType: hard + +"@react-pdf/primitives@npm:^4.0.0": + version: 4.0.0 + resolution: "@react-pdf/primitives@npm:4.0.0" + checksum: d39d05df20459e6fe4715c98488b8eb0d25d22ec0a339e3cd0b500d1ba93c22aa06cc565f3482abbc58fecedc4f793e409c81f5d289240e4f3e961e20beb8bd0 + languageName: node + linkType: hard + +"@react-pdf/reconciler@npm:^1.1.3": + version: 1.1.3 + resolution: "@react-pdf/reconciler@npm:1.1.3" + dependencies: + object-assign: ^4.1.1 + scheduler: 0.25.0-rc-603e6108-20241029 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 4ff6f460e807662c156ae0a8a79add3034d1d11678468e954f6a0bcccb4e1e8c396b7853db90a3454a12477d6cf5b3f9a4bd908c042d6993995dec1e016d3026 + languageName: node + linkType: hard + +"@react-pdf/render@npm:^4.0.2": + version: 4.0.2 + resolution: "@react-pdf/render@npm:4.0.2" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/fns": 3.0.0 + "@react-pdf/primitives": ^4.0.0 + "@react-pdf/textkit": ^5.0.1 + "@react-pdf/types": ^2.7.0 + abs-svg-path: ^0.1.1 + color-string: ^1.9.1 + normalize-svg-path: ^1.1.0 + parse-svg-path: ^0.1.2 + svg-arc-to-cubic-bezier: ^3.2.0 + checksum: 5834c9e055230df116b72cbd7445f5c5ff17eca36a3f93d18f22242570d908dfe99b853304561f97b5afa79dc5739e2b70c13925722a994f9243a21d575de4e2 + languageName: node + linkType: hard + +"@react-pdf/renderer@npm:^4.1.6": + version: 4.1.6 + resolution: "@react-pdf/renderer@npm:4.1.6" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/font": ^3.0.1 + "@react-pdf/layout": ^4.2.0 + "@react-pdf/pdfkit": ^4.0.0 + "@react-pdf/primitives": ^4.0.0 + "@react-pdf/reconciler": ^1.1.3 + "@react-pdf/render": ^4.0.2 + "@react-pdf/types": ^2.7.0 + events: ^3.3.0 + object-assign: ^4.1.1 + prop-types: ^15.6.2 + queue: ^6.0.1 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: c2d32a4ed2bf0e7ecc8ce9f9fd21fb674bc9b90e46912d0c155d364f3b674cb3f3c1570efb79c71769552db376f2440f5eb47e1bb1c1cdbde569b8d4f282826d + languageName: node + linkType: hard + +"@react-pdf/stylesheet@npm:^5.2.0": + version: 5.2.0 + resolution: "@react-pdf/stylesheet@npm:5.2.0" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/fns": 3.0.0 + "@react-pdf/types": ^2.7.0 + color-string: ^1.9.1 + hsl-to-hex: ^1.0.0 + media-engine: ^1.0.3 + postcss-value-parser: ^4.1.0 + checksum: dc413c5e130c24581097b1cb2e7cf921ff8987bc64ab1310d00d3036da9da01e8cea9473c5b97f82e3ca231381c2b0c65590e9115bd325a8a37fb7c5cfd196d5 + languageName: node + linkType: hard + +"@react-pdf/textkit@npm:^5.0.1": + version: 5.0.1 + resolution: "@react-pdf/textkit@npm:5.0.1" + dependencies: + "@babel/runtime": ^7.20.13 + "@react-pdf/fns": 3.0.0 + bidi-js: ^1.0.2 + hyphen: ^1.6.4 + unicode-properties: ^1.4.1 + checksum: 525b18e9ea04947c73638d3c1f79b4a73b1c6bd6e1321c2ce99fdb994d7110a9803736fcbc582ed854a56987fa499d654f02b06511e124917e29ed21a07f08a9 + languageName: node + linkType: hard + +"@react-pdf/types@npm:^2.7.0": + version: 2.7.0 + resolution: "@react-pdf/types@npm:2.7.0" + checksum: 199525ef20353e9c73d1b0f63a530da9a5a519dc62f692bcfe0933d234e263c03ca04a94cbf18a4b36b2bbdc8dbb6b3df1271fa9ef4f3e6737fddd08407ef2f0 + languageName: node + linkType: hard + "@remix-run/router@npm:1.21.1": version: 1.21.1 resolution: "@remix-run/router@npm:1.21.1" @@ -2544,6 +2713,15 @@ __metadata: languageName: node linkType: hard +"@swc/helpers@npm:^0.5.12": + version: 0.5.15 + resolution: "@swc/helpers@npm:0.5.15" + dependencies: + tslib: ^2.8.0 + checksum: 1a9e0dbb792b2d1e0c914d69c201dbc96af3a0e6e6e8cf5a7f7d6a5d7b0e8b762915cd4447acb6b040e2ecc1ed49822875a7239f99a2d63c96c3c3407fb6fccf + languageName: node + linkType: hard + "@tanstack/query-core@npm:5.64.0": version: 5.64.0 resolution: "@tanstack/query-core@npm:5.64.0" @@ -3540,6 +3718,13 @@ __metadata: languageName: node linkType: hard +"abs-svg-path@npm:^0.1.1": + version: 0.1.1 + resolution: "abs-svg-path@npm:0.1.1" + checksum: af1a167c09e8bdb76c80adca7333f3d828e5b50e37b9702aa03675e271919e7b1eeaa35cce939970ecba14769953b7465ea34c2129ab683ddff9d973a07f164f + languageName: node + linkType: hard + "acorn-jsx@npm:^5.0.0, acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -3574,16 +3759,7 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:^7.1.0": - version: 7.1.0 - resolution: "agent-base@npm:7.1.0" - dependencies: - debug: ^4.3.4 - checksum: f7828f991470a0cc22cb579c86a18cbae83d8a3cbed39992ab34fc7217c4d126017f1c74d0ab66be87f71455318a8ea3e757d6a37881b8d0f2a2c6aa55e5418f - languageName: node - linkType: hard - -"agent-base@npm:^7.1.2": +"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.3 resolution: "agent-base@npm:7.1.3" checksum: 87bb7ee54f5ecf0ccbfcba0b07473885c43ecd76cb29a8db17d6137a19d9f9cd443a2a7c5fd8a3f24d58ad8145f9eb49116344a66b107e1aeab82cf2383f4753 @@ -4003,6 +4179,13 @@ __metadata: languageName: node linkType: hard +"base64-js@npm:^1.1.2, base64-js@npm:^1.3.0": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 + languageName: node + linkType: hard + "better-opn@npm:^3.0.2": version: 3.0.2 resolution: "better-opn@npm:3.0.2" @@ -4012,6 +4195,15 @@ __metadata: languageName: node linkType: hard +"bidi-js@npm:^1.0.2": + version: 1.0.3 + resolution: "bidi-js@npm:1.0.3" + dependencies: + require-from-string: ^2.0.2 + checksum: 877c5dcfd69a35fd30fee9e49a03faf205a7a4cd04a38af7648974a659cab7b1cd51fa881d7957c07bd1fc5adf22b90a56da3617bb0885ee69d58ff41117658c + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -4059,6 +4251,15 @@ __metadata: languageName: node linkType: hard +"brotli@npm:^1.3.2": + version: 1.3.3 + resolution: "brotli@npm:1.3.3" + dependencies: + base64-js: ^1.1.2 + checksum: 2c97329f4ccb8e4332cedd2f63b85c2e15ffb305b1cbf046df86201434caf93cb7992ca73c0f7053b6a1417f595069ec7783c26e01510cefc10035a0f466e594 + languageName: node + linkType: hard + "browser-assert@npm:^1.2.1": version: 1.2.1 resolution: "browser-assert@npm:1.2.1" @@ -4066,6 +4267,15 @@ __metadata: languageName: node linkType: hard +"browserify-zlib@npm:^0.2.0": + version: 0.2.0 + resolution: "browserify-zlib@npm:0.2.0" + dependencies: + pako: ~1.0.5 + checksum: 5cd9d6a665190fedb4a97dfbad8dabc8698d8a507298a03f42c734e96d58ca35d3c7d4085e283440bbca1cd1938cff85031728079bedb3345310c58ab1ec92d6 + languageName: node + linkType: hard + "browserslist-useragent-regexp@npm:^4.1.3": version: 4.1.3 resolution: "browserslist-useragent-regexp@npm:4.1.3" @@ -4084,21 +4294,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.3, browserslist@npm:^4.24.0": - version: 4.24.3 - resolution: "browserslist@npm:4.24.3" - dependencies: - caniuse-lite: ^1.0.30001688 - electron-to-chromium: ^1.5.73 - node-releases: ^2.0.19 - update-browserslist-db: ^1.1.1 - bin: - browserslist: cli.js - checksum: 016efc9953350e3a7212edcfdd72210cb33b339c1a974a77c0715eb67d23d7e5cd0a073ce1c801ab09235d8c213425ca51b92d41bbb829b833872b45f885fe7c - languageName: node - linkType: hard - -"browserslist@npm:^4.24.4": +"browserslist@npm:^4.23.3, browserslist@npm:^4.24.0, browserslist@npm:^4.24.4": version: 4.24.4 resolution: "browserslist@npm:4.24.4" dependencies: @@ -4431,7 +4627,7 @@ __metadata: languageName: node linkType: hard -"clone@npm:2.x": +"clone@npm:2.x, clone@npm:^2.1.2": version: 2.1.2 resolution: "clone@npm:2.1.2" checksum: aaf106e9bc025b21333e2f4c12da539b568db4925c0501a1bf4070836c9e848c892fa22c35548ce0d1132b08bbbfa17a00144fe58fccdab6fa900fec4250f67d @@ -4484,13 +4680,23 @@ __metadata: languageName: node linkType: hard -"color-name@npm:~1.1.4": +"color-name@npm:^1.0.0, color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 languageName: node linkType: hard +"color-string@npm:^1.9.1": + version: 1.9.1 + resolution: "color-string@npm:1.9.1" + dependencies: + color-name: ^1.0.0 + simple-swizzle: ^0.2.2 + checksum: c13fe7cff7885f603f49105827d621ce87f4571d78ba28ef4a3f1a104304748f620615e6bf065ecd2145d0d9dad83a3553f52bb25ede7239d18e9f81622f1cc5 + languageName: node + linkType: hard + "color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" @@ -4650,6 +4856,13 @@ __metadata: languageName: node linkType: hard +"crypto-js@npm:^4.2.0": + version: 4.2.0 + resolution: "crypto-js@npm:4.2.0" + checksum: f051666dbc077c8324777f44fbd3aaea2986f198fe85092535130d17026c7c2ccf2d23ee5b29b36f7a4a07312db2fae23c9094b644cc35f7858b1b4fcaf27774 + languageName: node + linkType: hard + "css-color-keywords@npm:^1.0.0": version: 1.0.0 resolution: "css-color-keywords@npm:1.0.0" @@ -4932,6 +5145,13 @@ __metadata: languageName: node linkType: hard +"dfa@npm:^1.2.0": + version: 1.2.0 + resolution: "dfa@npm:1.2.0" + checksum: 83b954b856a4a0c4282550a35532ac66dfc6362a08500a4b09c0d7a306c6813cbf50cc18d81bf8997d98559fc2675df89f6ece255d92517cc46f6bf8ef5ff727 + languageName: node + linkType: hard + "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -6082,6 +6302,13 @@ __metadata: languageName: node linkType: hard +"events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 + languageName: node + linkType: hard + "execa@npm:~8.0.1": version: 8.0.1 resolution: "execa@npm:8.0.1" @@ -6303,6 +6530,23 @@ __metadata: languageName: node linkType: hard +"fontkit@npm:^2.0.2": + version: 2.0.4 + resolution: "fontkit@npm:2.0.4" + dependencies: + "@swc/helpers": ^0.5.12 + brotli: ^1.3.2 + clone: ^2.1.2 + dfa: ^1.2.0 + fast-deep-equal: ^3.1.3 + restructure: ^3.0.0 + tiny-inflate: ^1.0.3 + unicode-properties: ^1.4.0 + unicode-trie: ^2.0.0 + checksum: 5daefcfd2f3c93abedf9e086a6512310bf9c36d440de8500dd1fab001290b12e80a164b9d192163cec4738fd6218e981dbcca9c3005bfe44388dd7f9744a8a27 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -6322,18 +6566,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" - dependencies: - asynckit: ^0.4.0 - combined-stream: ^1.0.8 - mime-types: ^2.1.12 - checksum: 01135bf8675f9d5c61ff18e2e2932f719ca4de964e3be90ef4c36aacfc7b9cb2fceb5eca0b7e0190e3383fe51c5b37f4cb80b62ca06a99aaabfcfd6ac7c9328c - languageName: node - linkType: hard - -"form-data@npm:^4.0.1": +"form-data@npm:^4.0.0, form-data@npm:^4.0.1": version: 4.0.1 resolution: "form-data@npm:4.0.1" dependencies: @@ -6959,6 +7192,22 @@ __metadata: languageName: node linkType: hard +"hsl-to-hex@npm:^1.0.0": + version: 1.0.0 + resolution: "hsl-to-hex@npm:1.0.0" + dependencies: + hsl-to-rgb-for-reals: ^1.1.0 + checksum: e748cea0d9cdf444727bd3fc3f62515d0c5806ad4b52850730d365e54f6f0ae1e41e2076ab17de8523ab5ebdd30c62323f26b2cdd383529755ad27d1a33965b8 + languageName: node + linkType: hard + +"hsl-to-rgb-for-reals@npm:^1.1.0": + version: 1.1.1 + resolution: "hsl-to-rgb-for-reals@npm:1.1.1" + checksum: b31452617e6c399509c5b8016999d659f9e347e71290da287bf0f536da031609d51b240535dc8eb3dbd7770a7b367d9896ab6a13794db7d16e4cf86e363e156f + languageName: node + linkType: hard + "html-encoding-sniffer@npm:^4.0.0": version: 4.0.0 resolution: "html-encoding-sniffer@npm:4.0.0" @@ -7087,6 +7336,13 @@ __metadata: languageName: node linkType: hard +"hyphen@npm:^1.6.4": + version: 1.10.6 + resolution: "hyphen@npm:1.10.6" + checksum: a2b6a6b3cab3ed38574b726234cab066e4ac9d3f8f33f8df48f4694e23196019b19e04bd859a0731d2b5afa24b95b6c3bd989c844fb427625c23c8564b4b5faa + languageName: node + linkType: hard + "iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -7134,7 +7390,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:^2.0.3": +"inherits@npm:^2.0.3, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -7223,6 +7479,13 @@ __metadata: languageName: node linkType: hard +"is-arrayish@npm:^0.3.1": + version: 0.3.2 + resolution: "is-arrayish@npm:0.3.2" + checksum: 977e64f54d91c8f169b59afcd80ff19227e9f5c791fa28fa2e5bce355cbaf6c2c356711b734656e80c9dd4a854dd7efcf7894402f1031dfc5de5d620775b4d5f + languageName: node + linkType: hard + "is-async-function@npm:^2.0.0": version: 2.0.0 resolution: "is-async-function@npm:2.0.0" @@ -7533,6 +7796,13 @@ __metadata: languageName: node linkType: hard +"is-url@npm:^1.2.4": + version: 1.2.4 + resolution: "is-url@npm:1.2.4" + checksum: 100e74b3b1feab87a43ef7653736e88d997eb7bd32e71fd3ebc413e58c1cbe56269699c776aaea84244b0567f2a7d68dfaa512a062293ed2f9fdecb394148432 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.2": version: 2.0.2 resolution: "is-weakmap@npm:2.0.2" @@ -7661,6 +7931,15 @@ __metadata: languageName: node linkType: hard +"jay-peg@npm:^1.1.0": + version: 1.1.1 + resolution: "jay-peg@npm:1.1.1" + dependencies: + restructure: ^3.0.0 + checksum: c3786552cab6bc8f367fceddafa771f928a299fea329ff895d5f87161f4de4e2a434b59729361571156ebf8e198e62a1abb30fa4f375617472c8fef1dfabcf59 + languageName: node + linkType: hard + "jquery@npm:^3.6.0": version: 3.7.0 resolution: "jquery@npm:3.7.0" @@ -8337,6 +8616,13 @@ __metadata: languageName: node linkType: hard +"media-engine@npm:^1.0.3": + version: 1.0.3 + resolution: "media-engine@npm:1.0.3" + checksum: 3c2834077e7223d95cc137e4d13f777750887748a04b3aa29d1abfb05b35ad483ea074b7798827aedc29a6a6f6da299c5822d1ce99414d6c9c81b4299dbbd85a + languageName: node + linkType: hard + "memoizerific@npm:^1.11.3": version: 1.11.3 resolution: "memoizerific@npm:1.11.3" @@ -9109,6 +9395,15 @@ __metadata: languageName: node linkType: hard +"normalize-svg-path@npm:^1.1.0": + version: 1.1.0 + resolution: "normalize-svg-path@npm:1.1.0" + dependencies: + svg-arc-to-cubic-bezier: ^3.0.0 + checksum: 106e108b2f99e9e222a1c6edfc859523c6c3c2b0a6ba64743ed08af120b23b9bc2c16682bc2ae043a24c011c34c8252376c68525cf11735c6f110b571740eb2e + languageName: node + linkType: hard + "npm-run-all@npm:^4.1.5": version: 4.1.5 resolution: "npm-run-all@npm:4.1.5" @@ -9417,6 +9712,20 @@ __metadata: languageName: node linkType: hard +"pako@npm:^0.2.5": + version: 0.2.9 + resolution: "pako@npm:0.2.9" + checksum: 055f9487cd57fbb78df84315873bbdd089ba286f3499daed47d2effdc6253e981f5db6898c23486de76d4a781559f890d643bd3a49f70f1b4a18019c98aa5125 + languageName: node + linkType: hard + +"pako@npm:~1.0.5": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -9478,16 +9787,14 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^7.0.0": - version: 7.1.2 - resolution: "parse5@npm:7.1.2" - dependencies: - entities: ^4.4.0 - checksum: 59465dd05eb4c5ec87b76173d1c596e152a10e290b7abcda1aecf0f33be49646ea74840c69af975d7887543ea45564801736356c568d6b5e71792fd0f4055713 +"parse-svg-path@npm:^0.1.2": + version: 0.1.2 + resolution: "parse-svg-path@npm:0.1.2" + checksum: bba7d4b4207fcc9eaf553b0d34db96ea8a1173635bc94528b5b66e1581902d4792d8d6229103764f01af4d839274234e97a4fa1c6f0fe7dcce195383848cec56 languageName: node linkType: hard -"parse5@npm:^7.2.1": +"parse5@npm:^7.0.0, parse5@npm:^7.2.1": version: 7.2.1 resolution: "parse5@npm:7.2.1" dependencies: @@ -9717,7 +10024,7 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.0.2, postcss-value-parser@npm:^4.2.0": +"postcss-value-parser@npm:^4.0.2, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f @@ -9790,7 +10097,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.5.7, prop-types@npm:^15.8.1": +"prop-types@npm:^15.5.7, prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -9866,6 +10173,15 @@ __metadata: languageName: node linkType: hard +"queue@npm:^6.0.1": + version: 6.0.2 + resolution: "queue@npm:6.0.2" + dependencies: + inherits: ~2.0.3 + checksum: ebc23639248e4fe40a789f713c20548e513e053b3dc4924b6cb0ad741e3f264dcff948225c8737834dd4f9ec286dbc06a1a7c13858ea382d9379f4303bcc0916 + languageName: node + linkType: hard + "react-docgen-typescript@npm:^2.2.2": version: 2.2.2 resolution: "react-docgen-typescript@npm:2.2.2" @@ -9947,6 +10263,7 @@ __metadata: "@okta/okta-react": ^6.9.0 "@okta/okta-signin-widget": ^7.27.2 "@playwright/test": ^1.49.1 + "@react-pdf/renderer": ^4.1.6 "@rest-hooks/rest": ^3.0.3 "@rest-hooks/test": ^7.3.1 "@storybook/addon-a11y": ^8.4.7 @@ -10450,6 +10767,13 @@ __metadata: languageName: node linkType: hard +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: a03ef6895445f33a4015300c426699bc66b2b044ba7b670aa238610381b56d3f07c686251740d575e22f4c87531ba662d06937508f0f3c0f1ddc04db3130560b + languageName: node + linkType: hard + "requireindex@npm:^1.2.0": version: 1.2.0 resolution: "requireindex@npm:1.2.0" @@ -10564,6 +10888,13 @@ __metadata: languageName: node linkType: hard +"restructure@npm:^3.0.0": + version: 3.0.2 + resolution: "restructure@npm:3.0.2" + checksum: f1c45d249fe1c5d43286bd0d209fefe3db556726c3eb8eb648116757e065a6f0dba2b80bf62bcc5a7681dfe888b6ace1bb6e446ab1535eb5ca7b257207ebc817 + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -10770,6 +11101,13 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:0.25.0-rc-603e6108-20241029": + version: 0.25.0-rc-603e6108-20241029 + resolution: "scheduler@npm:0.25.0-rc-603e6108-20241029" + checksum: c24fb37561cf73c54177f47fa0e92c95f8555eaf25d42d0cd2c4280058c8a2bf57b0f68f179bf766178ce6b6ea8c27b9a0cf0832bb3c6cd4ed3a15174dadaf04 + languageName: node + linkType: hard + "scheduler@npm:^0.23.2": version: 0.23.2 resolution: "scheduler@npm:0.23.2" @@ -10970,6 +11308,15 @@ __metadata: languageName: node linkType: hard +"simple-swizzle@npm:^0.2.2": + version: 0.2.2 + resolution: "simple-swizzle@npm:0.2.2" + dependencies: + is-arrayish: ^0.3.1 + checksum: a7f3f2ab5c76c4472d5c578df892e857323e452d9f392e1b5cf74b74db66e6294a1e1b8b390b519fa1b96b5b613f2a37db6cffef52c3f1f8f3c5ea64eb2d54c0 + languageName: node + linkType: hard + "sirv@npm:^3.0.0": version: 3.0.0 resolution: "sirv@npm:3.0.0" @@ -11489,6 +11836,13 @@ __metadata: languageName: node linkType: hard +"svg-arc-to-cubic-bezier@npm:^3.0.0, svg-arc-to-cubic-bezier@npm:^3.2.0": + version: 3.2.0 + resolution: "svg-arc-to-cubic-bezier@npm:3.2.0" + checksum: 55bf17756d558b9c0daddf636a6c9f2fe01fd5ac412229dfa2d4b29740226a82c980bcd3b5eb09ce311cbea282106c7549d97f8c8dba3a5a7b75f786bcb5e155 + languageName: node + linkType: hard + "svg-parser@npm:^2.0.4": version: 2.0.4 resolution: "svg-parser@npm:2.0.4" @@ -11556,6 +11910,13 @@ __metadata: languageName: node linkType: hard +"tiny-inflate@npm:^1.0.0, tiny-inflate@npm:^1.0.3": + version: 1.0.3 + resolution: "tiny-inflate@npm:1.0.3" + checksum: 4086a1f8938dafa4a20c63b099aeb47bf8fef5aca991bf4ea4b35dd2684fa52363b2c19b3e76660311e7613cb7c4f063bc48751b9bdf9555e498d997c30bc2d6 + languageName: node + linkType: hard + "tiny-invariant@npm:^1.1.0, tiny-invariant@npm:^1.3.1, tiny-invariant@npm:^1.3.3": version: 1.3.3 resolution: "tiny-invariant@npm:1.3.3" @@ -11802,7 +12163,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.8.1": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.8.0, tslib@npm:^2.8.1": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a @@ -12005,6 +12366,26 @@ __metadata: languageName: node linkType: hard +"unicode-properties@npm:^1.4.0, unicode-properties@npm:^1.4.1": + version: 1.4.1 + resolution: "unicode-properties@npm:1.4.1" + dependencies: + base64-js: ^1.3.0 + unicode-trie: ^2.0.0 + checksum: 337fba8a3c4707692d662fafbea60718ca9d8dfd2147cb2642bc4a1b5ad11136d848fa9c92818a35f59e6c866674ec7fd140e3e25412aea8fb8817f1b32fc3fe + languageName: node + linkType: hard + +"unicode-trie@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-trie@npm:2.0.0" + dependencies: + pako: ^0.2.5 + tiny-inflate: ^1.0.0 + checksum: 19e637ce20953ec1fbfa9087abef4746a50352679b833be27924e4ba7ad753cc4073b74263747ccfccb5e38b30b17468cbb96f361eb49903ff8602396280b5a4 + languageName: node + linkType: hard + "unified@npm:^10.0.0, unified@npm:^10.1.2": version: 10.1.2 resolution: "unified@npm:10.1.2" @@ -12345,6 +12726,17 @@ __metadata: languageName: node linkType: hard +"vite-compatible-readable-stream@npm:^3.6.1": + version: 3.6.1 + resolution: "vite-compatible-readable-stream@npm:3.6.1" + dependencies: + inherits: ^2.0.3 + string_decoder: ^1.1.1 + util-deprecate: ^1.0.1 + checksum: 7fd50738616a7bd012fb936b7036877940a0a83078fbe2584726fa9d1a5d15c934a5883e12e16213d6be54996b4ad7b6368d2897f9867a6c1110d03eacd91302 + languageName: node + linkType: hard + "vite-node@npm:2.1.8": version: 2.1.8 resolution: "vite-node@npm:2.1.8" @@ -12726,17 +13118,7 @@ __metadata: languageName: node linkType: hard -"whatwg-url@npm:^14.0.0": - version: 14.0.0 - resolution: "whatwg-url@npm:14.0.0" - dependencies: - tr46: ^5.0.0 - webidl-conversions: ^7.0.0 - checksum: 4b5887e50f786583bead70916413e67a381d2126899b9eb5c67ce664bba1e7ec07cdff791404581ce73c6190d83c359c9ca1d50711631217905db3877dec075c - languageName: node - linkType: hard - -"whatwg-url@npm:^14.1.0": +"whatwg-url@npm:^14.0.0, whatwg-url@npm:^14.1.0": version: 14.1.0 resolution: "whatwg-url@npm:14.1.0" dependencies: @@ -13026,6 +13408,13 @@ __metadata: languageName: node linkType: hard +"yoga-layout@npm:^3.1.0": + version: 3.2.1 + resolution: "yoga-layout@npm:3.2.1" + checksum: 6d75e73f6b044414def48d2bcc05b0bbc44f9d21e2dd0e2df696edddb76ea2c7fa6a2821069152bf5bfeeadd86494847a918c25dd08881f911f7915638f2fc39 + 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 aeb4a2f63d6..86068186569 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.25 +systemProp.kotlinVersion=2.1.0 diff --git a/prime-router/build.gradle.kts b/prime-router/build.gradle.kts index ad8ffb8bd27..ec1e4ad2c81 100644 --- a/prime-router/build.gradle.kts +++ b/prime-router/build.gradle.kts @@ -21,6 +21,7 @@ import io.swagger.v3.plugins.gradle.tasks.ResolveTask import org.apache.commons.io.FileUtils import org.apache.commons.io.FilenameUtils import org.apache.tools.ant.filters.ReplaceTokens +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jooq.meta.jaxb.ForcedType import java.io.ByteArrayOutputStream @@ -66,8 +67,8 @@ val azureFunctionsDir = "azure-functions" val primeMainClass = "gov.cdc.prime.router.cli.MainKt" val defaultDuplicateStrategy = DuplicatesStrategy.WARN azurefunctions.appName = azureAppName -val appJvmTarget = "17" -val javaVersion = when (appJvmTarget) { +val appJvmTarget = JvmTarget.JVM_17 +val javaVersion = when (appJvmTarget.target) { "17" -> JavaVersion.VERSION_17 "19" -> JavaVersion.VERSION_19 "21" -> JavaVersion.VERSION_21 @@ -273,7 +274,7 @@ sourceSets.create("testIntegration") { } val compileTestIntegrationKotlin: KotlinCompile by tasks -compileTestIntegrationKotlin.kotlinOptions.jvmTarget = appJvmTarget +compileTestIntegrationKotlin.compilerOptions.jvmTarget.set(appJvmTarget) val testIntegrationImplementation: Configuration by configurations.getting { extendsFrom(configurations["testImplementation"]) @@ -707,7 +708,8 @@ flyway { jooq { version.set("3.18.6") configurations { - create("main") { // name of the jOOQ configuration + create("main") { + // name of the jOOQ configuration jooqConfiguration.apply { logging = org.jooq.meta.jaxb.Logging.INFO jdbc.apply { diff --git a/prime-router/docs/design/design/transformations.md b/prime-router/docs/design/design/transformations.md index 6f5eebb9f7c..9fab1db9753 100644 --- a/prime-router/docs/design/design/transformations.md +++ b/prime-router/docs/design/design/transformations.md @@ -33,7 +33,7 @@ TBD: Content to be added in a future story ## FHIR-to-FHIR Transformations -FHIR bundles are transformed (enriched) to FHIR during the [Convert](../../universal-pipeline/convert.md) and [Translate](../../universal-pipeline/translate.md) steps in the Universal Pipeline. The class `FhirTransformer` is used to perform these FHIR to FHIR transforms. +FHIR bundles are transformed to FHIR during the [Convert](../../universal-pipeline/convert.md) and then enriched during the [Receiver Enrichment](../../universal-pipeline/receiver-enrichment.md) step in the Universal Pipeline. The class `FhirTransformer` is used to perform these FHIR to FHIR transforms. ## Transform Schemas diff --git a/prime-router/docs/design/proposals/0023-condition-to-code-mapping/0023-condition-to-code-mapping.md b/prime-router/docs/design/proposals/0023-condition-to-code-mapping/0023-condition-to-code-mapping.md index 2e9917f0d8d..1630e81eabe 100644 --- a/prime-router/docs/design/proposals/0023-condition-to-code-mapping/0023-condition-to-code-mapping.md +++ b/prime-router/docs/design/proposals/0023-condition-to-code-mapping/0023-condition-to-code-mapping.md @@ -19,7 +19,7 @@ The below items are not covered in this proposal. Overview Diagram -![Code to Condition Overview Diagram](C:\Users\James.Gilmore\Documents\GitHub\PRIME\prime-reportstream\prime-router\docs\design\proposals\0023-condition-to-code-mapping\code-to-condtion-overview.png) +![Code to Condition Overview Diagram](https://github.com/CDCgov/prime-reportstream/blob/main/prime-router/docs/design/proposals/0023-condition-to-code-mapping/code-to-condtion-overview.png) ### Creating Observation Mapping Table diff --git a/prime-router/docs/universal-pipeline/README.md b/prime-router/docs/universal-pipeline/README.md index f854ccff2d4..788b35d6db5 100644 --- a/prime-router/docs/universal-pipeline/README.md +++ b/prime-router/docs/universal-pipeline/README.md @@ -18,9 +18,10 @@ In order to handle different data **formats** and **types** in a scalable and ma ```mermaid flowchart LR; Receive-->Convert; - Convert-->Destination Filter; - Destination Filter-->Receiver Filter; - Receiver Filter-->Translate; + Convert-->id4[Destination Filter]; + id4-->id5[Receiver Enrichment]; + id5-->id6[Receiver Filter]; + id6-->Translate; Translate-->Batch; Batch-->Send; ``` @@ -29,6 +30,7 @@ The sections listed below will aim to describe each step of the pipeline in tech - [Receive](./receive.md) - [Convert](./convert.md) - [Destination Filter](./destination-filter.md) +- [Receiver Enrichment](./receiver-enrichment.md) - [Receiver Filter](./receiver-filter.md) - [Translate](./translate.md) - [Batch](./batch.md) @@ -112,9 +114,10 @@ messagesToSend.add( ) ``` -The four queues specific to the Universal Pipeline are: +The five queues specific to the Universal Pipeline are: - elr-fhir-convert - elr-fhir-destination-filter +- elf-fhir-receiver-enrichment - elr-fhir-receiver-filter - elr-fhir-translate diff --git a/prime-router/docs/universal-pipeline/destination-filter.md b/prime-router/docs/universal-pipeline/destination-filter.md index 21fbe27e878..8af2f127604 100644 --- a/prime-router/docs/universal-pipeline/destination-filter.md +++ b/prime-router/docs/universal-pipeline/destination-filter.md @@ -9,7 +9,7 @@ the data that may meet those interests (other steps could still filter out the d The function follows the [Convert](convert.md) function. At this point all data will be in FHIR format. These messages are passed to the FHIR Destination Filter which first decodes a FHIR Bundle. It then evaluates jurisdiction filters for all active receivers with a matching topic to find receivers that could accept the bundle. The message is then passed to -the [Receiver Filter](receiver-filter.md) function to evaluate remaining receiver-specific filters. +the [Receiver Enrichment](receiver-enrichment.md) function to optionally add minor customizations. ### Topic diff --git a/prime-router/docs/universal-pipeline/receiver-enrichment.md b/prime-router/docs/universal-pipeline/receiver-enrichment.md new file mode 100644 index 00000000000..734037de9ea --- /dev/null +++ b/prime-router/docs/universal-pipeline/receiver-enrichment.md @@ -0,0 +1,12 @@ +# Universal Pipeline Receiver Enrichment Step +After the [destination filter](./destination-filter.md) step has completed, each configured receiver will receive the converted FHIR bundle for potential further enrichment. If the specific receiver in question has been configured with one or more enrichment schema names, each enrichment schema will be applied in turn and update the FHIR bundle. + +## Configuration + +The configuration for this step is done on an individual receiver level. The `enrichmentSchemaNames` is an attribute of the receiver element in the receiver YAML configuration that contains an array of file or classpath references which contain transforms that will be applied to the FHIR bundle. + +### Transforms +The transforms are stored in the files enumerated in the `enrichmentSchemaNames` array. For each element in the array a `FHIRTransform` object is instantiated with the individual enrichment schema name. Those objects contain [FHIRPath](http://hl7.org/fhirpath/r2) expressions which are subsequently used to process the bundle. (Additional useful information regarding FHIRPath can be found in [FHIR Functions](../getting-started/fhir-functions.md).) + +## How It Works +This step comes directly after the [destination filter](./destination-filter.md) step in the pipeline. For every receiver configured for the given organization, the converted FHIR bundle from the destination filter step will be forwarded to the receiver enrichment step (**elr-fhir-receiver-enrichment** queue) of the pipeline. If any enrichment transforms are defined, they will be applied to the bundle in order. Once processing is completed the bundle is uploaded to Azure and control is passed to the [receiver filter](./receiver-filter.md) step. diff --git a/prime-router/docs/universal-pipeline/receiver-filter.md b/prime-router/docs/universal-pipeline/receiver-filter.md index e98957961f0..bb57d46c83d 100644 --- a/prime-router/docs/universal-pipeline/receiver-filter.md +++ b/prime-router/docs/universal-pipeline/receiver-filter.md @@ -6,8 +6,7 @@ The Destination Filter function’s evaluates a receiver's filters on a bundle a may include pruning of observations in the bundle. Each receiver connected with ReportStream has unique interests in the data that flows through the pipeline. This step is designed to find the data that meet those interests. -The function follows the [Destination Filter](destination-filter.md) function. These messages are passed to the FHIRi -Receiver Filter which first decodes a FHIR Bundle. Then, quality, processing, routing, and condition filters are +The function follows the [Receiver Enrichment](receiver-enrichment.md) function. These messages are passed to the FHIR Receiver Filter which first decodes a FHIR Bundle. Then, quality, processing, routing, and condition filters are evaluated to determine if the bundle should be sent and prune it of unneeded data. If the message passes, it is sent to the [Translate](translate.md) function where receiver specific work is done to prepare for batching and sending. diff --git a/prime-router/settings/organizations.yml b/prime-router/settings/organizations.yml index 3b823ffa627..912f6ac0aae 100644 --- a/prime-router/settings/organizations.yml +++ b/prime-router/settings/organizations.yml @@ -1307,4 +1307,31 @@ host: sftp port: 22 filePath: ./upload - credentialName: DEFAULT-SFTP \ No newline at end of file + credentialName: DEFAULT-SFTP + - name: DEV_ENRICHMENT + externalName: Enrichment Development + organizationName: development + topic: full-elr + customerStatus: active + jurisdictionalFilter: [ "true" ] + qualityFilter: + - "true" + processingModeFilter: [ "true" ] + timing: + operation: MERGE + numberPerDay: 1440 # Every minute + initialTime: 00:00 + translation: + schemaName: "classpath:/metadata/hl7_mapping/ORU_R01/ORU_R01-base.yml" + useTestProcessingMode: false + useBatchHeaders: false + type: "HL7" + receivingApplicationName: "" + receivingFacilityName: "" + transport: + type: SFTP + host: sftp + port: 22 + filePath: ./upload + credentialName: DEFAULT-SFTP + enrichmentSchemaNames: ["classpath:/metadata/fhir_transforms/receivers/fhir-message-header-sample.yml"] \ No newline at end of file diff --git a/prime-router/src/main/kotlin/ActionLog.kt b/prime-router/src/main/kotlin/ActionLog.kt index ef3e9ceb19e..64cb142d4a0 100644 --- a/prime-router/src/main/kotlin/ActionLog.kt +++ b/prime-router/src/main/kotlin/ActionLog.kt @@ -54,9 +54,7 @@ data class ActionLog( ) { val scope = detail.scope - fun getActionId(): Long { - return action!!.actionId - } + fun getActionId(): Long = action!!.actionId } /** @@ -162,25 +160,19 @@ class ActionLogger(val logs: MutableList = mutableListOf()) { * Check if the logger has logged any errors. * @return true if there are errors logged, false otherwise */ - fun hasErrors(): Boolean { - return logs.any { it.type == ActionLogLevel.error } - } + fun hasErrors(): Boolean = logs.any { it.type == ActionLogLevel.error } /** * Check if the logger has logged any warnings. * @return true if there are warnings logged, false otherwise */ - fun hasWarnings(): Boolean { - return logs.any { it.type == ActionLogLevel.warning } - } + fun hasWarnings(): Boolean = logs.any { it.type == ActionLogLevel.warning } /** * Check if any logs have been logged. * @return true if any log has been logged, false otherwise. */ - fun isEmpty(): Boolean { - return logs.isEmpty() - } + fun isEmpty(): Boolean = logs.isEmpty() /** * Log a given [actionDetail] and a [level] log level. diff --git a/prime-router/src/main/kotlin/ActionMessages.kt b/prime-router/src/main/kotlin/ActionMessages.kt index 407d4006616..1d6ad1355ad 100644 --- a/prime-router/src/main/kotlin/ActionMessages.kt +++ b/prime-router/src/main/kotlin/ActionMessages.kt @@ -52,9 +52,7 @@ enum class ErrorCode { /** * Action details for item logs for specific [fieldMapping] and [errorCode]. */ -abstract class ItemActionLogDetail( - val fieldMapping: String = "", -) : ActionLogDetail { +abstract class ItemActionLogDetail(val fieldMapping: String = "") : ActionLogDetail { override val scope = ActionLogScope.item override val errorCode = ErrorCode.UNKNOWN } @@ -134,10 +132,7 @@ class InvalidCodeMessage( /** * Message for an invalid phone number for a given [fieldMapping] and [formattedValue]. */ -class InvalidPhoneMessage( - formattedValue: String = "", - fieldMapping: String, -) : ItemActionLogDetail(fieldMapping) { +class InvalidPhoneMessage(formattedValue: String = "", fieldMapping: String) : ItemActionLogDetail(fieldMapping) { override val message = "Invalid phone number '$formattedValue' for $fieldMapping. Reformat to a 10-digit phone " + "number (e.g. (555) - 555-5555)." override val errorCode = ErrorCode.INVALID_MSG_PARSE_TELEPHONE @@ -165,10 +160,7 @@ class InvalidPostalMessage( /** * Message for an invalid HD for a given [fieldMapping] and [formattedValue]. */ -class UnsupportedHDMessage( - formattedValue: String = "", - fieldMapping: String, -) : ItemActionLogDetail(fieldMapping) { +class UnsupportedHDMessage(formattedValue: String = "", fieldMapping: String) : ItemActionLogDetail(fieldMapping) { override val message = "Unsupported HD format for input: '$formattedValue' in $fieldMapping" override val errorCode = ErrorCode.INVALID_MSG_PARSE_HD } @@ -176,10 +168,7 @@ class UnsupportedHDMessage( /** * Message for an invalid EI for a given [fieldMapping] and [formattedValue]. */ -class UnsupportedEIMessage( - formattedValue: String = "", - fieldMapping: String, -) : ItemActionLogDetail(fieldMapping) { +class UnsupportedEIMessage(formattedValue: String = "", fieldMapping: String) : ItemActionLogDetail(fieldMapping) { override val message = "Unsupported EI format for input: '$formattedValue' in $fieldMapping" override val errorCode = ErrorCode.INVALID_MSG_PARSE_EI } @@ -187,9 +176,7 @@ class UnsupportedEIMessage( /** * A message to denote that equipment was not found in the LIVD table for a given [fieldMapping]. */ -class InvalidEquipmentMessage( - fieldMapping: String, -) : ItemActionLogDetail(fieldMapping) { +class InvalidEquipmentMessage(fieldMapping: String) : ItemActionLogDetail(fieldMapping) { override val message = "No match found for $fieldMapping; please refer to the " + "CDC LIVD table LOINC Mapping spreadsheet for acceptable values." override val errorCode = ErrorCode.INVALID_MSG_EQUIPMENT_MAPPING @@ -217,10 +204,8 @@ class FieldProcessingMessage( * A [message] for invalid HL7 message. Note field mapping is not available from the HAPI errors. * An optional [errorCode] for the error. Used to display a friendly error. */ -class InvalidHL7Message( - override val message: String, - override val errorCode: ErrorCode = ErrorCode.UNKNOWN, -) : ItemActionLogDetail("") +class InvalidHL7Message(override val message: String, override val errorCode: ErrorCode = ErrorCode.UNKNOWN) : + ItemActionLogDetail("") /** * A [message] for invalid request parameter. @@ -257,7 +242,7 @@ class DuplicateSubmissionMessage(val payloadName: String?) : ActionLogDetail { /** * A [message] for a duplicate item within a submission */ -class DuplicateItemMessage() : ActionLogDetail { +class DuplicateItemMessage : ActionLogDetail { override val scope = ActionLogScope.item override val errorCode = ErrorCode.UNKNOWN override val message = "Item is a duplicate." @@ -266,14 +251,13 @@ class DuplicateItemMessage() : ActionLogDetail { /** * A [message] for non-error details. */ -class FhirActionLogDetail( - override val message: String, -) : GenericActionLogDetail(message, ActionLogScope.report, ErrorCode.UNKNOWN) +class FhirActionLogDetail(override val message: String) : + GenericActionLogDetail(message, ActionLogScope.report, ErrorCode.UNKNOWN) /** * A return message for invalid processing type */ -class UnsupportedProcessingTypeMessage() : ActionLogDetail { +class UnsupportedProcessingTypeMessage : ActionLogDetail { override val scope = ActionLogScope.report override val errorCode = ErrorCode.UNKNOWN override val message = "Full ELR senders must be configured for async processing." @@ -291,7 +275,8 @@ class EvaluateFilterConditionErrorMessage(message: String?) : ActionLogDetail { /** * A [message] for when observations have been pruned for any receiver of a report */ -class PrunedObservationsLogMessage(override val message: String) : GenericActionLogDetail( +class PrunedObservationsLogMessage(override val message: String) : + GenericActionLogDetail( message, ActionLogScope.report ) { constructor(reportId: ReportId, filteredIdMap: Map>) : this( diff --git a/prime-router/src/main/kotlin/CsvComparer.kt b/prime-router/src/main/kotlin/CsvComparer.kt index 883a7fbaeaa..579084281d7 100644 --- a/prime-router/src/main/kotlin/CsvComparer.kt +++ b/prime-router/src/main/kotlin/CsvComparer.kt @@ -3,16 +3,12 @@ package gov.cdc.prime.router import java.io.File data class HeaderComparison(val fileOneHeaders: Set, val fileTwoHeaders: Set) { - fun hasErrors(): Boolean { - return fileOneHeaders.isNotEmpty() || fileTwoHeaders.isNotEmpty() - } + fun hasErrors(): Boolean = fileOneHeaders.isNotEmpty() || fileTwoHeaders.isNotEmpty() - override fun toString(): String { - return """ + override fun toString(): String = """ There are keys in fileOne that are not in fileTwo: ${fileOneHeaders.joinToString { "," }} There are keys in fileTwo that are not in fileOne: ${fileTwoHeaders.joinToString { "," }} """.trimIndent() - } } data class CsvComparer(val fileOnePath: String, val fileTwoPath: String, val recordId: String = "Patient_Id") { diff --git a/prime-router/src/main/kotlin/Element.kt b/prime-router/src/main/kotlin/Element.kt index bce0514ca59..3817209ab28 100644 --- a/prime-router/src/main/kotlin/Element.kt +++ b/prime-router/src/main/kotlin/Element.kt @@ -134,16 +134,9 @@ data class Element( BLANK, } - data class CsvField( - val name: String, - val format: String?, - ) + data class CsvField(val name: String, val format: String?) - data class HDFields( - val name: String, - val universalId: String?, - val universalIdSystem: String?, - ) + data class HDFields(val name: String, val universalId: String?, val universalIdSystem: String?) data class EIFields( val name: String, @@ -156,11 +149,7 @@ data class Element( * An element can have subfields, for example when more than CSV field makes up a single element. * See ElementTests for an example. **/ - data class SubValue( - val name: String, - val value: String, - val format: String?, - ) + data class SubValue(val name: String, val value: String, val format: String?) /** * @property ZERO_OR_ONE Can be null or present (default) @@ -174,19 +163,18 @@ data class Element( // ZERO is not a value, just remove the element to represent this concept // Other values including conditionals in the future. - fun toFormatted(): String { - return when (this) { + fun toFormatted(): String = when (this) { ZERO_OR_ONE -> "[0..1]" ONE -> "[1..1]" } - } } val isCodeType get() = this.type == Type.CODE val isOptional get() = this.cardinality == null || - this.cardinality == Cardinality.ZERO_OR_ONE || canBeBlank + this.cardinality == Cardinality.ZERO_OR_ONE || + canBeBlank val canBeBlank get() = type == Type.TEXT_OR_BLANK || @@ -212,8 +200,7 @@ data class Element( } } - fun inheritFrom(baseElement: Element): Element { - return Element( + fun inheritFrom(baseElement: Element): Element = Element( name = this.name, type = this.type ?: baseElement.type, valueSet = this.valueSet ?: baseElement.valueSet, @@ -240,7 +227,6 @@ data class Element( csvFields = this.csvFields ?: baseElement.csvFields, delimiter = this.delimiter ?: baseElement.delimiter ) - } /** * Generate validation error messages if this element is not valid. @@ -284,22 +270,18 @@ data class Element( return errorList } - fun nameContains(substring: String): Boolean { - return name.contains(substring, ignoreCase = true) - } + fun nameContains(substring: String): Boolean = name.contains(substring, ignoreCase = true) /** * Is there a default value for this element? * * @param defaultValues a dynamic set of default values to use */ - fun hasDefaultValue(defaultValues: DefaultValues): Boolean { - return defaultValues.containsKey(name) || default?.isNotBlank() == true - } + fun hasDefaultValue( + defaultValues: DefaultValues, + ): Boolean = defaultValues.containsKey(name) || default?.isNotBlank() == true - fun defaultValue(defaultValues: DefaultValues): String { - return defaultValues.getOrDefault(name, default ?: "") - } + fun defaultValue(defaultValues: DefaultValues): String = defaultValues.getOrDefault(name, default ?: "") /** * A formatted string is the Element's normalized value formatted using the format string passed in @@ -941,7 +923,8 @@ data class Element( tokenizeMapperValue(elementName, itemIndex) } else { val valueElement = schema.findElement(elementName) - if (valueElement != null && allElementValues.containsKey(elementName) && + if (valueElement != null && + allElementValues.containsKey(elementName) && !allElementValues[elementName].isNullOrEmpty() ) { ElementAndValue(valueElement, allElementValues[elementName]!!) @@ -1144,9 +1127,7 @@ data class Element( "UM" // US Minor Outlying Islands ) - fun csvFields(name: String, format: String? = null): List { - return listOf(CsvField(name, format)) - } + fun csvFields(name: String, format: String? = null): List = listOf(CsvField(name, format)) fun parseHD(value: String, maximumLength: Int? = null): HDFields { val parts = value.split(hdDelimiter) diff --git a/prime-router/src/main/kotlin/FakeReport.kt b/prime-router/src/main/kotlin/FakeReport.kt index d6cc6591eb7..431bcd07871 100644 --- a/prime-router/src/main/kotlin/FakeReport.kt +++ b/prime-router/src/main/kotlin/FakeReport.kt @@ -39,15 +39,13 @@ class FakeDataService : Logging { // creates a fake name for a person based on the patient name // in the row context - fun createFakeName(element: Element): String { - return when { + fun createFakeName(element: Element): String = when { element.nameContains("first") -> context.patientName.firstName() element.nameContains("last") -> context.patientName.lastName() element.nameContains("middle") -> context.patientName.firstName() // no middle name in faker element.nameContains("suffix") -> randomChoice(context.patientName.suffix(), "") else -> TODO() } - } // creates a fake date and formats it according to the element's // provided formatting string @@ -85,9 +83,7 @@ class FakeDataService : Logging { // creates a fake email for the patient name that is part // of the row context - fun createFakeEmail(): String { - return "${context.patientName.username()}@email.com" - } + fun createFakeEmail(): String = "${context.patientName.username()}@email.com" // ID values typically come from valuesets, so we pass in the element // and then we examine both the alt values and the value set for the element @@ -127,8 +123,7 @@ class FakeDataService : Logging { // and we then pull a random value from all the potential values available. // the rationale here is that if an element specifies alt values, they are preferred // to the values held in the value set collection, and so we use them first - fun createFakeCodeValue(element: Element): String { - return when (element.name) { + fun createFakeCodeValue(element: Element): String = when (element.name) { "specimen_source_site_code" -> "71836000" "test_result_status" -> randomChoice("F", "C") "processing_mode_code" -> "P" @@ -141,7 +136,6 @@ class FakeDataService : Logging { createFakeValueFromValueSet(element) } } - } // table values work in a similar fashion to the valuesets, but are // more flexible and allow for more filtering. @@ -197,8 +191,7 @@ class FakeDataService : Logging { } // creates fake text data - fun createFakeText(element: Element): String { - return when { + fun createFakeText(element: Element): String = when { element.nameContains("name_of_testing_lab") -> "Any lab USA" element.nameContains("lab_name") -> "Any lab USA" element.nameContains("sender_id") -> "${element.default}" // Allow the default to fill this in @@ -308,7 +301,6 @@ class FakeDataService : Logging { else -> faker.lorem().characters(5, 10) } - } // now that we've created all our functions, we can call them in our // when statement here, depending on the type of the element passed in. @@ -436,9 +428,8 @@ class FakeReport(val metadata: Metadata, val locale: Locale? = null) { } } - internal fun buildColumn(element: Element, context: RowContext): String { - return fakeDataService.getFakeValueForElement(element, context) - } + internal fun buildColumn(element: Element, context: RowContext): String = + fakeDataService.getFakeValueForElement(element, context) // mapped columns often refer back to non-mapped columns in the schema. in the past, for faked // values we have just hard coded in the name of the element and let the faker process it, @@ -565,12 +556,10 @@ class FakeReport(val metadata: Metadata, val locale: Locale? = null) { return list[next] } - fun getRandomSiteOfCare(): String { - return randomChoice( + fun getRandomSiteOfCare(): String = randomChoice( "airport", "assisted_living", "camp", "correctional_facility", "employer", "fqhc", "government_agency", "hospice", "hospital", "lab", "nursing_home", "other", "pharmacy", "primary_care", "shelter", "treatment_center", "university", "urgent_care" ) - } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/FileNameTemplate.kt b/prime-router/src/main/kotlin/FileNameTemplate.kt index fdf6449d925..3f72ab91e10 100644 --- a/prime-router/src/main/kotlin/FileNameTemplate.kt +++ b/prime-router/src/main/kotlin/FileNameTemplate.kt @@ -29,8 +29,9 @@ interface FileNameElement { class Literal : FileNameElement { override val name = "literal" - override fun getElementValue(args: List, translatorConfig: TranslatorConfiguration?): String { - return args.getOrElse(0) { "" } + override fun getElementValue(args: List, translatorConfig: TranslatorConfiguration?): String = + args.getOrElse(0) { + "" } } @@ -49,13 +50,12 @@ class FileUuid : FileNameElement { override val name: String get() = "uuid" - override fun getElementValue(args: List, translatorConfig: TranslatorConfiguration?): String { - return if (args.isEmpty()) { + override fun getElementValue(args: List, translatorConfig: TranslatorConfiguration?): String = + if (args.isEmpty()) { UUID.randomUUID().toString() } else { args[0] } - } } class RegexReplace : FileNameElement { @@ -107,8 +107,7 @@ class CreatedDate : FileNameElement { override val name = "createdDate" - override fun getElementValue(args: List, translatorConfig: TranslatorConfiguration?): String { - return try { + override fun getElementValue(args: List, translatorConfig: TranslatorConfiguration?): String = try { val pattern = if (args.isEmpty() || args[0].isEmpty()) { defaultFormat } else { @@ -120,7 +119,6 @@ class CreatedDate : FileNameElement { } catch (_: Exception) { "" } - } } /** @@ -186,11 +184,9 @@ open class FileNameTemplate( Rand6() ) - private fun findFileNameElement(elementName: String): FileNameElement? { - return fileNameElements.firstOrNull { + private fun findFileNameElement(elementName: String): FileNameElement? = fileNameElements.firstOrNull { it.name == elementName } - } fun parseFileNameElement(fileNameElement: String): Pair> { // Using a permissive match in the (arg1, arg2) section, to allow most regexes to be passed as args. diff --git a/prime-router/src/main/kotlin/FileSettings.kt b/prime-router/src/main/kotlin/FileSettings.kt index f31e28ce943..2958c0b4982 100644 --- a/prime-router/src/main/kotlin/FileSettings.kt +++ b/prime-router/src/main/kotlin/FileSettings.kt @@ -54,9 +54,8 @@ class FileSettings : SettingsProvider { return loadOrganizationList(list) } - fun loadOrganizations(vararg organizations: DeepOrganization): FileSettings { - return loadOrganizationList(organizations.toList()) - } + fun loadOrganizations(vararg organizations: DeepOrganization): FileSettings = + loadOrganizationList(organizations.toList()) fun loadOrganizationList(organizations: List): FileSettings { organizations.forEach { org -> @@ -94,17 +93,11 @@ class FileSettings : SettingsProvider { override val senders get() = this.senderStore.values override val receivers get() = this.receiverStore.values - override fun findOrganization(name: String): Organization? { - return organizationStore[name] - } + override fun findOrganization(name: String): Organization? = organizationStore[name] - override fun findReceiver(fullName: String): Receiver? { - return receiverStore[fullName] - } + override fun findReceiver(fullName: String): Receiver? = receiverStore[fullName] - override fun findSender(fullName: String): Sender? { - return senderStore[Sender.canonicalizeFullName(fullName)] - } + override fun findSender(fullName: String): Sender? = senderStore[Sender.canonicalizeFullName(fullName)] override fun findOrganizationAndReceiver(fullName: String): Pair? { val (organizationName, _) = Receiver.parseFullName(fullName) diff --git a/prime-router/src/main/kotlin/JOOQBindings.kt b/prime-router/src/main/kotlin/JOOQBindings.kt index 3acbca6d7db..5d5cf060ae5 100644 --- a/prime-router/src/main/kotlin/JOOQBindings.kt +++ b/prime-router/src/main/kotlin/JOOQBindings.kt @@ -20,21 +20,13 @@ import java.util.Objects class JsonConverter(val c: Class) : Converter { private val mapper = JacksonMapperUtilities.defaultMapper - override fun from(dbObject: JSONB): T { - return mapper.readValue(dbObject.toString(), c) - } + override fun from(dbObject: JSONB): T = mapper.readValue(dbObject.toString(), c) - override fun to(experiment: T): JSONB { - return JSONB.valueOf(mapper.writeValueAsString(experiment)) - } + override fun to(experiment: T): JSONB = JSONB.valueOf(mapper.writeValueAsString(experiment)) - override fun fromType(): Class { - return JSONB::class.java - } + override fun fromType(): Class = JSONB::class.java - override fun toType(): Class { - return c - } + override fun toType(): Class = c } /** @@ -46,9 +38,7 @@ class JsonConverter(val c: Class) : Converter { * @param klass The class of the POJO this should be used in the inheritance for the POJO specific binding */ abstract class JsonBinding(val klass: Class) : AbstractBinding() { - override fun converter(): Converter { - return JsonConverter(klass) - } + override fun converter(): Converter = JsonConverter(klass) override fun sql(ctx: BindingSQLContext) { val convert = ctx.convert(converter()) @@ -91,22 +81,16 @@ class TopicConverter : Converter { override fun to(topic: Topic?): String? = topic?.jsonVal - override fun fromType(): Class { - return String::class.java - } + override fun fromType(): Class = String::class.java - override fun toType(): Class { - return Topic::class.java - } + override fun toType(): Class = Topic::class.java } /** * A binding for Topics to be converted to and from string columns by JOOQ */ class TopicBinding : AbstractBinding() { - override fun converter(): Converter { - return TopicConverter() - } + override fun converter(): Converter = TopicConverter() override fun get(ctx: BindingGetResultSetContext) { ctx.convert(converter()).value(ctx.resultSet().getString(ctx.index())) diff --git a/prime-router/src/main/kotlin/Metadata.kt b/prime-router/src/main/kotlin/Metadata.kt index 89cc7f59217..1d914819a32 100644 --- a/prime-router/src/main/kotlin/Metadata.kt +++ b/prime-router/src/main/kotlin/Metadata.kt @@ -172,9 +172,7 @@ class Metadata : Logging { } } - fun loadSchemas(vararg schemas: Schema): Metadata { - return loadSchemaList(schemas.toList()) - } + fun loadSchemas(vararg schemas: Schema): Metadata = loadSchemaList(schemas.toList()) private fun loadSchemaList(schemas: List): Metadata { val fixedUpSchemas = mutableMapOf() @@ -198,9 +196,7 @@ class Metadata : Logging { return this } - fun findSchema(name: String): Schema? { - return schemaStore[normalizeSchemaName(name)] - } + fun findSchema(name: String): Schema? = schemaStore[normalizeSchemaName(name)] private fun readAllSchemas(catalogDir: File, dirRelPath: String): List { val outputSchemas = mutableListOf() @@ -251,9 +247,7 @@ class Metadata : Logging { return schema.copy(elements = schemaElements, basedOnRef = basedOnSchema, extendsRef = extendsSchema) } - private fun normalizeSchemaName(name: String): String { - return name.lowercase() - } + private fun normalizeSchemaName(name: String): String = name.lowercase() /** * The fixup process fills in references and inherited attributes. @@ -295,16 +289,15 @@ class Metadata : Logging { * Mappers */ - fun findMapper(name: String): Mapper? { - return mappers.find { it.name.equals(name, ignoreCase = true) } - } + fun findMapper(name: String): Mapper? = mappers.find { it.name.equals(name, ignoreCase = true) } /* * ReportStreamFilterDefinitions */ - fun findReportStreamFilterDefinitions(name: String): ReportStreamFilterDefinition? { - return reportStreamFilterDefinitions.find { it.name.equals(name, ignoreCase = true) } + fun findReportStreamFilterDefinitions(name: String): ReportStreamFilterDefinition? = + reportStreamFilterDefinitions.find { + it.name.equals(name, ignoreCase = true) } /* @@ -321,18 +314,14 @@ class Metadata : Logging { } } - fun loadValueSets(vararg sets: ValueSet): Metadata { - return loadValueSetList(sets.toList()) - } + fun loadValueSets(vararg sets: ValueSet): Metadata = loadValueSetList(sets.toList()) private fun loadValueSetList(sets: List): Metadata { this.valueSets = sets.map { normalizeValueSetName(it.name) to it }.toMap() return this } - fun findValueSet(name: String): ValueSet? { - return valueSets[normalizeValueSetName(name)] - } + fun findValueSet(name: String): ValueSet? = valueSets[normalizeValueSetName(name)] private fun readAllValueSets(catalogDir: File): List { // read the .valueset files in the director @@ -349,9 +338,7 @@ class Metadata : Logging { } } - private fun normalizeValueSetName(name: String): String { - return name.lowercase() - } + private fun normalizeValueSetName(name: String): String = name.lowercase() /** * Lookup Tables @@ -426,18 +413,14 @@ class Metadata : Logging { } } - fun findLookupTable(name: String): LookupTable? { - return lookupTableStore[name.lowercase()] - } + fun findLookupTable(name: String): LookupTable? = lookupTableStore[name.lowercase()] /* file name templates */ val fileNameTemplates get() = fileNameTemplatesStore - fun findFileNameTemplate(name: String): FileNameTemplate? { - return fileNameTemplatesStore[name.lowercase()] - } + fun findFileNameTemplate(name: String): FileNameTemplate? = fileNameTemplatesStore[name.lowercase()] private fun loadFileNameTemplates(filePath: String): Metadata { val catalogDir = File(filePath) @@ -496,9 +479,7 @@ class Metadata : Logging { * Get the singleton instance. * @return the metadata instance */ - fun getInstance(): Metadata { - return singletonInstance - } + fun getInstance(): Metadata = singletonInstance /** * The amount of seconds to wait before tables are checked again for updates. diff --git a/prime-router/src/main/kotlin/MimeFormat.kt b/prime-router/src/main/kotlin/MimeFormat.kt index 7fe35a9e96c..a27df188257 100644 --- a/prime-router/src/main/kotlin/MimeFormat.kt +++ b/prime-router/src/main/kotlin/MimeFormat.kt @@ -63,9 +63,7 @@ enum class MimeFormat(val ext: String, val mimeType: String, val isSingleItemFor * @return The corresponding MimeFormat. * @throws IllegalArgumentException If the MIME type does not match any known format. */ - fun valueOfFromMimeType(mimeType: String): MimeFormat { - return entries.find { it.mimeType == mimeType } + fun valueOfFromMimeType(mimeType: String): MimeFormat = entries.find { it.mimeType == mimeType } ?: throw IllegalArgumentException("Unexpected MIME type $mimeType.") - } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/Organization.kt b/prime-router/src/main/kotlin/Organization.kt index fce8db0be4e..da04ad2ad1c 100644 --- a/prime-router/src/main/kotlin/Organization.kt +++ b/prime-router/src/main/kotlin/Organization.kt @@ -54,8 +54,7 @@ open class Organization( /** * Validate the object and return null or an error message */ - fun consistencyErrorMessage(): String? { - return when (jurisdiction) { + fun consistencyErrorMessage(): String? = when (jurisdiction) { Jurisdiction.FEDERAL -> { if (stateCode != null || countyName != null) { "stateCode or countyName not allowed for FEDERAL organizations" @@ -78,11 +77,8 @@ open class Organization( } } } - } - fun makeCopyWithNewScopeAndJwk(scope: String, jwk: Jwk): Organization { - return Organization(this, scope, jwk) - } + fun makeCopyWithNewScopeAndJwk(scope: String, jwk: Jwk): Organization = Organization(this, scope, jwk) } /** diff --git a/prime-router/src/main/kotlin/Receiver.kt b/prime-router/src/main/kotlin/Receiver.kt index 3c7e5641795..5c19267b323 100644 --- a/prime-router/src/main/kotlin/Receiver.kt +++ b/prime-router/src/main/kotlin/Receiver.kt @@ -220,9 +220,7 @@ open class Receiver( } @JsonIgnore - fun isValid(): Boolean { - return numberPerDay in 1..(24 * 60) - } + fun isValid(): Boolean = numberPerDay in 1..(24 * 60) } enum class BatchOperation { @@ -233,10 +231,7 @@ open class Receiver( /** * Options when a receiver's batch is scheduled to run but there are no records for the receiver */ - data class WhenEmpty( - val action: EmptyOperation = EmptyOperation.NONE, - val onlyOncePerDay: Boolean = false, - ) + data class WhenEmpty(val action: EmptyOperation = EmptyOperation.NONE, val onlyOncePerDay: Boolean = false) /** * When it is batch time and there are no records should the receiver get a file or not @@ -286,9 +281,10 @@ open class Receiver( /** Global function to create receiver fullNames using * the [organizationName] and the [receiverName]. */ - fun createFullName(organizationName: String, receiverName: String): String { - return "$organizationName$fullNameSeparator$receiverName" - } + fun createFullName( + organizationName: String, + receiverName: String, + ): String = "$organizationName$fullNameSeparator$receiverName" fun parseFullName(fullName: String): Pair { val splits = fullName.split(Sender.fullNameSeparator) diff --git a/prime-router/src/main/kotlin/ReportStreamFilter.kt b/prime-router/src/main/kotlin/ReportStreamFilter.kt index ed18b910f88..0d9b88a912e 100644 --- a/prime-router/src/main/kotlin/ReportStreamFilter.kt +++ b/prime-router/src/main/kotlin/ReportStreamFilter.kt @@ -22,9 +22,7 @@ fun ReportStreamConditionFilter.codes(): List = this.flatMap { it.codes( abstract class ConditionFilter(val value: String) { abstract fun codes(): List - override fun toString(): String { - return value - } + override fun toString(): String = value } class CodeStringConditionFilter(value: String) : ConditionFilter(value) { diff --git a/prime-router/src/main/kotlin/Schema.kt b/prime-router/src/main/kotlin/Schema.kt index 84561f87885..03de2831b0b 100644 --- a/prime-router/src/main/kotlin/Schema.kt +++ b/prime-router/src/main/kotlin/Schema.kt @@ -73,26 +73,18 @@ data class Schema( private val elementIndex: Map = elements.mapIndexed { index, element -> element.name to index }.toMap() - fun findElement(name: String): Element? { - return elementIndex[name]?.let { elements[it] } - } + fun findElement(name: String): Element? = elementIndex[name]?.let { elements[it] } - fun findElementColumn(name: String): Int? { - return elementIndex[name] - } + fun findElementColumn(name: String): Int? = elementIndex[name] - fun findElementByCsvName(name: String): Element? { - return elements.firstOrNull { e -> + fun findElementByCsvName(name: String): Element? = elements.firstOrNull { e -> e.csvFields?.map { c -> c.name.lowercase() }?.contains(name.lowercase()) ?: false } - } - fun containsElement(name: String): Boolean { - return elementIndex[name] != null - } + fun containsElement(name: String): Boolean = elementIndex[name] != null - fun filterCsvFields(block: (Element) -> Boolean): List { - return elements.filter(block).flatMap { it.csvFields ?: emptyList() } + fun filterCsvFields(block: (Element) -> Boolean): List = elements.filter(block).flatMap { + it.csvFields ?: emptyList() } /** diff --git a/prime-router/src/main/kotlin/Sender.kt b/prime-router/src/main/kotlin/Sender.kt index 47b3a4355cb..1ac323e1a43 100644 --- a/prime-router/src/main/kotlin/Sender.kt +++ b/prime-router/src/main/kotlin/Sender.kt @@ -170,13 +170,12 @@ abstract class Sender( } } - fun createFullName(organizationName: String?, senderName: String?): String? { - return if (!organizationName.isNullOrEmpty() && !senderName.isNullOrEmpty()) { + fun createFullName(organizationName: String?, senderName: String?): String? = + if (!organizationName.isNullOrEmpty() && !senderName.isNullOrEmpty()) { "$organizationName${fullNameSeparator}$senderName" } else { null } - } } } @@ -225,16 +224,12 @@ class UniversalPipelineSender : Sender { /** * To ensure existing functionality, we need to be able to create a straight copy of this UniversalPipelineSender */ - override fun makeCopy(): Sender { - return UniversalPipelineSender(this) - } + override fun makeCopy(): Sender = UniversalPipelineSender(this) /** * For validation, not used in this context. Maybe refactor in the future. */ - override fun consistencyErrorMessage(metadata: Metadata): String? { - return null - } + override fun consistencyErrorMessage(metadata: Metadata): String? = null } open class LegacyPipelineSender : Sender { @@ -275,16 +270,12 @@ open class LegacyPipelineSender : Sender { /** * To ensure existing functionality, we need to be able to create a straight copy of this Sender */ - override fun makeCopy(): Sender { - return LegacyPipelineSender(this) - } + override fun makeCopy(): Sender = LegacyPipelineSender(this) /** * For validation, not used in this context. Maybe refactor in the future. */ - override fun consistencyErrorMessage(metadata: Metadata): String? { - return null - } + override fun consistencyErrorMessage(metadata: Metadata): String? = null } /** @@ -329,9 +320,7 @@ class CovidSender : LegacyPipelineSender { /** * To ensure existing functionality, we need to be able to create a straight copy of this CovidSender */ - override fun makeCopy(): Sender { - return CovidSender(this) - } + override fun makeCopy(): Sender = CovidSender(this) } /** @@ -373,14 +362,10 @@ class MonkeypoxSender : LegacyPipelineSender { /** * To ensure existing functionality, we need to be able to create a straight copy of this MonkeypoxSender */ - override fun makeCopy(): Sender { - return MonkeypoxSender(this) - } + override fun makeCopy(): Sender = MonkeypoxSender(this) /** * For validation, not used in this context. Maybe refactor in the future. */ - override fun consistencyErrorMessage(metadata: Metadata): String? { - return null - } + override fun consistencyErrorMessage(metadata: Metadata): String? = null } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/Translator.kt b/prime-router/src/main/kotlin/Translator.kt index 1fb965c8ac5..9259cecf3bc 100644 --- a/prime-router/src/main/kotlin/Translator.kt +++ b/prime-router/src/main/kotlin/Translator.kt @@ -25,15 +25,9 @@ class Translator(private val metadata: Metadata, private val settings: SettingsP val missing: Set, ) - data class RoutedReport( - val report: Report, - val receiver: Receiver, - ) + data class RoutedReport(val report: Report, val receiver: Receiver) - data class RoutedReportsResult( - val reports: List, - val details: List, - ) + data class RoutedReportsResult(val reports: List, val details: List) /** * Translate and filter by the list of receiver in metadata. Only return reports that have items. diff --git a/prime-router/src/main/kotlin/TranslatorConfiguration.kt b/prime-router/src/main/kotlin/TranslatorConfiguration.kt index 328da4c299f..5854e49153e 100644 --- a/prime-router/src/main/kotlin/TranslatorConfiguration.kt +++ b/prime-router/src/main/kotlin/TranslatorConfiguration.kt @@ -244,9 +244,7 @@ data class FHIRConfiguration * A translation for a Google/Apple Exposure Notification. This translation does not have any options. */ data class GAENConfiguration -@JsonCreator constructor( - val dummy: String? = null, -) : TranslatorConfiguration("GAEN") { +@JsonCreator constructor(val dummy: String? = null) : TranslatorConfiguration("GAEN") { @get:JsonIgnore override val format: MimeFormat get() = MimeFormat.CSV_SINGLE // Single item CSV diff --git a/prime-router/src/main/kotlin/TransportType.kt b/prime-router/src/main/kotlin/TransportType.kt index 2950cc95f84..e2742175c52 100644 --- a/prime-router/src/main/kotlin/TransportType.kt +++ b/prime-router/src/main/kotlin/TransportType.kt @@ -27,22 +27,19 @@ data class SFTPTransportType val port: String, val filePath: String, val credentialName: String? = null, -) : - TransportType("SFTP") +) : TransportType("SFTP") data class EmailTransportType @JsonCreator constructor( val addresses: List, val from: String = "qtv1@cdc.gov", // TODO: default to a better choice -) : - TransportType("EMAIL") +) : TransportType("EMAIL") data class BlobStoreTransportType @JsonCreator constructor( val storageName: String, // this looks for an env var with this name. env var value is the connection string. val containerName: String, // eg, hhsprotect -) : - TransportType("BLOBSTORE") +) : TransportType("BLOBSTORE") data class AS2TransportType @JsonCreator constructor( @@ -52,8 +49,7 @@ data class AS2TransportType val senderEmail: String = "reportstream@cdc.gov", // Default, val mimeType: String = "application/hl7-v2", val contentDescription: String = "SARS-CoV-2 Electronic Lab Results", -) : - TransportType("AS2") +) : TransportType("AS2") /** * The GAEN UUID Format instructs how the UUID field of the GAEN payload is built @@ -83,9 +79,7 @@ data class GAENTransportType } data class NullTransportType -@JsonCreator constructor( - val dummy: String? = null, -) : TransportType("NULL") +@JsonCreator constructor(val dummy: String? = null) : TransportType("NULL") /** * Holds the [gov.cdc.prime.router.transport.SoapTransport] parameters diff --git a/prime-router/src/main/kotlin/ValueSet.kt b/prime-router/src/main/kotlin/ValueSet.kt index 8ec5ce6b383..79e5bec8ebd 100644 --- a/prime-router/src/main/kotlin/ValueSet.kt +++ b/prime-router/src/main/kotlin/ValueSet.kt @@ -57,29 +57,23 @@ data class ValueSet( val system: SetSystem? = null, ) - fun toDisplayFromCode(code: String): String? { - return values.find { code.equals(it.code, ignoreCase = true) }?.display - } + fun toDisplayFromCode(code: String): String? = values.find { code.equals(it.code, ignoreCase = true) }?.display // set a version on the whole value set if you want, but you can still use // the value-specific version if you're adding something from a different version - fun toVersionFromCode(code: String): String? { - return values.find { code.equals(it.code, ignoreCase = true) }?.version + fun toVersionFromCode(code: String): String? = values.find { code.equals(it.code, ignoreCase = true) }?.version ?: this.version - } - fun toSystemFromCode(code: String): String? { - return values.find { code.equals(it.code, ignoreCase = true) }?.system?.toString()?.uppercase() + fun toSystemFromCode(code: String): String? = values.find { + code.equals(it.code, ignoreCase = true) + }?.system?.toString()?.uppercase() ?: this.systemCode - } - fun toCodeFromDisplay(display: String): String? { - return values.find { display.equals(it.display, ignoreCase = true) }?.code - } + fun toCodeFromDisplay(display: String): String? = values.find { + display.equals(it.display, ignoreCase = true) + }?.code - fun toNormalizedCode(code: String): String? { - return values.find { code.equals(it.code, ignoreCase = true) }?.code - } + fun toNormalizedCode(code: String): String? = values.find { code.equals(it.code, ignoreCase = true) }?.code fun mergeAltValues(altValues: List?): ValueSet { // if we have alt values then we need to merge them in diff --git a/prime-router/src/main/kotlin/azure/AdminApiFunctions.kt b/prime-router/src/main/kotlin/azure/AdminApiFunctions.kt index 917f2d13411..500c3794c67 100644 --- a/prime-router/src/main/kotlin/azure/AdminApiFunctions.kt +++ b/prime-router/src/main/kotlin/azure/AdminApiFunctions.kt @@ -34,9 +34,9 @@ class AdminApiFunctions( * level defaults to safe (admin) * @return the Okta authenticator */ - private fun getOktaAuthenticator(level: PrincipalLevel = PrincipalLevel.SYSTEM_ADMIN): OktaAuthentication { - return oktaAuthentication ?: OktaAuthentication(level) - } + private fun getOktaAuthenticator( + level: PrincipalLevel = PrincipalLevel.SYSTEM_ADMIN, + ): OktaAuthentication = oktaAuthentication ?: OktaAuthentication(level) /** * Fetch the list of send_errors. Spans orgs, so should ONLY be done diff --git a/prime-router/src/main/kotlin/azure/ApiKeysFunctions.kt b/prime-router/src/main/kotlin/azure/ApiKeysFunctions.kt index 7984ff7ddd9..7773723f8fc 100644 --- a/prime-router/src/main/kotlin/azure/ApiKeysFunctions.kt +++ b/prime-router/src/main/kotlin/azure/ApiKeysFunctions.kt @@ -300,9 +300,7 @@ class ApiKeysFunctions(private val settingsFacade: SettingsFacade = SettingsFaca ) @PathParam(PARAM_NAME_ORGNAME) @BindingName(PARAM_NAME_ORGNAME) orgName: String, - ): HttpResponseMessage { - return getApiKeysForOrg(request, orgName) - } + ): HttpResponseMessage = getApiKeysForOrg(request, orgName) @FunctionName("getApiKeysV1") @Operation( @@ -371,9 +369,7 @@ class ApiKeysFunctions(private val settingsFacade: SettingsFacade = SettingsFaca ) @PathParam(PARAM_NAME_ORGNAME) @BindingName(PARAM_NAME_ORGNAME) orgName: String, - ): HttpResponseMessage { - return getApiKeysForOrg(request, orgName, true) - } + ): HttpResponseMessage = getApiKeysForOrg(request, orgName, true) @FunctionName("postApiKey") @POST diff --git a/prime-router/src/main/kotlin/azure/ApiResponse.kt b/prime-router/src/main/kotlin/azure/ApiResponse.kt index 13450352a2f..0cf73485227 100644 --- a/prime-router/src/main/kotlin/azure/ApiResponse.kt +++ b/prime-router/src/main/kotlin/azure/ApiResponse.kt @@ -88,8 +88,7 @@ data class ApiResponse(val data: T, @JsonProperty("meta") val metaApiResponse type: String, search: ApiSearch, results: ApiSearchResult, - ): ApiResponse> { - return ApiResponse( + ): ApiResponse> = ApiResponse( results.results, MetaApiResponse( totalCount = results.totalCount, @@ -98,6 +97,5 @@ data class ApiResponse(val data: T, @JsonProperty("meta") val metaApiResponse type = type ) ) - } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/ApiSearch.kt b/prime-router/src/main/kotlin/azure/ApiSearch.kt index 370338ab4ef..ecb4a29c0ed 100644 --- a/prime-router/src/main/kotlin/azure/ApiSearch.kt +++ b/prime-router/src/main/kotlin/azure/ApiSearch.kt @@ -23,9 +23,9 @@ import org.jooq.impl.DSL import kotlin.time.ExperimentalTime data class ApiSearchResult(val totalCount: Int, val filteredCount: Int, val results: List) { - fun map(mappingFunction: (value: T) -> MappedType): ApiSearchResult { - return ApiSearchResult(totalCount, filteredCount, results.map { mappingFunction(it) }) - } + fun map( + mappingFunction: (value: T) -> MappedType, + ): ApiSearchResult = ApiSearchResult(totalCount, filteredCount, results.map { mappingFunction(it) }) } enum class SortDirection { ASC, @@ -46,9 +46,7 @@ interface ApiFilterNames interface ApiFilters, Names : ApiFilterNames> { val terms: Map> - fun getTerm(termName: Names): Class? { - return terms[termName] - } + fun getTerm(termName: Names): Class? = terms[termName] } interface ApiFilter { @@ -78,18 +76,16 @@ abstract class ApiSearchParser< * * @param query the query string */ - private fun parseFromQueryString(query: String): RawApiSearch { - throw NotImplementedError(query) - } + private fun parseFromQueryString(query: String): RawApiSearch = throw NotImplementedError(query) /** * Converts a request body into a RawApiSearch * * @param body the request body */ - private fun parseRawFromRequestBody(body: String): RawApiSearch { - return JacksonMapperUtilities.defaultMapper.readValue(body, RawApiSearch::class.java) - } + private fun parseRawFromRequestBody( + body: String, + ): RawApiSearch = JacksonMapperUtilities.defaultMapper.readValue(body, RawApiSearch::class.java) /** * Function that the subclass must implement to convert a RawApiSearch into the specific @@ -299,7 +295,5 @@ abstract class ApiSearch "receive" Event.EventAction.BATCH -> "batch" Event.EventAction.PROCESS -> "process" Event.EventAction.DESTINATION_FILTER -> "destination-filter" + Event.EventAction.RECEIVER_ENRICHMENT -> "receiver-enrichment" Event.EventAction.RECEIVER_FILTER -> "receiver-filter" Event.EventAction.ROUTE -> "route" Event.EventAction.TRANSLATE -> "translate" @@ -192,9 +183,7 @@ class BlobAccess() : Logging { /** * Obtain the blob connection string for a given environment variable name. */ - fun getBlobConnection(blobConnEnvVar: String = defaultEnvVar): String { - return System.getenv(blobConnEnvVar) - } + fun getBlobConnection(blobConnEnvVar: String = defaultEnvVar): String = System.getenv(blobConnEnvVar) /** * Obtain a client for interacting with the blob store. @@ -202,9 +191,9 @@ class BlobAccess() : Logging { private fun getBlobClient( blobUrl: String, blobConnInfo: BlobContainerMetadata = defaultBlobMetadata, - ): BlobClient { - return BlobClientBuilder().connectionString(blobConnInfo.connectionString).endpoint(blobUrl).buildClient() - } + ): BlobClient = BlobClientBuilder() + .connectionString(blobConnInfo.connectionString) + .endpoint(blobUrl).buildClient() /** * Upload a raw blob [bytes] as [blobName] @@ -226,9 +215,10 @@ class BlobAccess() : Logging { } /** Checks if a blob actually exists in the blobstore */ - fun exists(blobUrl: String, blobConnInfo: BlobContainerMetadata = defaultBlobMetadata): Boolean { - return getBlobClient(blobUrl, blobConnInfo).exists() - } + fun exists( + blobUrl: String, + blobConnInfo: BlobContainerMetadata = defaultBlobMetadata, + ): Boolean = getBlobClient(blobUrl, blobConnInfo).exists() /** * Copies all blobs prefixed with the [directory] value from the soure to destination @@ -485,8 +475,9 @@ class BlobAccess() : Logging { * If one exists for the container name and connection string, the existing one will be reused. * @return the blob container client */ - internal fun getBlobContainer(blobConnInfo: BlobContainerMetadata): BlobContainerClient { - return blobContainerClients.getOrElse(blobConnInfo) { + internal fun getBlobContainer( + blobConnInfo: BlobContainerMetadata, + ): BlobContainerClient = blobContainerClients.getOrElse(blobConnInfo) { val blobServiceClient = BlobServiceClientBuilder() .connectionString(blobConnInfo.connectionString) .buildClient() @@ -505,6 +496,5 @@ class BlobAccess() : Logging { } containerClient } - } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/CheckFunction.kt b/prime-router/src/main/kotlin/azure/CheckFunction.kt index 9b11303c524..30c3dc0bcfd 100644 --- a/prime-router/src/main/kotlin/azure/CheckFunction.kt +++ b/prime-router/src/main/kotlin/azure/CheckFunction.kt @@ -41,10 +41,7 @@ import java.util.logging.Logger class CheckFunction : Logging { // data structure for sftp file - data class SftpFile( - val name: String, - val contents: String, - ) + data class SftpFile(val name: String, val contents: String) /** * A class to wrap the connection check event diff --git a/prime-router/src/main/kotlin/azure/DatabaseAccess.kt b/prime-router/src/main/kotlin/azure/DatabaseAccess.kt index e293e6cdfa2..9dd6fbd8b8e 100644 --- a/prime-router/src/main/kotlin/azure/DatabaseAccess.kt +++ b/prime-router/src/main/kotlin/azure/DatabaseAccess.kt @@ -103,24 +103,22 @@ class DatabaseAccess(val create: DSLContext) : Logging { } /** Make the other calls in the context of a SQL transaction, returning a result */ - fun transactReturning(block: (txn: DataAccessTransaction) -> T): T { - return create.transactionResult { txn: Configuration -> block(txn) } - } + fun transactReturning( + block: (txn: DataAccessTransaction) -> T, + ): T = create.transactionResult { txn: Configuration -> block(txn) } /* * Task queries */ /** Fetch a task record and lock it so other connections can grab it */ - fun fetchAndLockTask(reportId: ReportId, txn: DataAccessTransaction): Task { - return DSL.using(txn) + fun fetchAndLockTask(reportId: ReportId, txn: DataAccessTransaction): Task = DSL.using(txn) .selectFrom(TASK) .where(TASK.REPORT_ID.eq(reportId)) .forUpdate() .fetchOne() ?.into(Task::class.java) ?: error("Could not find $reportId that matches a task") - } /** * Fetch multiple task records and lock them so other connections cannot grab them. @@ -248,13 +246,11 @@ class DatabaseAccess(val create: DSLContext) : Logging { return actionId } - fun fetchTask(reportId: ReportId): Task { - return create.selectFrom(TASK) + fun fetchTask(reportId: ReportId): Task = create.selectFrom(TASK) .where(TASK.REPORT_ID.eq(reportId)) .fetchOne() ?.into(Task::class.java) ?: error("Could not find $reportId that matches a task") - } /** Take a report and put into the database after already serializing the body of the report */ fun insertTask( @@ -664,14 +660,12 @@ class DatabaseAccess(val create: DSLContext) : Logging { * @param childReportId the id of the child to fetch the parent * @return the parent report */ - fun fetchParentReport(childReportId: UUID): ReportFile? { - return create + fun fetchParentReport(childReportId: UUID): ReportFile? = create .select(REPORT_FILE.asterisk()).from(REPORT_FILE) .join(REPORT_LINEAGE) .on(REPORT_LINEAGE.PARENT_REPORT_ID.eq(REPORT_FILE.REPORT_ID)) .where(REPORT_LINEAGE.CHILD_REPORT_ID.eq(childReportId)) .fetchOneInto(ReportFile::class.java) - } fun fetchChildReports( parentReportId: UUID, @@ -692,8 +686,7 @@ class DatabaseAccess(val create: DSLContext) : Logging { name: String, parentId: Int?, txn: DataAccessTransaction, - ): Setting? { - return DSL.using(txn) + ): Setting? = DSL.using(txn) .selectFrom(SETTING) .where( SETTING.IS_ACTIVE.isTrue, @@ -707,7 +700,6 @@ class DatabaseAccess(val create: DSLContext) : Logging { ) .fetchOne() ?.into(Setting::class.java) - } fun fetchSetting( type: SettingType, @@ -793,21 +785,18 @@ class DatabaseAccess(val create: DSLContext) : Logging { return Pair(orgSetting, itemSetting) } - fun fetchSettings(type: SettingType, txn: DataAccessTransaction): List { - return DSL.using(txn) + fun fetchSettings(type: SettingType, txn: DataAccessTransaction): List = DSL.using(txn) .selectFrom(SETTING) .where(SETTING.IS_ACTIVE.isTrue, SETTING.TYPE.eq(type)) .orderBy(SETTING.SETTING_ID) .fetch() .into(Setting::class.java) - } fun fetchSettings( type: SettingType, organizationId: Int, txn: DataAccessTransaction, - ): List { - return DSL.using(txn) + ): List = DSL.using(txn) .select() .from(SETTING) .where( @@ -818,7 +807,6 @@ class DatabaseAccess(val create: DSLContext) : Logging { .orderBy(SETTING.SETTING_ID) .fetch() .into(Setting::class.java) - } /** * data returned by fetchSettingRevisionHistory. Only used to shape json response. @@ -909,8 +897,7 @@ class DatabaseAccess(val create: DSLContext) : Logging { } } - fun insertSetting(setting: Setting, txn: DataAccessTransaction): Int { - return DSL.using(txn) + fun insertSetting(setting: Setting, txn: DataAccessTransaction): Int = DSL.using(txn) .insertInto(SETTING) .set(SETTING.SETTING_ID, DSL.defaultValue(SETTING.SETTING_ID)) .set(SETTING.TYPE, setting.type) @@ -926,7 +913,6 @@ class DatabaseAccess(val create: DSLContext) : Logging { .fetchOne() ?.value1() ?: error("Fetch error") - } fun updateOrganizationId( currentOrganizationId: Int, @@ -1011,8 +997,7 @@ class DatabaseAccess(val create: DSLContext) : Logging { name: String, organizationId: Int?, txn: DataAccessTransaction, - ): Int { - return DSL.using(txn) + ): Int = DSL.using(txn) .select(DSL.max(SETTING.VERSION)) .from(SETTING) .where( @@ -1027,7 +1012,6 @@ class DatabaseAccess(val create: DSLContext) : Logging { .fetchOne() ?.getValue(DSL.max(SETTING.VERSION)) ?: -1 - } fun insertJti(jti: String, expiresAt: OffsetDateTime? = null, txn: DataAccessTransaction) { val jtiCache = JtiCache() @@ -1043,13 +1027,11 @@ class DatabaseAccess(val create: DSLContext) : Logging { .execute() } - fun fetchJti(jti: String, txn: DataAccessTransaction): JtiCache? { - return DSL.using(txn) + fun fetchJti(jti: String, txn: DataAccessTransaction): JtiCache? = DSL.using(txn) .selectFrom(JTI_CACHE) .where(JTI_CACHE.JTI.eq(jti)) .fetchOne() ?.into(JtiCache::class.java) - } /** EmailSchedule queries */ fun fetchEmailSchedules(txn: DataAccessTransaction? = null): List { @@ -1482,8 +1464,7 @@ class DatabaseAccess(val create: DSLContext) : Logging { bodyFormat: String, bodyUrl: String, nextAction: Event, - ): TaskRecord { - return TaskRecord( + ): TaskRecord = TaskRecord( report.id, nextAction.eventAction.toTaskAction(), nextAction.at, @@ -1502,17 +1483,16 @@ class DatabaseAccess(val create: DSLContext) : Logging { null, null, null, + null, null ) - } fun createTask( report: Report, bodyFormat: String, bodyUrl: String, nextAction: Event, - ): Task { - return Task( + ): Task = Task( report.id, nextAction.eventAction.toTaskAction(), nextAction.at, @@ -1531,9 +1511,9 @@ class DatabaseAccess(val create: DSLContext) : Logging { null, null, null, + null, null ) - } /** * Saves metadata to database. Since jooq/postgres does not truncate data that is too long, any diff --git a/prime-router/src/main/kotlin/azure/EmailEngineFunction.kt b/prime-router/src/main/kotlin/azure/EmailEngineFunction.kt index 0895b59d6a5..ec412daba4c 100644 --- a/prime-router/src/main/kotlin/azure/EmailEngineFunction.kt +++ b/prime-router/src/main/kotlin/azure/EmailEngineFunction.kt @@ -28,6 +28,7 @@ import com.sendgrid.helpers.mail.Mail import com.sendgrid.helpers.mail.objects.Email import com.sendgrid.helpers.mail.objects.Personalization import gov.cdc.prime.router.azure.db.enums.SettingType +import gov.cdc.prime.router.azure.db.tables.pojos.Setting import gov.cdc.prime.router.common.BaseEngine import gov.cdc.prime.router.common.HttpClientUtils import gov.cdc.prime.router.secrets.SecretHelper @@ -217,24 +218,19 @@ class EmailScheduleEngine { * * @returns List of organizations for the schedule */ - private fun getOrgs(schedule: EmailSchedule): Iterable { - return (if (schedule.organizations.size > 0) schedule.organizations else fetchAllOrgs()) - } + private fun getOrgs( + schedule: EmailSchedule, + ): Iterable = (if (schedule.organizations.size > 0) schedule.organizations else fetchAllOrgs()) /** * Retrieves the list of all organization supported * * @returns List of all organizations supported */ - private fun fetchAllOrgs(): Iterable { - @Suppress("NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER") - return workflowEngine.db.transactReturning { tx -> - @Suppress("UNRESOLVED_REFERENCE") - val settings = workflowEngine.db.fetchSettings(SettingType.ORGANIZATION, tx) - @Suppress("NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER") - settings.map { it.getName() } + private fun fetchAllOrgs(): Iterable = workflowEngine.db.transactReturning { tx -> + val settings: List = workflowEngine.db.fetchSettings(SettingType.ORGANIZATION, tx) + settings.map { it.name } } - } /** * Validates the JWT Token supplied in the authorization header. To be valid, the token must be @@ -289,9 +285,7 @@ class EmailScheduleEngine { * * @returns encoded org name (ex. DHpima_az_phd ) */ - private fun encodeOrg(org: String): String { - return "DH" + org.replace("-", "_") - } + private fun encodeOrg(org: String): String = "DH" + org.replace("-", "_") /** * Retrieve a list of emails within an organization @@ -324,9 +318,11 @@ class EmailScheduleEngine { ) val users = JSONArray(respStrJson) - for (user in users) emails.add( + for (user in users) { + emails.add( (user as JSONObject).getJSONObject("profile").getString("email") ) + } } } catch (ex: Throwable) { logger.warning("Error in fetching emails, exception: ${ex.message}") diff --git a/prime-router/src/main/kotlin/azure/EmailSenderFunction.kt b/prime-router/src/main/kotlin/azure/EmailSenderFunction.kt index bd837e020b1..1eee418fe10 100644 --- a/prime-router/src/main/kotlin/azure/EmailSenderFunction.kt +++ b/prime-router/src/main/kotlin/azure/EmailSenderFunction.kt @@ -126,14 +126,12 @@ class EmailSenderFunction { * param) to parse many types of request bodies. Currently it only takes a single * class, TosAgreementForm. */ - private fun parseBody(requestBody: String, logger: Logger): TosAgreementForm? { - return try { + private fun parseBody(requestBody: String, logger: Logger): TosAgreementForm? = try { jacksonObjectMapper.readValue(requestBody, TosAgreementForm::class.java) } catch (ex: MismatchedInputException) { logger.info("There was an exception thrown when parsing your JSON") null } - } private fun createMail(body: TosAgreementForm): String { val mail: Mail = Mail() diff --git a/prime-router/src/main/kotlin/azure/Event.kt b/prime-router/src/main/kotlin/azure/Event.kt index cffcc485973..ec4d0e4c6a9 100644 --- a/prime-router/src/main/kotlin/azure/Event.kt +++ b/prime-router/src/main/kotlin/azure/Event.kt @@ -27,6 +27,7 @@ abstract class Event(val eventAction: EventAction, val at: OffsetDateTime?) { PROCESS_WARNING, // when an attempt at a process action fails, but will be retried PROCESS_ERROR, // when an attempt at a process action fails permanently DESTINATION_FILTER, + RECEIVER_ENRICHMENT, RECEIVER_FILTER, RECEIVE, CONVERT, // for universal pipeline converting to FHIR @@ -44,12 +45,12 @@ abstract class Event(val eventAction: EventAction, val at: OffsetDateTime?) { OTHER, // a default/unknown ; - fun toTaskAction(): TaskAction { - return when (this) { + fun toTaskAction(): TaskAction = when (this) { PROCESS -> TaskAction.process PROCESS_WARNING -> TaskAction.process_warning PROCESS_ERROR -> TaskAction.process_error DESTINATION_FILTER -> TaskAction.destination_filter + RECEIVER_ENRICHMENT -> TaskAction.receiver_enrichment RECEIVER_FILTER -> TaskAction.receiver_filter RECEIVE -> TaskAction.receive CONVERT -> TaskAction.convert @@ -67,10 +68,8 @@ abstract class Event(val eventAction: EventAction, val at: OffsetDateTime?) { // OTHER is not an expected value, more of a logical fallback/default used in BlobAccess.uploadBody OTHER -> TaskAction.other } - } - fun toQueueName(): String? { - return when (this) { + fun toQueueName(): String? = when (this) { PROCESS, TRANSLATE, BATCH, @@ -79,11 +78,9 @@ abstract class Event(val eventAction: EventAction, val at: OffsetDateTime?) { -> this.toString().lowercase() else -> null } - } companion object { - fun parseQueueMessage(action: String): EventAction { - return when (action.lowercase()) { + fun parseQueueMessage(action: String): EventAction = when (action.lowercase()) { "process" -> PROCESS "receive" -> RECEIVE "translate" -> TRANSLATE @@ -96,13 +93,13 @@ abstract class Event(val eventAction: EventAction, val at: OffsetDateTime?) { "wipe_error" -> WIPE_ERROR else -> error("Internal Error: $action does not match known action names") } - } } } companion object { - fun parsePrimeRouterQueueMessage(event: String): Event { - return when (val message = JacksonMapperUtilities.defaultMapper.readValue(event)) { + fun parsePrimeRouterQueueMessage( + event: String, + ): Event = when (val message = JacksonMapperUtilities.defaultMapper.readValue(event)) { is ReportEventQueueMessage -> { val at = if (message.at.isNotEmpty()) { OffsetDateTime.parse(message.at) @@ -146,7 +143,6 @@ abstract class Event(val eventAction: EventAction, val at: OffsetDateTime?) { } else -> error("Internal Error: invalid event type: $event") } - } } } @@ -177,13 +173,11 @@ class ProcessEvent( return JacksonMapperUtilities.objectMapper.writeValueAsString(queueMessage) } - override fun equals(other: Any?): Boolean { - return other is ProcessEvent && + override fun equals(other: Any?): Boolean = other is ProcessEvent && eventAction == other.eventAction && reportId == other.reportId && at == other.at && retryToken == other.retryToken - } override fun hashCode(): Int { // vars used in hashCode() must match those in equals() @@ -214,20 +208,16 @@ class ReportEvent( return JacksonMapperUtilities.objectMapper.writeValueAsString(queueMessage) } - override fun equals(other: Any?): Boolean { - return other is ReportEvent && + override fun equals(other: Any?): Boolean = other is ReportEvent && eventAction == other.eventAction && reportId == other.reportId && at == other.at && retryToken == other.retryToken - } - override fun hashCode(): Int { - return (7 * eventAction.hashCode()) + + override fun hashCode(): Int = (7 * eventAction.hashCode()) + (31 * reportId.hashCode()) + (17 * at.hashCode()) + (19 * retryToken.hashCode()) - } companion object { const val eventType = "report" @@ -251,18 +241,14 @@ class BatchEvent( return JacksonMapperUtilities.objectMapper.writeValueAsString(queueMessage) } - override fun equals(other: Any?): Boolean { - return other is BatchEvent && + override fun equals(other: Any?): Boolean = other is BatchEvent && eventAction == other.eventAction && receiverName == other.receiverName && at == other.at - } - override fun hashCode(): Int { - return (7 * eventAction.hashCode()) + + override fun hashCode(): Int = (7 * eventAction.hashCode()) + (19 * receiverName.hashCode()) + (17 * at.hashCode()) - } // this should say 'batch' but will break production on deploy if there is anything in the batch queue // when it goes to prod. This value is used only to queue and dequeue message types diff --git a/prime-router/src/main/kotlin/azure/HistoryFunctions.kt b/prime-router/src/main/kotlin/azure/HistoryFunctions.kt index 1266d823d59..7c96a429233 100644 --- a/prime-router/src/main/kotlin/azure/HistoryFunctions.kt +++ b/prime-router/src/main/kotlin/azure/HistoryFunctions.kt @@ -53,17 +53,9 @@ class Facility private constructor( } } -class Action private constructor( - val date: String?, - val user: String?, - val action: String?, -) { +class Action private constructor(val date: String?, val user: String?, val action: String?) { - data class Builder( - var date: String? = null, - var user: String? = null, - var action: String? = null, - ) { + data class Builder(var date: String? = null, var user: String? = null, var action: String? = null) { fun date(date: String) = apply { this.date = date } fun user(user: String) = apply { this.user = user } @@ -153,8 +145,7 @@ class ReportView private constructor( data class FileReturn(val content: String, val filename: String, val mimetype: String) -class GetReports : - BaseHistoryFunction() { +class GetReports : BaseHistoryFunction() { @FunctionName("getReports") @StorageAccount("AzureWebJobsStorage") fun run( @@ -204,8 +195,7 @@ class GetReports : } } -class GetReportById : - BaseHistoryFunction() { +class GetReportById : BaseHistoryFunction() { @FunctionName("getReportById") @StorageAccount("AzureWebJobsStorage") fun run( @@ -217,13 +207,10 @@ class GetReportById : ) request: HttpRequestMessage, @BindingName("reportId") reportId: String, context: ExecutionContext, - ): HttpResponseMessage { - return getReportById(request, reportId, context) - } + ): HttpResponseMessage = getReportById(request, reportId, context) } -class GetFacilitiesByReportId : - BaseHistoryFunction() { +class GetFacilitiesByReportId : BaseHistoryFunction() { @FunctionName("getFacilitiesByReportId") @StorageAccount("AzureWebJobsStorage") fun run( @@ -235,9 +222,7 @@ class GetFacilitiesByReportId : ) request: HttpRequestMessage, @BindingName("reportId") reportId: String, context: ExecutionContext, - ): HttpResponseMessage { - return getFacilitiesForReportId(request, reportId, context) - } + ): HttpResponseMessage = getFacilitiesForReportId(request, reportId, context) } open class BaseHistoryFunction : Logging { @@ -260,7 +245,6 @@ open class BaseHistoryFunction : Logging { organizationName ?: authClaims.organization.name ) - @Suppress("NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER") val reports = headers.sortedByDescending { it.createdAt }.map { // removing the call for facilities for now so we can call a // method directly to just get the facilities and display them then @@ -332,7 +316,8 @@ open class BaseHistoryFunction : Logging { val requestOrgName: String = request.headers["organization"] ?: return HttpUtilities.bad(request, "Missing organization in header") - if (claims == null || !claims.authorized( + if (claims == null || + !claims.authorized( setOf( PRIME_ADMIN_PATTERN, "$requestOrgName.*.*", @@ -470,10 +455,7 @@ open class BaseHistoryFunction : Logging { } } - data class AuthClaims( - val userName: String, - val organization: Organization, - ) + data class AuthClaims(val userName: String, val organization: Organization) /** * returns null if not authorized, otherwise returns a set of claims. diff --git a/prime-router/src/main/kotlin/azure/HttpUtilities.kt b/prime-router/src/main/kotlin/azure/HttpUtilities.kt index 9a8ae91165b..4fab35c86e3 100644 --- a/prime-router/src/main/kotlin/azure/HttpUtilities.kt +++ b/prime-router/src/main/kotlin/azure/HttpUtilities.kt @@ -40,70 +40,58 @@ class HttpUtilities { fun okResponse( request: HttpRequestMessage, responseBody: String, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.OK) .body(responseBody) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .build() - } fun okResponse( request: HttpRequestMessage, responseBody: String, lastModified: OffsetDateTime?, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.OK) .body(responseBody) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .also { addHeaderIfModified(it, lastModified) } .build() - } fun okResponse( request: HttpRequestMessage, lastModified: OffsetDateTime?, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.OK) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .also { addHeaderIfModified(it, lastModified) } .build() - } fun okJSONResponse( request: HttpRequestMessage, body: T, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.OK) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .body(mapper.writeValueAsString(body)) .build() - } fun okJSONResponse( request: HttpRequestMessage, body: ApiResponse, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.OK) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .body(mapper.writeValueAsString(body)) .build() - } fun createdResponse( request: HttpRequestMessage, responseBody: String, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.CREATED) .body(responseBody) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .build() - } /** * Allows the validator to figure out specific failure, and pass it in here. @@ -115,32 +103,26 @@ class HttpUtilities { request: HttpRequestMessage, responseBody: String, httpStatus: HttpStatus, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(httpStatus) .body(responseBody) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .build() - } fun badRequestResponse( request: HttpRequestMessage, responseBody: String, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.BAD_REQUEST) .body(responseBody) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .build() - } fun unauthorizedResponse( request: HttpRequestMessage, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.UNAUTHORIZED) .build() - } /** * Builds an HttpResponseMessage with an unauthorized(422) status @@ -149,22 +131,19 @@ class HttpUtilities { fun unauthorizedResponse( request: HttpRequestMessage, responseBody: T, - ): HttpResponseMessage { - return request.createResponseBuilder(HttpStatus.UNAUTHORIZED).body(mapper.writeValueAsString(responseBody)) + ): HttpResponseMessage = request.createResponseBuilder(HttpStatus.UNAUTHORIZED) + .body(mapper.writeValueAsString(responseBody)) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .build() - } fun unauthorizedResponse( request: HttpRequestMessage, responseBody: String, - ): HttpResponseMessage { - return request + ): HttpResponseMessage = request .createResponseBuilder(HttpStatus.UNAUTHORIZED) .body(responseBody) .header(HttpHeaders.CONTENT_TYPE, jsonMediaType) .build() - } fun notFoundResponse( request: HttpRequestMessage, @@ -203,9 +182,7 @@ class HttpUtilities { return response.build() } - fun errorJson(message: String): String { - return """{"error": "$message"}""" - } + fun errorJson(message: String): String = """{"error": "$message"}""" private fun addHeaderIfModified( builder: HttpResponseMessage.Builder, @@ -375,9 +352,11 @@ class HttpUtilities { * A generic function that posts data to a URL
    . * Returns a Pair (HTTP response code, text of the response) */ - fun postHttp(urlStr: String, bytes: ByteArray, headers: List>? = null): Pair { - return httpRequest("POST", urlStr, bytes, headers) - } + fun postHttp( + urlStr: String, + bytes: ByteArray, + headers: List>? = null, + ): Pair = httpRequest("POST", urlStr, bytes, headers) /** * A generic function for a GET to a URL
    . @@ -386,9 +365,7 @@ class HttpUtilities { fun getHttp( urlStr: String, headers: List>? = null, - ): Pair { - return httpRequest("GET", urlStr, null, headers) - } + ): Pair = httpRequest("GET", urlStr, null, headers) /** * A generic function for a DELETE to a URL
    . @@ -398,9 +375,7 @@ class HttpUtilities { urlStr: String, bytes: ByteArray, headers: List>? = null, - ): Pair { - return httpRequest("DELETE", urlStr, bytes, headers) - } + ): Pair = httpRequest("DELETE", urlStr, bytes, headers) /** * Private generic function for creating an http request diff --git a/prime-router/src/main/kotlin/azure/LookupTableFunctions.kt b/prime-router/src/main/kotlin/azure/LookupTableFunctions.kt index c9ceeb82e27..3dd49704fe3 100644 --- a/prime-router/src/main/kotlin/azure/LookupTableFunctions.kt +++ b/prime-router/src/main/kotlin/azure/LookupTableFunctions.kt @@ -20,9 +20,8 @@ import org.jooq.JSONB /** * Functions to manage lookup tables. */ -class LookupTableFunctions( - private val lookupTableAccess: DatabaseLookupTableAccess = DatabaseLookupTableAccess(), -) : Logging { +class LookupTableFunctions(private val lookupTableAccess: DatabaseLookupTableAccess = DatabaseLookupTableAccess()) : + Logging { /** * Mapper to convert objects to JSON. */ @@ -300,14 +299,12 @@ class LookupTableFunctions( /** * @return true if these [claims] allow the user to do writes to lookup tables. Otherwise return false */ - private fun authorizedForLookupWrite(claims: AuthenticatedClaims): Boolean { - return if (!claims.isPrimeAdmin) { + private fun authorizedForLookupWrite(claims: AuthenticatedClaims): Boolean = if (!claims.isPrimeAdmin) { logger.warn("Request to write lookup tables is Unauthorized. Must be a PrimeAdmin.") false } else { true } - } /** * Converts [rows] into a JSON string. diff --git a/prime-router/src/main/kotlin/azure/MessagesFunctions.kt b/prime-router/src/main/kotlin/azure/MessagesFunctions.kt index 909ea1af44f..4226a15fdf7 100644 --- a/prime-router/src/main/kotlin/azure/MessagesFunctions.kt +++ b/prime-router/src/main/kotlin/azure/MessagesFunctions.kt @@ -28,9 +28,7 @@ const val MESSAGE_ID_PARAMETER = "messageId" * Search and retrieve messages. In this case, "messages" means data related to * specific records in the `covid_result_metadata` table */ -class MessagesFunctions( - private val dbAccess: DatabaseAccess = DatabaseAccess(), -) : Logging { +class MessagesFunctions(private val dbAccess: DatabaseAccess = DatabaseAccess()) : Logging { /** * entry point for the /messages endpoint, * which searches for a given message_id in the covid_result_metadata table diff --git a/prime-router/src/main/kotlin/azure/QueueAccess.kt b/prime-router/src/main/kotlin/azure/QueueAccess.kt index 58795c46e9e..1edb2ed3cb7 100644 --- a/prime-router/src/main/kotlin/azure/QueueAccess.kt +++ b/prime-router/src/main/kotlin/azure/QueueAccess.kt @@ -86,8 +86,7 @@ object QueueAccess { * Creates the queue client for the given queue [name] or reuses an existing one. * @return the queue client */ - private fun createQueueClient(name: String): QueueClient { - return if (clients.containsKey(name)) { + private fun createQueueClient(name: String): QueueClient = if (clients.containsKey(name)) { clients[name]!! } else { clients[name] = QueueServiceClientBuilder() @@ -96,5 +95,4 @@ object QueueAccess { .createQueue(name) clients[name]!! } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/ReportFunction.kt b/prime-router/src/main/kotlin/azure/ReportFunction.kt index 00a2b8294ea..20bd7e20152 100644 --- a/prime-router/src/main/kotlin/azure/ReportFunction.kt +++ b/prime-router/src/main/kotlin/azure/ReportFunction.kt @@ -243,8 +243,7 @@ class ReportFunction( request: HttpRequestMessage, blobAccess: BlobAccess.Companion = BlobAccess, defaultBlobMetadata: BlobAccess.BlobContainerMetadata = BlobAccess.defaultBlobMetadata, - ): HttpResponseMessage { - return try { + ): HttpResponseMessage = try { val updatedBlobMetadata = defaultBlobMetadata.copy(containerName = "test-bank") val results = blobAccess.listBlobs("", updatedBlobMetadata) val reports = mutableListOf() @@ -271,13 +270,8 @@ class ReportFunction( logger.error("Unable to fetch messages from test bank", e) HttpUtilities.internalErrorResponse(request) } - } - class TestReportInfo( - var dateCreated: String, - var fileName: String, - var reportBody: String, - ) + class TestReportInfo(var dateCreated: String, var fileName: String, var reportBody: String) /** * GET report to download @@ -428,9 +422,7 @@ class ReportFunction( actionHistory.queueMessages(workflowEngine) } - private fun extractPayloadNameFromFilePath(filename: String): String { - return Path(filename).fileName.toString() - } + private fun extractPayloadNameFromFilePath(filename: String): String = Path(filename).fileName.toString() private fun getSenderIdFromFilePath(filename: String): String { val path = Path(filename) diff --git a/prime-router/src/main/kotlin/azure/RequestFunction.kt b/prime-router/src/main/kotlin/azure/RequestFunction.kt index e892befec42..e7bb9288533 100644 --- a/prime-router/src/main/kotlin/azure/RequestFunction.kt +++ b/prime-router/src/main/kotlin/azure/RequestFunction.kt @@ -29,9 +29,7 @@ const val REMOVE_PII = "removePII" /** * Base class for ReportFunction and ValidateFunction */ -abstract class RequestFunction( - private val workflowEngine: WorkflowEngine = WorkflowEngine(), -) { +abstract class RequestFunction(private val workflowEngine: WorkflowEngine = WorkflowEngine()) { /** * The data that wraps a request that we receive from a sender. */ diff --git a/prime-router/src/main/kotlin/azure/SenderFilesFunction.kt b/prime-router/src/main/kotlin/azure/SenderFilesFunction.kt index dc1869f561c..1b97fe8b862 100644 --- a/prime-router/src/main/kotlin/azure/SenderFilesFunction.kt +++ b/prime-router/src/main/kotlin/azure/SenderFilesFunction.kt @@ -38,8 +38,7 @@ class SenderFilesFunction( authLevel = AuthorizationLevel.ANONYMOUS, route = "sender-files" ) request: HttpRequestMessage, - ): HttpResponseMessage { - return oktaAuthentication.checkAccess(request) { + ): HttpResponseMessage = oktaAuthentication.checkAccess(request) { try { val parameters = checkParameters(request) val result = processRequest(parameters) @@ -54,21 +53,16 @@ class SenderFilesFunction( HttpUtilities.internalErrorResponse(request) } } - } /** * To indicate a bad request error throw an [IllegalArgumentException] with [message] */ - private fun badRequest(message: String): Nothing { - throw IllegalArgumentException(message) - } + private fun badRequest(message: String): Nothing = throw IllegalArgumentException(message) /** * To indicate a not found error throw an [FileNotFoundException] with [message] */ - private fun notFound(message: String): Nothing { - throw FileNotFoundException(message) - } + private fun notFound(message: String): Nothing = throw FileNotFoundException(message) /** * Encapsulates the possible query parameters @@ -146,10 +140,7 @@ class SenderFilesFunction( ) } - data class ProcessResult( - val payload: String, - val reportsIds: List? = null, - ) + data class ProcessResult(val payload: String, val reportsIds: List? = null) /** * Main logic of the Azure function. Useful for unit testing. @@ -171,17 +162,14 @@ class SenderFilesFunction( return ProcessResult(payload, senderReports.map { it.reportId }) } - private fun findOutputFile(parameters: FunctionParameters): ReportFile { - return when { + private fun findOutputFile(parameters: FunctionParameters): ReportFile = when { parameters.reportId != null -> dbAccess.fetchReportFile(parameters.reportId) parameters.reportFileName != null -> dbAccess.fetchReportFileByBlobURL(parameters.reportFileName) else -> null } ?: notFound("Could not find the specified report-file") - } - private fun findSenderItems(reportId: UUID, offset: Int, limit: Int): List { - return dbAccess.fetchSenderItems(reportId, offset, limit) - } + private fun findSenderItems(reportId: UUID, offset: Int, limit: Int): List = + dbAccess.fetchSenderItems(reportId, offset, limit) /** * Given a list of receiver items with their associated sender items in [items], @@ -233,28 +221,26 @@ class SenderFilesFunction( } } - private fun cutContent(reportBlob: String, senderFormat: MimeFormat, itemIndices: List): String { - return when (senderFormat) { + private fun cutContent(reportBlob: String, senderFormat: MimeFormat, itemIndices: List): String = + when (senderFormat) { MimeFormat.CSV -> CsvUtilities.cut(reportBlob, itemIndices) MimeFormat.HL7, MimeFormat.HL7_BATCH -> Hl7Utilities.cut(reportBlob, itemIndices) else -> throw IllegalStateException("Sender format $senderFormat is not supported") } - } /** * Write as a JSON string */ - private fun List.serialize(): String { - return mapper.writeValueAsString(this) - } + private fun List.serialize(): String = mapper.writeValueAsString(this) /** * Create a log message for the purpose of recording who downloaded what. * [reportIds] tell the what. [userName] tells the who. */ - private fun fromAuditMessage(userName: String, reportIds: List?): String { - return "User $userName has downloaded these reports through the sender-file API: $reportIds" - } + private fun fromAuditMessage( + userName: String, + reportIds: List?, + ): String = "User $userName has downloaded these reports through the sender-file API: $reportIds" companion object { /** @@ -294,12 +280,10 @@ class SenderFilesFunction( private val mapper = JacksonMapperUtilities.defaultMapper - private fun mapBodyFormatToSenderFormat(bodyFormat: String): MimeFormat { - return when (bodyFormat) { + private fun mapBodyFormatToSenderFormat(bodyFormat: String): MimeFormat = when (bodyFormat) { "CSV", "CSV_SINGLE", "INTERNAL" -> MimeFormat.CSV "HL7", "HL7_BATCH" -> MimeFormat.HL7 else -> error("Unknown body format type: $bodyFormat") } - } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/SettingsFacade.kt b/prime-router/src/main/kotlin/azure/SettingsFacade.kt index 7282c2fb955..1f5b5c20136 100644 --- a/prime-router/src/main/kotlin/azure/SettingsFacade.kt +++ b/prime-router/src/main/kotlin/azure/SettingsFacade.kt @@ -32,10 +32,9 @@ import java.time.OffsetDateTime * Settings for Organization, Receivers, and Senders from the Azure Database. * Contains all business logic regarding settings as well as JSON serialization. */ -class SettingsFacade( - private val metadata: Metadata, - private val db: DatabaseAccess = DatabaseAccess(), -) : SettingsProvider, Logging { +class SettingsFacade(private val metadata: Metadata, private val db: DatabaseAccess = DatabaseAccess()) : + SettingsProvider, + Logging { enum class AccessResult { SUCCESS, CREATED, @@ -60,9 +59,7 @@ class SettingsFacade( override val receivers: Collection get() = findSettings(ReceiverAPI::class.java) - override fun findOrganization(name: String): Organization? { - return findSetting(name, OrganizationAPI::class.java) - } + override fun findOrganization(name: String): Organization? = findSetting(name, OrganizationAPI::class.java) override fun findReceiver(fullName: String): Receiver? { try { @@ -84,9 +81,8 @@ class SettingsFacade( } } - override fun findOrganizationAndReceiver(fullName: String): Pair? { - return findOrganizationAndReceiver(fullName, null) - } + override fun findOrganizationAndReceiver(fullName: String): Pair? = + findOrganizationAndReceiver(fullName, null) fun findSettingAsJson( name: String, @@ -97,9 +93,7 @@ class SettingsFacade( return mapper.writeValueAsString(result) } - fun getLastModified(): OffsetDateTime? { - return db.fetchLastModified() - } + fun getLastModified(): OffsetDateTime? = db.fetchLastModified() private fun findSetting( name: String, @@ -308,18 +302,14 @@ class SettingsFacade( SettingsFacade(Metadata.getInstance(), DatabaseAccess()) } - private fun settingTypeFromClass(className: String): SettingType { - return when (className) { + private fun settingTypeFromClass(className: String): SettingType = when (className) { OrganizationAPI::class.qualifiedName -> SettingType.ORGANIZATION ReceiverAPI::class.qualifiedName -> SettingType.RECEIVER Sender::class.qualifiedName -> SettingType.SENDER else -> error("Internal Error: Unknown classname: $className") } - } - private fun errorJson(message: String): String { - return """{"error": "$message"}""" - } + private fun errorJson(message: String): String = """{"error": "$message"}""" } } @@ -361,9 +351,7 @@ class OrganizationAPI SettingAPI { @get:JsonIgnore override val organizationName: String? = null - override fun consistencyErrorMessage(metadata: Metadata): String? { - return this.consistencyErrorMessage() - } + override fun consistencyErrorMessage(metadata: Metadata): String? = this.consistencyErrorMessage() } @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/prime-router/src/main/kotlin/azure/SettingsFunctions.kt b/prime-router/src/main/kotlin/azure/SettingsFunctions.kt index ab054e64b62..c49cef5fad2 100644 --- a/prime-router/src/main/kotlin/azure/SettingsFunctions.kt +++ b/prime-router/src/main/kotlin/azure/SettingsFunctions.kt @@ -19,9 +19,7 @@ import org.apache.logging.log4j.kotlin.Logging /* * Settings API */ -class SettingsFunction( - settingsFacade: SettingsFacade = SettingsFacade.common, -) : BaseFunction(settingsFacade) { +class SettingsFunction(settingsFacade: SettingsFacade = SettingsFacade.common) : BaseFunction(settingsFacade) { /** * Get a full list of organizations and their settings. * @param request Incoming http request @@ -36,13 +34,11 @@ class SettingsFunction( authLevel = AuthorizationLevel.ANONYMOUS, route = "settings/organizations" ) request: HttpRequestMessage, - ): HttpResponseMessage { - return when (request.httpMethod) { + ): HttpResponseMessage = when (request.httpMethod) { HttpMethod.HEAD -> getHead(request) HttpMethod.GET -> getList(request, OrganizationAPI::class.java) else -> error("Unsupported method") } - } /** * Get settings for the given organization. @@ -60,9 +56,7 @@ class SettingsFunction( route = "settings/organizations/{organizationName}" ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, - ): HttpResponseMessage { - return getOne(request, organizationName, OrganizationAPI::class.java, organizationName) - } + ): HttpResponseMessage = getOne(request, organizationName, OrganizationAPI::class.java, organizationName) /** * Update settings for an organization @@ -81,13 +75,11 @@ class SettingsFunction( route = "settings/organizations/{organizationName}" ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, - ): HttpResponseMessage { - return updateOne( + ): HttpResponseMessage = updateOne( request, organizationName, OrganizationAPI::class.java ) - } /** * Get senders for an organization @@ -105,9 +97,7 @@ class SettingsFunction( route = "settings/organizations/{organizationName}/senders" ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, - ): HttpResponseMessage { - return getList(request, Sender::class.java, organizationName) - } + ): HttpResponseMessage = getList(request, Sender::class.java, organizationName) /** * Get a single sender for an organization @@ -127,9 +117,7 @@ class SettingsFunction( ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, @BindingName("senderName") senderName: String, - ): HttpResponseMessage { - return getOne(request, senderName, Sender::class.java, organizationName) - } + ): HttpResponseMessage = getOne(request, senderName, Sender::class.java, organizationName) /** * Update a single sender for an organization @@ -150,14 +138,12 @@ class SettingsFunction( ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, @BindingName("senderName") senderName: String, - ): HttpResponseMessage { - return updateOne( + ): HttpResponseMessage = updateOne( request, senderName, Sender::class.java, organizationName ) - } /** * Get receiver for an organization @@ -176,9 +162,7 @@ class SettingsFunction( route = "settings/organizations/{organizationName}/receivers" ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, - ): HttpResponseMessage { - return getList(request, ReceiverAPI::class.java, organizationName) - } + ): HttpResponseMessage = getList(request, ReceiverAPI::class.java, organizationName) /** * Get a single receiver for an organization @@ -198,9 +182,7 @@ class SettingsFunction( ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, @BindingName("receiverName") receiverName: String, - ): HttpResponseMessage { - return getOne(request, receiverName, ReceiverAPI::class.java, organizationName) - } + ): HttpResponseMessage = getOne(request, receiverName, ReceiverAPI::class.java, organizationName) /** * Update one receiver for an organization @@ -221,14 +203,12 @@ class SettingsFunction( ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, @BindingName("receiverName") receiverName: String, - ): HttpResponseMessage { - return updateOne( + ): HttpResponseMessage = updateOne( request, receiverName, ReceiverAPI::class.java, organizationName ) - } /** * Get a history of revisions for an organization's settings (by type). @@ -257,23 +237,19 @@ class SettingsFunction( ) request: HttpRequestMessage, @BindingName("organizationName") organizationName: String, @BindingName("settingSelector") settingSelector: String, - ): HttpResponseMessage { - return try { + ): HttpResponseMessage = try { // verify the settingsTypeString is in the allowed setting enumeration. val settingType = SettingType.valueOf(settingSelector.uppercase()) getListHistory(request, organizationName, settingType) } catch (e: EnumConstantNotPresentException) { HttpUtilities.badRequestResponse(request, "Invalid setting selector parameter") } - } } /** * Common Settings API */ -open class BaseFunction( - private val facade: SettingsFacade, -) : Logging { +open class BaseFunction(private val facade: SettingsFacade) : Logging { /** * Gets the list of settings for a given organization * @param request Incoming http request @@ -318,7 +294,8 @@ open class BaseFunction( settingType: SettingType, ): HttpResponseMessage { val claims = AuthenticatedClaims.authenticate(request) - if (claims == null || !claims.authorized( + if (claims == null || + !claims.authorized( setOf(PRIME_ADMIN_PATTERN, "$organizationName.*.admin", "$organizationName.*.user") ) ) { @@ -359,7 +336,8 @@ open class BaseFunction( organizationName: String? = null, ): HttpResponseMessage { val claims = AuthenticatedClaims.authenticate(request) - if (claims == null || !claims.authorized( + if (claims == null || + !claims.authorized( setOf(PRIME_ADMIN_PATTERN, "$organizationName.*.admin", "$organizationName.*.user") ) ) { @@ -424,14 +402,12 @@ open class BaseFunction( request: HttpRequestMessage, result: SettingsFacade.AccessResult, outputBody: String, - ): HttpResponseMessage { - return when (result) { + ): HttpResponseMessage = when (result) { SettingsFacade.AccessResult.SUCCESS -> HttpUtilities.okResponse(request, outputBody) SettingsFacade.AccessResult.CREATED -> HttpUtilities.createdResponse(request, outputBody) SettingsFacade.AccessResult.NOT_FOUND -> HttpUtilities.notFoundResponse(request) SettingsFacade.AccessResult.BAD_REQUEST -> HttpUtilities.badRequestResponse(request, outputBody) } - } /** * Convert a message to a JSON error diff --git a/prime-router/src/main/kotlin/azure/SubmissionTableService.kt b/prime-router/src/main/kotlin/azure/SubmissionTableService.kt index 0dd968afe3e..6aef5d7fbb6 100644 --- a/prime-router/src/main/kotlin/azure/SubmissionTableService.kt +++ b/prime-router/src/main/kotlin/azure/SubmissionTableService.kt @@ -24,9 +24,7 @@ class SubmissionTableService private constructor() : Logging { SubmissionTableService() } - fun getInstance(): SubmissionTableService { - return singletonInstance - } + fun getInstance(): SubmissionTableService = singletonInstance } private val tableName = "submission" diff --git a/prime-router/src/main/kotlin/azure/TableAccess.kt b/prime-router/src/main/kotlin/azure/TableAccess.kt index f4c4b50eb0e..a4fa5f8af04 100644 --- a/prime-router/src/main/kotlin/azure/TableAccess.kt +++ b/prime-router/src/main/kotlin/azure/TableAccess.kt @@ -34,11 +34,9 @@ class TableAccess : Logging { private var tableServiceClient: TableServiceClient = buildClient() - private fun buildClient(): TableServiceClient { - return TableServiceClientBuilder() + private fun buildClient(): TableServiceClient = TableServiceClientBuilder() .connectionString(getConnectionString()) .buildClient() - } fun reset() { tableServiceClient = buildClient() diff --git a/prime-router/src/main/kotlin/azure/TokenFunction.kt b/prime-router/src/main/kotlin/azure/TokenFunction.kt index 2b63d422165..99d23fe13e9 100644 --- a/prime-router/src/main/kotlin/azure/TokenFunction.kt +++ b/prime-router/src/main/kotlin/azure/TokenFunction.kt @@ -42,9 +42,7 @@ data class OAuthError( @JsonIgnore val errorUriLocation: String, ) { @JsonProperty("error_uri") - fun getErrorUri(): String { - return "$OAUTH_ERROR_BASE_LOCATION#$errorUriLocation" - } + fun getErrorUri(): String = "$OAUTH_ERROR_BASE_LOCATION#$errorUriLocation" } /** diff --git a/prime-router/src/main/kotlin/azure/ValidateFunction.kt b/prime-router/src/main/kotlin/azure/ValidateFunction.kt index 98a11008ce5..ee6b617354c 100644 --- a/prime-router/src/main/kotlin/azure/ValidateFunction.kt +++ b/prime-router/src/main/kotlin/azure/ValidateFunction.kt @@ -34,7 +34,8 @@ import java.time.OffsetDateTime class ValidateFunction( private val workflowEngine: WorkflowEngine = WorkflowEngine(), private val actionHistory: ActionHistory = ActionHistory(TaskAction.receive), -) : Logging, RequestFunction(workflowEngine) { +) : RequestFunction(workflowEngine), + Logging { /** * entry point for the /validate endpoint, which validates a potential submission without writing diff --git a/prime-router/src/main/kotlin/azure/WorkflowEngine.kt b/prime-router/src/main/kotlin/azure/WorkflowEngine.kt index 240602e6855..2d70fc03948 100644 --- a/prime-router/src/main/kotlin/azure/WorkflowEngine.kt +++ b/prime-router/src/main/kotlin/azure/WorkflowEngine.kt @@ -171,9 +171,7 @@ class WorkflowEngine( /** * Returns true if the [itemHash] passed in is already present in the database */ - fun isDuplicateItem(itemHash: String): Boolean { - return db.isDuplicateItem(itemHash) - } + fun isDuplicateItem(itemHash: String): Boolean = db.isDuplicateItem(itemHash) /** * Record a received [report] from a [sender] into the action history and save the original [rawBody] @@ -752,8 +750,7 @@ class WorkflowEngine( /** * Create a report object from a header including loading the blob data associated with it */ - fun createReport(header: Header): Report { - return when (header.task.bodyFormat) { + fun createReport(header: Header): Report = when (header.task.bodyFormat) { // TODO after the CSV internal format is flushed from the system, this code will be safe to remove "CSV", "CSV_SINGLE" -> { val result = csvSerializer.readExternal( @@ -780,14 +777,11 @@ class WorkflowEngine( else -> error("Unsupported read format") } - } /** * Create a report object from a header including loading the blob data associated with it */ - fun readBody(header: Header): ByteArray { - return BlobAccess.downloadBlobAsByteArray(header.task.bodyUrl) - } + fun readBody(header: Header): ByteArray = BlobAccess.downloadBlobAsByteArray(header.task.bodyUrl) fun recordAction(actionHistory: ActionHistory, txn: Configuration? = null) { if (txn != null) { @@ -800,8 +794,7 @@ class WorkflowEngine( private fun findOrganizationAndReceiver( fullName: String, txn: DataAccessTransaction? = null, - ): Pair { - return if (settings is SettingsFacade) { + ): Pair = if (settings is SettingsFacade) { val (organization, receiver) = (settings).findOrganizationAndReceiver(fullName, txn) ?: error("Receiver not found in database: $fullName") Pair(organization, receiver) @@ -810,14 +803,11 @@ class WorkflowEngine( ?: error("Invalid receiver name: $fullName") Pair(organization, receiver) } - } fun fetchDownloadableReportFiles( since: OffsetDateTime?, organizationName: String, - ): List { - return db.fetchDownloadableReportFiles(since, organizationName) - } + ): List = db.fetchDownloadableReportFiles(since, organizationName) /** * The header class provides the information needed to process a task. @@ -887,8 +877,7 @@ class WorkflowEngine( retryToken: String? = null, txn: DataAccessTransaction, ) { - fun finishedField(currentEventAction: Event.EventAction): Field { - return when (currentEventAction) { + fun finishedField(currentEventAction: Event.EventAction): Field = when (currentEventAction) { Event.EventAction.RECEIVE -> Tables.TASK.TRANSLATED_AT Event.EventAction.PROCESS -> Tables.TASK.PROCESSED_AT // we don't really use these *_AT columns for anything at this point, and 'convert' is another name @@ -896,6 +885,7 @@ class WorkflowEngine( Event.EventAction.CONVERT -> Tables.TASK.PROCESSED_AT Event.EventAction.ROUTE -> Tables.TASK.ROUTED_AT Event.EventAction.DESTINATION_FILTER -> Tables.TASK.DESTINATION_FILTERED_AT + Event.EventAction.RECEIVER_ENRICHMENT -> Tables.TASK.RECEIVER_ENRICHED_AT Event.EventAction.RECEIVER_FILTER -> Tables.TASK.RECEIVER_FILTERED_AT Event.EventAction.TRANSLATE -> Tables.TASK.TRANSLATED_AT Event.EventAction.REBATCH -> Tables.TASK.TRANSLATED_AT // overwrites prior date @@ -914,7 +904,6 @@ class WorkflowEngine( Event.EventAction.NONE -> error("Internal Error: NONE currentAction") Event.EventAction.OTHER -> error("Internal Error: OTHER currentAction") } - } db.updateTask( reportId, nextEventAction.toTaskAction(), @@ -939,8 +928,7 @@ class WorkflowEngine( sender: LegacyPipelineSender, content: String, defaults: Map, - ): ReadResult { - return when (sender.format) { + ): ReadResult = when (sender.format) { MimeFormat.CSV -> { try { this.csvSerializer.readExternal( @@ -986,5 +974,4 @@ class WorkflowEngine( else -> throw IllegalStateException("Sender format ${sender.format} is not supported") } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/batch/CovidBatchFunction.kt b/prime-router/src/main/kotlin/azure/batch/CovidBatchFunction.kt index 190d279d0d1..847a0b1e459 100644 --- a/prime-router/src/main/kotlin/azure/batch/CovidBatchFunction.kt +++ b/prime-router/src/main/kotlin/azure/batch/CovidBatchFunction.kt @@ -22,9 +22,7 @@ import java.time.OffsetDateTime * * A [workflowEngine] can be passed in for mocking/testing purposes. */ -class CovidBatchFunction( - private val workflowEngine: WorkflowEngine = WorkflowEngine(), -) : Logging { +class CovidBatchFunction(private val workflowEngine: WorkflowEngine = WorkflowEngine()) : Logging { @FunctionName(BatchConstants.Function.COVID_BATCH_FUNCTION) @StorageAccount("AzureWebJobsStorage") fun run( diff --git a/prime-router/src/main/kotlin/azure/batch/UniversalBatchFunction.kt b/prime-router/src/main/kotlin/azure/batch/UniversalBatchFunction.kt index 34b13969ac6..8cd0910142e 100644 --- a/prime-router/src/main/kotlin/azure/batch/UniversalBatchFunction.kt +++ b/prime-router/src/main/kotlin/azure/batch/UniversalBatchFunction.kt @@ -24,9 +24,7 @@ import java.time.OffsetDateTime * * A [workflowEngine] can be passed in for mocking/testing purposes. */ -class UniversalBatchFunction( - private val workflowEngine: WorkflowEngine = WorkflowEngine(), -) : Logging { +class UniversalBatchFunction(private val workflowEngine: WorkflowEngine = WorkflowEngine()) : Logging { @FunctionName(BatchConstants.Function.UNIVERSAL_BATCH_FUNCTION) @StorageAccount("AzureWebJobsStorage") fun run( @@ -128,7 +126,8 @@ class UniversalBatchFunction( receiver: Receiver, txn: Configuration?, ) { - if (!receiver.useBatching || receiver.timing == null || + if (!receiver.useBatching || + receiver.timing == null || receiver.timing.operation != Receiver.BatchOperation.MERGE ) { // Send each report separately diff --git a/prime-router/src/main/kotlin/azure/observability/AzureCustomDimensionsSerializable.kt b/prime-router/src/main/kotlin/azure/observability/AzureCustomDimensionsSerializable.kt index 387b72e977a..23bb7d22eb4 100644 --- a/prime-router/src/main/kotlin/azure/observability/AzureCustomDimensionsSerializable.kt +++ b/prime-router/src/main/kotlin/azure/observability/AzureCustomDimensionsSerializable.kt @@ -16,13 +16,11 @@ interface AzureCustomDimensionsSerializable { * * This will blow up if the implementing class serializes to something other than an object */ - fun serialize(): Map { - return JacksonMapperUtilities.jacksonObjectMapper + fun serialize(): Map = JacksonMapperUtilities.jacksonObjectMapper .valueToTree(this) .fields() .asSequence() .associate { it.key to serializeValue(it.value) } - } /** * All primitives will be in their string format. @@ -31,10 +29,8 @@ interface AzureCustomDimensionsSerializable { * All objects and arrays will be serialized to a string * Ex: obj -> "{\"key\": \"value\"}", arr -> "[\"a\", \"b\", \"c\"]" */ - private fun serializeValue(valueNode: JsonNode): String { - return when (valueNode) { + private fun serializeValue(valueNode: JsonNode): String = when (valueNode) { is ContainerNode<*> -> valueNode.toString() // object or array else -> valueNode.asText() } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/observability/bundleDigest/BundleDigestExtractor.kt b/prime-router/src/main/kotlin/azure/observability/bundleDigest/BundleDigestExtractor.kt index 07f8bf40158..84f9bc8bb1b 100644 --- a/prime-router/src/main/kotlin/azure/observability/bundleDigest/BundleDigestExtractor.kt +++ b/prime-router/src/main/kotlin/azure/observability/bundleDigest/BundleDigestExtractor.kt @@ -4,9 +4,7 @@ import gov.cdc.prime.router.azure.observability.event.ObservationSummary import org.hl7.fhir.r4.model.Bundle class BundleDigestExtractor(private val strategy: BundleDigestExtractorStrategy) { - fun generateDigest(bundle: Bundle): BundleDigest { - return strategy.extract(bundle) - } + fun generateDigest(bundle: Bundle): BundleDigest = strategy.extract(bundle) } interface BundleDigestExtractorStrategy { diff --git a/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestExtractorStrategy.kt b/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestExtractorStrategy.kt index 8f7575675a0..3341eb48ca3 100644 --- a/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestExtractorStrategy.kt +++ b/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestExtractorStrategy.kt @@ -4,9 +4,8 @@ import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext import gov.cdc.prime.router.fhirengine.translation.hl7.utils.FhirPathUtils import org.hl7.fhir.r4.model.Bundle -abstract class FhirPathBundleDigestExtractorStrategy( - private val context: CustomContext?, -) : BundleDigestExtractorStrategy { +abstract class FhirPathBundleDigestExtractorStrategy(private val context: CustomContext?) : + BundleDigestExtractorStrategy { internal fun getListOfFHIRValues( bundle: Bundle, path: String, diff --git a/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestLabResultExtractorStrategy.kt b/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestLabResultExtractorStrategy.kt index 1c7264381a8..33f84a838f6 100644 --- a/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestLabResultExtractorStrategy.kt +++ b/prime-router/src/main/kotlin/azure/observability/bundleDigest/FhirPathBundleDigestLabResultExtractorStrategy.kt @@ -5,9 +5,8 @@ import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext import gov.cdc.prime.router.fhirengine.translation.hl7.utils.FhirPathUtils import org.hl7.fhir.r4.model.Bundle -class FhirPathBundleDigestLabResultExtractorStrategy( - private val context: CustomContext? = null, -) : FhirPathBundleDigestExtractorStrategy(context) { +class FhirPathBundleDigestLabResultExtractorStrategy(private val context: CustomContext? = null) : + FhirPathBundleDigestExtractorStrategy(context) { private val eventCodePath = "Bundle.entry.resource.ofType(MessageHeader).event.display" private val patientStatePath = "Bundle.entry.resource.ofType(Patient).address.state" diff --git a/prime-router/src/main/kotlin/azure/observability/context/MDCUtils.kt b/prime-router/src/main/kotlin/azure/observability/context/MDCUtils.kt index f4700c09155..bea7d26d5b6 100644 --- a/prime-router/src/main/kotlin/azure/observability/context/MDCUtils.kt +++ b/prime-router/src/main/kotlin/azure/observability/context/MDCUtils.kt @@ -114,6 +114,7 @@ inline fun withLoggingContext(event: AzureCustomEvent, body: () -> T): T { /** * vararg convenience function for `withLoggingContext(contextMap, body)` */ -inline fun withLoggingContext(vararg pairs: Pair, body: () -> T): T { - return withLoggingContext(pairs.toMap(), body) -} \ No newline at end of file +inline fun withLoggingContext( + vararg pairs: Pair, + body: () -> T, +): T = withLoggingContext(pairs.toMap(), body) \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/observability/context/SendFunctionLoggingContext.kt b/prime-router/src/main/kotlin/azure/observability/context/SendFunctionLoggingContext.kt index c4cffd497e9..1c7438b1578 100644 --- a/prime-router/src/main/kotlin/azure/observability/context/SendFunctionLoggingContext.kt +++ b/prime-router/src/main/kotlin/azure/observability/context/SendFunctionLoggingContext.kt @@ -5,7 +5,4 @@ import gov.cdc.prime.router.ReportId /** * Fields added to the MDC during Send function calls */ -data class SendFunctionLoggingContext( - val reportId: ReportId, - val receiver: String, -) : AzureLoggingContext \ No newline at end of file +data class SendFunctionLoggingContext(val reportId: ReportId, val receiver: String) : AzureLoggingContext \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/observability/event/AzureEventService.kt b/prime-router/src/main/kotlin/azure/observability/event/AzureEventService.kt index 1b5865e9025..7e69f5a463e 100644 --- a/prime-router/src/main/kotlin/azure/observability/event/AzureEventService.kt +++ b/prime-router/src/main/kotlin/azure/observability/event/AzureEventService.kt @@ -20,9 +20,9 @@ interface AzureEventService { /** * Default implementation */ -class AzureEventServiceImpl( - private val telemetryClient: TelemetryClient = TelemetryClient(), -) : AzureEventService, Logging { +class AzureEventServiceImpl(private val telemetryClient: TelemetryClient = TelemetryClient()) : + AzureEventService, + Logging { /** * Send event to Azure AppInsights using the Azure TelemetryClient @@ -39,7 +39,9 @@ class AzureEventServiceImpl( } } -class InMemoryAzureEventService : AzureEventService, Logging { +class InMemoryAzureEventService : + AzureEventService, + Logging { val events: MutableList = mutableListOf() val reportStreamEvents = mutableMapOf>() diff --git a/prime-router/src/main/kotlin/azure/observability/event/AzureEventUtils.kt b/prime-router/src/main/kotlin/azure/observability/event/AzureEventUtils.kt index d4c6a16dea2..5dfde0ae4b4 100644 --- a/prime-router/src/main/kotlin/azure/observability/event/AzureEventUtils.kt +++ b/prime-router/src/main/kotlin/azure/observability/event/AzureEventUtils.kt @@ -20,13 +20,13 @@ object AzureEventUtils { * Retrieves all observations from a list of observations and maps them to a list of [ObservationSummary] each * containing a list of [TestSummary] */ - fun getObservationSummaries(observations: List): List { - return observations.map { observation -> + fun getObservationSummaries( + observations: List, + ): List = observations.map { observation -> ObservationSummary( observation.code.coding.map(TestSummary::fromCoding), ) } - } /** * For tracking the Message ID, we need the bundle.identifier value and system. @@ -37,7 +37,5 @@ object AzureEventUtils { /** * Returns the bundle identifier elements that relate to HL7v2 Message Header information for tracking */ - fun getIdentifier(bundle: Bundle): MessageID { - return MessageID(bundle.identifier?.value, bundle.identifier?.system) - } + fun getIdentifier(bundle: Bundle): MessageID = MessageID(bundle.identifier?.value, bundle.identifier?.system) } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/azure/observability/event/ObservationSummary.kt b/prime-router/src/main/kotlin/azure/observability/event/ObservationSummary.kt index e7ac17b56e3..0b70f823974 100644 --- a/prime-router/src/main/kotlin/azure/observability/event/ObservationSummary.kt +++ b/prime-router/src/main/kotlin/azure/observability/event/ObservationSummary.kt @@ -3,9 +3,7 @@ package gov.cdc.prime.router.azure.observability.event /** * An observation can include multiple mapped conditions to be queried */ -data class ObservationSummary( - val testSummary: List = emptyList(), -) { +data class ObservationSummary(val testSummary: List = emptyList()) { companion object { val EMPTY = ObservationSummary(emptyList()) diff --git a/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventBuilder.kt b/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventBuilder.kt index f7ed5a649e3..ce435933559 100644 --- a/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventBuilder.kt +++ b/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventBuilder.kt @@ -81,15 +81,13 @@ abstract class AbstractReportStreamEventBuilder( abstract fun buildEvent(): T - fun getReportEventData(): ReportEventData { - return reportEventService.getReportEventData( + fun getReportEventData(): ReportEventData = reportEventService.getReportEventData( childReportId, childBodyUrl, theParentReportId, pipelineStepName, theTopic ) - } fun send() { val event = buildEvent() @@ -131,12 +129,10 @@ open class ReportStreamReportEventBuilder( pipelineStepName ) { - override fun buildEvent(): ReportStreamReportEvent { - return ReportStreamReportEvent( + override fun buildEvent(): ReportStreamReportEvent = ReportStreamReportEvent( getReportEventData(), theParams ) - } } /** @@ -192,13 +188,11 @@ open class ReportStreamItemEventBuilder( ) } - override fun buildEvent(): ReportStreamItemEvent { - return ReportStreamItemEvent( + override fun buildEvent(): ReportStreamItemEvent = ReportStreamItemEvent( getReportEventData(), getItemEventData(), theParams ) - } } /** @@ -222,12 +216,10 @@ class ReportStreamReportProcessingErrorEventBuilder( theTopic, pipelineStepName ) { - override fun buildEvent(): ReportStreamReportEvent { - return ReportStreamReportEvent( + override fun buildEvent(): ReportStreamReportEvent = ReportStreamReportEvent( getReportEventData(), theParams + mapOf(ReportStreamEventProperties.PROCESSING_ERROR to error) ) - } } /** @@ -251,11 +243,9 @@ class ReportStreamItemProcessingErrorEventBuilder( theTopic, pipelineStepName ) { - override fun buildEvent(): ReportStreamItemEvent { - return ReportStreamItemEvent( + override fun buildEvent(): ReportStreamItemEvent = ReportStreamItemEvent( getReportEventData(), getItemEventData(), theParams + mapOf(ReportStreamEventProperties.PROCESSING_ERROR to error) ) - } } \ 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 d31404ebbe4..54571e993f6 100644 --- a/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventData.kt +++ b/prime-router/src/main/kotlin/azure/observability/event/ReportStreamEventData.kt @@ -73,9 +73,7 @@ enum class ReportStreamEventProperties { ; @JsonKey - fun externalKey(): String { - return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name) - } + fun externalKey(): String = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, name) } /** diff --git a/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt b/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt index eff8dcd02da..9af01074cc9 100644 --- a/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt +++ b/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt @@ -19,9 +19,7 @@ import org.apache.logging.log4j.kotlin.Logging * Builder class to create either JSON or HL7 response types based on the contents of the * submitted reports */ -class SubmissionResponseBuilder( - private val hL7ACKUtils: HL7ACKUtils = HL7ACKUtils(), -) : Logging { +class SubmissionResponseBuilder(private val hL7ACKUtils: HL7ACKUtils = HL7ACKUtils()) : Logging { /** * Builds a response to send to the client after submitting a report diff --git a/prime-router/src/main/kotlin/cli/CommandUtilities.kt b/prime-router/src/main/kotlin/cli/CommandUtilities.kt index d2f0e865755..7dc52043dc7 100644 --- a/prime-router/src/main/kotlin/cli/CommandUtilities.kt +++ b/prime-router/src/main/kotlin/cli/CommandUtilities.kt @@ -65,8 +65,11 @@ class CommandUtilities { * Checks if the API can be connected to. * @return true is the API is available, false otherwise */ - private fun isEndpointAvailable(url: URL, accessToken: String, httpClient: HttpClient? = null): Boolean { - return runBlocking { + private fun isEndpointAvailable( + url: URL, + accessToken: String, + httpClient: HttpClient? = null, + ): Boolean = runBlocking { val response = HttpClientUtils.head( url.toString(), accessToken = accessToken, @@ -74,7 +77,6 @@ class CommandUtilities { ) response.status == HttpStatusCode.OK } - } private val jsonMapper = jacksonObjectMapper @@ -145,8 +147,6 @@ class CommandUtilities { /** * Nice way to abort a command */ - fun abort(message: String): Nothing { - throw PrintMessage(message, printError = true) - } + fun abort(message: String): Nothing = throw PrintMessage(message, printError = true) } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/ConvertFileCommands.kt b/prime-router/src/main/kotlin/cli/ConvertFileCommands.kt index bc3268c59cd..b0a248bc2cf 100644 --- a/prime-router/src/main/kotlin/cli/ConvertFileCommands.kt +++ b/prime-router/src/main/kotlin/cli/ConvertFileCommands.kt @@ -15,9 +15,8 @@ import kotlin.io.path.nameWithoutExtension /** * Converts a file into a different output format */ -class ConvertFileCommands( - private val metadataInstance: Metadata? = null, -) : CliktCommand( +class ConvertFileCommands(private val metadataInstance: Metadata? = null) : + CliktCommand( name = "convert-file", help = """ diff --git a/prime-router/src/main/kotlin/cli/ConvertValuesetsYamlToCSV.kt b/prime-router/src/main/kotlin/cli/ConvertValuesetsYamlToCSV.kt index efa1887a566..083de336eae 100644 --- a/prime-router/src/main/kotlin/cli/ConvertValuesetsYamlToCSV.kt +++ b/prime-router/src/main/kotlin/cli/ConvertValuesetsYamlToCSV.kt @@ -29,7 +29,8 @@ import java.io.FileOutputStream * ./prime convert-valuesets-to-csv -i ./metadata/valuesets/sender-automation.valuesets * */ -class ConvertValuesetsYamlToCSV : CliktCommand( +class ConvertValuesetsYamlToCSV : + CliktCommand( name = "convert-valuesets-to-csv", help = """ This is a development tool that converts sender-automation.valuesets to two CSV files diff --git a/prime-router/src/main/kotlin/cli/CredentialsCli.kt b/prime-router/src/main/kotlin/cli/CredentialsCli.kt index ee09edd5934..40636cb4512 100644 --- a/prime-router/src/main/kotlin/cli/CredentialsCli.kt +++ b/prime-router/src/main/kotlin/cli/CredentialsCli.kt @@ -20,7 +20,8 @@ import org.apache.logging.log4j.Level import org.apache.logging.log4j.core.config.Configurator import java.util.Base64 -class CredentialsCli : CredentialManagement, CliktCommand( +class CredentialsCli : + CliktCommand( name = "create-credential", help = """ create credential JSON or persist to store @@ -38,7 +39,8 @@ class CredentialsCli : CredentialManagement, CliktCommand( getting_started.md for some ideas on how you can do that for your environment. """.trimIndent() -) { +), + CredentialManagement { val type by option(help = "Type of credential to create") .groupChoice( "UserPass" to UserPassCredentialOptions(), diff --git a/prime-router/src/main/kotlin/cli/FileUtilities.kt b/prime-router/src/main/kotlin/cli/FileUtilities.kt index 9eed920516f..222a2430177 100644 --- a/prime-router/src/main/kotlin/cli/FileUtilities.kt +++ b/prime-router/src/main/kotlin/cli/FileUtilities.kt @@ -46,8 +46,7 @@ object FileUtilities { targetStates: String? = null, targetCounties: String? = null, locale: Locale? = null, - ): Report { - return FakeReport(metadata, locale).build( + ): Report = FakeReport(metadata, locale).build( metadata.findSchema(schemaName) ?: error("Unable to find schema $schemaName"), count, @@ -55,7 +54,6 @@ object FileUtilities { targetStates, targetCounties, ) - } fun writeReportsToFile( reports: List>, @@ -140,10 +138,8 @@ object FileUtilities { /** * Does the file name of [file] match the internal file name pattern */ - fun isInternalFile(file: File): Boolean { - return file.extension.equals("INTERNAL", ignoreCase = true) || + fun isInternalFile(file: File): Boolean = file.extension.equals("INTERNAL", ignoreCase = true) || file.nameWithoutExtension.endsWith("INTERNAL", ignoreCase = true) - } /** * Save the passed in table as a CSV to the provided output file diff --git a/prime-router/src/main/kotlin/cli/LivdTableUpdate.kt b/prime-router/src/main/kotlin/cli/LivdTableUpdate.kt index 216d0f7d6a8..33485999ac0 100644 --- a/prime-router/src/main/kotlin/cli/LivdTableUpdate.kt +++ b/prime-router/src/main/kotlin/cli/LivdTableUpdate.kt @@ -45,7 +45,8 @@ import java.net.URL * */ -class LivdTableUpdate : CliktCommand( +class LivdTableUpdate : + CliktCommand( name = "livd-table-update", help = """ This updates the LIVD lookup table with a new version. @@ -169,7 +170,8 @@ class LivdTableUpdate : CliktCommand( if (cell.stringCellValue.last() == '*') stringValue = cell.stringCellValue.dropLast(1) // Add " to a string that contains quoted strings (i.e ""string"") - if (stringValue.contains("\n") || stringValue.contains(",") || + if (stringValue.contains("\n") || + stringValue.contains(",") || (stringValue.contains("\"")) ) { // String that contain special character(s) diff --git a/prime-router/src/main/kotlin/cli/LookupTableCommands.kt b/prime-router/src/main/kotlin/cli/LookupTableCommands.kt index c227bf92b35..7e42693e0fa 100644 --- a/prime-router/src/main/kotlin/cli/LookupTableCommands.kt +++ b/prime-router/src/main/kotlin/cli/LookupTableCommands.kt @@ -241,7 +241,9 @@ class LookupTableEndpointUtilities( checkResponseForCommonErrors(response, respStr) try { val info = mapper.readValue(respStr) - if (info.tableName.isNullOrBlank() || info.tableVersion < 1 || info.createdBy.isNullOrBlank() || + if (info.tableName.isNullOrBlank() || + info.tableVersion < 1 || + info.createdBy.isNullOrBlank() || info.createdAt.toString().isBlank() ) { throw IOException( @@ -316,16 +318,15 @@ class LookupTableEndpointUtilities( * Sets the JSON for a table [row]. * @return the JSON representation of the data */ - internal fun setTableRowToJson(row: Map): JSONB { - return JSONB.jsonb(mapper.writeValueAsString(row)) - } + internal fun setTableRowToJson(row: Map): JSONB = JSONB.jsonb(mapper.writeValueAsString(row)) } } /** * Commands to manipulate lookup tables. */ -class LookupTableCommands : CliktCommand( +class LookupTableCommands : + CliktCommand( name = "lookuptables", help = "Manage lookup tables" ) { @@ -464,11 +465,8 @@ class LookupTableCommands : CliktCommand( * Generic lookup table command. * parameter [httpClient] - inject a custom http client */ -abstract class GenericLookupTableCommand( - name: String, - help: String, - val httpClient: HttpClient? = null, -) : CliktCommand(name = name, help = help) { +abstract class GenericLookupTableCommand(name: String, help: String, val httpClient: HttpClient? = null) : + CliktCommand(name = name, help = help) { /** * The environment to connect to. */ @@ -498,7 +496,8 @@ abstract class GenericLookupTableCommand( /** * Print out a lookup table. */ -class LookupTableGetCommand(httpClient: HttpClient? = null) : GenericLookupTableCommand( +class LookupTableGetCommand(httpClient: HttpClient? = null) : + GenericLookupTableCommand( name = "get", help = "Fetch the contents of a lookup table", httpClient = httpClient @@ -557,7 +556,8 @@ class LookupTableGetCommand(httpClient: HttpClient? = null) : GenericLookupTable /** * Compare a sender compendium with an observation mapping lookup table. */ -class LookupTableCompareMappingCommand(httpClient: HttpClient? = null) : GenericLookupTableCommand( +class LookupTableCompareMappingCommand(httpClient: HttpClient? = null) : + GenericLookupTableCommand( name = "compare-mapping", help = "Compares a sender compendium against an observation mapping lookup table, outputting an annotated CSV", httpClient = httpClient @@ -600,8 +600,8 @@ class LookupTableCompareMappingCommand(httpClient: HttpClient? = null) : Generic fun compareMappings( compendium: List>, tableTestCodeMap: Map>, - ): List> { - return compendium.map { // process every code in the compendium + ): List> = compendium.map { + // process every code in the compendium if (tableTestCodeMap[it.getValue(SENDER_COMPENDIUM_CODE_KEY)]?.get( ObservationMappingConstants.TEST_CODESYSTEM_KEY ) == it.getValue(SENDER_COMPENDIUM_CODESYSTEM_KEY) @@ -611,7 +611,6 @@ class LookupTableCompareMappingCommand(httpClient: HttpClient? = null) : Generic it + (SENDER_COMPENDIUM_MAPPED_KEY to SENDER_COMPENDIUM_MAPPED_FALSE) } } - } } override fun run() { @@ -670,7 +669,8 @@ class LookupTableCompareMappingCommand(httpClient: HttpClient? = null) : Generic /** * Update an observation mapping table using the NLM Value Set Authority. */ -class LookupTableUpdateMappingCommand : GenericLookupTableCommand( +class LookupTableUpdateMappingCommand : + GenericLookupTableCommand( name = "update-mapping", help = "Update an observation mapping table using the NLM Value Set Authority." ) { @@ -759,9 +759,11 @@ class LookupTableUpdateMappingCommand : GenericLookupTableCommand( * @return updated test data grouped by OID, suitable for use with syncMappings() */ fun fetchLatestTestData(oids: List, client: IGenericClient): Map = - runBlocking { // wait + runBlocking { + // wait // for each oid, fetch the ValueSet and create a test map - oids.associateWith { oid -> // create a map of oids to their associated tests in the valueset + oids.associateWith { oid -> + // create a map of oids to their associated tests in the valueset async { try { fetchValueSetForOID(oid, client) @@ -805,7 +807,8 @@ class LookupTableUpdateMappingCommand : GenericLookupTableCommand( if (it[ObservationMappingConstants.CONDITION_VALUE_SOURCE_KEY] != OBX_MAPPING_RCTC_VALUE_SOURCE) { OBX_MAPPING_NON_RCTC_KEY // not an RCTC mapping - cannot be updated } else { - it[ObservationMappingConstants.TEST_OID_KEY].let { oid -> // group by OID + it[ObservationMappingConstants.TEST_OID_KEY].let { oid -> + // group by OID if (oid.isNullOrBlank()) OBX_MAPPING_NO_OID_KEY else oid // group mappings with blank/null OIDs } } @@ -819,15 +822,14 @@ class LookupTableUpdateMappingCommand : GenericLookupTableCommand( fun syncMappings( tableOIDMap: Map>>, updateOIDMap: Map, - ): List> { - return updateOIDMap.map { update -> // process every oid test group update + ): List> = updateOIDMap.map { update -> + // process every oid test group update val conditionData = tableOIDMap[update.key]!![0].filterKeys { it in ObservationMappingConstants.CONDITION_KEYS // fetch existing condition data for this oid } update.value.toMappings(conditionData) // flatten + add carryover }.flatten() + tableOIDMap.filterKeys { it !in updateOIDMap.keys }.values.flatten() - } /** * Load an [inputFile] or an existing lookup table by [tableName] and [tableVersion] using [tableUtil] and @@ -902,11 +904,13 @@ class LookupTableUpdateMappingCommand : GenericLookupTableCommand( // Save local csv of updated table if (( - !silent && YesNoPrompt( + !silent && + YesNoPrompt( "Save an updated local observation-mapping.csv with ${outputData.size} rows?", currentContext.terminal ).ask() == true - ) || silent + ) || + silent ) { val outputCSV = File(OBX_MAPPING_CSV_PATH) saveTableAsCSV(outputCSV.outputStream(), outputData) @@ -915,11 +919,13 @@ class LookupTableUpdateMappingCommand : GenericLookupTableCommand( // Save updated table to the database if (( - !silent && YesNoPrompt( + !silent && + YesNoPrompt( "Continue to create a new version of $tableName with ${outputData.size} rows?", currentContext.terminal ).ask() == true - ) || silent + ) || + silent ) { val newTableInfo = try { tableUtil.createTable(tableName, outputData, true) @@ -958,7 +964,8 @@ class LookupTableUpdateMappingCommand : GenericLookupTableCommand( /** * Create a new lookup table. */ -class LookupTableCreateCommand(httpClient: HttpClient? = null) : GenericLookupTableCommand( +class LookupTableCreateCommand(httpClient: HttpClient? = null) : + GenericLookupTableCommand( name = "create", help = "Create a new version of a lookup table", httpClient = httpClient @@ -1066,11 +1073,13 @@ class LookupTableCreateCommand(httpClient: HttpClient? = null) : GenericLookupTa // Now we are ready. Ask if we should proceed. if (( - !silent && YesNoPrompt( + !silent && + YesNoPrompt( "Continue to create a new version of $tableName with ${inputData.size} rows?", currentContext.terminal ).ask() == true - ) || silent + ) || + silent ) { val newTableInfo = try { tableUtil.createTable(tableName, inputData, forceTableToCreate) @@ -1115,7 +1124,8 @@ class LookupTableCreateCommand(httpClient: HttpClient? = null) : GenericLookupTa /** * List the available lookup tables. */ -class LookupTableListCommand(httpClient: HttpClient? = null) : GenericLookupTableCommand( +class LookupTableListCommand(httpClient: HttpClient? = null) : + GenericLookupTableCommand( name = "list", help = "List the lookup tables", httpClient = httpClient @@ -1157,7 +1167,8 @@ class LookupTableListCommand(httpClient: HttpClient? = null) : GenericLookupTabl /** * Show a diff between two lookup tables. */ -class LookupTableDiffCommand(httpClient: HttpClient? = null) : GenericLookupTableCommand( +class LookupTableDiffCommand(httpClient: HttpClient? = null) : + GenericLookupTableCommand( name = "diff", help = "Generate a difference between two versions of a lookup table", httpClient = httpClient @@ -1241,7 +1252,8 @@ class LookupTableDiffCommand(httpClient: HttpClient? = null) : GenericLookupTabl /** * Activate a lookup table. */ -class LookupTableActivateCommand(httpClient: HttpClient? = null) : GenericLookupTableCommand( +class LookupTableActivateCommand(httpClient: HttpClient? = null) : + GenericLookupTableCommand( name = "activate", help = "Activate a specific version of a lookup table", httpClient = httpClient @@ -1309,7 +1321,8 @@ class LookupTableActivateCommand(httpClient: HttpClient? = null) : GenericLookup /** * Load lookup tables from a directory. */ -class LookupTableLoadAllCommand(httpClient: HttpClient? = null) : GenericLookupTableCommand( +class LookupTableLoadAllCommand(httpClient: HttpClient? = null) : + GenericLookupTableCommand( name = "loadall", help = "Load all the tables stored as CSV in the specified directory", httpClient = httpClient diff --git a/prime-router/src/main/kotlin/cli/OktaCommands.kt b/prime-router/src/main/kotlin/cli/OktaCommands.kt index 4c798538b51..78b2a975787 100644 --- a/prime-router/src/main/kotlin/cli/OktaCommands.kt +++ b/prime-router/src/main/kotlin/cli/OktaCommands.kt @@ -46,7 +46,8 @@ private const val localTokenFileName = "accessToken.json" /** * Logs into the Okta account and saves the access token in a hidden file */ -class LoginCommand : OktaCommand( +class LoginCommand : + OktaCommand( name = "login", help = "Login to the ReportStream authorization service" ) { @@ -100,8 +101,8 @@ class LoginCommand : OktaCommand( return fetchAccessToken(code, codeVerifier, clientId, oktaBaseUrl) } - private fun authorizeUrl(clientId: String, oktaUrl: String, state: String, codeChallenge: String): String { - return "$oktaUrl$oktaAuthorizePath?" + + private fun authorizeUrl(clientId: String, oktaUrl: String, state: String, codeChallenge: String): String = + "$oktaUrl$oktaAuthorizePath?" + "client_id=$clientId&" + "response_type=code&" + "scope=$oktaScope&" + @@ -109,7 +110,6 @@ class LoginCommand : OktaCommand( "state=$state&" + "code_challenge_method=S256&" + "code_challenge=$codeChallenge" - } private fun startRedirectServer() { val server = HttpServer.create(InetSocketAddress(redirectPort), 0) @@ -138,8 +138,7 @@ class LoginCommand : OktaCommand( codeVerifier: String, clientId: String, oktaBaseUrl: String, - ): TokenInfo { - return HttpClientUtils.submitFormT( + ): TokenInfo = HttpClientUtils.submitFormT( url = "$oktaBaseUrl$oktaTokenPath", formParams = mapOf( Pair("grant_type", "authorization_code"), @@ -149,7 +148,6 @@ class LoginCommand : OktaCommand( Pair("code_verifier", codeVerifier) ) ) - } private fun generateCodeVerifier(): String { val bytes = Random.nextBytes(32) @@ -168,7 +166,8 @@ class LoginCommand : OktaCommand( /** * Logs out of the Okta account by deleting the saved access token file */ -class LogoutCommand : OktaCommand( +class LogoutCommand : + OktaCommand( name = "logout", help = "Logout of the ReportStream authorization service" ) { @@ -190,9 +189,7 @@ abstract class OktaCommand(name: String, help: String) : CliktCommand(name = nam data class AccessTokenFile(val token: String, val clientId: String, val expiresAt: LocalDateTime) - fun abort(message: String): Nothing { - throw PrintMessage(message, printError = true) - } + fun abort(message: String): Nothing = throw PrintMessage(message, printError = true) companion object { /** @@ -224,8 +221,7 @@ abstract class OktaCommand(name: String, help: String) : CliktCommand(name = nam * Returns the access token saved from the last login if valid given [app]. * @return the Okta access token, a dummy token if [app] is null. or null if there is no valid token */ - fun fetchAccessToken(app: OktaApp?): String? { - return if (app == null) { + fun fetchAccessToken(app: OktaApp?): String? = if (app == null) { dummyOktaAccessToken } else { val accessTokenFile = readAccessTokenFile(app) @@ -235,7 +231,6 @@ abstract class OktaCommand(name: String, help: String) : CliktCommand(name = nam null } } - } fun readAccessTokenFile(app: OktaApp): AccessTokenFile? { val filePath = primeAccessFilePath(app) @@ -286,16 +281,12 @@ abstract class OktaCommand(name: String, help: String) : CliktCommand(name = nam } } - private fun getOktaUrlBase(oktaApp: OktaApp): String { - return oktaBaseUrls.getValue(oktaApp) - } + private fun getOktaUrlBase(oktaApp: OktaApp): String = oktaBaseUrls.getValue(oktaApp) - private fun primeFolderPath(app: OktaApp): Path { - return Path.of(System.getProperty("user.home"), localPrimeFolder, app.name) - } + private fun primeFolderPath(app: OktaApp): Path = + Path.of(System.getProperty("user.home"), localPrimeFolder, app.name) - private fun primeAccessFilePath(app: OktaApp): Path { - return Path.of(System.getProperty("user.home"), localPrimeFolder, app.name, localTokenFileName) - } + private fun primeAccessFilePath(app: OktaApp): Path = + Path.of(System.getProperty("user.home"), localPrimeFolder, app.name, localTokenFileName) } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/OrganizationUtilsCommand.kt b/prime-router/src/main/kotlin/cli/OrganizationUtilsCommand.kt index 8e57820e2bc..47c40db2883 100644 --- a/prime-router/src/main/kotlin/cli/OrganizationUtilsCommand.kt +++ b/prime-router/src/main/kotlin/cli/OrganizationUtilsCommand.kt @@ -12,7 +12,8 @@ import gov.cdc.prime.router.tokens.JwkSet import gov.cdc.prime.router.tokens.Scope import java.io.File -class AddPublicKey : SettingCommand( +class AddPublicKey : + SettingCommand( name = "addkey", help = """ Add a public key to an existing organization's setting @@ -135,7 +136,8 @@ class AddPublicKey : SettingCommand( } } -class RemoveKey : SettingCommand( +class RemoveKey : + SettingCommand( name = "removekey", help = """ Removes a public key from an existing organization's setting @@ -227,7 +229,8 @@ class RemoveKey : SettingCommand( } } -class TokenUrl : SettingCommand( +class TokenUrl : + SettingCommand( name = "reqtoken", help = """ Use my private key to request a token from ReportStream diff --git a/prime-router/src/main/kotlin/cli/PIIRemovalCommands.kt b/prime-router/src/main/kotlin/cli/PIIRemovalCommands.kt index 6a9c48aac39..b9a0866989c 100644 --- a/prime-router/src/main/kotlin/cli/PIIRemovalCommands.kt +++ b/prime-router/src/main/kotlin/cli/PIIRemovalCommands.kt @@ -20,7 +20,8 @@ import org.hl7.fhir.r4.model.Patient.ContactComponent import org.hl7.fhir.r4.model.Practitioner import org.hl7.fhir.r4.model.StringType -class PIIRemovalCommands : CliktCommand( +class PIIRemovalCommands : + CliktCommand( name = "piiRemoval", help = "Remove PII" ) { @@ -201,18 +202,17 @@ class PIIRemovalCommands : CliktCommand( /** * Gets a fake value for a given type */ - private fun getFakeValueForElementCall(dataType: String): String { - return CustomFhirPathFunctions().getFakeValueForElement( + private fun getFakeValueForElementCall(dataType: String): String = CustomFhirPathFunctions().getFakeValueForElement( mutableListOf(mutableListOf(StringType(dataType))) )[0].primitiveValue() - } /** * Gets a fake value for a given type that requires geo data */ - private fun getFakeValueForElementCallUsingGeoData(dataType: String, state: String): String { - return CustomFhirPathFunctions().getFakeValueForElement( + private fun getFakeValueForElementCallUsingGeoData( + dataType: String, + state: String, + ): String = CustomFhirPathFunctions().getFakeValueForElement( mutableListOf(mutableListOf(StringType(dataType)), mutableListOf(StringType(state))) )[0].primitiveValue() - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/ProcessDataCommands.kt b/prime-router/src/main/kotlin/cli/ProcessDataCommands.kt index 623bcd78374..1c1ffc842d8 100644 --- a/prime-router/src/main/kotlin/cli/ProcessDataCommands.kt +++ b/prime-router/src/main/kotlin/cli/ProcessDataCommands.kt @@ -371,9 +371,13 @@ class ProcessData( } } - private fun getOutputFormat(default: MimeFormat): MimeFormat { - return if (forcedFormat != null) MimeFormat.valueOf(forcedFormat!!) else default - } + private fun getOutputFormat(default: MimeFormat): MimeFormat = if (forcedFormat != + null + ) { + MimeFormat.valueOf(forcedFormat!!) + } else { + default + } private fun getDefaultValues(): DefaultValues { val values = mutableMapOf() diff --git a/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt b/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt index 73012d84748..94f4f6fd241 100644 --- a/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt +++ b/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt @@ -66,7 +66,8 @@ import java.util.UUID /** * Process data into/from FHIR. */ -class ProcessFhirCommands : CliktCommand( +class ProcessFhirCommands : + CliktCommand( name = "fhirdata", help = "Process data into/from FHIR" ) { @@ -205,7 +206,8 @@ class ProcessFhirCommands : CliktCommand( val messageOrBundle = MessageOrBundle() when { // HL7 to FHIR conversion - inputFileType == "HL7" && ( + inputFileType == "HL7" && + ( (outputFormat == MimeFormat.FHIR.toString()) || (receiver != null && receiver.format == MimeFormat.FHIR) ) -> { @@ -217,7 +219,8 @@ class ProcessFhirCommands : CliktCommand( } // FHIR to HL7 conversion - (inputFileType == "FHIR" || inputFileType == "JSON") && ( + (inputFileType == "FHIR" || inputFileType == "JSON") && + ( (isCli && outputFormat == MimeFormat.HL7.toString()) || (receiver != null && (receiver.format == MimeFormat.HL7 || receiver.format == MimeFormat.HL7_BATCH)) ) -> { @@ -235,7 +238,8 @@ class ProcessFhirCommands : CliktCommand( } // FHIR to FHIR conversion - (inputFileType == "FHIR" || inputFileType == "JSON") && ( + (inputFileType == "FHIR" || inputFileType == "JSON") && + ( (isCli && outputFormat == MimeFormat.FHIR.toString()) || (receiver != null && receiver.format == MimeFormat.FHIR) ) -> { @@ -246,7 +250,8 @@ class ProcessFhirCommands : CliktCommand( } // HL7 to FHIR to HL7 conversion - inputFileType == "HL7" && ( + inputFileType == "HL7" && + ( (isCli && outputFormat == MimeFormat.HL7.toString()) || ( receiver != null && @@ -632,7 +637,8 @@ class ProcessFhirCommands : CliktCommand( * Process a FHIR path using a FHIR bundle as input. This command is useful to parse sample FHIR data to make * sure your FHIR path is correct in your schemas. */ -class FhirPathCommand : CliktCommand( +class FhirPathCommand : + CliktCommand( name = "fhirpath", help = "Input FHIR paths to be resolved using the input FHIR bundle" ) { @@ -797,8 +803,7 @@ class FhirPathCommand : CliktCommand( * Convert a [value] that is a FHIR base to a string. * @return a string representing the contents of the FHIR base */ - private fun fhirBaseAsString(value: Base): String { - return when { + private fun fhirBaseAsString(value: Base): String = when { value.isPrimitive -> "Primitive: $value" // References @@ -814,7 +819,6 @@ class FhirPathCommand : CliktCommand( else -> fhirPropertiesAsString(value) } - } /** * Generate a string representation of all the properties in a resource @@ -874,9 +878,7 @@ class FhirPathCommand : CliktCommand( // made static // TODO: https://github.com/CDCgov/prime-reportstream/issues/16407 class NoopReportStreamEventService : IReportStreamEventService { - override fun sendQueuedEvents() { - throw NotImplementedError() - } + override fun sendQueuedEvents(): Unit = throw NotImplementedError() override fun sendReportEvent( eventName: ReportStreamEventName, @@ -884,9 +886,7 @@ class NoopReportStreamEventService : IReportStreamEventService { pipelineStepName: TaskAction, shouldQueue: Boolean, initializer: ReportStreamReportEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun sendReportEvent( eventName: ReportStreamEventName, @@ -894,9 +894,7 @@ class NoopReportStreamEventService : IReportStreamEventService { pipelineStepName: TaskAction, shouldQueue: Boolean, initializer: ReportStreamReportEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun sendReportProcessingError( eventName: ReportStreamEventName, @@ -905,9 +903,7 @@ class NoopReportStreamEventService : IReportStreamEventService { error: String, shouldQueue: Boolean, initializer: ReportStreamReportProcessingErrorEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun sendReportProcessingError( eventName: ReportStreamEventName, @@ -916,9 +912,7 @@ class NoopReportStreamEventService : IReportStreamEventService { error: String, shouldQueue: Boolean, initializer: ReportStreamReportProcessingErrorEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun sendItemEvent( eventName: ReportStreamEventName, @@ -926,9 +920,7 @@ class NoopReportStreamEventService : IReportStreamEventService { pipelineStepName: TaskAction, shouldQueue: Boolean, initializer: ReportStreamItemEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun sendItemEvent( eventName: ReportStreamEventName, @@ -936,9 +928,7 @@ class NoopReportStreamEventService : IReportStreamEventService { pipelineStepName: TaskAction, shouldQueue: Boolean, initializer: ReportStreamItemEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun sendItemProcessingError( eventName: ReportStreamEventName, @@ -947,9 +937,7 @@ class NoopReportStreamEventService : IReportStreamEventService { error: String, shouldQueue: Boolean, initializer: ReportStreamItemProcessingErrorEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun sendItemProcessingError( eventName: ReportStreamEventName, @@ -958,9 +946,7 @@ class NoopReportStreamEventService : IReportStreamEventService { error: String, shouldQueue: Boolean, initializer: ReportStreamItemProcessingErrorEventBuilder.() -> Unit, - ) { - throw NotImplementedError() - } + ): Unit = throw NotImplementedError() override fun getReportEventData( childReportId: UUID, @@ -968,16 +954,12 @@ class NoopReportStreamEventService : IReportStreamEventService { parentReportId: UUID?, pipelineStepName: TaskAction, topic: Topic?, - ): ReportEventData { - throw NotImplementedError() - } + ): ReportEventData = throw NotImplementedError() override fun getItemEventData( childItemIndex: Int, parentReportId: UUID, parentItemIndex: Int, trackingId: String?, - ): ItemEventData { - throw NotImplementedError() - } + ): ItemEventData = throw NotImplementedError() } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt b/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt index 9c23ab1c9ec..c426a6eba7e 100644 --- a/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt +++ b/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt @@ -12,7 +12,8 @@ import gov.cdc.prime.router.fhirengine.utils.HL7Reader /** * Compare Hl7 files */ -class ProcessHl7Commands : CliktCommand( +class ProcessHl7Commands : + CliktCommand( name = "hl7data", help = "Compare HL7 Fields. This is the structure used for the segment numbering in th output: " + "https://hl7-definition.caristix.com/v2/HL7v2.5.1/TriggerEvents/ORU_R01." diff --git a/prime-router/src/main/kotlin/cli/SenderFilesCommand.kt b/prime-router/src/main/kotlin/cli/SenderFilesCommand.kt index 4ace7fede1a..9b909e8a527 100644 --- a/prime-router/src/main/kotlin/cli/SenderFilesCommand.kt +++ b/prime-router/src/main/kotlin/cli/SenderFilesCommand.kt @@ -19,7 +19,8 @@ import java.time.format.DateTimeFormatter import kotlin.io.path.Path import kotlin.io.path.name -class SenderFilesCommand : CliktCommand( +class SenderFilesCommand : + CliktCommand( name = "sender-files", help = "For a specified report, trace each item's ancestry and retrieve the source files submitted by senders." ) { @@ -232,7 +233,5 @@ class SenderFilesCommand : CliktCommand( /** * Abort the program with the message */ - private fun abort(message: String): Nothing { - throw PrintMessage(message, printError = true) - } + private fun abort(message: String): Nothing = throw PrintMessage(message, printError = true) } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/SettingCommands.kt b/prime-router/src/main/kotlin/cli/SettingCommands.kt index b329a111590..776fee7639b 100644 --- a/prime-router/src/main/kotlin/cli/SettingCommands.kt +++ b/prime-router/src/main/kotlin/cli/SettingCommands.kt @@ -50,10 +50,7 @@ private const val organizationsFile = "settings/organizations.yml" * It has a composable set of methods for listing, getting, diffing, putting and deleting settings. * The idea is to make concrete commands clear and concise by building primitives in this class. */ -abstract class SettingCommand( - name: String, - help: String, -) : CliktCommand(name = name, help = help) { +abstract class SettingCommand(name: String, help: String) : CliktCommand(name = name, help = help) { internal val env by option( "-e", "--env", metavar = "", @@ -300,9 +297,7 @@ abstract class SettingCommand( /** * Difference the YAML [inputFile]. Returns a list of all settings with differences. */ - protected fun diffAll(inputFile: File): List { - return diffAll(readYaml(inputFile)) - } + protected fun diffAll(inputFile: File): List = diffAll(readYaml(inputFile)) /** * Difference a list of organization settings [deepOrganizations] against a specified environment, or [environment] @@ -361,9 +356,7 @@ abstract class SettingCommand( /** * Call [put] on all the settings in the [inputFile]. Return the list of results. */ - protected fun putAll(inputFile: File): List { - return putAll(readYaml(inputFile)) - } + protected fun putAll(inputFile: File): List = putAll(readYaml(inputFile)) /** * Call [put] on a list of organization settings, [deepOrganizations] in a specified environment [env], which @@ -427,16 +420,14 @@ abstract class SettingCommand( ) } - fun fromJson(input: String, settingType: SettingType): Pair { - return readStructure(input, settingType, jsonMapper) - } + fun fromJson(input: String, settingType: SettingType): Pair = + readStructure(input, settingType, jsonMapper) - fun fromYaml(input: String, settingType: SettingType): Pair { - return readStructure(input, settingType, yamlMapper) - } + fun fromYaml(input: String, settingType: SettingType): Pair = + readStructure(input, settingType, yamlMapper) - private fun readStructure(input: String, settingType: SettingType, mapper: ObjectMapper): Pair { - return when (settingType) { + private fun readStructure(input: String, settingType: SettingType, mapper: ObjectMapper): Pair = + when (settingType) { SettingType.ORGANIZATION -> { val organization = mapper.readValue(input, OrganizationAPI::class.java) Pair(organization.name, jsonMapper.writeValueAsString(organization)) @@ -452,7 +443,6 @@ abstract class SettingCommand( Pair(receiver.fullName, jsonMapper.writeValueAsString(receiver)) } } - } fun toYaml(output: String, settingType: SettingType): String { // DevNote: could be handled by inherited methods, but decided that keeping all these together was maintainable @@ -540,12 +530,10 @@ abstract class SettingCommand( operation: Operation, settingType: SettingType, settingName: String, - ): String { - return environment.formUrl("$apiPath${settingPath(operation, settingType, settingName)}").toString() - } + ): String = environment.formUrl("$apiPath${settingPath(operation, settingType, settingName)}").toString() - private fun settingPath(operation: Operation, settingType: SettingType, settingName: String): String { - return if (operation == Operation.LIST) { + private fun settingPath(operation: Operation, settingType: SettingType, settingName: String): String = + if (operation == Operation.LIST) { when (settingType) { SettingType.ORGANIZATION -> "/organizations" SettingType.SENDER -> "/organizations/$settingName/senders" @@ -565,18 +553,14 @@ abstract class SettingCommand( } } } - } } } /** * List a single object entity */ -abstract class ListSettingCommand( - name: String, - help: String, - val settingType: SettingType, -) : SettingCommand(name, help) { +abstract class ListSettingCommand(name: String, help: String, val settingType: SettingType) : + SettingCommand(name, help) { private val settingName: String by nameOption override fun run() { @@ -592,11 +576,8 @@ abstract class ListSettingCommand( /** * Get a single object entity */ -abstract class GetSettingCommand( - name: String, - help: String, - val settingType: SettingType, -) : SettingCommand(name, help) { +abstract class GetSettingCommand(name: String, help: String, val settingType: SettingType) : + SettingCommand(name, help) { private val settingName: String by nameOption private val useJson: Boolean by jsonOption @@ -610,11 +591,8 @@ abstract class GetSettingCommand( /** * Remove a single entity */ -abstract class DeleteSettingCommand( - name: String, - help: String, - val settingType: SettingType, -) : SettingCommand(name, help) { +abstract class DeleteSettingCommand(name: String, help: String, val settingType: SettingType) : + SettingCommand(name, help) { private val settingName: String by nameOption override fun run() { @@ -627,11 +605,8 @@ abstract class DeleteSettingCommand( /** * Put an entity command with an input file */ -abstract class PutSettingCommand( - name: String, - help: String, - val settingType: SettingType, -) : SettingCommand(name, help) { +abstract class PutSettingCommand(name: String, help: String, val settingType: SettingType) : + SettingCommand(name, help) { private val inputFile by inputOption private val useJson: Boolean by jsonOption @@ -688,11 +663,8 @@ abstract class PutSettingCommand( /** * Diff an entity command with an input file */ -abstract class DiffSettingCommand( - name: String, - help: String, - val settingType: SettingType, -) : SettingCommand(name, help) { +abstract class DiffSettingCommand(name: String, help: String, val settingType: SettingType) : + SettingCommand(name, help) { private val inputFile by inputOption private val useJson: Boolean by jsonOption @@ -715,7 +687,8 @@ abstract class DiffSettingCommand( /** * Organization setting commands */ -class OrganizationSettings : CliktCommand( +class OrganizationSettings : + CliktCommand( name = "organization", help = "Fetch and update settings for an organization" ) { @@ -737,7 +710,8 @@ class OrganizationSettings : CliktCommand( } } -class ListOrganizationSetting : SettingCommand( +class ListOrganizationSetting : + SettingCommand( name = "list", help = "List the setting names of all organizations" ) { @@ -747,25 +721,29 @@ class ListOrganizationSetting : SettingCommand( } } -class GetOrganizationSetting : GetSettingCommand( +class GetOrganizationSetting : + GetSettingCommand( name = "get", help = "Fetch an organization's settings", settingType = SettingType.ORGANIZATION, ) -class PutOrganizationSetting : PutSettingCommand( +class PutOrganizationSetting : + PutSettingCommand( name = "set", help = "Update an organization's settings", settingType = SettingType.ORGANIZATION ) -class DeleteOrganizationSetting : DeleteSettingCommand( +class DeleteOrganizationSetting : + DeleteSettingCommand( name = "remove", help = "Remove an organization", settingType = SettingType.ORGANIZATION, ) -class DiffOrganizationSetting : DiffSettingCommand( +class DiffOrganizationSetting : + DiffSettingCommand( name = "diff", help = "Compare an organization's setting from an environment to those in an file", settingType = SettingType.ORGANIZATION @@ -774,7 +752,8 @@ class DiffOrganizationSetting : DiffSettingCommand( /** * Sender setting commands */ -class SenderSettings : CliktCommand( +class SenderSettings : + CliktCommand( name = "sender", help = "Fetch and update settings for a sender" ) { @@ -793,31 +772,36 @@ class SenderSettings : CliktCommand( } } -class ListSenderSetting : ListSettingCommand( +class ListSenderSetting : + ListSettingCommand( name = "list", help = "List all sender names for an organization", settingType = SettingType.SENDER, ) -class GetSenderSetting : GetSettingCommand( +class GetSenderSetting : + GetSettingCommand( name = "get", help = "Fetch a sender's settings", settingType = SettingType.SENDER, ) -class PutSenderSetting : PutSettingCommand( +class PutSenderSetting : + PutSettingCommand( name = "set", help = "Update a sender's settings", settingType = SettingType.SENDER, ) -class DeleteSenderSetting : DeleteSettingCommand( +class DeleteSenderSetting : + DeleteSettingCommand( name = "remove", help = "Remove a sender", settingType = SettingType.SENDER, ) -class DiffSenderSetting : DiffSettingCommand( +class DiffSenderSetting : + DiffSettingCommand( name = "diff", help = "Compare sender's settings from an environment to those in a file", settingType = SettingType.SENDER, @@ -826,7 +810,8 @@ class DiffSenderSetting : DiffSettingCommand( /** * Receiver setting commands */ -class ReceiverSettings : CliktCommand( +class ReceiverSettings : + CliktCommand( name = "receiver", help = "Fetch and update settings for a receiver" ) { @@ -845,31 +830,36 @@ class ReceiverSettings : CliktCommand( } } -class ListReceiverSetting : ListSettingCommand( +class ListReceiverSetting : + ListSettingCommand( name = "list", help = "Fetch the receiver names for an organization", settingType = SettingType.RECEIVER, ) -class GetReceiverSetting : GetSettingCommand( +class GetReceiverSetting : + GetSettingCommand( name = "get", help = "Fetch a receiver's settings", settingType = SettingType.RECEIVER, ) -class PutReceiverSetting : PutSettingCommand( +class PutReceiverSetting : + PutSettingCommand( name = "set", help = "Update a receiver's settings", settingType = SettingType.RECEIVER, ) -class DeleteReceiverSetting : DeleteSettingCommand( +class DeleteReceiverSetting : + DeleteSettingCommand( name = "remove", help = "Remove a receiver", settingType = SettingType.RECEIVER, ) -class DiffReceiverSetting : DiffSettingCommand( +class DiffReceiverSetting : + DiffSettingCommand( name = "diff", help = "Compare a receiver's settings from an environment to those in a file", settingType = SettingType.RECEIVER, @@ -878,7 +868,8 @@ class DiffReceiverSetting : DiffSettingCommand( /** * Update multiple settings */ -class MultipleSettings : CliktCommand( +class MultipleSettings : + CliktCommand( name = "multiple-settings", help = "Fetch and update multiple settings" ) { @@ -891,7 +882,8 @@ class MultipleSettings : CliktCommand( } } -class PutMultipleSettings : SettingCommand( +class PutMultipleSettings : + SettingCommand( name = "set", help = "Set all settings from a 'organizations.yml' file" ) { @@ -976,7 +968,8 @@ class PutMultipleSettings : SettingCommand( } } -class DiffMultipleSettings : SettingCommand( +class DiffMultipleSettings : + SettingCommand( name = "diff", help = "Compare all the settings from an environment to those in a file" ) { @@ -994,7 +987,8 @@ class DiffMultipleSettings : SettingCommand( } } -class GetMultipleSettings : SettingCommand( +class GetMultipleSettings : + SettingCommand( name = "get", help = "Get all settings from an environment in yaml format" ) { diff --git a/prime-router/src/main/kotlin/cli/ValidateUtilities.kt b/prime-router/src/main/kotlin/cli/ValidateUtilities.kt index d7c2f91dabd..a30b59f47f6 100644 --- a/prime-router/src/main/kotlin/cli/ValidateUtilities.kt +++ b/prime-router/src/main/kotlin/cli/ValidateUtilities.kt @@ -64,7 +64,5 @@ class ValidateUtilities(val service: ConfigurationValidationService) { /** * Returns whether the [result] is a [ConfigurationValidationFailure] or not. */ - private fun isFailure(result: ConfigurationValidationResult<*>): Boolean { - return result is ConfigurationValidationFailure - } + private fun isFailure(result: ConfigurationValidationResult<*>): Boolean = result is ConfigurationValidationFailure } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/ValidateYAMLCommand.kt b/prime-router/src/main/kotlin/cli/ValidateYAMLCommand.kt index 5ae8137b967..ddf9b6a070a 100644 --- a/prime-router/src/main/kotlin/cli/ValidateYAMLCommand.kt +++ b/prime-router/src/main/kotlin/cli/ValidateYAMLCommand.kt @@ -15,7 +15,8 @@ import gov.cdc.prime.router.config.validation.ConfigurationValidationServiceImpl import org.apache.commons.io.FileUtils import java.io.File -class ValidateYAMLCommand : CliktCommand( +class ValidateYAMLCommand : + CliktCommand( name = "validate-yaml", help = """ A CLI command to validate YAML files' structure and values. diff --git a/prime-router/src/main/kotlin/cli/helpers/HL7DiffHelper.kt b/prime-router/src/main/kotlin/cli/helpers/HL7DiffHelper.kt index f03b1e272e6..3fd90691445 100644 --- a/prime-router/src/main/kotlin/cli/helpers/HL7DiffHelper.kt +++ b/prime-router/src/main/kotlin/cli/helpers/HL7DiffHelper.kt @@ -345,10 +345,8 @@ class HL7DiffHelper { private fun effectivelyBlank( outputHL7v2Segment: Array, inputHL7v2Segment: Array, - ): Boolean { - return ((outputHL7v2Segment.size == 1 && outputHL7v2Segment[0].isEmpty) || outputHL7v2Segment.isEmpty()) && + ): Boolean = ((outputHL7v2Segment.size == 1 && outputHL7v2Segment[0].isEmpty) || outputHL7v2Segment.isEmpty()) && ((inputHL7v2Segment.size == 1 && inputHL7v2Segment[0].isEmpty) || inputHL7v2Segment.isEmpty()) - } /** * helper - heuristically check input and output are compatible types diff --git a/prime-router/src/main/kotlin/cli/main.kt b/prime-router/src/main/kotlin/cli/main.kt index b078996cbcd..e934353793c 100644 --- a/prime-router/src/main/kotlin/cli/main.kt +++ b/prime-router/src/main/kotlin/cli/main.kt @@ -29,7 +29,8 @@ import java.io.File import java.nio.file.Files import java.nio.file.Paths -class RouterCli : CliktCommand( +class RouterCli : + CliktCommand( name = "prime", help = "Tools and commands that support the PRIME Data Hub.", printHelpOnEmptyArgs = true, @@ -46,7 +47,8 @@ fun listSchemas(metadata: Metadata) { } } -class ListSchemas : CliktCommand( +class ListSchemas : + CliktCommand( name = "list", help = "list known schemas, senders, and receivers" ) { @@ -91,7 +93,8 @@ class ListSchemas : CliktCommand( } } -class GenerateDocs : CliktCommand( +class GenerateDocs : + CliktCommand( help = """ generate documentation for schemas @@ -236,7 +239,8 @@ class GenerateDocs : CliktCommand( } } -class CompareCsvFiles : CliktCommand( +class CompareCsvFiles : + CliktCommand( name = "compare", help = """ compares two CSV files so you can view the differences within them diff --git a/prime-router/src/main/kotlin/cli/tests/AuthTests.kt b/prime-router/src/main/kotlin/cli/tests/AuthTests.kt index 8a74452beee..d1e5fd712c9 100644 --- a/prime-router/src/main/kotlin/cli/tests/AuthTests.kt +++ b/prime-router/src/main/kotlin/cli/tests/AuthTests.kt @@ -48,9 +48,7 @@ class OktaAuthTests : CoolTest() { companion object { private const val accessTokenDummy = "dummy" - fun abort(message: String): Nothing { - throw PrintMessage(message, printError = true) - } + fun abort(message: String): Nothing = throw PrintMessage(message, printError = true) /** * Create a fake report file using the schema expected by [sender]. Creates a file locally. Does not submit it. @@ -79,8 +77,7 @@ class OktaAuthTests : CoolTest() { fun getOktaAccessToken( environment: Environment, testName: String = "", - ): String { - return if (environment.oktaApp == null) { + ): String = if (environment.oktaApp == null) { accessTokenDummy } else { OktaCommand.fetchAccessToken(environment.oktaApp) @@ -91,7 +88,6 @@ class OktaAuthTests : CoolTest() { ) } - } } override suspend fun run(environment: Environment, options: CoolTestOptions): Boolean { diff --git a/prime-router/src/main/kotlin/cli/tests/BasicTests.kt b/prime-router/src/main/kotlin/cli/tests/BasicTests.kt index 25333e9bc11..a5e4192af64 100644 --- a/prime-router/src/main/kotlin/cli/tests/BasicTests.kt +++ b/prime-router/src/main/kotlin/cli/tests/BasicTests.kt @@ -465,8 +465,9 @@ class End2End : CoolTest() { val processResults = pollForStepResult(internalReportId, TaskAction.process) // verify each result is valid - for (result in processResults.values) + for (result in processResults.values) { passed = passed && examineProcessResponse(result) + } if (!passed) { bad("***async end2end FAILED***: Process result invalid") } @@ -1029,10 +1030,11 @@ class QualityFilter : CoolTest() { val processResults = pollForStepResult(internalReportId, TaskAction.process) // verify each result is valid - for (result in processResults.values) + for (result in processResults.values) { passed = passed && examineProcessResponse(result) && checkJsonItemCountForReceiver(qualityAllReceiver, expectItemCount, result!!) + } } } else { passed = passed && checkJsonItemCountForReceiver(qualityAllReceiver, expectItemCount, json) @@ -1072,10 +1074,11 @@ class QualityFilter : CoolTest() { val processResults2 = pollForStepResult(internalReportId2, TaskAction.process) // verify each result is valid - for (result in processResults2.values) + for (result in processResults2.values) { passed = passed && examineProcessResponse(result) && checkJsonItemCountForReceiver(qualityGoodReceiver, expectItemCount, result!!) + } } } else { passed = passed && checkJsonItemCountForReceiver(qualityGoodReceiver, expectItemCount, json2) @@ -1115,10 +1118,11 @@ class QualityFilter : CoolTest() { val processResults3 = pollForStepResult(internalReportId3, TaskAction.process) // verify each result is valid - for (result in processResults3.values) + for (result in processResults3.values) { passed = passed && examineProcessResponse(result) && checkJsonItemCountForReceiver(qualityFailReceiver, expectItemCount, result!!) + } } } else { passed = passed && checkJsonItemCountForReceiver(qualityFailReceiver, expectItemCount, json3) @@ -1158,10 +1162,11 @@ class QualityFilter : CoolTest() { val processResults4 = pollForStepResult(internalReportId4, TaskAction.process) // verify each result is valid - for (result in processResults4.values) + for (result in processResults4.values) { passed = passed && examineProcessResponse(result) && checkJsonItemCountForReceiver(qualityReversedReceiver, expectItemCount, result!!) + } } } else { passed = passed && checkJsonItemCountForReceiver(qualityReversedReceiver, expectItemCount, json4) @@ -1482,9 +1487,7 @@ class SantaClaus : CoolTest() { return false } - private fun createBad(message: String): Boolean { - return bad("***$name Test FAILED***: $message") - } + private fun createBad(message: String): Boolean = bad("***$name Test FAILED***: $message") } class OtcProctored : CoolTest() { @@ -1533,10 +1536,11 @@ class OtcProctored : CoolTest() { val processResults = pollForStepResult(internalReportId, TaskAction.process) // verify each result is valid - for (result in processResults.values) + for (result in processResults.values) { if (!examineProcessResponse(result)) { bad("*** otcproctored FAILED***: Process result invalid") } + } } } good("Test PASSED: ${pair.first}") diff --git a/prime-router/src/main/kotlin/cli/tests/CompareData.kt b/prime-router/src/main/kotlin/cli/tests/CompareData.kt index 5ae9f38106d..5a22328d69a 100644 --- a/prime-router/src/main/kotlin/cli/tests/CompareData.kt +++ b/prime-router/src/main/kotlin/cli/tests/CompareData.kt @@ -119,7 +119,8 @@ class DataCompareTest : CoolTest() { val outputList = config.value val sender = settings.findSender(input.sender) ?: error("Unable to find sender ${input.sender}") - val receivers = outputList.map { testOutput -> // TODO do we need receivers + val receivers = outputList.map { testOutput -> + // TODO do we need receivers // We expect only one receiver back val rec = settings.receivers.filter { receiver -> receiver.organizationName == testOutput.orgName && receiver.name == testOutput.receiverName @@ -165,8 +166,9 @@ class DataCompareTest : CoolTest() { val processResults = pollForStepResult(internalReportId, TaskAction.process) // verify each result is valid - for (result in processResults.values) + for (result in processResults.values) { passed = passed && examineProcessResponse(result) + } if (!passed) { bad("***$name FAILED***: Process result invalid") } @@ -314,12 +316,10 @@ class CompareData { warnings.addAll(anotherResult.warnings) } - override fun toString(): String { - return """passed: $passed + override fun toString(): String = """passed: $passed errors: ${errors.joinToString()} warnings: ${warnings.joinToString()} """ - } } /** @@ -703,9 +703,11 @@ class CompareCsvData { null } - schemaMsgIdIndex != null && expectedMsgId == actualMsgId || + schemaMsgIdIndex != null && + expectedMsgId == actualMsgId || ( - schemaPatLastNameIndex != null && schemaPatStateIndex != null && + schemaPatLastNameIndex != null && + schemaPatStateIndex != null && expectedLastName == actualLastName && expectedPatState == actualPatState ) @@ -891,9 +893,7 @@ class CompareCsvData { * Compare the raw contents of a file. * @property result the result of the comparison */ -class CompareFile( - val result: CompareData.Result = CompareData.Result(), -) { +class CompareFile(val result: CompareData.Result = CompareData.Result()) { /** * Compare the contents of a file [actual] vs [expected] and provide the [result]. */ diff --git a/prime-router/src/main/kotlin/cli/tests/Hl7Tests.kt b/prime-router/src/main/kotlin/cli/tests/Hl7Tests.kt index 15bdc969686..8fb4026327a 100644 --- a/prime-router/src/main/kotlin/cli/tests/Hl7Tests.kt +++ b/prime-router/src/main/kotlin/cli/tests/Hl7Tests.kt @@ -62,8 +62,9 @@ class Hl7Ingest : CoolTest() { val processResults = pollForStepResult(internalReportId, TaskAction.process) // verify each result is valid - for (result in processResults.values) + for (result in processResults.values) { passed = passed && examineProcessResponse(result) + } if (!passed) { bad("***$name FAILED***: Process result invalid") } diff --git a/prime-router/src/main/kotlin/cli/tests/LivdApiTest.kt b/prime-router/src/main/kotlin/cli/tests/LivdApiTest.kt index 5b50014dd83..bf9488b2c41 100644 --- a/prime-router/src/main/kotlin/cli/tests/LivdApiTest.kt +++ b/prime-router/src/main/kotlin/cli/tests/LivdApiTest.kt @@ -92,8 +92,7 @@ class LivdApiTest : CoolTest() { /** * Run each individual test case */ - private fun runLivdApiTestCases(testCases: List): Boolean { - return testCases.map { + private fun runLivdApiTestCases(testCases: List): Boolean = testCases.map { ugly("Starting test: ${it.name}") val (queryPass, json) = livdApiQuery(it) val sanityCheck = when { @@ -106,7 +105,6 @@ class LivdApiTest : CoolTest() { if (sanityCheck) good("Test passed: ${it.name}") sanityCheck }.reduce { acc, onePassed -> acc and onePassed } - } /** * Runs the query against the LIVD API for the given path and parameters diff --git a/prime-router/src/main/kotlin/cli/tests/LoadTestSimulator.kt b/prime-router/src/main/kotlin/cli/tests/LoadTestSimulator.kt index ed803e3e549..871dac4d7f2 100644 --- a/prime-router/src/main/kotlin/cli/tests/LoadTestSimulator.kt +++ b/prime-router/src/main/kotlin/cli/tests/LoadTestSimulator.kt @@ -35,8 +35,7 @@ abstract class LoadTestSimulator : CoolTest() { val sender: Sender, val doBatchAndSend: Boolean = true, ) { - override fun toString(): String { - return "Simulation:\t$name\n" + + override fun toString(): String = "Simulation:\t$name\n" + "Simultaneous Threads:\t$numThreads\n" + "Submissions Per Thread:\t$numSubmissionsPerThread\n" + "Total Submissions:\t${numThreads * numSubmissionsPerThread}\n" + @@ -48,7 +47,6 @@ abstract class LoadTestSimulator : CoolTest() { "Delay between Submits:\t$millisBetweenSubmissions millis\n" + "Sending From:\t${sender.fullName}\n" + "Do Batch/Send too:\t$doBatchAndSend\n" - } } /** @@ -65,8 +63,7 @@ abstract class LoadTestSimulator : CoolTest() { var elapsedMillisForWholeSimulation: Long = -1, ) { - override fun toString(): String { - return "Simulation Results: \n" + + override fun toString(): String = "Simulation Results: \n" + "Passed:$passed\n" + "Total Submissions:\t$totalSubmissionsCount\n" + "Total Items Submitted:\t$totalItemsCount\n" + @@ -74,7 +71,6 @@ abstract class LoadTestSimulator : CoolTest() { "Elapsed Seconds for Simulation:\t${elapsedMillisForWholeSimulation / 1000} seconds\n" + "Processing Rate:\t$rateString submissions/second\n" + "Avg Speed per submission:\t$avgSecsPerSubmissionString seconds/submission\n" - } } /** @@ -168,8 +164,7 @@ abstract class LoadTestSimulator : CoolTest() { file: File, simulation: Simulation, options: CoolTestOptions, - ): Pair { - return HttpUtilities.postReportFile( + ): Pair = HttpUtilities.postReportFile( environment, file, simulation.sender, @@ -177,7 +172,6 @@ abstract class LoadTestSimulator : CoolTest() { options.key, (if (simulation.doBatchAndSend) null else Options.SkipSend) ) - } /** * A library of useful [Simulation] @@ -397,7 +391,8 @@ abstract class LoadTestSimulator : CoolTest() { val processWaitTimeMillis = measureTimeMillis { // if we are running in async mode, verify the correct number of 'process' records have been generated if (isAsyncProcessMode) { - passed = passed && checkTimedResults( + passed = passed && + checkTimedResults( expectedResults, afterActionId, TaskAction.process, @@ -434,7 +429,8 @@ abstract class LoadTestSimulator : CoolTest() { // poll for batch results - wait for up to 7 minutes // TODO: Will this always be 1 batch? Should it determine results count based on what tests were run? // TODO: Should this dynamically determine how long to wait in case of 60_MIN receiver? - passed = passed && checkTimedResults( + passed = passed && + checkTimedResults( expectedResults, afterActionId, TaskAction.batch, @@ -446,7 +442,8 @@ abstract class LoadTestSimulator : CoolTest() { // poll for send results - wait for up to 7 minutes // TODO: Will this always be 1 batch? Should it determine results count based on what tests were run? // TODO: Should this dynamically determine how long to wait in case of 60_MIN receiver? - passed = passed && checkTimedResults( + passed = passed && + checkTimedResults( expectedResults, afterActionId, TaskAction.send, diff --git a/prime-router/src/main/kotlin/cli/tests/SftpcheckTest.kt b/prime-router/src/main/kotlin/cli/tests/SftpcheckTest.kt index 832a2f4b654..80fa2aa939a 100644 --- a/prime-router/src/main/kotlin/cli/tests/SftpcheckTest.kt +++ b/prime-router/src/main/kotlin/cli/tests/SftpcheckTest.kt @@ -115,10 +115,8 @@ class SftpcheckTest : CoolTest() { private fun sftpReceiverIgnoreOrganizationCheck( path: String, accessToken: String, - ): Pair { - return HttpClientUtils.getWithStringResponse( + ): Pair = HttpClientUtils.getWithStringResponse( url = path, accessToken = accessToken ) - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/tests/TestReportStream.kt b/prime-router/src/main/kotlin/cli/tests/TestReportStream.kt index c20eeffe0ff..c8eaf59a68b 100644 --- a/prime-router/src/main/kotlin/cli/tests/TestReportStream.kt +++ b/prime-router/src/main/kotlin/cli/tests/TestReportStream.kt @@ -57,7 +57,8 @@ enum class TestStatus(val description: String) { * Each individual Test then implements [CoolTest]. * */ -class TestReportStream : CliktCommand( +class TestReportStream : + CliktCommand( name = "test", help = """Run tests of the Router functions @@ -814,7 +815,8 @@ abstract class CoolTest { val errorCount = tree["errorCount"] val destCount = tree["destinationCount"] - if (topic != null && !topic.isNull && + if (topic != null && + !topic.isNull && ( listOf( Topic.COVID_19.jsonVal, @@ -1073,25 +1075,19 @@ abstract class CoolTest { * Format a [msg] string as a good message. * @return a formatted message string */ - fun goodMsgFormat(msg: String): String { - return ANSI_GREEN + msg + ANSI_RESET - } + fun goodMsgFormat(msg: String): String = ANSI_GREEN + msg + ANSI_RESET /** * Format a [msg] string as a bad message. * @return a formatted message string */ - fun badMsgFormat(msg: String): String { - return ANSI_RED + msg + ANSI_RESET - } + fun badMsgFormat(msg: String): String = ANSI_RED + msg + ANSI_RESET /** * Format a [msg] string as an ugly message. * @return a formatted message string */ - fun uglyMsgFormat(msg: String): String { - return ANSI_CYAN + msg + ANSI_RESET - } + fun uglyMsgFormat(msg: String): String = ANSI_CYAN + msg + ANSI_RESET /** * Queries the database and pulls back the action_response json for the requested [reportId] diff --git a/prime-router/src/main/kotlin/common/AzureHttpUtils.kt b/prime-router/src/main/kotlin/common/AzureHttpUtils.kt index 211fd6a8c4c..e65688c4685 100644 --- a/prime-router/src/main/kotlin/common/AzureHttpUtils.kt +++ b/prime-router/src/main/kotlin/common/AzureHttpUtils.kt @@ -16,14 +16,12 @@ object AzureHttpUtils { * @param request The HTTP request message from which to extract the sender's IP address. * @return The sender's IP address as a [String], or `null` if not found. */ - fun getSenderIP(request: HttpRequestMessage<*>): String? { - return ( + fun getSenderIP(request: HttpRequestMessage<*>): String? = ( ( request.headers["x-forwarded-for"]?.split(",") ?.firstOrNull() )?.take(Tables.ACTION.SENDER_IP.dataType.length()) ?: request.headers["x-azure-clientip"] ) - } /** * Retrieves the sender's IP address from a map of HTTP headers. @@ -31,12 +29,10 @@ object AzureHttpUtils { * @param headers A map of HTTP headers from which to extract the sender's IP address. * @return The sender's IP address as a [String], or `null` if not found. */ - fun getSenderIP(headers: Map): String? { - return ( + fun getSenderIP(headers: Map): String? = ( ( headers["x-forwarded-for"]?.split(",") ?.firstOrNull() )?.take(Tables.ACTION.SENDER_IP.dataType.length()) ?: headers["x-azure-clientip"] ) - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/common/BaseEngine.kt b/prime-router/src/main/kotlin/common/BaseEngine.kt index f0e7bcede09..a3086f9396c 100644 --- a/prime-router/src/main/kotlin/common/BaseEngine.kt +++ b/prime-router/src/main/kotlin/common/BaseEngine.kt @@ -18,9 +18,7 @@ import org.apache.logging.log4j.kotlin.Logging * TODO: This class will need to be further refactored / fleshed out. Only minimal changes required for #4824 are * included in this file at this time to limit scope */ -abstract class BaseEngine( - val queue: QueueAccess = QueueAccess, -) : Logging { +abstract class BaseEngine(val queue: QueueAccess = QueueAccess) : Logging { companion object { val sequentialLimit = 500 diff --git a/prime-router/src/main/kotlin/common/CsvUtilities.kt b/prime-router/src/main/kotlin/common/CsvUtilities.kt index 6a243935d9c..32abb5da31f 100644 --- a/prime-router/src/main/kotlin/common/CsvUtilities.kt +++ b/prime-router/src/main/kotlin/common/CsvUtilities.kt @@ -43,15 +43,13 @@ class CsvUtilities { return tableToString(mergedTable) } - private fun stringToTable(csvTable: String): List> { - return csvReader { + private fun stringToTable(csvTable: String): List> = csvReader { quoteChar = '"' delimiter = ',' skipEmptyLine = false insufficientFieldsRowBehaviour = InsufficientFieldsRowBehaviour.ERROR excessFieldsRowBehaviour = ExcessFieldsRowBehaviour.ERROR }.readAll(csvTable) - } private fun tableToString(table: List>): String { val outputStream = ByteArrayOutputStream() diff --git a/prime-router/src/main/kotlin/common/DateUtilities.kt b/prime-router/src/main/kotlin/common/DateUtilities.kt index 1faa69329fb..646f3998d0e 100644 --- a/prime-router/src/main/kotlin/common/DateUtilities.kt +++ b/prime-router/src/main/kotlin/common/DateUtilities.kt @@ -121,8 +121,7 @@ object DateUtilities { fun getFormatter( dateTimeFormat: DateTimeFormat? = null, useHighPrecisionOffset: Boolean? = null, - ): DateTimeFormatter { - return when (dateTimeFormat) { + ): DateTimeFormatter = when (dateTimeFormat) { DateTimeFormat.HIGH_PRECISION_OFFSET -> highPrecisionDateTimeFormatter DateTimeFormat.LOCAL -> localDateTimeFormatter DateTimeFormat.DATE_ONLY -> dateFormatter @@ -134,7 +133,6 @@ object DateUtilities { } } } - } /** * This method takes a date value as a string and returns a @@ -174,8 +172,7 @@ object DateUtilities { } /** Parse the date according to the single pattern passed in, or return null */ - fun parseDate(dateValue: String, formatString: String): TemporalAccessor? { - return try { + fun parseDate(dateValue: String, formatString: String): TemporalAccessor? = try { DateTimeFormatter.ofPattern(formatString) .parseBest( dateValue, @@ -188,15 +185,12 @@ object DateUtilities { } catch (_: Throwable) { null } - } - fun tryParseIsoDate(dateValue: String): TemporalAccessor? { - return try { + fun tryParseIsoDate(dateValue: String): TemporalAccessor? = try { Instant.parse(dateValue) } catch (_: DateTimeParseException) { null } - } /** * Given a [temporalAccessor] this will check the type that it needs to return @@ -407,9 +401,7 @@ object DateUtilities { * @param [value] datetime value to be parsed. * @return [OffsetDateTime] the best parsed datetime value */ - private fun getBestDateTime(value: String): OffsetDateTime { - return parseDate(value).toOffsetDateTime() - } + private fun getBestDateTime(value: String): OffsetDateTime = parseDate(value).toOffsetDateTime() /** * Convert a [Duration] to an integer years value. It takes the [Duration.toDays] value @@ -420,17 +412,14 @@ object DateUtilities { * number of years calculated from this slightly based on birthdate, but it's an outside * chance, so it's acceptable */ - fun Duration.toYears(): Int { - return floor(abs(this.toDays() / 365.0)).toInt() - } + fun Duration.toYears(): Int = floor(abs(this.toDays() / 365.0)).toInt() /** * Given a temporal accessor of some sort, coerce it to an offset date time value. * If the temporal accessor is of type LocalDate, then we don't have a time, and we coerce it * to use the local "start of day", and then convert to the date time offset. */ - fun TemporalAccessor.toOffsetDateTime(zoneId: ZoneId? = null): OffsetDateTime { - return when (this) { + fun TemporalAccessor.toOffsetDateTime(zoneId: ZoneId? = null): OffsetDateTime = when (this) { // coerce the local date to the start of the day. it's not great, but if we did not // get the time, then pushing it to start of day is *probably* okay. At some point // we should probably throw a coercion warning when we do this @@ -446,7 +435,6 @@ object DateUtilities { is OffsetDateTime, is ZonedDateTime -> OffsetDateTime.from(this) else -> error("Unsupported format!") } - } /** * Given a temporal accessor, it converts it to a local date time instant. It can cleanly convert @@ -459,8 +447,7 @@ object DateUtilities { * time value to start of day is okay. Probably. Maybe. For future work we should probably throw * a coercion error when this happens and root these out of the system. */ - fun TemporalAccessor.toLocalDateTime(): LocalDateTime { - return when (this) { + fun TemporalAccessor.toLocalDateTime(): LocalDateTime = when (this) { is LocalDateTime -> this // we are coercing local date to start of date for the local time and then casting it to // local date time. This is a dicey proposition, and we should probably elicit some kind @@ -470,16 +457,13 @@ object DateUtilities { is ZonedDateTime, is OffsetDateTime, is Instant -> LocalDateTime.from(this) else -> error("Unsupported format") } - } /** Convert to a local date */ - fun TemporalAccessor.toLocalDate(): LocalDate { - return when (this) { + fun TemporalAccessor.toLocalDate(): LocalDate = when (this) { is LocalDate -> this is ZonedDateTime, is OffsetDateTime, is LocalDateTime, is Instant -> LocalDate.from(this) else -> error("Unsupported format") } - } /** * Given a Temporal Accessor, this attempts to convert to other date time types available. Some @@ -488,8 +472,7 @@ object DateUtilities { * try to convert a LocalDate to a ZonedDateTime without telling it what time zone "local" is, it's * going to fail. */ - fun TemporalAccessor.toZonedDateTime(zoneId: ZoneId? = null): ZonedDateTime { - return when (this) { + fun TemporalAccessor.toZonedDateTime(zoneId: ZoneId? = null): ZonedDateTime = when (this) { is ZonedDateTime -> { if (zoneId != null && this.zone != zoneId) { this.withZoneSameInstant(zoneId) @@ -514,7 +497,6 @@ object DateUtilities { } else -> error("Unsupported format for converting to ZonedDateTime") } - } /** * An extension method to TemporalAccessor that will format the date for us by calling the internal method @@ -550,11 +532,9 @@ object DateUtilities { fun TemporalAccessor.asFormattedString( dateTimeFormat: String? = null, convertPositiveDateTimeOffsetToNegative: Boolean = false, - ): String { - return getDateAsFormattedString( + ): String = getDateAsFormattedString( this, dateTimeFormat ?: DateUtilities.datetimePattern, convertPositiveDateTimeOffsetToNegative ) - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/common/Environment.kt b/prime-router/src/main/kotlin/common/Environment.kt index 012305920dd..3a5976d520c 100644 --- a/prime-router/src/main/kotlin/common/Environment.kt +++ b/prime-router/src/main/kotlin/common/Environment.kt @@ -51,15 +51,11 @@ enum class Environment( /** * Available feature flags for enabling different features */ - enum class FeatureFlags( - val enabledByDefault: Boolean = false, - ) { + enum class FeatureFlags(val enabledByDefault: Boolean = false) { FHIR_ENGINE_TEST_PIPELINE(), ; - fun enabled(): Boolean { - return enabledByDefault || System.getenv(this.toString()) == "enabled" - } + fun enabled(): Boolean = enabledByDefault || System.getenv(this.toString()) == "enabled" } companion object { @@ -68,9 +64,7 @@ enum class Environment( * @return an environment * @throws IllegalArgumentException if the environment cannot be found */ - fun get(environment: String): Environment { - return valueOf(environment.uppercase()) - } + fun get(environment: String): Environment = valueOf(environment.uppercase()) /** * Get the environment from the system environment. @@ -88,8 +82,7 @@ enum class Environment( /** * Get the baseUrl for a URL that contains only the host and port. */ - internal fun getBaseUrl(inputUrl: URL): String { - return if (inputUrl.port > 0 && + internal fun getBaseUrl(inputUrl: URL): String = if (inputUrl.port > 0 && ( (inputUrl.protocol == "http" && inputUrl.port != 80) || (inputUrl.protocol == "https" && inputUrl.port != 443) @@ -99,25 +92,20 @@ enum class Environment( } else { inputUrl.host } - } /** * Checks if the current environment is the local environment. * * @return true if local environment, false otherwise */ - fun isLocal(): Boolean { - return get() == LOCAL - } + fun isLocal(): Boolean = get() == LOCAL /** * Checks if the current environment is the production environment. * * @return true if production environment, false otherwise */ - fun isProd(): Boolean { - return get() == PROD - } + fun isProd(): Boolean = get() == PROD /** * Time zone to use for ReportStream. Note that Azure runs on UTC, so this forces our local runs to also be UTC. diff --git a/prime-router/src/main/kotlin/common/HttpClientUtils.kt b/prime-router/src/main/kotlin/common/HttpClientUtils.kt index b406ef0bcb3..2ebf0b35df4 100644 --- a/prime-router/src/main/kotlin/common/HttpClientUtils.kt +++ b/prime-router/src/main/kotlin/common/HttpClientUtils.kt @@ -108,8 +108,7 @@ class HttpClientUtils { timeout: Long = REQUEST_TIMEOUT_MILLIS, queryParameters: Map? = null, httpClient: HttpClient? = null, - ): HttpResponse { - return invoke( + ): HttpResponse = invoke( HttpMethod.Get, url = url, accessToken = accessToken, @@ -119,7 +118,6 @@ class HttpClientUtils { queryParameters = queryParameters, httpClient = httpClient ) - } /** * PUT (modify resource) operation to the given endpoint resource [url] @@ -184,8 +182,7 @@ class HttpClientUtils { queryParameters: Map? = null, jsonPayload: String? = null, httpClient: HttpClient? = null, - ): HttpResponse { - return invoke( + ): HttpResponse = invoke( method = HttpMethod.Put, url = url, accessToken = accessToken, @@ -196,7 +193,6 @@ class HttpClientUtils { jsonPayload = jsonPayload, httpClient = httpClient ) - } /** * POST (create resource) operation to the given endpoint resource [url] @@ -260,8 +256,7 @@ class HttpClientUtils { queryParameters: Map? = null, jsonPayload: String, httpClient: HttpClient? = null, - ): HttpResponse { - return invoke( + ): HttpResponse = invoke( method = HttpMethod.Post, url = url, accessToken = accessToken, @@ -272,7 +267,6 @@ class HttpClientUtils { jsonPayload = jsonPayload, httpClient = httpClient ) - } /** * Submit form to the endpoint as indicated by [url] @@ -294,8 +288,7 @@ class HttpClientUtils { timeout: Long = REQUEST_TIMEOUT_MILLIS, formParams: Map? = null, httpClient: HttpClient? = null, - ): T { - return runBlocking { + ): T = runBlocking { submitForm( url = url, accessToken = accessToken, @@ -306,7 +299,6 @@ class HttpClientUtils { httpClient = httpClient, ).body() } - } /** * Submit form to the endpoint as indicated by [url] @@ -328,8 +320,7 @@ class HttpClientUtils { timeout: Long = REQUEST_TIMEOUT_MILLIS, formParams: Map? = null, httpClient: HttpClient? = null, - ): HttpResponse { - return runBlocking { + ): HttpResponse = runBlocking { (httpClient ?: getDefaultHttpClient()).submitForm( url, formParameters = Parameters.build { @@ -357,7 +348,6 @@ class HttpClientUtils { accept(acceptedContent) } } - } /** * HEAD operation to the given endpoint resource [url] @@ -415,8 +405,7 @@ class HttpClientUtils { timeout: Long? = REQUEST_TIMEOUT_MILLIS, queryParameters: Map? = null, httpClient: HttpClient? = null, - ): HttpResponse { - return invoke( + ): HttpResponse = invoke( method = HttpMethod.Head, url = url, accessToken = accessToken, @@ -426,7 +415,6 @@ class HttpClientUtils { queryParameters = queryParameters, httpClient = httpClient ) - } /** * DELETE a resource by endpoint URL [url] @@ -490,8 +478,7 @@ class HttpClientUtils { timeout: Long = REQUEST_TIMEOUT_MILLIS, queryParameters: Map? = null, httpClient: HttpClient? = null, - ): HttpResponse { - return invoke( + ): HttpResponse = invoke( method = HttpMethod.Delete, url = url, accessToken = accessToken, @@ -501,7 +488,6 @@ class HttpClientUtils { queryParameters = queryParameters, httpClient = httpClient ) - } /** * Common helper for external func @@ -516,8 +502,7 @@ class HttpClientUtils { queryParameters: Map? = null, jsonPayload: String? = null, httpClient: HttpClient? = null, - ): HttpResponse { - return runBlocking { + ): HttpResponse = runBlocking { (httpClient ?: getDefaultHttpClient()).request(url) { this.method = method timeout { @@ -549,7 +534,6 @@ class HttpClientUtils { } } } - } /** * Get a http client with sensible default settings @@ -559,8 +543,6 @@ class HttpClientUtils { * * @return a HttpClient with all sensible defaults */ - fun getDefaultHttpClient(): HttpClient { - return httpClient - } + fun getDefaultHttpClient(): HttpClient = httpClient } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/config/validation/ConfigurationType.kt b/prime-router/src/main/kotlin/config/validation/ConfigurationType.kt index 44f2337790f..adefca7e0c0 100644 --- a/prime-router/src/main/kotlin/config/validation/ConfigurationType.kt +++ b/prime-router/src/main/kotlin/config/validation/ConfigurationType.kt @@ -47,9 +47,8 @@ sealed class ConfigurationType { override val konformValidation: KonformValidation> = OrganizationValidation - override fun convert(node: JsonNode): List { - return mapper.convertValue(node, Array::class.java).toList() - } + override fun convert(node: JsonNode): List = + mapper.convertValue(node, Array::class.java).toList() } /** @@ -62,9 +61,8 @@ sealed class ConfigurationType { override val konformValidation: KonformValidation = FhirToFhirTransformValidation - override fun convert(node: JsonNode): FhirTransformSchema { - return mapper.convertValue(node, FhirTransformSchema::class.java) - } + override fun convert(node: JsonNode): FhirTransformSchema = + mapper.convertValue(node, FhirTransformSchema::class.java) } data object FhirToHL7Mapping : ConfigurationType() { @@ -74,9 +72,8 @@ sealed class ConfigurationType { override val konformValidation: KonformValidation = FhirToHL7MappingValidation - override fun convert(node: JsonNode): HL7ConverterSchema { - return mapper.convertValue(node, HL7ConverterSchema::class.java) - } + override fun convert(node: JsonNode): HL7ConverterSchema = + mapper.convertValue(node, HL7ConverterSchema::class.java) } data object HL7ToFhirMappingMessageTemplate : ConfigurationType() { @@ -87,9 +84,8 @@ sealed class ConfigurationType { override val konformValidation: KonformValidation = HL7ToFHIRMappingMessageTemplateValidation - override fun convert(node: JsonNode): HL7ToFHIRMappingMessageTemplate { - return mapper.convertValue(node, HL7ToFHIRMappingMessageTemplate::class.java) - } + override fun convert(node: JsonNode): HL7ToFHIRMappingMessageTemplate = + mapper.convertValue(node, HL7ToFHIRMappingMessageTemplate::class.java) } data object HL7ToFhirMappingResourceTemplate : ConfigurationType() { diff --git a/prime-router/src/main/kotlin/config/validation/ConfigurationValidationResult.kt b/prime-router/src/main/kotlin/config/validation/ConfigurationValidationResult.kt index c510f40b9e5..3a9256909ba 100644 --- a/prime-router/src/main/kotlin/config/validation/ConfigurationValidationResult.kt +++ b/prime-router/src/main/kotlin/config/validation/ConfigurationValidationResult.kt @@ -9,15 +9,11 @@ sealed interface ConfigurationValidationResult * A successful configuration validation containing * the parsed class */ -data class ConfigurationValidationSuccess( - val parsed: T, -) : ConfigurationValidationResult +data class ConfigurationValidationSuccess(val parsed: T) : ConfigurationValidationResult /** * A failed configuration validation containing errors * and an optional thrown exception */ -data class ConfigurationValidationFailure( - val errors: List, - val cause: Throwable? = null, -) : ConfigurationValidationResult \ No newline at end of file +data class ConfigurationValidationFailure(val errors: List, val cause: Throwable? = null) : + ConfigurationValidationResult \ No newline at end of file diff --git a/prime-router/src/main/kotlin/config/validation/ConfigurationValidationService.kt b/prime-router/src/main/kotlin/config/validation/ConfigurationValidationService.kt index db90f500bb7..164b0dee438 100644 --- a/prime-router/src/main/kotlin/config/validation/ConfigurationValidationService.kt +++ b/prime-router/src/main/kotlin/config/validation/ConfigurationValidationService.kt @@ -40,22 +40,18 @@ class ConfigurationValidationServiceImpl( ConfigurationValueValidationServiceImpl(), ) : ConfigurationValidationService { - override fun validateYAML(configType: ConfigurationType, file: File): ConfigurationValidationResult { - return validateYAML(configType, file.inputStream()) - } + override fun validateYAML(configType: ConfigurationType, file: File): ConfigurationValidationResult = + validateYAML(configType, file.inputStream()) override fun validateYAML( configType: ConfigurationType, yamlString: String, - ): ConfigurationValidationResult { - return validateYAML(configType, yamlString.byteInputStream()) - } + ): ConfigurationValidationResult = validateYAML(configType, yamlString.byteInputStream()) override fun validateYAML( configType: ConfigurationType, inputStream: InputStream, - ): ConfigurationValidationResult { - return when ( + ): ConfigurationValidationResult = when ( val jsonSchemaValidation = jsonSchemaValidationService.validateYAMLStructure(configType, inputStream) ) { is ConfigurationValidationSuccess -> { @@ -63,5 +59,4 @@ class ConfigurationValidationServiceImpl( } is ConfigurationValidationFailure -> jsonSchemaValidation } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/config/validation/ConfigurationValueValidationService.kt b/prime-router/src/main/kotlin/config/validation/ConfigurationValueValidationService.kt index 8aeb6b651b5..75361892c3c 100644 --- a/prime-router/src/main/kotlin/config/validation/ConfigurationValueValidationService.kt +++ b/prime-router/src/main/kotlin/config/validation/ConfigurationValueValidationService.kt @@ -23,8 +23,7 @@ class ConfigurationValueValidationServiceImpl : ConfigurationValueValidationServ override fun validate( configType: ConfigurationType, config: T, - ): ConfigurationValidationResult { - return when (val result = configType.konformValidation.validation.validate(config)) { + ): ConfigurationValidationResult = when (val result = configType.konformValidation.validation.validate(config)) { is Valid -> { ConfigurationValidationSuccess(config) } @@ -33,9 +32,6 @@ class ConfigurationValueValidationServiceImpl : ConfigurationValueValidationServ ConfigurationValidationFailure(errors) } } - } - private fun formatErrorMessage(error: ValidationError): String { - return "path=${error.dataPath}, message=${error.message}" - } + private fun formatErrorMessage(error: ValidationError): String = "path=${error.dataPath}, message=${error.message}" } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/config/validation/JsonSchemaValidationService.kt b/prime-router/src/main/kotlin/config/validation/JsonSchemaValidationService.kt index 5de3051286a..6680358af33 100644 --- a/prime-router/src/main/kotlin/config/validation/JsonSchemaValidationService.kt +++ b/prime-router/src/main/kotlin/config/validation/JsonSchemaValidationService.kt @@ -38,16 +38,12 @@ class JsonSchemaValidationServiceImpl : JsonSchemaValidationService { override fun validateYAMLStructure( configType: ConfigurationType, file: File, - ): ConfigurationValidationResult { - return validateYAMLStructure(configType, file.inputStream()) - } + ): ConfigurationValidationResult = validateYAMLStructure(configType, file.inputStream()) override fun validateYAMLStructure( configType: ConfigurationType, yamlString: String, - ): ConfigurationValidationResult { - return validateYAMLStructure(configType, yamlString.byteInputStream()) - } + ): ConfigurationValidationResult = validateYAMLStructure(configType, yamlString.byteInputStream()) override fun validateYAMLStructure( configType: ConfigurationType, diff --git a/prime-router/src/main/kotlin/config/validation/Validations.kt b/prime-router/src/main/kotlin/config/validation/Validations.kt index 5b32bcb4472..bbce5722dd6 100644 --- a/prime-router/src/main/kotlin/config/validation/Validations.kt +++ b/prime-router/src/main/kotlin/config/validation/Validations.kt @@ -37,9 +37,7 @@ abstract class KonformValidation { /** * Is the FHIR path valid? */ - protected fun validFhirPath(path: String): Boolean { - return FhirPathUtils.validatePath(path, customResolver) - } + protected fun validFhirPath(path: String): Boolean = FhirPathUtils.validatePath(path, customResolver) } /** @@ -166,8 +164,7 @@ object HL7ToFHIRMappingResourceTemplateValidation : KonformValidation isFormatted(condition.var1) is CheckNull -> isFormatted(condition.var1) is SimpleBiCondition -> isFormatted(condition.var1) @@ -179,18 +176,13 @@ object HL7ToFHIRMappingResourceTemplateValidation : KonformValidation throw IllegalArgumentException("Condition is of unrecognized type: ${condition.javaClass.name}") } - } - private fun isFormatted(str: String): Boolean { - return str.startsWith("$") - } + private fun isFormatted(str: String): Boolean = str.startsWith("$") /** * recurses back over validateConditionFormatting for individual conditions */ - private fun checkCompoundCondition(conditions: List): Boolean { - return conditions.fold(true) { acc, cur -> + private fun checkCompoundCondition(conditions: List): Boolean = conditions.fold(true) { acc, cur -> acc && validateConditionFormatting(cur) } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/config/validation/models/HL7ToFHIRMappingMessageTemplate.kt b/prime-router/src/main/kotlin/config/validation/models/HL7ToFHIRMappingMessageTemplate.kt index 8decfd903c6..a714aa460f0 100644 --- a/prime-router/src/main/kotlin/config/validation/models/HL7ToFHIRMappingMessageTemplate.kt +++ b/prime-router/src/main/kotlin/config/validation/models/HL7ToFHIRMappingMessageTemplate.kt @@ -14,9 +14,7 @@ import io.github.linuxforhealth.hl7.message.HL7Segment * @see io.github.linuxforhealth.hl7.message.HL7FHIRResourceTemplateAttributes * @see io.github.linuxforhealth.hl7.message.HL7Segment */ -data class HL7ToFHIRMappingMessageTemplate( - val resources: List, -) +data class HL7ToFHIRMappingMessageTemplate(val resources: List) data class MappingTemplateResource( val resourceName: String, diff --git a/prime-router/src/main/kotlin/credentials/AzureCredentialService.kt b/prime-router/src/main/kotlin/credentials/AzureCredentialService.kt index d0847cc957f..3fef7812ea6 100644 --- a/prime-router/src/main/kotlin/credentials/AzureCredentialService.kt +++ b/prime-router/src/main/kotlin/credentials/AzureCredentialService.kt @@ -16,13 +16,11 @@ internal object AzureCredentialService : CredentialService() { internal fun initSecretClient( secretClientBuilder: SecretClientBuilder = SecretClientBuilder(), credential: TokenCredential = DefaultAzureCredentialBuilder().build(), - ): SecretClient { - return secretClientBuilder + ): SecretClient = secretClientBuilder .vaultUrl("https://$KEY_VAULT_NAME.vault.azure.net") .credential(credential) .retryPolicy(RetryPolicy(ExponentialBackoff(3, Duration.ofMillis(250L), Duration.ofSeconds(2)))) .buildClient() - } override fun fetchCredential(connectionId: String): Credential? { return secretClient.getSecret("$connectionId")?.let { diff --git a/prime-router/src/main/kotlin/credentials/Credential.kt b/prime-router/src/main/kotlin/credentials/Credential.kt index 76ef301cfc0..d6aea2c23ba 100644 --- a/prime-router/src/main/kotlin/credentials/Credential.kt +++ b/prime-router/src/main/kotlin/credentials/Credential.kt @@ -13,27 +13,24 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule * A simple user & password credential. Can be used for SFTP transports. */ data class UserPassCredential(val user: String, val pass: String) : - Credential(), SftpCredential, SoapCredential, RestCredential + Credential(), + SftpCredential, + SoapCredential, + RestCredential /** * A PPK credential. Can be used for SFTP transports. */ -data class UserPpkCredential( - val user: String, - val key: String, - val keyPass: String, - val pass: String? = null, -) : Credential(), SftpCredential +data class UserPpkCredential(val user: String, val key: String, val keyPass: String, val pass: String? = null) : + Credential(), + SftpCredential /** * A PEM credential. Can be used for SFTP transports. */ -data class UserPemCredential( - val user: String, - val key: String, - val keyPass: String, - val pass: String? = null, -) : Credential(), SftpCredential +data class UserPemCredential(val user: String, val key: String, val keyPass: String, val pass: String? = null) : + Credential(), + SftpCredential /** * A credential that is saved in a Java Key Store (JKS) @@ -63,7 +60,9 @@ data class UserJksCredential( * [trustAlias] is the alias for the trust/public certificate stored in the JKS */ val trustAlias: String, -) : Credential(), SoapCredential, RestCredential +) : Credential(), + SoapCredential, + RestCredential /** * An API Key credential along with the user who stored it @@ -77,7 +76,8 @@ data class UserApiKeyCredential( * [apiKey] is the api key */ val apiKey: String, -) : Credential(), RestCredential +) : Credential(), + RestCredential /** * An Assertion credential @@ -88,7 +88,8 @@ data class UserAssertionCredential( * [assertion] is the api key */ val assertion: String, -) : Credential(), RestCredential +) : Credential(), + RestCredential /** * The credential base class for all other credentials to inherit from diff --git a/prime-router/src/main/kotlin/credentials/CredentialManagement.kt b/prime-router/src/main/kotlin/credentials/CredentialManagement.kt index 2831cef3b58..21b17ad71c8 100644 --- a/prime-router/src/main/kotlin/credentials/CredentialManagement.kt +++ b/prime-router/src/main/kotlin/credentials/CredentialManagement.kt @@ -1,18 +1,14 @@ package gov.cdc.prime.router.credentials // Option to access credential service by static class -class CredentialHelper() { +class CredentialHelper { companion object { - fun getCredentialService(): CredentialService { - return credentialServiceForStorageMethod() - } + fun getCredentialService(): CredentialService = credentialServiceForStorageMethod() - fun formCredentialLabel(fromReceiverName: String): String { - return fromReceiverName + fun formCredentialLabel(fromReceiverName: String): String = fromReceiverName .replace(".", "--") .replace("_", "-") .uppercase() - } } } @@ -23,10 +19,9 @@ interface CredentialManagement { get() = credentialServiceForStorageMethod() } -internal fun credentialServiceForStorageMethod(): CredentialService { - return when (System.getenv("CREDENTIAL_STORAGE_METHOD")) { +internal fun credentialServiceForStorageMethod(): CredentialService = + when (System.getenv("CREDENTIAL_STORAGE_METHOD")) { "AZURE" -> AzureCredentialService "HASHICORP_VAULT" -> HashicorpVaultCredentialService else -> MemoryCredentialService - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/credentials/HashicorpVaultCredentialService.kt b/prime-router/src/main/kotlin/credentials/HashicorpVaultCredentialService.kt index 0eddb617f0c..af9b456cd88 100644 --- a/prime-router/src/main/kotlin/credentials/HashicorpVaultCredentialService.kt +++ b/prime-router/src/main/kotlin/credentials/HashicorpVaultCredentialService.kt @@ -13,11 +13,9 @@ internal object HashicorpVaultCredentialService : CredentialService(), Logging { override fun fetchCredential( connectionId: String, - ): Credential? { - return fetchCredentialHelper( + ): Credential? = fetchCredentialHelper( connectionId = connectionId ) - } /** * object specific impl - also adapted to unit tests @@ -45,12 +43,10 @@ internal object HashicorpVaultCredentialService : CredentialService(), Logging { override fun saveCredential( connectionId: String, credential: Credential, - ) { - return saveCredentialHelper( + ) = saveCredentialHelper( connectionId = connectionId, credential = credential ) - } /** * object specific impl - also adapted to unit tests diff --git a/prime-router/src/main/kotlin/credentials/MemoryCredentialService.kt b/prime-router/src/main/kotlin/credentials/MemoryCredentialService.kt index 5d866ee1138..caf3ab8ee40 100644 --- a/prime-router/src/main/kotlin/credentials/MemoryCredentialService.kt +++ b/prime-router/src/main/kotlin/credentials/MemoryCredentialService.kt @@ -3,9 +3,7 @@ package gov.cdc.prime.router.credentials internal object MemoryCredentialService : CredentialService() { private val credentialList: HashMap = HashMap() - override fun fetchCredential(connectionId: String): Credential? { - return credentialList[connectionId] - } + override fun fetchCredential(connectionId: String): Credential? = credentialList[connectionId] override fun saveCredential(connectionId: String, credential: Credential) { credentialList[connectionId] = credential diff --git a/prime-router/src/main/kotlin/db/ReportFileDatabaseAccess.kt b/prime-router/src/main/kotlin/db/ReportFileDatabaseAccess.kt index 5d25284f5d2..fc524443f02 100644 --- a/prime-router/src/main/kotlin/db/ReportFileDatabaseAccess.kt +++ b/prime-router/src/main/kotlin/db/ReportFileDatabaseAccess.kt @@ -29,8 +29,7 @@ sealed class ReportFileApiFilter : ApiFilter { * Filters results to those where the created_at is greater than or equal to the passed in date * @param value the date that results will be greater than or equal to */ - class Since(override val value: OffsetDateTime) : - ReportFileApiFilter() { + class Since(override val value: OffsetDateTime) : ReportFileApiFilter() { override val tableField: TableField = ReportFile.REPORT_FILE.CREATED_AT } @@ -80,21 +79,15 @@ class ReportFileApiSearch internal constructor( ) { /** Converts a [ReportFileApiFilter] into a JOOQ condition */ - override fun getCondition(filter: ReportFileApiFilter<*>): Condition { - return when (filter) { + override fun getCondition(filter: ReportFileApiFilter<*>): Condition = when (filter) { is ReportFileApiFilter.Since -> filter.tableField.ge(filter.value) is ReportFileApiFilter.Until -> filter.tableField.le(filter.value) } - } /** Defaults to [ReportFile.CREATED_AT] if no sort is set */ - override fun getSortColumn(): Field<*> { - return sortParameter ?: ReportFile.REPORT_FILE.CREATED_AT - } + override fun getSortColumn(): Field<*> = sortParameter ?: ReportFile.REPORT_FILE.CREATED_AT - override fun getPrimarySortColumn(): Field<*> { - return ReportFile.REPORT_FILE.REPORT_ID - } + override fun getPrimarySortColumn(): Field<*> = ReportFile.REPORT_FILE.REPORT_ID /** * Companion object that implements [ApiSearchResult] and parses a value into [ReportFileApiSearch] @@ -144,13 +137,11 @@ class ReportFileDatabaseAccess(val db: DatabaseAccess = BaseEngine.databaseAcces * * @param search the search configuration to apply to the query */ - fun getReports(search: ReportFileApiSearch): ApiSearchResult { - return db.transactReturning { txn -> + fun getReports(search: ReportFileApiSearch): ApiSearchResult = db.transactReturning { txn -> search.fetchResults( DSL.using(txn), ReportFile.REPORT_FILE.asterisk(), ReportFile.REPORT_FILE, ) } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/docgenerators/DocumentationFactory.kt b/prime-router/src/main/kotlin/docgenerators/DocumentationFactory.kt index ad35789afd0..ca39d6a90f3 100644 --- a/prime-router/src/main/kotlin/docgenerators/DocumentationFactory.kt +++ b/prime-router/src/main/kotlin/docgenerators/DocumentationFactory.kt @@ -37,13 +37,11 @@ abstract class DocumentationFactory { schema: Schema, includeTimestamps: Boolean, fileExtension: String, - ): String { - return (outputFileName ?: canonicalizeSchemaName(schema)) + if (includeTimestamps) { + ): String = (outputFileName ?: canonicalizeSchemaName(schema)) + if (includeTimestamps) { "-${LocalDate.now().format(formatter)}.$fileExtension" } else { ".$fileExtension" } - } /** * Verifies the output directory exists and creates it if it doesn't diff --git a/prime-router/src/main/kotlin/fhirengine/azure/FHIRFunctions.kt b/prime-router/src/main/kotlin/fhirengine/azure/FHIRFunctions.kt index 0166b734662..83e23240bae 100644 --- a/prime-router/src/main/kotlin/fhirengine/azure/FHIRFunctions.kt +++ b/prime-router/src/main/kotlin/fhirengine/azure/FHIRFunctions.kt @@ -23,6 +23,7 @@ import gov.cdc.prime.router.common.BaseEngine import gov.cdc.prime.router.fhirengine.engine.FHIRConverter import gov.cdc.prime.router.fhirengine.engine.FHIRDestinationFilter import gov.cdc.prime.router.fhirengine.engine.FHIREngine +import gov.cdc.prime.router.fhirengine.engine.FHIRReceiverEnrichment import gov.cdc.prime.router.fhirengine.engine.FHIRReceiverFilter import gov.cdc.prime.router.fhirengine.engine.FHIRTranslator import gov.cdc.prime.router.fhirengine.engine.FhirConvertSubmissionQueueMessage @@ -154,6 +155,24 @@ class FHIRFunctions( ) } + /** + * An Azure function for enriching ELR FHIR receiver data. + */ + @FunctionName("elr-fhir-receiver-enrichment") + @StorageAccount("AzureWebJobsStorage") + fun receiverEnrichment( + @QueueTrigger(name = "message", queueName = QueueMessage.elrReceiverEnrichmentQueueName) + message: String, + @BindingName("DequeueCount") dequeueCount: Int = 1, + ) { + process( + message, + dequeueCount, + FHIRReceiverEnrichment(reportStreamEventService = reportStreamEventService), + ActionHistory(TaskAction.receiver_enrichment) + ) + } + /** * Functionality separated from azure function call so a mocked fhirEngine can be passed in for testing. * Reads the [message] passed in and processes it using the appropriate [fhirEngine]. If there is an error diff --git a/prime-router/src/main/kotlin/fhirengine/config/HL7TranslationConfig.kt b/prime-router/src/main/kotlin/fhirengine/config/HL7TranslationConfig.kt index 233104b7a47..95a5eb80fe4 100644 --- a/prime-router/src/main/kotlin/fhirengine/config/HL7TranslationConfig.kt +++ b/prime-router/src/main/kotlin/fhirengine/config/HL7TranslationConfig.kt @@ -8,9 +8,6 @@ import gov.cdc.prime.router.fhirengine.translation.hl7.config.TruncationConfig /** * HL7 specific custom context configuration */ -data class HL7TranslationConfig( - val hl7Configuration: Hl7Configuration, - val receiver: Receiver?, -) : ContextConfig { +data class HL7TranslationConfig(val hl7Configuration: Hl7Configuration, val receiver: Receiver?) : ContextConfig { val truncationConfig: TruncationConfig = hl7Configuration.truncationConfig } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/engine/CustomFhirPathFunctions.kt b/prime-router/src/main/kotlin/fhirengine/engine/CustomFhirPathFunctions.kt index 0f8988d1453..a49af410097 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/CustomFhirPathFunctions.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/CustomFhirPathFunctions.kt @@ -44,13 +44,11 @@ class CustomFhirPathFunctions : FhirPathFunctions { * Get from a [functionName]. * @return the function name enum or null if not found */ - fun get(functionName: String?): CustomFhirPathFunctionNames? { - return try { + fun get(functionName: String?): CustomFhirPathFunctionNames? = try { functionName?.let { CustomFhirPathFunctionNames.valueOf(it.replaceFirstChar(Char::titlecase)) } } catch (e: IllegalArgumentException) { null } - } } } @@ -61,8 +59,7 @@ class CustomFhirPathFunctions : FhirPathFunctions { override fun resolveFunction( functionName: String?, additionalFunctions: FhirPathFunctions?, - ): FunctionDetails? { - return when (CustomFhirPathFunctionNames.get(functionName)) { + ): FunctionDetails? = when (CustomFhirPathFunctionNames.get(functionName)) { CustomFhirPathFunctionNames.LivdTableLookup -> { FunctionDetails( "looks up data in the LIVD table that match the information provided", @@ -95,7 +92,6 @@ class CustomFhirPathFunctions : FhirPathFunctions { else -> null } - } /** * Execute the function on a [focus] resource for a given [functionName] and [parameters]. @@ -202,7 +198,8 @@ class CustomFhirPathFunctions : FhirPathFunctions { metadata: Metadata = Metadata.getInstance(), ): MutableList { val type = GeoData.DataTypes.valueOf(parameters!!.first().first().primitiveValue()) - if (type == GeoData.DataTypes.CITY || type == GeoData.DataTypes.COUNTY || + if (type == GeoData.DataTypes.CITY || + type == GeoData.DataTypes.COUNTY || type == GeoData.DataTypes.POSTAL_CODE ) { if (parameters.size != 2) { diff --git a/prime-router/src/main/kotlin/fhirengine/engine/CustomTranslationFunctions.kt b/prime-router/src/main/kotlin/fhirengine/engine/CustomTranslationFunctions.kt index 428bae601ec..f161d04f6e9 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/CustomTranslationFunctions.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/CustomTranslationFunctions.kt @@ -99,8 +99,7 @@ class CustomTranslationFunctions( hl7FieldPath: String, terser: Terser, appContext: CustomContext?, - ): String { - return if (appContext?.config is HL7TranslationConfig) { + ): String = if (appContext?.config is HL7TranslationConfig) { val config = appContext.config val truncationConfig = config.truncationConfig @@ -128,5 +127,4 @@ class CustomTranslationFunctions( } else { super.maybeTruncateHL7Field(value, hl7FieldPath, terser, appContext) } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/engine/FHIRDestinationFilter.kt b/prime-router/src/main/kotlin/fhirengine/engine/FHIRDestinationFilter.kt index 5be29b4626e..9de1bca597f 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/FHIRDestinationFilter.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/FHIRDestinationFilter.kt @@ -71,8 +71,7 @@ class FHIRDestinationFilter( message: T, actionLogger: ActionLogger, actionHistory: ActionHistory, - ): List { - return when (message) { + ): List = when (message) { is FhirDestinationFilterQueueMessage -> { check(message.topic.isUniversalPipeline) { "Unexpected topic $message.topic in the Universal Pipeline routing step." @@ -86,7 +85,6 @@ class FHIRDestinationFilter( ) } } - } /** * Process a [queueMessage] off of the raw-elr azure queue, convert it into FHIR, and store for next step. @@ -140,7 +138,7 @@ class FHIRDestinationFilter( metadata = this.metadata, topic = queueMessage.topic, destination = receiver, - nextAction = TaskAction.receiver_filter + nextAction = TaskAction.receiver_enrichment ) // create item lineage @@ -159,7 +157,7 @@ class FHIRDestinationFilter( ) val nextEvent = ProcessEvent( - Event.EventAction.RECEIVER_FILTER, + Event.EventAction.RECEIVER_ENRICHMENT, report.id, Options.None, emptyMap(), @@ -209,7 +207,7 @@ class FHIRDestinationFilter( nextEvent, report, blobInfo.blobUrl, - FhirReceiverFilterQueueMessage( + FhirReceiverEnrichmentQueueMessage( report.id, blobInfo.blobUrl, BlobUtils.digestToString(blobInfo.digest), diff --git a/prime-router/src/main/kotlin/fhirengine/engine/FHIREngine.kt b/prime-router/src/main/kotlin/fhirengine/engine/FHIREngine.kt index 04cb5a563f1..3128b31b12b 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/FHIREngine.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/FHIREngine.kt @@ -151,6 +151,19 @@ abstract class FHIREngine( reportService ?: ReportService() ) ) + TaskAction.receiver_enrichment -> FHIRReceiverEnrichment( + metadata ?: Metadata.getInstance(), + settingsProvider!!, + databaseAccess ?: databaseAccessSingleton, + blobAccess ?: BlobAccess(), + azureEventService ?: AzureEventServiceImpl(), + reportService ?: ReportService(), + ReportStreamEventService( + databaseAccess ?: databaseAccessSingleton, + azureEventService ?: AzureEventServiceImpl(), + reportService ?: ReportService() + ) + ) TaskAction.receiver_filter -> FHIRReceiverFilter( metadata ?: Metadata.getInstance(), settingsProvider!!, diff --git a/prime-router/src/main/kotlin/fhirengine/engine/FHIRReceiverEnrichment.kt b/prime-router/src/main/kotlin/fhirengine/engine/FHIRReceiverEnrichment.kt new file mode 100644 index 00000000000..df90422dd25 --- /dev/null +++ b/prime-router/src/main/kotlin/fhirengine/engine/FHIRReceiverEnrichment.kt @@ -0,0 +1,208 @@ +package gov.cdc.prime.router.fhirengine.engine + +import fhirengine.engine.CustomFhirPathFunctions +import gov.cdc.prime.reportstream.shared.BlobUtils +import gov.cdc.prime.reportstream.shared.QueueMessage +import gov.cdc.prime.router.ActionLogger +import gov.cdc.prime.router.Metadata +import gov.cdc.prime.router.MimeFormat +import gov.cdc.prime.router.Options +import gov.cdc.prime.router.Receiver +import gov.cdc.prime.router.Report +import gov.cdc.prime.router.SettingsProvider +import gov.cdc.prime.router.azure.ActionHistory +import gov.cdc.prime.router.azure.BlobAccess +import gov.cdc.prime.router.azure.DatabaseAccess +import gov.cdc.prime.router.azure.Event +import gov.cdc.prime.router.azure.ProcessEvent +import gov.cdc.prime.router.azure.db.Tables +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.observability.bundleDigest.BundleDigestExtractor +import gov.cdc.prime.router.azure.observability.bundleDigest.FhirPathBundleDigestLabResultExtractorStrategy +import gov.cdc.prime.router.azure.observability.context.MDCUtils +import gov.cdc.prime.router.azure.observability.context.withLoggingContext +import gov.cdc.prime.router.azure.observability.event.AzureEventService +import gov.cdc.prime.router.azure.observability.event.AzureEventServiceImpl +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.fhirengine.translation.hl7.FhirTransformer +import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext +import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder +import gov.cdc.prime.router.logging.LogMeasuredTime +import gov.cdc.prime.router.report.ReportService +import org.jooq.Field +import java.time.OffsetDateTime + +class FHIRReceiverEnrichment( + metadata: Metadata = Metadata.getInstance(), + settings: SettingsProvider = this.settingsProviderSingleton, + db: DatabaseAccess = this.databaseAccessSingleton, + blob: BlobAccess = BlobAccess(), + azureEventService: AzureEventService = AzureEventServiceImpl(), + reportService: ReportService = ReportService(), + reportStreamEventService: IReportStreamEventService, +) : FHIREngine(metadata, settings, db, blob, azureEventService, reportService, reportStreamEventService) { + + /** + * Accepts a [FhirReceiverEnrichmentQueueMessage] and sends a report to the + * next pipeline step containing enrichments configured per the receiver's settings. + * [actionHistory] and [actionLogger] ensure all activities are recorded to the database and logged. + */ + override fun doWork( + message: T, + actionLogger: ActionLogger, + actionHistory: ActionHistory, + ): List { + when (message) { + is FhirReceiverEnrichmentQueueMessage -> { + val contextMap = mapOf( + MDCUtils.MDCProperty.ACTION_NAME to actionHistory.action.actionName.name, + MDCUtils.MDCProperty.REPORT_ID to message.reportId, + MDCUtils.MDCProperty.TOPIC to message.topic, + MDCUtils.MDCProperty.BLOB_URL to message.blobURL + ) + withLoggingContext(contextMap) { + logger.trace("Starting FHIR ReceiverEnrichment work") + actionHistory.trackExistingInputReport(message.reportId) + val receiver = settings.findReceiver(message.receiverFullName) + ?: throw RuntimeException("Receiver with name ${message.receiverFullName} was not found") + actionHistory.trackActionReceiverInfo(receiver.organizationName, receiver.name) + return fhirEngineRunResults(message, receiver, actionHistory) + } + } + else -> { + // Handle the case where casting failed + throw RuntimeException( + "Message was not a FhirReceiverEnrichmentQueueMessage and cannot be " + + "processed by FHIRReceiverEnrichment: $message" + ) + } + } + } + + /** + * Process a [queueMessage] off of the elr-fhir-receiver-enrichment-queue azure queue, add enrichments, and store + * for next step. + * [actionHistory] ensures all activities are logged. + */ + private fun fhirEngineRunResults( + queueMessage: FhirReceiverEnrichmentQueueMessage, + receiver: Receiver, + actionHistory: ActionHistory, + ): List { + // pull fhir document and parse FHIR document + val fhirJson = LogMeasuredTime.measureAndLogDurationWithReturnedValue( + "Downloaded content from queue message" + ) { + BlobAccess.downloadBlob(queueMessage.blobURL, queueMessage.digest) + } + val bundle = FhirTranscoder.decode(fhirJson) + if (receiver.enrichmentSchemaNames.isNotEmpty()) { + receiver.enrichmentSchemaNames.forEach { enrichmentSchemaName -> + logger.info( + "Applying enrichment schema '$enrichmentSchemaName' " + + "to reportId '${queueMessage.reportId}'" + ) + val transformer = FhirTransformer( + enrichmentSchemaName, + ) + transformer.process(bundle) + } + } + val bodyString = FhirTranscoder.encode(bundle) + + val report = Report( + MimeFormat.FHIR, + emptyList(), + 1, + metadata = this.metadata, + topic = queueMessage.topic, + destination = receiver, + nextAction = TaskAction.receiver_enrichment + ) + + // create item lineage + report.itemLineages = listOf( + ItemLineage( + null, + queueMessage.reportId, + 1, + report.id, + 1, + null, + null, + null, + report.getItemHashForRow(1) + ) + ) + + val nextEvent = ProcessEvent( + Event.EventAction.RECEIVER_FILTER, + report.id, + Options.None, + emptyMap(), + emptyList() + ) + + // upload new copy to blobstore + val blobInfo = BlobAccess.uploadBody( + MimeFormat.FHIR, + bodyString.toByteArray(), + report.id.toString(), + queueMessage.blobSubFolderName, + nextEvent.eventAction + ) + report.bodyURL = blobInfo.blobUrl + // ensure tracking is set + actionHistory.trackCreatedReport(nextEvent, report, blobInfo = blobInfo) + + val bundleDigestExtractor = BundleDigestExtractor( + FhirPathBundleDigestLabResultExtractorStrategy( + CustomContext( + bundle, + bundle, + mutableMapOf(), + CustomFhirPathFunctions() + ) + ) + ) + reportEventService.sendItemEvent( + eventName = ReportStreamEventName.ITEM_TRANSFORMED, + childReport = report, + pipelineStepName = TaskAction.receiver_enrichment + ) { + parentReportId(queueMessage.reportId) + params( + mapOf( + ReportStreamEventProperties.RECEIVER_NAME to receiver.fullName, + ReportStreamEventProperties.BUNDLE_DIGEST + to bundleDigestExtractor.generateDigest(bundle), + ReportStreamEventProperties.ENRICHMENTS to receiver.enrichmentSchemaNames + ) + ) + trackingId(bundle) + } + + return listOf( + FHIREngineRunResult( + nextEvent, + report, + blobInfo.blobUrl, + FhirReceiverFilterQueueMessage( + report.id, + blobInfo.blobUrl, + BlobUtils.digestToString(blobInfo.digest), + queueMessage.blobSubFolderName, + queueMessage.topic, + receiver.fullName + ) + ) + ) + } + + override val finishedField: Field = Tables.TASK.RECEIVER_ENRICHED_AT + override val engineType: String = "ReceiverEnrichment" + override val taskAction: TaskAction = TaskAction.receiver_enrichment +} \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/engine/FHIRTranslator.kt b/prime-router/src/main/kotlin/fhirengine/engine/FHIRTranslator.kt index 5364a806886..636a65a565c 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/FHIRTranslator.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/FHIRTranslator.kt @@ -218,15 +218,6 @@ class FHIRTranslator( receiver: Receiver, bundle: Bundle, ): ByteArray { - if (receiver.enrichmentSchemaNames.isNotEmpty()) { - receiver.enrichmentSchemaNames.forEach { enrichmentSchemaName -> - logger.info("Applying enrichment schema $enrichmentSchemaName") - val transformer = FhirTransformer( - enrichmentSchemaName, - ) - transformer.process(bundle) - } - } when (receiver.format) { MimeFormat.FHIR -> { if (receiver.schemaName.isNotEmpty()) { diff --git a/prime-router/src/main/kotlin/fhirengine/engine/LookupTableValueSet.kt b/prime-router/src/main/kotlin/fhirengine/engine/LookupTableValueSet.kt index ae13f1b778a..5a1d173c178 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/LookupTableValueSet.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/LookupTableValueSet.kt @@ -15,18 +15,15 @@ import java.util.SortedMap * * [valueColumn]: name of the lookup table column containing the value pair values */ -data class LookupTableValueSetConfig( - val tableName: String, - val keyColumn: String, - val valueColumn: String, -) +data class LookupTableValueSetConfig(val tableName: String, val keyColumn: String, val valueColumn: String) /** * Implementation of [ValueSetCollection] to allow valueSet to be retrieved from a lookup table. * Provide [LookupTableValueSetConfig] to configure the lookup table source. */ class LookupTableValueSet - (@JsonProperty("lookupTable") private val configData: LookupTableValueSetConfig) : ValueSetCollection { + (@JsonProperty("lookupTable") private val configData: LookupTableValueSetConfig) : + ValueSetCollection { private val mapVal: SortedMap by lazy { val metadata = Metadata.getInstance() val lookupTable = metadata.findLookupTable(name = configData.tableName) @@ -49,16 +46,12 @@ class LookupTableValueSet return@lazy result.toSortedMap() } - override fun toSortedMap(): SortedMap { - return mapVal - } + override fun toSortedMap(): SortedMap = mapVal override fun getMappedValue(keyValue: String): String? { val lowerSet = toSortedMap().mapKeys { it.key.lowercase() } return lowerSet[keyValue.lowercase()] } - override fun isNotEmpty(): Boolean { - return toSortedMap().isNotEmpty() - } + override fun isNotEmpty(): Boolean = toSortedMap().isNotEmpty() } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/engine/MessageType.kt b/prime-router/src/main/kotlin/fhirengine/engine/MessageType.kt index 7251f9d5460..844da44e5be 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/MessageType.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/MessageType.kt @@ -27,14 +27,12 @@ enum class MessageType { * @param type The string representation of the HL7 message type. * @return The corresponding MessageType instance, or null if the type is unsupported. */ - private fun fromString(type: String): MessageType? { - return when (type) { + private fun fromString(type: String): MessageType? = when (type) { "ORU_R01" -> ORU_R01 "ORM_O01" -> ORM_O01 "OML_O21" -> OML_O21 else -> null } - } /** * Validates the type of the given message. diff --git a/prime-router/src/main/kotlin/fhirengine/engine/PrimeRouterQueueMessage.kt b/prime-router/src/main/kotlin/fhirengine/engine/PrimeRouterQueueMessage.kt index f547b24b975..b515cef99d4 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/PrimeRouterQueueMessage.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/PrimeRouterQueueMessage.kt @@ -18,6 +18,7 @@ import java.util.UUID @JsonSubTypes( JsonSubTypes.Type(FhirConvertQueueMessage::class, name = "convert"), JsonSubTypes.Type(FhirDestinationFilterQueueMessage::class, name = "destination-filter"), + JsonSubTypes.Type(FhirReceiverEnrichmentQueueMessage::class, name = "receiver-enrichment"), JsonSubTypes.Type(FhirReceiverFilterQueueMessage::class, name = "receiver-filter"), JsonSubTypes.Type(FhirTranslateQueueMessage::class, name = "translate"), JsonSubTypes.Type(BatchEventQueueMessage::class, name = "batch"), @@ -33,8 +34,8 @@ abstract class PrimeRouterQueueMessage : QueueMessage { } abstract class ReportPipelineMessage : - QueueMessage.ReportInformation, - PrimeRouterQueueMessage() + PrimeRouterQueueMessage(), + QueueMessage.ReportInformation @JsonTypeName("receive") data class FhirConvertSubmissionQueueMessage( @@ -43,7 +44,8 @@ data class FhirConvertSubmissionQueueMessage( override val digest: String, override val blobSubFolderName: String, override val headers: Map = emptyMap(), -) : ReportPipelineMessage(), QueueMessage.ReceiveInformation { +) : ReportPipelineMessage(), + QueueMessage.ReceiveInformation { override val messageQueueName = QueueMessage.Companion.elrSubmissionConvertQueueName } @@ -70,6 +72,18 @@ data class FhirDestinationFilterQueueMessage( override val messageQueueName = QueueMessage.Companion.elrDestinationFilterQueueName } +@JsonTypeName("receiver-enrichment") +data class FhirReceiverEnrichmentQueueMessage( + override val reportId: ReportId, + override val blobURL: String, + override val digest: String, + override val blobSubFolderName: String, + val topic: Topic, + val receiverFullName: String, +) : ReportPipelineMessage() { + override val messageQueueName = QueueMessage.Companion.elrReceiverEnrichmentQueueName +} + @JsonTypeName("receiver-filter") data class FhirReceiverFilterQueueMessage( override val reportId: ReportId, @@ -135,6 +149,7 @@ fun registerPrimeRouterQueueMessageSubtypes() { QueueMessage.ObjectMapperProvider.registerSubtypes( FhirConvertQueueMessage::class.java, FhirDestinationFilterQueueMessage::class.java, + FhirReceiverEnrichmentQueueMessage::class.java, FhirReceiverFilterQueueMessage::class.java, FhirTranslateQueueMessage::class.java, BatchEventQueueMessage::class.java, diff --git a/prime-router/src/main/kotlin/fhirengine/engine/ProcessedItem.kt b/prime-router/src/main/kotlin/fhirengine/engine/ProcessedItem.kt index b52bad6a315..98d91d39c4b 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/ProcessedItem.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/ProcessedItem.kt @@ -17,9 +17,7 @@ interface IProcessedItem { * * @param hl7Message the message to get the tracking ID */ - fun extractTrackingId(hl7Message: Message): String { - return Terser(hl7Message).get("MSH-10") ?: "" - } + fun extractTrackingId(hl7Message: Message): String = Terser(hl7Message).get("MSH-10") ?: "" /** * Extracts a tracking id from an FHIR Bundle (Bundle.identifier) which @@ -28,9 +26,7 @@ interface IProcessedItem { * * @param bundle the message to get the tracking ID */ - fun extractTrackingId(bundle: Bundle): String { - return bundle.identifier?.value ?: "" - } + fun extractTrackingId(bundle: Bundle): String = bundle.identifier?.value ?: "" } val rawItem: String @@ -61,13 +57,11 @@ data class ProcessedFHIRItem( override val validationError: FHIRConverter.InvalidItemActionLogDetail? = null, override val bundle: Bundle? = null, ) : IProcessedItem { - override fun updateParsed(error: FHIRConverter.InvalidItemActionLogDetail): ProcessedFHIRItem { - return this.copy(parseError = error) - } + override fun updateParsed( + error: FHIRConverter.InvalidItemActionLogDetail, + ): ProcessedFHIRItem = this.copy(parseError = error) - override fun updateParsed(parsed: Bundle): ProcessedFHIRItem { - return this.copy(parsedItem = parsed) - } + override fun updateParsed(parsed: Bundle): ProcessedFHIRItem = this.copy(parsedItem = parsed) override fun updateValidation(error: FHIRConverter.InvalidItemActionLogDetail): ProcessedFHIRItem { if (parseError == null && parsedItem != null) { @@ -90,9 +84,7 @@ data class ProcessedFHIRItem( return "" } - override fun getError(): FHIRConverter.InvalidItemActionLogDetail? { - return parseError ?: validationError - } + override fun getError(): FHIRConverter.InvalidItemActionLogDetail? = parseError ?: validationError } data class ProcessedHL7Item( @@ -104,13 +96,11 @@ data class ProcessedHL7Item( val conversionError: FHIRConverter.InvalidItemActionLogDetail? = null, override val bundle: Bundle? = null, ) : IProcessedItem { - override fun updateParsed(error: FHIRConverter.InvalidItemActionLogDetail): ProcessedHL7Item { - return this.copy(parseError = error) - } + override fun updateParsed( + error: FHIRConverter.InvalidItemActionLogDetail, + ): ProcessedHL7Item = this.copy(parseError = error) - override fun updateParsed(parsed: Message): ProcessedHL7Item { - return this.copy(parsedItem = parsed) - } + override fun updateParsed(parsed: Message): ProcessedHL7Item = this.copy(parsedItem = parsed) override fun updateValidation(error: FHIRConverter.InvalidItemActionLogDetail): ProcessedHL7Item { if (parseError == null && parsedItem != null) { @@ -135,11 +125,9 @@ data class ProcessedHL7Item( return "" } - fun setConversionError(error: FHIRConverter.InvalidItemActionLogDetail): ProcessedHL7Item { - return this.copy(conversionError = error) - } + fun setConversionError(error: FHIRConverter.InvalidItemActionLogDetail): ProcessedHL7Item = + this.copy(conversionError = error) - override fun getError(): FHIRConverter.InvalidItemActionLogDetail? { - return parseError ?: validationError ?: conversionError - } + override fun getError(): FHIRConverter.InvalidItemActionLogDetail? = + parseError ?: validationError ?: conversionError } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/HL7toFhirTranslator.kt b/prime-router/src/main/kotlin/fhirengine/translation/HL7toFhirTranslator.kt index 77e283bea2e..0fdfceea428 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/HL7toFhirTranslator.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/HL7toFhirTranslator.kt @@ -33,11 +33,10 @@ class HL7toFhirTranslator( private val hl7ToFhirTranslatorInstances = Collections.synchronizedMap(mutableMapOf()) - fun getHL7ToFhirTranslatorInstance(configFolderPath: String = "./metadata/HL7/catchall"): HL7toFhirTranslator { - return hl7ToFhirTranslatorInstances.getOrPut(configFolderPath) { + fun getHL7ToFhirTranslatorInstance(configFolderPath: String = "./metadata/HL7/catchall"): HL7toFhirTranslator = + hl7ToFhirTranslatorInstances.getOrPut(configFolderPath) { HL7toFhirTranslator(configFolderPath) } - } } /** diff --git a/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt b/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt index b5c97da7463..aa5eef4653f 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt @@ -68,11 +68,7 @@ class TranslationSchemaManager : Logging { } } - data class ValidationResult( - val path: String, - val passes: Boolean, - val didError: Boolean = false, - ) + data class ValidationResult(val path: String, val passes: Boolean, val didError: Boolean = false) data class ValidationContainer(val input: String, val output: String, val schemaUri: String) @@ -82,7 +78,7 @@ class TranslationSchemaManager : Logging { class TranslationSyncException(override val message: String, override val cause: Throwable? = null) : RuntimeException(cause) - class TranslationStateUninitalized() : RuntimeException() { + class TranslationStateUninitalized : RuntimeException() { override val message = "The azure account and container have not been initialized" } diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/ConfigSchemaProcessor.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/ConfigSchemaProcessor.kt index 1d744d91adf..e7da90cff26 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/ConfigSchemaProcessor.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/ConfigSchemaProcessor.kt @@ -18,8 +18,7 @@ abstract class ConfigSchemaProcessor< val schema: Schema, val errors: MutableList, val warnings: MutableList, -) : - Logging { +) : Logging { /** * Validates the schema the processor will use is valid given a sample input and output @@ -119,8 +118,7 @@ abstract class ConfigSchemaProcessor< focusResource: Base, schemaResource: Base, context: CustomContext, - ): Boolean { - return element.condition?.let { + ): Boolean = element.condition?.let { try { FhirPathUtils.evaluateCondition(context, focusResource, schemaResource, bundle, it) } catch (e: SchemaException) { @@ -131,5 +129,4 @@ abstract class ConfigSchemaProcessor< false } } ?: true - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirToHl7Converter.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirToHl7Converter.kt index 8ddb085e4c5..64b6438a2b4 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirToHl7Converter.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirToHl7Converter.kt @@ -112,9 +112,8 @@ Logging { return message } - override fun checkForEquality(converted: Message, expectedOutput: Message): Boolean { - return converted.encodePreserveEncodingChars() == expectedOutput.encodePreserveEncodingChars() - } + override fun checkForEquality(converted: Message, expectedOutput: Message): Boolean = + converted.encodePreserveEncodingChars() == expectedOutput.encodePreserveEncodingChars() /** * Get the first valid string from the list of values specified in the schema for a given [element] using diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirTransformer.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirTransformer.kt index 5d07fbf2e04..b6a68432d62 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirTransformer.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/FhirTransformer.kt @@ -62,15 +62,10 @@ class FhirTransformer( return input } - class BundleWithMessages( - var bundle: Bundle, - val warnings: MutableList, - val errors: MutableList, - ) + class BundleWithMessages(var bundle: Bundle, val warnings: MutableList, val errors: MutableList) - override fun checkForEquality(converted: Bundle, expectedOutput: Bundle): Boolean { - return converted.equalsDeep(expectedOutput) - } + override fun checkForEquality(converted: Bundle, expectedOutput: Bundle): Boolean = + converted.equalsDeep(expectedOutput) /** * Transform the [bundle] using the elements in the given [schema] using [context] starting at the @@ -259,13 +254,9 @@ class FhirTransformer( val extensionUrl: String?, val index: Int?, ) { - fun isExtension(): Boolean { - return propertyString == "extension" - } + fun isExtension(): Boolean = propertyString == "extension" - fun isValue(): Boolean { - return propertyString == "value" - } + fun isValue(): Boolean = propertyString == "value" } /** diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/HL7Truncator.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/HL7Truncator.kt index 6debe3e6ff3..c547dfade84 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/HL7Truncator.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/HL7Truncator.kt @@ -82,15 +82,13 @@ sealed interface HL7Truncator { * @param truncationLimit the starting limit * @return the new truncation limit or starting limit if no special characters are found */ - fun getTruncationLimitWithEncoding(value: String, truncationLimit: Int?): Int? { - return truncationLimit?.let { limit -> + fun getTruncationLimitWithEncoding(value: String, truncationLimit: Int?): Int? = truncationLimit?.let { limit -> val regex = "[&^~|]".toRegex() val endIndex = min(value.length, limit) val matchCount = regex.findAll(value.substring(0, endIndex)).count() limit - (matchCount * 2) } - } /** * Attempts to lookup a subcomponents max length on our internal table @@ -128,24 +126,18 @@ sealed interface HL7Truncator { segment: Segment, field: Type, parts: HL7FieldComponents, - ): Int? { - return if (parts.third != null) { + ): Int? = if (parts.third != null) { getMaxLengthForCompositeType(field, parts.third) } else if (parts.second != null) { getMaxLengthForCompositeType(field, parts.second) } else { segment.getLength(parts.first) } - } /** * Container for an HL7 field's components */ - data class HL7FieldComponents( - val first: Int, - val second: Int?, - val third: Int?, - ) { + data class HL7FieldComponents(val first: Int, val second: Int?, val third: Int?) { companion object { /** * This will blow up if a malformed string is passed diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/ValueSetCollection.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/ValueSetCollection.kt index a6b636bf138..39ca6854daf 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/ValueSetCollection.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/ValueSetCollection.kt @@ -36,16 +36,12 @@ interface ValueSetCollection { class InlineValueSet (@JsonProperty("values") private val values: SortedMap) : ValueSetCollection { - override fun toSortedMap(): SortedMap { - return values - } + override fun toSortedMap(): SortedMap = values override fun getMappedValue(keyValue: String): String? { val lowerSet = values.mapKeys { it.key.lowercase() } return lowerSet[keyValue.lowercase()] } - override fun isNotEmpty(): Boolean { - return values.isNotEmpty() - } + override fun isNotEmpty(): Boolean = values.isNotEmpty() } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/ConfigSchema.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/ConfigSchema.kt index 16c64228b63..9661eaa9b34 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/ConfigSchema.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/ConfigSchema.kt @@ -184,9 +184,7 @@ abstract class ConfigSchemaElement< ) { private var validationErrors: MutableSet = mutableSetOf() - override fun toString(): String { - return "$name" - } + override fun toString(): String = "$name" /** * Add an error [msg] to the list of errors. @@ -226,7 +224,8 @@ abstract class ConfigSchemaElement< // value set keys and values cannot be null if (valueSet?.toSortedMap()?.keys?.any { it == null - } == true || valueSet?.toSortedMap()?.values?.any { it == null } == true + } == true || + valueSet?.toSortedMap()?.values?.any { it == null } == true ) { addError("Value sets cannot contain null values") } diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/ConverterSchemaReader.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/ConverterSchemaReader.kt index 15976198e45..53438c8046b 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/ConverterSchemaReader.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/ConverterSchemaReader.kt @@ -11,10 +11,8 @@ import gov.cdc.prime.router.fhirengine.translation.hl7.schema.ConfigSchemaReader fun converterSchemaFromFile( schemaName: String, blobConnectionInfo: BlobAccess.BlobContainerMetadata, -): HL7ConverterSchema { - return ConfigSchemaReader.fromFile( +): HL7ConverterSchema = ConfigSchemaReader.fromFile( schemaName, schemaClass = HL7ConverterSchema::class.java, blobConnectionInfo - ) -} \ No newline at end of file + ) \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchema.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchema.kt index bc84c547e54..97e6ab68629 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchema.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchema.kt @@ -29,9 +29,7 @@ class HL7ConverterSchema( extends = extends ) { - override fun toString(): String { - return "${if (extends != null) "$extends->" else ""}$name" - } + override fun toString(): String = "${if (extends != null) "$extends->" else ""}$name" override fun validate(isChildSchema: Boolean): List { if (isChildSchema) { diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaReader.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaReader.kt index c9cfa961978..adb43c903da 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaReader.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaReader.kt @@ -11,10 +11,8 @@ import gov.cdc.prime.router.fhirengine.translation.hl7.schema.ConfigSchemaReader fun fhirTransformSchemaFromFile( schemaName: String, blobConnectionInfo: BlobAccess.BlobContainerMetadata, -): FhirTransformSchema { - return ConfigSchemaReader.fromFile( +): FhirTransformSchema = ConfigSchemaReader.fromFile( schemaName, schemaClass = FhirTransformSchema::class.java, blobConnectionInfo - ) -} \ No newline at end of file + ) \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/ConstantResolver.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/ConstantResolver.kt index d15800abd95..d4b593f794f 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/ConstantResolver.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/ConstantResolver.kt @@ -55,16 +55,16 @@ data class CustomContext( * Add [constants] to a context. * @return a new context with the [constants] added or the existing context if no new constants are specified */ - fun addConstants(constants: Map, previousContext: CustomContext): CustomContext { - return addConstants(constants, previousContext, true) - } + fun addConstants( + constants: Map, + previousContext: CustomContext, + ): CustomContext = addConstants(constants, previousContext, true) private fun addConstants( constants: Map, previousContext: CustomContext, checkReservedNames: Boolean, - ): CustomContext { - return if (constants.isEmpty()) { + ): CustomContext = if (constants.isEmpty()) { previousContext } else { if (checkReservedNames && constants.keys.any { reservedConstantNames.contains(it) }) { @@ -85,23 +85,25 @@ data class CustomContext( constants.forEach { newContext.constants[it.key] = it.value } newContext } - } /** * Add constant with [key] and [value] to a context. * @return a new context with the constant added or the existing context of no new constant is specified */ - fun addConstant(key: String, value: String, previousContext: CustomContext): CustomContext { - return addConstants(mapOf(key to value), previousContext, true) - } + fun addConstant( + key: String, + value: String, + previousContext: CustomContext, + ): CustomContext = addConstants(mapOf(key to value), previousContext, true) - fun setAppendToIndex(index: Int, previousContext: CustomContext): CustomContext { - return addConstants(mapOf(appendToIndexKey to index.toString()), previousContext, false) - } + fun setAppendToIndex(index: Int, previousContext: CustomContext): CustomContext = addConstants( + mapOf( + appendToIndexKey to index.toString() + ), + previousContext, false + ) - fun getAppendToIndex(context: CustomContext): Int? { - return context.constants[appendToIndexKey]?.toInt() - } + fun getAppendToIndex(context: CustomContext): Int? = context.constants[appendToIndexKey]?.toInt() } } @@ -118,14 +120,17 @@ class ConstantSubstitutor { * Replace the constants in a given [inputText] using the [context]. * @return the resolved string */ - fun replace(inputText: String, context: CustomContext?): String { - return constantResolver.setVariableResolver(StringCustomResolver(context)).replace(inputText) - } + fun replace( + inputText: String, + context: CustomContext?, + ): String = constantResolver.setVariableResolver(StringCustomResolver(context)).replace(inputText) /** * Custom resolver for the [ConstantSubstitutor] that uses the [context] to resolve the constants. */ - internal class StringCustomResolver(val context: CustomContext?) : StringLookup, Logging { + internal class StringCustomResolver(val context: CustomContext?) : + StringLookup, + Logging { override fun lookup(key: String?): String { require(!key.isNullOrBlank()) when { @@ -144,7 +149,8 @@ class ConstantSubstitutor { * Custom resolver for the FHIR path engine. */ class FhirPathCustomResolver(private val customFhirFunctions: FhirPathFunctions? = null) : - FHIRPathEngine.IEvaluationContext, Logging { + FHIRPathEngine.IEvaluationContext, + Logging { override fun resolveConstant( engine: FHIRPathEngine?, appContext: Any?, @@ -221,20 +227,15 @@ class FhirPathCustomResolver(private val customFhirFunctions: FhirPathFunctions? appContext: Any?, name: String?, explicitConstant: Boolean, - ): TypeDetails { - throw NotImplementedError("Not implemented") - } + ): TypeDetails = throw NotImplementedError("Not implemented") - override fun log(argument: String?, focus: MutableList?): Boolean { + override fun log(argument: String?, focus: MutableList?): Boolean = throw NotImplementedError("Not implemented") - } override fun resolveFunction( engine: FHIRPathEngine?, functionName: String?, - ): FHIRPathUtilityClasses.FunctionDetails? { - return CustomFHIRFunctions.resolveFunction(functionName, customFhirFunctions) - } + ): FHIRPathUtilityClasses.FunctionDetails? = CustomFHIRFunctions.resolveFunction(functionName, customFhirFunctions) override fun checkFunction( engine: FHIRPathEngine?, @@ -242,9 +243,7 @@ class FhirPathCustomResolver(private val customFhirFunctions: FhirPathFunctions? functionName: String?, focus: TypeDetails?, parameters: MutableList?, - ): TypeDetails { - throw NotImplementedError("Not implemented") - } + ): TypeDetails = throw NotImplementedError("Not implemented") override fun executeFunction( engine: FHIRPathEngine?, @@ -273,11 +272,9 @@ class FhirPathCustomResolver(private val customFhirFunctions: FhirPathFunctions? } } - override fun conformsToProfile(engine: FHIRPathEngine?, appContext: Any?, item: Base?, url: String?): Boolean { + override fun conformsToProfile(engine: FHIRPathEngine?, appContext: Any?, item: Base?, url: String?): Boolean = throw NotImplementedError("Not implemented") - } - override fun resolveValueSet(engine: FHIRPathEngine?, appContext: Any?, url: String?): ValueSet { + override fun resolveValueSet(engine: FHIRPathEngine?, appContext: Any?, url: String?): ValueSet = throw NotImplementedError("Not implemented") - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/CustomFHIRFunctions.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/CustomFHIRFunctions.kt index 3ee64310671..fc40b66b162 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/CustomFHIRFunctions.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/CustomFHIRFunctions.kt @@ -49,13 +49,11 @@ object CustomFHIRFunctions : FhirPathFunctions { * Get from a [functionName]. * @return the function name enum or null if not found */ - fun get(functionName: String?): CustomFHIRFunctionNames? { - return try { + fun get(functionName: String?): CustomFHIRFunctionNames? = try { functionName?.let { CustomFHIRFunctionNames.valueOf(it.replaceFirstChar(Char::titlecase)) } } catch (e: IllegalArgumentException) { null } - } } } @@ -67,8 +65,7 @@ object CustomFHIRFunctions : FhirPathFunctions { override fun resolveFunction( functionName: String?, additionalFunctions: FhirPathFunctions?, - ): FunctionDetails? { - return when (CustomFHIRFunctionNames.get(functionName)) { + ): FunctionDetails? = when (CustomFHIRFunctionNames.get(functionName)) { CustomFHIRFunctionNames.GetPhoneNumberCountryCode -> { FunctionDetails("extract country code from FHIR phone number", 0, 0) } @@ -141,7 +138,6 @@ object CustomFHIRFunctions : FhirPathFunctions { else -> additionalFunctions?.resolveFunction(functionName) } - } /** * Execute the function on a [focus] resource for a given [functionName] and [parameters]. [additionalFunctions] can @@ -268,8 +264,10 @@ object CustomFHIRFunctions : FhirPathFunctions { * Splits the [focus] into multiple strings using the delimiter provided in [parameters] * @returns list of strings */ - fun split(focus: MutableList, parameters: MutableList>?): MutableList { - return if (!parameters.isNullOrEmpty() && + fun split( + focus: MutableList, + parameters: MutableList>?, + ): MutableList = if (!parameters.isNullOrEmpty() && parameters.size == 1 && parameters.first().size == 1 && focus.size == 1 && @@ -282,7 +280,6 @@ object CustomFHIRFunctions : FhirPathFunctions { } else { mutableListOf() } - } /** * Enum representing the CodingSystemMapping. @@ -309,11 +306,9 @@ object CustomFHIRFunctions : FhirPathFunctions { * Get a coding system mapper by its [fhirURL] * @return an enum instance representing the appropriate mapping */ - fun getByFhirUrl(fhirURL: String): CodingSystemMapper { - return CodingSystemMapper.values().find { + fun getByFhirUrl(fhirURL: String): CodingSystemMapper = CodingSystemMapper.values().find { it.fhirURL == fhirURL } ?: NONE - } } } @@ -322,9 +317,9 @@ object CustomFHIRFunctions : FhirPathFunctions { * HL7 v2.5.1 - 0396 - Coding system. * @return a mutable list containing the single character HL7 result status */ - private fun getCodingSystemMapping(focus: MutableList): MutableList { - return mutableListOf(StringType(CodingSystemMapper.getByFhirUrl(focus[0].primitiveValue()).hl7ID)) - } + private fun getCodingSystemMapping( + focus: MutableList, + ): MutableList = mutableListOf(StringType(CodingSystemMapper.getByFhirUrl(focus[0].primitiveValue()).hl7ID)) /** * Regex to identify OIDs. Source: https://www.hl7.org/fhir/datatypes.html#oid @@ -430,15 +425,13 @@ object CustomFHIRFunctions : FhirPathFunctions { return if (type != null) mutableListOf(StringType(type)) else mutableListOf() } - fun getPrimitiveValue(focus: MutableList): MutableList { - return focus.map { + fun getPrimitiveValue(focus: MutableList): MutableList = focus.map { if (it.isPrimitive) { it.copy() } else { it } }.toMutableList() - } /** * Applies a timezone given by [parameters] to a dateTime in [focus] and returns the result. diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/FhirPathUtils.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/FhirPathUtils.kt index b1689acd6ea..1e72824781c 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/FhirPathUtils.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/FhirPathUtils.kt @@ -61,22 +61,21 @@ object FhirPathUtils : Logging { * @return the validated FHIR path * @throws FHIRLexerException if the path is invalid */ - fun parsePath(fhirPath: String?): ExpressionNode? { - return if (fhirPath.isNullOrBlank()) { + fun parsePath(fhirPath: String?): ExpressionNode? = if (fhirPath.isNullOrBlank()) { null } else { pathEngine.parse(fhirPath) } - } /** * Is the provided path a valid FHIR path given the evaluation context? */ - fun validatePath(path: String, evaluationContext: FHIRPathEngine.IEvaluationContext): Boolean { - return withEvaluationContext(evaluationContext) { + fun validatePath( + path: String, + evaluationContext: FHIRPathEngine.IEvaluationContext, + ): Boolean = withEvaluationContext(evaluationContext) { runCatching { parsePath(path) }.isSuccess } - } /** * Gets a FHIR base resource from the given [expression] using [bundle] and starting from a specific [focusResource]. diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtils.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtils.kt index 57f6c632396..7b3be509fe2 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtils.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtils.kt @@ -13,9 +13,7 @@ import java.util.UUID /** * Helper class to generate HL7 ACK response */ -class HL7ACKUtils( - private val clock: Clock = Clock.systemUTC(), -) { +class HL7ACKUtils(private val clock: Clock = Clock.systemUTC()) { /** * Creates the output ACK message according to the spec defined in #16394 diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Constants.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Constants.kt index f59545bc846..23c9eaf2ffb 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Constants.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Constants.kt @@ -99,7 +99,5 @@ object HL7Constants { // Extend further here ) - fun getHL7ComponentMaxLengthList(componentName: String): List? { - return hl7ComponentMaxLength[componentName] - } + fun getHL7ComponentMaxLengthList(componentName: String): List? = hl7ComponentMaxLength[componentName] } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Utils.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Utils.kt index bc84b503185..433431464da 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Utils.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/HL7Utils.kt @@ -28,8 +28,7 @@ object HL7Utils : Logging { * Gets a new object for the given [hl7Class]. * @return a message object */ - internal fun getMessage(hl7Class: String): Message { - return try { + internal fun getMessage(hl7Class: String): Message = try { val messageClass = Class.forName(hl7Class) if (AbstractMessage::class.java.isAssignableFrom(messageClass)) { // We verify above that we have a valid subclass of Message as required for parsing @@ -46,7 +45,6 @@ object HL7Utils : Logging { } catch (e: Exception) { throw IllegalArgumentException("$hl7Class is not a class to use for the conversion.") } - } /** * Gets the type string for the given [message]. @@ -65,14 +63,12 @@ object HL7Utils : Logging { * Checks if a specific HL7 message [hl7Class] is supported. * @return true if the HL7 message is supported, false otherwise */ - fun supports(hl7Class: String): Boolean { - return try { + fun supports(hl7Class: String): Boolean = try { getMessage(hl7Class) true } catch (e: java.lang.IllegalArgumentException) { false } - } /** * Get an instance of a message. diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/TranslationFunctions.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/TranslationFunctions.kt index 6fffddb0522..0a447c8b683 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/TranslationFunctions.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/TranslationFunctions.kt @@ -101,7 +101,5 @@ open class Hl7TranslationFunctions : TranslationFunctions { hl7FieldPath: String, terser: Terser, appContext: CustomContext?, - ): String { - return value - } + ): String = value } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/helpers/ConvertDateToAge.kt b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/helpers/ConvertDateToAge.kt index 71aff3e3461..1182e8c25f4 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/helpers/ConvertDateToAge.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/hl7/utils/helpers/ConvertDateToAge.kt @@ -84,8 +84,7 @@ internal fun calculateAgeWithSpecifiedTimeUnit( dateOfBirth: LocalDate, referenceDate: LocalDate, ageUnit: TemporalPrecisionEnum?, -): Age { - return when (ageUnit) { +): Age = when (ageUnit) { TemporalPrecisionEnum.DAY -> { createAge(ageUnit, BigDecimal(ChronoUnit.DAYS.between(dateOfBirth, referenceDate))) } @@ -97,7 +96,6 @@ internal fun calculateAgeWithSpecifiedTimeUnit( } else -> throw SchemaException("Age unit must be one of: year, month, day") } -} /** * This method calculates the time passed from the [referenceDate] to the [dateOfBirth] while assuming what unit the diff --git a/prime-router/src/main/kotlin/fhirengine/utils/CompareFhirData.kt b/prime-router/src/main/kotlin/fhirengine/utils/CompareFhirData.kt index de0d80104af..08f3975d949 100644 --- a/prime-router/src/main/kotlin/fhirengine/utils/CompareFhirData.kt +++ b/prime-router/src/main/kotlin/fhirengine/utils/CompareFhirData.kt @@ -320,8 +320,7 @@ class CompareFhirData( * Filter the properties of a given [resource], so only the properties we want to compare are listed. * @return the list of properties to compare. */ - internal fun filterResourceProperties(resource: Base): List { - return resource.children().filter { + internal fun filterResourceProperties(resource: Base): List = resource.children().filter { // Skip any properties to be ignored val isSkipped = skippedProperties.contains("${resource.fhirType()}.${it.name}") // Skip any resource IDs @@ -330,7 +329,6 @@ class CompareFhirData( val isBundleEntry = (resource.hasType("Bundle") && it.name == "entry") !(isSkipped || isResourceId || isBundleEntry) } - } /** * There can be many reasons that the expected and actual child values differ. @@ -395,8 +393,7 @@ class CompareFhirData( * is NOT the same as a FHIR path, and we use it to log the resources we are comparing and to match types we * want to ignore. E.g. Bundle.meta.lastUpdated, Organization.name. */ - internal fun getFhirIdPath(parentIdPath: String, resource: Base): String { - return when { + internal fun getFhirIdPath(parentIdPath: String, resource: Base): String = when { resource is Extension -> "$parentIdPath->${resource.fhirType()}(${resource.url.substringAfterLast("/")})" @@ -410,16 +407,13 @@ class CompareFhirData( else -> parentIdPath } - } /** * comma delimited list of IDs */ - internal fun generateResourcesId(resources: List): String? { - return resources + internal fun generateResourcesId(resources: List): String? = resources .mapNotNull { it.idBase } .joinToString() .ifEmpty { null } - } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt b/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt index a5accd62201..54732fb2da5 100644 --- a/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt +++ b/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt @@ -61,12 +61,10 @@ fun Observation.getCodeSourcesMap(): Map> { /** * Gets mapped condition extensions present on an [Observation] */ -fun Observation.getMappedConditionExtensions(): List { - return this.getCodeSourcesMap() +fun Observation.getMappedConditionExtensions(): List = this.getCodeSourcesMap() .flatMap { it.value } .flatMap { it.extension } .filter { it.url == CONDITION_CODE_EXTENSION_URL } -} /** * Gets mapped conditions present on an [Observation] @@ -77,9 +75,7 @@ fun Observation.getMappedConditions(): List = /** * Gets mapped condition codes present on an [Observation] */ -fun Observation.getMappedConditionCodes(): List { - return this.getMappedConditions().map { it.code } -} +fun Observation.getMappedConditionCodes(): List = this.getMappedConditions().map { it.code } fun Bundle.getObservations() = this.entry.map { it.resource }.filterIsInstance() @@ -141,12 +137,10 @@ fun Bundle.isElr(): Boolean { * * @return RSMessageType of this Bundle. */ -fun Bundle.getRSMessageType(): RSMessageType { - return when { +fun Bundle.getRSMessageType(): RSMessageType = when { isElr() -> RSMessageType.LAB_RESULT else -> RSMessageType.UNKNOWN } -} /** * Gets all properties for a [Base] resource recursively and filters only its references @@ -154,32 +148,29 @@ fun Bundle.getRSMessageType(): RSMessageType { * @return a list of reference identifiers for a [Base] resource * */ -fun Base.getResourceReferences(): List { - return FHIRBundleHelpers.filterReferenceProperties(this.getResourceProperties()) -} +fun Base.getResourceReferences(): List = + FHIRBundleHelpers.filterReferenceProperties(this.getResourceProperties()) /** * Gets all properties for a [Base] resource recursively * * @return a list of all [Property] for a [Base] resource */ -fun Base.getResourceProperties(): List { - return this.children().stream().flatMap { getChildProperties(it) }.collect(Collectors.toList()) -} +fun Base.getResourceProperties(): List = this.children().stream().flatMap { + getChildProperties(it) +}.collect(Collectors.toList()) /** * Gets all diagnostic report that have no observations from a [bundle] * * @return a list of [Base] diagnostic reports that have no observations */ -fun Bundle.getDiagnosticReportNoObservations(): List { - return FhirPathUtils.evaluate( +fun Bundle.getDiagnosticReportNoObservations(): List = FhirPathUtils.evaluate( null, this, this, "Bundle.entry.resource.ofType(DiagnosticReport).where(result.empty())" ) -} /** * Deletes a [resource] from a bundle, removes all references to the [resource] and any orphaned children. @@ -490,24 +481,20 @@ class FHIRBundleHelpers { * * @return a list containing only the references in [properties] */ - fun filterReferenceProperties(properties: List): List { - return properties + fun filterReferenceProperties(properties: List): List = properties .filter { it.hasValues() } .flatMap { it.values } .filterIsInstance() .map { it.reference } - } /** * Gets all child properties for a resource [property] recursively * * @return a flatmap stream of all child properties on a [property] */ - fun getChildProperties(property: Property): Stream { - return Stream.concat( + fun getChildProperties(property: Property): Stream = Stream.concat( Stream.of(property), property.values.flatMap { it.children() }.stream().flatMap { getChildProperties(it) } ) - } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/utils/FhirTranscoder.kt b/prime-router/src/main/kotlin/fhirengine/utils/FhirTranscoder.kt index b3a6faf06ec..bb8a2852bfe 100644 --- a/prime-router/src/main/kotlin/fhirengine/utils/FhirTranscoder.kt +++ b/prime-router/src/main/kotlin/fhirengine/utils/FhirTranscoder.kt @@ -48,17 +48,19 @@ object FhirTranscoder : Logging { * Encode a FHIR [bundle] to a string using [parser]. * @return a JSON string of the bundle */ - fun encode(bundle: Bundle, parser: IParser = defaultContext.newJsonParser()): String { - return parser.encodeResourceToString(bundle) - } + fun encode( + bundle: Bundle, + parser: IParser = defaultContext.newJsonParser(), + ): String = parser.encodeResourceToString(bundle) /** * Decode a FHIR bundle from a [json] string using [parser]. * @return a FHIR bundle */ - fun decode(json: String, parser: IParser = defaultContext.newJsonParser()): Bundle { - return parser.parseResource(Bundle::class.java, json) - } + fun decode( + json: String, + parser: IParser = defaultContext.newJsonParser(), + ): Bundle = parser.parseResource(Bundle::class.java, json) /** * Converts a FHIR [rawMessage] in ndjson format (Newline Delimited Json) into [Bundle] objects diff --git a/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt b/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt index cbd8b1b4700..b9b9cf355ee 100644 --- a/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt +++ b/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt @@ -104,7 +104,6 @@ object HL7MessageHelpers : Logging { return builder.toString() } - fun messageCount(rawHl7: String): Int { - return Hl7InputStreamMessageStringIterator(rawHl7.byteInputStream()).asSequence().count() - } + fun messageCount(rawHl7: String): Int = + Hl7InputStreamMessageStringIterator(rawHl7.byteInputStream()).asSequence().count() } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/fhirengine/utils/HL7Reader.kt b/prime-router/src/main/kotlin/fhirengine/utils/HL7Reader.kt index 698b41c7697..98d97692654 100644 --- a/prime-router/src/main/kotlin/fhirengine/utils/HL7Reader.kt +++ b/prime-router/src/main/kotlin/fhirengine/utils/HL7Reader.kt @@ -74,8 +74,7 @@ class HL7Reader { */ private fun getHL7ParsingContext( hl7MessageType: HL7MessageType?, - ): HapiContext { - return when (hl7MessageType?.msh93) { + ): HapiContext = when (hl7MessageType?.msh93) { "ORU_R01" -> { DefaultHapiContext( ParserConfiguration(), @@ -101,7 +100,6 @@ class HL7Reader { DefaultHapiContext(ValidationContextFactory.noValidation()) } } - } /** * Parses just the first line of an HL7 string to determine @@ -138,111 +136,93 @@ class HL7Reader { * Get the [message] timestamp from MSH-7. * @return the timestamp or null if not specified */ - fun getMessageTimestamp(message: Message): Date? { - return when (val structure = message[MSH_SEGMENT_NAME]) { + fun getMessageTimestamp(message: Message): Date? = when (val structure = message[MSH_SEGMENT_NAME]) { is NIST_MSH -> structure.msh7_DateTimeOfMessage.ts1_Time.valueAsDate is v27_MSH -> structure.msh7_DateTimeOfMessage.valueAsDate is v251_MSH -> structure.msh7_DateTimeOfMessage.ts1_Time.valueAsDate else -> null } - } /** * Get the type of the [message] * @return the type of message ex. ORU */ - fun getMessageType(message: Message): String { - return when (val structure = message[MSH_SEGMENT_NAME]) { + fun getMessageType(message: Message): String = when (val structure = message[MSH_SEGMENT_NAME]) { is NIST_MSH -> structure.msh9_MessageType.msg1_MessageCode.toString() is v27_MSH -> structure.msh9_MessageType.msg1_MessageCode.toString() is v251_MSH -> structure.msh9_MessageType.msg1_MessageCode.toString() else -> "" } - } /** * Get the birthTime from the [message] * @return the birthTime, if available or blank if not */ - fun getBirthTime(message: Message): String { - return try { + fun getBirthTime(message: Message): String = try { Terser(message).get("${getPatientPath(message)}/PID-7") } catch (e: HL7Exception) { "" } catch (e: NullPointerException) { "" } - } /** * Get the path that is needed to retrieve the patient info, based on the type of the [hl7Message] * @return the path for retrieving patient info */ - fun getPatientPath(hl7Message: Message): String? { - return when (getMessageType(hl7Message)) { + fun getPatientPath(hl7Message: Message): String? = when (getMessageType(hl7Message)) { "ORM" -> "PATIENT" "OML" -> "PATIENT" "ORU" -> "PATIENT_RESULT/PATIENT" else -> null } - } /** * Reads MSH.3 which is the Sending Application field */ - fun getSendingApplication(message: Message): String? { - return when (val structure = message[MSH_SEGMENT_NAME]) { + fun getSendingApplication(message: Message): String? = when (val structure = message[MSH_SEGMENT_NAME]) { is NIST_MSH -> structure.msh3_SendingApplication.encode() is v27_MSH -> structure.msh3_SendingApplication.encode() is v251_MSH -> structure.msh3_SendingApplication.encode() else -> null } - } /** * Reads MSH.4 which is the Sending Facility field */ - fun getSendingFacility(message: Message): String? { - return when (val structure = message[MSH_SEGMENT_NAME]) { + fun getSendingFacility(message: Message): String? = when (val structure = message[MSH_SEGMENT_NAME]) { is NIST_MSH -> structure.msh4_SendingFacility.encode() is v27_MSH -> structure.msh4_SendingFacility.encode() is v251_MSH -> structure.msh4_SendingFacility.encode() else -> null } - } /** * Reads MSH.10 which is the Message Control ID field */ - fun getMessageControlId(message: Message): String? { - return when (val structure = message[MSH_SEGMENT_NAME]) { + fun getMessageControlId(message: Message): String? = when (val structure = message[MSH_SEGMENT_NAME]) { is NIST_MSH -> structure.msh10_MessageControlID.encode() is v27_MSH -> structure.msh10_MessageControlID.encode() is v251_MSH -> structure.msh10_MessageControlID.encode() else -> null } - } /** * Reads MSH.15 which is the Accept Acknowledgment Type field */ - fun getAcceptAcknowledgmentType(message: Message): String? { - return when (val structure = message[MSH_SEGMENT_NAME]) { + fun getAcceptAcknowledgmentType(message: Message): String? = when (val structure = message[MSH_SEGMENT_NAME]) { is NIST_MSH -> structure.msh15_AcceptAcknowledgmentType.encode() is v27_MSH -> structure.msh15_AcceptAcknowledgmentType.encode() is v251_MSH -> structure.msh15_AcceptAcknowledgmentType.encode() else -> null } - } /** * Takes a [rawMessage] and the number of messages [numMessages] in the rawMessage and determines if it is a batch * or singular HL7 message. It will qualify as a batch message if it follows the HL7 standards and have the Hl7 * batch headers which start with "FHS" or if they left off the batch headers and just sent multiple messages */ - fun isBatch(rawMessage: String, numMessages: Int): Boolean { - return rawMessage.startsWith("FHS") || numMessages > 1 - } + fun isBatch(rawMessage: String, numMessages: Int): Boolean = rawMessage.startsWith("FHS") || numMessages > 1 /** * Takes an [exception] thrown by the HL7 HAPI library, gets the root cause and logs the error into [actionLogger]. diff --git a/prime-router/src/main/kotlin/history/DeliveryHistory.kt b/prime-router/src/main/kotlin/history/DeliveryHistory.kt index d90227fc436..225034dafac 100644 --- a/prime-router/src/main/kotlin/history/DeliveryHistory.kt +++ b/prime-router/src/main/kotlin/history/DeliveryHistory.kt @@ -72,8 +72,7 @@ class DeliveryHistory( fun createDeliveryHistoryFromReportAndAction( reportFile: ReportFile, action: Action, - ): DeliveryHistory { - return DeliveryHistory( + ): DeliveryHistory = DeliveryHistory( actionId = action.actionId, createdAt = action.createdAt, receivingOrg = reportFile.receivingOrg, @@ -86,7 +85,6 @@ class DeliveryHistory( schemaName = reportFile.schemaName, bodyFormat = reportFile.bodyFormat ) - } } @JsonIgnore diff --git a/prime-router/src/main/kotlin/history/ReportHistory.kt b/prime-router/src/main/kotlin/history/ReportHistory.kt index 4d187c5398a..06618d226eb 100644 --- a/prime-router/src/main/kotlin/history/ReportHistory.kt +++ b/prime-router/src/main/kotlin/history/ReportHistory.kt @@ -160,9 +160,9 @@ class ConsolidatedActionLog(log: DetailedActionLog) { * Tests if a detail action log [other] can be consolidated into this existing consolidated log. * @return true if the log can be consolidated, false otherwise */ - fun canBeConsolidatedWith(other: DetailedActionLog): Boolean { - return this.message == other.detail.message && this.scope == other.scope && this.type == other.type - } + fun canBeConsolidatedWith( + other: DetailedActionLog, + ): Boolean = this.message == other.detail.message && this.scope == other.scope && this.type == other.type } /** diff --git a/prime-router/src/main/kotlin/history/azure/DatabaseDeliveryAccess.kt b/prime-router/src/main/kotlin/history/azure/DatabaseDeliveryAccess.kt index a32ccde7a2e..b87643d039f 100644 --- a/prime-router/src/main/kotlin/history/azure/DatabaseDeliveryAccess.kt +++ b/prime-router/src/main/kotlin/history/azure/DatabaseDeliveryAccess.kt @@ -16,9 +16,7 @@ import java.util.UUID /** * Class to access lookup tables stored in the database. */ -class DatabaseDeliveryAccess( - db: DatabaseAccess = BaseEngine.databaseAccessSingleton, -) : HistoryDatabaseAccess(db) { +class DatabaseDeliveryAccess(db: DatabaseAccess = BaseEngine.databaseAccessSingleton) : HistoryDatabaseAccess(db) { /** * Values that facilities can be sorted by @@ -79,8 +77,7 @@ class DatabaseDeliveryAccess( actionId: Long, orgName: String?, klass: Class, - ): T? { - return db.transactReturning { txn -> + ): T? = db.transactReturning { txn -> DSL.using(txn) .select( ACTION.ACTION_ID, @@ -106,7 +103,6 @@ class DatabaseDeliveryAccess( ACTION.ACTION_ID.eq(actionId) ).fetchOne()?.into(klass) } - } override fun fetchRelatedActions(reportId: UUID, klass: Class): List { TODO("Not yet implemented") diff --git a/prime-router/src/main/kotlin/history/azure/DatabaseSubmissionsAccess.kt b/prime-router/src/main/kotlin/history/azure/DatabaseSubmissionsAccess.kt index 62ec4ec5b33..36f79f64bab 100644 --- a/prime-router/src/main/kotlin/history/azure/DatabaseSubmissionsAccess.kt +++ b/prime-router/src/main/kotlin/history/azure/DatabaseSubmissionsAccess.kt @@ -21,9 +21,9 @@ import java.util.UUID /** * Class to access lookup tables stored in the database. */ -class DatabaseSubmissionsAccess( - db: DatabaseAccess = BaseEngine.databaseAccessSingleton, -) : HistoryDatabaseAccess(db), Logging { +class DatabaseSubmissionsAccess(db: DatabaseAccess = BaseEngine.databaseAccessSingleton) : + HistoryDatabaseAccess(db), + Logging { /** * Creates a condition filter based on the given organization parameters. @@ -128,8 +128,7 @@ class DatabaseSubmissionsAccess( ) } - override fun fetchRelatedActions(reportId: UUID, klass: Class): List { - return db.transactReturning { txn -> + override fun fetchRelatedActions(reportId: UUID, klass: Class): List = db.transactReturning { txn -> // We need to use the report ID to find the correct start of the report lineage. This allows for // flexibility in the pipelines to not have a child report on the very first action on a submitted report. // Report lineages for a parent report that is the submitted report (what was received) always have the same @@ -157,7 +156,6 @@ class DatabaseSubmissionsAccess( emptyList() } } - } /** * Fetch the details of an action's relations (descendants). @@ -166,8 +164,7 @@ class DatabaseSubmissionsAccess( * @param actionId the action id attached to the action to find relations for. * @return a list of descendants for the given action id. */ - private fun reportDescendantExpression(actionId: Long): CommonTableExpression<*> { - return DSL.name("t").fields( + private fun reportDescendantExpression(actionId: Long): CommonTableExpression<*> = DSL.name("t").fields( "action_id", "child_report_id", "parent_report_id" @@ -194,5 +191,4 @@ class DatabaseSubmissionsAccess( ) ) ) - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/history/azure/DeliveryFacade.kt b/prime-router/src/main/kotlin/history/azure/DeliveryFacade.kt index 17421529b50..f143ae261ab 100644 --- a/prime-router/src/main/kotlin/history/azure/DeliveryFacade.kt +++ b/prime-router/src/main/kotlin/history/azure/DeliveryFacade.kt @@ -145,13 +145,11 @@ class DeliveryFacade( reportId: ReportId, sortDir: HistoryDatabaseAccess.SortDir, sortColumn: DatabaseDeliveryAccess.FacilitySortColumn, - ): List { - return dbDeliveryAccess.fetchFacilityList( + ): List = dbDeliveryAccess.fetchFacilityList( reportId, sortDir, sortColumn, ) - } /** * Check whether these [claims] from this [request] @@ -163,9 +161,7 @@ class DeliveryFacade( claims: AuthenticatedClaims, action: Action, request: HttpRequestMessage, - ): Boolean { - return claims.authorizedForSendOrReceive(action.receivingOrg, null, request) - } + ): Boolean = claims.authorizedForSendOrReceive(action.receivingOrg, null, request) companion object { val instance: DeliveryFacade by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { diff --git a/prime-router/src/main/kotlin/history/azure/DeliveryFunction.kt b/prime-router/src/main/kotlin/history/azure/DeliveryFunction.kt index 3a8c9c7065e..a62c6e4a228 100644 --- a/prime-router/src/main/kotlin/history/azure/DeliveryFunction.kt +++ b/prime-router/src/main/kotlin/history/azure/DeliveryFunction.kt @@ -89,21 +89,17 @@ class DeliveryFunction( * @param query Incoming query params * @return encoded param */ - fun extractFileName(query: Map): String? { - return if (query["fileName"] != null) { + fun extractFileName(query: Map): String? = if (query["fileName"] != null) { URLEncoder.encode(query["fileName"], Charset.defaultCharset()) } else { null } - } - fun extractReceivingOrgSvcStatus(query: Map): List? { - return try { + fun extractReceivingOrgSvcStatus(query: Map): List? = try { query["receivingOrgSvcStatus"]?.split(",")?.map { CustomerStatus.valueOf(it) } } catch (e: IllegalArgumentException) { throw InvalidParameterException("Invalid value for receivingOrgSvcStatus.") } - } } } @@ -113,13 +109,13 @@ class DeliveryFunction( * @param organization Name of organization and optionally a receiver channel in the format {orgName}.{receiver} * @return Name for the organization */ - override fun validateOrgSvcName(organization: String): String? { - return if (organization.contains(Sender.fullNameSeparator)) { + override fun validateOrgSvcName( + organization: String, + ): String? = if (organization.contains(Sender.fullNameSeparator)) { workflowEngine.settings.findReceiver(organization).also { receivingOrgSvc = it?.name }?.organizationName } else { workflowEngine.settings.findOrganization(organization)?.name } - } /** * Verify that the action being checked has the correct data/parameters @@ -128,9 +124,9 @@ class DeliveryFunction( * @param action DB Action that we are reviewing * @return true if action is valid, else false */ - override fun actionIsValid(action: Action): Boolean { - return action.actionName == TaskAction.batch || action.actionName == TaskAction.send - } + override fun actionIsValid( + action: Action, + ): Boolean = action.actionName == TaskAction.batch || action.actionName == TaskAction.send /** * Get a list of delivery history @@ -166,9 +162,11 @@ class DeliveryFunction( * @param action Action from which the data for the delivery is loaded * @return */ - override fun singleDetailedHistory(id: String, txn: DataAccessTransaction, action: Action): DeliveryHistory? { - return deliveryFacade.findDetailedDeliveryHistory(id, action.actionId) - } + override fun singleDetailedHistory( + id: String, + txn: DataAccessTransaction, + action: Action, + ): DeliveryHistory? = deliveryFacade.findDetailedDeliveryHistory(id, action.actionId) @FunctionName("getDeliveriesV1") fun getDeliveriesV1( @@ -186,7 +184,8 @@ class DeliveryFunction( request, "No such receiver $receiverName" ) - if (claims == null || !claims.authorizedForSendOrReceive( + if (claims == null || + !claims.authorizedForSendOrReceive( requiredOrganization = receiver.organizationName, request = request ) @@ -275,9 +274,7 @@ class DeliveryFunction( route = "waters/org/{organization}/deliveries" ) request: HttpRequestMessage, @BindingName("organization") organization: String, - ): HttpResponseMessage { - return this.getListByOrg(request, organization) - } + ): HttpResponseMessage = this.getListByOrg(request, organization) /** * Get expanded details for a single report @@ -295,9 +292,7 @@ class DeliveryFunction( route = "waters/report/{id}/delivery" ) request: HttpRequestMessage, @BindingName("id") id: String, - ): HttpResponseMessage { - return this.getDetailedView(request, id) - } + ): HttpResponseMessage = this.getDetailedView(request, id) /** * Endpoint for intermediary receivers to verify status of messages. It passes @@ -316,9 +311,7 @@ class DeliveryFunction( ) request: HttpRequestMessage, @BindingName("reportId") reportId: UUID, context: ExecutionContext, - ): HttpResponseMessage { - return this.retrieveETORIntermediaryMetadata(request, reportId, context, null) - } + ): HttpResponseMessage = this.retrieveETORIntermediaryMetadata(request, reportId, context, null) /** * Function for finding the associated report ID that the intermediary knows about given the report ID that the receiver is @@ -428,9 +421,7 @@ class DeliveryFunction( * * @property sortColumn sort the table by specific column; default created_at. */ - data class FacilityListApiParameters( - val sortColumn: DatabaseDeliveryAccess.FacilitySortColumn, - ) { + data class FacilityListApiParameters(val sortColumn: DatabaseDeliveryAccess.FacilitySortColumn) { constructor(query: Map) : this( sortColumn = extractSortCol(query) ) @@ -471,7 +462,8 @@ class DeliveryFunction( request, "No such receiver $receiverName" ) - if (claims == null || !claims.authorizedForSendOrReceive( + if (claims == null || + !claims.authorizedForSendOrReceive( requiredOrganization = receiver.organizationName, request = request ) diff --git a/prime-router/src/main/kotlin/history/azure/HistoryDatabaseAccess.kt b/prime-router/src/main/kotlin/history/azure/HistoryDatabaseAccess.kt index 294d46ac835..b136a4eeb7e 100644 --- a/prime-router/src/main/kotlin/history/azure/HistoryDatabaseAccess.kt +++ b/prime-router/src/main/kotlin/history/azure/HistoryDatabaseAccess.kt @@ -12,9 +12,7 @@ import org.jooq.impl.DSL import java.time.OffsetDateTime import java.util.UUID -abstract class HistoryDatabaseAccess( - internal val db: DatabaseAccess = BaseEngine.databaseAccessSingleton, -) { +abstract class HistoryDatabaseAccess(internal val db: DatabaseAccess = BaseEngine.databaseAccessSingleton) { /** * Values that results can be sorted by. */ diff --git a/prime-router/src/main/kotlin/history/azure/ReportFileFacade.kt b/prime-router/src/main/kotlin/history/azure/ReportFileFacade.kt index e95a6e2319d..0488a15685e 100644 --- a/prime-router/src/main/kotlin/history/azure/ReportFileFacade.kt +++ b/prime-router/src/main/kotlin/history/azure/ReportFileFacade.kt @@ -14,29 +14,24 @@ import java.util.UUID * Submissions / history API * Contains all business logic regarding submissions and JSON serialization. */ -abstract class ReportFileFacade( - private val dbAccess: DatabaseAccess = BaseEngine.databaseAccessSingleton, -) : Logging { +abstract class ReportFileFacade(private val dbAccess: DatabaseAccess = BaseEngine.databaseAccessSingleton) : Logging { /** * @return a single Action associated with this [reportId] */ - fun fetchActionForReportId(reportId: UUID): Action? { - return dbAccess.fetchActionForReportId(reportId) - } + fun fetchActionForReportId(reportId: UUID): Action? = dbAccess.fetchActionForReportId(reportId) /** * @return a single Report associated with this [actionId] */ - fun fetchReportForActionId(actionId: Long, txn: DataAccessTransaction? = null): ReportFile? { - return dbAccess.fetchReportForActionId(actionId, txn) - } + fun fetchReportForActionId( + actionId: Long, + txn: DataAccessTransaction? = null, + ): ReportFile? = dbAccess.fetchReportForActionId(actionId, txn) /** * @return a single Action, given its key [actionId] */ - fun fetchAction(actionId: Long): Action? { - return dbAccess.fetchAction(actionId) - } + fun fetchAction(actionId: Long): Action? = dbAccess.fetchAction(actionId) /** * Check whether these [claims] allow access to this [orgName]. @@ -52,9 +47,7 @@ abstract class ReportFileFacade( orgName: String? = null, senderOrReceiver: String? = null, request: HttpRequestMessage, - ): Boolean { - return claims.authorizedForSendOrReceive(orgName, senderOrReceiver, request) - } + ): Boolean = claims.authorizedForSendOrReceive(orgName, senderOrReceiver, request) /** * Check whether these [claims] from this [request] diff --git a/prime-router/src/main/kotlin/history/azure/ReportFileFunction.kt b/prime-router/src/main/kotlin/history/azure/ReportFileFunction.kt index c3f6827c0b9..2780d69d684 100644 --- a/prime-router/src/main/kotlin/history/azure/ReportFileFunction.kt +++ b/prime-router/src/main/kotlin/history/azure/ReportFileFunction.kt @@ -417,25 +417,22 @@ abstract class ReportFileFunction( * @param query Incoming query params * @return converted params */ - fun extractShowFailed(query: Map): Boolean { - return query["showfailed"]?.toBoolean() ?: false - } + fun extractShowFailed(query: Map): Boolean = query["showfailed"]?.toBoolean() ?: false /** * Convert fileName from query into param used for the DB * @param query Incoming query params * @return encoded param */ - fun extractFileName(query: Map): String? { - return if (query["fileName"] != null) { + fun extractFileName(query: Map): String? = if (query["fileName"] != null) { URLEncoder.encode(query["fileName"], Charset.defaultCharset()) } else { null } - } - fun extractReceivingOrgSvcStatus(query: Map): List? { - return query["receivingOrgSvcStatus"]?.split(",")?.map { CustomerStatus.valueOf(it) } + fun extractReceivingOrgSvcStatus(query: Map): List? = + query["receivingOrgSvcStatus"]?.split(",")?.map { + CustomerStatus.valueOf(it) } } } @@ -446,12 +443,10 @@ abstract class ReportFileFunction( * @param str Potential UUID * @return a valid UUID, or null if this [str] cannot be parsed into a valid UUID. */ - internal fun toUuidOrNull(str: String): UUID? { - return try { + internal fun toUuidOrNull(str: String): UUID? = try { UUID.fromString(str) } catch (e: IllegalArgumentException) { logger.debug("Invalid format for report ID: $str", e) null } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/history/azure/SubmissionFunction.kt b/prime-router/src/main/kotlin/history/azure/SubmissionFunction.kt index c63d1dcdb18..195272e9f79 100644 --- a/prime-router/src/main/kotlin/history/azure/SubmissionFunction.kt +++ b/prime-router/src/main/kotlin/history/azure/SubmissionFunction.kt @@ -44,11 +44,11 @@ class SubmissionFunction( * @param organization Name of organization and optionally a sender in the format {orgName}.{sender} * @return Name for the organization */ - override fun validateOrgSvcName(organization: String): String? { - return workflowEngine.settings.findSender(organization).also { + override fun validateOrgSvcName( + organization: String, + ): String? = workflowEngine.settings.findSender(organization).also { if (organization.contains(Sender.fullNameSeparator)) sendingOrgSvc = it?.name }?.organizationName - } /** * Verify that the action being checked has the correct data/parameters @@ -57,9 +57,9 @@ class SubmissionFunction( * @param action DB Action that we are reviewing * @return true if action is valid, else false */ - override fun actionIsValid(action: Action): Boolean { - return action.sendingOrg != null && action.actionName == TaskAction.receive - } + override fun actionIsValid( + action: Action, + ): Boolean = action.sendingOrg != null && action.actionName == TaskAction.receive /** * Get a list of submission history @@ -122,9 +122,7 @@ class SubmissionFunction( route = "waters/org/{organization}/submissions" ) request: HttpRequestMessage, @BindingName("organization") organization: String, - ): HttpResponseMessage { - return this.getListByOrg(request, organization) - } + ): HttpResponseMessage = this.getListByOrg(request, organization) /** * API endpoint to return history of a single report. @@ -139,9 +137,7 @@ class SubmissionFunction( route = "waters/report/{id}/history" ) request: HttpRequestMessage, @BindingName("id") id: String, - ): HttpResponseMessage { - return this.getDetailedView(request, id) - } + ): HttpResponseMessage = this.getDetailedView(request, id) /** * Endpoint for intermediary senders to verify status of messages. It passes @@ -160,9 +156,7 @@ class SubmissionFunction( ) request: HttpRequestMessage, @BindingName("reportId") reportId: UUID, context: ExecutionContext, - ): HttpResponseMessage { - return this.retrieveETORIntermediaryMetadata(request, reportId, context, null) - } + ): HttpResponseMessage = this.retrieveETORIntermediaryMetadata(request, reportId, context, null) /** * Function for finding the associated report ID that the intermediary knows about given the report ID that the sender is diff --git a/prime-router/src/main/kotlin/history/azure/SubmissionsFacade.kt b/prime-router/src/main/kotlin/history/azure/SubmissionsFacade.kt index d83a1bd7918..efa09420b23 100644 --- a/prime-router/src/main/kotlin/history/azure/SubmissionsFacade.kt +++ b/prime-router/src/main/kotlin/history/azure/SubmissionsFacade.kt @@ -184,9 +184,7 @@ class SubmissionsFacade( claims: AuthenticatedClaims, action: Action, request: HttpRequestMessage, - ): Boolean { - return claims.authorizedForSendOrReceive(action.sendingOrg, null, request) - } + ): Boolean = claims.authorizedForSendOrReceive(action.sendingOrg, null, request) companion object { val instance: SubmissionsFacade by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { diff --git a/prime-router/src/main/kotlin/history/db/DeliveryDatabaseAccess.kt b/prime-router/src/main/kotlin/history/db/DeliveryDatabaseAccess.kt index 10174e8a206..df3fe6bdcbe 100644 --- a/prime-router/src/main/kotlin/history/db/DeliveryDatabaseAccess.kt +++ b/prime-router/src/main/kotlin/history/db/DeliveryDatabaseAccess.kt @@ -54,9 +54,7 @@ class DeliveryTable : CustomTable(DSL.name("delivery")) { val DELIVERY = DeliveryTable() } - override fun getRecordType(): Class { - return DeliveryRecord::class.java - } + override fun getRecordType(): Class = DeliveryRecord::class.java } class DeliveryRecord : CustomRecord(DeliveryTable.DELIVERY) @@ -71,8 +69,7 @@ sealed class DeliveryApiSearchFilter : ApiFilter { * Filters results to those where the created_at is greater than or equal to the passed in date * @param value the date that results will be greater than or equal to */ - class Since(override val value: OffsetDateTime) : - DeliveryApiSearchFilter() { + class Since(override val value: OffsetDateTime) : DeliveryApiSearchFilter() { override val tableField: TableField = DeliveryTable.DELIVERY.CREATED_AT } @@ -80,8 +77,7 @@ sealed class DeliveryApiSearchFilter : ApiFilter { * Filters results to those where the created_at is less than or equal to the passed in date * @param value the date that results will be less than or equal to */ - class Until(override val value: OffsetDateTime) : - DeliveryApiSearchFilter() { + class Until(override val value: OffsetDateTime) : DeliveryApiSearchFilter() { override val tableField: TableField = DeliveryTable.DELIVERY.CREATED_AT } } @@ -104,20 +100,14 @@ class DeliveryApiSearch( page, limit ) { - override fun getCondition(filter: DeliveryApiSearchFilter<*>): Condition { - return when (filter) { + override fun getCondition(filter: DeliveryApiSearchFilter<*>): Condition = when (filter) { is DeliveryApiSearchFilter.Since -> filter.tableField.ge(filter.value) is DeliveryApiSearchFilter.Until -> filter.tableField.le(filter.value) } - } - override fun getSortColumn(): Field<*> { - return sortParameter ?: DeliveryTable.DELIVERY.CREATED_AT - } + override fun getSortColumn(): Field<*> = sortParameter ?: DeliveryTable.DELIVERY.CREATED_AT - override fun getPrimarySortColumn(): Field<*> { - return DeliveryTable.DELIVERY.SORT_ID - } + override fun getPrimarySortColumn(): Field<*> = DeliveryTable.DELIVERY.SORT_ID companion object : ApiSearchParser>(), diff --git a/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt b/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt index 031fc71892e..609edca4951 100644 --- a/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt +++ b/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt @@ -84,9 +84,7 @@ class DeliveryHistoryTable : CustomTable(DSL.name("delive val DELIVERY_HISTORY = DeliveryHistoryTable() } - override fun getRecordType(): Class { - return DeliveryHistoryRecord::class.java - } + override fun getRecordType(): Class = DeliveryHistoryRecord::class.java } class DeliveryHistoryRecord : CustomRecord(DeliveryHistoryTable.DELIVERY_HISTORY) @@ -101,8 +99,7 @@ sealed class DeliveryHistoryApiSearchFilter : ApiFilter() { + class Since(override val value: OffsetDateTime) : DeliveryHistoryApiSearchFilter() { override val tableField: TableField = DeliveryHistoryTable.DELIVERY_HISTORY.CREATED_AT } @@ -111,8 +108,7 @@ sealed class DeliveryHistoryApiSearchFilter : ApiFilter() { + class Until(override val value: OffsetDateTime) : DeliveryHistoryApiSearchFilter() { override val tableField: TableField = DeliveryHistoryTable.DELIVERY_HISTORY.CREATED_AT } @@ -137,20 +133,14 @@ class DeliveryHistoryApiSearch( page, limit ) { - override fun getCondition(filter: DeliveryHistoryApiSearchFilter<*>): Condition { - return when (filter) { + override fun getCondition(filter: DeliveryHistoryApiSearchFilter<*>): Condition = when (filter) { is DeliveryHistoryApiSearchFilter.Since -> filter.tableField.ge(filter.value) is DeliveryHistoryApiSearchFilter.Until -> filter.tableField.le(filter.value) } - } - override fun getSortColumn(): Field<*> { - return sortParameter ?: DeliveryHistoryTable.DELIVERY_HISTORY.CREATED_AT - } + override fun getSortColumn(): Field<*> = sortParameter ?: DeliveryHistoryTable.DELIVERY_HISTORY.CREATED_AT - override fun getPrimarySortColumn(): Field<*> { - return DeliveryHistoryTable.DELIVERY_HISTORY.REPORT_ID - } + override fun getPrimarySortColumn(): Field<*> = DeliveryHistoryTable.DELIVERY_HISTORY.REPORT_ID companion object : ApiSearchParser< diff --git a/prime-router/src/main/kotlin/history/db/ReportGraph.kt b/prime-router/src/main/kotlin/history/db/ReportGraph.kt index 103b9831eb3..51a903c1fd2 100644 --- a/prime-router/src/main/kotlin/history/db/ReportGraph.kt +++ b/prime-router/src/main/kotlin/history/db/ReportGraph.kt @@ -54,9 +54,7 @@ class ItemGraphTable : CustomTable(DSL.name("item_graph")) { val ITEM_GRAPH = ItemGraphTable() } - override fun getRecordType(): Class { - return ItemGraphRecord::class.java - } + override fun getRecordType(): Class = ItemGraphRecord::class.java } class ItemGraphRecord : CustomRecord(ItemGraphTable.ITEM_GRAPH) @@ -74,9 +72,7 @@ class ItemGraphRecord : CustomRecord(ItemGraphTable.ITEM_GRAPH) * * @param db database access to run the generated queries against */ -class ReportGraph( - val db: DatabaseAccess = BaseEngine.databaseAccessSingleton, -) : Logging { +class ReportGraph(val db: DatabaseAccess = BaseEngine.databaseAccessSingleton) : Logging { /** * @@ -88,8 +84,7 @@ class ReportGraph( receiver: Receiver, taskAction: TaskAction, dslContext: DSLContext, - ): List { - return dslContext + ): List = dslContext .select(REPORT_FILE.REPORT_ID) .from(REPORT_FILE) .join(Action.ACTION).on(Action.ACTION.ACTION_ID.eq(REPORT_FILE.ACTION_ID)) @@ -97,7 +92,6 @@ class ReportGraph( .and(Action.ACTION.RECEIVING_ORG_SVC.eq(receiver.name)) .and(Action.ACTION.ACTION_NAME.eq(taskAction)) .fetchInto(UUID::class.java) - } /** * Returns all the metadata for the items in the past in reports; will recursively walk up the report lineage @@ -129,12 +123,10 @@ class ReportGraph( * This will return null if no report with action type "receive" is present or if * the root is passed in */ - fun getRootReport(childReportId: UUID): ReportFile? { - return db.transactReturning { txn -> + fun getRootReport(childReportId: UUID): ReportFile? = db.transactReturning { txn -> val cte = reportAncestorGraphCommonTableExpression(listOf(childReportId)) rootReportRecords(txn, cte).fetchOneInto(ReportFile::class.java) } - } /** * This data class captures the rough details that corresponds to an "item" which currently is not directly captured @@ -144,12 +136,7 @@ class ReportGraph( * batch of several results. * */ - data class Item( - val parentReportId: UUID, - val parentIndex: Int, - val childReportId: UUID, - val childIndex: Int, - ) + data class Item(val parentReportId: UUID, val parentIndex: Int, val childReportId: UUID, val childIndex: Int) /** * Retrieves the root "item" by recursing up the item lineage @@ -188,12 +175,10 @@ class ReportGraph( * * If the passed in report ID has multiple root reports, they will all be returned */ - fun getRootReports(childReportId: UUID): List { - return db.transactReturning { txn -> + fun getRootReports(childReportId: UUID): List = db.transactReturning { txn -> val cte = reportAncestorGraphCommonTableExpression(listOf(childReportId)) rootReportRecords(txn, cte).fetchInto(ReportFile::class.java) } - } /** * Recursively goes down the report_lineage table from any report until it reaches @@ -239,8 +224,7 @@ class ReportGraph( */ fun metadataCommonTableExpression( itemGraphRecords: CommonTableExpression, - ): CommonTableExpression { - return DSL.name(METADATA_CTE).`as`( + ): CommonTableExpression = DSL.name(METADATA_CTE).`as`( selectDistinct(COVID_RESULT_METADATA.asterisk()) .from(COVID_RESULT_METADATA) .where( @@ -262,7 +246,6 @@ class ReportGraph( ) ).coerce(COVID_RESULT_METADATA) ) - } /** * Accepts a list of report ids and then finds all the items associated with that report diff --git a/prime-router/src/main/kotlin/history/db/SubmitterDatabaseAccess.kt b/prime-router/src/main/kotlin/history/db/SubmitterDatabaseAccess.kt index 7a158d38b32..4a8dd344833 100644 --- a/prime-router/src/main/kotlin/history/db/SubmitterDatabaseAccess.kt +++ b/prime-router/src/main/kotlin/history/db/SubmitterDatabaseAccess.kt @@ -50,9 +50,7 @@ class SubmitterTable : CustomTable(DSL.name("submitter")) { val SUBMITTER = SubmitterTable() } - override fun getRecordType(): Class { - return SubmitterRecord::class.java - } + override fun getRecordType(): Class = SubmitterRecord::class.java } /** @@ -85,8 +83,7 @@ sealed class SubmitterApiSearchFilter : ApiFilter { * Filters results to those where the created_at is greater than or equal to the passed in date * @param value the date that results will be greater than or equal to */ - class Since(override val value: LocalDateTime) : - SubmitterApiSearchFilter() { + class Since(override val value: LocalDateTime) : SubmitterApiSearchFilter() { override val tableField: TableField = SubmitterTable.SUBMITTER.FIRST_REPORT_DATE } @@ -124,20 +121,14 @@ class SubmitterApiSearch( page, limit ) { - override fun getCondition(filter: SubmitterApiSearchFilter<*>): Condition { - return when (filter) { + override fun getCondition(filter: SubmitterApiSearchFilter<*>): Condition = when (filter) { is SubmitterApiSearchFilter.Since -> filter.tableField.ge(filter.value) is SubmitterApiSearchFilter.Until -> filter.tableField.le(filter.value) } - } - override fun getSortColumn(): Field<*> { - return sortParameter ?: SubmitterTable.SUBMITTER.FIRST_REPORT_DATE - } + override fun getSortColumn(): Field<*> = sortParameter ?: SubmitterTable.SUBMITTER.FIRST_REPORT_DATE - override fun getPrimarySortColumn(): Field<*> { - return SubmitterTable.SUBMITTER.SORT_ID - } + override fun getPrimarySortColumn(): Field<*> = SubmitterTable.SUBMITTER.SORT_ID companion object : ApiSearchParser>(), Logging { diff --git a/prime-router/src/main/kotlin/messageTracker/MessageActionLog.kt b/prime-router/src/main/kotlin/messageTracker/MessageActionLog.kt index 64339192298..0aeb942a684 100644 --- a/prime-router/src/main/kotlin/messageTracker/MessageActionLog.kt +++ b/prime-router/src/main/kotlin/messageTracker/MessageActionLog.kt @@ -12,7 +12,4 @@ import gov.cdc.prime.router.ActionLogDetail */ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -class MessageActionLog( - val trackingId: String?, - val detail: ActionLogDetail, -) \ No newline at end of file +class MessageActionLog(val trackingId: String?, val detail: ActionLogDetail) \ No newline at end of file diff --git a/prime-router/src/main/kotlin/metadata/LivdLookup.kt b/prime-router/src/main/kotlin/metadata/LivdLookup.kt index 700b77ae034..bbd995afa05 100644 --- a/prime-router/src/main/kotlin/metadata/LivdLookup.kt +++ b/prime-router/src/main/kotlin/metadata/LivdLookup.kt @@ -136,9 +136,7 @@ object LivdLookup { tableColumn: String, value: String, filters: LookupTable.FilterBuilder, - ): String? { - return lookup(tableColumn, value, LivdTableColumns.EQUIPMENT_UID.colName, filters) - } + ): String? = lookup(tableColumn, value, LivdTableColumns.EQUIPMENT_UID.colName, filters) /** * Does a lookup in the LIVD table based on the test kit Id @@ -190,9 +188,7 @@ object LivdLookup { lookup: String, onColumn: String, filters: LookupTable.FilterBuilder, - ): String? { - return filters.equalsIgnoreCase(onColumn, lookup).findSingleResult(lookupColumn) - } + ): String? = filters.equalsIgnoreCase(onColumn, lookup).findSingleResult(lookupColumn) /** * Does the lookup in the LIVD table based on the lookup type and the values passed in, @@ -208,7 +204,5 @@ object LivdLookup { lookup: String, onColumn: String, filters: LookupTable.FilterBuilder, - ): String? { - return filters.startsWithIgnoreCase(onColumn, lookup).findSingleResult(lookupColumn) - } + ): String? = filters.startsWithIgnoreCase(onColumn, lookup).findSingleResult(lookupColumn) } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/metadata/LivdMappers.kt b/prime-router/src/main/kotlin/metadata/LivdMappers.kt index eb52c84bbf0..a29925a4789 100644 --- a/prime-router/src/main/kotlin/metadata/LivdMappers.kt +++ b/prime-router/src/main/kotlin/metadata/LivdMappers.kt @@ -129,7 +129,8 @@ class LIVDLookupMapper : Mapper { return ElementResult(null).also { // Hide any warnings to fields the user does not send to us - if (!element.csvFields.isNullOrEmpty() || !element.hl7Field.isNullOrBlank() || + if (!element.csvFields.isNullOrEmpty() || + !element.hl7Field.isNullOrBlank() || !element.hl7OutputFields.isNullOrEmpty() ) { it.warning(InvalidEquipmentMessage(element.fieldMapping)) @@ -158,8 +159,7 @@ class Obx17Mapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.isEmpty()) { null } else { @@ -187,7 +187,6 @@ class Obx17Mapper : Mapper { } } ) - } } /** @@ -210,8 +209,7 @@ class Obx17TypeMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.isEmpty()) { null } else { @@ -232,5 +230,4 @@ class Obx17TypeMapper : Mapper { } } ) - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/metadata/LookupTable.kt b/prime-router/src/main/kotlin/metadata/LookupTable.kt index 7f16533f70c..f21c738f239 100644 --- a/prime-router/src/main/kotlin/metadata/LookupTable.kt +++ b/prime-router/src/main/kotlin/metadata/LookupTable.kt @@ -169,9 +169,7 @@ open class LookupTable : Logging { * Test if a [column] name exists in the table. The search is case-insensitive. * @return true if the column exists, false otherwise */ - fun hasColumn(column: String): Boolean { - return table.containsColumn(column) - } + fun hasColumn(column: String): Boolean = table.containsColumn(column) /** * Filter builder used to find values or filter the lookup table. @@ -192,18 +190,14 @@ open class LookupTable : Logging { * Predicate to do starts with search while ignoring case. */ inner class StartsWithIgnoreCasePredicate : BiPredicate { - override fun test(t: String, u: String): Boolean { - return t.startsWith(u, true) - } + override fun test(t: String, u: String): Boolean = t.startsWith(u, true) } /** * Predicate to do not equals search while ignoring case. */ inner class NotEqualsIgnoreCasePredicate : BiPredicate { - override fun test(t: String, u: String): Boolean { - return !t.equals(u, true) - } + override fun test(t: String, u: String): Boolean = !t.equals(u, true) } /** @@ -218,15 +212,13 @@ open class LookupTable : Logging { * Get the reference to a column using [colName]. * @return the reference to the column or null if the column was not found */ - private fun getColumn(colName: String): StringColumn? { - return try { + private fun getColumn(colName: String): StringColumn? = try { table.stringColumn(colName) } catch (e: IllegalStateException) { logger.error("Invalid column name $colName specified for lookup table $name.", e) hasError = true null } - } /** * Lookup in column name [colName] for case-sensitive values that start with [value]. @@ -278,8 +270,7 @@ open class LookupTable : Logging { * @return a list of unique values or an empty list if no values found or an error occurred */ @Suppress("UNCHECKED_CAST") // All columns are string columns - fun findAllUnique(lookupColumn: String): List { - return try { + fun findAllUnique(lookupColumn: String): List = try { when { hasError -> emptyList() selector == null -> table.column(lookupColumn).unique().asList() as List @@ -289,7 +280,6 @@ open class LookupTable : Logging { logger.error("Invalid column name $lookupColumn specified for lookup table $name.", e) emptyList() } - } /** * Return a single value if the filter matched one and only one unique value. @@ -305,21 +295,17 @@ open class LookupTable : Logging { * this function will return a copy of the same lookup table. * @return the filtered table or an empty table if an error occurred */ - fun filter(): LookupTable { - return when { + fun filter(): LookupTable = when { hasError -> LookupTable(name) selector == null -> LookupTable(name, table) else -> LookupTable(name, table.where(selector)) } - } /** * Make a copy of the filter. * @return a copy of the filter. */ - fun copy(): FilterBuilder { - return filter().FilterBuilder() - } + fun copy(): FilterBuilder = filter().FilterBuilder() } /** @@ -346,13 +332,11 @@ open class LookupTable : Logging { filterColumn: String? = null, filterValue: String? = null, ): String? { - fun filterRows(): Table { - return if (filterColumn != null && filterValue != null) { + fun filterRows(): Table = if (filterColumn != null && filterValue != null) { FilterBuilder().equalsIgnoreCase(filterColumn, filterValue).filter().table } else { table } - } // Split into words fun wordsFromRaw(input: String): List { diff --git a/prime-router/src/main/kotlin/metadata/Mappers.kt b/prime-router/src/main/kotlin/metadata/Mappers.kt index 46c53f34b41..74430891a61 100644 --- a/prime-router/src/main/kotlin/metadata/Mappers.kt +++ b/prime-router/src/main/kotlin/metadata/Mappers.kt @@ -82,10 +82,7 @@ interface Mapper { ): ElementResult } -data class ElementAndValue( - val element: Element, - val value: String, -) +data class ElementAndValue(val element: Element, val value: String) class MiddleInitialMapper : Mapper { override val name = "middleInitial" @@ -100,8 +97,7 @@ class MiddleInitialMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.isEmpty()) { null } else { @@ -114,7 +110,6 @@ class MiddleInitialMapper : Mapper { } } ) - } } /** @@ -150,9 +145,10 @@ class IfThenElseMapper : Mapper { * @param mapperArg one of the arguments from a schema elements mapper property * @return Either a passed-in Elements .value or the mapper argument as a string literal */ - internal fun decodeArg(values: List, mapperArg: String): String { - return values.find { it.element.name.equals(mapperArg, ignoreCase = true) }?.value ?: mapperArg - } + internal fun decodeArg( + values: List, + mapperArg: String, + ): String = values.find { it.element.name.equals(mapperArg, ignoreCase = true) }?.value ?: mapperArg /** * Compares two strings for equalities @@ -201,8 +197,7 @@ class IfThenElseMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return if ( + ): ElementResult = if ( comp( decodeArg(values, args[0]), decodeArg(values, args[1]), @@ -213,7 +208,6 @@ class IfThenElseMapper : Mapper { } else { ElementResult(decodeArg(values, args[4])) // see decodeArg() } - } } /** @@ -230,8 +224,7 @@ class UseMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.isEmpty()) { null } else { @@ -247,7 +240,6 @@ class UseMapper : Mapper { } } ) - } } /** @@ -420,8 +412,7 @@ class ConcatenateMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.isEmpty()) { null } else { @@ -429,7 +420,6 @@ class ConcatenateMapper : Mapper { values.joinToString(separator = element.delimiter ?: ", ") { it.value } } ) - } } /** @@ -449,15 +439,13 @@ class IfPresentMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.size == 1) { args[1] } else { null } ) - } } /** @@ -529,8 +517,7 @@ class IfNPIMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.size != 1) { null } else if (NPIUtilities.isValidNPI(values[0].value)) { @@ -541,7 +528,6 @@ class IfNPIMapper : Mapper { null } ) - } } /** @@ -565,8 +551,7 @@ class LookupMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( // args should be twice size of values as it includes the index column names // ex: lookup(patient_state, $Column:State, patient_county, $Column:County) // there are four args two represent fields with values. Two are lookup table columns @@ -591,7 +576,6 @@ class LookupMapper : Mapper { tableFilter.findSingleResult(lookupColumn) } ) - } } /** @@ -606,9 +590,7 @@ class LookupMapper : Mapper { class LookupSenderAutomationValuesets : Mapper { override val name = "lookupSenderAutomationValuesets" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, @@ -750,17 +732,14 @@ class NpiLookupMapper : Mapper { class Obx8Mapper : Mapper { override val name = "obx8" - override fun valueNames(element: Element, args: List): List { - return listOf("test_result") - } + override fun valueNames(element: Element, args: List): List = listOf("test_result") override fun apply( element: Element, args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult( + ): ElementResult = ElementResult( if (values.isEmpty() || values.size > 1) { null } else { @@ -789,15 +768,12 @@ class Obx8Mapper : Mapper { } } ) - } } class TimestampMapper : Mapper { override val name = "timestamp" - override fun valueNames(element: Element, args: List): List { - return emptyList() - } + override fun valueNames(element: Element, args: List): List = emptyList() override fun apply( element: Element, @@ -827,9 +803,7 @@ class TimestampMapper : Mapper { class DateTimeOffsetMapper : Mapper { override val name = "offsetDateTime" - override fun valueNames(element: Element, args: List): List { - return listOf(args[0]) - } + override fun valueNames(element: Element, args: List): List = listOf(args[0]) override fun apply( element: Element, @@ -837,13 +811,11 @@ class DateTimeOffsetMapper : Mapper { values: List, sender: Sender?, ): ElementResult { - fun parseDateTime(value: String): OffsetDateTime { - return try { + fun parseDateTime(value: String): OffsetDateTime = try { DateUtilities.parseDate(value.trim()).toOffsetDateTime() } catch (t: Throwable) { error("Invalid date: '$value' for element '${element.name}'") } - } return ElementResult( if (values.isEmpty() || values.size > 1 || values[0].value.isBlank()) { null @@ -869,9 +841,7 @@ class DateTimeOffsetMapper : Mapper { class CoalesceMapper : Mapper { override val name = "coalesce" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, @@ -890,9 +860,7 @@ class CoalesceMapper : Mapper { class TrimBlanksMapper : Mapper { override val name = "trimBlanks" - override fun valueNames(element: Element, args: List): List { - return listOf(args[0]) - } + override fun valueNames(element: Element, args: List): List = listOf(args[0]) override fun apply( element: Element, @@ -930,9 +898,7 @@ class StripPhoneFormattingMapper : Mapper { class StripNonNumericDataMapper : Mapper { override val name = "stripNonNumeric" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, @@ -950,9 +916,7 @@ class StripNonNumericDataMapper : Mapper { class StripNumericDataMapper : Mapper { override val name = "stripNumeric" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, @@ -970,9 +934,7 @@ class StripNumericDataMapper : Mapper { class SplitMapper : Mapper { override val name = "split" - override fun valueNames(element: Element, args: List): List { - return listOf(args[0]) - } + override fun valueNames(element: Element, args: List): List = listOf(args[0]) override fun apply( element: Element, @@ -996,9 +958,7 @@ class SplitMapper : Mapper { class SplitByCommaMapper : Mapper { override val name = "splitByComma" - override fun valueNames(element: Element, args: List): List { - return listOf(args[0]) - } + override fun valueNames(element: Element, args: List): List = listOf(args[0]) override fun apply( element: Element, @@ -1018,9 +978,7 @@ class SplitByCommaMapper : Mapper { class ZipCodeToCountyMapper : Mapper { override val name = "zipCodeToCounty" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, @@ -1050,9 +1008,7 @@ class ZipCodeToCountyMapper : Mapper { class ZipCodeToStateMapper : Mapper { override val name = "zipCodeToState" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, @@ -1092,9 +1048,7 @@ class ZipCodeToStateMapper : Mapper { class CountryMapper : Mapper { override val name = "countryMapper" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, @@ -1192,9 +1146,7 @@ class NullMapper : Mapper { args: List, values: List, sender: Sender?, - ): ElementResult { - return ElementResult(null) - } + ): ElementResult = ElementResult(null) } object Mappers { diff --git a/prime-router/src/main/kotlin/report/ReportService.kt b/prime-router/src/main/kotlin/report/ReportService.kt index 1997c5a63f7..45e53f14f30 100644 --- a/prime-router/src/main/kotlin/report/ReportService.kt +++ b/prime-router/src/main/kotlin/report/ReportService.kt @@ -22,10 +22,8 @@ class ReportService( * @param childReportId child report ID * @return ReportFile object of the root report -- of the child report itself if it has no parents */ - fun getRootReport(childReportId: ReportId): ReportFile { - return reportGraph.getRootReport(childReportId) + fun getRootReport(childReportId: ReportId): ReportFile = reportGraph.getRootReport(childReportId) ?: reportGraph.db.fetchReportFile(childReportId) - } /** * Gets the index of the item in the submitted report by recursing the item lineage @@ -47,9 +45,9 @@ class ReportService( * @param childReportId child report ID * @return List of ReportFile objects of the root reports */ - fun getRootReports(childReportId: ReportId): List { - return reportGraph.getRootReports(childReportId).distinctBy { it.reportId } - } + fun getRootReports( + childReportId: ReportId, + ): List = reportGraph.getRootReports(childReportId).distinctBy { it.reportId } /** * Accepts a descendant item (report id and index) and finds the ancestor report associated with the @@ -61,11 +59,13 @@ class ReportService( * * @return the [ReportFile] ancestor at the passed [TaskAction] */ - fun getReportForItemAtTask(childReportId: ReportId, childIndex: Int, task: TaskAction): ReportFile? { - return db.transactReturning { txn -> + fun getReportForItemAtTask( + childReportId: ReportId, + childIndex: Int, + task: TaskAction, + ): ReportFile? = db.transactReturning { txn -> reportGraph.getAncestorReport(txn, childReportId, childIndex, task) } - } /** * Gets the root report and concatenates sender fields diff --git a/prime-router/src/main/kotlin/secrets/AzureSecretService.kt b/prime-router/src/main/kotlin/secrets/AzureSecretService.kt index 1f122852b8a..b67c093f054 100644 --- a/prime-router/src/main/kotlin/secrets/AzureSecretService.kt +++ b/prime-router/src/main/kotlin/secrets/AzureSecretService.kt @@ -15,13 +15,11 @@ internal object AzureSecretService : SecretService() { internal fun initSecretClient( secretClientBuilder: SecretClientBuilder = SecretClientBuilder(), credential: TokenCredential = DefaultAzureCredentialBuilder().build(), - ): SecretClient { - return secretClientBuilder + ): SecretClient = secretClientBuilder .vaultUrl("https://$KEY_VAULT_NAME.vault.azure.net") .credential(credential) .retryPolicy(RetryPolicy(ExponentialBackoff(3, Duration.ofMillis(250L), Duration.ofSeconds(2)))) .buildClient() - } override fun fetchSecretFromStore(secretName: String): String? { val azureSafeSecretName = secretName.lowercase().replace("_", "-") diff --git a/prime-router/src/main/kotlin/secrets/EnvVarSecretService.kt b/prime-router/src/main/kotlin/secrets/EnvVarSecretService.kt index c26e331b886..1d70647ff0d 100644 --- a/prime-router/src/main/kotlin/secrets/EnvVarSecretService.kt +++ b/prime-router/src/main/kotlin/secrets/EnvVarSecretService.kt @@ -1,11 +1,7 @@ package gov.cdc.prime.router.secrets internal object EnvVarSecretService : SecretService() { - override fun fetchSecretFromStore(secretName: String): String? { - return fetchEnvironmentVariable(secretName) - } + override fun fetchSecretFromStore(secretName: String): String? = fetchEnvironmentVariable(secretName) - internal fun fetchEnvironmentVariable(secretName: String): String? { - return System.getenv(secretName) - } + internal fun fetchEnvironmentVariable(secretName: String): String? = System.getenv(secretName) } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/secrets/SecretManagement.kt b/prime-router/src/main/kotlin/secrets/SecretManagement.kt index 28be24ed87b..8bd20a94563 100644 --- a/prime-router/src/main/kotlin/secrets/SecretManagement.kt +++ b/prime-router/src/main/kotlin/secrets/SecretManagement.kt @@ -1,11 +1,9 @@ package gov.cdc.prime.router.secrets // Option to access credential service by static class -class SecretHelper() { +class SecretHelper { companion object { - fun getSecretService(): SecretService { - return secretServiceForStorageMethod() - } + fun getSecretService(): SecretService = secretServiceForStorageMethod() } } @@ -16,9 +14,7 @@ interface SecretManagement { get() = secretServiceForStorageMethod() } -internal fun secretServiceForStorageMethod(): SecretService { - return when (System.getenv("SECRET_STORAGE_METHOD")) { +internal fun secretServiceForStorageMethod(): SecretService = when (System.getenv("SECRET_STORAGE_METHOD")) { "AZURE" -> AzureSecretService else -> EnvVarSecretService - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/serializers/CsvSerializer.kt b/prime-router/src/main/kotlin/serializers/CsvSerializer.kt index 8823be91a73..a856b8a77bb 100644 --- a/prime-router/src/main/kotlin/serializers/CsvSerializer.kt +++ b/prime-router/src/main/kotlin/serializers/CsvSerializer.kt @@ -48,9 +48,8 @@ class CsvSerializer(val metadata: Metadata) : Logging { val warnings: List, ) - fun readExternal(schemaName: String, input: InputStream, source: Source, sender: Sender? = null): ReadResult { - return readExternal(schemaName, input, listOf(source), sender = sender) - } + fun readExternal(schemaName: String, input: InputStream, source: Source, sender: Sender? = null): ReadResult = + readExternal(schemaName, input, listOf(source), sender = sender) fun readExternal( schemaName: String, @@ -181,8 +180,7 @@ class CsvSerializer(val metadata: Metadata) : Logging { fun buildHeader(): List = schema.csvFields.distinctBy { it.name }.map { it.name } - fun buildRows(): List> { - return report.itemIndices.map { row -> + fun buildRows(): List> = report.itemIndices.map { row -> schema .elements.filterNot { it.csvFields.isNullOrEmpty() } .groupBy { @@ -231,7 +229,6 @@ class CsvSerializer(val metadata: Metadata) : Logging { } } } - } val allRows = listOf(buildHeader()).plus(buildRows()) csvWriter { @@ -245,9 +242,7 @@ class CsvSerializer(val metadata: Metadata) : Logging { fun buildHeader(): List = schema.elements.map { it.name } - fun buildRows(): List> { - return report.itemIndices.map { row -> report.getRow(row) } - } + fun buildRows(): List> = report.itemIndices.map { row -> report.getRow(row) } val allRows = listOf(buildHeader()).plus(buildRows()) csvWriter { @@ -262,9 +257,7 @@ class CsvSerializer(val metadata: Metadata) : Logging { row: Map, actionLogs: ActionLogger, ): CsvMapping { - fun rowContainsAll(fields: List): Boolean { - return fields.find { !row.containsKey(it.name) } == null - } + fun rowContainsAll(fields: List): Boolean = fields.find { !row.containsKey(it.name) } == null val useCsv = schema .elements @@ -280,7 +273,8 @@ class CsvSerializer(val metadata: Metadata) : Logging { val optionalHeaders = schema .filterCsvFields { (it.cardinality == null || it.cardinality == Element.Cardinality.ZERO_OR_ONE) && - it.default == null && it.mapper == null + it.default == null && + it.mapper == null } .map { it.name } .toSet() diff --git a/prime-router/src/main/kotlin/serializers/Hl7Serializer.kt b/prime-router/src/main/kotlin/serializers/Hl7Serializer.kt index 34067679059..3efa5cb4b52 100644 --- a/prime-router/src/main/kotlin/serializers/Hl7Serializer.kt +++ b/prime-router/src/main/kotlin/serializers/Hl7Serializer.kt @@ -77,10 +77,7 @@ class Hl7Serializer( /** * HL7 mapping of all messages in a report submission. */ - data class Hl7Mapping( - val mappedRows: Map>, - val items: List, - ) + data class Hl7Mapping(val mappedRows: Map>, val items: List) /** * Result of one HL7 message. @@ -168,7 +165,8 @@ class Hl7Serializer( */ fun parseStringMessage(message: String) { val parsedMessage = convertMessageToMap(message, messageIndex, schema, sender) - if (parsedMessage.item.isNotEmpty() || parsedMessage.errors.isNotEmpty() || + if (parsedMessage.item.isNotEmpty() || + parsedMessage.errors.isNotEmpty() || parsedMessage.warnings.isNotEmpty() ) { rowResults.add(parsedMessage) @@ -1828,10 +1826,8 @@ class Hl7Serializer( /** * Creates the footers for the report */ - private fun createFooters(report: Report): String { - return "BTS|${report.itemCount}$hl7SegmentDelimiter" + + private fun createFooters(report: Report): String = "BTS|${report.itemCount}$hl7SegmentDelimiter" + "FTS|1$hl7SegmentDelimiter" - } private fun buildComponent(spec: String, component: Int = 1): String { if (!isField(spec)) error("Not a component path spec") @@ -1858,13 +1854,13 @@ class Hl7Serializer( error("Did match on component or subcomponent") } - private fun formatHD(hdFields: Element.HDFields, separator: String = DEFAULT_COMPONENT_SEPARATOR): String { - return if (hdFields.universalId != null && hdFields.universalIdSystem != null) { + private fun formatHD(hdFields: Element.HDFields, separator: String = DEFAULT_COMPONENT_SEPARATOR): String = + if (hdFields.universalId != null && hdFields.universalIdSystem != null + ) { "${hdFields.name}$separator${hdFields.universalId}$separator${hdFields.universalIdSystem}" } else { hdFields.name } - } /** * Coverts unicode string to ASCII string if any special characters are found. @@ -1877,18 +1873,18 @@ class Hl7Serializer( */ internal fun unicodeToAscii( message: String, - ): String { - return AnyAscii.transliterate(message) - } + ): String = AnyAscii.transliterate(message) - private fun formatEI(eiFields: Element.EIFields, separator: String = DEFAULT_COMPONENT_SEPARATOR): String { - return if (eiFields.namespace != null && eiFields.universalId != null && eiFields.universalIdSystem != null) { + private fun formatEI(eiFields: Element.EIFields, separator: String = DEFAULT_COMPONENT_SEPARATOR): String = + if (eiFields.namespace != null && + eiFields.universalId != null && + eiFields.universalIdSystem != null + ) { "${eiFields.name}$separator${eiFields.namespace}" + "$separator${eiFields.universalId}$separator${eiFields.universalIdSystem}" } else { eiFields.name } - } /** * Get a phone number from an XTN (e.g. phone number) field of an HL7 message. @@ -2105,26 +2101,22 @@ class Hl7Serializer( * @param value is the value to search for * @return a bool indicating is 1 or more rows were identified after filtering on params */ - fun checkLIVDValueExists(column: String, value: String): Boolean { - return if (livdLookupTable.value.hasColumn(column)) { + fun checkLIVDValueExists(column: String, value: String): Boolean = if (livdLookupTable.value.hasColumn(column)) { val rowCount = livdLookupTable.value.FilterBuilder().equalsIgnoreCase(column, value).filter().rowCount rowCount > 0 } else { false } - } /** * Gets the HAPI Terser spec from the provided [hl7Field] string. * @returns the HAPI Terser spec */ - internal fun getTerserSpec(hl7Field: String): String { - return if (hl7Field.isNotBlank() && hl7Field.startsWith("MSH")) { + internal fun getTerserSpec(hl7Field: String): String = if (hl7Field.isNotBlank() && hl7Field.startsWith("MSH")) { "/$hl7Field" } else { "/.$hl7Field" } - } companion object { const val HL7_SPEC_VERSION: String = "2.5.1" @@ -2339,8 +2331,7 @@ class HL7HapiErrorProcessor : Logging { fun getExceptionActionMessage( exception: HL7Exception, schema: Schema, - ): ItemActionLogDetail { - return when (exception) { + ): ItemActionLogDetail = when (exception) { is EncodingNotSupportedException -> InvalidHL7Message( "Invalid HL7 message format. Please " + @@ -2363,7 +2354,6 @@ class HL7HapiErrorProcessor : Logging { ) } } - } /** * Attempts to find a parsing [ErrorCode] matching the given [elementType]. Returns UNKNOWN error code if a matching @@ -2384,9 +2374,7 @@ class HL7HapiErrorProcessor : Logging { * Converts a field as it appears in a HAPI exception message to a format ReportStream schemas expect * @param field the field to convert */ - private fun getCleanedField(field: String): String { - return field.replaceFirst("(", "-") + private fun getCleanedField(field: String): String = field.replaceFirst("(", "-") .replace(")", "") .removeSuffix("-0") - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/serializers/SoapSerializer.kt b/prime-router/src/main/kotlin/serializers/SoapSerializer.kt index fe5b2d247f9..e36c0592fb4 100644 --- a/prime-router/src/main/kotlin/serializers/SoapSerializer.kt +++ b/prime-router/src/main/kotlin/serializers/SoapSerializer.kt @@ -66,9 +66,7 @@ class SoapEnvelope( * The SOAP serializer that takes our [SoapEnvelope] and turns it into a proper SOAP object that can be sent * to our endpoint */ -class SoapSerializer( - private val envelope: Class?, -) : StdSerializer(envelope) { +class SoapSerializer(private val envelope: Class?) : StdSerializer(envelope) { // Jackson Mapper requires this even if we don't use it in our code constructor() : this(null) diff --git a/prime-router/src/main/kotlin/tokens/AuthUtils.kt b/prime-router/src/main/kotlin/tokens/AuthUtils.kt index 66cf3fa159d..3dd2df595b7 100644 --- a/prime-router/src/main/kotlin/tokens/AuthUtils.kt +++ b/prime-router/src/main/kotlin/tokens/AuthUtils.kt @@ -67,33 +67,33 @@ class AuthUtils { keyId: String, expirationSecondsFromNow: Int = 300, jti: String? = UUID.randomUUID().toString(), - ): String { - return generateToken(organization.name, baseUrl, privateKey, keyId, jti, expirationSecondsFromNow) - } + ): String = generateToken(organization.name, baseUrl, privateKey, keyId, jti, expirationSecondsFromNow) /** * [organizationToken] is a signed JWT from this organization, to go to the api/token endpoint. * [scope] is the desired scope being requested. See [Scope] for details on format. * @return a map of the standard parameters needed to create an acceptable token request. */ - private fun generateOrganizationUrlParameterMap(organizationToken: String, scope: String): Map { - return mapOf( + private fun generateOrganizationUrlParameterMap( + organizationToken: String, + scope: String, + ): Map = mapOf( "scope" to scope, "grant_type" to "client_credentials", "client_assertion_type" to "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_assertion" to organizationToken, ) - } /** * [organizationToken] is a signed JWT from this organization, to go to the api/token endpoint. * [scope] is the desired scope being requested. See [Scope] for details on format. * @return a string of the standard parameters needed to create an acceptable token request. */ - fun generateOrganizationUrlParameterString(organizationToken: String, scope: String): String { - return generateOrganizationUrlParameterMap(organizationToken, scope) + fun generateOrganizationUrlParameterString( + organizationToken: String, + scope: String, + ): String = generateOrganizationUrlParameterMap(organizationToken, scope) .map { "${it.key}=${it.value}" }.joinToString("&") - } fun readPublicKeyPemFile(pemFile: File): Jwk { if (!pemFile.exists()) error("Cannot file file ${pemFile.absolutePath}") diff --git a/prime-router/src/main/kotlin/tokens/AuthenticatedClaims.kt b/prime-router/src/main/kotlin/tokens/AuthenticatedClaims.kt index ef2ee3db2d0..84dc0d3c329 100644 --- a/prime-router/src/main/kotlin/tokens/AuthenticatedClaims.kt +++ b/prime-router/src/main/kotlin/tokens/AuthenticatedClaims.kt @@ -96,9 +96,7 @@ class AuthenticatedClaims : Logging { fun authorizedForSendOrReceive( requiredSender: Sender, request: HttpRequestMessage, - ): Boolean { - return authorizedForSendOrReceive(requiredSender.organizationName, requiredSender.name, request) - } + ): Boolean = authorizedForSendOrReceive(requiredSender.organizationName, requiredSender.name, request) /** * Determine if these claims authorize access to submission related resources in the @@ -151,9 +149,7 @@ class AuthenticatedClaims : Logging { /** * @return true if these claims authorize access to the [requiredScopes]. False if unauthorized. */ - fun authorized(requiredScopes: Set): Boolean { - return Scope.authorized(this.scopes, requiredScopes) - } + fun authorized(requiredScopes: Set): Boolean = Scope.authorized(this.scopes, requiredScopes) companion object : Logging { /** @@ -162,8 +158,7 @@ class AuthenticatedClaims : Logging { * Even if local, if the [accessToken] is there, then do real Okta auth. * @return true if we should do 'local' auth, false if we should do Okta auth. */ - fun isLocal(accessToken: String?): Boolean { - return when { + fun isLocal(accessToken: String?): Boolean = when { (!Environment.isLocal()) -> false (accessToken != null && accessToken.split(".").size == 3) -> { // For testing auth. Running local, but test using the real production parser. @@ -175,7 +170,6 @@ class AuthenticatedClaims : Logging { else -> true } - } /** * Utility function to extract and @return the bearer access token from the [request] Authorization header, diff --git a/prime-router/src/main/kotlin/tokens/DatabaseJtiCache.kt b/prime-router/src/main/kotlin/tokens/DatabaseJtiCache.kt index d1df58b13ab..242d473727c 100644 --- a/prime-router/src/main/kotlin/tokens/DatabaseJtiCache.kt +++ b/prime-router/src/main/kotlin/tokens/DatabaseJtiCache.kt @@ -15,9 +15,7 @@ class DatabaseJtiCache(val db: DatabaseAccess) : JtiCache() { db.transact { txn -> db.insertJti(jti, expiresAt, txn) } } - override fun isPresentInCache(jti: String): Boolean { - return db.transactReturning { txn -> + override fun isPresentInCache(jti: String): Boolean = db.transactReturning { txn -> (db.fetchJti(jti, txn) != null) } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/tokens/Jwk.kt b/prime-router/src/main/kotlin/tokens/Jwk.kt index cb508030616..2e799b9bbda 100644 --- a/prime-router/src/main/kotlin/tokens/Jwk.kt +++ b/prime-router/src/main/kotlin/tokens/Jwk.kt @@ -77,21 +77,13 @@ class Jwk( } companion object { - fun generateECPublicKey(jwkString: String): ECPublicKey { - return ECKey.parse(jwkString).toECPublicKey() - } + fun generateECPublicKey(jwkString: String): ECPublicKey = ECKey.parse(jwkString).toECPublicKey() - fun generateECPrivateKey(jwkString: String): ECPrivateKey { - return ECKey.parse(jwkString).toECPrivateKey() - } + fun generateECPrivateKey(jwkString: String): ECPrivateKey = ECKey.parse(jwkString).toECPrivateKey() - fun generateRSAPublicKey(jwkString: String): RSAPublicKey { - return RSAKey.parse(jwkString).toRSAPublicKey() - } + fun generateRSAPublicKey(jwkString: String): RSAPublicKey = RSAKey.parse(jwkString).toRSAPublicKey() - fun generateRSAPrivateKey(jwkString: String): RSAPrivateKey { - return RSAKey.parse(jwkString).toRSAPrivateKey() - } + fun generateRSAPrivateKey(jwkString: String): RSAPrivateKey = RSAKey.parse(jwkString).toRSAPrivateKey() } } @@ -107,9 +99,7 @@ data class JwkSet( // overlapping key rotation val keys: List, ) { - fun filterByKid(kid: String): List { - return keys.filter { !it.kid.isNullOrEmpty() && kid == it.kid } - } + fun filterByKid(kid: String): List = keys.filter { !it.kid.isNullOrEmpty() && kid == it.kid } companion object { @@ -120,9 +110,7 @@ data class JwkSet( * * @return the maximum number of keys can be configured for a scope */ - fun getMaximumNumberOfKeysPerScope(): Int { - return (System.getenv("MAX_NUM_KEY_PER_SCOPE") ?: "10").toInt() - } + fun getMaximumNumberOfKeysPerScope(): Int = (System.getenv("MAX_NUM_KEY_PER_SCOPE") ?: "10").toInt() /** * Copy an old set of authorizations to a new set, and add one to it, if needed. diff --git a/prime-router/src/main/kotlin/tokens/MemoryJtiCache.kt b/prime-router/src/main/kotlin/tokens/MemoryJtiCache.kt index e8279a228e4..b61e411ebba 100644 --- a/prime-router/src/main/kotlin/tokens/MemoryJtiCache.kt +++ b/prime-router/src/main/kotlin/tokens/MemoryJtiCache.kt @@ -20,7 +20,5 @@ class MemoryJtiCache : JtiCache() { cache[jti] = expiresAt } - override fun isPresentInCache(jti: String): Boolean { - return cache[jti] != null - } + override fun isPresentInCache(jti: String): Boolean = cache[jti] != null } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/tokens/ReportStreamSecretFinder.kt b/prime-router/src/main/kotlin/tokens/ReportStreamSecretFinder.kt index e4463e8d628..240a6bda2eb 100644 --- a/prime-router/src/main/kotlin/tokens/ReportStreamSecretFinder.kt +++ b/prime-router/src/main/kotlin/tokens/ReportStreamSecretFinder.kt @@ -15,9 +15,7 @@ interface ReportStreamSecretFinder { private val TOKEN_SIGNING_KEY_ALGORITHM = SignatureAlgorithm.HS384 // convenience method that knows how to generate the right kind of secret. - fun generateSecret(): String { - return Encoders.BASE64.encode(Keys.secretKeyFor(TOKEN_SIGNING_KEY_ALGORITHM).encoded) - } + fun generateSecret(): String = Encoders.BASE64.encode(Keys.secretKeyFor(TOKEN_SIGNING_KEY_ALGORITHM).encoded) } } diff --git a/prime-router/src/main/kotlin/tokens/Scope.kt b/prime-router/src/main/kotlin/tokens/Scope.kt index b53bd56da61..80cde3e7115 100644 --- a/prime-router/src/main/kotlin/tokens/Scope.kt +++ b/prime-router/src/main/kotlin/tokens/Scope.kt @@ -42,18 +42,14 @@ class Scope { * Convert a string representation of a DetailedScope to a DetailedScope obj. * @return the matching DetailedScope, or null if no such DetailedScope exists. */ - fun toDetailedScope(strRep: String): DetailedScope? { - return values().find { it.strRep == strRep } - } + fun toDetailedScope(strRep: String): DetailedScope? = values().find { it.strRep == strRep } } } /** * Return true if scope is the PrimeAdmin scope */ - fun isPrimeAdmin(scope: String?): Boolean { - return scope == primeAdminScope - } + fun isPrimeAdmin(scope: String?): Boolean = scope == primeAdminScope /** * @return true if this [scope] syntax is correct. Otherwise false. @@ -126,9 +122,12 @@ class Scope { * * @return the (possibly empty) intersection of the two sets. Empty Set == not authorized for anything. */ - internal fun authorizedScopes(userScopes: Set, requiredScopes: Set): Set { - return userScopes.filter { it.isNotBlank() }.intersect(requiredScopes.filter { it.isNotBlank() }.toSet()) - } + internal fun authorizedScopes( + userScopes: Set, + requiredScopes: Set, + ): Set = userScopes.filter { + it.isNotBlank() + }.intersect(requiredScopes.filter { it.isNotBlank() }.toSet()) /** * @return true if there is at least one match between the set of scopes the user claims ([userScopeList]) diff --git a/prime-router/src/main/kotlin/tokens/Server2ServerAuthentication.kt b/prime-router/src/main/kotlin/tokens/Server2ServerAuthentication.kt index 201e15d3277..28bd1ce4248 100644 --- a/prime-router/src/main/kotlin/tokens/Server2ServerAuthentication.kt +++ b/prime-router/src/main/kotlin/tokens/Server2ServerAuthentication.kt @@ -259,9 +259,9 @@ class Server2ServerAuthentication(val workflowEngine: WorkflowEngine) : Logging * * @return the ReportStream AuthenticatedClaims obj if authentication was successful. Otherwise returns null. */ - fun authenticate(accessToken: String): AuthenticatedClaims? { - return authenticate(accessToken, FindReportStreamSecretInVault()) - } + fun authenticate( + accessToken: String, + ): AuthenticatedClaims? = authenticate(accessToken, FindReportStreamSecretInVault()) /** * This confirms that [accessToken] is properly unexpired and signed by that secret. This does not do authorization, diff --git a/prime-router/src/main/kotlin/tokens/Server2ServerAuthenticationException.kt b/prime-router/src/main/kotlin/tokens/Server2ServerAuthenticationException.kt index c21a95dfcac..030d7fef25c 100644 --- a/prime-router/src/main/kotlin/tokens/Server2ServerAuthenticationException.kt +++ b/prime-router/src/main/kotlin/tokens/Server2ServerAuthenticationException.kt @@ -53,8 +53,7 @@ class Server2ServerAuthenticationException( val server2ServerError: Server2ServerError, val scope: String, val iss: String? = null, -) : - Exception() { +) : Exception() { override fun getLocalizedMessage(): String { val message = "${server2ServerError.name} while generating token for scope: $scope" if (iss != null) { diff --git a/prime-router/src/main/kotlin/transport/AS2Transport.kt b/prime-router/src/main/kotlin/transport/AS2Transport.kt index b79e2f2d444..6dfe063aaf3 100644 --- a/prime-router/src/main/kotlin/transport/AS2Transport.kt +++ b/prime-router/src/main/kotlin/transport/AS2Transport.kt @@ -38,7 +38,9 @@ import java.util.concurrent.TimeUnit * See [Wikapedia AS2](https://en.wikipedia.org/wiki/AS2) for details on AS2. * See [PHAX as2-lib](https://github.com/phax/as2-lib) for the details on the library we use. */ -class AS2Transport(val metadata: Metadata? = null) : ITransport, Logging { +class AS2Transport(val metadata: Metadata? = null) : + ITransport, + Logging { /** * The send a report or return [RetryItems] */ @@ -167,8 +169,7 @@ class AS2Transport(val metadata: Metadata? = null) : ITransport, Logging { /** * Look at the [ex] exception and determine if the error is possibly transient and it is worth retrying. */ - private fun isErrorTransient(ex: Throwable): Boolean { - return when { + private fun isErrorTransient(ex: Throwable): Boolean = when { // Connection to service is down, possibly a service down or under load situation ex is WrappedAS2Exception && ex.cause is ConnectException -> true ex is WrappedAS2Exception && ex.cause is ConnectTimeoutException -> true @@ -177,5 +178,4 @@ class AS2Transport(val metadata: Metadata? = null) : ITransport, Logging { // Assume everything else is not transient else -> false } - } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/transport/GAENTransport.kt b/prime-router/src/main/kotlin/transport/GAENTransport.kt index a5abfbdd829..e6e475e8740 100644 --- a/prime-router/src/main/kotlin/transport/GAENTransport.kt +++ b/prime-router/src/main/kotlin/transport/GAENTransport.kt @@ -43,7 +43,9 @@ import java.io.ByteArrayInputStream * See [Google API for Exposure Notification](https://https://developers.google.com/android/exposure-notifications/exposure-notifications-api) for details on GAEN. * See [Issue API](https://https://github.com/google/exposure-notifications-verification-server/blob/main/docs/api.md#apiissue) for the details on the issue API ReportStream calls. */ -class GAENTransport(val httpClient: HttpClient? = null) : ITransport, Logging { +class GAENTransport(val httpClient: HttpClient? = null) : + ITransport, + Logging { /** * Information helpful for the sending, logging and recording history all bundled together * to avoid long parameter lists in functions diff --git a/prime-router/src/main/kotlin/transport/RESTTransport.kt b/prime-router/src/main/kotlin/transport/RESTTransport.kt index 232689792df..748f84adab2 100644 --- a/prime-router/src/main/kotlin/transport/RESTTransport.kt +++ b/prime-router/src/main/kotlin/transport/RESTTransport.kt @@ -293,15 +293,16 @@ class RESTTransport(private val httpClient: HttpClient? = null) : ITransport { * @param [restTransportInfo] holds receiver-specific Rest parameters * @param [reportId] report id to be added as header */ - fun getHeaders(restTransportInfo: RESTTransportType, reportId: String): MutableMap { - return restTransportInfo.headers.mapValues { + fun getHeaders( + restTransportInfo: RESTTransportType, + reportId: String, + ): MutableMap = restTransportInfo.headers.mapValues { if (it.value == "header.reportFile.reportId") { reportId } else { it.value } }.toMutableMap() - } /** * Get the Accesstoken based on authType given in Restransport header @@ -634,17 +635,16 @@ class RESTTransport(private val httpClient: HttpClient? = null) : ITransport { private const val TIMEOUT = 120_000 /** Get Authentication header. It is hear to ease Unit Test */ - fun getAuthorizationHeader(restTransportInfo: RESTTransportType?): String { - return restTransportInfo?.headers?.get("BearerToken") ?: "Bearer" - } + fun getAuthorizationHeader( + restTransportInfo: RESTTransportType?, + ): String = restTransportInfo?.headers?.get("BearerToken") ?: "Bearer" /** Our default Http Client, with an optional SSL context, and optional auth token */ fun createDefaultHttpClient( jks: UserJksCredential?, accessToken: String?, restTransportInfo: RESTTransportType?, - ): HttpClient { - return HttpClient(Apache) { + ): HttpClient = HttpClient(Apache) { // installs logging into the call to post to the server install(Logging) { logger = io.ktor.client.plugins.logging.Logger.Companion.SIMPLE @@ -680,7 +680,6 @@ class RESTTransport(private val httpClient: HttpClient? = null) : ITransport { } } } - } /*** * Create an SSL context with the provided cert diff --git a/prime-router/src/main/kotlin/transport/RetryToken.kt b/prime-router/src/main/kotlin/transport/RetryToken.kt index ce8c80a4285..4521f57c97c 100644 --- a/prime-router/src/main/kotlin/transport/RetryToken.kt +++ b/prime-router/src/main/kotlin/transport/RetryToken.kt @@ -6,13 +6,8 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule typealias RetryItems = List -data class RetryToken( - var retryCount: Int, - val items: List, -) { - fun toJSON(): String { - return mapper.writeValueAsString(this) - } +data class RetryToken(var retryCount: Int, val items: List) { + fun toJSON(): String = mapper.writeValueAsString(this) companion object { private val mapper = ObjectMapper().registerModule( diff --git a/prime-router/src/main/kotlin/transport/SftpTransport.kt b/prime-router/src/main/kotlin/transport/SftpTransport.kt index 266960e2dd9..ac94bd8440c 100644 --- a/prime-router/src/main/kotlin/transport/SftpTransport.kt +++ b/prime-router/src/main/kotlin/transport/SftpTransport.kt @@ -38,7 +38,9 @@ import org.apache.logging.log4j.kotlin.Logging import java.io.InputStream import java.io.StringReader -class SftpTransport : ITransport, Logging { +class SftpTransport : + ITransport, + Logging { override fun send( transportType: TransportType, @@ -222,9 +224,7 @@ class SftpTransport : ITransport, Logging { } } - fun ls(sshClient: SSHClient, path: String): List { - return ls(sshClient, path, null) - } + fun ls(sshClient: SSHClient, path: String): List = ls(sshClient, path, null) fun ls(sshClient: SSHClient, path: String, resourceFilter: RemoteResourceFilter?): List { val lsResults = mutableListOf() @@ -292,33 +292,20 @@ class SftpTransport : ITransport, Logging { return pwd } - private fun makeSourceFile(contents: ByteArray, fileName: String): LocalSourceFile { - return object : InMemorySourceFile() { - override fun getName(): String { - return fileName - } + private fun makeSourceFile(contents: ByteArray, fileName: String): LocalSourceFile = + object : InMemorySourceFile() { + override fun getName(): String = fileName - override fun getLength(): Long { - return contents.size.toLong() - } + override fun getLength(): Long = contents.size.toLong() - override fun getInputStream(): InputStream { - return contents.inputStream() - } + override fun getInputStream(): InputStream = contents.inputStream() - override fun isDirectory(): Boolean { - return false - } + override fun isDirectory(): Boolean = false - override fun isFile(): Boolean { - return true - } + override fun isFile(): Boolean = true - override fun getPermissions(): Int { - return 777 - } + override fun getPermissions(): Int = 777 } - } // allow us to mock SSHClient because there is no dependency injection in this class fun createDefaultSSHClient(): SSHClient { diff --git a/prime-router/src/main/kotlin/transport/SoapTransport.kt b/prime-router/src/main/kotlin/transport/SoapTransport.kt index ec771d556e5..6c9c14d3527 100644 --- a/prime-router/src/main/kotlin/transport/SoapTransport.kt +++ b/prime-router/src/main/kotlin/transport/SoapTransport.kt @@ -316,8 +316,7 @@ class SoapTransport(private val httpClient: HttpClient? = null) : ITransport { private const val TIMEOUT = 50_000 /** Our default Http Client, with an optional SSL context */ - private fun createDefaultHttpClient(jks: UserJksCredential?): HttpClient { - return HttpClient(Apache) { + private fun createDefaultHttpClient(jks: UserJksCredential?): HttpClient = HttpClient(Apache) { // installs logging into the call to post to the server install(Logging) { logger = io.ktor.client.plugins.logging.Logger.Companion.SIMPLE @@ -334,7 +333,6 @@ class SoapTransport(private val httpClient: HttpClient? = null) : ITransport { } } } - } /*** * Create an SSL context with the provided cert diff --git a/prime-router/src/main/kotlin/validation/IItemValidator.kt b/prime-router/src/main/kotlin/validation/IItemValidator.kt index 3656becb726..cb88625f006 100644 --- a/prime-router/src/main/kotlin/validation/IItemValidator.kt +++ b/prime-router/src/main/kotlin/validation/IItemValidator.kt @@ -48,16 +48,12 @@ data class HL7ValidationResult(val rawReport: Report) : IItemValidationResult { } data class FHIRValidationResult(val rawValidationResult: ValidationResult) : IItemValidationResult { - override fun isValid(): Boolean { - return rawValidationResult.isSuccessful - } + override fun isValid(): Boolean = rawValidationResult.isSuccessful - override fun getErrorsMessage(validator: IItemValidator): String { - return rawValidationResult + override fun getErrorsMessage(validator: IItemValidator): String = rawValidationResult .messages .filter { it.severity == ResultSeverityEnum.ERROR || it.severity == ResultSeverityEnum.FATAL } .joinToString { it.message } - } } abstract class AbstractItemValidator : IItemValidator { @@ -67,8 +63,7 @@ abstract class AbstractItemValidator : IItemValidator { private val hl7Validators = mutableMapOf() private val classLoader = this::class.java.classLoader - fun getHL7Validator(profileLocation: String): SyncHL7Validator { - return hl7Validators.getOrElse(profileLocation) { + fun getHL7Validator(profileLocation: String): SyncHL7Validator = hl7Validators.getOrElse(profileLocation) { ( classLoader.getResourceAsStream("$profileLocation/profile.xml") ?: throw RuntimeException("profile.xml does not exist") @@ -107,7 +102,6 @@ abstract class AbstractItemValidator : IItemValidator { validator } } - } } open val hl7ConformanceProfileLocation: String? = null diff --git a/prime-router/src/main/kotlin/validation/MarsOtcElrOnboardingValidator.kt b/prime-router/src/main/kotlin/validation/MarsOtcElrOnboardingValidator.kt index 2e75fcf60a9..155f6010662 100644 --- a/prime-router/src/main/kotlin/validation/MarsOtcElrOnboardingValidator.kt +++ b/prime-router/src/main/kotlin/validation/MarsOtcElrOnboardingValidator.kt @@ -6,9 +6,7 @@ class MarsOtcElrOnboardingValidator : AbstractItemValidator() { override val hl7ConformanceProfileLocation: String = "metadata/hl7_validation/v251/radxmars/onboarding" - override fun validateFHIR(bundle: Bundle): IItemValidationResult { - return NoopItemValidationResult() - } + override fun validateFHIR(bundle: Bundle): IItemValidationResult = NoopItemValidationResult() override val validatorProfileName: String = "RADx MARS Onboarding" } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/validation/MarsOtcElrValidator.kt b/prime-router/src/main/kotlin/validation/MarsOtcElrValidator.kt index 5717aae3fef..6145672ef53 100644 --- a/prime-router/src/main/kotlin/validation/MarsOtcElrValidator.kt +++ b/prime-router/src/main/kotlin/validation/MarsOtcElrValidator.kt @@ -6,9 +6,7 @@ class MarsOtcElrValidator : AbstractItemValidator() { override val hl7ConformanceProfileLocation: String = "metadata/hl7_validation/v251/radxmars/production" - override fun validateFHIR(bundle: Bundle): IItemValidationResult { - return NoopItemValidationResult() - } + override fun validateFHIR(bundle: Bundle): IItemValidationResult = NoopItemValidationResult() override val validatorProfileName: String = "RADx MARS" } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/validation/NoopItemValidator.kt b/prime-router/src/main/kotlin/validation/NoopItemValidator.kt index 26261ddb292..4dc0c53b1a0 100644 --- a/prime-router/src/main/kotlin/validation/NoopItemValidator.kt +++ b/prime-router/src/main/kotlin/validation/NoopItemValidator.kt @@ -1,20 +1,14 @@ package gov.cdc.prime.router.validation class NoopItemValidationResult : IItemValidationResult { - override fun isValid(): Boolean { - return true - } + override fun isValid(): Boolean = true - override fun getErrorsMessage(validator: IItemValidator): String { - return "" - } + override fun getErrorsMessage(validator: IItemValidator): String = "" } class NoopItemValidator : AbstractItemValidator() { - override fun validate(message: Any): IItemValidationResult { - return NoopItemValidationResult() - } + override fun validate(message: Any): IItemValidationResult = NoopItemValidationResult() override val validatorProfileName: String = "NOOP" } \ No newline at end of file diff --git a/prime-router/src/main/resources/db/migration/V72__add_receiver_enrichment_task.sql b/prime-router/src/main/resources/db/migration/V72__add_receiver_enrichment_task.sql new file mode 100644 index 00000000000..b7f3698dfc9 --- /dev/null +++ b/prime-router/src/main/resources/db/migration/V72__add_receiver_enrichment_task.sql @@ -0,0 +1,14 @@ +/* + * The Flyway tool applies this migration to create the database. + * + * Follow this style guide https://about.gitlab.com/handbook/business-ops/data-team/platform/sql-style-guide/ + * use VARCHAR(63) for names in organization and schema + * + * Copy a version of this comment into the next migration + * + */ + +/* + * Add receiver-enrichment enum type to underlying custom data types. + */ +ALTER TYPE public.task_action ADD VALUE 'receiver-enrichment' after 'destination-filter'; diff --git a/prime-router/src/main/resources/db/migration/V73__add_receiver_enriched_at.sql b/prime-router/src/main/resources/db/migration/V73__add_receiver_enriched_at.sql new file mode 100644 index 00000000000..f928ea3ab4c --- /dev/null +++ b/prime-router/src/main/resources/db/migration/V73__add_receiver_enriched_at.sql @@ -0,0 +1,15 @@ +/* + * The Flyway tool applies this migration to create the database. + * + * Follow this style guide https://about.gitlab.com/handbook/business-ops/data-team/platform/sql-style-guide/ + * use VARCHAR(63) for names in organization and schema + * + * Copy a version of this comment into the next migration + * + */ + +/* + * Adds 'receiver_enriched_at' to the task table. + */ + ALTER TABLE task ADD receiver_enriched_at timestamp with time zone +; \ No newline at end of file diff --git a/prime-router/src/main/resources/metadata/fhir_transforms/receivers/fhir-message-header-sample.yml b/prime-router/src/main/resources/metadata/fhir_transforms/receivers/fhir-message-header-sample.yml new file mode 100644 index 00000000000..36fb76f4aaa --- /dev/null +++ b/prime-router/src/main/resources/metadata/fhir_transforms/receivers/fhir-message-header-sample.yml @@ -0,0 +1,23 @@ +# $schema: ../../../../../../metadata/json_schema/fhir/fhir-to-fhir-transform.json +constants: + rsext: '"https://reportstream.cdc.gov/fhir/StructureDefinition/"' + +elements: + ######################### + # ReportStream specific # + ######################### + - name: software-vendor-name + condition: 'true' + resource: 'Bundle.entry.resource.ofType(Provenance).where(entity.exists()).entity.what.resolve().where(extension(%`rsext-software-vendor-org`).exists())' + bundleProperty: '%resource.extension(%`rsext-software-vendor-org`).value.resolve().name' + value: [ '"Orange Software Vendor Name"' ] + + - name: software-name + resource: 'Bundle.entry.resource.ofType(Provenance).where(entity.exists()).entity.what.resolve().where(extension(%`rsext-software-vendor-org`).exists())' + bundleProperty: '%resource.deviceName.name' + value: [ '"Purple PRIME ReportStream"' ] + + - name: software-version + resource: 'Bundle.entry.resource.ofType(Provenance).where(entity.exists()).entity.what.resolve().where(extension(%`rsext-software-vendor-org`).exists())' + bundleProperty: '%resource.version.value' + value: [ '"0.2-YELLOW"' ] \ No newline at end of file diff --git a/prime-router/src/test/kotlin/ElementTests.kt b/prime-router/src/test/kotlin/ElementTests.kt index f04b4b9dfcf..ebae0f11d59 100644 --- a/prime-router/src/test/kotlin/ElementTests.kt +++ b/prime-router/src/test/kotlin/ElementTests.kt @@ -1130,17 +1130,14 @@ internal class ElementTests { class SomeCoolMapper : Mapper { override val name = "some" - override fun valueNames(element: Element, args: List): List { - return args - } + override fun valueNames(element: Element, args: List): List = args override fun apply( element: Element, args: List, values: List, sender: Sender?, - ): ElementResult { - return if (args.isEmpty()) { + ): ElementResult = if (args.isEmpty()) { ElementResult(null) } else { when (args[0]) { @@ -1155,7 +1152,6 @@ internal class ElementTests { else -> throw UnsupportedOperationException() } } - } } val elementA = Element("a", mapperRef = SomeCoolMapper()) diff --git a/prime-router/src/test/kotlin/SettingsProviderTest.kt b/prime-router/src/test/kotlin/SettingsProviderTest.kt index 6553e6aa463..3995d5ec8fb 100644 --- a/prime-router/src/test/kotlin/SettingsProviderTest.kt +++ b/prime-router/src/test/kotlin/SettingsProviderTest.kt @@ -56,20 +56,13 @@ class SettingsProviderTest { override val receivers: Collection, ) : SettingsProvider { - override fun findOrganization(name: String): Organization? { - return organizations.find { org -> org.name == name } - } + override fun findOrganization(name: String): Organization? = organizations.find { org -> org.name == name } - override fun findReceiver(fullName: String): Receiver? { - throw NotImplementedError() - } + override fun findReceiver(fullName: String): Receiver? = throw NotImplementedError() - override fun findSender(fullName: String): Sender? { - return senders.find { sender -> sender.fullName == fullName } - } + override fun findSender(fullName: String): Sender? = senders.find { sender -> sender.fullName == fullName } - override fun findOrganizationAndReceiver(fullName: String): Pair? { + override fun findOrganizationAndReceiver(fullName: String): Pair? = throw NotImplementedError() - } } } \ No newline at end of file diff --git a/prime-router/src/test/kotlin/SubmissionReceiverTests.kt b/prime-router/src/test/kotlin/SubmissionReceiverTests.kt index 60e68bc44ba..bcfd859ebc2 100644 --- a/prime-router/src/test/kotlin/SubmissionReceiverTests.kt +++ b/prime-router/src/test/kotlin/SubmissionReceiverTests.kt @@ -299,12 +299,10 @@ class SubmissionReceiverTests { "42&ISO||445297001^Swab of internal nose^SCT^^^^2.67||||53342003^Internal nose structure (body structure)^" + "SCT^^^^2020-09-01|||||||||202108020000-0500|20210802000006.0000-0500" - private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine { - return spyk( + private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine = spyk( WorkflowEngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) .blobAccess(blobMock).queueAccess(queueMock).build() ) - } @BeforeEach fun reset() { diff --git a/prime-router/src/test/kotlin/azure/ApiKeysFunctionsTest.kt b/prime-router/src/test/kotlin/azure/ApiKeysFunctionsTest.kt index 7bb05e6b677..d26a94a669e 100644 --- a/prime-router/src/test/kotlin/azure/ApiKeysFunctionsTest.kt +++ b/prime-router/src/test/kotlin/azure/ApiKeysFunctionsTest.kt @@ -788,7 +788,7 @@ class ApiKeysFunctionsTest { } @Nested - inner class V1() { + inner class V1 { @Test fun `Test get keys`() { settings.organizationStore.put( diff --git a/prime-router/src/test/kotlin/azure/ApiSearchTest.kt b/prime-router/src/test/kotlin/azure/ApiSearchTest.kt index 91b9dda7135..9ca102ae652 100644 --- a/prime-router/src/test/kotlin/azure/ApiSearchTest.kt +++ b/prime-router/src/test/kotlin/azure/ApiSearchTest.kt @@ -29,9 +29,7 @@ class ApiSearchTest { val TEST = TestTable() } - override fun getRecordType(): Class { - return TestRecord::class.java - } + override fun getRecordType(): Class = TestRecord::class.java } class TestRecord : CustomRecord(TestTable.TEST) @@ -60,21 +58,14 @@ class ApiSearchTest { override val sortDirection: SortDirection = SortDirection.DESC, page: Int = 1, limit: Int = 25, - ) : - ApiSearch>(TestPojo::class.java, page, limit) { - override fun getCondition(filter: TestApiFilter<*>): Condition { - return when (filter) { + ) : ApiSearch>(TestPojo::class.java, page, limit) { + override fun getCondition(filter: TestApiFilter<*>): Condition = when (filter) { is TestApiFilter.FooFilter -> filter.tableField.eq(filter.value) } - } - override fun getSortColumn(): Field<*> { - return sortParameter ?: TestTable.TEST.CREATED_AT - } + override fun getSortColumn(): Field<*> = sortParameter ?: TestTable.TEST.CREATED_AT - override fun getPrimarySortColumn(): Field<*> { - return TestTable.TEST.FOO - } + override fun getPrimarySortColumn(): Field<*> = TestTable.TEST.FOO companion object : ApiSearchParser>() { override fun parseRawApiSearch(rawApiSearch: RawApiSearch): TestApiSearch { diff --git a/prime-router/src/test/kotlin/azure/HttpTestUtils.kt b/prime-router/src/test/kotlin/azure/HttpTestUtils.kt index b171a3763bb..b23cf143102 100644 --- a/prime-router/src/test/kotlin/azure/HttpTestUtils.kt +++ b/prime-router/src/test/kotlin/azure/HttpTestUtils.kt @@ -12,22 +12,18 @@ import gov.cdc.prime.router.SettingsProvider import java.net.URI import kotlin.collections.Map -class MockHttpResponseMessage : HttpResponseMessage.Builder, HttpResponseMessage { +class MockHttpResponseMessage : + HttpResponseMessage.Builder, + HttpResponseMessage { var httpStatus: HttpStatusType = HttpStatus.OK var content: Any? = null var headers: MutableMap = mutableMapOf() - override fun getStatus(): HttpStatusType { - return this.httpStatus - } + override fun getStatus(): HttpStatusType = this.httpStatus - override fun getHeader(var1: String): String { - return headers.getOrDefault(var1, "world") - } + override fun getHeader(var1: String): String = headers.getOrDefault(var1, "world") - override fun getBody(): Any? { - return this.content - } + override fun getBody(): Any? = this.content override fun status(status: HttpStatusType): HttpResponseMessage.Builder { this.httpStatus = status @@ -44,45 +40,29 @@ class MockHttpResponseMessage : HttpResponseMessage.Builder, HttpResponseMessage return this } - override fun build(): HttpResponseMessage { - return this - } + override fun build(): HttpResponseMessage = this } -class MockHttpRequestMessage( - val content: String? = null, - val method: HttpMethod = HttpMethod.GET, -) : HttpRequestMessage { +class MockHttpRequestMessage(val content: String? = null, val method: HttpMethod = HttpMethod.GET) : + HttpRequestMessage { val httpHeaders = mutableMapOf() val parameters = mutableMapOf() - override fun getUri(): URI { - return URI.create("http://localhost/") - } + override fun getUri(): URI = URI.create("http://localhost/") - override fun getHttpMethod(): HttpMethod { - return method - } + override fun getHttpMethod(): HttpMethod = method - override fun getHeaders(): Map { - return this.httpHeaders - } + override fun getHeaders(): Map = this.httpHeaders - override fun getQueryParameters(): MutableMap { - return this.parameters - } + override fun getQueryParameters(): MutableMap = this.parameters - override fun getBody(): String? { - return content - } + override fun getBody(): String? = content - override fun createResponseBuilder(var1: HttpStatus): HttpResponseMessage.Builder { - return MockHttpResponseMessage().status(var1) - } + override fun createResponseBuilder(var1: HttpStatus): HttpResponseMessage.Builder = + MockHttpResponseMessage().status(var1) - override fun createResponseBuilder(var1: HttpStatusType): HttpResponseMessage.Builder { - return MockHttpResponseMessage().status(var1) - } + override fun createResponseBuilder(var1: HttpStatusType): HttpResponseMessage.Builder = + MockHttpResponseMessage().status(var1) } class MockSettings : SettingsProvider { @@ -94,17 +74,11 @@ class MockSettings : SettingsProvider { override val senders get() = this.senderStore.values override val receivers get() = this.receiverStore.values - override fun findOrganization(name: String): Organization? { - return organizationStore[name] - } + override fun findOrganization(name: String): Organization? = organizationStore[name] - override fun findReceiver(fullName: String): Receiver? { - return receiverStore[fullName] - } + override fun findReceiver(fullName: String): Receiver? = receiverStore[fullName] - override fun findSender(fullName: String): Sender? { - return senderStore[Sender.canonicalizeFullName(fullName)] - } + override fun findSender(fullName: String): Sender? = senderStore[Sender.canonicalizeFullName(fullName)] override fun findOrganizationAndReceiver(fullName: String): Pair? { val (organizationName, _) = Receiver.parseFullName(fullName) diff --git a/prime-router/src/test/kotlin/azure/MessagesFunctionsTests.kt b/prime-router/src/test/kotlin/azure/MessagesFunctionsTests.kt index b6f2457e646..397f3eec532 100644 --- a/prime-router/src/test/kotlin/azure/MessagesFunctionsTests.kt +++ b/prime-router/src/test/kotlin/azure/MessagesFunctionsTests.kt @@ -42,8 +42,7 @@ class MessagesFunctionsTests { reportId: UUID? = null, messageId: String? = null, id: Long? = 0, - ): CovidResultMetadata { - return CovidResultMetadata( + ): CovidResultMetadata = CovidResultMetadata( id, reportId, 1, @@ -90,10 +89,8 @@ class MessagesFunctionsTests { null, null ) - } - private fun buildReportFile(reportId: UUID): ReportFile { - return ReportFile( + private fun buildReportFile(reportId: UUID): ReportFile = ReportFile( reportId, 1, TaskAction.send, @@ -116,7 +113,6 @@ class MessagesFunctionsTests { "", null ) - } private fun buildActionLogs(): List { val actionLogDetail = InvalidCodeMessage("", "Specimen_type_code (specimen_type)", null) @@ -133,8 +129,7 @@ class MessagesFunctionsTests { ) } - private fun buildReportDescendantsFromReportId(): List { - return listOf( + private fun buildReportDescendantsFromReportId(): List = listOf( ReportFile( UUID.randomUUID(), 11, @@ -159,10 +154,8 @@ class MessagesFunctionsTests { null ) ) - } - private fun buildReportFileByIds(reportId: ReportId): List { - return listOf( + private fun buildReportFileByIds(reportId: ReportId): List = listOf( ReportFile( reportId, 1, @@ -187,7 +180,6 @@ class MessagesFunctionsTests { null ) ) - } private fun buildActionLogsByReportIdAndFilterType(trackingId: String): List { val actionLogDetail1 = InvalidCodeMessage("", "Specimen_type_code (specimen_type)", null) diff --git a/prime-router/src/test/kotlin/azure/SendFunctionTests.kt b/prime-router/src/test/kotlin/azure/SendFunctionTests.kt index 57c6eb9a0cf..b166b42fe97 100644 --- a/prime-router/src/test/kotlin/azure/SendFunctionTests.kt +++ b/prime-router/src/test/kotlin/azure/SendFunctionTests.kt @@ -61,6 +61,7 @@ class SendFunctionTests { null, null, null, + null, null ) private val reportFile = ReportFile( @@ -92,8 +93,7 @@ class SendFunctionTests { every { workflowEngine.reportService }.returns(mockk(relaxed = true)) } - fun makeIgnoreDotCSVHeader(): WorkflowEngine.Header { - return WorkflowEngine.Header( + fun makeIgnoreDotCSVHeader(): WorkflowEngine.Header = WorkflowEngine.Header( task, reportFile, null, settings.findOrganization("ignore"), @@ -101,10 +101,8 @@ class SendFunctionTests { metadata.findSchema("covid-19"), "hello".toByteArray(), true ) - } - fun makeIgnoreDotHL7NullHeader(): WorkflowEngine.Header { - return WorkflowEngine.Header( + fun makeIgnoreDotHL7NullHeader(): WorkflowEngine.Header = WorkflowEngine.Header( task, reportFile, null, settings.findOrganization("ignore"), @@ -112,7 +110,6 @@ class SendFunctionTests { metadata.findSchema("covid-19"), "hello".toByteArray(), true ) - } @AfterEach fun reset() { diff --git a/prime-router/src/test/kotlin/azure/SenderFilesFunctionTests.kt b/prime-router/src/test/kotlin/azure/SenderFilesFunctionTests.kt index ba5c376577c..d5a1b282b79 100644 --- a/prime-router/src/test/kotlin/azure/SenderFilesFunctionTests.kt +++ b/prime-router/src/test/kotlin/azure/SenderFilesFunctionTests.kt @@ -48,8 +48,7 @@ class SenderFilesFunctionTests { return mockRequest } - private fun buildReportFile(reportId: UUID): ReportFile { - return ReportFile( + private fun buildReportFile(reportId: UUID): ReportFile = ReportFile( reportId, 1, TaskAction.send, @@ -72,10 +71,9 @@ class SenderFilesFunctionTests { "", null, ) - } - private fun buildCovidResultMetadata(reportId: UUID? = null, messageID: String? = null): CovidResultMetadata { - return CovidResultMetadata( + private fun buildCovidResultMetadata(reportId: UUID? = null, messageID: String? = null): CovidResultMetadata = + CovidResultMetadata( 0, reportId, 1, @@ -122,7 +120,6 @@ class SenderFilesFunctionTests { null, null ) - } @Test fun `test checkParameters`() { diff --git a/prime-router/src/test/kotlin/azure/SettingFacadeTests.kt b/prime-router/src/test/kotlin/azure/SettingFacadeTests.kt index a0c13323baa..2396bdb8a99 100644 --- a/prime-router/src/test/kotlin/azure/SettingFacadeTests.kt +++ b/prime-router/src/test/kotlin/azure/SettingFacadeTests.kt @@ -132,9 +132,7 @@ class SettingFacadeTests { }.returns(null) } - private fun testMetadata(): Metadata { - return UnitTestUtils.simpleMetadata - } + private fun testMetadata(): Metadata = UnitTestUtils.simpleMetadata @BeforeEach fun reset() { diff --git a/prime-router/src/test/kotlin/azure/ValidateFunctionTests.kt b/prime-router/src/test/kotlin/azure/ValidateFunctionTests.kt index f78de63b4f4..96e2c4e2003 100644 --- a/prime-router/src/test/kotlin/azure/ValidateFunctionTests.kt +++ b/prime-router/src/test/kotlin/azure/ValidateFunctionTests.kt @@ -75,12 +75,10 @@ class ValidateFunctionTests { "Reicherts,NormanB,19931,97D0667471,Any lab USA,DE,19931,122554006,esyuj9,vhd3cfvvt,DE,NO,bgq0b2e," + "840533007,NO,NO,h8jev96rc,YES,YES,YES,257628001,60001007" - private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine { - return spyk( + private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine = spyk( WorkflowEngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) .blobAccess(blobMock).queueAccess(queueMock).build() ) - } @BeforeEach fun reset() { diff --git a/prime-router/src/test/kotlin/azure/WorkflowEngineTests.kt b/prime-router/src/test/kotlin/azure/WorkflowEngineTests.kt index a3cb73ffe24..8889f2baf67 100644 --- a/prime-router/src/test/kotlin/azure/WorkflowEngineTests.kt +++ b/prime-router/src/test/kotlin/azure/WorkflowEngineTests.kt @@ -52,10 +52,14 @@ class WorkflowEngineTests { receivers = listOf(Receiver("elr", "phd", Topic.TEST, CustomerStatus.INACTIVE, "one")) ) - private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine { - return WorkflowEngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) - .blobAccess(blobMock).queueAccess(queueMock).build() - } + private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine = + WorkflowEngine.Builder() + .metadata(metadata) + .settingsProvider(settings) + .databaseAccess(accessSpy) + .blobAccess(blobMock) + .queueAccess(queueMock) + .build() @BeforeEach fun reset() { diff --git a/prime-router/src/test/kotlin/azure/batch/BatchDeciderTests.kt b/prime-router/src/test/kotlin/azure/batch/BatchDeciderTests.kt index d8c8c15e692..3c399a1cbb4 100644 --- a/prime-router/src/test/kotlin/azure/batch/BatchDeciderTests.kt +++ b/prime-router/src/test/kotlin/azure/batch/BatchDeciderTests.kt @@ -61,10 +61,14 @@ class BatchDeciderTests { ), ) - private fun makeEngine(settings: SettingsProvider): WorkflowEngine { - return WorkflowEngine.Builder().metadata(UnitTestUtils.simpleMetadata).settingsProvider(settings) - .databaseAccess(accessSpy).blobAccess(blobMock).queueAccess(queueMock).build() - } + private fun makeEngine(settings: SettingsProvider): WorkflowEngine = + WorkflowEngine.Builder() + .metadata(UnitTestUtils.simpleMetadata) + .settingsProvider(settings) + .databaseAccess(accessSpy) + .blobAccess(blobMock) + .queueAccess(queueMock) + .build() @BeforeTest fun reset() { diff --git a/prime-router/src/test/kotlin/azure/batch/CovidBatchFunctionTests.kt b/prime-router/src/test/kotlin/azure/batch/CovidBatchFunctionTests.kt index 65ede50edea..99a1d90cbab 100644 --- a/prime-router/src/test/kotlin/azure/batch/CovidBatchFunctionTests.kt +++ b/prime-router/src/test/kotlin/azure/batch/CovidBatchFunctionTests.kt @@ -30,6 +30,7 @@ import io.mockk.mockkObject import io.mockk.runs import io.mockk.spyk import io.mockk.verify +import org.jooq.Configuration import org.jooq.tools.jdbc.MockConnection import org.jooq.tools.jdbc.MockDataProvider import org.jooq.tools.jdbc.MockResult @@ -59,12 +60,14 @@ class CovidBatchFunctionTests { ), ) - private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine { - return spyk( - WorkflowEngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) + private fun makeEngine( + metadata: Metadata, + settings: SettingsProvider, + dbAccess: DatabaseAccess = accessSpy, + ): WorkflowEngine = spyk( + WorkflowEngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(dbAccess) .blobAccess(blobMock).queueAccess(queueMock).build() ) - } @BeforeEach fun reset() { @@ -102,41 +105,62 @@ class CovidBatchFunctionTests { every { timing1.isValid() } returns true every { timing1.maxReportCount } returns 500 every { timing1.numberPerDay } returns 1440 - every { timing1.operation } returns Receiver.BatchOperation.NONE + mockkObject(BlobAccess.Companion) mockkObject(ActionHistory) - every { BlobAccess.Companion.downloadBlobAsByteArray(any()) } returns ByteArray(4) + + // IMPORTANT: Mock the same signature (3-arg) that your real code calls + every { BlobAccess.Companion.downloadBlobAsByteArray(ofType(), any(), any()) } returns ByteArray(4) every { BlobAccess.Companion.deleteBlob(any()) } just runs every { BlobAccess.Companion.exists(any()) } returns true + every { ActionHistory.sanityCheckReports(any(), any(), any()) } just runs + val settings = FileSettings().loadOrganizations(oneOrganization) - val engine = makeEngine(UnitTestUtils.simpleMetadata, settings) + val mockDb = mockk() + every { mockDb.transact(any()) } answers { + val txn = mockk() + firstArg<(Configuration?) -> Unit>().invoke(txn) + } + val engine = makeEngine(UnitTestUtils.simpleMetadata, settings, mockDb) + val mockReportFile = mockk() val randomUUID = UUID.randomUUID() val bodyURL = "someurl" val bodyFormat = "CSV" val schemaName = "one" + val mockBlobInfo = mockk() every { mockBlobInfo.blobUrl } returns bodyURL every { mockBlobInfo.format } returns MimeFormat.CSV every { mockBlobInfo.digest } returns ByteArray(4) + + // If your code calls uploadReport, keep mocking it every { blobMock.uploadReport(any(), any(), any(), any()) } returns mockBlobInfo + every { mockReportFile.reportId } returns randomUUID every { mockReportFile.bodyUrl } returns bodyURL every { mockReportFile.schemaName } returns schemaName every { mockReportFile.bodyFormat } returns bodyFormat + val mockTask = mockk() every { mockTask.reportId } returns randomUUID every { mockTask.bodyUrl } returns bodyURL every { mockTask.schemaName } returns schemaName every { mockTask.bodyFormat } returns bodyFormat + + // Prevent empty reports from short-circuiting the normal batch logic every { engine.generateEmptyReport(any(), any()) } returns Unit - every { engine.db.fetchAndLockBatchTasksForOneReceiver(any(), any(), any(), any(), any()) } returns listOf( - mockTask - ) - every { engine.db.fetchReportFile(any(), any(), any()) } returns mockReportFile - // the message that will be passed to batchFunction + // Return your one task from the DB + every { + mockDb.fetchAndLockBatchTasksForOneReceiver(any(), any(), any(), any(), any()) + } returns listOf(mockTask) + + // Return your mockReportFile from the DB + every { mockDb.fetchReportFile(any(), any(), any()) } returns mockReportFile + + // The message we pass to the batch function val message = BatchEvent(Event.EventAction.BATCH, "phd.elr", false) // invoke batch function run for legacy pipeline diff --git a/prime-router/src/test/kotlin/azure/batch/UniversalBatchFunctionTests.kt b/prime-router/src/test/kotlin/azure/batch/UniversalBatchFunctionTests.kt index 3b1a7581f56..6bcc29da909 100644 --- a/prime-router/src/test/kotlin/azure/batch/UniversalBatchFunctionTests.kt +++ b/prime-router/src/test/kotlin/azure/batch/UniversalBatchFunctionTests.kt @@ -70,12 +70,14 @@ class UniversalBatchFunctionTests { ), ) - private fun makeEngine(metadata: Metadata, settings: SettingsProvider): WorkflowEngine { - return spyk( - WorkflowEngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) + private fun makeEngine( + metadata: Metadata, + settings: SettingsProvider, + dbAccess: DatabaseAccess = accessSpy, + ): WorkflowEngine = spyk( + WorkflowEngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(dbAccess) .blobAccess(blobMock).queueAccess(queueMock).build() ) - } @BeforeEach fun reset() { @@ -426,8 +428,13 @@ class UniversalBatchFunctionTests { every { BlobAccess.Companion.deleteBlob(any()) } just runs every { BlobAccess.Companion.exists(any()) } returns true every { ActionHistory.sanityCheckReports(any(), any(), any()) } just runs + val mockDb = mockk() + every { mockDb.transact(any()) } answers { + val txn = mockk() + firstArg<(Configuration?) -> Unit>().invoke(txn) + } val settings = FileSettings().loadOrganizations(oneOrganization) - val engine = makeEngine(UnitTestUtils.simpleMetadata, settings) + val engine = makeEngine(UnitTestUtils.simpleMetadata, settings, mockDb) val mockReportFile = mockk() val randomUUID = UUID.randomUUID() val bodyURL = "someurl" @@ -448,10 +455,10 @@ class UniversalBatchFunctionTests { every { mockTask.schemaName } returns schemaName every { mockTask.bodyFormat } returns bodyFormat every { engine.generateEmptyReport(any(), any()) } returns Unit - every { engine.db.fetchAndLockBatchTasksForOneReceiver(any(), any(), any(), any(), any()) } returns listOf( + every { mockDb.fetchAndLockBatchTasksForOneReceiver(any(), any(), any(), any(), any()) } returns listOf( mockTask ) - every { engine.db.fetchReportFile(any(), any(), any()) } returns mockReportFile + every { mockDb.fetchReportFile(any(), any(), any()) } returns mockReportFile every { BlobAccess.Companion.exists(any()) } returns true mockkObject(Topic.COVID_19) every { Topic.COVID_19.isUniversalPipeline } returns true diff --git a/prime-router/src/test/kotlin/azure/observability/AzureCustomDimensionsSerializableTest.kt b/prime-router/src/test/kotlin/azure/observability/AzureCustomDimensionsSerializableTest.kt index cdab361e27f..2323377f332 100644 --- a/prime-router/src/test/kotlin/azure/observability/AzureCustomDimensionsSerializableTest.kt +++ b/prime-router/src/test/kotlin/azure/observability/AzureCustomDimensionsSerializableTest.kt @@ -54,7 +54,4 @@ data class TestSerializable( val nested: Nested, ) : AzureCustomDimensionsSerializable -data class Nested( - val key1: String, - val key2: Int, -) \ No newline at end of file +data class Nested(val key1: String, val key2: Int) \ No newline at end of file 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 3d51e2ffac2..ac17b91c11a 100644 --- a/prime-router/src/test/kotlin/azure/observability/context/MDCUtilsTest.kt +++ b/prime-router/src/test/kotlin/azure/observability/context/MDCUtilsTest.kt @@ -153,7 +153,4 @@ class MDCUtilsTest { } } -data class TestContext( - val key1: String, - val key2: Int, -) : AzureLoggingContext \ No newline at end of file +data class TestContext(val key1: String, val key2: Int) : AzureLoggingContext \ No newline at end of file diff --git a/prime-router/src/test/kotlin/azure/observability/event/AzureEventServiceTest.kt b/prime-router/src/test/kotlin/azure/observability/event/AzureEventServiceTest.kt index 5db283d99b1..81f30a56a33 100644 --- a/prime-router/src/test/kotlin/azure/observability/event/AzureEventServiceTest.kt +++ b/prime-router/src/test/kotlin/azure/observability/event/AzureEventServiceTest.kt @@ -53,7 +53,4 @@ class AzureEventServiceTest { } } -data class TestEvent( - val property1: String, - val property2: Int, -) : AzureCustomEvent \ No newline at end of file +data class TestEvent(val property1: String, val property2: Int) : AzureCustomEvent \ No newline at end of file diff --git a/prime-router/src/test/kotlin/cli/LookupTableCommandsTest.kt b/prime-router/src/test/kotlin/cli/LookupTableCommandsTest.kt index d92f8927004..8d8314ebc72 100644 --- a/prime-router/src/test/kotlin/cli/LookupTableCommandsTest.kt +++ b/prime-router/src/test/kotlin/cli/LookupTableCommandsTest.kt @@ -28,8 +28,7 @@ class LookupTableCommandsTest { url: String, status: HttpStatusCode, body: String, - ): LookupTableEndpointUtilities { - return LookupTableEndpointUtilities( + ): LookupTableEndpointUtilities = LookupTableEndpointUtilities( Environment.LOCAL, useThisToken = null, ApiMockEngine( @@ -38,7 +37,6 @@ class LookupTableCommandsTest { body ).client() ) - } @Test fun `test lookuptable list invalid response body`() { diff --git a/prime-router/src/test/kotlin/common/ReportNodeBuilder.kt b/prime-router/src/test/kotlin/common/ReportNodeBuilder.kt index 616773f1e6d..19bda5731f4 100644 --- a/prime-router/src/test/kotlin/common/ReportNodeBuilder.kt +++ b/prime-router/src/test/kotlin/common/ReportNodeBuilder.kt @@ -191,9 +191,9 @@ class ReportGraphBuilder { class ReportNodeBuilder { companion object { - fun reportGraph(initializer: ReportGraphBuilder.() -> Unit): ReportGraphBuilder { - return ReportGraphBuilder().apply(initializer) - } + fun reportGraph( + initializer: ReportGraphBuilder.() -> Unit, + ): ReportGraphBuilder = ReportGraphBuilder().apply(initializer) } lateinit var theAction: TaskAction var theNextAction: TaskAction? = null diff --git a/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt b/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt index a61f5b13d86..c426480c678 100644 --- a/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt +++ b/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt @@ -357,8 +357,7 @@ object UniversalPipelineTestUtils { val enrichmentSchemaNames: List = emptyList(), ) - fun createReceivers(receiverSetupDataList: List): List { - return receiverSetupDataList.map { + fun createReceivers(receiverSetupDataList: List): List = receiverSetupDataList.map { Receiver( it.name, it.orgName, @@ -376,10 +375,8 @@ object UniversalPipelineTestUtils { enrichmentSchemaNames = it.enrichmentSchemaNames ) } - } - fun createOrganizationWithReceivers(receiverList: List): DeepOrganization { - return DeepOrganization( + fun createOrganizationWithReceivers(receiverList: List): DeepOrganization = DeepOrganization( "phd", "test", Organization.Jurisdiction.FEDERAL, @@ -392,7 +389,6 @@ object UniversalPipelineTestUtils { ), receivers = receiverList ) - } fun createFHIRFunctionsInstance(): FHIRFunctions { val settings = FileSettings().loadOrganizations(universalPipelineOrganization) diff --git a/prime-router/src/test/kotlin/config/validation/ValueValidationTest.kt b/prime-router/src/test/kotlin/config/validation/ValueValidationTest.kt index 5fefde4127e..c28d32079f3 100644 --- a/prime-router/src/test/kotlin/config/validation/ValueValidationTest.kt +++ b/prime-router/src/test/kotlin/config/validation/ValueValidationTest.kt @@ -63,8 +63,4 @@ class ValueValidationTest { } } -private data class TestDataClass( - val string: String, - val nullable: Int?, - val list: List, -) \ No newline at end of file +private data class TestDataClass(val string: String, val nullable: Int?, val list: List) \ No newline at end of file diff --git a/prime-router/src/test/kotlin/credentials/HashicorpVaultCredentialServiceTests.kt b/prime-router/src/test/kotlin/credentials/HashicorpVaultCredentialServiceTests.kt index c456d41220f..089c3fd68e1 100644 --- a/prime-router/src/test/kotlin/credentials/HashicorpVaultCredentialServiceTests.kt +++ b/prime-router/src/test/kotlin/credentials/HashicorpVaultCredentialServiceTests.kt @@ -19,14 +19,12 @@ internal class HashicorpVaultCredentialServiceTests { status: HttpStatusCode, body: String, f: ((request: HttpRequestData) -> Unit)? = null, - ): HttpClient { - return ApiMockEngine( + ): HttpClient = ApiMockEngine( url, status, body, f = f ).client() - } @Test fun `uses Vault api to fetch a credential`() { diff --git a/prime-router/src/test/kotlin/db/ReportFileDatabaseAccessTest.kt b/prime-router/src/test/kotlin/db/ReportFileDatabaseAccessTest.kt index 50fe117012a..6f4d4871605 100644 --- a/prime-router/src/test/kotlin/db/ReportFileDatabaseAccessTest.kt +++ b/prime-router/src/test/kotlin/db/ReportFileDatabaseAccessTest.kt @@ -18,7 +18,7 @@ import java.util.UUID class ReportFileDatabaseAccessTest { @Nested - inner class ReportFileDatabaseTests() { + inner class ReportFileDatabaseTests { @Test fun `Test can find a single report`() { @@ -51,7 +51,7 @@ class ReportFileDatabaseAccessTest { } @Nested - inner class ReportFileApiSearchTest() { + inner class ReportFileApiSearchTest { @Test fun `Test generates filters correctly, multiple filters`() { diff --git a/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseContainer.kt b/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseContainer.kt index 4502a3ce352..67e2357dcfb 100644 --- a/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseContainer.kt +++ b/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseContainer.kt @@ -10,7 +10,8 @@ import org.testcontainers.containers.PostgreSQLContainer * Test container for the report stream database * */ -class ReportStreamTestDatabaseContainer : PostgreSQLContainer( +class ReportStreamTestDatabaseContainer : + PostgreSQLContainer( TestcontainersUtils.createPostgresContainer() ) { diff --git a/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseSetupExtension.kt b/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseSetupExtension.kt index 7f35806c7ef..e9bc2c6a0ce 100644 --- a/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseSetupExtension.kt +++ b/prime-router/src/test/kotlin/db/ReportStreamTestDatabaseSetupExtension.kt @@ -18,7 +18,9 @@ import org.testcontainers.junit.jupiter.Testcontainers * */ @Testcontainers -class ReportStreamTestDatabaseSetupExtension : BeforeAllCallback, AfterEachCallback { +class ReportStreamTestDatabaseSetupExtension : + BeforeAllCallback, + AfterEachCallback { /** * Starts and sets up the test Postgres container */ diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt index b534f994a12..90fdc27dacf 100644 --- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt @@ -162,8 +162,7 @@ class FHIRConverterIntegrationTests { report: Report, blobContents: String, sender: Sender, - ): String { - return """ + ): String = """ { "type": "convert", "reportId": "${report.id}", @@ -174,7 +173,6 @@ class FHIRConverterIntegrationTests { "schemaName": "${sender.schemaName}" } """.trimIndent() - } private fun generateFHIRConvertSubmissionQueueMessage( report: Report, @@ -241,8 +239,7 @@ class FHIRConverterIntegrationTests { sender: Sender, receiveReportBlobUrl: String, itemCount: Int, - ): Report { - return ReportStreamTestDatabaseContainer.testDatabaseAccess.transactReturning { txn -> + ): Report = ReportStreamTestDatabaseContainer.testDatabaseAccess.transactReturning { txn -> val report = Report( format, emptyList(), @@ -284,7 +281,6 @@ class FHIRConverterIntegrationTests { report } - } @Test fun `should add a message to the poison queue if the sender is not found and not do any work`() { @@ -778,10 +774,12 @@ class FHIRConverterIntegrationTests { assertThat(actionLogs).hasSize(4) @Suppress("ktlint:standard:max-line-length") val expectedDetailedActions = listOf( - 2 to "Item 2 in the report was not parseable. Reason: exception while parsing FHIR: HAPI-1838: Invalid JSON content detected, missing required element: 'resourceType'", + 2 to + "Item 2 in the report was not parseable. Reason: exception while parsing FHIR: HAPI-1838: Invalid JSON content detected, missing required element: 'resourceType'", 3 to "Missing mapping for code(s): 41458-1", 3 to "Missing mapping for code(s): 260373001", - 4 to "Item 4 in the report was not parseable. Reason: exception while parsing FHIR: HAPI-1861: Failed to parse JSON encoded FHIR content: Unexpected end-of-input: was expecting closing quote for a string value\n" + + 4 to + "Item 4 in the report was not parseable. Reason: exception while parsing FHIR: HAPI-1861: Failed to parse JSON encoded FHIR content: Unexpected end-of-input: was expecting closing quote for a string value\n" + " at [line: 1, column: 23]" ) @@ -968,7 +966,8 @@ class FHIRConverterIntegrationTests { ReportStreamEventProperties.VALIDATION_PROFILE to Topic.MARS_OTC_ELR.validator.validatorProfileName, @Suppress("ktlint:standard:max-line-length") ReportStreamEventProperties.PROCESSING_ERROR - to "Item 2 in the report was not valid. Reason: HL7 was not valid at MSH[1]-21[1].3 for validator: RADx MARS" + to + "Item 2 in the report was not valid. Reason: HL7 was not valid at MSH[1]-21[1].3 for validator: RADx MARS" ) ) } diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt index 2c8f314fbe6..cbc4cb36ab9 100644 --- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRDestinationFilterIntegrationTests.kt @@ -42,7 +42,7 @@ import gov.cdc.prime.router.common.validFHIRRecord1 import gov.cdc.prime.router.db.ReportStreamTestDatabaseContainer import gov.cdc.prime.router.db.ReportStreamTestDatabaseSetupExtension import gov.cdc.prime.router.fhirengine.engine.FHIRDestinationFilter -import gov.cdc.prime.router.fhirengine.engine.FhirReceiverFilterQueueMessage +import gov.cdc.prime.router.fhirengine.engine.FhirReceiverEnrichmentQueueMessage import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder import gov.cdc.prime.router.history.db.ReportGraph import gov.cdc.prime.router.metadata.LookupTable @@ -132,8 +132,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { ) } - fun generateQueueMessage(action: TaskAction, report: Report, blobContents: String, sender: Sender): String { - return """ + fun generateQueueMessage(action: TaskAction, report: Report, blobContents: String, sender: Sender): String = """ { "type": "${action.literal}", "reportId": "${report.id}", @@ -144,7 +143,6 @@ class FHIRDestinationFilterIntegrationTests : Logging { "schemaName": "${sender.schemaName}" } """.trimIndent() - } @Test fun `should send valid FHIR report only to receivers listening to full-elr`() { @@ -204,7 +202,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { ReportStreamTestDatabaseContainer.testDatabaseAccess.transact { txn -> val routedReports = fetchChildReports(report, txn, 2, 2) with(routedReports.first()) { - assertThat(this.nextAction).isEqualTo(TaskAction.receiver_filter) + assertThat(this.nextAction).isEqualTo(TaskAction.receiver_enrichment) assertThat(this.receivingOrg).isEqualTo("phd") assertThat(this.receivingOrgSvc).isEqualTo("x") assertThat(this.schemaName).isEqualTo("None") @@ -212,7 +210,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { assertThat(this.bodyFormat).isEqualTo("FHIR") } with(routedReports.last()) { - assertThat(this.nextAction).isEqualTo(TaskAction.receiver_filter) + assertThat(this.nextAction).isEqualTo(TaskAction.receiver_enrichment) assertThat(this.receivingOrg).isEqualTo("phd") assertThat(this.receivingOrgSvc).isEqualTo("y") assertThat(this.schemaName).isEqualTo("None") @@ -233,7 +231,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { // check queue message val expectedRouteQueueMessages = routedReports.flatMap { report -> listOf( - FhirReceiverFilterQueueMessage( + FhirReceiverEnrichmentQueueMessage( report.reportId, report.bodyUrl, BlobUtils.digestToString(report.blobDigest), @@ -241,7 +239,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { UniversalPipelineTestUtils.fhirSenderWithNoTransform.topic, "phd.x" ), - FhirReceiverFilterQueueMessage( + FhirReceiverEnrichmentQueueMessage( report.reportId, report.bodyUrl, BlobUtils.digestToString(report.blobDigest), @@ -256,7 +254,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { verify(exactly = 2) { QueueAccess.sendMessage( - QueueMessage.elrReceiverFilterQueueName, + QueueMessage.elrReceiverEnrichmentQueueName, match { expectedRouteQueueMessages.contains(it) } @@ -309,7 +307,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { // check results ReportStreamTestDatabaseContainer.testDatabaseAccess.transact { txn -> val routedReport = fetchChildReports(report, txn, 1).single() - assertThat(routedReport.nextAction).isEqualTo(TaskAction.receiver_filter) + assertThat(routedReport.nextAction).isEqualTo(TaskAction.receiver_enrichment) assertThat(routedReport.receivingOrg).isEqualTo("phd") assertThat(routedReport.receivingOrgSvc).isEqualTo("x") assertThat(routedReport.schemaName).isEqualTo("None") @@ -324,7 +322,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { assertThat(reportContents).isEqualTo(routedBundle) // check queue message - val expectedQueueMessage = FhirReceiverFilterQueueMessage( + val expectedQueueMessage = FhirReceiverEnrichmentQueueMessage( routedReport.reportId, routedReport.bodyUrl, BlobUtils.digestToString(routedReport.blobDigest), @@ -336,7 +334,7 @@ class FHIRDestinationFilterIntegrationTests : Logging { // filter should permit message and should not mangle message verify(exactly = 1) { QueueAccess.sendMessage( - QueueMessage.elrReceiverFilterQueueName, + QueueMessage.elrReceiverEnrichmentQueueName, expectedQueueMessage.serialize() ) } diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverEnrichmentIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverEnrichmentIntegrationTests.kt new file mode 100644 index 00000000000..1d5fefee874 --- /dev/null +++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverEnrichmentIntegrationTests.kt @@ -0,0 +1,258 @@ +package gov.cdc.prime.router.fhirengine.azure + +import assertk.assertThat +import assertk.assertions.hasSize +import assertk.assertions.isEqualTo +import assertk.assertions.isInstanceOf +import assertk.assertions.isNotNull +import gov.cdc.prime.reportstream.shared.BlobUtils +import gov.cdc.prime.router.DeepOrganization +import gov.cdc.prime.router.FileSettings +import gov.cdc.prime.router.MimeFormat +import gov.cdc.prime.router.Report +import gov.cdc.prime.router.Sender +import gov.cdc.prime.router.Topic +import gov.cdc.prime.router.azure.ActionHistory +import gov.cdc.prime.router.azure.BlobAccess +import gov.cdc.prime.router.azure.Event +import gov.cdc.prime.router.azure.QueueAccess +import gov.cdc.prime.router.azure.db.enums.TaskAction +import gov.cdc.prime.router.azure.db.tables.Task +import gov.cdc.prime.router.azure.observability.event.AzureEventService +import gov.cdc.prime.router.azure.observability.event.InMemoryAzureEventService +import gov.cdc.prime.router.azure.observability.event.ReportStreamEventName +import gov.cdc.prime.router.azure.observability.event.ReportStreamEventProperties +import gov.cdc.prime.router.azure.observability.event.ReportStreamEventService +import gov.cdc.prime.router.azure.observability.event.ReportStreamItemEvent +import gov.cdc.prime.router.common.TestcontainersUtils +import gov.cdc.prime.router.common.UniversalPipelineTestUtils +import gov.cdc.prime.router.common.UniversalPipelineTestUtils.fetchChildReports +import gov.cdc.prime.router.db.ReportStreamTestDatabaseContainer +import gov.cdc.prime.router.db.ReportStreamTestDatabaseSetupExtension +import gov.cdc.prime.router.fhirengine.engine.FHIRReceiverEnrichment +import gov.cdc.prime.router.fhirengine.translation.hl7.utils.FhirPathUtils +import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder +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 io.mockk.every +import io.mockk.mockkObject +import io.mockk.unmockkAll +import io.mockk.verify +import org.apache.logging.log4j.kotlin.Logging +import org.hl7.fhir.r4.model.Reference +import org.hl7.fhir.r4.model.StringType +import org.jooq.impl.DSL +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.testcontainers.junit.jupiter.Container +import org.testcontainers.junit.jupiter.Testcontainers +import java.io.File + +private const val MULTIPLE_TARGETS_FHIR_PATH = + "src/test/resources/fhirengine/engine/valid_data_multiple_targets.fhir" + +@Testcontainers +@ExtendWith(ReportStreamTestDatabaseSetupExtension::class) +class FHIRReceiverEnrichmentIntegrationTests : Logging { + @Container + val azuriteContainer = TestcontainersUtils.createAzuriteContainer( + customImageName = "azurite_fhirfunctionintegration1", + customEnv = mapOf( + "AZURITE_ACCOUNTS" to "devstoreaccount1:keydevstoreaccount1" + ) + ) + + val azureEventService = InMemoryAzureEventService() + + @BeforeEach + fun beforeEach() { + mockkObject(QueueAccess) + every { QueueAccess.sendMessage(any(), any()) } returns "" + mockkObject(BlobAccess) + every { BlobAccess getProperty "defaultBlobMetadata" } returns UniversalPipelineTestUtils + .getBlobContainerMetadata(azuriteContainer) + mockkObject(BlobAccess.BlobContainerMetadata) + every { + BlobAccess.BlobContainerMetadata.build( + any(), + any() + ) + } returns UniversalPipelineTestUtils.getBlobContainerMetadata(azuriteContainer) + } + + @AfterEach + fun afterEach() { + unmockkAll() + azureEventService.events.clear() + } + + private fun createFHIRReceiverEnrichment( + azureEventService: AzureEventService, + org: DeepOrganization? = null, + ): FHIRReceiverEnrichment { + val settings = FileSettings().loadOrganizations(org ?: UniversalPipelineTestUtils.universalPipelineOrganization) + val metadata = UnitTestUtils.simpleMetadata + metadata.lookupTableStore += mapOf( + "observation-mapping" to LookupTable("observation-mapping", emptyList()) + ) + return FHIRReceiverEnrichment( + metadata, + settings, + reportService = ReportService(ReportGraph(ReportStreamTestDatabaseContainer.testDatabaseAccess)), + azureEventService = azureEventService, + reportStreamEventService = ReportStreamEventService( + ReportStreamTestDatabaseContainer.testDatabaseAccess, azureEventService, + ReportService( + ReportGraph(ReportStreamTestDatabaseContainer.testDatabaseAccess), + ReportStreamTestDatabaseContainer.testDatabaseAccess + ) + ) + ) + } + + private fun generateQueueMessage( + report: Report, + blobContents: String, + sender: Sender, + receiverName: String, + ): String = """ + { + "type": "${TaskAction.receiver_enrichment.literal}", + "reportId": "${report.id}", + "blobURL": "${report.bodyURL}", + "digest": "${BlobUtils.digestToString(BlobUtils.sha256Digest(blobContents.toByteArray()))}", + "blobSubFolderName": "${sender.fullName}", + "topic": "${sender.topic.jsonVal}", + "receiverFullName": "$receiverName" + } + """.trimIndent() + + @Test + fun `successfully add enrichments`() { + // set up + // the selected transform alters the software name and software version + val receiverSetupData = listOf( + UniversalPipelineTestUtils.ReceiverSetupData( + "x", + jurisdictionalFilter = listOf("true"), + qualityFilter = listOf("true"), + routingFilter = listOf("true"), + conditionFilter = listOf("true"), + format = MimeFormat.FHIR, + enrichmentSchemaNames = listOf( + "classpath:/enrichments/testing.yml", + "classpath:/enrichments/testing2.yml" + ) + ) + ) + val receivers = UniversalPipelineTestUtils.createReceivers(receiverSetupData) + val org = UniversalPipelineTestUtils.createOrganizationWithReceivers(receivers) + val fhirReceiverEnrichment = createFHIRReceiverEnrichment(azureEventService, org) + val reportContents = File(MULTIPLE_TARGETS_FHIR_PATH).readText() + val receiveReport = UniversalPipelineTestUtils.createReport( + reportContents, + TaskAction.receiver_enrichment, + Event.EventAction.RECEIVER_ENRICHMENT, + azuriteContainer + ) + + val queueMessage = generateQueueMessage( + receiveReport, + reportContents, + UniversalPipelineTestUtils.fhirSenderWithNoTransform, + "phd.x" + ) + val fhirFunctions = UniversalPipelineTestUtils.createFHIRFunctionsInstance() + + // execute + fhirFunctions.process(queueMessage, 1, fhirReceiverEnrichment, ActionHistory(TaskAction.receiver_enrichment)) + + // no queue messages should have been sent + verify(exactly = 1) { + QueueAccess.sendMessage(any(), any()) + } + + // check events + assertThat(azureEventService.reportStreamEvents[ReportStreamEventName.ITEM_TRANSFORMED]!!).hasSize(1) + assertThat( + azureEventService + .reportStreamEvents[ReportStreamEventName.ITEM_TRANSFORMED]!!.first() + ).isInstanceOf() + val event = azureEventService + .reportStreamEvents[ReportStreamEventName.ITEM_TRANSFORMED]!!.first() as ReportStreamItemEvent + assertThat(event.params[ReportStreamEventProperties.RECEIVER_NAME]).isEqualTo("phd.x") + val enrichments = event.params[ReportStreamEventProperties.ENRICHMENTS] as List<*> + assertThat(enrichments).hasSize(2) + assertThat(enrichments).isEqualTo(receiverSetupData.first().enrichmentSchemaNames) + + // check action table + UniversalPipelineTestUtils.checkActionTable(listOf(TaskAction.receive, TaskAction.receiver_enrichment)) + + // verify task and report_file tables were updated correctly in the FHIRReceiverEnrichment function + // (new task and new record file created) + ReportStreamTestDatabaseContainer.testDatabaseAccess.transact { txn -> + val report = fetchChildReports(receiveReport, txn, 1).single() + assertThat(report.nextAction).isEqualTo(TaskAction.receiver_filter) + assertThat(report.receivingOrg).isEqualTo("phd") + assertThat(report.receivingOrgSvc).isEqualTo("x") + assertThat(report.schemaName).isEqualTo("None") + assertThat(report.schemaTopic).isEqualTo(Topic.FULL_ELR) + assertThat(report.bodyFormat).isEqualTo("FHIR") + + val receiverFilterTask = DSL.using(txn).select(Task.TASK.asterisk()).from(Task.TASK) + .where(Task.TASK.NEXT_ACTION.eq(TaskAction.receiver_filter)) + .fetchOneInto(Task.TASK) + + // verify receiver filter queue task exists + assertThat(receiverFilterTask).isNotNull() + assertThat(receiverFilterTask!!.reportId).isEqualTo(report.reportId) + + // verify message format is FHIR and is for the expected receiver + assertThat(receiverFilterTask.receiverName).isEqualTo("phd.x") + assertThat(receiverFilterTask.bodyFormat).isEqualTo("FHIR") + + // verify message matches the expected output + val blob = BlobAccess.downloadBlobAsBinaryData( + report.bodyUrl, + UniversalPipelineTestUtils.getBlobContainerMetadata(azuriteContainer) + ).toString() + val enrichedBundle = FhirTranscoder.decode(blob) + assertThat(enrichedBundle).isNotNull() + val software = FhirPathUtils.evaluate( + null, + enrichedBundle, + enrichedBundle, + "Bundle.entry.resource.ofType(MessageHeader).source.software" + ) + .filterIsInstance() + .firstOrNull() + ?.value + val version = FhirPathUtils.evaluate( + null, + enrichedBundle, + enrichedBundle, + "Bundle.entry.resource.ofType(MessageHeader).source.version" + ) + .filterIsInstance() + .firstOrNull() + ?.value + assertThat(software).isEqualTo("Purple PRIME ReportStream") + assertThat(version).isEqualTo("0.2-YELLOW") + val vendorOrganization = FhirPathUtils.evaluate( + null, + enrichedBundle, + enrichedBundle, + "Bundle.entry.resource.ofType(MessageHeader).source" + + ".extension('https://reportstream.cdc.gov/fhir/StructureDefinition/software-vendor-org').value" + ) + .filterIsInstance() + .firstOrNull() + ?.resource as org.hl7.fhir.r4.model.Organization + assertThat(vendorOrganization.name.toString()).isEqualTo("Orange Software Vendor Name") + } + } +} \ No newline at end of file diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt index d8bae65c3eb..ce40bc56086 100644 --- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt @@ -231,8 +231,7 @@ class FHIRReceiverFilterIntegrationTests : Logging { blobContents: String, sender: Sender, receiverName: String, - ): String { - return """ + ): String = """ { "type": "${TaskAction.receiver_filter.literal}", "reportId": "${report.id}", @@ -243,7 +242,6 @@ class FHIRReceiverFilterIntegrationTests : Logging { "receiverFullName": "$receiverName" } """.trimIndent() - } @Test fun `should send valid FHIR report filtered by condition filter`() { diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt index 99eada67991..3a91c64649a 100644 --- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt @@ -111,7 +111,7 @@ class FHIRTranslatorIntegrationTests : Logging { azureEventService = azureEventService, reportStreamEventService = ReportStreamEventService( ReportStreamTestDatabaseContainer.testDatabaseAccess, azureEventService, - ReportService( + ReportService( ReportGraph(ReportStreamTestDatabaseContainer.testDatabaseAccess), ReportStreamTestDatabaseContainer.testDatabaseAccess ) @@ -124,8 +124,7 @@ class FHIRTranslatorIntegrationTests : Logging { blobContents: String, sender: Sender, receiverName: String, - ): String { - return """ + ): String = """ { "type": "${TaskAction.translate.literal}", "reportId": "${report.id}", @@ -136,7 +135,6 @@ class FHIRTranslatorIntegrationTests : Logging { "receiverFullName": "$receiverName" } """.trimIndent() - } @Test fun `successfully translate for HL7 receiver when isSendOriginal is false`() { @@ -163,7 +161,8 @@ class FHIRTranslatorIntegrationTests : Logging { ) @Suppress("ktlint:standard:max-line-length") - val expectedOutput = "MSH|^~\\&|||||||ORU/ACK - Unsolicited transmission of an observation message|849547|P|2.5.1|||||USA\r" + + val expectedOutput = + "MSH|^~\\&|||||||ORU/ACK - Unsolicited transmission of an observation message|849547|P|2.5.1|||||USA\r" + "SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME Data Hub|0.1-SNAPSHOT||20210622\r" + "PID|1||||Steuber||20150707|O||^^^^^^^^Native Hawaiian or Other Pacific Islander|^^^IG^^s4fgh||~|||||||||^^^^^^^^Non Hispanic or Latino|||||||20210614\r" + "ORC|||||||||||||||||||||Any facility USA|^^^IG||^^^IG\r" + @@ -246,119 +245,6 @@ class FHIRTranslatorIntegrationTests : Logging { } } - @Test - fun `successfully translate for HL7 receiver with enrichments when isSendOriginal is false`() { - // set up - // the selected transform alters the software name and software version - val receiverSetupData = listOf( - UniversalPipelineTestUtils.ReceiverSetupData( - "x", - jurisdictionalFilter = listOf("true"), - qualityFilter = listOf("true"), - routingFilter = listOf("true"), - conditionFilter = listOf("true"), - format = MimeFormat.HL7, - enrichmentSchemaNames = listOf( - "classpath:/enrichments/testing.yml", - "classpath:/enrichments/testing2.yml" - ) - ) - ) - val receivers = UniversalPipelineTestUtils.createReceivers(receiverSetupData) - val org = UniversalPipelineTestUtils.createOrganizationWithReceivers(receivers) - val translator = createFHIRTranslator(azureEventService, org) - val reportContents = File(MULTIPLE_TARGETS_FHIR_PATH).readText() - val receiveReport = UniversalPipelineTestUtils.createReport( - reportContents, - TaskAction.receive, - Event.EventAction.CONVERT, - azuriteContainer - ) - - @Suppress("ktlint:standard:max-line-length") - val expectedOutput = "MSH|^~\\&|||||||ORU/ACK - Unsolicited transmission of an observation message|849547|P|2.5.1|||||USA\r" + - "SFT|Orange Software Vendor Name|0.2-YELLOW|Purple PRIME ReportStream|0.1-SNAPSHOT||20210622\r" + - "PID|1||||Steuber||20150707|O||^^^^^^^^Native Hawaiian or Other Pacific Islander|^^^IG^^s4fgh||~|||||||||^^^^^^^^Non Hispanic or Latino|||||||20210614\r" + - "ORC|||||||||||||||||||||Any facility USA|^^^IG||^^^IG\r" + - "OBR|1|||^^^^^^^^SARS-CoV+SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay\r" + - "OBX|1||^^^^^^^^SARS-CoV+SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay|770814|^^^^^^^^Not detected||Abnormal|||||||||||LumiraDx Platform_LumiraDx\r" + - "NTE|1|L|ciu1se|^^^^^^^^Remark\r" + - "OBX|2||^^^^^^^^Whether patient is employed in a healthcare setting||^^^^^^^^Yes\r" + - "OBX|3||^^^^^^^^First test for condition of interest||^^^^^^^^Yes\r" + - "OBX|4||^^^^^^^^Patient was hospitalized because of this condition||^^^^^^^^Unknown\r" + - "OBX|5||^^^^^^^^Admitted to intensive care unit for condition of interest||^^^^^^^^Yes\r" + - "OBX|6||^^^^^^^^Date and time of symptom onset\r" + - "OBX|7||^^^^^^^^Age\r" + - "OBX|8||^^^^^^^^Pregnancy status||^^^^^^^^Unknown\r" + - "OBX|9||^^^^^^^^Resides in a congregate care setting||^^^^^^^^Yes\r" + - "OBX|10||^^^^^^^^Has symptoms related to condition of interest||^^^^^^^^No\r" + - "SPM|1|||^^^^^^^^Sputum specimen|||^^^^^^^^Pinworm Prep|^^^^^^^^Nasopharyngeal structure (body structure)|||||||||20210617070000-0400|20210613045200-0400\r" - val queueMessage = generateQueueMessage( - receiveReport, - reportContents, - UniversalPipelineTestUtils.fhirSenderWithNoTransform, - "phd.x" - ) - val fhirFunctions = UniversalPipelineTestUtils.createFHIRFunctionsInstance() - - // execute - fhirFunctions.process(queueMessage, 1, translator, ActionHistory(TaskAction.translate)) - - // no queue messages should have been sent - verify(exactly = 0) { - QueueAccess.sendMessage(any(), any()) - } - - // check events - assertThat(azureEventService.reportStreamEvents[ReportStreamEventName.ITEM_TRANSFORMED]!!).hasSize(1) - assertThat( - azureEventService - .reportStreamEvents[ReportStreamEventName.ITEM_TRANSFORMED]!!.first() - ).isInstanceOf() - val event = azureEventService - .reportStreamEvents[ReportStreamEventName.ITEM_TRANSFORMED]!!.first() as ReportStreamItemEvent - assertThat(event.params[ReportStreamEventProperties.ORIGINAL_FORMAT]).isEqualTo("FHIR") - assertThat(event.params[ReportStreamEventProperties.TARGET_FORMAT]).isEqualTo("HL7") - assertThat(event.params[ReportStreamEventProperties.RECEIVER_NAME]).isEqualTo("phd.x") - val enrichments = event.params[ReportStreamEventProperties.ENRICHMENTS] as List<*> - assertThat(enrichments).hasSize(1) - assertThat(enrichments.first()).isEqualTo(receiverSetupData.first().schemaName) - - // check action table - UniversalPipelineTestUtils.checkActionTable(listOf(TaskAction.receive, TaskAction.translate)) - - // verify task and report_file tables were updated correctly in the Translate function (new task and new - // record file created) - ReportStreamTestDatabaseContainer.testDatabaseAccess.transact { txn -> - val report = fetchChildReports(receiveReport, txn, 1).single() - assertThat(report.nextAction).isEqualTo(TaskAction.batch) - assertThat(report.receivingOrg).isEqualTo("phd") - assertThat(report.receivingOrgSvc).isEqualTo("x") - assertThat(report.schemaName).isEqualTo("None") - assertThat(report.schemaTopic).isEqualTo(Topic.FULL_ELR) - assertThat(report.bodyFormat).isEqualTo("HL7") - - val batchTask = DSL.using(txn).select(Task.TASK.asterisk()).from(Task.TASK) - .where(Task.TASK.NEXT_ACTION.eq(TaskAction.batch)) - .fetchOneInto(Task.TASK) - - // verify batch queue task exists - assertThat(batchTask).isNotNull() - assertThat(batchTask!!.reportId).isEqualTo(report.reportId) - - // verify message format is HL7 and is for the expected receiver - assertThat(batchTask.receiverName).isEqualTo("phd.x") - assertThat(batchTask.bodyFormat).isEqualTo("HL7") - - // verify message matches the expected HL7 output - val translatedValue = BlobAccess.downloadBlobAsBinaryData( - report.bodyUrl, - UniversalPipelineTestUtils.getBlobContainerMetadata(azuriteContainer) - ).toString() - assertThat(translatedValue).isEqualTo(expectedOutput) - } - } - @Test fun `successfully translate for FHIR receiver without transform when isSendOriginal is false`() { // set up diff --git a/prime-router/src/test/kotlin/fhirengine/engine/FHIRReceiverEnrichmentTests.kt b/prime-router/src/test/kotlin/fhirengine/engine/FHIRReceiverEnrichmentTests.kt new file mode 100644 index 00000000000..4039d12f5d1 --- /dev/null +++ b/prime-router/src/test/kotlin/fhirengine/engine/FHIRReceiverEnrichmentTests.kt @@ -0,0 +1,327 @@ +package gov.cdc.prime.router.fhirengine.engine + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import gov.cdc.prime.router.ActionLogger +import gov.cdc.prime.router.CustomerStatus +import gov.cdc.prime.router.DeepOrganization +import gov.cdc.prime.router.FileSettings +import gov.cdc.prime.router.Metadata +import gov.cdc.prime.router.MimeFormat +import gov.cdc.prime.router.Organization +import gov.cdc.prime.router.Receiver +import gov.cdc.prime.router.Report +import gov.cdc.prime.router.ReportStreamConditionFilter +import gov.cdc.prime.router.ReportStreamFilter +import gov.cdc.prime.router.Schema +import gov.cdc.prime.router.SettingsProvider +import gov.cdc.prime.router.TestSource +import gov.cdc.prime.router.Topic +import gov.cdc.prime.router.azure.ActionHistory +import gov.cdc.prime.router.azure.BlobAccess +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.azure.observability.event.InMemoryAzureEventService +import gov.cdc.prime.router.fhirengine.translation.hl7.utils.FhirPathUtils +import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder +import gov.cdc.prime.router.metadata.LookupTable +import gov.cdc.prime.router.report.ReportService +import gov.cdc.prime.router.unittest.UnitTestUtils +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkClass +import io.mockk.mockkObject +import io.mockk.slot +import io.mockk.spyk +import org.hl7.fhir.r4.model.Reference +import org.hl7.fhir.r4.model.StringType +import org.jooq.tools.jdbc.MockConnection +import org.jooq.tools.jdbc.MockDataProvider +import org.jooq.tools.jdbc.MockResult +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.TestInstance +import java.io.ByteArrayInputStream +import java.io.File +import java.util.UUID +import kotlin.test.Test +import kotlin.test.assertFailsWith + +private const val ORGANIZATION_NAME = "co-phd" +private const val RECEIVER_NAME = "full-elr-hl7" +private const val BLOB_URL = "https://blob.url" +private const val BLOB_SUB_FOLDER_NAME = "test-sender" +private val FILTER_FAIL: ReportStreamFilter = listOf("false") +private const val ORU_R01_SCHEMA = "classpath:/metadata/hl7_mapping/receivers/STLTs/CA/CA-receiver-transform.yml" + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class FHIRReceiverEnrichmentTests { + val dataProvider = MockDataProvider { emptyArray() } + val connection = MockConnection(dataProvider) + val accessSpy = spyk(DatabaseAccess(connection)) + val blobMock = mockkClass(BlobAccess::class) + private val actionHistory = ActionHistory(TaskAction.receiver_enrichment) + private val azureEventService = InMemoryAzureEventService() + private val reportServiceMock = mockk() + private val submittedId = UUID.randomUUID() + + val csv = """ + variable,fhirPath + processingId,Bundle.entry.resource.ofType(MessageHeader).meta.extension('https://reportstream.cdc.gov/fhir/StructureDefinition/source-processing-id').value.coding.code + messageId,Bundle.entry.resource.ofType(MessageHeader).id + patient,Bundle.entry.resource.ofType(Patient) + performerState,Bundle.entry.resource.ofType(ServiceRequest)[0].requester.resolve().organization.resolve().address.state + patientState,Bundle.entry.resource.ofType(Patient).address.state + specimen,Bundle.entry.resource.ofType(Specimen) + serviceRequest,Bundle.entry.resource.ofType(ServiceRequest) + observation,Bundle.entry.resource.ofType(Observation) + test-dash,Bundle.test.dash + test_underscore,Bundle.test.underscore + test'apostrophe,Bundle.test.apostrophe + """.trimIndent() + + private val shorthandTable = LookupTable.read(inputStream = ByteArrayInputStream(csv.toByteArray())) + val one = Schema(name = "None", topic = Topic.FULL_ELR, elements = emptyList()) + val metadata = Metadata(schema = one).loadLookupTable("fhirpath_filter_shorthand", shorthandTable) + val report = Report(one, listOf(listOf("1", "2")), TestSource, metadata = UnitTestUtils.simpleMetadata) + + private var actionLogger = ActionLogger() + + private fun makeFhirEngine(metadata: Metadata, settings: SettingsProvider): FHIREngine { + val rootReport = mockk() + every { rootReport.reportId } returns submittedId + every { rootReport.sendingOrg } returns "sendingOrg" + every { rootReport.sendingOrgClient } returns "sendingOrgClient" + every { reportServiceMock.getRootReport(any()) } returns rootReport + every { reportServiceMock.getRootReports(any()) } returns listOf(rootReport) + every { reportServiceMock.getRootItemIndex(any(), any()) } returns 1 + + return FHIREngine.Builder() + .metadata(metadata) + .settingsProvider(settings) + .databaseAccess(accessSpy) + .blobAccess(blobMock) + .azureEventService(azureEventService) + .reportService(reportServiceMock) + .build(TaskAction.receiver_enrichment) + } + + private fun createOrganizationWithFilteredReceivers( + jurisdictionFilter: List = emptyList(), + qualityFilter: List = emptyList(), + routingFilter: List = emptyList(), + processingModeFilter: List = emptyList(), + conditionFilter: List = emptyList(), + mappedConditionFilter: ReportStreamConditionFilter = emptyList(), + ) = DeepOrganization( + ORGANIZATION_NAME, + "test", + Organization.Jurisdiction.FEDERAL, + receivers = listOf( + Receiver( + RECEIVER_NAME, + ORGANIZATION_NAME, + Topic.FULL_ELR, + CustomerStatus.ACTIVE, + "one", + jurisdictionalFilter = jurisdictionFilter, + qualityFilter = qualityFilter, + routingFilter = routingFilter, + processingModeFilter = processingModeFilter, + conditionFilter = conditionFilter, + mappedConditionFilter = mappedConditionFilter + ), + Receiver( + "full-elr-hl7-2", + ORGANIZATION_NAME, + Topic.FULL_ELR, + CustomerStatus.INACTIVE, + "one", + jurisdictionalFilter = jurisdictionFilter, + qualityFilter = qualityFilter, + routingFilter = routingFilter, + processingModeFilter = processingModeFilter, + conditionFilter = conditionFilter, + mappedConditionFilter = mappedConditionFilter + ) + ) + ) + + @BeforeEach + fun reset() { + actionLogger = ActionLogger() + actionHistory.reportsIn.clear() + actionHistory.reportsOut.clear() + actionHistory.reportsReceived.clear() + actionHistory.actionLogs.clear() + azureEventService.events.clear() + mockkObject(BlobAccess) + clearAllMocks() + } + + @Test + fun `fail - invalid message queue type`() { + // engine setup + val settings = FileSettings().loadOrganizations( + createOrganizationWithFilteredReceivers( + qualityFilter = FILTER_FAIL + ) + ) + val engine = spyk(makeFhirEngine(metadata, settings) as FHIRReceiverEnrichment) + val messages = settings.receivers.map { + spyk( + FhirReceiverFilterQueueMessage( + UUID.randomUUID(), + BLOB_URL, + "test", + BLOB_SUB_FOLDER_NAME, + topic = Topic.FULL_ELR, + it.fullName + ) + ) + } + + // act on each message (with assert) + val exception = assertFailsWith { + messages.forEach { message -> + // act + assert + accessSpy.transact { txn -> + engine.run(message, actionLogger, actionHistory, txn) + } + } + } + assertThat(exception.message.toString()) + .contains( + "Message was not a FhirReceiverEnrichmentQueueMessage and cannot be processed by FHIRReceiverEnrichment" + ) + } + + @Test + fun `fail - missing receiver full name`() { + // engine setup + val settings = FileSettings().loadOrganizations( + createOrganizationWithFilteredReceivers( + qualityFilter = FILTER_FAIL + ) + ) + val engine = spyk(makeFhirEngine(metadata, settings) as FHIRReceiverEnrichment) + val messages = settings.receivers.map { + spyk( + FhirReceiverEnrichmentQueueMessage( + UUID.randomUUID(), + BLOB_URL, + "test", + BLOB_SUB_FOLDER_NAME, + topic = Topic.FULL_ELR, + "'missing-full-name'" + ) + ) + } + + // act on each message (with assert) + val exception = assertFailsWith { + messages.forEach { message -> + // act + assert + accessSpy.transact { txn -> + engine.run(message, actionLogger, actionHistory, txn) + } + } + } + assertThat(exception.message.toString()) + .contains( + "Receiver with name 'missing-full-name' was not found" + ) + } + + @Test + fun `test receiver enrichment`() { + mockkClass(BlobAccess::class) + mockkObject(BlobAccess.Companion) + val blobInfo = BlobAccess.BlobInfo(MimeFormat.FHIR, BLOB_URL, "test".toByteArray(Charsets.UTF_8)) + val slot = slot() + every { BlobAccess.uploadBody(any(), capture(slot), any(), any(), any()) } returns blobInfo + every { BlobAccess.Companion.getBlobConnection(any()) } returns "testconnection" + + // set up + val schemaName = ORU_R01_SCHEMA + val receiver = Receiver( + RECEIVER_NAME, + ORGANIZATION_NAME, + Topic.FULL_ELR, + CustomerStatus.ACTIVE, + schemaName, + translation = UnitTestUtils.createConfig(useTestProcessingMode = false, schemaName = schemaName), + enrichmentSchemaNames = listOf( + "classpath:/enrichments/testing.yml", + "classpath:/enrichments/testing2.yml" + ) + ) + + val testOrg = DeepOrganization( + ORGANIZATION_NAME, "test", Organization.Jurisdiction.FEDERAL, + receivers = listOf(receiver) + ) + + val settings = FileSettings().loadOrganizations(testOrg) + + val fhirData = File("src/test/resources/fhirengine/engine/valid_data_testing_sender.fhir").readText() + every { BlobAccess.downloadBlob(BLOB_URL, "test") } returns fhirData + + val engine = makeFhirEngine(metadata, settings = settings) + val messages = settings.receivers.map { + spyk( + FhirReceiverEnrichmentQueueMessage( + UUID.randomUUID(), + BLOB_URL, + "test", + BLOB_SUB_FOLDER_NAME, + topic = Topic.FULL_ELR, + "co-phd.full-elr-hl7" + ) + ) + } + messages.forEach { message -> + // act + assert + accessSpy.transact { txn -> + engine.run(message, actionLogger, actionHistory, txn) + } + } + val uploadedFhir = slot.captured.decodeToString() + val enrichedBundle = FhirTranscoder.decode(uploadedFhir) + assertThat(enrichedBundle).isNotNull() + val software = FhirPathUtils.evaluate( + null, + enrichedBundle, + enrichedBundle, + "Bundle.entry.resource.ofType(MessageHeader).source.software" + ) + .filterIsInstance() + .firstOrNull() + ?.value + val version = FhirPathUtils.evaluate( + null, + enrichedBundle, + enrichedBundle, + "Bundle.entry.resource.ofType(MessageHeader).source.version" + ) + .filterIsInstance() + .firstOrNull() + ?.value + assertThat(software).isEqualTo("Purple PRIME ReportStream") + assertThat(version).isEqualTo("0.2-YELLOW") + val vendorOrganization = FhirPathUtils.evaluate( + null, + enrichedBundle, + enrichedBundle, + "Bundle.entry.resource.ofType(MessageHeader)" + + ".source.extension('https://reportstream.cdc.gov/fhir/StructureDefinition/software-vendor-org').value" + ) + .filterIsInstance() + .firstOrNull() + ?.resource as org.hl7.fhir.r4.model.Organization + assertThat(vendorOrganization.name.toString()).isEqualTo("Orange Software Vendor Name") + } +} \ No newline at end of file diff --git a/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt b/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt index 1d03e60a0c4..93017f3c9b7 100644 --- a/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt @@ -141,11 +141,11 @@ class FhirConverterTests { "2222542&ISO||445297001^Swab of internal nose^SCT^^^^2.67||||53342003^Internal nose structure (body " + "structure)^SCT^^^^2020-09-01|||||||||202108020000-0500|20210802000006.0000-0500" - private fun makeFhirEngine(metadata: Metadata, settings: SettingsProvider, taskAction: TaskAction): FHIREngine { - return FHIREngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) + private fun makeFhirEngine(metadata: Metadata, settings: SettingsProvider, taskAction: TaskAction): FHIREngine = + FHIREngine.Builder() + .metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) .reportService(reportService).blobAccess(blobMock) .submissionTableService(mockSubmissionTableService).build(taskAction) - } @BeforeEach fun reset() { diff --git a/prime-router/src/test/kotlin/fhirengine/engine/FhirTranslatorTests.kt b/prime-router/src/test/kotlin/fhirengine/engine/FhirTranslatorTests.kt index 3f0ad2531aa..b1fe8bb35d6 100644 --- a/prime-router/src/test/kotlin/fhirengine/engine/FhirTranslatorTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/engine/FhirTranslatorTests.kt @@ -5,7 +5,6 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import ca.uhn.hl7v2.util.Hl7InputStreamMessageIterator import ca.uhn.hl7v2.util.Terser import gov.cdc.prime.router.ActionLogDetail import gov.cdc.prime.router.ActionLogger @@ -91,11 +90,9 @@ class FhirTranslatorTests { ) ), settings: SettingsProvider = FileSettings().loadOrganizations(oneOrganization), - ): FHIRTranslator { - return FHIREngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) + ): FHIRTranslator = FHIREngine.Builder().metadata(metadata).settingsProvider(settings).databaseAccess(accessSpy) .blobAccess(blobMock).reportService(reportServiceMock).reportEventService(reportStreamEventService) .build(TaskAction.translate) as FHIRTranslator - } @BeforeEach fun reset() { @@ -513,55 +510,6 @@ class FhirTranslatorTests { assertThat(terser.get(MSH_11_1)).isEqualTo("T") } - /** - * When the receiver is in production mode and sender is in testing mode, output HL7 should be 'T' - */ - @Test - fun `test receiver enrichment`() { - mockkClass(BlobAccess::class) - mockkObject(BlobAccess.Companion) - every { BlobAccess.Companion.getBlobConnection(any()) } returns "testconnection" - - // set up - val schemaName = ORU_R01_SCHEMA - val receiver = Receiver( - RECEIVER_NAME, - ORGANIZATION_NAME, - Topic.FULL_ELR, - CustomerStatus.ACTIVE, - schemaName, - translation = UnitTestUtils.createConfig(useTestProcessingMode = false, schemaName = schemaName), - enrichmentSchemaNames = listOf( - "classpath:/enrichments/testing.yml", - "classpath:/enrichments/testing2.yml" - ) - ) - - val testOrg = DeepOrganization( - ORGANIZATION_NAME, "test", Organization.Jurisdiction.FEDERAL, - receivers = listOf(receiver) - ) - - val settings = FileSettings().loadOrganizations(testOrg) - - val fhirData = File("src/test/resources/fhirengine/engine/valid_data_testing_sender.fhir").readText() - val bundle = FhirTranscoder.decode(fhirData) - - val engine = makeFhirEngine(settings = settings) - - // act - val byteArray = engine.getByteArrayFromBundle(receiver, bundle) - val messageIterator = Hl7InputStreamMessageIterator(byteArray.inputStream()) - val message = messageIterator.next() - val terser = Terser(message) - - // assert - assertThat(terser.get("SFT-1-1")).isEqualTo("Orange Software Vendor Name") - assertThat(terser.get("SFT-2")).isEqualTo("0.2-YELLOW") - // because while it will initially get set, it will then be overridden by the transform - assertThat(terser.get("SFT-3")).isEqualTo("PRIME ReportStream") - } - @Test fun `test full elr translation hl7 translation exception`() { mockkObject(BlobAccess) diff --git a/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchemaTests.kt b/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchemaTests.kt index 2a410e10d31..4d301ea5de5 100644 --- a/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchemaTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/converter/HL7ConverterSchemaTests.kt @@ -156,13 +156,11 @@ class HL7ConverterSchemaTests { @Test fun `test merge of element`() { - fun newParent(): ConverterSchemaElement { - return ConverterSchemaElement( + fun newParent(): ConverterSchemaElement = ConverterSchemaElement( "name", condition = "condition1", required = true, schema = "schema1", schemaRef = HL7ConverterSchema(), resource = "resource1", resourceIndex = "index1", value = listOf("value1"), hl7Spec = listOf("hl7spec1"), constants = sortedMapOf("k1" to "v1") ) - } val originalElement = newParent() val elementA = ConverterSchemaElement("name") diff --git a/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaTests.kt b/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaTests.kt index a7d1e63f042..8273e97869b 100644 --- a/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/translation/hl7/schema/fhirTransform/FhirTransformSchemaTests.kt @@ -142,13 +142,11 @@ class FhirTransformSchemaTests { @Test fun `test merge of element`() { - fun newParent(): FhirTransformSchemaElement { - return FhirTransformSchemaElement( + fun newParent(): FhirTransformSchemaElement = FhirTransformSchemaElement( "name", condition = "condition1", schema = "schema1", schemaRef = FhirTransformSchema(), resource = "resource1", resourceIndex = "index1", value = listOf("value1"), constants = sortedMapOf("k1" to "v1"), bundleProperty = "%resource.status" ) - } val originalElement = newParent() val elementA = FhirTransformSchemaElement("name") diff --git a/prime-router/src/test/kotlin/history/SubmissionHistoryTests.kt b/prime-router/src/test/kotlin/history/SubmissionHistoryTests.kt index 53bb97ddd05..fb2ca2f98ab 100644 --- a/prime-router/src/test/kotlin/history/SubmissionHistoryTests.kt +++ b/prime-router/src/test/kotlin/history/SubmissionHistoryTests.kt @@ -28,12 +28,10 @@ import kotlin.test.Test class SubmissionHistoryTests { @Test fun `tests consolidation of logs`() { - fun createLogs(logs: List): DetailedSubmissionHistory { - return DetailedSubmissionHistory( + fun createLogs(logs: List): DetailedSubmissionHistory = DetailedSubmissionHistory( 1, TaskAction.receive, OffsetDateTime.now(), null, mutableListOf(), logs ) - } val messageM = "message 1" val messageA = "A message 2" diff --git a/prime-router/src/test/kotlin/history/azure/DeliveryFunctionTests.kt b/prime-router/src/test/kotlin/history/azure/DeliveryFunctionTests.kt index b059d88242f..1900189292f 100644 --- a/prime-router/src/test/kotlin/history/azure/DeliveryFunctionTests.kt +++ b/prime-router/src/test/kotlin/history/azure/DeliveryFunctionTests.kt @@ -88,17 +88,12 @@ class DeliveryFunctionTests : Logging { private val oktaClaimsOrganizationName = "DHSender_$organizationName" private val otherOrganizationName = "test-lab-2" - private fun buildClaimsMap(organizationName: String): Map { - return mapOf( + private fun buildClaimsMap(organizationName: String): Map = mapOf( "sub" to "test", "organization" to listOf(organizationName) ) - } - data class ExpectedAPIResponse( - val status: HttpStatus, - val body: List? = null, - ) + data class ExpectedAPIResponse(val status: HttpStatus, val body: List? = null) data class DeliveryUnitTestCase( val headers: Map, diff --git a/prime-router/src/test/kotlin/history/azure/ReportFileFunctionTests.kt b/prime-router/src/test/kotlin/history/azure/ReportFileFunctionTests.kt index 17686ac1818..4f261bf316b 100644 --- a/prime-router/src/test/kotlin/history/azure/ReportFileFunctionTests.kt +++ b/prime-router/src/test/kotlin/history/azure/ReportFileFunctionTests.kt @@ -10,7 +10,7 @@ import kotlin.test.Test class ReportFileFunctionTests { @Nested - inner class HistoryApiParameters() { + inner class HistoryApiParameters { @Test fun `test fileName gets encoded`() { val httpRequestMessage = MockHttpRequestMessage() diff --git a/prime-router/src/test/kotlin/history/azure/SubmissionFunctionTests.kt b/prime-router/src/test/kotlin/history/azure/SubmissionFunctionTests.kt index caa067dcdea..ae4d3398308 100644 --- a/prime-router/src/test/kotlin/history/azure/SubmissionFunctionTests.kt +++ b/prime-router/src/test/kotlin/history/azure/SubmissionFunctionTests.kt @@ -82,17 +82,12 @@ class SubmissionFunctionTests : Logging { private val oktaClaimsOrganizationName = "DHSender_$organizationName" private val otherOrganizationName = "test-lab-2" - private fun buildClaimsMap(organizationName: String): Map { - return mapOf( + private fun buildClaimsMap(organizationName: String): Map = mapOf( "sub" to "test", "organization" to listOf(organizationName) ) - } - data class ExpectedAPIResponse( - val status: HttpStatus, - val body: List? = null, - ) + data class ExpectedAPIResponse(val status: HttpStatus, val body: List? = null) data class SubmissionUnitTestCase( val headers: Map, diff --git a/prime-router/src/test/kotlin/metadata/LivdLookupTests.kt b/prime-router/src/test/kotlin/metadata/LivdLookupTests.kt index 12b383769ad..4511a30c749 100644 --- a/prime-router/src/test/kotlin/metadata/LivdLookupTests.kt +++ b/prime-router/src/test/kotlin/metadata/LivdLookupTests.kt @@ -430,9 +430,10 @@ class LivdLookupTests { * be used as input to a mapper. * @return the element value object with the data */ - private fun createValue(element: Element, deviceindex: Int): ElementAndValue { - return ElementAndValue(element, getDeviceCol(deviceindex, element)) - } + private fun createValue( + element: Element, + deviceindex: Int, + ): ElementAndValue = ElementAndValue(element, getDeviceCol(deviceindex, element)) @Test fun `test LIVD apply mapper lookup logic`() { diff --git a/prime-router/src/test/kotlin/metadata/LivdMapperTests.kt b/prime-router/src/test/kotlin/metadata/LivdMapperTests.kt index 4e221b5a3a1..0b9f77e96ca 100644 --- a/prime-router/src/test/kotlin/metadata/LivdMapperTests.kt +++ b/prime-router/src/test/kotlin/metadata/LivdMapperTests.kt @@ -299,9 +299,10 @@ class LivdMapperTests { * be used as input to a mapper. * @return the element value object with the data */ - private fun createValue(element: Element, deviceindex: Int): ElementAndValue { - return ElementAndValue(element, getDeviceCol(deviceindex, element)) - } + private fun createValue( + element: Element, + deviceindex: Int, + ): ElementAndValue = ElementAndValue(element, getDeviceCol(deviceindex, element)) @Test fun `test LIVD apply mapper lookup logic`() { diff --git a/prime-router/src/test/kotlin/tokens/Server2ServerAuthenticationTests.kt b/prime-router/src/test/kotlin/tokens/Server2ServerAuthenticationTests.kt index 034a6cdfaf2..aeb63de971f 100644 --- a/prime-router/src/test/kotlin/tokens/Server2ServerAuthenticationTests.kt +++ b/prime-router/src/test/kotlin/tokens/Server2ServerAuthenticationTests.kt @@ -123,13 +123,12 @@ class Server2ServerAuthenticationTests { // Good for testing: Each time you create a new GetTestSecret() obj, its a totally new secret. private val tokenSigningSecret = this.generateSecret() - override fun getReportStreamTokenSigningSecret(): SecretKey { - return Keys.hmacShaKeyFor(Decoders.BASE64.decode(tokenSigningSecret)) - } + override fun getReportStreamTokenSigningSecret(): SecretKey = + Keys.hmacShaKeyFor(Decoders.BASE64.decode(tokenSigningSecret)) - private fun generateSecret(): String { - return Encoders.BASE64.encode(Keys.secretKeyFor(TOKEN_SIGNING_KEY_ALGORITHM).encoded) - } + private fun generateSecret(): String = + Encoders.BASE64 + .encode(Keys.secretKeyFor(TOKEN_SIGNING_KEY_ALGORITHM).encoded) } @BeforeEach diff --git a/prime-router/src/test/kotlin/tokens/TestDefaultJwt.kt b/prime-router/src/test/kotlin/tokens/TestDefaultJwt.kt index 3717b03201c..dde35a51406 100644 --- a/prime-router/src/test/kotlin/tokens/TestDefaultJwt.kt +++ b/prime-router/src/test/kotlin/tokens/TestDefaultJwt.kt @@ -5,31 +5,18 @@ import java.time.Instant import java.util.Collections import kotlin.collections.LinkedHashMap -class TestDefaultJwt( - tokenValue: String, - issuedAt: Instant, - expiresAt: Instant, - claims: Map, -) : Jwt { +class TestDefaultJwt(tokenValue: String, issuedAt: Instant, expiresAt: Instant, claims: Map) : Jwt { private val tokenValue: String private val claims: Map private val issuedAt: Instant private val expiresAt: Instant - override fun getTokenValue(): String { - return tokenValue - } + override fun getTokenValue(): String = tokenValue - override fun getIssuedAt(): Instant { - return issuedAt - } + override fun getIssuedAt(): Instant = issuedAt - override fun getExpiresAt(): Instant { - return expiresAt - } + override fun getExpiresAt(): Instant = expiresAt - override fun getClaims(): Map { - return claims - } + override fun getClaims(): Map = claims init { this.tokenValue = tokenValue diff --git a/prime-router/src/test/kotlin/unittest/UnitTestUtils.kt b/prime-router/src/test/kotlin/unittest/UnitTestUtils.kt index d6b908f9e87..0812ac9b0f7 100644 --- a/prime-router/src/test/kotlin/unittest/UnitTestUtils.kt +++ b/prime-router/src/test/kotlin/unittest/UnitTestUtils.kt @@ -47,8 +47,7 @@ object UnitTestUtils { useTestProcessingMode: Boolean = false, convertTimestampToDateTime: String? = null, schemaName: String = "covid-19", - ): Hl7Configuration { - return Hl7Configuration( + ): Hl7Configuration = Hl7Configuration( messageProfileId = "", receivingApplicationOID = "", receivingApplicationName = "", @@ -68,7 +67,6 @@ object UnitTestUtils { schemaName = schemaName, convertTimestampToDateTime = convertTimestampToDateTime ) - } fun createCustomContext( bundle: Bundle = Bundle(), @@ -77,8 +75,7 @@ object UnitTestUtils { customFhirFunctions: FhirPathFunctions? = null, config: ContextConfig? = null, translationFunctions: TranslationFunctions? = Hl7TranslationFunctions(), - ): CustomContext { - return CustomContext( + ): CustomContext = CustomContext( bundle, focusResource, constants, @@ -86,5 +83,4 @@ object UnitTestUtils { config, translationFunctions ) - } } \ No newline at end of file diff --git a/prime-router/src/test/kotlin/validation/MessageValidatorTests.kt b/prime-router/src/test/kotlin/validation/MessageValidatorTests.kt index 37aabefa7b2..866f77a5933 100644 --- a/prime-router/src/test/kotlin/validation/MessageValidatorTests.kt +++ b/prime-router/src/test/kotlin/validation/MessageValidatorTests.kt @@ -20,7 +20,7 @@ private const val radxmarsProfileLocation = "metadata/hl7_validation/v251/radxma class MessageValidatorTests { @Nested - inner class MessageValidationResultTests() { + inner class MessageValidationResultTests { @Test fun `test HL7ValidationResult is valid`() { diff --git a/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt b/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt index f18c57cfbdc..0855e0dcd52 100644 --- a/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt +++ b/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt @@ -255,8 +255,7 @@ class TranslationTests { * Get the report format from the extension of a [filename]. * @return the report format */ - private fun getFormat(filename: String): MimeFormat { - return when { + private fun getFormat(filename: String): MimeFormat = when { File(filename).extension.uppercase() == "INTERNAL" || filename.uppercase().endsWith("INTERNAL.CSV") -> { MimeFormat.INTERNAL } @@ -273,7 +272,6 @@ class TranslationTests { MimeFormat.CSV } } - } /** * Perform test based on the given configuration. diff --git a/prime-router/src/testIntegration/kotlin/serializers/Hl7SerializerIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/serializers/Hl7SerializerIntegrationTests.kt index e5bfde140f6..ec3dc8bea3a 100644 --- a/prime-router/src/testIntegration/kotlin/serializers/Hl7SerializerIntegrationTests.kt +++ b/prime-router/src/testIntegration/kotlin/serializers/Hl7SerializerIntegrationTests.kt @@ -105,8 +105,7 @@ NTE|1|L|This is a final comment|RE stripInvalidCharsRegex: String? = null, replaceUnicodeWithAscii: Boolean = false, useBatchHeaders: Boolean = false, - ): Hl7Configuration { - return Hl7Configuration( + ): Hl7Configuration = Hl7Configuration( messageProfileId = "", receivingApplicationOID = "", receivingApplicationName = "", @@ -124,7 +123,6 @@ NTE|1|L|This is a final comment|RE replaceUnicodeWithAscii = replaceUnicodeWithAscii, useBatchHeaders = useBatchHeaders ) - } @Test fun `test write batch`() { diff --git a/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt index eb8032f034b..c2466683b57 100644 --- a/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt +++ b/prime-router/src/testIntegration/kotlin/transport/AS2TransportIntegrationTests.kt @@ -56,6 +56,7 @@ class AS2TransportIntegrationTests { null, null, null, + null, null ) private val reportFile = ReportFile( diff --git a/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt index c7d7682a6b4..8018f54e0b7 100644 --- a/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt +++ b/prime-router/src/testIntegration/kotlin/transport/GAENTransportIntegrationTests.kt @@ -31,15 +31,13 @@ class GAENTransportIntegrationTests : TransportIntegrationTests() { url: String, status: HttpStatusCode, body: String, - ): GAENTransport { - return GAENTransport( + ): GAENTransport = GAENTransport( ApiMockEngineForIntegrationTests( url, status, body ).client() ) - } private val metadata = Metadata.getInstance() private val settings = FileSettings(FileSettings.defaultSettingsDirectory) @@ -85,6 +83,7 @@ class GAENTransportIntegrationTests : TransportIntegrationTests() { null, null, null, + null, null ) diff --git a/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt index 9c1e3bb17a1..0f94369a44d 100644 --- a/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt +++ b/prime-router/src/testIntegration/kotlin/transport/RESTTransportIntegrationTests.kt @@ -66,91 +66,68 @@ class RESTTransportIntegrationTests : TransportIntegrationTests() { private val settings = FileSettings(FileSettings.defaultSettingsDirectory) private val responseHeaders = headersOf("Content-Type" to listOf("application/json;charset=UTF-8")) - private fun mockClientStringTokenOk(): HttpClient { - return mockJsonResponseWithSuccess( + private fun mockClientStringTokenOk(): HttpClient = mockJsonResponseWithSuccess( "RjY2NjM5NzA2OWJjuE7c" ) - } - private fun mockClientAuthOk(): HttpClient { - return mockJsonResponseWithSuccess( + private fun mockClientAuthOk(): HttpClient = mockJsonResponseWithSuccess( """{"access_token": "AYjcyMzY3ZDhiNmJkNTY", |"refresh_token": "RjY2NjM5NzA2OWJjuE7c", |"token_type": "Bearer", "expires_in": 3600} """ ) - } - private fun mockClientAuthIDTokenOk(): HttpClient { - return mockJsonResponseWithSuccess( + private fun mockClientAuthIDTokenOk(): HttpClient = mockJsonResponseWithSuccess( """{"email": "test-email@test.com", "idToken": "AYjcyMzY3ZDhiNmJkNTY", |"expiresIn": 3600, "refreshToken": "RjY2NjM5NzA2OWJjuE7c"} """ ) - } - private fun mockClientAuthError(): HttpClient { - return mockJsonResponseWithError( + private fun mockClientAuthError(): HttpClient = mockJsonResponseWithError( """{"error": {"code": 500,"message": "Mock internal server error."}}""" ) - } - private fun mockClientUnauthorized(): HttpClient { - return mockJsonResponseWithUnauthorized( + private fun mockClientUnauthorized(): HttpClient = mockJsonResponseWithUnauthorized( """{"error": {"code": 401,"message": "Mock unauthorized error."}}""" ) - } - private fun mockClientPostOk(): HttpClient { - return mockJsonResponseWithSuccess( + private fun mockClientPostOk(): HttpClient = mockJsonResponseWithSuccess( """{"status": "Success", |"statusDesc": "Received. LIN:4299844", |"respTrackingId": "UT-20211119-746000000-54"} """ ) - } - private fun mockClientPostError(): HttpClient { - return mockJsonResponseWithError( + private fun mockClientPostError(): HttpClient = mockJsonResponseWithError( """{"error": {"code": 500,"message": "Mock internal server error."}}""" ) - } - private fun mockClientTooManyRequests(): HttpClient { - return mockJsonResponseWithTooManyRequests( + private fun mockClientTooManyRequests(): HttpClient = mockJsonResponseWithTooManyRequests( """{"error": {"code": 429,"message": "Mock too many requests error."}}""" ) - } - private fun mockClientUnknownError(): HttpClient { - return mockJsonResponseWithUnknown( + private fun mockClientUnknownError(): HttpClient = mockJsonResponseWithUnknown( """{"error": {"code": 999,"message": "Mock internal server error."}}""" ) - } - private fun mockJsonResponseWithSuccess(jsonResponse: String): HttpClient { - return mockJsonResponse(jsonResponse, HttpStatusCode.OK) - } + private fun mockJsonResponseWithSuccess(jsonResponse: String): HttpClient = + mockJsonResponse(jsonResponse, HttpStatusCode.OK) - private fun mockJsonResponseWithError(jsonResponse: String): HttpClient { - return mockJsonResponse(jsonResponse, HttpStatusCode.InternalServerError) - } + private fun mockJsonResponseWithError(jsonResponse: String): HttpClient = + mockJsonResponse(jsonResponse, HttpStatusCode.InternalServerError) - private fun mockJsonResponseWithUnauthorized(jsonResponse: String): HttpClient { - return mockJsonResponse(jsonResponse, HttpStatusCode.Unauthorized) - } + private fun mockJsonResponseWithUnauthorized(jsonResponse: String): HttpClient = + mockJsonResponse(jsonResponse, HttpStatusCode.Unauthorized) - private fun mockJsonResponseWithTooManyRequests(jsonResponse: String): HttpClient { - return mockJsonResponse(jsonResponse, HttpStatusCode.TooManyRequests) - } + private fun mockJsonResponseWithTooManyRequests(jsonResponse: String): HttpClient = + mockJsonResponse(jsonResponse, HttpStatusCode.TooManyRequests) - private fun mockJsonResponseWithUnknown(jsonResponse: String): HttpClient { - return mockJsonResponse(jsonResponse, HttpStatusCode(999, "Uknown Error")) - } + private fun mockJsonResponseWithUnknown(jsonResponse: String): HttpClient = + mockJsonResponse(jsonResponse, HttpStatusCode(999, "Uknown Error")) - private fun mockJsonResponse(jsonResponse: String, whatStatusCode: HttpStatusCode): HttpClient { - return HttpClient(MockEngine) { + private fun mockJsonResponse(jsonResponse: String, whatStatusCode: HttpStatusCode): HttpClient = + HttpClient(MockEngine) { engine { addHandler { respond( @@ -162,7 +139,6 @@ class RESTTransportIntegrationTests : TransportIntegrationTests() { } install(ClientContentNegotiation) { json() } } - } private var actionHistory = ActionHistory(TaskAction.send) private val transportType = RESTTransportType( @@ -249,6 +225,7 @@ hnm8COa8Kr+bnTqzScpQuOfujHcFEtfcYUGfSS6HusxidwXx+lYi1A== null, null, null, + null, null ) diff --git a/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt index 578f3997a6d..83a4120e050 100644 --- a/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt +++ b/prime-router/src/testIntegration/kotlin/transport/SftpTransportIntegrationTests.kt @@ -88,6 +88,7 @@ class SftpTransportIntegrationTests : TransportIntegrationTests() { null, null, null, + null, null ) val contents = "HL7|Stuff" diff --git a/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt b/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt index 36bbf5221ed..1d5e6337817 100644 --- a/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt +++ b/prime-router/src/testIntegration/kotlin/transport/SoapTransportIntegrationTests.kt @@ -83,6 +83,7 @@ class SoapTransportIntegrationTests : TransportIntegrationTests() { null, null, null, + null, null ) diff --git a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/BlobUtils.kt b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/BlobUtils.kt index cff4360ffc4..0eec4ef0ea4 100644 --- a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/BlobUtils.kt +++ b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/BlobUtils.kt @@ -7,23 +7,19 @@ object BlobUtils { /** * Create a hex string style of a digest. */ - fun digestToString(digest: ByteArray): String { - return digest.joinToString(separator = "", limit = 40) { Integer.toHexString(it.toInt()) } - } + fun digestToString( + digest: ByteArray, + ): String = digest.joinToString(separator = "", limit = 40) { Integer.toHexString(it.toInt()) } /** * Hash a ByteArray [input] with SHA 256 */ - fun sha256Digest(input: ByteArray): ByteArray { - return hashBytes("SHA-256", input) - } + fun sha256Digest(input: ByteArray): ByteArray = hashBytes("SHA-256", input) /** * Hash a ByteArray [input] with method [type] */ - private fun hashBytes(type: String, input: ByteArray): ByteArray { - return MessageDigest + private fun hashBytes(type: String, input: ByteArray): ByteArray = MessageDigest .getInstance(type) .digest(input) - } } \ No newline at end of file diff --git a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/QueueMessage.kt b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/QueueMessage.kt index 06146f7bf86..b7df845e99d 100644 --- a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/QueueMessage.kt +++ b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/QueueMessage.kt @@ -90,6 +90,11 @@ interface QueueMessage { */ const val elrTranslationQueueName = "elr-fhir-translate" + /** + * Constant for receiver enrichment queue on UP. + */ + const val elrReceiverEnrichmentQueueName = "elr-fhir-receiver-enrichment" + /** * Constant for send queue */ diff --git a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/StringUtilities.kt b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/StringUtilities.kt index 5e3d4d8d9c1..3bf231c6382 100644 --- a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/StringUtilities.kt +++ b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/StringUtilities.kt @@ -28,9 +28,7 @@ object StringUtilities { * usage: * request.queryParameters['foo']?.toIntOrDefault(30) */ - fun String?.toIntOrDefault(default: Int = 0): Int { - return this?.toIntOrNull() ?: default - } + fun String?.toIntOrDefault(default: Int = 0): Int = this?.toIntOrNull() ?: default /** * Trim and truncate the string to the [maxLength] preserving as much of the non-whitespace as possible diff --git a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/Submission.kt b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/Submission.kt index 8d8dd2e11d2..32b462b2f38 100644 --- a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/Submission.kt +++ b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/Submission.kt @@ -32,15 +32,13 @@ data class Submission( * @param tableEntity The TableEntity to convert. * @return The corresponding SubmissionEntity. */ - fun fromTableEntity(tableEntity: TableEntity): Submission { - return Submission( + fun fromTableEntity(tableEntity: TableEntity): Submission = Submission( submissionId = tableEntity.partitionKey, status = tableEntity.rowKey, bodyURL = tableEntity.getProperty("body_url") as String, detail = tableEntity.getProperty("detail") as String?, timestamp = tableEntity.timestamp ) - } } /** @@ -50,13 +48,11 @@ data class Submission( * * @return A TableEntity object that can be inserted into an Azure Table. */ - fun toTableEntity(): TableEntity { - return TableEntity(submissionId, status) + fun toTableEntity(): TableEntity = TableEntity(submissionId, status) .setProperties( mapOf( "body_url" to bodyURL, "detail" to detail.takeIf { it != null } ) ) - } } \ No newline at end of file diff --git a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/AuthZService.kt b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/AuthZService.kt index 52668e7cfd3..7ea312e3df7 100644 --- a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/AuthZService.kt +++ b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/AuthZService.kt @@ -6,9 +6,7 @@ import gov.cdc.prime.reportstream.shared.auth.jwt.OktaGroupsJWTReader /** * Shared authorization service to allow routes to check if an incoming request should be allowed access */ -class AuthZService( - private val oktaGroupsJWTReader: OktaGroupsJWTReader, -) { +class AuthZService(private val oktaGroupsJWTReader: OktaGroupsJWTReader) { private val adminGroup = "DHPrimeAdmins" private val senderPrefix = "DHSender_" @@ -19,19 +17,21 @@ class AuthZService( * This function takes in a request headers function to be web framework agnostic. It will * do the work of reading the Okta-Groups header, parsing the JWT, and checking the values within. */ - fun isSenderAuthorized(clientId: String, requestHeaderFn: (String) -> String?): Boolean { - return requestHeaderFn(OktaGroupsJWTConstants.OKTA_GROUPS_HEADER)?.let { oktaGroupsHeader -> + fun isSenderAuthorized( + clientId: String, + requestHeaderFn: (String) -> String?, + ): Boolean = requestHeaderFn(OktaGroupsJWTConstants.OKTA_GROUPS_HEADER)?.let { oktaGroupsHeader -> val oktaGroupsJWT = oktaGroupsJWTReader.read(oktaGroupsHeader) isSenderAuthorized(clientId, oktaGroupsJWT.groups) } ?: false - } /** * Simpler sender authorization check function that assumings you have the JWT parsing already completed */ - fun isSenderAuthorized(clientId: String, oktaGroups: List): Boolean { - return oktaGroups.any { senderAuthorized(clientId, it) } - } + fun isSenderAuthorized( + clientId: String, + oktaGroups: List, + ): Boolean = oktaGroups.any { senderAuthorized(clientId, it) } /** * Check that a sender matches our client id @@ -43,8 +43,7 @@ class AuthZService( * clientId=org.test, oktaGroup=DHSender_org, authorized=true * clientId=org.test, oktaGroup=DHSender_differentOrg, authorized=false */ - private fun senderAuthorized(clientId: String, oktaGroup: String): Boolean { - return if (oktaGroup == adminGroup) { + private fun senderAuthorized(clientId: String, oktaGroup: String): Boolean = if (oktaGroup == adminGroup) { true } else if (oktaGroup.startsWith(senderPrefix)) { val oktaOrganization = oktaGroup @@ -60,5 +59,4 @@ class AuthZService( } else { false } - } } \ No newline at end of file diff --git a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWT.kt b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWT.kt index 2c05b37bdf6..f9c44a4aa1b 100644 --- a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWT.kt +++ b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWT.kt @@ -3,7 +3,4 @@ package gov.cdc.prime.reportstream.shared.auth.jwt /** * Model containing the useful fields from our Okta Groups JWT */ -data class OktaGroupsJWT( - val appId: String, - val groups: List, -) \ No newline at end of file +data class OktaGroupsJWT(val appId: String, val groups: List) \ No newline at end of file diff --git a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWTReader.kt b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWTReader.kt index 3f3c7c116f3..afaa90ef852 100644 --- a/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWTReader.kt +++ b/shared/src/main/kotlin/gov/cdc/prime/reportstream/shared/auth/jwt/OktaGroupsJWTReader.kt @@ -11,9 +11,7 @@ import gov.cdc.prime.reportstream.shared.StringUtilities.base64Decode /** * Common Okta Groups JWT reader and validator */ -class OktaGroupsJWTReader( - publicKey: JWK, -) { +class OktaGroupsJWTReader(publicKey: JWK) { constructor(encodedJWK: String) : this(JWK.parse(encodedJWK.base64Decode())) 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 b82c0993a31..f1432b3a8cd 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 @@ -18,7 +18,4 @@ data class SubmissionReceivedEvent( val pipelineStepName: String, ) -data class SubmissionDetails( - val headers: Map, - val queryParameters: Map>, -) \ No newline at end of file +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/AuthConfig.kt b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AuthConfig.kt index 88ad05e1115..82b83f684bf 100644 --- a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AuthConfig.kt +++ b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AuthConfig.kt @@ -11,9 +11,7 @@ import org.springframework.context.annotation.Configuration * our configured public key */ @Configuration -class AuthConfig( - private val jwtKeyConfig: JWTKeyConfig, -) { +class AuthConfig(private val jwtKeyConfig: JWTKeyConfig) { @Bean fun authZService(): AuthZService { @@ -22,7 +20,5 @@ class AuthConfig( } @ConfigurationProperties(prefix = "auth") - data class JWTKeyConfig( - val jwtEncodedPublicKeyJWK: String, - ) + data class JWTKeyConfig(val jwtEncodedPublicKeyJWK: String) } \ No newline at end of file diff --git a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AzureConfig.kt b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AzureConfig.kt index f46897339fe..c28109762d1 100644 --- a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AzureConfig.kt +++ b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/config/AzureConfig.kt @@ -42,12 +42,10 @@ class AzureConfig { } @Bean - fun queueClient(): QueueClient { - return QueueServiceClientBuilder() + fun queueClient(): QueueClient = QueueServiceClientBuilder() .connectionString(connectionString) .buildClient() .getQueueClient(queueName) - } @Bean fun tableClient(): TableClient { diff --git a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/HealthController.kt b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/HealthController.kt index b51bdb4f72d..afe004e70d6 100644 --- a/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/HealthController.kt +++ b/submissions/src/main/kotlin/gov/cdc/prime/reportstream/submissions/controllers/HealthController.kt @@ -6,7 +6,5 @@ import org.springframework.web.bind.annotation.RestController @RestController class HealthController { @GetMapping("/health") - suspend fun health(): String { - return "up" - } + suspend fun health(): String = "up" } \ No newline at end of file diff --git a/submissions/src/test/kotlin/SubmissionControllerTest.kt b/submissions/src/test/kotlin/SubmissionControllerTest.kt index 59f0ae710b4..fbf2ed91474 100644 --- a/submissions/src/test/kotlin/SubmissionControllerTest.kt +++ b/submissions/src/test/kotlin/SubmissionControllerTest.kt @@ -71,29 +71,19 @@ class SubmissionControllerTest { @TestConfiguration class Config { @Bean - fun blobContainerClient(): BlobContainerClient { - return mock() - } + fun blobContainerClient(): BlobContainerClient = mock() @Bean - fun queueClient(): QueueClient { - return mock() - } + fun queueClient(): QueueClient = mock() @Bean - fun tableClient(): TableClient { - return mock() - } + fun tableClient(): TableClient = mock() @Bean - fun telemetryService(): TelemetryService { - return mock() - } + fun telemetryService(): TelemetryService = mock() @Bean - fun authZService(): AuthZService { - return mock() - } + fun authZService(): AuthZService = mock() } private lateinit var objectMapper: ObjectMapper @@ -328,8 +318,7 @@ class SubmissionControllerTest { @Test fun `submitReport should log SUBMISSION_RECEIVED with correct details`() { // Helper function to safely cast the captured map to Map - fun mapToStringString(input: Map<*, *>): Map { - return input.mapNotNull { (key, value) -> + fun mapToStringString(input: Map<*, *>): Map = input.mapNotNull { (key, value) -> val stringKey = key as? String val stringValue = value as? String if (stringKey != null && stringValue != null) { @@ -338,7 +327,6 @@ class SubmissionControllerTest { null } }.toMap() - } val data = mapOf("key" to "value") val requestBody = objectMapper.writeValueAsString(data)