diff --git a/operations/app/terraform/modules/chatops/main.tf b/operations/app/terraform/modules/chatops/main.tf
index 722ee471762..99e8705d0e7 100644
--- a/operations/app/terraform/modules/chatops/main.tf
+++ b/operations/app/terraform/modules/chatops/main.tf
@@ -48,3 +48,4 @@ resource "azurerm_container_group" "chatops" {
var.storage_account
]
}
+
diff --git a/prime-router/docs/authz/authz-deprecated-implementation.md b/prime-router/docs/authz/authz-deprecated-implementation.md
new file mode 100644
index 00000000000..4beaa25b869
--- /dev/null
+++ b/prime-router/docs/authz/authz-deprecated-implementation.md
@@ -0,0 +1,151 @@
+# Authorization Deep Dive
+
+## Current Design
+
+To authorize a user for an endpoint, we compare the scopes that are present on the access token versus what scope is
+required.
+
+### Scope Design
+
+- A client can have multiple scopes
+- Scopes are in the format of `{organization}.{senderOrReceiver}.{role}`
+- `organization` and `senderOrReceiver` can be omitted with a wildcard `*` or `default`
+
+There are four possible roles.
+
+| Role | Definition |
+|------------|---------------------------------------------------------------------------|
+| primeadmin | system administrator with full access to all endpoints |
+| admin | administrator with access to endpoints that manage their own organization |
+| user | user with read access to endpoints scoped to their organization |
+| report | able to submit a report as an organization |
+
+
+Here are some examples given an organization of md-phd (Maryland Public Health Department)
+
+| Scope | Access |
+|-------------------------|------------------------------------------------------------------------|
+| `*.*.primeadmin` | System Administrator |
+| `md-phd.*.admin` | Organization Administrator for md-phd |
+| `md-phd.*.user` | Organization User for md-phd |
+| `md-phd.*.report` | Organization Report Submitter for md-phd |
+| `md-phd.default.admin` | Organization Administrator for md-phd |
+| `md-phd.default.user` | Organization User for md-phd |
+| `md-phd.default.report` | Organization Report Submitter for md-phd |
+| `md-phd.abc.admin` | Organization Administrator for md-phd scoped to abc sender or receiver |
+| `md-phd.abc.user` | Organization User for md-phd scoped to abc sender or receiver |
+
+
+
+We currently have three methods of authenticating and authorizing a user or machine for accessing
+ReportStream endpoints.
+
+### Server2Server
+
+This is ReportStream code that handles the client credentials OAuth 2.0 flow. This is most often used in machine-to-machine
+communication such as senders submitting reports. The important bit for authorization happens based on the scope requested
+in the call to the `/api/token` endpoint. A member of the engagement team sets up an organization's public key under a
+specific scope in the organization settings. When a request for an access token comes in with a requested scope, we check
+the JWT assertion was signed with the public key under that scope in settings.
+
+If the request is successful, an access token is granted with a scope matching the pattern above.
+
+### Okta
+
+Users are all set up under our Okta instance. We add users to appropriate groups in Okta that correspond to what they
+should be allowed to access. Those groups are in a custom string array claim called `organization` in the access token
+which we then map to scopes that our system can handle. Scopes internal to okta are ignored.
+
+Here is the Okta group to scope mapping strategy given an organization of md-phd (Maryland Public Health Department)
+
+| Okta Group | ReportStream Scope |
+|-------------------------|--------------------|
+| `DHPrimeAdmins` | `*.*.primeadmin` |
+| `DHmd-phdAdmins` | `md-phd.*.admin` |
+| `DHSender_md-phdAdmins` | `md-phd.*.admin` |
+| `DHSender_md-phd` | `md-phd.*.user` |
+| `DHmd-phd` | `md-phd.*.user` |
+* Note: There is no group that maps to the `report` role
+
+
+### Azure Function Keys
+
+This is the default authentication process built into the Azure functions library. It is a simple shared secret that is
+stored in Azure and shared with the client. It is still used on some endpoints for its simplicity of setup. It includes
+no authorization check at all and should be deprecated for that reason.
+
+### Current system pros
+- Flexible for onboarding senders/receivers to allow them to use the system they prefer
+- The scopes are fine-grained allowing us to be specific about what resources a client should be able to access
+
+### Current system cons
+- x-function-key authentication is almost a backdoor into our system given that it does not do authorization
+- Scopes are difficult to understand and are often tailored to a single client rather than generic permissions
+- Authorization code is spread widely across the codebase with a lot of duplication
+- Okta group to scope mapping is clunky and prone to errors based on group naming in the Okta admin portal
+- Difficult to keep track of which client is using what authentication system
+
+
+## Authorization requirements by endpoint
+
+| Location | Function Name | Verb | URL | Auth Strategy | Role requirements | Restrictions |
+|-------------------------------------------------------------------------------------------|-----------------------------|------------------|-------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|------------------------------------------------------------------|----------------------------------|
+| [SenderFilesFunction](../../src/main/kotlin/azure/SenderFilesFunction.kt) | getSenderFiles | GET | /api/sender-files | [OktaAuthentication](../../src/main/kotlin/tokens/OktaAuthentication.kt) | System Admin | None |
+| [AdminApiFunctions](../../src/main/kotlin/azure/AdminApiFunctions.kt) | getSendFailures | GET | /api/adm/getsendfailures | [OktaAuthentication](../../src/main/kotlin/tokens/OktaAuthentication.kt) | System Admin | None |
+| [AdminApiFunctions](../../src/main/kotlin/azure/AdminApiFunctions.kt) | listreceiversconnstatus | GET | /api/adm/listreceiversconnstatus | [OktaAuthentication](../../src/main/kotlin/tokens/OktaAuthentication.kt) | System Admin | None |
+| [AdminApiFunctions](../../src/main/kotlin/azure/AdminApiFunctions.kt) | getresend | GET | /api/adm/getresend | [OktaAuthentication](../../src/main/kotlin/tokens/OktaAuthentication.kt) | System Admin | None |
+| [ApiKeysFunctions](../../src/main/kotlin/azure/ApiKeysFunctions.kt) | getApiKeys | GET | /api/settings/organizations/{organizationName}/public-keys | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [ApiKeysFunctions](../../src/main/kotlin/azure/ApiKeysFunctions.kt) | getApiKeysV1 | GET | /api/v1/settings/organizations/{organizationName}/public-keys | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [ApiKeysFunctions](../../src/main/kotlin/azure/ApiKeysFunctions.kt) | postApiKey | POST | /api/settings/organizations/{organizationName}/public-keys | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin or org admin | Organization |
+| [ApiKeysFunctions](../../src/main/kotlin/azure/ApiKeysFunctions.kt) | deleteApiKey | DELETE | settings/organizations/{organizationName}/public-keys/{scope}/{kid} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin or org admin | Organization |
+| [CheckFunction](../../src/main/kotlin/azure/CheckFunction.kt) | check | GET,POST | /api/check | x-functions-key | None | None |
+| [CheckFunction](../../src/main/kotlin/azure/CheckFunction.kt) | checkreceiver | POST | /api/checkreceiver/org/{orgName}/receiver/{receiverName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin or org admin | Organization |
+| [CovidResultMetadataFunction](../../src/main/kotlin/azure/CovidResultMetaDataFunction.kt) | save-covid-result-metadata | GET | /api/saveTestData | x-functions-key | None | None |
+| [EmailEngineFunction](../../src/main/kotlin/azure/EmailEngineFunction.kt) | createEmailSchedule | POST | /api/email-schedule | EmailEngineFunction.validateUser (Custom Okta) | System Admin | None |
+| [EmailEngineFunction](../../src/main/kotlin/azure/EmailEngineFunction.kt) | deleteEmailSchedule | DELETE | /api/email-schedule/{scheduleId} | EmailEngineFunction.validateUser (Custom Okta) | System Admin | None |
+| [EmailSenderFunction](../../src/main/kotlin/azure/EmailSenderFunction.kt) | emailRegisteredOrganization | POST | /api/email-registered | None | None | None |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | getOrganizations | GET,HEAD | /api/settings/organizations | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | HEAD: System Admin
GET: System Admin, org admin, or org user | Head: None
GET: Organization |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | getOneOrganization | GET | /api/settings/organizations/{organizationName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | getSenders | GET | /api/settings/organizations/{organizationName}/senders | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | updateOneOrganization | DELETE,PUT | /api/settings/organizations/{organizationName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | getOneSender | GET | /api/settings/organizations/{organizationName}/senders/{senderName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | updateOneSender | DELETE,PUT | /api/settings/organizations/{organizationName}/senders/{senderName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | getReceivers | GET | /api/settings/organizations/{organizationName}/receivers | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | getOneReceiver | GET | /api/settings/organizations/{organizationName}/receivers/{receiverName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | updateOneReceiver | DELETE,PUT | /api/settings/organizations/{organizationName}/receivers/{receiverName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [SettingsFunctions](../../src/main/kotlin/azure/SettingsFunctions.kt) | getSettingRevisionHistory | GET | /api/waters/org/{organizationName}/settings/revs/{settingSelector} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, or org user | Organization |
+| [RequeueFunction](../../src/main/kotlin/azure/RequeueFunction.kt) | requeue | POST | /api/requeue/send | x-functions-key | None | None |
+| [RequeueFunction](../../src/main/kotlin/azure/RequeueFunction.kt) | doResendFunction | POST | /api/adm/resend | [OktaAuthentication](../../src/main/kotlin/tokens/OktaAuthentication.kt) | System Admin | None |
+| [TokenFunction](../../src/main/kotlin/azure/TokenFunction.kt) | token | POST | /api/token | [Server2ServerAuthentication](../../src/main/kotlin/tokens/Server2ServerAuthentication.kt) | Requires properly signed client assertion JWT | None |
+| [HistoryFunctions](../../src/main/kotlin/azure/HistoryFunctions.kt) | getReports | GET,HEAD,OPTIONS | /api/history/report | HistoryFunctions.checkAuthenticated (Custom) | System Admin or org user (no org Admin) | Organization |
+| [HistoryFunctions](../../src/main/kotlin/azure/HistoryFunctions.kt) | searchReports | POST | /api/v1/reports/search | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [HistoryFunctions](../../src/main/kotlin/azure/HistoryFunctions.kt) | getReportById | GET | /api/history/report/{reportId} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [HistoryFunctions](../../src/main/kotlin/azure/HistoryFunctions.kt) | getFacilitiesByReportId | GET | /api/history/report/{reportId}/facilities | HistoryFunctions.checkAuthenticated (Custom) | System Admin or org user (no org Admin) | Organization |
+| [LookupTableFunctions](../../src/main/kotlin/azure/LookupTableFunctions.kt) | getLookupTableList | GET,HEAD | /api/lookuptables/list | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | None | None |
+| [LookupTableFunctions](../../src/main/kotlin/azure/LookupTableFunctions.kt) | getLookupTableData | GET | /api/lookuptables/{tableName}/{tableVersion}/content | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | None | None |
+| [LookupTableFunctions](../../src/main/kotlin/azure/LookupTableFunctions.kt) | getActiveLookupTableData | GET | /api/lookuptables/{tableName}/content | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | None | None |
+| [LookupTableFunctions](../../src/main/kotlin/azure/LookupTableFunctions.kt) | getLookupTableInfo | GET | /api/lookuptables/{tableName}/{tableVersion}/info | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | None | None |
+| [LookupTableFunctions](../../src/main/kotlin/azure/LookupTableFunctions.kt) | createLookupTable | POST | /api/lookuptables/{tableName} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [LookupTableFunctions](../../src/main/kotlin/azure/LookupTableFunctions.kt) | activateLookupTable | PUT | /api/lookuptables/{tableName}/{tableVersion}/activate | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [MessagesFunctions](../../src/main/kotlin/azure/MessagesFunctions.kt) | messageSearch | GET | /api/messages | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [MessagesFunctions](../../src/main/kotlin/azure/MessagesFunctions.kt) | messageDetails | GET | /api/message/{id} | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [MetaDataFunction](../../src/main/kotlin/azure/MetaDataFunction.kt) | getLivdData | GET | /api/metadata/livd | None | None | None |
+| [ReportFunction](../../src/main/kotlin/azure/ReportFunction.kt) | reports | POST | /api/reports | x-functions-key | None | None |
+| [ReportFunction](../../src/main/kotlin/azure/ReportFunction.kt) | getMessagesFromTestBank | POST | /api/reports/testing | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [ReportFunction](../../src/main/kotlin/azure/ReportFunction.kt) | processFhirDataRequest | POST | /api/reports/testing/test | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [ReportFunction](../../src/main/kotlin/azure/ReportFunction.kt) | downloadReport | GET | /api/reports/download | x-functions-key | None | None |
+| [ReportFunction](../../src/main/kotlin/azure/ReportFunction.kt) | waters | POST | /api/waters | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization and Sender |
+| [ValidateFunction](../../src/main/kotlin/azure/ValidateFunction.kt) | validate | POST | /api/validate | None | None | None |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getDeliveriesV1 | POST | /api/v1/receivers/{receiverName}/deliveries | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getDeliveriesHistory | POST | /api/v1/waters/org/{organization}/deliveries | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getDeliveries | GET | /api/waters/org/{organization}/deliveries | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getDeliveryDetails | GET | /api/waters/report/{id}/delivery | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getEtorMetadataForDelivery | GET | /api/waters/report/{reportId}/delivery/etorMetadata | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getDeliveryFacilities | GET | /api/waters/report/{id}/facilities | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getReportItemsV1 | GET | /api/v1/report/{reportId}/items | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin | None |
+| [DeliveryFunction](../../src/main/kotlin/history/azure/DeliveryFunction.kt) | getSubmittersV1 | POST | /api/v1/receivers/{receiverName}/deliveries/submitters/search | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [SubmissionFunction](../../src/main/kotlin/history/azure/SubmissionFunction.kt) | getOrgSubmissionsList | GET | /api/waters/org/{organization}/submissions | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [SubmissionFunction](../../src/main/kotlin/history/azure/SubmissionFunction.kt) | getReportDetailedHistory | GET | /api/waters/report/{id}/history | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+| [SubmissionFunction](../../src/main/kotlin/history/azure/SubmissionFunction.kt) | getEtorMetadataForHistory | GET | /api/waters/report/{reportId}/history/etorMetadata | [AuthenticatedClaims](../../src/main/kotlin/tokens/AuthenticatedClaims.kt) | System Admin, org admin, org user, or report | Organization |
+
+*This is valid as of 12/16/24
diff --git a/prime-router/docs/design/design/RS-Extention-Registry.yml b/prime-router/docs/design/design/RS-Extention-Registry.yml
index 8b3bb667f28..247103ae682 100644
--- a/prime-router/docs/design/design/RS-Extention-Registry.yml
+++ b/prime-router/docs/design/design/RS-Extention-Registry.yml
@@ -3418,6 +3418,99 @@
valueType: String
hl7Use: XTN.12
+- Resource: Any
+ FHIR-Type: Extension
+ HL7-Type:
+ CP:
+ Example:
+ {
+ "fullUrl": "Coverage/1733795578717198000.8888487d-55f4-4d1d-963d-f8692fc77e9c",
+ "resource": {
+ "resourceType": "Coverage",
+ "id": "1733795578717198000.8888487d-55f4-4d1d-963d-f8692fc77e9c",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.40"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 33.33
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "MXN"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P3"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 3.003
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -3
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ # CWE to CodeableConcept
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "P"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ Extension:
+ - id: composite-price
+ url: "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price"
+ valueType: Extension
+ hl7Use: NA
+ extension:
+ - id: hl7v2Field
+ url: "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field"
+ valueType: String
+ hl7Use: NA
+ description: Identifies the HL7 field of the data.
+ - id: cp-price-quantity
+ url: "CP.1.1"
+ valueType: Decimal
+ hl7use: CP.1.1
+ - id: cp-price-denomination
+ url: "CP.1.2"
+ valueType: String
+ hl7use: CP.1.2
+ - id: cp-price-type
+ url: "CP.2"
+ valueType: String
+ hl7use: CP.2
+ - id: cp-from-value
+ url: "CP.3"
+ valueType: Decimal
+ hl7use: CP.3
+ - id: cp-to-value
+ url: "CP.4"
+ valueType: Decimal
+ hl7use: CP.4
+ - id: cp-range-units
+ url: "CP.5"
+ valueType: CodeableConcept
+ hl7use: CP.5
+ - id: cp-range-type
+ url: "CP.6"
+ valueType: String
+ hl7use: CP.6
+
- Resource: Any
FHIR-Type: MessageHeader.destination
HL7-Type:
diff --git a/prime-router/metadata/HL7/catchall/hl7/codesystem/ExtensionUrlMapping.yml b/prime-router/metadata/HL7/catchall/hl7/codesystem/ExtensionUrlMapping.yml
index 2643e984fed..cc27f956220 100644
--- a/prime-router/metadata/HL7/catchall/hl7/codesystem/ExtensionUrlMapping.yml
+++ b/prime-router/metadata/HL7/catchall/hl7/codesystem/ExtensionUrlMapping.yml
@@ -65,6 +65,10 @@
- id: "xon-organization"
url: "https://reportstream.cdc.gov/fhir/StructureDefinition/xon-organization"
+# CP -> Extension
+- id: "composite-price"
+ url: "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price"
+
# CX -> Identifier
- id: "assigning-facility"
url: "https://reportstream.cdc.gov/fhir/StructureDefinition/assigning-facility"
diff --git a/prime-router/metadata/HL7/catchall/hl7/datatypes/CP/CPExtension.yml b/prime-router/metadata/HL7/catchall/hl7/datatypes/CP/CPExtension.yml
new file mode 100644
index 00000000000..ad352c02b56
--- /dev/null
+++ b/prime-router/metadata/HL7/catchall/hl7/datatypes/CP/CPExtension.yml
@@ -0,0 +1,103 @@
+# $schema: ./../../../../../json_schema/fhir/hl7-to-fhir-mapping-resource-template.json
+
+url:
+ type: SYSTEM_URL
+ value: composite-price
+
+extension:
+ expressionType: nested
+ generateList: true
+ expressions:
+ - expressionType: nested
+ expressionsMap:
+ url:
+ type: SYSTEM_URL
+ value: hl7v2Field
+ valueString:
+ type: STRING
+ expressionType: HL7Spec
+ valueOf: $cpExtensionName
+ - expressionType: nested
+ vars:
+ cp11: STRING, CP.1
+ condition: $cp11 NOT_NULL
+ expressionsMap:
+ url:
+ type: STRING
+ value: CP.1.1
+ valueDecimal:
+ type: STRING
+ expressionType: HL7Spec
+ valueOf: CP.1.1
+ - expressionType: nested
+ vars:
+ cp12: STRING, CP.1.2
+ condition: $cp12 NOT_NULL
+ expressionsMap:
+ url:
+ type: STRING
+ value: CP.1.2
+ valueString:
+ type: STRING
+ expressionType: HL7Spec
+ valueOf: CP.1.2
+ - expressionType: nested
+ vars:
+ cp2: STRING, CP.2
+ condition: $cp2 NOT_NULL
+ expressionsMap:
+ url:
+ type: STRING
+ valueOf: CP.2
+ valueString:
+ type: STRING
+ expressionType: HL7Spec
+ valueOf: CP.2
+ - expressionType: nested
+ vars:
+ cp3: STRING, CP.3
+ condition: $cp3 NOT_NULL
+ expressionsMap:
+ url:
+ type: STRING
+ valueOf: CP.3
+ valueDecimal:
+ type: STRING
+ expressionType: HL7Spec
+ valueOf: CP.3
+ - expressionType: nested
+ vars:
+ cp4: STRING, CP.4
+ condition: $cp4 NOT_NULL
+ expressionsMap:
+ url:
+ type: STRING
+ valueOf: CP.4
+ valueDecimal:
+ type: STRING
+ expressionType: HL7Spec
+ valueOf: CP.4
+ - expressionType: nested
+ vars:
+ cp5: STRING_ALL, CP.5
+ condition: $cp5 NOT_NULL
+ expressionsMap:
+ url:
+ type: STRING
+ value: CP.5
+ valueCodeableConcept:
+ valueOf: datatypes/CWE/CodeableConcept
+ specs: CP.5
+ expressionType: resource
+ - expressionType: nested
+ vars:
+ cp6: STRING, CP.6
+ condition: $cp6 NOT_NULL
+ expressionsMap:
+ url:
+ type: STRING
+ valueOf: CP.6
+ valueString:
+ type: STRING
+ expressionType: HL7Spec
+ valueOf: CP.6
diff --git a/prime-router/metadata/HL7/catchall/hl7/datatypes/MOC/Extension.yml b/prime-router/metadata/HL7/catchall/hl7/datatypes/MOC/MOCExtension.yml
similarity index 100%
rename from prime-router/metadata/HL7/catchall/hl7/datatypes/MOC/Extension.yml
rename to prime-router/metadata/HL7/catchall/hl7/datatypes/MOC/MOCExtension.yml
diff --git a/prime-router/metadata/HL7/catchall/hl7/segments/IN1/Coverage.yml b/prime-router/metadata/HL7/catchall/hl7/segments/IN1/Coverage.yml
index 8479f85f8bc..c4bfd5d0c1b 100644
--- a/prime-router/metadata/HL7/catchall/hl7/segments/IN1/Coverage.yml
+++ b/prime-router/metadata/HL7/catchall/hl7/segments/IN1/Coverage.yml
@@ -13,4 +13,36 @@ extension:
expressions:
- expressionType: resource
valueOf: datatypes/AUI/AUIExtension
- specs: IN1.14
\ No newline at end of file
+ specs: IN1.14
+ - expressionType: resource
+ vars:
+ in137: STRING, IN1.37
+ condition: $in137 NOT_NULL
+ valueOf: datatypes/CP/CPExtension
+ constants:
+ cpExtensionName: IN1.37
+ specs: IN1.37
+ - expressionType: resource
+ vars:
+ in138: STRING, IN1.38
+ condition: $in138 NOT_NULL
+ valueOf: datatypes/CP/CPExtension
+ constants:
+ cpExtensionName: IN1.38
+ specs: IN1.38
+ - expressionType: resource
+ vars:
+ in140: STRING, IN1.40
+ condition: $in140 NOT_NULL
+ valueOf: datatypes/CP/CPExtension
+ constants:
+ cpExtensionName: IN1.40
+ specs: IN1.40
+ - expressionType: resource
+ vars:
+ in141: STRING, IN1.41
+ condition: $in141 NOT_NULL
+ valueOf: datatypes/CP/CPExtension
+ constants:
+ cpExtensionName: IN1.41
+ specs: IN1.41
diff --git a/prime-router/metadata/HL7/catchall/hl7/segments/ORC/OBRExtension.yml b/prime-router/metadata/HL7/catchall/hl7/segments/ORC/OBRExtension.yml
index c2509802ecf..78c9142b244 100644
--- a/prime-router/metadata/HL7/catchall/hl7/segments/ORC/OBRExtension.yml
+++ b/prime-router/metadata/HL7/catchall/hl7/segments/ORC/OBRExtension.yml
@@ -335,7 +335,7 @@ extension:
expressionType: HL7Spec
valueOf: OBR.22
- expressionType: resource
- valueOf: datatypes/MOC/Extension
+ valueOf: datatypes/MOC/MOCExtension
vars:
obr23: STRING_ALL, OBR.23
condition: $obr23 NOT_NULL
diff --git a/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt b/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt
index e5cd8908412..87bff24f1e9 100644
--- a/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt
+++ b/prime-router/src/main/kotlin/fhirengine/engine/FHIRConverter.kt
@@ -44,6 +44,7 @@ import gov.cdc.prime.router.azure.observability.event.AzureEventServiceImpl
import gov.cdc.prime.router.azure.observability.event.IReportStreamEventService
import gov.cdc.prime.router.azure.observability.event.ReportStreamEventName
import gov.cdc.prime.router.azure.observability.event.ReportStreamEventProperties
+import gov.cdc.prime.router.common.BaseEngine
import gov.cdc.prime.router.fhirengine.translation.HL7toFhirTranslator
import gov.cdc.prime.router.fhirengine.translation.hl7.FhirTransformer
import gov.cdc.prime.router.fhirengine.translation.hl7.utils.CustomContext
@@ -52,6 +53,8 @@ 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
@@ -111,6 +114,7 @@ class FHIRConverter(
companion object {
private val clientIdHeader = "client_id"
+ private val payloadNameHeader = "payloadname"
/**
* Converts a [FhirConvertQueueMessage] into the input to the convert processing
@@ -157,6 +161,7 @@ class FHIRConverter(
val blobSubFolderName = message.blobSubFolderName
val clientId = message.headers[clientIdHeader]
+ val payloadName = message.headers[payloadNameHeader]
val sender = clientId?.takeIf { it.isNotBlank() }?.let { settings.findSender(it) }
if (sender == null) {
throw SubmissionSenderNotFound(clientId ?: "", reportId, blobUrl)
@@ -178,7 +183,8 @@ class FHIRConverter(
// is properly recorded in the report file table with the correct sender
actionHistory.trackExternalInputReport(
report,
- BlobAccess.BlobInfo(format, blobUrl, blobDigest.toByteArray())
+ BlobAccess.BlobInfo(format, blobUrl, blobDigest.toByteArray()),
+ payloadName
)
actionHistory.trackActionSenderInfo(sender.fullName)
@@ -258,7 +264,7 @@ class FHIRConverter(
// TODO: https://github.com/CDCgov/prime-reportstream/issues/14287
FhirPathUtils
- val processedItems = process(format, input.blobURL, input.blobDigest, input.topic, actionLogger)
+ val processedItems = process(format, input, actionLogger)
// processedItems can be empty in three scenarios:
// - the blob had no contents, i.e. an empty file was submitted
@@ -336,6 +342,12 @@ class FHIRConverter(
nextAction = TaskAction.destination_filter
)
+ logger.info(
+ "Applied transform - parentReportId=[${input.reportId}]" +
+ ", childReportId=[${report.id}], schemaName=[${input.schemaName}]" +
+ ", trackingId=[${processedItem.getTrackingId()}]"
+ )
+
// create route event
val routeEvent = ProcessEvent(
Event.EventAction.DESTINATION_FILTER,
@@ -382,7 +394,8 @@ class FHIRConverter(
mapOf(
ReportStreamEventProperties.BUNDLE_DIGEST
to bundleDigestExtractor.generateDigest(processedItem.bundle!!),
- ReportStreamEventProperties.ITEM_FORMAT to format
+ ReportStreamEventProperties.ITEM_FORMAT to format,
+ ReportStreamEventProperties.ENRICHMENTS to input.schemaName
)
)
}
@@ -450,14 +463,12 @@ class FHIRConverter(
*/
internal fun process(
format: MimeFormat,
- blobURL: String,
- blobDigest: String,
- topic: Topic,
+ input: FHIRConvertInput,
actionLogger: ActionLogger,
routeReportWithInvalidItems: Boolean = true,
): List> {
- val validator = topic.validator
- val rawReport = BlobAccess.downloadBlob(blobURL, blobDigest)
+ val validator = input.topic.validator
+ val rawReport = BlobAccess.downloadBlob(input.blobURL, input.blobDigest)
return if (rawReport.isBlank()) {
actionLogger.error(InvalidReportMessage("Provided raw data is empty."))
emptyList()
@@ -471,7 +482,7 @@ class FHIRConverter(
"format" to format.name
)
) {
- getBundlesFromRawHL7(rawReport, validator, topic.hl7ParseConfiguration)
+ getBundlesFromRawHL7(rawReport, validator, input.topic.hl7ParseConfiguration)
}
} catch (ex: ParseFailureError) {
actionLogger.error(
@@ -508,21 +519,24 @@ class FHIRConverter(
}
// 'stamp' observations with their condition code
if (item.bundle != null) {
+ val isElr = item.bundle!!.getRSMessageType() == RSMessageType.LAB_RESULT
item.bundle!!.getObservations().forEach { observation ->
- val result = stamper.stampObservation(observation)
- if (!result.success) {
- val logger = actionLogger.getItemLogger(item.index + 1, observation.id)
- if (result.failures.isEmpty()) {
- logger.warn(UnmappableConditionMessage())
- } else {
- logger.warn(
- result.failures.map {
- UnmappableConditionMessage(
- it.failures.map { it.code },
- it.source
+ if (isElr) {
+ val result = stamper.stampObservation(observation)
+ if (!result.success) {
+ val logger = actionLogger.getItemLogger(item.index + 1, observation.id)
+ if (result.failures.isEmpty()) {
+ logger.warn(UnmappableConditionMessage())
+ } else {
+ logger.warn(
+ result.failures.map {
+ UnmappableConditionMessage(
+ it.failures.map { it.code },
+ it.source
+ )
+ }
)
}
- )
}
}
}
diff --git a/prime-router/src/main/kotlin/fhirengine/engine/RSMessageType.kt b/prime-router/src/main/kotlin/fhirengine/engine/RSMessageType.kt
new file mode 100644
index 00000000000..d1db9dd9d12
--- /dev/null
+++ b/prime-router/src/main/kotlin/fhirengine/engine/RSMessageType.kt
@@ -0,0 +1,11 @@
+package gov.cdc.prime.router.fhirengine.engine
+
+/**
+ * This class represents a way to group message types from an RS perspective. As we add additional logical
+ * groupings, FHIRBundleHelpers.getRSMessageType will need to be updated.
+ *
+ */
+enum class RSMessageType {
+ LAB_RESULT,
+ UNKNOWN,
+}
\ No newline at end of file
diff --git a/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt b/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt
index 564d0d5e0e7..dc12c4a9e8c 100644
--- a/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt
+++ b/prime-router/src/main/kotlin/fhirengine/utils/FHIRBundleHelpers.kt
@@ -9,12 +9,14 @@ import gov.cdc.prime.router.azure.ConditionStamper.Companion.BUNDLE_CODE_IDENTIF
import gov.cdc.prime.router.azure.ConditionStamper.Companion.BUNDLE_VALUE_IDENTIFIER
import gov.cdc.prime.router.azure.ConditionStamper.Companion.conditionCodeExtensionURL
import gov.cdc.prime.router.codes
+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
import gov.cdc.prime.router.fhirengine.utils.FHIRBundleHelpers.Companion.getChildProperties
import io.github.linuxforhealth.hl7.data.Hl7RelatedGeneralUtils
import org.hl7.fhir.r4.model.Base
import org.hl7.fhir.r4.model.Bundle
+import org.hl7.fhir.r4.model.CodeType
import org.hl7.fhir.r4.model.CodeableConcept
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.DateTimeType
@@ -116,6 +118,36 @@ fun Bundle.addProvenanceReference() {
}
}
+/**
+ * Return true if Bundle contains an ELR in the MessageHeader.
+ *
+ * @return true if has a MesssageHeader that contains an R01 or ORU_R01, otherwise false.
+ */
+fun Bundle.isElr(): Boolean {
+ val code = FhirPathUtils.evaluate(
+ null,
+ this,
+ this,
+ "Bundle.entry.resource.ofType(MessageHeader).event.code"
+ )
+ .filterIsInstance()
+ .firstOrNull()
+ ?.code
+ return ((code == "R01") || (code == "ORU_R01"))
+}
+
+/**
+ * Return RSMessageType based on grouping logic.
+ *
+ * @return RSMessageType of this Bundle.
+ */
+fun Bundle.getRSMessageType(): RSMessageType {
+ return when {
+ isElr() -> RSMessageType.LAB_RESULT
+ else -> RSMessageType.UNKNOWN
+ }
+}
+
/**
* Gets all properties for a [Base] resource recursively and filters only its references
*
diff --git a/prime-router/src/main/kotlin/history/db/ReportGraph.kt b/prime-router/src/main/kotlin/history/db/ReportGraph.kt
index 103b9831eb3..0b44dcfe271 100644
--- a/prime-router/src/main/kotlin/history/db/ReportGraph.kt
+++ b/prime-router/src/main/kotlin/history/db/ReportGraph.kt
@@ -123,11 +123,10 @@ class ReportGraph(
}
/**
- * Recursively goes up the report_linage table from any report until it reaches
- * a report with an action type of "receive" (the root report)
+ * Recursively goes up the report_lineage table from any report until it reaches
+ * a report that does not appear in report_lineage as a child report (the root report)
*
- * This will return null if no report with action type "receive" is present or if
- * the root is passed in
+ * This will return null if the root is passed in
*/
fun getRootReport(childReportId: UUID): ReportFile? {
return db.transactReturning { txn ->
@@ -172,19 +171,19 @@ class ReportGraph(
.from(cte)
.join(REPORT_FILE)
.on(REPORT_FILE.REPORT_ID.eq(ItemGraphTable.ITEM_GRAPH.PARENT_REPORT_ID))
- .join(ACTION)
- .on(ACTION.ACTION_ID.eq(REPORT_FILE.ACTION_ID))
- .where(ACTION.ACTION_NAME.eq(TaskAction.receive))
+ .leftJoin(REPORT_LINEAGE)
+ .on(REPORT_FILE.REPORT_ID.eq(REPORT_LINEAGE.CHILD_REPORT_ID))
+ .where(REPORT_LINEAGE.PARENT_REPORT_ID.isNull())
+ .orderBy(REPORT_FILE.ACTION_ID.asc())
.fetchOneInto(Item::class.java)
return rootItem
}
/**
- * Recursively goes up the report_linage table from any report until it reaches
- * all reports with an action type of "receive" (the root report)
+ * Recursively goes up the report_lineage table from any report until it reaches
+ * all reports that do not appear in report_lineage as a child report (the root report)
*
- * This will return null if no report with action type "receive" is present or if
- * the root is passed in
+ * This will return null if the root is passed in
*
* If the passed in report ID has multiple root reports, they will all be returned
*/
@@ -476,9 +475,10 @@ class ReportGraph(
.from(cte)
.join(REPORT_FILE)
.on(REPORT_FILE.REPORT_ID.eq(cte.field(0, UUID::class.java)))
- .join(ACTION)
- .on(ACTION.ACTION_ID.eq(REPORT_FILE.ACTION_ID))
- .where(ACTION.ACTION_NAME.eq(TaskAction.receive))
+ .leftJoin(REPORT_LINEAGE)
+ .on(REPORT_FILE.REPORT_ID.eq(REPORT_LINEAGE.CHILD_REPORT_ID))
+ .where(REPORT_LINEAGE.PARENT_REPORT_ID.isNull())
+ .orderBy(REPORT_FILE.ACTION_ID.asc())
/**
* Accepts a list of ids and walks down the report lineage graph
diff --git a/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-base.yml b/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-base.yml
index 305e890b694..5038b8227fa 100644
--- a/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-base.yml
+++ b/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-base.yml
@@ -1,6 +1,6 @@
# $schema: ./../../../../../../metadata/json_schema/fhir/fhir-to-hl7-mapping.json
-hl7Class: ca.uhn.hl7v2.model.v27.message.OML_O21
+hl7Class: fhirengine.translation.hl7.structures.fhirinventory.message.OML_O21
constants:
# Prefix for RS custom extension URLs
diff --git a/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-test.yml b/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-test.yml
index d9c02000ec8..5dda094bd39 100644
--- a/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-test.yml
+++ b/prime-router/src/main/resources/metadata/hl7_mapping/OML_O21/OML_O21-test.yml
@@ -1,6 +1,6 @@
# $schema: ./../../../../../../metadata/json_schema/fhir/fhir-to-hl7-mapping.json
-hl7Class: ca.uhn.hl7v2.model.v27.message.OML_O21
+hl7Class: fhirengine.translation.hl7.structures.fhirinventory.message.OML_O21
extends: classpath:/metadata/hl7_mapping/OML_O21/OML_O21-base.yml
elements:
diff --git a/prime-router/src/main/resources/metadata/hl7_mapping/datatypes/extensionCP/CP.yml b/prime-router/src/main/resources/metadata/hl7_mapping/datatypes/extensionCP/CP.yml
new file mode 100644
index 00000000000..12df1c0bab0
--- /dev/null
+++ b/prime-router/src/main/resources/metadata/hl7_mapping/datatypes/extensionCP/CP.yml
@@ -0,0 +1,33 @@
+# $schema: ./../../../../../../../metadata/json_schema/fhir/fhir-to-hl7-mapping.json
+
+elements:
+
+ - name: cp-price-quantity
+ value: [ '%resource.extension.where(url = "CP.1.1").value' ]
+ hl7Spec: [ '%{cpField}-1-1' ]
+
+ - name: cp-price-denomination
+ value: [ '%resource.extension.where(url = "CP.1.2").value' ]
+ hl7Spec: [ '%{cpField}-1-2' ]
+
+ - name: cp-price-type
+ value: [ '%resource.extension.where(url = "CP.2").value' ]
+ hl7Spec: [ '%{cpField}-2' ]
+
+ - name: cp-from-value
+ value: [ '%resource.extension.where(url = "CP.3").value' ]
+ hl7Spec: [ '%{cpField}-3' ]
+
+ - name: cp-to-value
+ value: [ '%resource.extension.where(url = "CP.4").value' ]
+ hl7Spec: [ '%{cpField}-4' ]
+
+ - name: cp-range-units
+ resource: '%resource.extension.where(url = "CP.5").value'
+ schema: classpath:/metadata/hl7_mapping/datatypes/codeableConcept/CWE.yml
+ constants:
+ cweField: '%{cpField}-5'
+
+ - name: cp-range-type
+ value: [ '%resource.extension.where(url = "CP.6").value' ]
+ hl7Spec: [ '%{cpField}-6' ]
diff --git a/prime-router/src/main/resources/metadata/hl7_mapping/resources/Coverage/IN1.yml b/prime-router/src/main/resources/metadata/hl7_mapping/resources/Coverage/IN1.yml
index fe437d13fce..b412907a538 100644
--- a/prime-router/src/main/resources/metadata/hl7_mapping/resources/Coverage/IN1.yml
+++ b/prime-router/src/main/resources/metadata/hl7_mapping/resources/Coverage/IN1.yml
@@ -5,8 +5,32 @@ constants:
elements:
- - name: aui-extension
+ - name: in1-authorization-information
resource: '%resource.extension.where(url = "IN1.14")'
schema: classpath:/metadata/hl7_mapping/datatypes/extensionAUI/AUI.yml
constants:
- auiField: '%{hl7IN1Field}-14'
\ No newline at end of file
+ auiField: '%{hl7IN1Field}-14'
+
+ - name: in1-policy-deductable
+ resource: '%resource.extension(%`rsext-composite-price`).where(extension(%`rsext-hl7v2Field`).value = "IN1.37")'
+ schema: classpath:/metadata/hl7_mapping/datatypes/extensionCP/CP.yml
+ constants:
+ cpField: '%{hl7IN1Field}-37'
+
+ - name: in1-policy-limit-amount
+ resource: '%resource.extension(%`rsext-composite-price`).where(extension(%`rsext-hl7v2Field`).value = "IN1.38")'
+ schema: classpath:/metadata/hl7_mapping/datatypes/extensionCP/CP.yml
+ constants:
+ cpField: '%{hl7IN1Field}-38'
+
+ - name: in1-room-rate-semi-private
+ resource: '%resource.extension(%`rsext-composite-price`).where(extension(%`rsext-hl7v2Field`).value = "IN1.40")'
+ schema: classpath:/metadata/hl7_mapping/datatypes/extensionCP/CP.yml
+ constants:
+ cpField: '%{hl7IN1Field}-40'
+
+ - name: in1-room-rate-private
+ resource: '%resource.extension(%`rsext-composite-price`).where(extension(%`rsext-hl7v2Field`).value = "IN1.41")'
+ schema: classpath:/metadata/hl7_mapping/datatypes/extensionCP/CP.yml
+ constants:
+ cpField: '%{hl7IN1Field}-41'
diff --git a/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/onboarding/constraints.xml b/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/onboarding/constraints.xml
index 75e5166f1dc..ec78da25b9d 100644
--- a/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/onboarding/constraints.xml
+++ b/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/onboarding/constraints.xml
@@ -584,27 +584,12 @@
Text="ISO"/>
-
- MSH-5.2 (Universal ID) shall contain one of the values in the list: ['2.16.840.1.114222.4.3.15.2','2.16.840.1.114222.4.3.15.1'].
-
-
-
-
MSH-6.3 (Universal ID Type) shall contain the value 'ISO'.
-
- MSH-6.2 (Universal ID) shall contain the value '2.16.840.1.114222.4.1.217446'.
-
-
-
-
MSH-7.1 (Time) shall match the regular expression '^(\d{14}\.\d{1,4}|\d{14})((\x2D|\x2B)\d{4})$'.
@@ -612,13 +597,6 @@
Regex="^(\d{14}\.\d{1,4}|\d{14})((\x2D|\x2B)\d{4})$"/>
-
- MSH-6.1 (Namespace ID) shall contain the value 'AIMS.PLATFORM'.
-
-
-
-
MSH-1 (Field Separator) shall contain the value '|'.
@@ -679,14 +657,6 @@
-
- MSH-5.1 (Namespace ID) shall contain one of the values in the list: ['AIMS.INTEGRATION.STG','AIMS.INTEGRATION.PRD'].
-
-
-
-
diff --git a/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/production/constraints.xml b/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/production/constraints.xml
index 6be6551b1dc..f96e980e3f9 100644
--- a/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/production/constraints.xml
+++ b/prime-router/src/main/resources/metadata/hl7_validation/v251/radxmars/production/constraints.xml
@@ -684,27 +684,12 @@
Text="ISO"/>
-
- MSH-5.2 (Universal ID) shall contain one of the values in the list: ['2.16.840.1.114222.4.3.15.2','2.16.840.1.114222.4.3.15.1'].
-
-
-
-
MSH-6.3 (Universal ID Type) shall contain the value 'ISO'.
-
- MSH-6.2 (Universal ID) shall contain the value '2.16.840.1.114222.4.1.217446'.
-
-
-
-
MSH-7.1 (Time) shall match the regular expression '^(\d{14}\.\d{1,4}|\d{14})((\x2D|\x2B)\d{4})$'.
@@ -712,13 +697,6 @@
Regex="^(\d{14}\.\d{1,4}|\d{14})((\x2D|\x2B)\d{4})$"/>
-
- MSH-6.1 (Namespace ID) shall contain the value 'AIMS.PLATFORM'.
-
-
-
-
MSH-1 (Field Separator) shall contain the value '|'.
@@ -779,14 +757,6 @@
-
- MSH-5.1 (Namespace ID) shall contain one of the values in the list: ['AIMS.INTEGRATION.STG','AIMS.INTEGRATION.PRD'].
-
-
-
-
diff --git a/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt b/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt
index 9a3aaf1c8b8..96ac1340b5d 100644
--- a/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt
+++ b/prime-router/src/test/kotlin/common/UniversalPipelineTestUtils.kt
@@ -40,16 +40,16 @@ import java.time.OffsetDateTime
@Suppress("ktlint:standard:max-line-length")
const val validFHIRRecord1 =
- """{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c12347","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dba581c"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"80382-5"}],"text":"Flu A"},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
+ """{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c12347","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dba581c"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl":"MessageHeader/0993dd0b-6ce5-3caf-a177-0b81cc780c18","resource":{"resourceType":"MessageHeader","id":"0993dd0b-6ce5-3caf-a177-0b81cc780c18","extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/encoding-characters","valueString":"^~\\"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/character-set","valueString":"UNICODE UTF-8"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/msh-message-header","extension":[{"url":"MSH.7","valueString":"20230501102531-0400"}]}],"eventCoding":{"system":"http://terminology.hl7.org/CodeSystem/v2-0003","code":"R01","display":"ORU^R01^ORU_R01"},"sender":{"reference":"Organization/1710886092467181000.213628f7-9569-4400-a95d-621c3bfbf121"}}},{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"80382-5"}],"text":"Flu A"},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
const val validFHIRRecord1Identifier = "1234d1d1-95fe-462c-8ac6-46728dba581c"
@Suppress("ktlint:standard:max-line-length")
const val conditionCodedValidFHIRRecord1 =
- """{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c12347","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dba581c"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/condition-code","valueCoding":{"system":"SNOMEDCT","code":"6142004","display":"Influenza (disorder)"}}],"system":"http://loinc.org","code":"80382-5"}],"text":"Flu A"},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
+ """{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c12347","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dba581c"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl":"MessageHeader/0993dd0b-6ce5-3caf-a177-0b81cc780c18","resource":{"resourceType":"MessageHeader","id":"0993dd0b-6ce5-3caf-a177-0b81cc780c18","extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/encoding-characters","valueString":"^~\\"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/character-set","valueString":"UNICODE UTF-8"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/msh-message-header","extension":[{"url":"MSH.7","valueString":"20230501102531-0400"}]}],"eventCoding":{"system":"http://terminology.hl7.org/CodeSystem/v2-0003","code":"R01","display":"ORU^R01^ORU_R01"},"sender":{"reference":"Organization/1710886092467181000.213628f7-9569-4400-a95d-621c3bfbf121"}}},{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/condition-code","valueCoding":{"system":"SNOMEDCT","code":"6142004","display":"Influenza (disorder)"}}],"system":"http://loinc.org","code":"80382-5"}],"text":"Flu A"},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
@Suppress("ktlint:standard:max-line-length")
const val validFHIRRecord2 =
- """{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c09876","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dbau8cd"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"41458-1"}],"text":"SARS "},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
+ """{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c09876","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dbau8cd"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl":"MessageHeader/0993dd0b-6ce5-3caf-a177-0b81cc780c18","resource":{"resourceType":"MessageHeader","id":"0993dd0b-6ce5-3caf-a177-0b81cc780c18","extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/encoding-characters","valueString":"^~\\"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/character-set","valueString":"UNICODE UTF-8"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/msh-message-header","extension":[{"url":"MSH.7","valueString":"20230501102531-0400"}]}],"eventCoding":{"system":"http://terminology.hl7.org/CodeSystem/v2-0003","code":"R01","display":"ORU^R01^ORU_R01"},"sender":{"reference":"Organization/1710886092467181000.213628f7-9569-4400-a95d-621c3bfbf121"}}},{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"41458-1"}],"text":"SARS "},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
const val invalidEmptyFHIRRecord = "{}"
const val invalidMalformedFHIRRecord = """{"resourceType":"Bund}"""
@@ -441,7 +441,7 @@ object UniversalPipelineTestUtils {
fileName: String = "mr_fhir_face.fhir",
): Report {
val blobUrl = BlobAccess.uploadBlob(
- "${TaskAction.receive.literal}/$fileName",
+ "${previousAction.literal}/$fileName",
reportContents.toByteArray(),
getBlobContainerMetadata(azuriteContainer)
)
diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt
index e95fb3d5806..b534f994a12 100644
--- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRConverterIntegrationTests.kt
@@ -508,7 +508,8 @@ class FHIRConverterIntegrationTests {
orderingFacilityState = listOf("FL"),
performerState = emptyList(),
eventType = "ORU^R01^ORU_R01"
- )
+ ),
+ ReportStreamEventProperties.ENRICHMENTS to ""
)
)
}
@@ -663,7 +664,8 @@ class FHIRConverterIntegrationTests {
orderingFacilityState = listOf("FL"),
performerState = emptyList(),
eventType = "ORU^R01^ORU_R01"
- )
+ ),
+ ReportStreamEventProperties.ENRICHMENTS to ""
)
)
}
@@ -798,7 +800,7 @@ class FHIRConverterIntegrationTests {
)
assertThat(azureEventService.reportStreamEvents[ReportStreamEventName.ITEM_ACCEPTED]!!).hasSize(2)
- val event = azureEventService
+ var event = azureEventService
.reportStreamEvents[ReportStreamEventName.ITEM_ACCEPTED]!!.last() as ReportStreamItemEvent
assertThat(event.reportEventData).isEqualToIgnoringGivenProperties(
ReportEventData(
@@ -835,8 +837,9 @@ class FHIRConverterIntegrationTests {
patientState = emptyList(),
orderingFacilityState = emptyList(),
performerState = emptyList(),
- eventType = ""
- )
+ eventType = "ORU^R01^ORU_R01"
+ ),
+ ReportStreamEventProperties.ENRICHMENTS to ""
)
)
}
diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt
index 4acfecbf826..d8bae65c3eb 100644
--- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRReceiverFilterIntegrationTests.kt
@@ -920,7 +920,7 @@ class FHIRReceiverFilterIntegrationTests : Logging {
ReportStreamEventProperties.FILTER_TYPE to ReportStreamFilterType.QUALITY_FILTER,
ReportStreamEventProperties.BUNDLE_DIGEST to BundleDigestLabResult(
observationSummaries = AzureEventUtils.getObservationSummaries(bundle),
- eventType = "",
+ eventType = "ORU^R01^ORU_R01",
patientState = emptyList(),
performerState = emptyList(),
orderingFacilityState = emptyList()
diff --git a/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt b/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt
index 99eada67991..d8a9adeb1dd 100644
--- a/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/azure/FHIRTranslatorIntegrationTests.kt
@@ -640,7 +640,7 @@ class FHIRTranslatorIntegrationTests : Logging {
}
@Test
- fun `successfully translate HL7 for FHIR receiver when isSendOriginal is true`() {
+ fun `successfully translate HL7 for FHIR receiver when isSendOriginal is true from receive step`() {
// set up
val receiverSetupData = listOf(
UniversalPipelineTestUtils.ReceiverSetupData(
@@ -720,7 +720,7 @@ class FHIRTranslatorIntegrationTests : Logging {
}
@Test
- fun `successfully translate for FHIR receiver when isSendOriginal is true`() {
+ fun `successfully translate for FHIR receiver when isSendOriginal is true from receive step`() {
// set up
val receiverSetupData = listOf(
UniversalPipelineTestUtils.ReceiverSetupData(
@@ -798,4 +798,166 @@ class FHIRTranslatorIntegrationTests : Logging {
assertThat(translatedValue).isEqualTo(reportContents.toByteArray())
}
}
+
+ @Test
+ fun `successfully translate HL7 for FHIR receiver when isSendOriginal is true from convert step`() {
+ // set up
+ val receiverSetupData = listOf(
+ UniversalPipelineTestUtils.ReceiverSetupData(
+ "x",
+ jurisdictionalFilter = listOf("true"),
+ qualityFilter = listOf("true"),
+ routingFilter = listOf("true"),
+ conditionFilter = listOf("true"),
+ format = MimeFormat.FHIR
+ )
+ )
+ val receivers = UniversalPipelineTestUtils.createReceivers(receiverSetupData)
+ val org = UniversalPipelineTestUtils.createOrganizationWithReceivers(receivers)
+ val translator = createFHIRTranslator(azureEventService, org)
+ val reportContents = File(HL7_WITH_BIRTH_TIME).readText()
+ val convertReport = UniversalPipelineTestUtils.createReport(
+ reportContents,
+ TaskAction.convert,
+ Event.EventAction.CONVERT,
+ azuriteContainer,
+ TaskAction.convert,
+ fileName = "originalhl7.hl7"
+ )
+ val queueMessage = generateQueueMessage(
+ convertReport,
+ reportContents,
+ UniversalPipelineTestUtils.hl7SenderWithSendOriginal,
+ "phd.x"
+ )
+ val fhirFunctions = UniversalPipelineTestUtils.createFHIRFunctionsInstance()
+
+ // execute
+ fhirFunctions.process(queueMessage, 1, translator, ActionHistory(TaskAction.translate))
+
+ // check that send queue was updated
+ verify(exactly = 1) {
+ QueueAccess.sendMessage(QueueMessage.elrSendQueueName, any())
+ }
+
+ // check action table
+ UniversalPipelineTestUtils.checkActionTable(listOf(TaskAction.convert, TaskAction.translate))
+
+ // verify task and report_file tables were updated correctly in the Translate function (new task and new
+ // record file created)
+ ReportStreamTestDatabaseContainer.testDatabaseAccess.transact { txn ->
+ val report = fetchChildReports(convertReport, txn, 1).single()
+ assertThat(report.nextAction).isEqualTo(TaskAction.send)
+ assertThat(report.receivingOrg).isEqualTo("phd")
+ assertThat(report.receivingOrgSvc).isEqualTo("x")
+ assertThat(report.schemaName).isEqualTo("None")
+ assertThat(report.schemaTopic).isEqualTo(Topic.ELR_ELIMS)
+ assertThat(report.bodyFormat).isEqualTo("HL7")
+
+ val batchTask = DSL.using(txn).select(Task.TASK.asterisk()).from(Task.TASK)
+ .where(Task.TASK.NEXT_ACTION.eq(TaskAction.batch))
+ .fetchOneInto(Task.TASK)
+ // verify batch queue task does not exist
+ assertThat(batchTask).isNull()
+
+ val sendTask = DSL.using(txn).select(Task.TASK.asterisk()).from(Task.TASK)
+ .where(Task.TASK.NEXT_ACTION.eq(TaskAction.send))
+ .fetchOneInto(Task.TASK)
+ // verify send queue task exists
+ assertThat(sendTask).isNotNull()
+ assertThat(sendTask!!.reportId).isEqualTo(report.reportId)
+
+ // verify message format is HL7 and is for the expected receiver
+ assertThat(sendTask.receiverName).isEqualTo("phd.x")
+ assertThat(sendTask.bodyFormat).isEqualTo("HL7")
+
+ // verify message matches the original HL7 input
+ val translatedValue = BlobAccess.downloadBlobAsByteArray(
+ report.bodyUrl,
+ UniversalPipelineTestUtils.getBlobContainerMetadata(azuriteContainer)
+ )
+ assertThat(translatedValue).isEqualTo(reportContents.toByteArray())
+ }
+ }
+
+ @Test
+ fun `successfully translate for FHIR receiver when isSendOriginal is true from convert step`() {
+ // set up
+ val receiverSetupData = listOf(
+ UniversalPipelineTestUtils.ReceiverSetupData(
+ "x",
+ jurisdictionalFilter = listOf("true"),
+ qualityFilter = listOf("true"),
+ routingFilter = listOf("true"),
+ conditionFilter = listOf("true"),
+ format = MimeFormat.FHIR
+ )
+ )
+ val receivers = UniversalPipelineTestUtils.createReceivers(receiverSetupData)
+ val org = UniversalPipelineTestUtils.createOrganizationWithReceivers(receivers)
+ val translator = createFHIRTranslator(azureEventService, org)
+ val reportContents = File(MULTIPLE_TARGETS_FHIR_PATH).readText()
+ val convertReport = UniversalPipelineTestUtils.createReport(
+ reportContents,
+ TaskAction.convert,
+ Event.EventAction.CONVERT,
+ azuriteContainer,
+ TaskAction.convert
+ )
+
+ val queueMessage = generateQueueMessage(
+ convertReport,
+ reportContents,
+ UniversalPipelineTestUtils.fhirSenderWithSendOriginal,
+ "phd.x"
+ )
+ val fhirFunctions = UniversalPipelineTestUtils.createFHIRFunctionsInstance()
+
+ // execute
+ fhirFunctions.process(queueMessage, 1, translator, ActionHistory(TaskAction.translate))
+
+ // check that send queue was updated
+ verify(exactly = 1) {
+ QueueAccess.sendMessage(QueueMessage.elrSendQueueName, any())
+ }
+
+ // check action table
+ UniversalPipelineTestUtils.checkActionTable(listOf(TaskAction.convert, TaskAction.translate))
+
+ // verify task and report_file tables were updated correctly in the Translate function (new task and new
+ // record file created)
+ ReportStreamTestDatabaseContainer.testDatabaseAccess.transact { txn ->
+ val report = fetchChildReports(convertReport, txn, 1).single()
+ assertThat(report.nextAction).isEqualTo(TaskAction.send)
+ assertThat(report.receivingOrg).isEqualTo("phd")
+ assertThat(report.receivingOrgSvc).isEqualTo("x")
+ assertThat(report.schemaName).isEqualTo("None")
+ assertThat(report.schemaTopic).isEqualTo(Topic.ELR_ELIMS)
+ assertThat(report.bodyFormat).isEqualTo("FHIR")
+
+ val batchTask = DSL.using(txn).select(Task.TASK.asterisk()).from(Task.TASK)
+ .where(Task.TASK.NEXT_ACTION.eq(TaskAction.batch))
+ .fetchOneInto(Task.TASK)
+ // verify batch queue task does not exist
+ assertThat(batchTask).isNull()
+
+ val sendTask = DSL.using(txn).select(Task.TASK.asterisk()).from(Task.TASK)
+ .where(Task.TASK.NEXT_ACTION.eq(TaskAction.send))
+ .fetchOneInto(Task.TASK)
+ // verify send queue task exists
+ assertThat(sendTask).isNotNull()
+ assertThat(sendTask!!.reportId).isEqualTo(report.reportId)
+
+ // verify message format is FHIR and is for the expected receiver
+ assertThat(sendTask.receiverName).isEqualTo("phd.x")
+ assertThat(sendTask.bodyFormat).isEqualTo("FHIR")
+
+ // verify message matches the original FHIR input
+ val translatedValue = BlobAccess.downloadBlobAsByteArray(
+ report.bodyUrl,
+ UniversalPipelineTestUtils.getBlobContainerMetadata(azuriteContainer)
+ )
+ assertThat(translatedValue).isEqualTo(reportContents.toByteArray())
+ }
+ }
}
\ No newline at end of file
diff --git a/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt b/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt
index 33731c0e5af..e23ef1b6e35 100644
--- a/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/engine/FhirConverterTests.kt
@@ -344,6 +344,106 @@ class FhirConverterTests {
@Test
fun `test condition code stamping`() {
+ @Suppress("ktlint:standard:max-line-length")
+ val fhirRecord =
+ """{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c12347","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dba581c"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl" : "MessageHeader/0993dd0b-6ce5-3caf-a177-0b81cc780c18","resource" : {"resourceType" : "MessageHeader","id" : "0993dd0b-6ce5-3caf-a177-0b81cc780c18","extension" : [ {"url" : "https://reportstream.cdc.gov/fhir/StructureDefinition/encoding-characters","valueString" : "^~\\"}, {"url" : "https://reportstream.cdc.gov/fhir/StructureDefinition/character-set","valueString" : "UNICODE UTF-8"}, {"url" : "https://reportstream.cdc.gov/fhir/StructureDefinition/msh-message-header","extension" : [ {"url" : "MSH.7","valueString" : "20230501102531-0400"} ]} ],"eventCoding" : {"system" : "http://terminology.hl7.org/CodeSystem/v2-0003","code" : "R01","display" : "ORU^R01^ORU_R01"},"sender" : {"reference" : "Organization/1710886092467181000.213628f7-9569-4400-a95d-621c3bfbf121"}}},{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"80382-5"}],"text":"Flu A"},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
+
+ val conditionCodeExtensionURL = "https://reportstream.cdc.gov/fhir/StructureDefinition/condition-code"
+ mockkObject(BlobAccess)
+ mockkObject(Report)
+ metadata.lookupTableStore += mapOf(
+ "observation-mapping" to LookupTable(
+ "observation-mapping",
+ listOf(
+ listOf(
+ ObservationMappingConstants.TEST_CODE_KEY,
+ ObservationMappingConstants.CONDITION_CODE_KEY,
+ ObservationMappingConstants.CONDITION_CODE_SYSTEM_KEY,
+ ObservationMappingConstants.CONDITION_NAME_KEY
+ ),
+ listOf(
+ "80382-5",
+ "6142004",
+ "SNOMEDCT",
+ "Influenza (disorder)"
+ ),
+ listOf(
+ "260373001",
+ "Some Condition Code",
+ "Condition Code System",
+ "Condition Name"
+ )
+ )
+ )
+ )
+
+ // set up
+ val actionHistory = mockk()
+ val actionLogger = mockk()
+ val transformer = mockk()
+
+ val engine = spyk(makeFhirEngine(metadata, settings, TaskAction.process) as FHIRConverter)
+ val message = spyk(
+ FhirConvertQueueMessage(
+ UUID.randomUUID(),
+ BLOB_FHIR_URL,
+ "test",
+ BLOB_SUB_FOLDER_NAME,
+ Topic.FULL_ELR,
+ SCHEMA_NAME
+ )
+ )
+
+ val bodyFormat = MimeFormat.FHIR
+ val bodyUrl = "https://anyblob.com"
+
+ every { actionLogger.hasErrors() } returns false
+ every { actionLogger.getItemLogger(any(), any()) } returns actionLogger
+ every { actionLogger.warn(any>()) } just runs
+ every { actionLogger.setReportId(any()) } returns actionLogger
+ every { BlobAccess.downloadBlob(any(), any()) } returns (fhirRecord)
+ every { Report.getFormatFromBlobURL(message.blobURL) } returns MimeFormat.FHIR
+ every { BlobAccess.Companion.uploadBlob(any(), any()) } returns "test"
+ every { accessSpy.insertTask(any(), bodyFormat.toString(), bodyUrl, any()) }.returns(Unit)
+ every { actionHistory.trackCreatedReport(any(), any(), blobInfo = any()) }.returns(Unit)
+ every { actionHistory.trackExistingInputReport(any()) }.returns(Unit)
+ val action = Action()
+ action.actionName = TaskAction.convert
+ every { actionHistory.action } returns action
+ every { engine.getTransformerFromSchema(SCHEMA_NAME) }.returns(transformer)
+ every { transformer.process(any()) } returnsArgument (0)
+
+ // act
+ accessSpy.transact { txn ->
+ engine.run(message, actionLogger, actionHistory, txn)
+ }
+
+ val bundle = FhirContext.forR4().newJsonParser().parseResource(Bundle::class.java, fhirRecord)
+ bundle.entry.filter { it.resource is Observation }.forEach {
+ val observation = (it.resource as Observation)
+ observation.code.coding[0].addExtension(
+ conditionCodeExtensionURL,
+ Coding("SNOMEDCT", "6142004", "Influenza (disorder)")
+ )
+ observation.valueCodeableConcept.coding[0].addExtension(
+ conditionCodeExtensionURL,
+ Coding("Condition Code System", "Some Condition Code", "Condition Name")
+ )
+ }
+
+ // assert
+ verify(exactly = 1) {
+ // TODO clean up assertions
+ // engine.getContentFromFHIR(any(), any())
+ actionHistory.trackExistingInputReport(any())
+ transformer.process(any())
+ actionHistory.trackCreatedReport(any(), any(), blobInfo = any())
+ BlobAccess.Companion.uploadBlob(any(), FhirTranscoder.encode(bundle).toByteArray(), any())
+ }
+ }
+
+ @Test
+ fun `test condition code stamping without message header`() {
@Suppress("ktlint:standard:max-line-length")
val fhirRecord =
"""{"resourceType":"Bundle","id":"1667861767830636000.7db38d22-b713-49fc-abfa-2edba9c12347","meta":{"lastUpdated":"2022-11-07T22:56:07.832+00:00"},"identifier":{"value":"1234d1d1-95fe-462c-8ac6-46728dba581c"},"type":"message","timestamp":"2021-08-03T13:15:11.015+00:00","entry":[{"fullUrl":"Observation/d683b42a-bf50-45e8-9fce-6c0531994f09","resource":{"resourceType":"Observation","id":"d683b42a-bf50-45e8-9fce-6c0531994f09","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"80382-5"}],"text":"Flu A"},"subject":{"reference":"Patient/9473889b-b2b9-45ac-a8d8-191f27132912"},"performer":[{"reference":"Organization/1a0139b9-fc23-450b-9b6c-cd081e5cea9d"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/equipment-uid","valueCoding":{"code":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B_Becton, Dickinson and Company (BD)"}}],"coding":[{"display":"BD Veritor System for Rapid Detection of SARS-CoV-2 & Flu A+B*"}]},"specimen":{"reference":"Specimen/52a582e4-d389-42d0-b738-bee51cf5244d"},"device":{"reference":"Device/78dc4d98-2958-43a3-a445-76ceef8c0698"}}}]}"""
@@ -438,6 +538,8 @@ class FhirConverterTests {
actionHistory.trackExistingInputReport(any())
transformer.process(any())
actionHistory.trackCreatedReport(any(), any(), blobInfo = any())
+ }
+ verify(exactly = 0) {
BlobAccess.Companion.uploadBlob(any(), FhirTranscoder.encode(bundle).toByteArray(), any())
}
}
@@ -560,12 +662,11 @@ class FhirConverterTests {
@Test
fun `should log an error and return no bundles if the message is empty`() {
mockkObject(BlobAccess)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
val engine = spyk(makeFhirEngine(metadata, settings, TaskAction.process) as FHIRConverter)
val actionLogger = ActionLogger()
every { BlobAccess.downloadBlob(any(), any()) } returns ""
- val bundles = engine.process(
- MimeFormat.FHIR, "", "", Topic.FULL_ELR, actionLogger
- )
+ val bundles = engine.process(MimeFormat.FHIR, input, actionLogger)
assertThat(bundles).isEmpty()
assertThat(actionLogger.errors.map { it.detail.message }).contains("Provided raw data is empty.")
}
@@ -587,9 +688,8 @@ class FhirConverterTests {
every { mockMessage.topic } returns Topic.FULL_ELR
every { mockMessage.reportId } returns UUID.randomUUID()
every { BlobAccess.downloadBlob(any(), any()) } returns simpleHL7
- val bundles = engine.process(
- MimeFormat.HL7, "", "", Topic.FULL_ELR, actionLogger
- )
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val bundles = engine.process(MimeFormat.HL7, input, actionLogger)
assertThat(bundles).isEmpty()
assertThat(
actionLogger.errors.map {
@@ -604,9 +704,8 @@ class FhirConverterTests {
val engine = spyk(makeFhirEngine(metadata, settings, TaskAction.process) as FHIRConverter)
val actionLogger = ActionLogger()
every { BlobAccess.downloadBlob(any(), any()) } returns "test,1,2"
- val bundles = engine.process(
- MimeFormat.CSV, "", "", Topic.FULL_ELR, actionLogger
- )
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val bundles = engine.process(MimeFormat.CSV, input, actionLogger)
assertThat(bundles).isEmpty()
assertThat(actionLogger.errors.map { it.detail.message })
.contains("Received unsupported report format: CSV")
@@ -618,7 +717,8 @@ class FhirConverterTests {
val engine = spyk(makeFhirEngine(metadata, settings, TaskAction.process) as FHIRConverter)
val actionLogger = ActionLogger()
every { BlobAccess.downloadBlob(any(), any()) } returns "{\"id\":}"
- val processedItems = engine.process(MimeFormat.FHIR, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val processedItems = engine.process(MimeFormat.FHIR, input, actionLogger)
assertThat(processedItems).hasSize(1)
assertThat(processedItems.first().bundle).isNull()
assertThat(actionLogger.errors.map { it.detail.message }).contains(
@@ -648,9 +748,8 @@ class FhirConverterTests {
every { mockMessage.topic } returns Topic.FULL_ELR
every { mockMessage.reportId } returns UUID.randomUUID()
every { BlobAccess.downloadBlob(any(), any()) } returns "{\"id\":\"1\", \"resourceType\":\"Bundle\"}"
- val processedItems = engine.process(
- MimeFormat.FHIR, "", "", Topic.FULL_ELR, actionLogger
- )
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val processedItems = engine.process(MimeFormat.FHIR, input, actionLogger)
assertThat(processedItems).hasSize(1)
assertThat(processedItems.first().bundle).isNull()
assertThat(actionLogger.errors.map { it.detail.message }).contains(
@@ -669,7 +768,8 @@ class FhirConverterTests {
every {
BlobAccess.downloadBlob(any(), any())
} returns unparseableHL7
- val processedItems = engine.process(MimeFormat.HL7, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val processedItems = engine.process(MimeFormat.HL7, input, actionLogger)
assertThat(processedItems).hasSize(1)
assertThat(processedItems.first().bundle).isNull()
assertThat(
@@ -704,7 +804,8 @@ class FhirConverterTests {
every {
BlobAccess.downloadBlob(any(), any())
} returns simpleHL7
- val processedItems = engine.process(MimeFormat.HL7, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val processedItems = engine.process(MimeFormat.HL7, input, actionLogger)
assertThat(processedItems).hasSize(1)
assertThat(processedItems.first().bundle).isNull()
@Suppress("ktlint:standard:max-line-length")
@@ -733,7 +834,8 @@ class FhirConverterTests {
every {
BlobAccess.downloadBlob(any(), any())
} returns simpleHL7
- val processedItems = engine.process(MimeFormat.HL7, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val processedItems = engine.process(MimeFormat.HL7, input, actionLogger)
assertThat(processedItems).hasSize(1)
assertThat(processedItems.first().bundle).isNull()
assertThat(
@@ -759,14 +861,15 @@ class FhirConverterTests {
} returns """{\"id\":}
{"id":"1", "resourceType":"Bundle"}
""".trimMargin()
- val processedItems = engine.process(MimeFormat.FHIR, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val processedItems = engine.process(MimeFormat.FHIR, input, actionLogger)
assertThat(processedItems).hasSize(2)
assertThat(actionLogger.errors.map { it.detail.message }).contains(
@Suppress("ktlint:standard:max-line-length")
"Item 1 in the report was not parseable. Reason: exception while parsing FHIR: HAPI-1861: Failed to parse JSON encoded FHIR content: Unexpected character ('\\' (code 92)): was expecting double-quote to start field name\n at [line: 1, column: 2]"
)
- val bundles2 = engine.process(MimeFormat.FHIR, "", "", Topic.FULL_ELR, actionLogger, false)
+ val bundles2 = engine.process(MimeFormat.FHIR, input, actionLogger, false)
assertThat(bundles2).hasSize(0)
assertThat(actionLogger.errors.map { it.detail.message }).contains(
@Suppress("ktlint:standard:max-line-length")
@@ -786,7 +889,8 @@ class FhirConverterTests {
every {
BlobAccess.downloadBlob(any(), any())
} returns simpleHL7
- val bundles = engine.process(MimeFormat.HL7, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val bundles = engine.process(MimeFormat.HL7, input, actionLogger)
assertThat(bundles).hasSize(1)
assertThat(actionLogger.errors).isEmpty()
}
@@ -806,7 +910,8 @@ class FhirConverterTests {
every {
BlobAccess.downloadBlob(any(), any())
} returns simpleHL7 + "\n" + simpleHL7 + "\n" + simpleHL7
- val bundles = engine.process(MimeFormat.HL7, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val bundles = engine.process(MimeFormat.HL7, input, actionLogger)
assertThat(bundles).hasSize(3)
assertThat(actionLogger.errors).isEmpty()
@@ -837,7 +942,8 @@ class FhirConverterTests {
every {
BlobAccess.downloadBlob(any(), any())
} returns simpleHL7
- val bundles = engine.process(MimeFormat.HL7, "", "", Topic.FULL_ELR, actionLogger)
+ val input = FHIRConverter.FHIRConvertInput(UUID.randomUUID(), Topic.FULL_ELR, "", "", "", "")
+ val bundles = engine.process(MimeFormat.HL7, input, actionLogger)
assertThat(bundles).hasSize(1)
assertThat(actionLogger.errors).isEmpty()
}
diff --git a/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt b/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt
index a9d4a282cf6..5abc630dc42 100644
--- a/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt
+++ b/prime-router/src/test/kotlin/fhirengine/utils/FHIRBundleHelpersTests.kt
@@ -31,6 +31,7 @@ import gov.cdc.prime.router.azure.ConditionStamper.Companion.conditionCodeExtens
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
import gov.cdc.prime.router.metadata.LookupTable
@@ -45,6 +46,7 @@ import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.DiagnosticReport
import org.hl7.fhir.r4.model.Endpoint
import org.hl7.fhir.r4.model.Extension
+import org.hl7.fhir.r4.model.MessageHeader
import org.hl7.fhir.r4.model.Observation
import org.hl7.fhir.r4.model.Patient
import org.hl7.fhir.r4.model.PractitionerRole
@@ -195,6 +197,91 @@ class FHIRBundleHelpersTests {
assertThat(diagnosticReport.getResourceProperties()).isNotEmpty()
}
+ @Test
+ fun `Test if ELR when bundle is empty`() {
+ val fhirBundle = Bundle()
+ assertThat(fhirBundle.isElr()).isFalse()
+ }
+
+ @Test
+ fun `Test if ELR when bundle is not BundleType MESSAGE`() {
+ val fhirBundle = Bundle()
+ fhirBundle.type = Bundle.BundleType.DOCUMENT
+ assertThat(fhirBundle.isElr()).isFalse()
+ }
+
+ @Test
+ fun `Test if ELR when bundle has no entries`() {
+ val fhirBundle = Bundle()
+ fhirBundle.type = Bundle.BundleType.MESSAGE
+ assertThat(fhirBundle.isElr()).isFalse()
+ }
+
+ @Test
+ fun `Test if ELR when bundle has no MessageHeader`() {
+ val fhirBundle = Bundle()
+ fhirBundle.type = Bundle.BundleType.MESSAGE
+ val entry = Bundle.BundleEntryComponent()
+ fhirBundle.entry.add(0, entry)
+ assertThat(fhirBundle.isElr()).isFalse()
+ }
+
+ @Test
+ fun `Test if ELR when bundle has MessageHeader but no Coding event`() {
+ val fhirBundle = Bundle()
+ fhirBundle.type = Bundle.BundleType.MESSAGE
+ val entry = Bundle.BundleEntryComponent()
+ entry.resource = MessageHeader()
+ fhirBundle.entry.add(0, entry)
+ assertThat(fhirBundle.isElr()).isFalse()
+ }
+
+ @Test
+ fun `Test if ELR when bundle has MessageHeader but Coding event not R01`() {
+ val fhirBundle = Bundle()
+ fhirBundle.type = Bundle.BundleType.MESSAGE
+ val entry = Bundle.BundleEntryComponent()
+ val messageHeader = MessageHeader()
+ val event = Coding()
+ messageHeader.event = event
+ entry.resource = messageHeader
+ fhirBundle.entry.add(0, entry)
+ assertThat(fhirBundle.isElr()).isFalse()
+ }
+
+ @Test
+ fun `Test if ELR when bundle is happy path`() {
+ val fhirBundle = Bundle()
+ fhirBundle.type = Bundle.BundleType.MESSAGE
+ val entry = Bundle.BundleEntryComponent()
+ val messageHeader = MessageHeader()
+ var event = Coding()
+ event.code = "R01"
+ messageHeader.event = event
+ entry.resource = messageHeader
+ fhirBundle.entry.add(0, entry)
+ assertThat(fhirBundle.isElr()).isTrue()
+ event.code = "ORU_R01"
+ assertThat(fhirBundle.isElr()).isTrue()
+ event.code = "R21"
+ assertThat(fhirBundle.isElr()).isFalse()
+ }
+
+ @Test
+ fun `Test current values for rs message type`() {
+ val fhirBundle = Bundle()
+ assertThat(fhirBundle.getRSMessageType()).isEqualTo(RSMessageType.UNKNOWN)
+ fhirBundle.type = Bundle.BundleType.MESSAGE
+ val entry = Bundle.BundleEntryComponent()
+ val messageHeader = MessageHeader()
+ val event = Coding()
+ event.code = "R01"
+ messageHeader.event = event
+ entry.resource = messageHeader
+ fhirBundle.entry.add(0, entry)
+ assertThat(fhirBundle.getRSMessageType()).isEqualTo(RSMessageType.LAB_RESULT)
+ }
+
@Test
fun `Test find Diagnostic report no observation`() {
val actionLogger = ActionLogger()
diff --git a/prime-router/src/test/kotlin/tokens/ScopeTests.kt b/prime-router/src/test/kotlin/tokens/ScopeTests.kt
index 8e3e1ad53b2..c0d0caf36da 100644
--- a/prime-router/src/test/kotlin/tokens/ScopeTests.kt
+++ b/prime-router/src/test/kotlin/tokens/ScopeTests.kt
@@ -144,4 +144,23 @@ class ScopeTests {
assertThat(Scope.scopeListContainsScope("x x", "")).isFalse()
assertThat(Scope.scopeListContainsScope("x x", " ")).isFalse()
}
+
+ @Test
+ fun `test mapped scopes`() {
+ val groupMemberships = listOf(
+ "DHPrimeAdmins",
+ "DHmd-phdAdmins",
+ "DHSender_md-phd",
+ "DHmd-phd"
+ )
+
+ val mappedScopes = Scope.mapOktaGroupsToScopes(groupMemberships)
+
+ val expected = setOf(
+ "*.*.primeadmin",
+ "md-phd.*.admin",
+ "md-phd.*.user"
+ )
+ assertThat(mappedScopes).isEqualTo(expected)
+ }
}
\ 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 8bcf227d3b4..16b35f45c91 100644
--- a/prime-router/src/test/kotlin/validation/MarsOtcElrValidatorTests.kt
+++ b/prime-router/src/test/kotlin/validation/MarsOtcElrValidatorTests.kt
@@ -32,4 +32,15 @@ class MarsOtcElrValidatorTests {
val report = validator.validate(messages[0])
assertThat(report.isValid()).isTrue()
}
+
+ @Test
+ fun `test valid RADxMARS message with NIST invalid MSH-5-1, MSH-5-2, MSH-6-1, MSH-6-2 `() {
+ val sampleMessageInputStream =
+ 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])
+ assertThat(report.isValid()).isTrue()
+ }
}
\ No newline at end of file
diff --git a/prime-router/src/test/resources/validation/marsotcelr/valid_altered_msh.hl7 b/prime-router/src/test/resources/validation/marsotcelr/valid_altered_msh.hl7
new file mode 100644
index 00000000000..dbdea1b3ef7
--- /dev/null
+++ b/prime-router/src/test/resources/validation/marsotcelr/valid_altered_msh.hl7
@@ -0,0 +1,9 @@
+MSH|^~\&|AbbottInformatics^2.16.840.1.113883.3.8589.4.1.22^ISO|AbbottInformatics^00Z0000002^CLIA|CDC PRIME^2.16.840.1.114222.4.1.237821^ISO|CDC PRIME^2.16.840.1.114222.4.1.237821^ISO|20241204094313+0100||ORU^R01^ORU_R01|20241204094313+0100_Your Test Kit ID|T|2.5.1|||NE|NE|||||PHLabReport-NoAck^ELR251R1_Rcvr_Prof^2.16.840.1.113883.9.11^ISO
+SFT|Meadows Design, LLC|1.0.0|RADx MARS Hub API|1.0.0||
+PID|1||c2sense079177^^^&2.16.840.1.113883.3.8589.4.1.22&ISO^PI||Smith^Rebecca^P||20241204|F||2076-8^Native Hawaiian or Other Pacific Islander^HL70005^^^^2.5.1|^^^^60013^^^^||^^PH^^^111^1111111|||||||||H^Hispanic or Latino^HL70189^^^^2.5.1
+ORC|RE||Your Test Kit ID^^2.16.840.1.113883.3.8589.4.1.22^ISO|||||||||^^SA.Proctor|||||||||SA.Proctor|^^^^60013^^^^|^^PH^^^111^1111111|
+OBR|1||Your Test Kit ID^^2.16.840.1.113883.3.8589.4.1.22^ISO|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN^^^^2.71|||20241204094313+0100|||||||||^^SA.Proctor||||||20241204094313+0100|||F
+OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay^LN^^^^2.71||260415000^Not Detected^SCT^^^^20210301|||N^Normal^HL70078^^^^2.5.1|||F||||00Z0000016||10811877011337_DIT^^99ELR^^^^Vunknown||20241204094313+0100||||SA.Proctor^^^^^&2.16.840.1.113883.3.8589.4.1.152&ISO^XX^^^00Z0000016|13 Fake AtHome Test Street^^Fake City|
+NTE|1|L|10811877011337_DIT
+OBX|2|NM|35659-2^Age at specimen collection^LN^^^^2.71||28|a^year^UCUM^^^^2.1|||||F||||00Z0000016||||||||SA.Proctor^^^^^&2.16.840.1.113883.3.8589.4.1.152&ISO^XX^^^00Z0000016|13 Fake AtHome Test Street^^Fake City|||||QST
+SPM|1|^Your Test Kit ID&&2.16.840.1.113883.3.8589.4.1.22&ISO||697989009^Anterior nares swab (specimen)^SCT^^^^20210301|||||||||||||20241204094313+0100|20241204094313+0100
\ No newline at end of file
diff --git a/prime-router/src/testIntegration/kotlin/datatests/mappinginventory/catchall/cp/CPTests.kt b/prime-router/src/testIntegration/kotlin/datatests/mappinginventory/catchall/cp/CPTests.kt
new file mode 100644
index 00000000000..6fa650b9945
--- /dev/null
+++ b/prime-router/src/testIntegration/kotlin/datatests/mappinginventory/catchall/cp/CPTests.kt
@@ -0,0 +1,16 @@
+package gov.cdc.prime.router.datatests.mappinginventory.cp
+
+import gov.cdc.prime.router.datatests.mappinginventory.verifyHL7ToFHIRToHL7Mapping
+import org.junit.jupiter.api.Test
+
+class CPTests {
+ @Test
+ fun `test CP mapped to CPExtension`() {
+ assert(
+ verifyHL7ToFHIRToHL7Mapping(
+ "catchall/cp/cp-to-extension",
+ outputSchema = "classpath:/metadata/hl7_mapping/OML_O21/OML_O21-test.yml"
+ ).passed
+ )
+ }
+}
\ No newline at end of file
diff --git a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/cp/cp-to-extension.fhir b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/cp/cp-to-extension.fhir
new file mode 100644
index 00000000000..86c2f6a5a65
--- /dev/null
+++ b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/cp/cp-to-extension.fhir
@@ -0,0 +1,195 @@
+{
+ "resourceType": "Bundle",
+ "id": "1733797768446514000.f1548dec-5abc-4302-9b8d-59f755ec9b2f",
+ "meta": {
+ "lastUpdated": "2024-12-09T18:29:28.450-08:00"
+ },
+ "identifier": {
+ "system": "https://reportstream.cdc.gov/prime-router",
+ "value": "MSG00001"
+ },
+ "type": "message",
+ "entry": [
+ {
+ "fullUrl": "MessageHeader/1733797768476500000.9c68bbf7-cd15-490d-a357-ac0d515575f8",
+ "resource": {
+ "resourceType": "MessageHeader",
+ "id": "1733797768476500000.9c68bbf7-cd15-490d-a357-ac0d515575f8",
+ "meta": {
+ "tag": [
+ {
+ "system": "http://terminology.hl7.org/CodeSystem/v2-0103",
+ "code": "P"
+ }
+ ]
+ },
+ "eventCoding": {
+ "system": "http://terminology.hl7.org/CodeSystem/v2-0003",
+ "code": "O21",
+ "display": "OML^O21^OML_O21"
+ }
+ }
+ },
+ {
+ "fullUrl": "Provenance/1733797768760500000.23e4e2bd-4532-468d-89ec-e012d5702477",
+ "resource": {
+ "resourceType": "Provenance",
+ "id": "1733797768760500000.23e4e2bd-4532-468d-89ec-e012d5702477",
+ "activity": {
+ "coding": [
+ {
+ "display": "OML^O21^OML_O21"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "fullUrl": "Provenance/1733797768767441000.19ac0704-100d-4d66-8cb9-3094fba92b95",
+ "resource": {
+ "resourceType": "Provenance",
+ "id": "1733797768767441000.19ac0704-100d-4d66-8cb9-3094fba92b95",
+ "recorded": "2024-12-09T18:29:28Z",
+ "policy": [
+ "http://hl7.org/fhir/uv/v2mappings/message-oru-r01-to-bundle"
+ ],
+ "activity": {
+ "coding": [
+ {
+ "code": "v2-FHIR transformation"
+ }
+ ]
+ },
+ "agent": [
+ {
+ "type": {
+ "coding": [
+ {
+ "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type",
+ "code": "assembler"
+ }
+ ]
+ },
+ "who": {
+ "reference": "Organization/1733797768766853000.2f23f653-749b-4b43-b159-d1d955375f3a"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "fullUrl": "Organization/1733797768766853000.2f23f653-749b-4b43-b159-d1d955375f3a",
+ "resource": {
+ "resourceType": "Organization",
+ "id": "1733797768766853000.2f23f653-749b-4b43-b159-d1d955375f3a",
+ "identifier": [
+ {
+ "value": "CDC PRIME - Atlanta"
+ },
+ {
+ "type": {
+ "coding": [
+ {
+ "system": "http://terminology.hl7.org/CodeSystem/v2-0301"
+ }
+ ]
+ },
+ "system": "urn:ietf:rfc:3986",
+ "value": "2.16.840.1.114222.4.1.237821"
+ }
+ ]
+ }
+ },
+ {
+ "fullUrl": "Patient/1733797768779870000.bb1ddb84-5fa1-484f-96a4-4616988212fc",
+ "resource": {
+ "resourceType": "Patient",
+ "id": "1733797768779870000.bb1ddb84-5fa1-484f-96a4-4616988212fc"
+ }
+ },
+ {
+ "fullUrl": "Provenance/1733797768780413000.e0ff5861-fd54-4149-8ee0-415a1dcc9123",
+ "resource": {
+ "resourceType": "Provenance",
+ "id": "1733797768780413000.e0ff5861-fd54-4149-8ee0-415a1dcc9123",
+ "target": [
+ {
+ "reference": "Patient/1733797768779870000.bb1ddb84-5fa1-484f-96a4-4616988212fc"
+ }
+ ],
+ "recorded": "2024-12-09T18:29:28Z",
+ "activity": {
+ "coding": [
+ {
+ "system": "https://terminology.hl7.org/CodeSystem/v3-DataOperation",
+ "code": "UPDATE"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "fullUrl": "Coverage/1733797768783117000.2931a2e0-097c-481f-9adf-46e114f827b2",
+ "resource": {
+ "resourceType": "Coverage",
+ "id": "1733797768783117000.2931a2e0-097c-481f-9adf-46e114f827b2",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.40"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 33.33
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "MXN"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P3"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 3.003
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -3
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "OR",
+ "display": "Oven Range"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "P"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/cp/cp-to-extension.hl7 b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/cp/cp-to-extension.hl7
new file mode 100644
index 00000000000..be3735df492
--- /dev/null
+++ b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/cp/cp-to-extension.hl7
@@ -0,0 +1,3 @@
+MSH|^~\&|||||||OML^O21^OML_O21|MSG00001|P|2.5.1
+PID|1
+IN1||||||||||||||||||||||||||||||||||||||||33.33&MXN^P3^3.003^-3^OR&Oven Range&HL79999^P
\ No newline at end of file
diff --git a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.fhir b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.fhir
index bac350c4bb3..c341f40f512 100644
--- a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.fhir
+++ b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.fhir
@@ -1,8 +1,8 @@
{
"resourceType": "Bundle",
- "id": "1732324230716018000.38edba19-1d39-4040-85af-9f5f6248813c",
+ "id": "1733797806213402000.63933238-a67a-4568-8544-178612c57351",
"meta": {
- "lastUpdated": "2024-11-22T17:10:30.720-08:00"
+ "lastUpdated": "2024-12-09T18:30:06.217-08:00"
},
"identifier": {
"system": "https://reportstream.cdc.gov/prime-router",
@@ -11,10 +11,10 @@
"type": "message",
"entry": [
{
- "fullUrl": "MessageHeader/1732324230752828000.fc7cea9d-d7ca-4378-91d4-ea97c86b4787",
+ "fullUrl": "MessageHeader/1733797806248346000.554ce0a6-e3fb-4b4e-b8e2-c111f8944cd5",
"resource": {
"resourceType": "MessageHeader",
- "id": "1732324230752828000.fc7cea9d-d7ca-4378-91d4-ea97c86b4787",
+ "id": "1733797806248346000.554ce0a6-e3fb-4b4e-b8e2-c111f8944cd5",
"meta": {
"tag": [
{
@@ -31,17 +31,108 @@
}
},
{
- "fullUrl": "Patient/1732324231018814000.533db9a5-5375-4746-be23-f7140d4a3280",
+ "fullUrl": "Provenance/1733797806498618000.239e218a-0960-4528-939e-5ee68bfbba1d",
+ "resource": {
+ "resourceType": "Provenance",
+ "id": "1733797806498618000.239e218a-0960-4528-939e-5ee68bfbba1d",
+ "activity": {
+ "coding": [
+ {
+ "display": "OML^O21^OML_O21"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "fullUrl": "Provenance/1733797806506657000.35b16b0c-d80a-4017-8cad-868a79863b64",
+ "resource": {
+ "resourceType": "Provenance",
+ "id": "1733797806506657000.35b16b0c-d80a-4017-8cad-868a79863b64",
+ "recorded": "2024-12-09T18:30:06Z",
+ "policy": [
+ "http://hl7.org/fhir/uv/v2mappings/message-oru-r01-to-bundle"
+ ],
+ "activity": {
+ "coding": [
+ {
+ "code": "v2-FHIR transformation"
+ }
+ ]
+ },
+ "agent": [
+ {
+ "type": {
+ "coding": [
+ {
+ "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type",
+ "code": "assembler"
+ }
+ ]
+ },
+ "who": {
+ "reference": "Organization/1733797806506092000.a6f8b4f5-e971-4ee2-8891-8b67af7df93d"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "fullUrl": "Organization/1733797806506092000.a6f8b4f5-e971-4ee2-8891-8b67af7df93d",
+ "resource": {
+ "resourceType": "Organization",
+ "id": "1733797806506092000.a6f8b4f5-e971-4ee2-8891-8b67af7df93d",
+ "identifier": [
+ {
+ "value": "CDC PRIME - Atlanta"
+ },
+ {
+ "type": {
+ "coding": [
+ {
+ "system": "http://terminology.hl7.org/CodeSystem/v2-0301"
+ }
+ ]
+ },
+ "system": "urn:ietf:rfc:3986",
+ "value": "2.16.840.1.114222.4.1.237821"
+ }
+ ]
+ }
+ },
+ {
+ "fullUrl": "Patient/1733797806517233000.6ddc53a3-0587-4db5-8c73-5b6634eb1369",
"resource": {
"resourceType": "Patient",
- "id": "1732324231018814000.533db9a5-5375-4746-be23-f7140d4a3280"
+ "id": "1733797806517233000.6ddc53a3-0587-4db5-8c73-5b6634eb1369"
}
},
{
- "fullUrl": "Coverage/1732324231019741000.a36368bf-6c63-4970-8948-034a92248964",
+ "fullUrl": "Provenance/1733797806517793000.28f02504-824d-4c22-a6a1-e234b3affd5f",
+ "resource": {
+ "resourceType": "Provenance",
+ "id": "1733797806517793000.28f02504-824d-4c22-a6a1-e234b3affd5f",
+ "target": [
+ {
+ "reference": "Patient/1733797806517233000.6ddc53a3-0587-4db5-8c73-5b6634eb1369"
+ }
+ ],
+ "recorded": "2024-12-09T18:30:06Z",
+ "activity": {
+ "coding": [
+ {
+ "system": "https://terminology.hl7.org/CodeSystem/v3-DataOperation",
+ "code": "UPDATE"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "fullUrl": "Coverage/1733797806523309000.c5403041-1e4b-4f81-bc46-f16237041fec",
"resource": {
"resourceType": "Coverage",
- "id": "1732324231019741000.a36368bf-6c63-4970-8948-034a92248964",
+ "id": "1733797806523309000.c5403041-1e4b-4f81-bc46-f16237041fec",
"extension": [
{
"url": "IN1.14",
@@ -59,6 +150,222 @@
"valueString": "DR TEETH AND THE ELECTRIC MAYHEM"
}
]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.37"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 11.11
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "USD"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P1"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 1.001
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -1
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "R",
+ "display": "Range Units"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "F"
+ }
+ ]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.38"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 22.22
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "RUB"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P2"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 2.002
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -2
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "RR",
+ "display": "Russian Rubble"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "F"
+ }
+ ]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.40"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 33.33
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "MXN"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P3"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 3.003
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -3
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "OR",
+ "display": "Oven Range"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "P"
+ }
+ ]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.41"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 44.44
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "CAD"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P4"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 4.004
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -4
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "HHR",
+ "display": "Home Home Range"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "P"
+ }
+ ]
}
]
}
diff --git a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.hl7 b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.hl7
index 6d424a80fa2..0279bf1db76 100644
--- a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.hl7
+++ b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/in1/IN1-to-Coverage.hl7
@@ -1,3 +1,3 @@
MSH|^~\&|||||||OML^O21^OML_O21|MSG00001|P|2.5.1
PID|1
-IN1||||||||||||||1701^19700101^DR TEETH AND THE ELECTRIC MAYHEM
\ No newline at end of file
+IN1||||||||||||||1701^19700101^DR TEETH AND THE ELECTRIC MAYHEM|||||||||||||||||||||||11.11&USD^P1^1.001^-1^R&Range Units&HL79999^F|22.22&RUB^P2^2.002^-2^RR&Russian Rubble&HL79999^F||33.33&MXN^P3^3.003^-3^OR&Oven Range&HL79999^P|44.44&CAD^P4^4.004^-4^HHR&Home Home Range&HL79999^P
\ No newline at end of file
diff --git a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.fhir b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.fhir
index 214a988f494..d24dfe4dad2 100644
--- a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.fhir
+++ b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.fhir
@@ -13262,6 +13262,222 @@
"valueString": "DR TEETH AND THE ELECTRIC MAYHEM"
}
]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.37"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 11.11
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "USD"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P1"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 1.001
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -1
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "R",
+ "display": "Range Units"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "F"
+ }
+ ]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.38"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 22.22
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "RUB"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P2"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 2.002
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -2
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "RR",
+ "display": "Russian Rubble"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "F"
+ }
+ ]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.40"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 33.33
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "MXN"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P3"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 3.003
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -3
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "OR",
+ "display": "Oven Range"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "P"
+ }
+ ]
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/composite-price",
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field",
+ "valueString": "IN1.41"
+ },
+ {
+ "url": "CP.1.1",
+ "valueDecimal": 44.44
+ },
+ {
+ "url": "CP.1.2",
+ "valueString": "CAD"
+ },
+ {
+ "url": "CP.2",
+ "valueString": "P4"
+ },
+ {
+ "url": "CP.3",
+ "valueDecimal": 4.004
+ },
+ {
+ "url": "CP.4",
+ "valueDecimal": -4
+ },
+ {
+ "url": "CP.5",
+ "valueCodeableConcept": {
+ "coding": [
+ {
+ "extension": [
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding",
+ "valueString": "coding"
+ },
+ {
+ "url": "https://reportstream.cdc.gov/fhir/StructureDefinition/cwe-coding-system",
+ "valueString": "HL79999"
+ }
+ ],
+ "code": "HHR",
+ "display": "Home Home Range"
+ }
+ ]
+ }
+ },
+ {
+ "url": "CP.6",
+ "valueString": "P"
+ }
+ ]
}
]
}
diff --git a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.hl7 b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.hl7
index 23863a51bb8..baa36fced05 100644
--- a/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.hl7
+++ b/prime-router/src/testIntegration/resources/datatests/mappinginventory/catchall/omlo21/oml_o21-full.hl7
@@ -8,7 +8,7 @@ NK1|1|SURYAN&Prefix&Own&SpousePrefix&Spouse^GENARO^GR^JR^Sir^Md^L^I^CON&Context
NK1|2|SUPERMAN&Prefix&Own&SpousePrefix&Spouse^GENARO^GR^JR^Sir^Md^L^I^CON&Context the namee&HL70448^2000&2030^G^20000501102531^2030501102531^Dr|OTH^Other^HL70063^OT^OTHER RELATIONSHIP^L|4861 20TH AVE^^THUNDER MOUNTAIN^IG^99999^USA^H|^PRN^PH^example2@exmaple.com^1^720^5553954^2^any^^^+1 720 555 3955|^WPN^PH^^1^555^4672293^^^^^+1 555 467 2294|F^Federal Agency^HL70131|20220501102531-0400|20230501102531-0400|||052479^^^^^^20160822|HospitalsRUs^^112233^^^^^^^HRU||N^Not Applicable^HL70001|19860505||||E^English^HL70296|||||||||||^WPN^PH^^1^720^5553954^^^^^+1 720 555 3955|4861 20TH AVE^#B^AURORA^IG^99999^USA^H^World^King^12^8^2017&2025^2020^2021|052479^^^^^^^20210428|||||||^VHN^SAT^^1^314^5553131^^^^^+1 314 555 3132|^AWN^FX^^1^281^5558181^^^^^+1 281 555 8182
PV1|1|O|A&Point OF Care&C^1&Room&3^B&Bed&A^Hospital Assigned&2.4.4.4&ISO^^R^&Building^&Floor^Totally A Real Location^Comprehensive&&UID4This^AA&AssigningAUTH&ISO|R^Routine^HL70007|232323|^^^Hospital Prio&2.4.4.4&ISO^active^location type^^^Description^Entity ID&NAME&UNI&ISO^ASSIGNEE&222.1111.22222&UUID|1^BEETHOVEN^LUDWIG^B^2ND^DR^MD^^Namespace&AssigningSystem&UUID^B^^^DL^^^^^^^^MD~1^BEETHOVEN2^LUDWIG^B^2ND^DR^MD^^Namespace&AssigningSystem&UUID^B^^^DL^^^^^^^^MD|1^MOZART~1^MOZARTJR|1^CHOPIN~1^CHOPINSR|URO^Urology Service^HL70069|^^^^^^^^Its Temporary|P^Passed^HL70087|R^Re-admission^HL70092|RL^Real Life^HL70023||VIP^Very Interesting Person^HL70099|1^BACH~1^BACHtheSecond|H^Human Patient^HL70018|22|||||||||||20020101|C^Collectors^HL70021|1|0|Y^Yes^HL70111|20080101|H^Happy^HL70112|^202305061200|F^Fed^HL70114|H^A Hospital Of Course^HL70115||A^Active^HL70117|^^^^^^^^Pending Location|^^^^^^^^Prior Location|20240801102531-0400|20240801102531-0400|100|199|142|130|alternate visit|A^Account Level^HL70326||Service Description|episode identifier
PV2|^^^Hospital PriorPending&2.4.4.4&ISO^active^location type^^^Description^Entity ID&NAME&UNI&ISO^ASSIGNEE&222.1111.22222&UUID||1^AD||||413^V~423^X|20230601102531-0400|20230701102531-0400|5|12|Description|1^BEETHOVEN&VAN&Referral Source Code1&VAL&ROGER^LUDWIG^B^2ND^DR^MD^SRC^&AssigningSystem&ISO^B^A^NPI^DL^^A^NameContext^^G^20220501102531-0400^20230501102531-0400^MD^AssignJ^AssignA~1^BEETHOVEN&VAN&Referral Source Code2&VAL&ROGER^LUDWIG^B^2ND^DR^MD^SRC^&AssigningSystem&ISO^B^A^NPI^DL^^A^NameContext^^G^20220501102531-0400^20230501102531-0400^MD^AssignJ^AssignA||EMP_ILL||||||100^PublicCode|SEC|Org1^1234-5&TestText&LN&1234-5&TestAltText&LN&1&2&OriginalText^123^Check Digit^C1^Assigning Authority&2.1.4.1&ISO^MD^Hospital A&2.16.840.1.113883.9.11&ISO~Org2^1234-5&TestText&LN&1234-5&TestAltText&LN&1&2&OriginalText^123^Check Digit^C1^Assigning Authority&2.1.4.1&ISO^MD^Hospital A&2.16.840.1.113883.9.11&ISO||3^Elective^HL70217|20230501102531-0400|||20220501102531-0400|||||||||444^MODE||123^CARELEVEL1
-IN1||||||||||||||1701^19700101^DR TEETH AND THE ELECTRIC MAYHEM
+IN1||||||||||||||1701^19700101^DR TEETH AND THE ELECTRIC MAYHEM|||||||||||||||||||||||11.11&USD^P1^1.001^-1^R&Range Units&HL79999^F|22.22&RUB^P2^2.002^-2^RR&Russian Rubble&HL79999^F||33.33&MXN^P3^3.003^-3^OR&Oven Range&HL79999^P|44.44&CAD^P4^4.004^-4^HHR&Home Home Range&HL79999^P
ORC|RE|Specimen123^SPHL-000048^2.16.840.1.114222.4.1.10765^ISO|Specimen12311^SPHL-000048^2.16.840.1.114222.4.1.10765^ISO|Specimen12322^SPHL-000048^2.16.840.1.114222.4.1.10765^ISO|CM|E||Specimen12333&SPHL-000048&2.16.840.1.114222.4.1.10765&ISO^Specimen12333454&SPHL-000048&2.16.840.1.114222.4.1.10765&ISO|20230725|71^^ORC.10Name~71^^ORC.10Name2|82^^ORC.11Name~82^^ORC.11Name2|93^^ORC.12Name~93^^ORC.12Name2|12.12.12&enter location id&L|123^^^+123~1234^^^+1234|202101021000||EO^entering org text^HL79999|ED^entering device text^HL79999|60^^ORC.19Name~60^^ORC.19Name2||CDPH, Viral and Rickettsial Disease Laboratory^^^^^STARLIMS.CDC.Stag&2.16.840.1.114222.4.3.3.2.1.2&ISO^XX^STARLINKS.CDC.Stag&2.16.840.1.114222.8.7.6.5.4.1&ISO^A^SPHL-000048~CDPH, Viral and Rickettsial Disease Laboratory part two^^^^^STARLIMS.CDC.Stag&2.16.840.1.114222.4.3.3.2.1.2&ISO|111 Street St^^Streetsville^ST~112 Street St^^Streetsville^ST|456^^^+456~4567^^^+4567|111 Road Rd^^Roadsville^RD~112 Road Rd^^Roadsville^RD|OS^order status^HL79999||20260404|EMP^Employee^HL70177|I^Inpatient Order^HL70482|EL^Electronic^HL70483|UMM^Universal Modifier^HL70000|20250403|33~332
OBR|1|Placer Identifier^Placer Identifier Namespace^Placer Universal ID^ISO|Filler Identifier^Filler Identifier Namespace^Filler Universal ID^ISO|123^Universal service identifier||202202021022||20240220|1771|1^Collector&VAN&Identifier&VAL&ROGER^LUDWIG^B^2ND^DR^MD^SRC^&AssigningSystem&ISO^B^A^NPI^DL^^A^NameContext~1^Collectorx2&VAN&Identifier&VAL&ROGER^LUDWIG^B^2ND^DR^MD^SRC^&AssigningSystem&ISO^B^A^NPI^DL^^A^NameContext|G|512^Danger code|R^relevent info^ISO|202102021000|ID^BOUIN&Bouin's solution&HL70371^Collection Method^LN&Left Naris&HL70163^^CMMC&Collection Method Modifer Code&HL7|1^Ordering&VAN&Provider&VAL&JollyROGER~2^Ordering&VAN&Provider&VAL&JollyROGER|^WPN^BP^^1^260^7595016^^^^^+1 260 759 5016~^WPN^Internet^order.callback@email.com~^WPN^BP^^1^260^7595016^^^^^+1 260 759 5016~^WPN^Internet^order.callback2@email.com|placer1|placer2|filler1|filler2||100&$^16&code|OTH|F|444&ParentId^888^ParentOBSdescriptor||1^result&VAN&copiesto&VAL&NotSoJollyROGER~2^results&VAN&copiesto&VAL&NotSoJollyROGER|adb4a5cc-50ec-4f1e-95d7-0c1f77cacee1&CSV&11D1111111&CLIA^f34b0f57-1601-4480-ae8a-d4006e50f38d&Other CSV&22D2222222&CLIA2||3216^ReasonForStudy~3216^ReasonForStudy2||123&Assistant&Results Interpreter&S&ESQ&DR&MD&&Assigning Authority&2.1.4.1&ISO^20230401102531-0400^20230501102531-0400^Point of Care^Room 101^Bed A^Hospital A&2.16.840.1.113883.9.11&ISO^active^^Building 123^Floor A~123&Assistant&Results Interpreter&S&ESQ&DR&MD&&Assigning Authority&2.1.4.1&ISO^20230401102531-0400^20230501102531-0400^Point of Care^Room 101^Bed A^Hospital A&2.16.840.1.113883.9.11&ISO^active^^Building 123^Floor A|||20230806123359-0500|||4438^Collectors Comment~4438^Collectors Comment2|||||5019^Procedure Code|887766^Procedure Code Modifier~887766^Procedure Code Modifier2|7461^Placer Supplemental~7461^Placer Supplemental2|8811^Fillter Supplemental~8811^Fillter Supplemental2|71435^Medically Necessary Duplicate Procedure|N|443331^Parent Universal Service Identifier|||Alt^Placer Order~Alt^Placer Order2|adb4a5cc-50ec-4f1e-95d7-0c1f77cacee1&CSV&11D1111111&CLIA^f34b0f57-1601-4480-ae8a-d4006e50f38d&Other CSV&22D2222222&CLIA2
NTE|1|L|Note about the observation request~second~third~fourth|||20230207|20230208|20230209|CC^Coded Request note