From bcf34d93685761d27759368b96ed1d7b85fe63da Mon Sep 17 00:00:00 2001 From: Abi Philip Date: Mon, 6 Jan 2025 17:03:31 -0600 Subject: [PATCH 01/13] Updated Washington STLT to hard code MSH 4 and filter for ONLY Positive Covid --- prime-router/settings/STLTs/WA/wa-phd.yml | 1 + .../receivers/STLTs/WA/WA-receiver-transform.yml | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/prime-router/settings/STLTs/WA/wa-phd.yml b/prime-router/settings/STLTs/WA/wa-phd.yml index b4c3e2d43b7..5c7ae4ba5f4 100644 --- a/prime-router/settings/STLTs/WA/wa-phd.yml +++ b/prime-router/settings/STLTs/WA/wa-phd.yml @@ -90,6 +90,7 @@ reverseTheQualityFilter: false conditionFilter: #Accept COVID only, "matches(abnormal_flag, A)" + #Verified that Covid only, "matches(abnormal_flag, A)" Abi Philip Jan 6 2025 - "%resource.interpretation.coding.code = 'A' and (%resource.code.coding.extension('https://reportstream.cdc.gov/fhir/StructureDefinition/condition-code').value.where(code in ('840539006')).exists())" mappedConditionFilter: [] deidentify: false diff --git a/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml b/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml index 8d233b16b8d..30c7c344195 100644 --- a/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml +++ b/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml @@ -42,6 +42,18 @@ elements: value: [ '"ISO"' ] hl7Spec: [ '%{MSH}-3-3' ] + - name: wa-sending-facility-namespace-id + value: [ '"7uycso49' ] + hl7Spec: [ '%{MSH}-4-1' ] + + - name: wa-sending-facility-universal-id + value: [ '"1.3.6.1.4.1.38630.2.1.1.519"' ] + hl7Spec: [ '%{MSH}-4-2' ] + + - name: wa-sending-facility-universal-id-type + value: [ '"ISO"' ] + hl7Spec: [ '%{MSH}-4-3' ] + - name: wa-receiving-application-namespace-id value: [ '"WADOHPHRED"' ] hl7Spec: [ '%{MSH}-5-1' ] From 50f46917b00cc9592210922488dc2eebf1f4ac25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 01:01:42 -0600 Subject: [PATCH 02/13] Bump the fetching group across 1 directory with 2 updates (#16980) Bumps the fetching group with 2 updates in the /frontend-react directory: [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) and [@tanstack/react-query-devtools](https://github.com/TanStack/query/tree/HEAD/packages/react-query-devtools). Updates `@tanstack/react-query` from 5.62.11 to 5.62.16 - [Release notes](https://github.com/TanStack/query/releases) - [Commits](https://github.com/TanStack/query/commits/v5.62.16/packages/react-query) Updates `@tanstack/react-query-devtools` from 5.62.11 to 5.62.16 - [Release notes](https://github.com/TanStack/query/releases) - [Commits](https://github.com/TanStack/query/commits/v5.62.16/packages/react-query-devtools) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fetching - dependency-name: "@tanstack/react-query-devtools" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: fetching ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend-react/package.json | 4 ++-- frontend-react/yarn.lock | 42 ++++++++++++++++++------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/frontend-react/package.json b/frontend-react/package.json index cbb39055b49..d5f5e5156c6 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -10,8 +10,8 @@ "@okta/okta-react": "^6.9.0", "@okta/okta-signin-widget": "^7.26.1", "@rest-hooks/rest": "^3.0.3", - "@tanstack/react-query": "^5.62.11", - "@tanstack/react-query-devtools": "^5.62.11", + "@tanstack/react-query": "^5.62.16", + "@tanstack/react-query-devtools": "^5.62.16", "@trussworks/react-uswds": "^9.1.0", "@uswds/uswds": "3.7.1", "axios": "^1.7.9", diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index 4c930a6b876..d3e11045b5e 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -2459,40 +2459,40 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:5.62.9": - version: 5.62.9 - resolution: "@tanstack/query-core@npm:5.62.9" - checksum: 3dc3ca1654a3e3a3f92e569d2bf9e8c09b6bb670562090af357db7e95175ad0397a01dfd2c2eda34bdfe0666e7d6fb841d0c435223119681b1ba1f28d4fcd6b1 +"@tanstack/query-core@npm:5.62.16": + version: 5.62.16 + resolution: "@tanstack/query-core@npm:5.62.16" + checksum: 96e712dba70b9234884108ecac2fa05ae588f7a1758377d80aa30d4e830f00cbef95309d2806828ca224d1db51e3c8364857a6f3581edb989f2f16cb98ad0f26 languageName: node linkType: hard -"@tanstack/query-devtools@npm:5.62.9": - version: 5.62.9 - resolution: "@tanstack/query-devtools@npm:5.62.9" - checksum: 08415a0bc3661f088cecc44cecbd782eb54aef8e6ed05f6d7d56e92e96f2933f52a73d227e5925f141f46068ea68e6521a01f7773a96057ef96523c738b26e36 +"@tanstack/query-devtools@npm:5.62.16": + version: 5.62.16 + resolution: "@tanstack/query-devtools@npm:5.62.16" + checksum: 1b6554334ef00818ae4ba3cea1a369c70ce2d9605e7c44de3dd802cce355a8d559108f93a68cd5df5de7e5eeee4e89259577cf83611a43f62f67f27fc9cdb61a languageName: node linkType: hard -"@tanstack/react-query-devtools@npm:^5.62.11": - version: 5.62.11 - resolution: "@tanstack/react-query-devtools@npm:5.62.11" +"@tanstack/react-query-devtools@npm:^5.62.16": + version: 5.62.16 + resolution: "@tanstack/react-query-devtools@npm:5.62.16" dependencies: - "@tanstack/query-devtools": 5.62.9 + "@tanstack/query-devtools": 5.62.16 peerDependencies: - "@tanstack/react-query": ^5.62.11 + "@tanstack/react-query": ^5.62.16 react: ^18 || ^19 - checksum: c16732d639a91060dfaa68dd9e41c57b13baf626523034f944759196293e861c37596d2ecddf9d5de6d85981a98513b18ef4a2d5cb6d470d4260cd12039e1df1 + checksum: 1acd07c82cd37a73731e180b4511025d33143a5820c09450143b81bde24871a7f7bccb44d6d488583dc319ae3ed22779bd38157836d37e2673a1c9efa004a3b6 languageName: node linkType: hard -"@tanstack/react-query@npm:^5.62.11": - version: 5.62.11 - resolution: "@tanstack/react-query@npm:5.62.11" +"@tanstack/react-query@npm:^5.62.16": + version: 5.62.16 + resolution: "@tanstack/react-query@npm:5.62.16" dependencies: - "@tanstack/query-core": 5.62.9 + "@tanstack/query-core": 5.62.16 peerDependencies: react: ^18 || ^19 - checksum: dcd0fc21eead400f385299f3afd230a98884a8eda5fe55e1803bf0b2657ddf109509afcb12f3c23ef143ec8debcbece908c99c59729c3ab911068619015c0eb7 + checksum: dbf9cf549799d96ecefea237617e15234d9fa446460efe5286e128840b1bcfff5d7ff8a9eb9208aefe469800b50fa2ad31d93e631be3e0a58baac61a3094fb33 languageName: node linkType: hard @@ -9827,8 +9827,8 @@ __metadata: "@storybook/react-vite": ^8.4.7 "@storybook/testing-library": ^0.2.2 "@storybook/theming": ^8.4.7 - "@tanstack/react-query": ^5.62.11 - "@tanstack/react-query-devtools": ^5.62.11 + "@tanstack/react-query": ^5.62.16 + "@tanstack/react-query-devtools": ^5.62.16 "@testing-library/dom": ^10.4.0 "@testing-library/jest-dom": ^6.6.3 "@testing-library/react": ^16.1.0 From ea8f4d375e52983fe5b44c67ce7c1765c258e519 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 07:13:15 +0000 Subject: [PATCH 03/13] Bump the utils group across 1 directory with 2 updates (#16979) Bumps the utils group with 2 updates in the /frontend-react directory: [uuid](https://github.com/uuidjs/uuid) and [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash). Updates `uuid` from 11.0.3 to 11.0.4 - [Release notes](https://github.com/uuidjs/uuid/releases) - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md) - [Commits](https://github.com/uuidjs/uuid/compare/v11.0.3...v11.0.4) Updates `@types/lodash` from 4.17.13 to 4.17.14 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-patch dependency-group: utils - dependency-name: "@types/lodash" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: utils ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend-react/package.json | 4 ++-- frontend-react/yarn.lock | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend-react/package.json b/frontend-react/package.json index d5f5e5156c6..c48ecb8c60c 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -42,7 +42,7 @@ "sanitize-html": "^2.14.0", "tsx": "^4.19.2", "use-deep-compare-effect": "^1.8.1", - "uuid": "^11.0.3", + "uuid": "^11.0.4", "web-vitals": "^3.4.0" }, "scripts": { @@ -137,7 +137,7 @@ "@types/eslint__js": "^8.42.3", "@types/github-slugger": "^2.0.0", "@types/html-to-text": "^9.0.4", - "@types/lodash": "^4.17.13", + "@types/lodash": "^4.17.14", "@types/mdx": "^2.0.13", "@types/node": "^20.12.5", "@types/react": "^18.3.11", diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index d3e11045b5e..10e3b4e5b80 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -2880,10 +2880,10 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:^4.17.13": - version: 4.17.13 - resolution: "@types/lodash@npm:4.17.13" - checksum: d0bf8fbd950be71946e0076b30fd40d492293baea75f05931b6b5b906fd62583708c6229abdb95b30205ad24ce1ed2f48bc9d419364f682320edd03405cc0c7e +"@types/lodash@npm:^4.17.14": + version: 4.17.14 + resolution: "@types/lodash@npm:4.17.14" + checksum: 2dbeaff92b31cb523f6bc4bb99a3d8c88fbb001d54f2367a888add85784fb213744a9b1600e1e98b6790ab191fdb6ec839eb0e3d63fcf6fb6cf1ebe4c3d21149 languageName: node linkType: hard @@ -9839,7 +9839,7 @@ __metadata: "@types/eslint__js": ^8.42.3 "@types/github-slugger": ^2.0.0 "@types/html-to-text": ^9.0.4 - "@types/lodash": ^4.17.13 + "@types/lodash": ^4.17.14 "@types/mdx": ^2.0.13 "@types/node": ^20.12.5 "@types/react": ^18.3.11 @@ -9921,7 +9921,7 @@ __metadata: typescript-eslint: ^8.19.0 undici: ^6.20.1 use-deep-compare-effect: ^1.8.1 - uuid: ^11.0.3 + uuid: ^11.0.4 vite: ^6.0.6 vite-plugin-checker: ^0.8.0 vite-plugin-svgr: ^4.3.0 @@ -12120,12 +12120,12 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^11.0.3": - version: 11.0.3 - resolution: "uuid@npm:11.0.3" +"uuid@npm:^11.0.4": + version: 11.0.4 + resolution: "uuid@npm:11.0.4" bin: uuid: dist/esm/bin/uuid - checksum: 646181c77e8b8df9bd07254faa703943e1c4d5ccde7d080312edf12f443f6c5750801fd9b27bf2e628594182165e6b1b880c0382538f7eca00b26622203741dc + checksum: 88860d20dafa648642581b2e7bb364588149df50d41c812100839aec095593fcc8548fde6929605efd42f556455cf5683e3bad9e9e311f4b62d7efbb68598d3e languageName: node linkType: hard From d0a8a94a7cae171e2dec57fd4e68b77fb04c5af2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 07:25:11 +0000 Subject: [PATCH 04/13] Bump react-markdown in /frontend-react in the markdown group (#16978) Bumps the markdown group in /frontend-react with 1 update: [react-markdown](https://github.com/remarkjs/react-markdown). Updates `react-markdown` from 9.0.1 to 9.0.3 - [Release notes](https://github.com/remarkjs/react-markdown/releases) - [Changelog](https://github.com/remarkjs/react-markdown/blob/main/changelog.md) - [Commits](https://github.com/remarkjs/react-markdown/compare/9.0.1...9.0.3) --- updated-dependencies: - dependency-name: react-markdown dependency-type: direct:production update-type: version-update:semver-patch dependency-group: markdown ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend-react/package.json | 2 +- frontend-react/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend-react/package.json b/frontend-react/package.json index c48ecb8c60c..b60f8569714 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -30,7 +30,7 @@ "react-helmet-async": "^2.0.5", "react-idle-timer": "^5.7.2", "react-loader-spinner": "^6.1.6", - "react-markdown": "^9.0.1", + "react-markdown": "^9.0.3", "react-query-kit": "^3.3.1", "react-router": "^6.28.0", "react-router-dom": "^6.28.0", diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index 10e3b4e5b80..d3c0eca02b9 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -9898,7 +9898,7 @@ __metadata: react-helmet-async: ^2.0.5 react-idle-timer: ^5.7.2 react-loader-spinner: ^6.1.6 - react-markdown: ^9.0.1 + react-markdown: ^9.0.3 react-query-kit: ^3.3.1 react-router: ^6.28.0 react-router-dom: ^6.28.0 @@ -9996,9 +9996,9 @@ __metadata: languageName: node linkType: hard -"react-markdown@npm:^9.0.1": - version: 9.0.1 - resolution: "react-markdown@npm:9.0.1" +"react-markdown@npm:^9.0.3": + version: 9.0.3 + resolution: "react-markdown@npm:9.0.3" dependencies: "@types/hast": ^3.0.0 devlop: ^1.0.0 @@ -10013,7 +10013,7 @@ __metadata: peerDependencies: "@types/react": ">=18" react: ">=18" - checksum: ca1daa650d48b84a5a9771683cdb3f3d2d418247ce0faf73ede3207c65f2a21cdebb9df37afda67f6fc8f0f0a7b9ce00eb239781954a4d6c7ad88ea4df068add + checksum: 7ebb01b295f7c9acddcd305308a8531c58c582c24fb8d6a4897f16b21ba0bd7e9e20ddae4a9024767e910310d22db0003489b61478cdb491a3d802343cf3a931 languageName: node linkType: hard From b10534a1b82e2a9648694fd3405ab690af599888 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 07:46:31 +0000 Subject: [PATCH 05/13] Bump @okta/okta-signin-widget in /frontend-react in the auth group (#16960) Bumps the auth group in /frontend-react with 1 update: [@okta/okta-signin-widget](https://github.com/okta/okta-signin-widget). Updates `@okta/okta-signin-widget` from 7.26.1 to 7.27.1 - [Release notes](https://github.com/okta/okta-signin-widget/releases) - [Changelog](https://github.com/okta/okta-signin-widget/blob/master/webpack.release.config.js) - [Commits](https://github.com/okta/okta-signin-widget/compare/okta-signin-widget-7.26.1...okta-signin-widget-7.27.1) --- updated-dependencies: - dependency-name: "@okta/okta-signin-widget" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: auth ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend-react/package.json | 2 +- frontend-react/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend-react/package.json b/frontend-react/package.json index b60f8569714..b535b84e1f5 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -8,7 +8,7 @@ "@microsoft/applicationinsights-react-js": "^17.3.4", "@microsoft/applicationinsights-web": "^3.3.4", "@okta/okta-react": "^6.9.0", - "@okta/okta-signin-widget": "^7.26.1", + "@okta/okta-signin-widget": "^7.27.1", "@rest-hooks/rest": "^3.0.3", "@tanstack/react-query": "^5.62.16", "@tanstack/react-query-devtools": "^5.62.16", diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index d3c0eca02b9..2e7d7454f70 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -1407,9 +1407,9 @@ __metadata: languageName: node linkType: hard -"@okta/okta-signin-widget@npm:^7.26.1": - version: 7.26.1 - resolution: "@okta/okta-signin-widget@npm:7.26.1" +"@okta/okta-signin-widget@npm:^7.27.1": + version: 7.27.1 + resolution: "@okta/okta-signin-widget@npm:7.27.1" dependencies: "@okta/okta-auth-js": ^7.9.0 "@sindresorhus/to-milliseconds": ^1.0.0 @@ -1433,7 +1433,7 @@ __metadata: dependenciesMeta: fsevents: optional: true - checksum: bac77bb6cda8c34a5155859f11e4c1a50ff62a523fe48e2bbfdb58527ffa1265e7eabbacdc67c104b43b32b3f60b0a82704f075a65f7008090dc32a80b549433 + checksum: 704c599fcab1009ea29a8fdab59c3b37b26942e4b19436d09a544cee00289c00ca425b00440f29c01e7ccfd6d522b7e7dbeb7697b063fab3b0afb5b1eff92bab languageName: node linkType: hard @@ -9810,7 +9810,7 @@ __metadata: "@microsoft/applicationinsights-react-js": ^17.3.4 "@microsoft/applicationinsights-web": ^3.3.4 "@okta/okta-react": ^6.9.0 - "@okta/okta-signin-widget": ^7.26.1 + "@okta/okta-signin-widget": ^7.27.1 "@playwright/test": ^1.49.1 "@rest-hooks/rest": ^3.0.3 "@rest-hooks/test": ^7.3.1 From 6aa6f664947b37afdeafe9aba2c4b96860bf377b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 07:57:18 +0000 Subject: [PATCH 06/13] Bump sass from 1.83.0 to 1.83.1 in /frontend-react in the css group (#16958) Bumps the css group in /frontend-react with 1 update: [sass](https://github.com/sass/dart-sass). Updates `sass` from 1.83.0 to 1.83.1 - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.83.0...1.83.1) --- updated-dependencies: - dependency-name: sass dependency-type: direct:development update-type: version-update:semver-patch dependency-group: css ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend-react/package.json | 2 +- frontend-react/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend-react/package.json b/frontend-react/package.json index b535b84e1f5..229b9ef4e59 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -182,7 +182,7 @@ "remark-frontmatter": "^5.0.0", "remark-mdx-frontmatter": "^5.0.0", "remark-mdx-toc": "^0.3.1", - "sass": "^1.83.0", + "sass": "^1.83.1", "storybook": "^8.4.7", "storybook-addon-remix-react-router": "^3.0.2", "ts-node": "^10.9.2", diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index 2e7d7454f70..b6c28da739a 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -9911,7 +9911,7 @@ __metadata: remark-mdx-toc: ^0.3.1 rest-hooks: ^6.1.7 sanitize-html: ^2.14.0 - sass: ^1.83.0 + sass: ^1.83.1 storybook: ^8.4.7 storybook-addon-remix-react-router: ^3.0.2 ts-node: ^10.9.2 @@ -10609,9 +10609,9 @@ __metadata: languageName: node linkType: hard -"sass@npm:^1.83.0": - version: 1.83.0 - resolution: "sass@npm:1.83.0" +"sass@npm:^1.83.1": + version: 1.83.1 + resolution: "sass@npm:1.83.1" dependencies: "@parcel/watcher": ^2.4.1 chokidar: ^4.0.0 @@ -10622,7 +10622,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: ab7ba7829b3a53f4cc209dba4b515e48b67fbabfa7afc1195275991a18511e04259c1e1b19bf93d77e6f5c39556e022f2ea2ce68117668f59f6679cd8c58ddd9 + checksum: 367a9f270c74a9ad2851955e1cf5b2a05e57d27aec4bf054be1da48eb49858076467b65ec180d8c4392b5c55c0f4d4ba644855f985652ffca15d68a20641d5e0 languageName: node linkType: hard From 9fbaa6cd7b5d0cd18d97c2b1f1c155fdd3b6b99b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 08:09:21 +0000 Subject: [PATCH 07/13] Bump the storybook group in /frontend-react with 2 updates (#16957) Bumps the storybook group in /frontend-react with 2 updates: [chromatic](https://github.com/chromaui/chromatic-cli) and [eslint-plugin-storybook](https://github.com/storybookjs/eslint-plugin-storybook). Updates `chromatic` from 11.20.2 to 11.22.0 - [Release notes](https://github.com/chromaui/chromatic-cli/releases) - [Changelog](https://github.com/chromaui/chromatic-cli/blob/main/CHANGELOG.md) - [Commits](https://github.com/chromaui/chromatic-cli/compare/v11.20.2...v11.22.0) Updates `eslint-plugin-storybook` from 0.11.1 to 0.11.2 - [Release notes](https://github.com/storybookjs/eslint-plugin-storybook/releases) - [Changelog](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/CHANGELOG.md) - [Commits](https://github.com/storybookjs/eslint-plugin-storybook/compare/v0.11.1...v0.11.2) --- updated-dependencies: - dependency-name: chromatic dependency-type: direct:development update-type: version-update:semver-minor dependency-group: storybook - dependency-name: eslint-plugin-storybook dependency-type: direct:development update-type: version-update:semver-patch dependency-group: storybook ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend-react/package.json | 4 ++-- frontend-react/yarn.lock | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend-react/package.json b/frontend-react/package.json index 229b9ef4e59..2f634260758 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -151,7 +151,7 @@ "autoprefixer": "^10.4.20", "browserslist": "^4.24.3", "browserslist-useragent-regexp": "^4.1.3", - "chromatic": "^11.20.2", + "chromatic": "^11.22.0", "cross-env": "^7.0.3", "dotenv-flow": "^4.1.0", "eslint": "^9.17.0", @@ -164,7 +164,7 @@ "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.16", - "eslint-plugin-storybook": "^0.11.1", + "eslint-plugin-storybook": "^0.11.2", "eslint-plugin-testing-library": "^7.1.1", "eslint-plugin-vitest": "^0.5.4", "globals": "^15.14.0", diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index b6c28da739a..34fcaef571f 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -4230,9 +4230,9 @@ __metadata: languageName: node linkType: hard -"chromatic@npm:^11.20.2": - version: 11.20.2 - resolution: "chromatic@npm:11.20.2" +"chromatic@npm:^11.22.0": + version: 11.22.0 + resolution: "chromatic@npm:11.22.0" peerDependencies: "@chromatic-com/cypress": ^0.*.* || ^1.0.0 "@chromatic-com/playwright": ^0.*.* || ^1.0.0 @@ -4245,7 +4245,7 @@ __metadata: chroma: dist/bin.js chromatic: dist/bin.js chromatic-cli: dist/bin.js - checksum: fb1732aa78b42ee18934a40b53c7e09a701f3a6a75cb4323a1c449a5488178da0774e7063a281349d09cfa1cf57e275c9363e3d58af850cd93f1388caf9461ad + checksum: 7ec1d7000f30356f99210b086b04ad1041ab254fe06bb27294e762b57816ce998bf29666d4641a6555fdac46ecb474fde024e4fbc4af101ee666dac33fde6a87 languageName: node linkType: hard @@ -5702,16 +5702,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-storybook@npm:^0.11.1": - version: 0.11.1 - resolution: "eslint-plugin-storybook@npm:0.11.1" +"eslint-plugin-storybook@npm:^0.11.2": + version: 0.11.2 + resolution: "eslint-plugin-storybook@npm:0.11.2" dependencies: "@storybook/csf": ^0.1.11 "@typescript-eslint/utils": ^8.8.1 ts-dedent: ^2.2.0 peerDependencies: - eslint: ">=6" - checksum: 8b8eb30b598f3c44c2bbf921e318215338f1159c1fba2d2f6cd5bc0b2ec14515655cf1760b5e11355baddabc2ac8446d4dc18ee6df6d2252555b126ff649a421 + eslint: ">=8" + checksum: 04dab47b676db57aa6c2325d4ab66283f5970133a2615cc68a97e42cb4ae1c79317e6db73bcfc24468e146bdff4c9ffe90a368bf2bcb67593954485f058b33ca languageName: node linkType: hard @@ -9855,7 +9855,7 @@ __metadata: axios: ^1.7.9 browserslist: ^4.24.3 browserslist-useragent-regexp: ^4.1.3 - chromatic: ^11.20.2 + chromatic: ^11.22.0 classnames: ^2.5.1 cross-env: ^7.0.3 date-fns: ^4.1.0 @@ -9872,7 +9872,7 @@ __metadata: eslint-plugin-react: ^7.37.3 eslint-plugin-react-hooks: ^5.1.0 eslint-plugin-react-refresh: ^0.4.16 - eslint-plugin-storybook: ^0.11.1 + eslint-plugin-storybook: ^0.11.2 eslint-plugin-testing-library: ^7.1.1 eslint-plugin-vitest: ^0.5.4 export-to-csv-fix-source-map: ^0.2.1 From dd62868bf884d822d3672d55d087d49a44aca07b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:26:09 +0000 Subject: [PATCH 08/13] Bump vite from 6.0.6 to 6.0.7 in /frontend-react in the bundler group (#16955) Bumps the bundler group in /frontend-react with 1 update: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 6.0.6 to 6.0.7 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.0.7/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development update-type: version-update:semver-patch dependency-group: bundler ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend-react/package.json | 2 +- frontend-react/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend-react/package.json b/frontend-react/package.json index 2f634260758..699756c0082 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -190,7 +190,7 @@ "typescript": "^5.7.2", "typescript-eslint": "^8.19.0", "undici": "^6.20.1", - "vite": "^6.0.6", + "vite": "^6.0.7", "vite-plugin-checker": "^0.8.0", "vite-plugin-svgr": "^4.3.0", "vitest": "^2.1.8" diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index 34fcaef571f..575be4b47ab 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -9922,7 +9922,7 @@ __metadata: undici: ^6.20.1 use-deep-compare-effect: ^1.8.1 uuid: ^11.0.4 - vite: ^6.0.6 + vite: ^6.0.7 vite-plugin-checker: ^0.8.0 vite-plugin-svgr: ^4.3.0 vitest: ^2.1.8 @@ -12331,9 +12331,9 @@ __metadata: languageName: node linkType: hard -"vite@npm:^6.0.6": - version: 6.0.6 - resolution: "vite@npm:6.0.6" +"vite@npm:^6.0.7": + version: 6.0.7 + resolution: "vite@npm:6.0.7" dependencies: esbuild: ^0.24.2 fsevents: ~2.3.3 @@ -12379,7 +12379,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 6786a42805dc7f277462ace1890cdf07562fefd79ef00e15e5e7eca64b3ef90589b768cf44622f4b6fa336ef167e6f7611d0a3cc81fe25798a1eb7e1b3d659b6 + checksum: 0a948bcd33cf1891a1079ccea6d7e2d1e030ed5d1ad86c9de41a5cb2935da50f77ef699a57ea12fa4a332d9def5494e80d75ea6504758d9d1d9a139f3c1c7fbe languageName: node linkType: hard From ba46ef8c4b85b8e39b432f6fcfa653e2818e8246 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:09:59 +0000 Subject: [PATCH 09/13] Bump the ui group across 1 directory with 3 updates (#16952) * Bump the ui group across 1 directory with 3 updates Bumps the ui group with 3 updates in the /frontend-react directory: [focus-trap-react](https://github.com/focus-trap/focus-trap-react), [react-toastify](https://github.com/fkhadra/react-toastify) and [react-error-boundary](https://github.com/bvaughn/react-error-boundary). Updates `focus-trap-react` from 10.3.1 to 11.0.2 - [Release notes](https://github.com/focus-trap/focus-trap-react/releases) - [Changelog](https://github.com/focus-trap/focus-trap-react/blob/master/CHANGELOG.md) - [Commits](https://github.com/focus-trap/focus-trap-react/compare/v10.3.1...v11.0.2) Updates `react-toastify` from 10.0.6 to 11.0.2 - [Release notes](https://github.com/fkhadra/react-toastify/releases) - [Commits](https://github.com/fkhadra/react-toastify/compare/v10.0.6...v11.0.2) Updates `react-error-boundary` from 4.1.2 to 5.0.0 - [Release notes](https://github.com/bvaughn/react-error-boundary/releases) - [Commits](https://github.com/bvaughn/react-error-boundary/compare/4.1.2...5.0.0) --- updated-dependencies: - dependency-name: focus-trap-react dependency-type: direct:production update-type: version-update:semver-major dependency-group: ui - dependency-name: react-toastify dependency-type: direct:production update-type: version-update:semver-major dependency-group: ui - dependency-name: react-error-boundary dependency-type: direct:development update-type: version-update:semver-major dependency-group: ui ... Signed-off-by: dependabot[bot] * focus trap type fix, toastify css no longer manually loaded --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Joseph Andersen <12385932+jpandersen87@users.noreply.github.com> --- frontend-react/__mocks__/focus-trap-react.tsx | 10 +--- frontend-react/package.json | 6 +- frontend-react/src/components/App/App.tsx | 58 ++++--------------- frontend-react/yarn.lock | 55 +++++++++--------- 4 files changed, 45 insertions(+), 84 deletions(-) diff --git a/frontend-react/__mocks__/focus-trap-react.tsx b/frontend-react/__mocks__/focus-trap-react.tsx index 3fef86165be..1abc45ef15b 100644 --- a/frontend-react/__mocks__/focus-trap-react.tsx +++ b/frontend-react/__mocks__/focus-trap-react.tsx @@ -1,17 +1,13 @@ -import type * as FocusTrapType from "focus-trap-react"; +import { FocusTrapProps } from "focus-trap-react"; import React from "react"; import { vi } from "vitest"; -const FocusTrap = (await vi.importActual("focus-trap-react")) - .default as React.ComponentType; +const FocusTrap = (await vi.importActual("focus-trap-react")).default as React.ComponentType; /** * Override displayCheck for testing. See: https://github.com/focus-trap/tabbable#testing-in-jsdom */ -const FixedComponent = ({ - focusTrapOptions, - ...props -}: FocusTrapType.Props) => { +const FixedComponent = ({ focusTrapOptions, ...props }: FocusTrapProps) => { const fixedOptions = { ...focusTrapOptions }; fixedOptions.tabbableOptions = { ...fixedOptions.tabbableOptions, diff --git a/frontend-react/package.json b/frontend-react/package.json index 699756c0082..79667c7bdc8 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -20,7 +20,7 @@ "date-fns-tz": "^3.2.0", "dompurify": "^3.2.3", "export-to-csv-fix-source-map": "^0.2.1", - "focus-trap-react": "^10.3.1", + "focus-trap-react": "^11.0.2", "history": "^5.3.0", "html-to-text": "^9.0.5", "lodash": "^4.17.21", @@ -35,7 +35,7 @@ "react-router": "^6.28.0", "react-router-dom": "^6.28.0", "react-scroll-sync": "^0.11.2", - "react-toastify": "^10.0.6", + "react-toastify": "^11.0.2", "rehype-raw": "^7.0.0", "rehype-slug": "^5.1.0", "rest-hooks": "^6.1.7", @@ -178,7 +178,7 @@ "patch-package": "^8.0.0", "postcss": "^8.4.49", "prettier": "^3.4.2", - "react-error-boundary": "^4.1.2", + "react-error-boundary": "^5.0.0", "remark-frontmatter": "^5.0.0", "remark-mdx-frontmatter": "^5.0.0", "remark-mdx-toc": "^0.3.1", diff --git a/frontend-react/src/components/App/App.tsx b/frontend-react/src/components/App/App.tsx index 4d84e0b8731..3f09079c12a 100644 --- a/frontend-react/src/components/App/App.tsx +++ b/frontend-react/src/components/App/App.tsx @@ -5,11 +5,7 @@ import { QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { Suspense, useCallback, useMemo } from "react"; import { HelmetProvider } from "react-helmet-async"; -import { - createBrowserRouter, - RouteObject, - RouterProvider, -} from "react-router-dom"; +import { createBrowserRouter, RouteObject, RouterProvider } from "react-router-dom"; import { CacheProvider, NetworkErrorBoundary } from "rest-hooks"; import AuthStateGate from "./AuthStateGate"; @@ -25,7 +21,6 @@ import { RSConsole } from "../../utils/rsConsole/rsConsole"; import { createTelemetryService } from "../../utils/TelemetryService/TelemetryService"; import { PERMISSIONS } from "../../utils/UsefulTypes"; -import "react-toastify/dist/ReactToastify.css"; import RSErrorBoundary from "../RSErrorBoundary/RSErrorBoundary"; export interface AppProps { @@ -48,10 +43,7 @@ function App({ config, routes }: AppProps) { () => new RSConsole({ ai: aiReactPlugin, ...config.RSCONSOLE }), [aiReactPlugin, config.RSCONSOLE], ); - const oktaAuth = useMemo( - () => new OktaAuth(config.OKTA_AUTH), - [config.OKTA_AUTH], - ); + const oktaAuth = useMemo(() => new OktaAuth(config.OKTA_AUTH), [config.OKTA_AUTH]); const router = useMemo(() => createBrowserRouter(routes), [routes]); const Fallback = useCallback(() => , []); @@ -67,25 +59,13 @@ function App({ config, routes }: AppProps) { let url = originalUri; if (originalUri === "/") { /* PERMISSIONS REFACTOR: Redirect URL should be determined by active membership type */ - if ( - authState?.accessToken && - permissionCheck( - PERMISSIONS.PRIME_ADMIN, - authState.accessToken, - ) - ) { + if (authState?.accessToken && permissionCheck(PERMISSIONS.PRIME_ADMIN, authState.accessToken)) { url = "/admin/settings"; } - if ( - authState?.accessToken && - permissionCheck(PERMISSIONS.SENDER, authState.accessToken) - ) { + if (authState?.accessToken && permissionCheck(PERMISSIONS.SENDER, authState.accessToken)) { url = "/submissions"; } - if ( - authState?.accessToken && - permissionCheck(PERMISSIONS.RECEIVER, authState.accessToken) - ) { + if (authState?.accessToken && permissionCheck(PERMISSIONS.RECEIVER, authState.accessToken)) { url = "/daily-data"; } } @@ -101,36 +81,20 @@ function App({ config, routes }: AppProps) { return ( - + - + - + - + - + - + diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index 575be4b47ab..0027b2b3293 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -4339,10 +4339,10 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^2.1.0": - version: 2.1.0 - resolution: "clsx@npm:2.1.0" - checksum: 43fefc29b6b49c9476fbce4f8b1cc75c27b67747738e598e6651dd40d63692135dc60b18fa1c5b78a2a9ba8ae6fd2055a068924b94e20b42039bd53b78b98e1d +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: acd3e1ab9d8a433ecb3cc2f6a05ab95fe50b4a3cfc5ba47abb6cbf3754585fcb87b84e90c822a1f256c4198e3b41c7f6c391577ffc8678ad587fc0976b24fd57 languageName: node linkType: hard @@ -6162,21 +6162,22 @@ __metadata: languageName: node linkType: hard -"focus-trap-react@npm:^10.3.1": - version: 10.3.1 - resolution: "focus-trap-react@npm:10.3.1" +"focus-trap-react@npm:^11.0.2": + version: 11.0.2 + resolution: "focus-trap-react@npm:11.0.2" dependencies: - focus-trap: ^7.6.1 + focus-trap: ^7.6.2 tabbable: ^6.2.0 peerDependencies: - prop-types: ^15.8.1 - react: ">=16.3.0" - react-dom: ">=16.3.0" - checksum: 7992402b86a2ebada9232f36388fe7997e395365f1ca89927114caf84c53c546940c066b0a60417b01b4b366d97d0ac313d7c65ef69b74d188fc0863f850f480 + "@types/react": ^18.0.0 || ^19.0.0 + "@types/react-dom": ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + checksum: 992b6330101ff71abba01c0ea0c85104a0bbf3bf91f335ca004776e77c7d700d8f1e8a12425cb7a1bc8a041169c2b0d4c25c9ac0db5b5bdf8d17c21d66085ab8 languageName: node linkType: hard -"focus-trap@npm:^7.6.1": +"focus-trap@npm:^7.6.2": version: 7.6.2 resolution: "focus-trap@npm:7.6.2" dependencies: @@ -9781,14 +9782,14 @@ __metadata: languageName: node linkType: hard -"react-error-boundary@npm:^4.1.2": - version: 4.1.2 - resolution: "react-error-boundary@npm:4.1.2" +"react-error-boundary@npm:^5.0.0": + version: 5.0.0 + resolution: "react-error-boundary@npm:5.0.0" dependencies: "@babel/runtime": ^7.12.5 peerDependencies: react: ">=16.13.1" - checksum: afe692f1bbbfb5998b49e1001d7682a3cbfdc623dca1318b408e738606f3450d925c28fbbfa5dc84d2cf285d17c2e7f079d59386a27da354dea9c902a935149b + checksum: 4fa78890bb254fe1f0ee1eed893ac161a27482c4567f7667ef83a8339432eb99e323ee69757f01f4864e0037b01a9b6822735ea122f02e749d0bf7a781d9ea53 languageName: node linkType: hard @@ -9876,7 +9877,7 @@ __metadata: eslint-plugin-testing-library: ^7.1.1 eslint-plugin-vitest: ^0.5.4 export-to-csv-fix-source-map: ^0.2.1 - focus-trap-react: ^10.3.1 + focus-trap-react: ^11.0.2 globals: ^15.14.0 history: ^5.3.0 html-to-text: ^9.0.5 @@ -9894,7 +9895,7 @@ __metadata: prettier: ^3.4.2 react: ^18.3.1 react-dom: ^18.3.1 - react-error-boundary: ^4.1.2 + react-error-boundary: ^5.0.0 react-helmet-async: ^2.0.5 react-idle-timer: ^5.7.2 react-loader-spinner: ^6.1.6 @@ -9903,7 +9904,7 @@ __metadata: react-router: ^6.28.0 react-router-dom: ^6.28.0 react-scroll-sync: ^0.11.2 - react-toastify: ^10.0.6 + react-toastify: ^11.0.2 rehype-raw: ^7.0.0 rehype-slug: ^5.1.0 remark-frontmatter: ^5.0.0 @@ -10070,15 +10071,15 @@ __metadata: languageName: node linkType: hard -"react-toastify@npm:^10.0.6": - version: 10.0.6 - resolution: "react-toastify@npm:10.0.6" +"react-toastify@npm:^11.0.2": + version: 11.0.2 + resolution: "react-toastify@npm:11.0.2" dependencies: - clsx: ^2.1.0 + clsx: ^2.1.1 peerDependencies: - react: ">=18" - react-dom: ">=18" - checksum: 89fa24718eba0800e2bc1f88121a6e119efd87df26344d5b3c86442d4a13fbd65b67932e01bd6442758f4fe3f5eca6c8fc80c1dfb51c8dc2a4ec06bdaf9762da + react: ^18 || ^19 + react-dom: ^18 || ^19 + checksum: b951638b517e110f09a60f8164d759d29d480132832d574a57b5724ed6887ec728401f6fe9bf00d4a70ec5edb5a7871fc45f18ddeffdecf677b1dbbdddc55b2c languageName: node linkType: hard From f337fd99d7712a9e15ad58cc599e29bd97a1833f Mon Sep 17 00:00:00 2001 From: James Gilmore <109554461+GilmoreA6@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:20:24 -0800 Subject: [PATCH 10/13] Flexion/jgilmore/update blob storage transport filenames (#16920) * modify blob transport to utilize external filename * rename variables to be clearer * update blobaccess tests * add logging * remove unused component * remove copyBlob and update blob transport to directly call downloadBlobAsByteArray --- .../src/main/kotlin/azure/BlobAccess.kt | 13 -------- .../kotlin/transport/BlobStoreTransport.kt | 6 +++- .../src/test/kotlin/azure/BlobAccessTests.kt | 32 ------------------- 3 files changed, 5 insertions(+), 46 deletions(-) diff --git a/prime-router/src/main/kotlin/azure/BlobAccess.kt b/prime-router/src/main/kotlin/azure/BlobAccess.kt index 8b88fbcf017..8cbe6f1b68e 100644 --- a/prime-router/src/main/kotlin/azure/BlobAccess.kt +++ b/prime-router/src/main/kotlin/azure/BlobAccess.kt @@ -401,19 +401,6 @@ class BlobAccess() : Logging { return binaryData } - /** - * Copy a blob at [fromBlobUrl] to a blob in [blobConnInfo] - */ - fun copyBlob(fromBlobUrl: String, blobConnInfo: BlobContainerMetadata): String { - val fromBytes = downloadBlobAsByteArray(fromBlobUrl) - logger.info("Ready to copy ${fromBytes.size} bytes from $fromBlobUrl") - val toFilename = BlobInfo.getBlobFilename(fromBlobUrl) - logger.info("New blob filename will be $toFilename") - val toBlobUrl = uploadBlob(toFilename, fromBytes, blobConnInfo) - logger.info("New blob URL is $toBlobUrl") - return toBlobUrl - } - /** * Accepts a [BlobItemAndPreviousVersions] and grabs the most recent previous version and updates * the blob to it. diff --git a/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt b/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt index ea372653d59..2f3dfe9e6dd 100644 --- a/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt +++ b/prime-router/src/main/kotlin/transport/BlobStoreTransport.kt @@ -31,7 +31,11 @@ class BlobStoreTransport : ITransport { val receiver = header.receiver ?: error("No receiver defined for report ${header.reportFile.reportId}") val bodyUrl = header.reportFile.bodyUrl ?: error("Report ${header.reportFile.reportId} has no blob to copy") context.logger.info("About to copy $bodyUrl to $envVar:$storageName") - val newUrl = BlobAccess.copyBlob(bodyUrl, BlobAccess.BlobContainerMetadata.build(transportType)) + val blobConnection = BlobAccess.BlobContainerMetadata.build(transportType) + val blobFile = BlobAccess.downloadBlobAsByteArray(bodyUrl) + context.logger.info("New blob filename will be $externalFileName") + val newUrl = BlobAccess.uploadBlob(externalFileName, blobFile, blobConnection) + context.logger.info("New blob URL is $newUrl") val msg = "Successfully copied $bodyUrl to $newUrl" context.logger.info(msg) actionHistory.trackActionResult(msg) diff --git a/prime-router/src/test/kotlin/azure/BlobAccessTests.kt b/prime-router/src/test/kotlin/azure/BlobAccessTests.kt index bdda5a8abdd..11a3a4d52b2 100644 --- a/prime-router/src/test/kotlin/azure/BlobAccessTests.kt +++ b/prime-router/src/test/kotlin/azure/BlobAccessTests.kt @@ -704,38 +704,6 @@ class BlobAccessTests { assertThat(resultBinaryData.toString()).isEqualTo(expectedResult) } - @Test - fun `copy blob`() { - mockkClass(BlobAccess::class) - mockkObject(BlobAccess.Companion) - every { BlobAccess.Companion.getBlobConnection(any()) } returns "testconnection" - - val testUrl = "http://testurl/testfile" - val testFile = BlobAccess.BlobInfo.getBlobFilename(testUrl) - val testBlobMetadata = BlobAccess.BlobContainerMetadata.build("testcontainer", "testenvvar") - - every { BlobAccess.Companion.downloadBlobAsByteArray(testUrl) }.returns("testblob".toByteArray()) - every { - BlobAccess.Companion.uploadBlob( - testFile, - "testblob".toByteArray(), - testBlobMetadata - ) - }.returns("http://testurl2") - - val result = BlobAccess.copyBlob(testUrl, testBlobMetadata) - - verify(exactly = 1) { BlobAccess.Companion.downloadBlobAsByteArray(testUrl, any(), any()) } - verify(exactly = 1) { - BlobAccess.Companion.uploadBlob( - testFile, - "testblob".toByteArray(), - testBlobMetadata - ) - } - assertThat(result).isEqualTo("http://testurl2") - } - @Test fun `delete blob`() { val testUrl = "http://deleteblob" From f2eb88919c1013287a85d47c055a5603a4a42efd Mon Sep 17 00:00:00 2001 From: James Gilmore <109554461+GilmoreA6@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:17:22 -0800 Subject: [PATCH 11/13] add new filename template for UUID (#17008) --- .../metadata/file_name_templates/file-name-templates.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prime-router/metadata/file_name_templates/file-name-templates.yml b/prime-router/metadata/file_name_templates/file-name-templates.yml index d449942f837..8388d9bc672 100644 --- a/prime-router/metadata/file_name_templates/file-name-templates.yml +++ b/prime-router/metadata/file_name_templates/file-name-templates.yml @@ -122,4 +122,8 @@ elements: - GOLDEN-COPY - "-" + - uuid() + +- name: uuid + elements: - uuid() \ No newline at end of file From 843e0c2d5fb1a4e1b51d4c249008534acfbece28 Mon Sep 17 00:00:00 2001 From: Abi Philip Date: Tue, 7 Jan 2025 16:18:51 -0600 Subject: [PATCH 12/13] Updated Washington STLT to hard code MSH 4 and filter for ONLY Positive Covid after integation test 12.7.2025 --- .../hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml | 2 +- .../resources/datatests/FHIR_to_HL7/sample_WA_20240719-0001.hl7 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml b/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml index 30c7c344195..c4fc5487234 100644 --- a/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml +++ b/prime-router/src/main/resources/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml @@ -43,7 +43,7 @@ elements: hl7Spec: [ '%{MSH}-3-3' ] - name: wa-sending-facility-namespace-id - value: [ '"7uycso49' ] + value: [ '"7uycso49"' ] hl7Spec: [ '%{MSH}-4-1' ] - name: wa-sending-facility-universal-id diff --git a/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_WA_20240719-0001.hl7 b/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_WA_20240719-0001.hl7 index 94859fda75e..5c68836448b 100644 --- a/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_WA_20240719-0001.hl7 +++ b/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_WA_20240719-0001.hl7 @@ -1,4 +1,4 @@ -MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Testing Lab^12D4567890^CLIA|WADOHPHRED^2.16.840.1.113883.3.237.4.2^ISO|dn1fro00^1.3.6.1.4.1.38630.2.1.1.19^ISO|20240815054718||ORU^R01^ORU_R01|12256b9a-0ca9-47ed-95b4-0d4ca60c4324|P|2.5.1|||NE|NE|USA|UNICODE UTF-8|ENG^English^ISO||PHLabReport-NoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO +MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|7uycso49^1.3.6.1.4.1.38630.2.1.1.519^ISO|WADOHPHRED^2.16.840.1.113883.3.237.4.2^ISO|dn1fro00^1.3.6.1.4.1.38630.2.1.1.19^ISO|20240815054718||ORU^R01^ORU_R01|12256b9a-0ca9-47ed-95b4-0d4ca60c4324|P|2.5.1|||NE|NE|USA|UNICODE UTF-8|ENG^English^ISO||PHLabReport-NoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.2-SNAPSHOT|PRIME ReportStream|0.2-SNAPSHOT||20240812210736 PID|1||10083d1d-dc8b-4ea0-91fa-8744cf0f013b^^^Testing Lab&12D4567890&CLIA^PI^Testing Lab&12D4567890&CLIA||Wolf^Karolann^^^^^L||19700201|F||1002-5^native^HL70005^^^^2.5.1^^native|123 Main St^^Tamuning^ND^55987^USA||8002324636^PRS^CP^^1^800^2324636^^^^^8002324636~^NET^Internet^wolf@test.com||333^Unknown^99WA_LANG^^^^1|||||||H^Hispanic or Latino^HL70189^^^^2.9^^Hispanic or Latino||||||||N ORC|RE|12256b9a-0ca9-47ed-95b4-0d4ca60c4324^Testing Lab^12D4567890^CLIA|12256b9a-0ca9-47ed-95b4-0d4ca60c4324^Testing Lab^12D4567890^CLIA|||||||||1245319599^McTester^Phil^^^^^^NPI&2.16.840.1.113883.4.6&ISO^L^^^NPI||^WPN^PH^^1^530^8675309|20240815054718||||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA|^WPN^PH^^1^530^8675309|321 Ocean Drive^^Denver^CO^80210^USA From aeb20c7b87b261eca28b7badc55accb3ade9f289 Mon Sep 17 00:00:00 2001 From: Angela DeGolier <39102333+adegolier@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:23:23 -0500 Subject: [PATCH 13/13] Platform/angela/14116/read hl7 (#16859) * [WIP] Still working on tests. Fixed HL7AckUtilsTest and moved isBatch() into the companion object in HL7Reader. * [WIP] Tests still broken, fixed FHIRBundleHelpersTests by using default parsing to get the message type instead of RS specific parsing * [WIP] Fixed HL7Reader tests and marked a couple things in HL7Reader as deprecated because they are only being used in the CLI and should probably not be used elsewhere. Removing useages from CLI was out of scope for this ticket. * [WIP] Added error handling to HL7MessageHelpers for batching files * Dealing with merge conflicts * More effort to deal with merge conflicts * One more little leftover from merge conflicts * Fixed some more references to getMessages() that came in from main and added some HL7Reader tests back in with modifications * Minor fix to HL7MessageHelpers * added requested helper function * removed unnecessary test * Removed MessageType validation from SubmissionReceiver * Removed HL7MessageParseAndConvertConfiguration * Modified ProcessFhirCommands to remove things from HL7Reader that were only otherwise part of the getMessages() saga * removed deprecation mark from ProcessFhirCommands --- .../src/main/kotlin/SettingsProvider.kt | 2 - .../src/main/kotlin/SubmissionReceiver.kt | 17 +- .../service/SubmissionResponseBuilder.kt | 11 +- .../main/kotlin/cli/ProcessFhirCommands.kt | 18 +- .../src/main/kotlin/cli/ProcessHl7Commands.kt | 13 +- .../kotlin/fhirengine/engine/FHIRConverter.kt | 39 +-- .../translation/TranslationSchemaManager.kt | 9 +- .../fhirengine/utils/HL7MessageHelpers.kt | 21 +- .../main/kotlin/fhirengine/utils/HL7Reader.kt | 326 ++++-------------- .../kotlin/cli/helpers/HL7DiffHelperTests.kt | 111 +++--- .../fhirengine/engine/FhirConverterTests.kt | 11 - .../translation/HL7toFhirTranslatorTests.kt | 50 ++- .../translation/hl7/utils/HL7ACKUtilsTest.kt | 17 +- .../utils/FHIRBundleHelpersTests.kt | 46 ++- .../utils/HL7QueueMessageHelpersTests.kt | 7 +- .../kotlin/fhirengine/utils/HL7ReaderTests.kt | 290 ++++------------ .../MarsOtcElrOnboardingValidatorTests.kt | 9 +- .../validation/MarsOtcElrValidatorTests.kt | 13 +- .../kotlin/datatests/TranslationTests.kt | 5 +- 19 files changed, 316 insertions(+), 699 deletions(-) diff --git a/prime-router/src/main/kotlin/SettingsProvider.kt b/prime-router/src/main/kotlin/SettingsProvider.kt index ba98f8a168f..7eaa32d3dee 100644 --- a/prime-router/src/main/kotlin/SettingsProvider.kt +++ b/prime-router/src/main/kotlin/SettingsProvider.kt @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonValue import gov.cdc.prime.router.CustomerStatus.ACTIVE import gov.cdc.prime.router.CustomerStatus.INACTIVE import gov.cdc.prime.router.CustomerStatus.TESTING -import gov.cdc.prime.router.fhirengine.utils.HL7Reader import gov.cdc.prime.router.validation.IItemValidator import gov.cdc.prime.router.validation.MarsOtcElrOnboardingValidator import gov.cdc.prime.router.validation.MarsOtcElrValidator @@ -55,7 +54,6 @@ enum class Topic( val isUniversalPipeline: Boolean = true, val isSendOriginal: Boolean = false, val validator: IItemValidator = NoopItemValidator(), - val hl7ParseConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration? = null, ) { FULL_ELR("full-elr", true, false), ETOR_TI("etor-ti", true, false), diff --git a/prime-router/src/main/kotlin/SubmissionReceiver.kt b/prime-router/src/main/kotlin/SubmissionReceiver.kt index d9cc88a1e5b..fe819504005 100644 --- a/prime-router/src/main/kotlin/SubmissionReceiver.kt +++ b/prime-router/src/main/kotlin/SubmissionReceiver.kt @@ -9,8 +9,8 @@ import gov.cdc.prime.router.azure.ReportWriter import gov.cdc.prime.router.azure.WorkflowEngine import gov.cdc.prime.router.azure.db.enums.TaskAction import gov.cdc.prime.router.fhirengine.engine.FhirConvertQueueMessage -import gov.cdc.prime.router.fhirengine.engine.MessageType import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder +import gov.cdc.prime.router.fhirengine.utils.HL7MessageHelpers import gov.cdc.prime.router.fhirengine.utils.HL7Reader /** @@ -268,14 +268,14 @@ class UniversalPipelineReceiver : SubmissionReceiver { when (sender.format) { MimeFormat.HL7 -> { - val messages = HL7Reader(actionLogs).getMessages(content) - val isBatch = HL7Reader(actionLogs).isBatch(content, messages.size) - // create a Report for this incoming HL7 message to use for tracking in the database + val messageCount = HL7MessageHelpers.messageCount(content) + val isBatch = HL7Reader.isBatch(content, messageCount) + // create a Report for this incoming HL7 message to use for tracking in the database report = Report( if (isBatch) MimeFormat.HL7_BATCH else MimeFormat.HL7, sources, - messages.size, + messageCount, metadata = metadata, nextAction = TaskAction.convert, topic = sender.topic, @@ -290,11 +290,8 @@ class UniversalPipelineReceiver : SubmissionReceiver { // actionLogs // ) // } - - // check for valid message type - messages.forEachIndexed { - idx, element -> - MessageType.validateMessageType(element, actionLogs, idx + 1) + if (messageCount == 0 && !actionLogs.hasErrors()) { + actionLogs.error(InvalidReportMessage("Unable to find HL7 messages in provided data.")) } } diff --git a/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt b/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt index c01f4a76fbc..eff8dcd02da 100644 --- a/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt +++ b/prime-router/src/main/kotlin/azure/service/SubmissionResponseBuilder.kt @@ -5,12 +5,12 @@ import com.google.common.net.HttpHeaders import com.microsoft.azure.functions.HttpRequestMessage import com.microsoft.azure.functions.HttpResponseMessage import com.microsoft.azure.functions.HttpStatus -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.Sender import gov.cdc.prime.router.azure.HttpUtilities import gov.cdc.prime.router.azure.HttpUtilities.Companion.isSuccessful import gov.cdc.prime.router.common.JacksonMapperUtilities import gov.cdc.prime.router.fhirengine.translation.hl7.utils.HL7ACKUtils +import gov.cdc.prime.router.fhirengine.utils.HL7MessageHelpers import gov.cdc.prime.router.fhirengine.utils.HL7Reader import gov.cdc.prime.router.history.DetailedSubmissionHistory import org.apache.logging.log4j.kotlin.Logging @@ -106,12 +106,11 @@ class SubmissionResponseBuilder( contentType == HttpUtilities.hl7V2MediaType && requestBody != null ) { - val hl7Reader = HL7Reader(ActionLogger()) - val messages = hl7Reader.getMessages(requestBody) - val isBatch = hl7Reader.isBatch(requestBody, messages.size) + val messageCount = HL7MessageHelpers.messageCount(requestBody) + val isBatch = HL7Reader.isBatch(requestBody, messageCount) - if (!isBatch && messages.size == 1) { - val message = messages.first() + if (!isBatch && messageCount == 1) { + val message = HL7Reader.parseHL7Message(requestBody) val acceptAcknowledgementType = HL7Reader.getAcceptAcknowledgmentType(message) val ackResponseRequired = acceptAcknowledgmentTypeRespondValues.contains(acceptAcknowledgementType) if (ackResponseRequired) { diff --git a/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt b/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt index c1a91a09b92..d3ecb48bf78 100644 --- a/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt +++ b/prime-router/src/main/kotlin/cli/ProcessFhirCommands.kt @@ -223,8 +223,8 @@ class ProcessFhirCommands : CliktCommand( (isCli && outputFormat == MimeFormat.HL7.toString()) || ( receiver != null && - (receiver.format == MimeFormat.HL7 || receiver.format == MimeFormat.HL7_BATCH) - ) + (receiver.format == MimeFormat.HL7 || receiver.format == MimeFormat.HL7_BATCH) + ) ) -> { val (bundle2, inputMessage) = convertHl7ToFhir(contents, receiver) @@ -297,7 +297,7 @@ class ProcessFhirCommands : CliktCommand( } } - private fun evaluateReceiverFilters(receiver: Receiver?, messageOrBundle: MessageOrBundle, isCli: Boolean) { + private fun evaluateReceiverFilters(receiver: Receiver?, messageOrBundle: MessageOrBundle, isCli: Boolean) { if (receiver != null && messageOrBundle.bundle != null) { val reportStreamFilters = mutableListOf>() reportStreamFilters.add(Pair("Jurisdictional Filter", receiver.jurisdictionalFilter)) @@ -522,10 +522,8 @@ class ProcessFhirCommands : CliktCommand( // However, the library used to encode the HL7 message throws an error it there are more than 4 encoding // characters, so this work around exists for that scenario val stringToEncode = hl7String.replace("MSH|^~\\&#|", "MSH|^~\\&|") - val hl7message = HL7Reader.parseHL7Message( - stringToEncode, - null - ) + val hl7message = HL7Reader.parseHL7Message(stringToEncode) + // if a hl7 parsing failure happens, throw error and show the message if (hl7message.toString().lowercase().contains("failed")) { throw CliktError("HL7 parser failure. $hl7message") @@ -534,12 +532,8 @@ class ProcessFhirCommands : CliktCommand( val msh = hl7message.get("MSH") as Segment Terser.set(msh, 2, 0, 1, 1, "^~\\&#") } - val hl7profile = HL7Reader.getMessageProfile(hl7message.toString()) // search hl7 profile map and create translator with config path if found - var fhirMessage = when (val configPath = HL7Reader.profileDirectoryMap[hl7profile]) { - null -> HL7toFhirTranslator(inputSchema).translate(hl7message) - else -> HL7toFhirTranslator(configPath).translate(hl7message) - } + var fhirMessage = HL7toFhirTranslator(inputSchema).translate(hl7message) val stamper = ConditionStamper(LookupTableConditionMapper(Metadata.getInstance())) fhirMessage.getObservations().forEach { observation -> diff --git a/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt b/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt index 1f89270217f..9c23ab1c9ec 100644 --- a/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt +++ b/prime-router/src/main/kotlin/cli/ProcessHl7Commands.kt @@ -1,11 +1,11 @@ package gov.cdc.prime.router.cli +import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.CliktError import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.required import com.github.ajalt.clikt.parameters.types.file -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.cli.helpers.HL7DiffHelper import gov.cdc.prime.router.fhirengine.utils.HL7Reader @@ -45,9 +45,14 @@ class ProcessHl7Commands : CliktCommand( val comparisonFile = comparisonFile.inputStream().readBytes().toString(Charsets.UTF_8) if (comparisonFile.isBlank()) throw CliktError("File ${this.comparisonFile.absolutePath} is empty.") - val actionLogger = ActionLogger() - val starterMessages = HL7Reader(actionLogger).getMessages(starterFile) - val comparisonMessages = HL7Reader(actionLogger).getMessages(comparisonFile) + val starterMessages = Hl7InputStreamMessageStringIterator(starterFile.byteInputStream()).asSequence() + .map { rawItem -> + HL7Reader.parseHL7Message(rawItem) + }.toList() + val comparisonMessages = Hl7InputStreamMessageStringIterator(comparisonFile.byteInputStream()).asSequence() + .map { rawItem -> + HL7Reader.parseHL7Message(rawItem) + }.toList() starterMessages.forEachIndexed { counter, message -> val differences = hl7DiffHelper.diffHl7(message, comparisonMessages[counter]) diff --git a/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt b/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt index 53a4242dfc4..0ce0eb45ab7 100644 --- a/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt +++ b/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt @@ -50,11 +50,9 @@ 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.translation.hl7.utils.FhirPathUtils import gov.cdc.prime.router.fhirengine.utils.FhirTranscoder -import gov.cdc.prime.router.fhirengine.utils.HL7Reader import gov.cdc.prime.router.fhirengine.utils.HL7Reader.Companion.parseHL7Message import gov.cdc.prime.router.fhirengine.utils.getObservations import gov.cdc.prime.router.fhirengine.utils.getRSMessageType -import gov.cdc.prime.router.fhirengine.utils.isElr import gov.cdc.prime.router.logging.LogMeasuredTime import gov.cdc.prime.router.report.ReportService import gov.cdc.prime.router.validation.IItemValidator @@ -397,7 +395,7 @@ class FHIRConverter( ) } - FHIREngineRunResult( + FHIREngineRunResult( routeEvent, report, blobInfo.blobUrl, @@ -427,7 +425,7 @@ class FHIRConverter( report, TaskAction.convert, "Submitted report was either empty or could not be parsed into HL7" - ) { + ) { parentReportId(input.reportId) params( mapOf( @@ -479,7 +477,7 @@ class FHIRConverter( "format" to format.name ) ) { - getBundlesFromRawHL7(rawReport, validator, input.topic.hl7ParseConfiguration) + getBundlesFromRawHL7(rawReport, validator) } } catch (ex: ParseFailureError) { actionLogger.error( @@ -571,7 +569,6 @@ class FHIRConverter( private fun getBundlesFromRawHL7( rawReport: String, validator: IItemValidator, - hL7MessageParseAndConvertConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration?, ): List> { val itemStream = Hl7InputStreamMessageStringIterator(rawReport.byteInputStream()).asSequence() @@ -580,17 +577,16 @@ class FHIRConverter( }.toList() return maybeParallelize(itemStream.size, itemStream.stream(), "Generating FHIR bundles in").map { item -> - parseHL7Item(item, hL7MessageParseAndConvertConfiguration) + parseHL7Item(item) }.map { item -> - validateAndConvertHL7Item(item, validator, hL7MessageParseAndConvertConfiguration) + validateAndConvertHL7Item(item, validator) }.collect(Collectors.toList()) } private fun parseHL7Item( item: ProcessedHL7Item, - hL7MessageParseAndConvertConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration?, ) = try { - val message = parseHL7Message(item.rawItem, hL7MessageParseAndConvertConfiguration) + val message = parseHL7Message(item.rawItem) item.updateParsed(message) } catch (e: HL7Exception) { item.updateParsed( @@ -605,20 +601,11 @@ class FHIRConverter( private fun validateAndConvertHL7Item( item: ProcessedHL7Item, validator: IItemValidator, - hL7MessageParseAndConvertConfiguration: HL7Reader.Companion.HL7MessageParseAndConvertConfiguration?, ): ProcessedHL7Item = if (item.parsedItem != null) { val validationResult = validator.validate(item.parsedItem) if (validationResult.isValid()) { try { - val bundle = when (hL7MessageParseAndConvertConfiguration) { - null -> HL7toFhirTranslator.getHL7ToFhirTranslatorInstance().translate(item.parsedItem) - else -> - HL7toFhirTranslator - .getHL7ToFhirTranslatorInstance( - hL7MessageParseAndConvertConfiguration.hl7toFHIRMappingLocation - ) - .translate(item.parsedItem) - } + val bundle = HL7toFhirTranslator.getHL7ToFhirTranslatorInstance().translate(item.parsedItem) item.setBundle(bundle) } catch (ex: Exception) { item.setConversionError( @@ -759,13 +746,13 @@ class FHIRConverter( * transformer in tests. */ fun getTransformerFromSchema(schemaName: String): FhirTransformer? = if (schemaName.isNotBlank()) { - withLoggingContext(mapOf("schemaName" to schemaName)) { - logger.info("Apply a sender transform to the items in the report") - } - FhirTransformer(schemaName) - } else { - null + withLoggingContext(mapOf("schemaName" to schemaName)) { + logger.info("Apply a sender transform to the items in the report") } + FhirTransformer(schemaName) + } else { + null + } } /** diff --git a/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt b/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt index 8890f198b63..b5c97da7463 100644 --- a/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt +++ b/prime-router/src/main/kotlin/fhirengine/translation/TranslationSchemaManager.kt @@ -1,9 +1,9 @@ package gov.cdc.prime.router.fhirengine.translation +import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator import com.azure.storage.blob.models.BlobItem import fhirengine.engine.CustomFhirPathFunctions import fhirengine.engine.CustomTranslationFunctions -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.Hl7Configuration import gov.cdc.prime.router.azure.BlobAccess import gov.cdc.prime.router.fhirengine.config.HL7TranslationConfig @@ -41,7 +41,6 @@ class TranslationSchemaManager : Logging { Regex("/$previousValidBlobName-$timestampRegex") private val previousPreviousValidBlobNameRegex = Regex("/$previousPreviousValidBlobName-$timestampRegex") - private val hL7Reader = HL7Reader(ActionLogger()) /** * Container class that holds the current state for a schema type in a particular azure store. @@ -440,7 +439,11 @@ class TranslationSchemaManager : Logging { ) ).validate( inputBundle, - hL7Reader.getMessages(rawValidationInput.output)[0] + HL7Reader.parseHL7Message( + Hl7InputStreamMessageStringIterator(rawValidationInput.output.byteInputStream()) + .asSequence() + .first() + ), ) } } diff --git a/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt b/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt index 6b05e746c36..cbd8b1b4700 100644 --- a/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt +++ b/prime-router/src/main/kotlin/fhirengine/utils/HL7MessageHelpers.kt @@ -1,6 +1,8 @@ package gov.cdc.prime.router.fhirengine.utils +import ca.uhn.hl7v2.AbstractHL7Exception import ca.uhn.hl7v2.model.v251.datatype.DTM +import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator import ca.uhn.hl7v2.util.Terser import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.Hl7Configuration @@ -24,6 +26,8 @@ object HL7MessageHelpers : Logging { */ const val hl7SegmentDelimiter = "\r" + val actionLogger = ActionLogger() + /** * Generate a HL7 Batch file from the list of [hl7RawMsgs] for the given [receiver]. The [hl7RawMsgs] are expected * to be real HL7 messages at this point, so we will not validate their contents here for performance reasons. @@ -34,12 +38,17 @@ object HL7MessageHelpers : Logging { val useBatchHeaders = receiver.translation.useBatchHeaders // Grab the first message to extract some data if not set in the settings val firstMessage = if (hl7RawMsgs.isNotEmpty()) { - val messages = HL7Reader(ActionLogger()).getMessages(hl7RawMsgs[0]) - if (messages.isEmpty()) { + try { + val message = HL7Reader.parseHL7Message(hl7RawMsgs[0]) + Terser(message) + } catch (exception: Hl7InputStreamMessageStringIterator.ParseFailureError) { + logger.warn("Unable to extract batch header values from HL7: ${hl7RawMsgs[0].take(80)} ...") + HL7Reader.logHL7ParseFailure(exception, actionLogger) + null + } catch (exception: AbstractHL7Exception) { logger.warn("Unable to extract batch header values from HL7: ${hl7RawMsgs[0].take(80)} ...") + HL7Reader.recordError(exception, actionLogger) null - } else { - Terser(messages[0]) } } else { null @@ -94,4 +103,8 @@ object HL7MessageHelpers : Logging { return builder.toString() } + + fun messageCount(rawHl7: String): Int { + return 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 aa48c011d8f..698b41c7697 100644 --- a/prime-router/src/main/kotlin/fhirengine/utils/HL7Reader.kt +++ b/prime-router/src/main/kotlin/fhirengine/utils/HL7Reader.kt @@ -5,10 +5,8 @@ import ca.uhn.hl7v2.DefaultHapiContext import ca.uhn.hl7v2.ErrorCode import ca.uhn.hl7v2.HL7Exception import ca.uhn.hl7v2.HapiContext -import ca.uhn.hl7v2.model.AbstractMessage import ca.uhn.hl7v2.model.Message import ca.uhn.hl7v2.parser.ParserConfiguration -import ca.uhn.hl7v2.util.Hl7InputStreamMessageIterator import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator import ca.uhn.hl7v2.util.Terser import ca.uhn.hl7v2.validation.ValidationException @@ -21,13 +19,10 @@ import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.InvalidReportMessage import org.apache.commons.lang3.exception.ExceptionUtils import org.apache.logging.log4j.Level -import org.apache.logging.log4j.kotlin.Logging +import org.apache.logging.log4j.kotlin.logger import java.util.Date -import ca.uhn.hl7v2.model.v251.message.ORU_R01 as v251_ORU_R01 import ca.uhn.hl7v2.model.v251.segment.MSH as v251_MSH -import ca.uhn.hl7v2.model.v27.message.ORU_R01 as v27_ORU_R01 import ca.uhn.hl7v2.model.v27.segment.MSH as v27_MSH -import fhirengine.translation.hl7.structures.nistelr251.message.ORU_R01 as NIST_ELR_ORU_R01 import fhirengine.translation.hl7.structures.nistelr251.segment.MSH as NIST_MSH private const val MSH_SEGMENT_NAME = "MSH" @@ -35,184 +30,12 @@ private const val MSH_SEGMENT_NAME = "MSH" /** * Converts raw HL7 data (message or batch) to HL7 message objects. */ -class HL7Reader(private val actionLogger: ActionLogger) : Logging { - - /** - * Returns one or more messages read from the raw HL7 data. - * - * This function takes a couple of different approaches to transforming the raw string into messages. - * - * First, it will read the message type from MSH.9 and attempt to find the list of mapped MessageModels. - * See [getMessageModelClasses]. These mappings will typically consist of the v27 structure and the v25 structure - * for that message type. If models are found, the code will iterate over the models and attempt to parse the - * message. If messages are parsed, loop short circuits. - * - * The reason we need to use multiple message models is due to inconsistencies of the specs across different - * organizations. For example, the NIST profile for v251 includes fields that are only available in the v27 - * standard spec. To get around this fact, we take advantage that the specs are mostly backwards compatible; - * a NIST v251 can be parsed using the v271 structure successfully and will now also include the data from the - * fields only available in the standard v27. The only caveat to this approach is that the HAPI library itself - * is not 100% backwards compatible. A common error is that a v251 message will specify a component is a CE, but - * the v27 spec says it must be a CWE; though these two data types are compatible from a field standpoint, the HAPI - * library will throw a type error along the lines of "a CWE field cannot be set to a CE type". To get around this - * issue, if the message cannot be parsed to v27 we fall back to parsing it as a v251 message. - * - * - * If no message models are returned by [getMessageModelClasses], the string is parsed using the default behavior - * of [Hl7InputStreamMessageIterator]. - * - * - * @return one or more HL7 messages - * @throws IllegalArgumentException if the raw data cannot be parsed or no messages were read - */ - fun getMessages(rawMessage: String): List { - val messageModelsToTry = getMessageModelClasses(rawMessage) - val messages: MutableList = mutableListOf() - if (rawMessage.isBlank()) { - actionLogger.error(InvalidReportMessage("Provided raw data is empty.")) - } else if (messageModelsToTry.isEmpty()) { - try { - val iterator = Hl7InputStreamMessageIterator(rawMessage.byteInputStream()) - while (iterator.hasNext()) { - messages.add(iterator.next()) - } - } catch (e: Hl7InputStreamMessageStringIterator.ParseFailureError) { - logHL7ParseFailure(e) - } - } else { - val validationContext = ValidationContextFactory.noValidation() - val parseError = mutableListOf() - run modelLoop@{ - messageModelsToTry.forEach { model -> - val context = DefaultHapiContext(ReportStreamCanonicalModelClassFactory(model)) - context.validationContext = validationContext - try { - val iterator = Hl7InputStreamMessageIterator(rawMessage.byteInputStream(), context) - while (iterator.hasNext()) { - messages.add(iterator.next()) - } - } catch (e: Hl7InputStreamMessageStringIterator.ParseFailureError) { - messages.clear() - parseError.add(e) - } - - if (messages.isNotEmpty()) { - // Don't try other message models if we were able to parse - return@modelLoop - } - } - } - - // if it was able to parse the message through one of the models, then we do not want to log it as an error - val parseLogLevel = if (parseError.size == messageModelsToTry.size) Level.ERROR else Level.WARN - parseError.forEach { currentError -> - logHL7ParseFailure(currentError, messages.isEmpty(), parseLogLevel) - } - } - - if (messages.isEmpty() && !actionLogger.hasErrors()) { - actionLogger.error(InvalidReportMessage("Unable to find HL7 messages in provided data.")) - } - - return messages - } - - /** - * Extracts the message type from the MSH segment and returns the list of message models to use to - * try to parse the messages. - * - * This function assumes all the message types will be the same if this is a HL7 batch. - */ - private fun getMessageModelClasses(rawMessage: String): List> { - try { - val messageProfile = getMessageProfile(rawMessage) - if (messageProfile != null) { - when (messageProfile.typeID) { - "ORU" -> { - return when (messageProfile.profileID) { - // TODO: NIST ELR conformance profile to be enabled in a future PR (rename to "NIST_ELR") - "NIST_ELR_TEST" -> listOf( - NIST_ELR_ORU_R01::class.java - ) - else -> listOf( - v27_ORU_R01::class.java, - v251_ORU_R01::class.java - ) - } - } - else -> { - logger.warn( - "${messageProfile.typeID} did not have any mapped message model classes, " + - "using default behavior" - ) - return emptyList() - } - } - } - } catch (ex: Hl7InputStreamMessageStringIterator.ParseFailureError) { - logHL7ParseFailure(ex) - return emptyList() - } - actionLogger.error(InvalidReportMessage("String did not contain any HL7 messages")) - return emptyList() - } - - /** - * 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 - } - - /** - * Takes an [exception] thrown by the HL7 HAPI library, gets the root cause and logs the error into [actionLogger]. - * Sample error messages returned by the HAPI library are: - * Error Code = DATA_TYPE_ERROR-102: 'test' in record 3 is invalid for version 2.5.1 - * Error Code = REQUIRED_FIELD_MISSING-101: Can't find version ID - MSH.12 is null - * This functions only logs messages that contain meaningful data. - * - */ - private fun logHL7ParseFailure( - exception: Hl7InputStreamMessageStringIterator.ParseFailureError, - isError: Boolean = true, - logLevel: Level = Level.ERROR, - ) { - logger.log(logLevel, "Failed to parse message: ${exception.message}") - - // Get the exception root cause and log it accordingly - when (val rootCause = ExceptionUtils.getRootCause(exception)) { - is AbstractHL7Exception -> recordError(rootCause, isError) - else -> throw rootCause - } - } - - private fun recordError(exception: AbstractHL7Exception, isError: Boolean) { - val errorMessage: String = when (exception) { - is ValidationException -> "Validation Failed: ${exception.message}" - - is HL7Exception -> { - when (exception.errorCode) { - ErrorCode.REQUIRED_FIELD_MISSING.code -> "Required field missing: ${exception.message}" - ErrorCode.DATA_TYPE_ERROR.code -> "Data type error: ${exception.message}" - else -> "Failed to parse message" - } - } - - else -> "Failed to parse message" - } - if (isError) { - actionLogger.error(InvalidReportMessage(errorMessage)) - } else { - actionLogger.warn(InvalidReportMessage(errorMessage)) - } - } - +class HL7Reader { companion object { // This regex is used to replace \n with \r while not replacing \r\n val newLineRegex = Regex("(?, - val hl7toFHIRMappingLocation: String, - ) - - /** - * Map of configured message types to their configuration - */ - val messageToConfigMap = mapOf( - HL7MessageType( - "ORU_R01", - "2.5.1", - "2.16.840.1.113883.9.10" - ) to HL7MessageParseAndConvertConfiguration( - ORU_R01::class.java, - "./metadata/HL7/catchall" - ), - HL7MessageType( - "ORU_R01", - "2.5.1", - "2.16.840.1.113883.9.11" - ) to HL7MessageParseAndConvertConfiguration( - ORU_R01::class.java, - "./metadata/HL7/catchall" - ) - ) - - // TODO: https://github.com/CDCgov/prime-reportstream/issues/14116 /** * Accepts a raw HL7 string and uses the MSH segment to detect the [HL7MessageType] which is then used - * to parse the string into an instance of [Message]. If the type is not one that is configured in - * [messageToConfigMap] the default HAPI parsing logic is used + * to parse the string into an instance of [Message]. If the type is not one that is supported in + * [getHL7ParsingContext] the default HAPI parsing logic is used * * @param rawHL7 the HL7 string to convert into a [Message] * - * @return a [Pair] with parsed message and optional type + * @return a [Message] with parsed message and optional type */ fun parseHL7Message( rawHL7: String, - parseConfiguration: HL7MessageParseAndConvertConfiguration?, ): Message { // A carriage return is the official segment delimiter; a newline is not recognized so we replace // them val carriageReturnFixedHL7 = rawHL7.replace(newLineRegex, "\r") val hl7MessageType = getMessageType(carriageReturnFixedHL7) - return getHL7ParsingContext(hl7MessageType, parseConfiguration).pipeParser.parse(carriageReturnFixedHL7) + return getHL7ParsingContext(hl7MessageType).pipeParser.parse(carriageReturnFixedHL7) } /** * Creates a HAPI context that can be used to parse an HL7 string. If no configuration is passed, the function * will return a context with the HAPI defaults which will defer to that library to determine the kind of message * - * @param hl7MessageParseAndConvertConfiguration optional configuration to use when creating a context */ private fun getHL7ParsingContext( hl7MessageType: HL7MessageType?, - hl7MessageParseAndConvertConfiguration: HL7MessageParseAndConvertConfiguration?, ): HapiContext { - return if (hl7MessageParseAndConvertConfiguration == null) { - if (hl7MessageType?.msh93 == "ORU_R01") { + return when (hl7MessageType?.msh93) { + "ORU_R01" -> { DefaultHapiContext( ParserConfiguration(), ValidationContextFactory.noValidation(), ReportStreamCanonicalModelClassFactory(ORU_R01::class.java), ) - } else if (hl7MessageType?.msh93 == "OML_O21") { + } + "OML_O21" -> { DefaultHapiContext( ParserConfiguration(), ValidationContextFactory.noValidation(), ReportStreamCanonicalModelClassFactory(OML_O21::class.java), ) - } else if (hl7MessageType?.msh93 == "ORM_O01") { + } + "ORM_O01" -> { DefaultHapiContext( ParserConfiguration(), ValidationContextFactory.noValidation(), ReportStreamCanonicalModelClassFactory(ORM_O01::class.java), ) - } else { + } + else -> { DefaultHapiContext(ValidationContextFactory.noValidation()) } - } else { - DefaultHapiContext( - ParserConfiguration(), - ValidationContextFactory.noValidation(), - ReportStreamCanonicalModelClassFactory(hl7MessageParseAndConvertConfiguration.messageModelClass), - ) } } @@ -335,7 +117,7 @@ class HL7Reader(private val actionLogger: ActionLogger) : Logging { */ @Throws(HL7Exception::class) internal fun getMessageType(rawHL7: String): HL7MessageType { - val message = getHL7ParsingContext(null, null) + val message = getHL7ParsingContext(null) .pipeParser // In order to determine the message configuration, only parse the MSH segment since the type of message // is required in order to accurately parse the message in its entirety @@ -349,19 +131,6 @@ class HL7Reader(private val actionLogger: ActionLogger) : Logging { ) } - // map of HL7 message profiles: maps profile to configuration directory path - val profileDirectoryMap: Map = mapOf( - // TODO: https://github.com/CDCgov/prime-reportstream/issues/14124 - // Pair(MessageProfile("ORU", "NIST_ELR"), "./metadata/HL7/v251-elr"), - ) - - // map of HL7 OIDs to supported conformance profiles - // list of OIDs for NIST ELR retrieved from https://oidref.com/2.16.840.1.113883.9 - private val oidProfileMap: Map = mapOf( - Pair("2.16.840.1.113883.9.10", "NIST_ELR"), - Pair("2.16.840.1.113883.9.11", "NIST_ELR") - ) - // data class to uniquely identify a message profile data class MessageProfile(val typeID: String, val profileID: String) @@ -391,21 +160,6 @@ class HL7Reader(private val actionLogger: ActionLogger) : Logging { } } - /** - * Get the profile of the [rawmessage] - * If there are multiple HL7 messages the first message's data will be returned - * @param rawmessage string representative of hl7 messages - * @return the message profile, or null if there is no message - */ - fun getMessageProfile(rawmessage: String): MessageProfile? { - val iterator = Hl7InputStreamMessageIterator(rawmessage.byteInputStream()) - if (!iterator.hasNext()) return null - val hl7message = iterator.next() - val msh9 = Terser(hl7message).get("MSH-9") - val profileID = oidProfileMap[Terser(hl7message).get("MSH-21-3")] ?: "" - return MessageProfile(msh9 ?: "", profileID) - } - /** * Get the birthTime from the [message] * @return the birthTime, if available or blank if not @@ -480,5 +234,53 @@ class HL7Reader(private val actionLogger: ActionLogger) : Logging { 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 + } + + /** + * Takes an [exception] thrown by the HL7 HAPI library, gets the root cause and logs the error into [actionLogger]. + * Sample error messages returned by the HAPI library are: + * Error Code = DATA_TYPE_ERROR-102: 'test' in record 3 is invalid for version 2.5.1 + * Error Code = REQUIRED_FIELD_MISSING-101: Can't find version ID - MSH.12 is null + * This functions only logs messages that contain meaningful data. + * + */ + fun logHL7ParseFailure( + exception: Hl7InputStreamMessageStringIterator.ParseFailureError, + actionLogger: ActionLogger, + logLevel: Level = Level.ERROR, + ) { + logger.log(logLevel, "Failed to parse message: ${exception.message}") + + // Get the exception root cause and log it accordingly + when (val rootCause = ExceptionUtils.getRootCause(exception)) { + is AbstractHL7Exception -> recordError(rootCause, actionLogger) + else -> throw rootCause + } + } + + fun recordError(exception: AbstractHL7Exception, actionLogger: ActionLogger) { + val errorMessage: String = when (exception) { + is ValidationException -> "Validation Failed: ${exception.message}" + + is HL7Exception -> { + when (exception.errorCode) { + ErrorCode.REQUIRED_FIELD_MISSING.code -> "Required field missing: ${exception.message}" + ErrorCode.DATA_TYPE_ERROR.code -> "Data type error: ${exception.message}" + else -> "Failed to parse message" + } + } + + else -> "Failed to parse message" + } + actionLogger.error(InvalidReportMessage(errorMessage)) + } } } \ No newline at end of file diff --git a/prime-router/src/test/kotlin/cli/helpers/HL7DiffHelperTests.kt b/prime-router/src/test/kotlin/cli/helpers/HL7DiffHelperTests.kt index f17ab0631d3..07e2b485888 100644 --- a/prime-router/src/test/kotlin/cli/helpers/HL7DiffHelperTests.kt +++ b/prime-router/src/test/kotlin/cli/helpers/HL7DiffHelperTests.kt @@ -11,13 +11,12 @@ import ca.uhn.hl7v2.model.Varies import ca.uhn.hl7v2.model.v27.datatype.ID import ca.uhn.hl7v2.model.v27.datatype.NM import ca.uhn.hl7v2.model.v27.datatype.ST -import ca.uhn.hl7v2.model.v27.message.ORU_R01 -import gov.cdc.prime.router.ActionLogger +import fhirengine.translation.hl7.structures.fhirinventory.message.ORU_R01 import gov.cdc.prime.router.fhirengine.utils.HL7Reader import kotlin.test.Test class HL7DiffHelperTests { - private val hL7DiffHelper = HL7DiffHelper() + private val hl7DiffHelper = HL7DiffHelper() private val originalMessage = "MSH|^~\\&#|STARLIMS.CDC.Stag^2.16.840.1.114222.4.3.3.2.1.2^ISO|CDC Atlanta2^" + "11D0668319^CLIA|MEDSS-ELR ^2.16.840.1.114222.4.3.3.6.2.1^ISO|MNDOH^2.16.840.1.114222.4.1.3661^ISO|" + "20230501102531-0400||ORU^R01^ORU_R01|3003786103_4988249_33033|T|2.5.1|||NE|NE|USA||||" + @@ -107,25 +106,21 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- @Test fun `diff hl7`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val inputMessage = hl7Reader.getMessages(originalMessage) - val outputMessage = hl7Reader.getMessages(comparisonMessage) - val differences = hL7DiffHelper.diffHl7(inputMessage[0], outputMessage[0]) + val inputMessage = HL7Reader.parseHL7Message(originalMessage) + val outputMessage = HL7Reader.parseHL7Message(comparisonMessage) + val differences = hl7DiffHelper.diffHl7(inputMessage, outputMessage) assertThat(differences.size).isEqualTo(15) - val differences2 = hL7DiffHelper.diffHl7(outputMessage[0], inputMessage[0]) + val differences2 = hl7DiffHelper.diffHl7(outputMessage, inputMessage) assertThat(differences2.size).isEqualTo(15) } @Test fun `test index structure`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val outputMessage = hl7Reader.getMessages(comparisonMessage) - val outputNames = outputMessage[0].names + val outputMessage = HL7Reader.parseHL7Message(comparisonMessage) + val outputNames = outputMessage.names val outputMap: MutableMap = mutableMapOf() - hL7DiffHelper.filterNames(outputMessage[0], outputNames, outputMap) + hl7DiffHelper.filterNames(outputMessage, outputNames, outputMap) assertThat(outputMap.size).isEqualTo(9) assertThat( @@ -138,16 +133,14 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- @Test fun `test compareHl7Type primitive`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val inputMessage = hl7Reader.getMessages(originalMessage) - val outputMessage = hl7Reader.getMessages(comparisonMessage) - val inputVal = ST(inputMessage[0]) + val inputMessage = HL7Reader.parseHL7Message(originalMessage) + val outputMessage = HL7Reader.parseHL7Message(comparisonMessage) + val inputVal = ST(inputMessage) inputVal.value = "blah" - val outputVal = ST(outputMessage[0]) + val outputVal = ST(outputMessage) outputVal.value = "blah" - val samePrimitive = hL7DiffHelper.compareHl7Type( + val samePrimitive = hl7DiffHelper.compareHl7Type( "", inputVal, outputVal, @@ -160,7 +153,7 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- assertThat(samePrimitive).isEmpty() outputVal.value = "test" - val differentPrimitive = hL7DiffHelper.compareHl7Type( + val differentPrimitive = hl7DiffHelper.compareHl7Type( "", inputVal, outputVal, @@ -175,21 +168,19 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- @Test fun `test compareHl7Type varies`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val inputMessage = hl7Reader.getMessages(originalMessage) - val outputMessage = hl7Reader.getMessages(comparisonMessage) - val inputType = ST(inputMessage[0]) + val inputMessage = HL7Reader.parseHL7Message(originalMessage) + val outputMessage = HL7Reader.parseHL7Message(comparisonMessage) + val inputType = ST(inputMessage) inputType.value = "blah" - val outputType = ST(outputMessage[0]) + val outputType = ST(outputMessage) outputType.value = "blah" - val inputVal = Varies(inputMessage[0]) + val inputVal = Varies(inputMessage) inputVal.data = inputType - val outputVal = Varies(outputMessage[0]) + val outputVal = Varies(outputMessage) outputVal.data = outputType - val sameVaries = hL7DiffHelper.compareHl7Type( + val sameVaries = hl7DiffHelper.compareHl7Type( "", inputVal, outputVal, @@ -202,7 +193,7 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- assertThat(sameVaries).isEmpty() outputType.value = "test" - val differentVaries = hL7DiffHelper.compareHl7Type( + val differentVaries = hl7DiffHelper.compareHl7Type( "", inputVal, outputVal, @@ -217,17 +208,15 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- @Test fun `test compareHl7Type composite`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val inputMessage = hl7Reader.getMessages(originalMessage) - val id = ID(inputMessage[0]) + val inputMessage = HL7Reader.parseHL7Message(originalMessage) + val id = ID(inputMessage) id.value = "blah" - val nm = NM(inputMessage[0]) + val nm = NM(inputMessage) nm.value = "blah2" - val sameComposite = hL7DiffHelper.compareHl7Type( + val sameComposite = hl7DiffHelper.compareHl7Type( "", - (inputMessage[0] as ORU_R01).msh.getField(4)[0], - (inputMessage[0] as ORU_R01).msh.getField(4)[0], + (inputMessage as ORU_R01).msh.getField(4)[0], + inputMessage.msh.getField(4)[0], "", 0, 0, @@ -235,11 +224,11 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- ) assertThat(sameComposite).isEmpty() - val outputMessage = hl7Reader.getMessages(comparisonMessage) - val differentComposite = hL7DiffHelper.compareHl7Type( + val outputMessage = HL7Reader.parseHL7Message(comparisonMessage) + val differentComposite = hl7DiffHelper.compareHl7Type( "", - (inputMessage[0] as ORU_R01).msh.getField(4)[0], - (outputMessage[0] as ORU_R01).msh.getField(4)[0], + inputMessage.msh.getField(4)[0], + (outputMessage as ORU_R01).msh.getField(4)[0], "", 0, 0, @@ -250,19 +239,17 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- @Test fun `test compareHl7Type different types`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val inputMessage = hl7Reader.getMessages(originalMessage) - val outputMessage = hl7Reader.getMessages(comparisonMessage) - val inputType = ST(inputMessage[0]) + val inputMessage = HL7Reader.parseHL7Message(originalMessage) + val outputMessage = HL7Reader.parseHL7Message(comparisonMessage) + val inputType = ST(inputMessage) inputType.value = "blah" - val outputType = ST(outputMessage[0]) + val outputType = ST(outputMessage) outputType.value = "blah" - val inputVal = Varies(inputMessage[0]) + val inputVal = Varies(inputMessage) inputVal.data = inputType - val differentVaries = hL7DiffHelper.compareHl7Type( + val differentVaries = hl7DiffHelper.compareHl7Type( "", inputVal, outputType, @@ -277,29 +264,25 @@ OBR|1||232270000212^ProPhase Diagnostics^2.16.840.1.114222.4.1.238646^ISO|55454- @Test fun `expect no diff messages have blank vs empty MSH 8 (ST), OBR 49 (CWE) respectively`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val inputMessage = hl7Reader.getMessages(msgMSH8OBR49Blank) - val outputMessage = hl7Reader.getMessages(msgMSH8OBR49Empty) - val differences = hL7DiffHelper.diffHl7(inputMessage[0], outputMessage[0]) + val inputMessage = HL7Reader.parseHL7Message(msgMSH8OBR49Blank) + val outputMessage = HL7Reader.parseHL7Message(msgMSH8OBR49Empty) + val differences = hl7DiffHelper.diffHl7(inputMessage, outputMessage) assertThat(differences.size).isEqualTo(0) - val differences2 = hL7DiffHelper.diffHl7(outputMessage[0], inputMessage[0]) + val differences2 = hl7DiffHelper.diffHl7(outputMessage, inputMessage) assertThat(differences2.size).isEqualTo(0) } @Test fun `diff output, input missing segments`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) val msg = originalMessage.split("\n").toMutableList() msg.removeAt(1) - val inputMessage = hl7Reader.getMessages(msg.joinToString("\n")) - val outputMessage = hl7Reader.getMessages(originalMessage) - val differences = hL7DiffHelper.diffHl7(inputMessage[0], outputMessage[0]) + val inputMessage = HL7Reader.parseHL7Message(msg.joinToString("\n")) + val outputMessage = HL7Reader.parseHL7Message(originalMessage) + val differences = hl7DiffHelper.diffHl7(inputMessage, outputMessage) // input missing seg SFT assertThat(differences.size).isEqualTo(1) assertThat(differences[0].toString().contains("Input missing segment SFT")) - val differences2 = hL7DiffHelper.diffHl7(outputMessage[0], inputMessage[0]) + val differences2 = hl7DiffHelper.diffHl7(outputMessage, inputMessage) // output missing seg SFT assertThat(differences2.size).isEqualTo(1) assertThat(differences[0].toString().contains("Output missing segment SFT")) diff --git a/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt b/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt index e23ef1b6e35..3a762bd29ae 100644 --- a/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt @@ -11,7 +11,6 @@ import ca.uhn.fhir.validation.ResultSeverityEnum import ca.uhn.fhir.validation.SingleValidationMessage import ca.uhn.fhir.validation.ValidationResult import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator -import fhirengine.translation.hl7.structures.nistelr251.message.ORU_R01 import gov.cdc.prime.router.ActionLogDetail import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.CustomerStatus @@ -922,16 +921,6 @@ class FhirConverterTests { fun `should process an HL7 message with a registered profile`() { mockkObject(BlobAccess) mockkObject(HL7Reader.Companion) - every { HL7Reader.Companion.messageToConfigMap } returns mapOf( - HL7Reader.Companion.HL7MessageType( - "ORU_R01", - "2.5.1", - "2.16.840.1.113883.9.11" - ) to HL7Reader.Companion.HL7MessageParseAndConvertConfiguration( - ORU_R01::class.java, - "./metadata/test_fhir_mapping" - ) - ) val engine = spyk(makeFhirEngine(metadata, settings, TaskAction.process) as FHIRConverter) val actionLogger = ActionLogger() diff --git a/prime-router/src/test/kotlin/fhirengine/translation/HL7toFhirTranslatorTests.kt b/prime-router/src/test/kotlin/fhirengine/translation/HL7toFhirTranslatorTests.kt index 533c1386ea2..a7d8e1838a4 100644 --- a/prime-router/src/test/kotlin/fhirengine/translation/HL7toFhirTranslatorTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/translation/HL7toFhirTranslatorTests.kt @@ -6,8 +6,8 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotEmpty import assertk.assertions.isNotNull import assertk.assertions.isNull -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.fhirengine.translation.HL7toFhirTranslator +import gov.cdc.prime.router.fhirengine.utils.HL7MessageHelpers import gov.cdc.prime.router.fhirengine.utils.HL7Reader import io.github.linuxforhealth.hl7.data.Hl7RelatedGeneralUtils import org.hl7.fhir.r4.model.Bundle @@ -48,16 +48,20 @@ OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by @Test fun `test get message template`() { - val message = HL7Reader(ActionLogger()).getMessages(supportedHL7) - assertThat(message.size).isEqualTo(1) - assertThat(translator.getMessageTemplateType(message[0])).isEqualTo("ORU_R01") + val message = HL7Reader.parseHL7Message(supportedHL7) + assertThat( + HL7MessageHelpers.messageCount(supportedHL7) + ).isEqualTo(1) + assertThat(translator.getMessageTemplateType(message)).isEqualTo("ORU_R01") } @Test fun `test get message model`() { - var message = HL7Reader(ActionLogger()).getMessages(supportedHL7) - assertThat(message.size).isEqualTo(1) - val model = translator.getHL7MessageModel(message[0]) + val supportedMessage = HL7Reader.parseHL7Message(supportedHL7) + assertThat( + HL7MessageHelpers.messageCount(supportedHL7) + ).isEqualTo(1) + val model = translator.getHL7MessageModel(supportedMessage) assertThat(model).isNotNull() assertThat(model.messageName).isEqualTo("ORU_R01") @@ -70,17 +74,21 @@ ORC|NW|ORD448811^NIST EHR|||||||20120628070100|||5742200012^Radon^Nicholas^^^^^^ OBR|1|ORD448811^NIST EHR||1000^Hepatitis A B C Panel^99USL|||20120628070100|||||||||5742200012^Radon^Nicholas^^^^^^NPI^L^^^NPI DG1|1||F11.129^Opioid abuse with intoxication,unspecified^I10C|||W|||||||||1 """.trimIndent() - message = HL7Reader(ActionLogger()).getMessages(unsupportedHL7) - assertThat(message.size).isEqualTo(1) - assertFailure { translator.getHL7MessageModel(message[0]) } + val unsupportedMessage = HL7Reader.parseHL7Message(unsupportedHL7) + assertThat( + HL7MessageHelpers.messageCount(unsupportedHL7) + ).isEqualTo(1) + assertFailure { translator.getHL7MessageModel(unsupportedMessage) } } @Test fun `test a quick translation to FHIR`() { // Note that FHIR content will be tested as an integration test - val message = HL7Reader(ActionLogger()).getMessages(supportedHL7) - assertThat(message.size).isEqualTo(1) - val bundle = translator.translate(message[0]) + val message = HL7Reader.parseHL7Message(supportedHL7) + assertThat( + HL7MessageHelpers.messageCount(supportedHL7) + ).isEqualTo(1) + val bundle = translator.translate(message) assertThat(bundle).isNotNull() assertThat(bundle.type).isEqualTo(Bundle.BundleType.MESSAGE) assertThat(bundle.id).isNotEmpty() @@ -88,9 +96,11 @@ DG1|1||F11.129^Opioid abuse with intoxication,unspecified^I10C|||W|||||||||1 @Test fun `test birth date extension addition`() { - val message = HL7Reader(ActionLogger()).getMessages(supportedHL7ORMWithBirthDateTime) - assertThat(message.size).isEqualTo(1) - val bundle = translator.translate(message[0]) + val message = HL7Reader.parseHL7Message(supportedHL7ORMWithBirthDateTime) + assertThat( + HL7MessageHelpers.messageCount(supportedHL7ORMWithBirthDateTime) + ).isEqualTo(1) + val bundle = translator.translate(message) assertThat(bundle).isNotNull() assertThat(bundle.type).isEqualTo(Bundle.BundleType.MESSAGE) assertThat(bundle.id).isNotEmpty() @@ -111,9 +121,11 @@ DG1|1||F11.129^Opioid abuse with intoxication,unspecified^I10C|||W|||||||||1 @Test fun `test birth date extension is missing when birthdate is only date`() { - val message = HL7Reader(ActionLogger()).getMessages(supportedHL7ORMWithBirthDate) - assertThat(message.size).isEqualTo(1) - val bundle = translator.translate(message[0]) + val message = HL7Reader.parseHL7Message(supportedHL7ORMWithBirthDate) + assertThat( + HL7MessageHelpers.messageCount(supportedHL7ORMWithBirthDateTime) + ).isEqualTo(1) + val bundle = translator.translate(message) assertThat(bundle).isNotNull() assertThat(bundle.type).isEqualTo(Bundle.BundleType.MESSAGE) assertThat(bundle.id).isNotEmpty() diff --git a/prime-router/src/test/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtilsTest.kt b/prime-router/src/test/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtilsTest.kt index a519bcf396e..8fb1d4308ee 100644 --- a/prime-router/src/test/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtilsTest.kt +++ b/prime-router/src/test/kotlin/fhirengine/translation/hl7/utils/HL7ACKUtilsTest.kt @@ -2,7 +2,6 @@ package gov.cdc.prime.router.fhirengine.translation.hl7.utils import assertk.assertThat import assertk.assertions.isEqualTo -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.cli.helpers.HL7DiffHelper import gov.cdc.prime.router.fhirengine.utils.HL7Reader import io.mockk.every @@ -19,7 +18,6 @@ import kotlin.test.Test class HL7ACKUtilsTest { inner class Fixture { - val hl7Reader = HL7Reader(ActionLogger()) val hl7DiffHelper = HL7DiffHelper() private val clock = Clock.fixed( @@ -45,17 +43,16 @@ class HL7ACKUtilsTest { val incomingMessage = """ MSH|^~\&|Epic|Hospital|LIMS|StatePHL|20241003000000||ORM^O01^ORM_O01|4AFA57FE-D41D-4631-9500-286AAAF797E4|T|2.5.1|||AL|NE """.trimIndent() - val parsedIncomingMessage = f.hl7Reader.getMessages(incomingMessage).first() + val expectedMessage = """ + MSH|^~\&|ReportStream|CDC|Epic|Hospital|20240921000000+0000||ACK|$id|T|2.5.1|||NE|NE + MSA|CA|4AFA57FE-D41D-4631-9500-286AAAF797E4 + """.trimIndent() + val parsedIncomingMessage = HL7Reader.parseHL7Message(incomingMessage) val ack = f.utils.generateOutgoingACKMessage(parsedIncomingMessage) - val expected = f.hl7Reader.getMessages( - """ - MSH|^~\&|ReportStream|CDC|Epic|Hospital|20240921000000+0000||ACK|$id|T|2.5.1|||NE|NE - MSA|CA|4AFA57FE-D41D-4631-9500-286AAAF797E4 - """ - ).first() - val actual = f.hl7Reader.getMessages(ack).first() + val expected = HL7Reader.parseHL7Message(expectedMessage) + val actual = HL7Reader.parseHL7Message(ack) val diffs = f.hl7DiffHelper.diffHl7(expected, actual) if (diffs.isNotEmpty()) { diff --git a/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt b/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt index 5abc630dc42..52fdafd144b 100644 --- a/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt @@ -16,6 +16,7 @@ import assertk.assertions.isNull import assertk.assertions.isTrue import ca.uhn.fhir.context.FhirContext import ca.uhn.hl7v2.model.v251.segment.MSH +import ca.uhn.hl7v2.util.Hl7InputStreamMessageIterator import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.CodeStringConditionFilter import gov.cdc.prime.router.CustomerStatus @@ -30,7 +31,6 @@ import gov.cdc.prime.router.azure.ConditionStamper import gov.cdc.prime.router.azure.ConditionStamper.Companion.conditionCodeExtensionURL import gov.cdc.prime.router.azure.DatabaseAccess import gov.cdc.prime.router.azure.LookupTableConditionMapper -import gov.cdc.prime.router.azure.QueueAccess import gov.cdc.prime.router.fhirengine.engine.RSMessageType import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext import gov.cdc.prime.router.fhirengine.translation.hl7.utils.FhirPathUtils @@ -77,7 +77,6 @@ class FHIRBundleHelpersTests { val connection = MockConnection(dataProvider) val accessSpy = spyk(DatabaseAccess(connection)) val blobMock = mockkClass(BlobAccess::class) - val queueMock = mockkClass(QueueAccess::class) val metadata = Metadata(schema = Schema(name = "None", topic = Topic.FULL_ELR, elements = emptyList())) private val shorthandLookupTable = emptyMap().toMutableMap() @@ -666,12 +665,11 @@ class FHIRBundleHelpersTests { val bundle = messages[0] assertThat(bundle).isNotNull() - // create the hl7 reader - val hl7Reader = HL7Reader(actionLogger) + // create the hl7 message val hl7Message = File("src/test/resources/fhirengine/engine/hl7_with_birth_time.hl7").readText() - val hl7messages = hl7Reader.getMessages(hl7Message) + val parsedHl7Message = Hl7InputStreamMessageIterator(hl7Message.byteInputStream()).next() - bundle.handleBirthTime(hl7messages[0]) + bundle.handleBirthTime(parsedHl7Message) val patient = FhirPathUtils.evaluate( CustomContext(bundle, bundle), @@ -697,12 +695,11 @@ class FHIRBundleHelpersTests { val bundle = messages[0] assertThat(bundle).isNotNull() - // create the hl7 reader - val hl7Reader = HL7Reader(actionLogger) + // create the hl7 message val hl7Message = File("src/test/resources/fhirengine/engine/hl7_with_birth_time.hl7").readText() - val hl7messages = hl7Reader.getMessages(hl7Message) + val parsedHl7Message = Hl7InputStreamMessageIterator(hl7Message.byteInputStream()).next() - bundle.handleBirthTime(hl7messages[0]) + bundle.handleBirthTime(parsedHl7Message) val patient = FhirPathUtils.evaluate( CustomContext(bundle, bundle), @@ -728,14 +725,13 @@ class FHIRBundleHelpersTests { val bundle = messages[0] assertThat(bundle).isNotNull() - // create the hl7 reader - val hl7Reader = HL7Reader(actionLogger) + // create the hl7 message val hl7Message = File("src/test/resources/fhirengine/engine/hl7_with_birth_time.hl7").readText() - val hl7Messages = hl7Reader.getMessages(hl7Message) + val parsedHl7Message = Hl7InputStreamMessageIterator(hl7Message.byteInputStream()).next() - assertThat(hl7Messages[0]["MSH"] is MSH).isTrue() + assertThat(parsedHl7Message["MSH"] is MSH).isTrue() - bundle.enhanceBundleMetadata(hl7Messages[0]) + bundle.enhanceBundleMetadata(parsedHl7Message) val expectedDate = Date(1612994857000) // Wednesday, February 10, 2021 10:07:37 PM GMT assertThat(bundle.timestamp).isEqualTo(expectedDate) @@ -753,14 +749,13 @@ class FHIRBundleHelpersTests { val bundle = messages[0] assertThat(bundle).isNotNull() - // create the hl7 reader - val hl7Reader = HL7Reader(actionLogger) + // create the hl7 message val hl7Message = File("src/test/resources/fhirengine/engine/hl7_2.7.hl7").readText() - val hl7Messages = hl7Reader.getMessages(hl7Message) + val parsedHl7Message = Hl7InputStreamMessageIterator(hl7Message.byteInputStream()).next() - assertThat(hl7Messages[0]["MSH"] is ca.uhn.hl7v2.model.v27.segment.MSH).isTrue() + assertThat(parsedHl7Message["MSH"] is ca.uhn.hl7v2.model.v27.segment.MSH).isTrue() - bundle.enhanceBundleMetadata(hl7Messages[0]) + bundle.enhanceBundleMetadata(parsedHl7Message) val expectedDate = Date(1612994857000) // Wednesday, February 10, 2021 10:07:37 PM GMT assertThat(bundle.timestamp).isEqualTo(expectedDate) @@ -778,15 +773,14 @@ class FHIRBundleHelpersTests { val bundle = messages[0] assertThat(bundle).isNotNull() - // create the hl7 reader - val hl7Reader = HL7Reader(actionLogger) + // create the hl7 message val hl7Message = File("src/test/resources/fhirengine/engine/hl7_2.6.hl7").readText() - val hl7Messages = hl7Reader.getMessages(hl7Message) + val parsedHl7Message = Hl7InputStreamMessageIterator(hl7Message.byteInputStream()).next() - assertThat(hl7Messages[0]["MSH"] is MSH).isFalse() - assertThat(hl7Messages[0]["MSH"] is ca.uhn.hl7v2.model.v27.segment.MSH).isFalse() + assertThat(parsedHl7Message["MSH"] is MSH).isFalse() + assertThat(parsedHl7Message["MSH"] is ca.uhn.hl7v2.model.v27.segment.MSH).isFalse() - bundle.enhanceBundleMetadata(hl7Messages[0]) + bundle.enhanceBundleMetadata(parsedHl7Message) assertThat(bundle.timestamp).isNull() assertThat(bundle.identifier.value).isNull() diff --git a/prime-router/src/test/kotlin/fhirengine/utils/HL7QueueMessageHelpersTests.kt b/prime-router/src/test/kotlin/fhirengine/utils/HL7QueueMessageHelpersTests.kt index 21e56122939..6f08e4cc95d 100644 --- a/prime-router/src/test/kotlin/fhirengine/utils/HL7QueueMessageHelpersTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/utils/HL7QueueMessageHelpersTests.kt @@ -7,8 +7,8 @@ import assertk.assertions.isNotEmpty import assertk.assertions.isNotNull import assertk.assertions.isTrue import assertk.assertions.startsWith +import ca.uhn.hl7v2.util.Hl7InputStreamMessageStringIterator import ca.uhn.hl7v2.util.Terser -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.Hl7Configuration import gov.cdc.prime.router.Receiver import gov.cdc.prime.router.Topic @@ -135,7 +135,10 @@ OBX|1|ST|MLI-4000.15^TEMPERATURE||97.7|deg f|||||R|||19980601184619 """.trimIndent() val batchFile = HL7MessageHelpers.batchMessages(listOf(hl7Message, hl7Message), receiver) - val messages = HL7Reader(ActionLogger()).getMessages(batchFile) + val messages = Hl7InputStreamMessageStringIterator(batchFile.byteInputStream()).asSequence() + .map { rawItem -> + HL7Reader.parseHL7Message(rawItem) + }.toList() assertThat(messages).isNotEmpty() assertThat(messages.size).isEqualTo(2) val a = Terser(messages[0]) diff --git a/prime-router/src/test/kotlin/fhirengine/utils/HL7ReaderTests.kt b/prime-router/src/test/kotlin/fhirengine/utils/HL7ReaderTests.kt index aef41a860bf..286074b1230 100644 --- a/prime-router/src/test/kotlin/fhirengine/utils/HL7ReaderTests.kt +++ b/prime-router/src/test/kotlin/fhirengine/utils/HL7ReaderTests.kt @@ -2,23 +2,17 @@ package gov.cdc.prime.router.fhirengine.utils import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.hasSize import assertk.assertions.isEqualTo import assertk.assertions.isFalse -import assertk.assertions.isInstanceOf import assertk.assertions.isNotNull import assertk.assertions.isNull import assertk.assertions.isTrue +import ca.uhn.hl7v2.ErrorCode +import ca.uhn.hl7v2.HL7Exception import ca.uhn.hl7v2.model.Message -import ca.uhn.hl7v2.model.v27.datatype.CWE -import ca.uhn.hl7v2.model.v27.segment.OBX -import ca.uhn.hl7v2.util.Terser -import gov.cdc.prime.router.ActionLogger -import io.mockk.every -import io.mockk.mockk -import io.mockk.spyk -import io.mockk.verify -import org.apache.logging.log4j.kotlin.KotlinLogger +import ca.uhn.hl7v2.parser.EncodingNotSupportedException +import org.apache.commons.lang3.exception.ExceptionUtils +import java.lang.Exception import java.text.SimpleDateFormat import kotlin.test.Test @@ -44,36 +38,39 @@ class HL7ReaderTests { @Test fun `test decoding of bad HL7 messages`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - // Empty data val badData1 = "" - hl7Reader.getMessages(badData1) - assertThat(actionLogger.hasErrors()).isTrue() - actionLogger.logs.clear() + try { + HL7Reader.parseHL7Message(badData1) + } catch (e: Exception) { + assertThat(e is HL7Exception).isTrue() + assertThat(ExceptionUtils.getRootCause(e) is EncodingNotSupportedException).isTrue() + } // Some CSV was sent val badData2 = """ a,b,c 1,2,3 """.trimIndent() - hl7Reader.getMessages(badData2) - assertThat(actionLogger.hasErrors()).isTrue() - actionLogger.logs.clear() + try { + HL7Reader.parseHL7Message(badData2) + } catch (e: Exception) { + assertThat(e is HL7Exception).isTrue() + assertThat(ExceptionUtils.getRootCause(e) is EncodingNotSupportedException).isTrue() + } // Some truncated HL7 val badData3 = "MSH|^~\\&#|MEDITECH^2.16.840.1.114222.4.3.2.2.1.321.111^ISO|COCAA^1.2." - hl7Reader.getMessages(badData3) - assertThat(actionLogger.hasErrors()).isTrue() - actionLogger.logs.clear() + try { + HL7Reader.parseHL7Message(badData3) + } catch (e: Exception) { + assertThat(e is HL7Exception).isTrue() + assertThat(ExceptionUtils.getRootCause(e) is EncodingNotSupportedException).isTrue() + } } @Test fun `test decoding of good HL7 messages`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val goodData1 = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 @@ -89,49 +86,12 @@ OBX|6|CWE|82810-3^Pregnant^LN^^^^2.69||N^No^HL70136||||||F|||202102090000-0600|| NTE|1|L|This is a note|RE SPM|1|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b||258500001^Nasopharyngeal swab^SCT||||71836000^Nasopharyngeal structure (body structure)^SCT^^^^2020-09-01|||||||||202102090000-0600|202102090000-0600 """.trimIndent() - var messages = hl7Reader.getMessages(goodData1) - assertThat(actionLogger.hasErrors()).isFalse() - assertThat(messages.size).isEqualTo(1) - actionLogger.logs.clear() - - val goodData2 = """ -FHS|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|||202102101707-0500 -BHS|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|||202102101707-0500 -MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO -SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 -PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N -ORC|RE|73a6e9bd-aaec-418e-813a-0ad33366ca85|73a6e9bd-aaec-418e-813a-0ad33366ca85|||||||||1629082607^Eddin^Husam^^^^^^CMS&2.16.840.1.113883.3.249&ISO^^^^NPI||^WPN^^^1^386^6825220|20210209||||||Avante at Ormond Beach|170 North King Road^^Ormond Beach^FL^32174^^^^12127|^WPN^^jbrush@avantecenters.com^1^407^7397506|^^^^32174 -OBR|1|73a6e9bd-aaec-418e-813a-0ad33366ca85|0cba76f5-35e0-4a28-803a-2f31308aae9b|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN|||202102090000-0600|202102090000-0600||||||||1629082607^Eddin^Husam^^^^^^CMS&2.16.840.1.113883.3.249&ISO^^^^NPI|^WPN^^^1^386^6825220|||||202102090000-0600|||F -OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN||260415000^Not detected^SCT|||N^Normal (applies to non-numeric results)^HL70078|||F|||202102090000-0600|||CareStart COVID-19 Antigen test_Access Bio, Inc._EUA^^99ELR||202102090000-0600||||Avante at Ormond Beach^^^^^CLIA&2.16.840.1.113883.4.7&ISO^^^^10D0876999^CLIA|170 North King Road^^Ormond Beach^FL^32174^^^^12127 -OBX|2|CWE|95418-0^Whether patient is employed in a healthcare setting^LN^^^^2.69||Y^Yes^HL70136||||||F|||202102090000-0600|||||||||||||||QST -OBX|3|CWE|95417-2^First test for condition of interest^LN^^^^2.69||Y^Yes^HL70136||||||F|||202102090000-0600|||||||||||||||QST -OBX|4|CWE|95421-4^Resides in a congregate care setting^LN^^^^2.69||N^No^HL70136||||||F|||202102090000-0600|||||||||||||||QST -OBX|5|CWE|95419-8^Has symptoms related to condition of interest^LN^^^^2.69||N^No^HL70136||||||F|||202102090000-0600|||||||||||||||QST -SPM|1|0cba76f5-35e0-4a28-803a-2f31308aae9b||258500001^Nasopharyngeal swab^SCT||||71836000^Nasopharyngeal structure (body structure)^SCT^^^^2020-09-01|||||||||202102090000-0600|202102090000-0600 -MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|612092|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO -SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 -PID|1||a40ea680-51bc-4a05-bf23-786dd08e64f2^^^Avante at Ormond Beach^PI||Keeling^Tyson^Chuck^^^^L||19550206|M||2106-3^White^HL70005^^^^2.5.1|97065 Mohr Island^Street^North Taylor^TX^69622^^^^48077||7275555555:1:^PRN^^kenton.wilderman@email.com^1^281^2498561|||||||||U^Unknown^HL70189||||||||N -ORC|RE|4ba0f2c4-5f39-4716-9daa-d450573e7019|4ba0f2c4-5f39-4716-9daa-d450573e7019|||||||||1629082607^Eddin^Husam^^^^^^CMS&2.16.840.1.113883.3.249&ISO^^^^NPI||^WPN^^^1^386^6825220|20210209||||||Avante at Ormond Beach|170 North King Road^^Ormond Beach^FL^32174^^^^12127|^WPN^^jbrush@avantecenters.com^1^407^7397506|^^^^32174 -OBR|1|4ba0f2c4-5f39-4716-9daa-d450573e7019|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN|||202102090000-0600|202102090000-0600||||||||1629082607^Eddin^Husam^^^^^^CMS&2.16.840.1.113883.3.249&ISO^^^^NPI|^WPN^^^1^386^6825220|||||202102090000-0600|||F -OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN||260415000^Not detected^SCT|||N^Normal (applies to non-numeric results)^HL70078|||F|||202102090000-0600|||CareStart COVID-19 Antigen test_Access Bio, Inc._EUA^^99ELR||202102090000-0600||||Avante at Ormond Beach^^^^^CLIA&2.16.840.1.113883.4.7&ISO^^^^10D0876999^CLIA|170 North King Road^^Ormond Beach^FL^32174^^^^12127 -OBX|2|CWE|95418-0^Whether patient is employed in a healthcare setting^LN^^^^2.69||Y^Yes^HL70136||||||F|||202102090000-0600|||||||||||||||QST -OBX|3|CWE|95417-2^First test for condition of interest^LN^^^^2.69||Y^Yes^HL70136||||||F|||202102090000-0600|||||||||||||||QST -OBX|4|CWE|95421-4^Resides in a congregate care setting^LN^^^^2.69||N^No^HL70136||||||F|||202102090000-0600|||||||||||||||QST -OBX|5|CWE|95419-8^Has symptoms related to condition of interest^LN^^^^2.69||N^No^HL70136||||||F|||202102090000-0600|||||||||||||||QST -SPM|1|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b||258500001^Nasopharyngeal swab^SCT||||71836000^Nasopharyngeal structure (body structure)^SCT^^^^2020-09-01|||||||||202102090000-0600|202102090000-0600 -BTS|2 -FTS|1 - """.trimIndent() - messages = hl7Reader.getMessages(goodData2) - assertThat(actionLogger.hasErrors()).isFalse() - assertThat(messages.size).isEqualTo(2) - actionLogger.logs.clear() + val message = HL7Reader.parseHL7Message(goodData1) + assertThat(message.isEmpty).isFalse() } @Test fun `test get message time stamp`() { - val hl7Reader = HL7Reader(ActionLogger()) - fun getTestMessage(timestampStr: String): Message { val rawData = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|$timestampStr||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO @@ -141,9 +101,9 @@ ORC|RE|73a6e9bd-aaec-418e-813a-0ad33366ca85|73a6e9bd-aaec-418e-813a-0ad33366ca85 OBR|1|73a6e9bd-aaec-418e-813a-0ad33366ca85|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN|||202102090000-0600|202102090000-0600||||||||1629082607^Eddin^Husam^^^^^^CMS&2.16.840.1.113883.3.249&ISO^^^^NPI|^WPN^^^1^386^6825220|||||202102090000-0600|||F OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN||260415000^Not detected^SCT|||N^Normal (applies to non-numeric results)^HL70078|||F|||202102090000-0600|||CareStart COVID-19 Antigen test_Access Bio, Inc._EUA^^99ELR||202102090000-0600||||Avante at Ormond Beach^^^^^CLIA&2.16.840.1.113883.4.7&ISO^^^^10D0876999^CLIA|170 North King Road^^Ormond Beach^FL^32174^^^^12127 """.trimIndent() - val message = hl7Reader.getMessages(rawData) - assertThat(message.size).isEqualTo(1) - return message[0] + val message = HL7Reader.parseHL7Message(rawData) + assertThat(message.isEmpty).isFalse() + return message } var timestampStr = "" @@ -163,38 +123,32 @@ OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by @Test fun `test decoding of bad HL7 message - MSH Version ID missing`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val goodData1 = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P||||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO """.trimIndent() - hl7Reader.getMessages(goodData1) - assertThat(actionLogger.hasErrors()).isTrue() - assertThat(actionLogger.logs[0].detail.message).contains("Required field missing") - actionLogger.logs.clear() + try { + HL7Reader.parseHL7Message(goodData1) + } catch (e: HL7Exception) { + assertThat(e.errorCode).isEqualTo(ErrorCode.REQUIRED_FIELD_MISSING.code) + assertThat(ExceptionUtils.getMessage(e)).contains("Can't find version ID - MSH.12 is null") + } } @Test fun `test decoding of bad HL7 message - OBX Wrong data type`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val goodData1 = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO OBX|1|test|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN||260415000^Not detected^SCT|||N^Normal (applies to non-numeric results)^HL70078|||F|||202102090000-0600|||CareStart COVID-19 Antigen test_Access Bio, Inc._EUA^^99ELR||202102090000-0600||||Avante at Ormond Beach^^^^^CLIA&2.16.840.1.113883.4.7&ISO^^^^10D0876999^CLIA|170 North King Road^^Ormond Beach^FL^32174^^^^12127 """.trimIndent() - hl7Reader.getMessages(goodData1) - assertThat(actionLogger.hasErrors()).isTrue() - assertThat(actionLogger.logs[0].detail.message).contains("Data type error") - actionLogger.logs.clear() + try { + HL7Reader.parseHL7Message(goodData1) + } catch (e: HL7Exception) { + assertThat(ExceptionUtils.getMessage(e)).contains("trying to set data type of OBX-5") + } } @Test fun `test isBatch with FSH Header`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val goodData1 = """ FHS|^~\&|||0.0.0.0.1|0.0.0.0.1|202106221314-0400 BHS|^~\&|||0.0.0.0.1|0.0.0.0.1|202106221314-0400 @@ -214,16 +168,15 @@ OBX|1|test|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen b BTS|2 FTS|1 """.trimIndent() - val messages = hl7Reader.getMessages(goodData1) - val isBatch = hl7Reader.isBatch(goodData1, messages.size) + val isBatch = HL7Reader.isBatch( + goodData1, + HL7MessageHelpers.messageCount(goodData1) + ) assertThat(isBatch).isTrue() } @Test fun `test isBatch without FSH Header`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val goodData1 = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 @@ -252,16 +205,15 @@ OBX|1|test|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen b NTE|1|L|This is a note|RE SPM|1|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b||258500001^Nasopharyngeal swab^SCT||||71836000^Nasopharyngeal structure (body structure)^SCT^^^^2020-09-01|||||||||202102090000-0600|202102090000-0600 """.trimIndent() - val messages = hl7Reader.getMessages(goodData1) - val isBatch = hl7Reader.isBatch(goodData1, messages.size) + val isBatch = HL7Reader.isBatch( + goodData1, + HL7MessageHelpers.messageCount(goodData1) + ) assertThat(isBatch).isTrue() } @Test fun `test isBatch singular message`() { - val actionLogger = ActionLogger() - val hl7Reader = HL7Reader(actionLogger) - val goodData1 = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 @@ -277,207 +229,101 @@ OBX|1|test|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen b NTE|1|L|This is a note|RE SPM|1|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b||258500001^Nasopharyngeal swab^SCT||||71836000^Nasopharyngeal structure (body structure)^SCT^^^^2020-09-01|||||||||202102090000-0600|202102090000-0600 """.trimIndent() - val messages = hl7Reader.getMessages(goodData1) - val isBatch = hl7Reader.isBatch(goodData1, messages.size) + val isBatch = HL7Reader.isBatch( + goodData1, + HL7MessageHelpers.messageCount(goodData1) + ) assertThat(isBatch).isFalse() } @Test fun `test getMessageType`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) val justMSH = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO """.trimIndent() - val messages = hL7Reader.getMessages(justMSH) - val type = HL7Reader.getMessageType(messages[0]) + val message = HL7Reader.parseHL7Message(justMSH) + val type = HL7Reader.getMessageType(message) assertThat(type).isEqualTo("ORU") } - @Test - fun `test getMessageProfile`() { - val justMSH = """ - MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO - """.trimIndent() - val messages = HL7Reader.getMessageProfile(justMSH) - assertThat(messages).isEqualTo( - HL7Reader.Companion.MessageProfile( - "ORU", - "NIST_ELR" - ) - ) - - val noProfile = """ - MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA - """.trimIndent() - val messages2 = HL7Reader.getMessageProfile(noProfile) - assertThat(messages2).isEqualTo( - HL7Reader.Companion.MessageProfile( - "ORU", - "" - ) - ) - } - @Test fun `test getBirthTime_DateTime`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) val birthTimeMessage = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORM^O01^ORM_O01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||195808100102034|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N """.trimIndent() - val messages = hL7Reader.getMessages(birthTimeMessage) - val type = HL7Reader.getBirthTime(messages[0]) + val message = HL7Reader.parseHL7Message(birthTimeMessage) + val type = HL7Reader.getBirthTime(message) assertThat(type).isEqualTo("195808100102034") } @Test fun `test getBirthTime_Date`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) val birthDateMessage = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORM^O01^ORM_O01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N """.trimIndent() - val messages = hL7Reader.getMessages(birthDateMessage) - val type = HL7Reader.getBirthTime(messages[0]) + val message = HL7Reader.parseHL7Message(birthDateMessage) + val type = HL7Reader.getBirthTime(message) assertThat(type).isEqualTo("19580810") } @Test fun `test getPatientPath_ORM`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) val birthDateMessage = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORM^O01^ORM_O01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N """.trimIndent() - val messages = hL7Reader.getMessages(birthDateMessage) - val type = HL7Reader.getPatientPath(messages[0]) + val message = HL7Reader.parseHL7Message(birthDateMessage) + val type = HL7Reader.getPatientPath(message) assertThat(type).isEqualTo("PATIENT") } @Test fun `test getPatientPath_OML`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) val birthDateMessage = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||OML^O21^OML_O21|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N """.trimIndent() - val messages = hL7Reader.getMessages(birthDateMessage) - val type = HL7Reader.getPatientPath(messages[0]) + val message = HL7Reader.parseHL7Message(birthDateMessage) + val type = HL7Reader.getPatientPath(message) assertThat(type).isEqualTo("PATIENT") } @Test fun `test getPatientPath_ORU`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) val birthDateMessage = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N """.trimIndent() - val messages = hL7Reader.getMessages(birthDateMessage) - val type = HL7Reader.getPatientPath(messages[0]) + val message = HL7Reader.parseHL7Message(birthDateMessage) + val type = HL7Reader.getPatientPath(message) assertThat(type).isEqualTo("PATIENT_RESULT/PATIENT") } @Test fun `test getPatientPath_Other`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) val birthDateMessage = """ MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||TEST^O01^TEST_O01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N """.trimIndent() - val messages = hL7Reader.getMessages(birthDateMessage) - val type = HL7Reader.getPatientPath(messages[0]) + val message = HL7Reader.parseHL7Message(birthDateMessage) + val type = HL7Reader.getPatientPath(message) assertThat(type).isEqualTo(null) } - @Test - fun `get getMessages no mapped models`() { - val actionLogger = ActionLogger() - val hl7Reader = spyk(HL7Reader(actionLogger), recordPrivateCalls = true) - val mockedLogger = mockk() - - every { hl7Reader.logger } returns mockedLogger - every { mockedLogger.warn(any()) } returns Unit - - val goodData1 = """ - MSH|^~\&|ADT1|GOOD HEALTH HOSPITAL|GHH LAB, INC.|GOOD HEALTH HOSPITAL|198808181126|SECURITY|ADT^A01^ADT_A01|MSG00001|P|2.5.1|| - EVN|A01|200708181123|| - PID|1||PATID1234^5^M11^ADT1^MR^GOOD HEALTH HOSPITAL~123456789^^^USSSA^SS||EVERYMAN^ADAM^A^III||19610615|M||C|2222 HOME STREET^^GREENSBORO^NC^27401-1020|GL|(555) 555-2004|(555)555-2004||S||PATID12345001^2^M10^ADT1^AN^A|444333333|987654^NC| - NK1|1|NUCLEAR^NELDA^W|SPO^SPOUSE||||NK^NEXT OF KIN - PV1|1|I|2000^2012^01||||004777^ATTEND^AARON^A|||SUR||||ADM|A0| - """.trimIndent() - val messages = hl7Reader.getMessages(goodData1) - assertThat(messages).hasSize(1) - verify { - mockedLogger.warn("ADT did not have any mapped message model classes, using default behavior") - } - } - - @Test - fun `get getMessages v27 succeeds`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) - - val data = """ - MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA||||PHLabReportNoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO - SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 - PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N - ORC|RE|73a6e9bd-aaec-418e-813a-0ad33366ca85|73a6e9bd-aaec-418e-813a-0ad33366ca85|||||||||1629082607^Eddin^Husam^^^^^^CMS&2.16.840.1.113883.3.249&ISO^^^^NPI||^WPN^^^1^386^6825220|20210209||||||Avante at Ormond Beach|170 North King Road^^Ormond Beach^FL^32174^^^^12127|^WPN^^jbrush@avantecenters.com^1^407^7397506|^^^^32174 - OBR|1|sphlspid^SPHL-000048^2.16.840.1.114222.4.1.10765^ISO|3015894676_04608646^STARLIMS.CDC.Stag^2.16.840.1.114222.4.3.3.2.1.2^ISO|68991-9^Epidemiologically Important Information^LN^^^^2.69^^^CDC-10516^Poxvirus Serology^L^^2.16.840.1.113883.6.1|||202302101135|||||||||SPHL-000148^CA-Veterans Affairs Palo Alto Healthcare System^^^^^^^STARLIMS.CDC.Stag&2.16.840.1.114222.4.3.3.2.1.2&ISO^^^^XX|^NET^Internet^mark.holodniy@va.gov|||||202302131116-0500|||F - OBX|1|CWE|68993-5^Human RNase P RNA XXX Ql NAA+probe^LN^3844^RNaseP human DNA^L^2.69^v_unknown^RNaseP human DNA|ZZYGNASR-1|260385009^Negative^SCT^^^^09012018^^Negative (No human DNA present)||||||C|||202302101135|11D0668319^Centers for Disease Control and Prevention^CLIA^47^Poxvirus Laboratory/Poxvirus and Rabies Branch^L|UIE8@CDC.GOV^Perkins ^Kayla|||20230213102132||||Centers for Disease Control and Prevention^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^11D0668319|1600 Clifton Rd^^Atlanta^GA^30329^USA^B - NTE|1|L|This is a note|RE - SPM|1|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b||258500001^Nasopharyngeal swab^SCT||||71836000^Nasopharyngeal structure (body structure)^SCT^^^^2020-09-01|||||||||202102090000-0600|202102090000-0600 - """.trimIndent() - - val messages = hL7Reader.getMessages(data) - assertThat(messages).hasSize(1) - assertThat(actionLogger.hasErrors()).isFalse() - } - - @Test - fun `get getMessages can parse a message that uses the deprecated CE type in OBX2`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) - - val data = """ - MSH|^~\&#|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Avante at Ormond Beach^10D0876999^CLIA|PRIME_DOH|Prime ReportStream|20210210170737||ORU^R01^ORU_R01|371784|P|2.5.1|||NE|NE|USA - SFT|Centers for Disease Control and Prevention|0.1-SNAPSHOT|PRIME ReportStream|0.1-SNAPSHOT||20210210 - PID|1||2a14112c-ece1-4f82-915c-7b3a8d152eda^^^Avante at Ormond Beach^PI||Buckridge^Kareem^Millie^^^^L||19580810|F||2106-3^White^HL70005^^^^2.5.1|688 Leighann Inlet^^South Rodneychester^TX^67071^^^^48077||7275555555:1:^PRN^^roscoe.wilkinson@email.com^1^211^2240784|||||||||U^Unknown^HL70189||||||||N - ORC|RE|73a6e9bd-aaec-418e-813a-0ad33366ca85|73a6e9bd-aaec-418e-813a-0ad33366ca85|||||||||1629082607^Eddin^Husam^^^^^^CMS&2.16.840.1.113883.3.249&ISO^^^^NPI||^WPN^^^1^386^6825220|20210209||||||Avante at Ormond Beach|170 North King Road^^Ormond Beach^FL^32174^^^^12127|^WPN^^jbrush@avantecenters.com^1^407^7397506|^^^^32174 - OBR|1|sphlspid^SPHL-000048^2.16.840.1.114222.4.1.10765^ISO|3015894676_04608646^STARLIMS.CDC.Stag^2.16.840.1.114222.4.3.3.2.1.2^ISO|68991-9^Epidemiologically Important Information^LN^^^^2.69^^^CDC-10516^Poxvirus Serology^L^^2.16.840.1.113883.6.1|||202302101135|||||||||SPHL-000148^CA-Veterans Affairs Palo Alto Healthcare System^^^^^^^STARLIMS.CDC.Stag&2.16.840.1.114222.4.3.3.2.1.2&ISO^^^^XX|^NET^Internet^mark.holodniy@va.gov|||||202302131116-0500|||F - OBX|1|CE|68993-5^Human RNase P RNA XXX Ql NAA+probe^LN^3844^RNaseP human DNA^L^2.69^v_unknown^RNaseP human DNA|ZZYGNASR-1|260385009^Negative^SCT^^^^09012018^^Negative (No human DNA present)||||||C|||202302101135|11D0668319^Centers for Disease Control and Prevention^CLIA^47^Poxvirus Laboratory/Poxvirus and Rabies Branch^L|UIE8@CDC.GOV^Perkins ^Kayla|||20230213102132||||Centers for Disease Control and Prevention^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^11D0668319|1600 Clifton Rd^^Atlanta^GA^30329^USA^B - NTE|1|L|This is a note|RE - SPM|1|b518ef23-1d9a-40c1-ac4b-ed7b438dfc4b||258500001^Nasopharyngeal swab^SCT||||71836000^Nasopharyngeal structure (body structure)^SCT^^^^2020-09-01|||||||||202102090000-0600|202102090000-0600 - """.trimIndent() - - val messages = hL7Reader.getMessages(data) - assertThat(messages).hasSize(1) - assertThat(actionLogger.hasWarnings()).isFalse() - val obxSegment = Terser(messages[0]).getSegment("/PATIENT_RESULT/ORDER_OBSERVATION/OBSERVATION/OBX") as OBX - assertThat(obxSegment.getObservationValue(0).data).isInstanceOf(CWE::class) - } - @Test fun `extract MSH segment values`() { - val actionLogger = ActionLogger() - val hL7Reader = HL7Reader(actionLogger) - @Suppress("ktlint:standard:max-line-length") - val rawMessage = "MSH|^~\\&|Epic|Hospital|LIMS|StatePHL|20241003000000||ORM^O01^ORM_O01|4AFA57FE-D41D-4631-9500-286AAAF797E4|T|2.5.1|||AL|NE" - val message = hL7Reader.getMessages(rawMessage).first() + val rawMessage = + "MSH|^~\\&|Epic|Hospital|LIMS|StatePHL|20241003000000||ORM^O01^ORM_O01|4AFA57FE-D41D-4631-9500-286AAAF797E4|T|2.5.1|||AL|NE" + val message = HL7Reader.parseHL7Message(rawMessage) assertThat( HL7Reader.getSendingApplication(message) diff --git a/prime-router/src/test/kotlin/validation/MarsOtcElrOnboardingValidatorTests.kt b/prime-router/src/test/kotlin/validation/MarsOtcElrOnboardingValidatorTests.kt index b7c5e9ce64c..851576300b1 100644 --- a/prime-router/src/test/kotlin/validation/MarsOtcElrOnboardingValidatorTests.kt +++ b/prime-router/src/test/kotlin/validation/MarsOtcElrOnboardingValidatorTests.kt @@ -3,7 +3,6 @@ package gov.cdc.prime.router.validation import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.fhirengine.utils.HL7Reader import org.junit.jupiter.api.Test @@ -17,8 +16,8 @@ class MarsOtcElrOnboardingValidatorTests { this.javaClass.classLoader.getResourceAsStream("validation/marsotcelr/fail_onboarding_pass_prod.hl7") val sampleMessage = sampleMessageInputStream!!.bufferedReader().use { it.readText() } - val messages = HL7Reader(ActionLogger()).getMessages(sampleMessage) - val report = validator.validate(messages[0]) + val message = HL7Reader.parseHL7Message(sampleMessage) + val report = validator.validate(message) assertThat(report.isValid()).isFalse() } @@ -28,8 +27,8 @@ class MarsOtcElrOnboardingValidatorTests { this.javaClass.classLoader.getResourceAsStream("validation/marsotcelr/valid.hl7") val sampleMessage = sampleMessageInputStream!!.bufferedReader().use { it.readText() } - val messages = HL7Reader(ActionLogger()).getMessages(sampleMessage) - val report = validator.validate(messages[0]) + val message = HL7Reader.parseHL7Message(sampleMessage) + val report = validator.validate(message) assertThat(report.isValid()).isTrue() } } \ No newline at end of file diff --git a/prime-router/src/test/kotlin/validation/MarsOtcElrValidatorTests.kt b/prime-router/src/test/kotlin/validation/MarsOtcElrValidatorTests.kt index 16b35f45c91..ee5ceb366a0 100644 --- a/prime-router/src/test/kotlin/validation/MarsOtcElrValidatorTests.kt +++ b/prime-router/src/test/kotlin/validation/MarsOtcElrValidatorTests.kt @@ -3,7 +3,6 @@ package gov.cdc.prime.router.validation import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import gov.cdc.prime.router.ActionLogger import gov.cdc.prime.router.fhirengine.utils.HL7Reader import org.junit.jupiter.api.Test @@ -17,8 +16,8 @@ class MarsOtcElrValidatorTests { this.javaClass.classLoader.getResourceAsStream("validation/marsotcelr/sample_2.hl7") val sampleMessage = sampleMessageInputStream!!.bufferedReader().use { it.readText() } - val messages = HL7Reader(ActionLogger()).getMessages(sampleMessage) - val report = validator.validate(messages[0]) + val message = HL7Reader.parseHL7Message(sampleMessage) + val report = validator.validate(message) assertThat(report.isValid()).isFalse() } @@ -28,8 +27,8 @@ class MarsOtcElrValidatorTests { this.javaClass.classLoader.getResourceAsStream("validation/marsotcelr/valid.hl7") val sampleMessage = sampleMessageInputStream!!.bufferedReader().use { it.readText() } - val messages = HL7Reader(ActionLogger()).getMessages(sampleMessage) - val report = validator.validate(messages[0]) + val message = HL7Reader.parseHL7Message(sampleMessage) + val report = validator.validate(message) assertThat(report.isValid()).isTrue() } @@ -39,8 +38,8 @@ class MarsOtcElrValidatorTests { this.javaClass.classLoader.getResourceAsStream("validation/marsotcelr/valid_altered_msh.hl7") val sampleMessage = sampleMessageInputStream!!.bufferedReader().use { it.readText() } - val messages = HL7Reader(ActionLogger()).getMessages(sampleMessage) - val report = validator.validate(messages[0]) + val message = HL7Reader.parseHL7Message(sampleMessage) + val report = validator.validate(message) assertThat(report.isValid()).isTrue() } } \ No newline at end of file diff --git a/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt b/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt index cfca5b5c76a..f18c57cfbdc 100644 --- a/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt +++ b/prime-router/src/testIntegration/kotlin/datatests/TranslationTests.kt @@ -453,10 +453,7 @@ class TranslationTests { * @return a FHIR bundle as a JSON input stream */ private fun translateToFhir(hl7: String, profile: String? = null): InputStream { - val hl7message = HL7Reader.parseHL7Message( - hl7, - null - ) + val hl7message = HL7Reader.parseHL7Message(hl7) val fhirBundle = if (profile == null) { HL7toFhirTranslator().translate(hl7message) } else {