From 429d8ea04624778ed0adc83fbb817d0eb82f5019 Mon Sep 17 00:00:00 2001 From: 360dgries <139473729+360dgries@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:55:09 -0400 Subject: [PATCH] FI-2763 Export in workflow (#16) * Add export routes * Parse export response as json * Update endpoints and postman collection * Update wait test prompt * Change URL in request field of -poll-status payload * fix postman documentation error and slight renaming of requests * clarify postman requests * updated requester patient id in postman collection --------- Co-authored-by: Karl Naden --- .env.development | 1 + .env.production | 3 +- PDEX.postman_collection.json | 95 ++++++++++++++++++- lib/davinci_pdex_test_kit/mock_server.rb | 55 ++++++++--- .../client_validation_test.rb | 63 +++--------- .../initial_scratch_storing.rb | 16 ++-- .../initial_wait_test.rb | 2 + .../pdex_payer_client_suite.rb | 20 +++- lib/davinci_pdex_test_kit/tags.rb | 4 +- lib/davinci_pdex_test_kit/urls.rb | 18 +++- 10 files changed, 190 insertions(+), 87 deletions(-) diff --git a/.env.development b/.env.development index 6c4b1a2..2a712cc 100644 --- a/.env.development +++ b/.env.development @@ -2,3 +2,4 @@ INFERNO_HOST=http://localhost:4567 FHIR_RESOURCE_VALIDATOR_URL=http://localhost/hl7validatorapi REDIS_URL=redis://localhost:6379/0 FHIR_REFERENCE_SERVER=http://localhost:8080/reference-server/r4 +HOST_HEADER=localhost:8080 \ No newline at end of file diff --git a/.env.production b/.env.production index bc42392..b121d38 100644 --- a/.env.production +++ b/.env.production @@ -1,4 +1,5 @@ INFERNO_HOST=http://localhost REDIS_URL=redis://redis:6379/0 -FHIR_RESOURCE_VALIDATOR_URL=http://hl7_validator_service:3500 +VALIDATOR_URL=http://validator_service:4567 FHIR_REFERENCE_SERVER=http://host.docker.internal:8080/reference-server/r4 +HOST_HEADER=host.docker.internal:8080 \ No newline at end of file diff --git a/PDEX.postman_collection.json b/PDEX.postman_collection.json index adedbd3..0abe02d 100644 --- a/PDEX.postman_collection.json +++ b/PDEX.postman_collection.json @@ -1,8 +1,8 @@ { "info": { - "_postman_id": "77fbbca5-e1d0-4969-8358-8e69d5c08e0c", + "_postman_id": "315195b9-bba1-42dc-99b1-8c98c2bf6942", "name": "PDEX", - "description": "The variables tab in this collection controls port for inferno and token used to establish a session.\n\n- url_prefix: points to a running instance of inferno. Typical values will be\n \n - Inferno production: https://inferno.healthit.gov/suites\n \n - Inferno QA: https://inferno-qa.healthit.gov/suites\n \n - Local docker: http://localhost\n \n - Local development: [http://localhost:4657](http://localhost:4657)\n \n- token: placed in the Authentication header as a bearer token and used by Inferno to recognize requests for a test session. This value will need to be entered in the Access Token input of the PDex Payer Client test suite.", + "description": "The variables tab in this collection controls port for inferno and token used to establish a session.\n\n- url_prefix: points to a running instance of inferno. Typical values will be\n \n - Inferno production: [https://inferno.healthit.gov/suites](https://inferno.healthit.gov/suites)\n \n - Inferno QA: [https://inferno-qa.healthit.gov/suites](https://inferno-qa.healthit.gov/suites)\n \n - Local docker: [http://localhost](http://localhost)\n \n - Local development: http://localhost:4567\n \n- token: placed in the Authentication header as a bearer token and used by Inferno to recognize requests for a test session. This value will need to be entered in the Access Token input of the PDex Payer Client test suite.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "32597978" }, @@ -23,7 +23,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"resourceType\" : \"Parameters\",\r\n \"id\" : \"member-match-in\",\r\n \"parameter\" : [\r\n {\r\n \"name\" : \"MemberPatient\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Patient\",\r\n \"id\" : \"999\",\r\n \"identifier\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\" : \"MB\"\r\n }\r\n ]\r\n },\r\n \"system\" : \"http://github.com/inferno-framework/target-payer/identifiers/member\",\r\n \"value\" : \"55678\",\r\n \"assigner\" : {\r\n \"display\" : \"Old Payer\"\r\n }\r\n }\r\n ],\r\n \"name\" : [\r\n {\r\n \"use\" : \"official\",\r\n \"family\" : \"Person\",\r\n \"given\" : [\r\n \"Patricia\",\r\n \"Ann\"\r\n ]\r\n }\r\n ],\r\n \"gender\" : \"female\",\r\n \"birthDate\" : \"1974-12-25\"\r\n }\r\n },\r\n {\r\n \"name\" : \"CoverageToMatch\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Coverage\",\r\n \"id\" : \"9876B1\",\r\n \"identifier\" : [\r\n {\r\n \"system\" : \"http://github.com/inferno-framework/old-payer\",\r\n \"value\" : \"DH10001235\"\r\n }\r\n ],\r\n \"status\" : \"draft\",\r\n \"beneficiary\" : {\r\n \"reference\" : \"Patient/999\"\r\n },\r\n \"period\" : {\r\n \"start\" : \"2011-05-23\",\r\n \"end\" : \"2012-05-23\"\r\n },\r\n \"payor\" : [\r\n {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"9876543210\"\r\n },\r\n \"display\" : \"Old Health Plan\"\r\n }\r\n ],\r\n \"class\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"group\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"CB135\"\r\n },\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"plan\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"B37FC\"\r\n },\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"subplan\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"P7\"\r\n },\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"class\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"SILVER\"\r\n }\r\n ]\r\n }\r\n },\r\n {\r\n \"name\" : \"CoverageToLink\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Coverage\",\r\n \"id\" : \"AA87654\",\r\n \"identifier\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\" : \"MB\"\r\n }\r\n ]\r\n },\r\n \"system\" : \"http://github.com/inferno-framework/new-payer/identifiers/coverage\",\r\n \"value\" : \"234567\"\r\n }\r\n ],\r\n \"status\" : \"active\",\r\n \"beneficiary\" : {\r\n \"reference\" : \"https://hl7.org/fhir/us/core/STU6.1/Patient/example\"\r\n },\r\n \"relationship\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/data-absent-reason\",\r\n \"code\" : \"unknown\"\r\n }\r\n ]\r\n },\r\n \"payor\" : [\r\n {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"0123456789\"\r\n },\r\n \"display\" : \"New Health Plan\"\r\n }\r\n ]\r\n }\r\n },\r\n {\r\n \"name\" : \"Consent\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Consent\",\r\n \"status\" : \"active\",\r\n \"scope\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentscope\",\r\n \"code\" : \"patient-privacy\"\r\n }\r\n ]\r\n },\r\n \"category\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\r\n \"code\" : \"IDSCL\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"patient\" : {\r\n \"reference\" : \"Patient/999\"\r\n },\r\n \"performer\" : [\r\n {\r\n \"reference\" : \"Patient/999\"\r\n }\r\n ],\r\n \"sourceReference\" : {\r\n \"reference\" : \"http://github.com/inferno-framework/DocumentReference/someconsent.pdf\"\r\n },\r\n \"policy\" : [\r\n {\r\n \"uri\" : \"http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#regular\"\r\n }\r\n ],\r\n \"provision\" : {\r\n \"type\" : \"permit\",\r\n \"period\" : {\r\n \"start\" : \"2022-01-01\",\r\n \"end\" : \"2022-01-31\"\r\n },\r\n \"actor\" : [\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/provenance-participant-type\",\r\n \"code\" : \"performer\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"9876543210\"\r\n },\r\n \"display\" : \"Old Health Plan\"\r\n }\r\n },\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ParticipationType\",\r\n \"code\" : \"IRCP\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"0000000001\"\r\n },\r\n \"display\" : \"Provider organization\"\r\n }\r\n }\r\n ],\r\n \"action\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentaction\",\r\n \"code\" : \"disclose\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n }\r\n ]\r\n}", + "raw": "{\r\n \"resourceType\" : \"Parameters\",\r\n \"id\" : \"member-match-in\",\r\n \"parameter\" : [\r\n {\r\n \"name\" : \"MemberPatient\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Patient\",\r\n \"id\": \"12345\",\r\n \"identifier\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\" : \"MB\"\r\n }\r\n ]\r\n },\r\n \"system\" : \"http://github.com/inferno-framework/target-payer/identifiers/member\",\r\n \"value\" : \"99999\",\r\n \"assigner\" : {\r\n \"display\" : \"Old Payer\"\r\n }\r\n }\r\n ],\r\n \"name\" : [\r\n {\r\n \"use\" : \"official\",\r\n \"family\" : \"Person\",\r\n \"given\" : [\r\n \"Patricia\",\r\n \"Ann\"\r\n ]\r\n }\r\n ],\r\n \"gender\" : \"female\",\r\n \"birthDate\" : \"1974-12-25\"\r\n }\r\n },\r\n {\r\n \"name\" : \"CoverageToMatch\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Coverage\",\r\n \"id\" : \"9876B1\",\r\n \"identifier\" : [\r\n {\r\n \"system\" : \"http://github.com/inferno-framework/old-payer\",\r\n \"value\" : \"DH10001235\"\r\n }\r\n ],\r\n \"status\" : \"draft\",\r\n \"beneficiary\" : {\r\n \"reference\" : \"Patient/12345\"\r\n },\r\n \"period\" : {\r\n \"start\" : \"2011-05-23\",\r\n \"end\" : \"2012-05-23\"\r\n },\r\n \"payor\" : [\r\n {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"9876543210\"\r\n },\r\n \"display\" : \"Old Health Plan\"\r\n }\r\n ],\r\n \"class\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"group\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"CB135\"\r\n },\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"plan\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"B37FC\"\r\n },\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"subplan\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"P7\"\r\n },\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\" : \"class\"\r\n }\r\n ]\r\n },\r\n \"value\" : \"SILVER\"\r\n }\r\n ]\r\n }\r\n },\r\n {\r\n \"name\" : \"CoverageToLink\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Coverage\",\r\n \"id\" : \"AA87654\",\r\n \"identifier\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\" : \"MB\"\r\n }\r\n ]\r\n },\r\n \"system\" : \"http://github.com/inferno-framework/new-payer/identifiers/coverage\",\r\n \"value\" : \"234567\"\r\n }\r\n ],\r\n \"status\" : \"active\",\r\n \"beneficiary\" : {\r\n \"reference\" : \"Patient/12345\"\r\n },\r\n \"relationship\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/data-absent-reason\",\r\n \"code\" : \"unknown\"\r\n }\r\n ]\r\n },\r\n \"payor\" : [\r\n {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"0123456789\"\r\n },\r\n \"display\" : \"New Health Plan\"\r\n }\r\n ]\r\n }\r\n },\r\n {\r\n \"name\" : \"Consent\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Consent\",\r\n \"status\" : \"active\",\r\n \"scope\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentscope\",\r\n \"code\" : \"patient-privacy\"\r\n }\r\n ]\r\n },\r\n \"category\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\r\n \"code\" : \"IDSCL\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"patient\" : {\r\n \"reference\" : \"Patient/12345\"\r\n },\r\n \"performer\" : [\r\n {\r\n \"reference\" : \"Patient/12345\"\r\n }\r\n ],\r\n \"sourceReference\" : {\r\n \"reference\" : \"http://github.com/inferno-framework/DocumentReference/someconsent.pdf\"\r\n },\r\n \"policy\" : [\r\n {\r\n \"uri\" : \"http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#regular\"\r\n }\r\n ],\r\n \"provision\" : {\r\n \"type\" : \"permit\",\r\n \"period\" : {\r\n \"start\" : \"2022-01-01\",\r\n \"end\" : \"2022-01-31\"\r\n },\r\n \"actor\" : [\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/provenance-participant-type\",\r\n \"code\" : \"performer\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"9876543210\"\r\n },\r\n \"display\" : \"Old Health Plan\"\r\n }\r\n },\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ParticipationType\",\r\n \"code\" : \"IRCP\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"0000000001\"\r\n },\r\n \"display\" : \"Provider organization\"\r\n }\r\n }\r\n ],\r\n \"action\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentaction\",\r\n \"code\" : \"disclose\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n }\r\n ]\r\n}", "options": { "raw": { "language": "json" @@ -59,7 +59,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"resourceType\" : \"Parameters\",\r\n \"id\" : \"member-match-in\",\r\n \"parameter\" : [\r\n {\r\n \"name\" : \"MemberPatient\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Patient\",\r\n \"id\" : \"999\",\r\n \"identifier\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\" : \"MB\"\r\n }\r\n ]\r\n },\r\n \"system\" : \"http://github.com/inferno-framework/target-payer/identifiers/member\",\r\n \"value\" : \"55678\",\r\n \"assigner\" : {\r\n \"display\" : \"Old Payer\"\r\n }\r\n }\r\n ],\r\n \"name\" : [\r\n {\r\n \"use\" : \"official\",\r\n \"family\" : \"Person\",\r\n \"given\" : [\r\n \"Patricia\",\r\n \"Ann\"\r\n ]\r\n }\r\n ],\r\n \"gender\" : \"female\",\r\n \"birthDate\" : \"1974-12-25\"\r\n }\r\n },\r\n {\r\n \"name\" : \"Consent\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Consent\",\r\n \"status\" : \"active\",\r\n \"scope\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentscope\",\r\n \"code\" : \"patient-privacy\"\r\n }\r\n ]\r\n },\r\n \"category\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\r\n \"code\" : \"IDSCL\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"patient\" : {\r\n \"reference\" : \"Patient/999\"\r\n },\r\n \"performer\" : [\r\n {\r\n \"reference\" : \"Patient/999\"\r\n }\r\n ],\r\n \"sourceReference\" : {\r\n \"reference\" : \"http://github.com/inferno-framework/DocumentReference/someconsent.pdf\"\r\n },\r\n \"policy\" : [\r\n {\r\n \"uri\" : \"http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#regular\"\r\n }\r\n ],\r\n \"provision\" : {\r\n \"type\" : \"permit\",\r\n \"period\" : {\r\n \"start\" : \"2022-01-01\",\r\n \"end\" : \"2022-01-31\"\r\n },\r\n \"actor\" : [\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/provenance-participant-type\",\r\n \"code\" : \"performer\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"9876543210\"\r\n },\r\n \"display\" : \"Old Health Plan\"\r\n }\r\n },\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ParticipationType\",\r\n \"code\" : \"IRCP\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"0000000001\"\r\n },\r\n \"display\" : \"Provider organization\"\r\n }\r\n }\r\n ],\r\n \"action\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentaction\",\r\n \"code\" : \"disclose\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n }\r\n ]\r\n}", + "raw": "{\r\n \"resourceType\" : \"Parameters\",\r\n \"id\" : \"member-match-in\",\r\n \"parameter\" : [\r\n {\r\n \"name\" : \"MemberPatient\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Patient\",\r\n \"id\": \"12345\",\r\n \"identifier\" : [\r\n {\r\n \"type\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\" : \"MB\"\r\n }\r\n ]\r\n },\r\n \"system\" : \"http://github.com/inferno-framework/target-payer/identifiers/member\",\r\n \"value\" : \"99999\",\r\n \"assigner\" : {\r\n \"display\" : \"Old Payer\"\r\n }\r\n }\r\n ],\r\n \"name\" : [\r\n {\r\n \"use\" : \"official\",\r\n \"family\" : \"Person\",\r\n \"given\" : [\r\n \"Patricia\",\r\n \"Ann\"\r\n ]\r\n }\r\n ],\r\n \"gender\" : \"female\",\r\n \"birthDate\" : \"1974-12-25\"\r\n }\r\n },\r\n {\r\n \"name\" : \"Consent\",\r\n \"resource\" : {\r\n \"resourceType\" : \"Consent\",\r\n \"status\" : \"active\",\r\n \"scope\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentscope\",\r\n \"code\" : \"patient-privacy\"\r\n }\r\n ]\r\n },\r\n \"category\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\r\n \"code\" : \"IDSCL\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"patient\" : {\r\n \"reference\" : \"Patient/12345\"\r\n },\r\n \"performer\" : [\r\n {\r\n \"reference\" : \"Patient/12345\"\r\n }\r\n ],\r\n \"sourceReference\" : {\r\n \"reference\" : \"http://github.com/inferno-framework/DocumentReference/someconsent.pdf\"\r\n },\r\n \"policy\" : [\r\n {\r\n \"uri\" : \"http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#regular\"\r\n }\r\n ],\r\n \"provision\" : {\r\n \"type\" : \"permit\",\r\n \"period\" : {\r\n \"start\" : \"2022-01-01\",\r\n \"end\" : \"2022-01-31\"\r\n },\r\n \"actor\" : [\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/provenance-participant-type\",\r\n \"code\" : \"performer\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"9876543210\"\r\n },\r\n \"display\" : \"Old Health Plan\"\r\n }\r\n },\r\n {\r\n \"role\" : {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/v3-ParticipationType\",\r\n \"code\" : \"IRCP\"\r\n }\r\n ]\r\n },\r\n \"reference\" : {\r\n \"identifier\" : {\r\n \"system\" : \"http://hl7.org/fhir/sid/us-npi\",\r\n \"value\" : \"0000000001\"\r\n },\r\n \"display\" : \"Provider organization\"\r\n }\r\n }\r\n ],\r\n \"action\" : [\r\n {\r\n \"coding\" : [\r\n {\r\n \"system\" : \"http://terminology.hl7.org/CodeSystem/consentaction\",\r\n \"code\" : \"disclose\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n }\r\n ]\r\n}", "options": { "raw": { "language": "json" @@ -723,6 +723,93 @@ } } ] + }, + { + "name": "$export Requests", + "item": [ + { + "name": "export kick-off", + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "accept": true + } + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/fhir+json", + "type": "text" + }, + { + "key": "Prefer", + "value": "respond-async", + "type": "text" + } + ], + "url": { + "raw": "{{url_prefix}}/custom/pdex_payer_client/fhir/Patient/$export", + "host": [ + "{{url_prefix}}" + ], + "path": [ + "custom", + "pdex_payer_client", + "fhir", + "Patient", + "$export" + ] + } + }, + "response": [] + }, + { + "name": "export status", + "protocolProfileBehavior": { + "disabledSystemHeaders": { + "accept": true + } + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/fhir+ndjson", + "type": "text" + }, + { + "key": "Prefer", + "value": "respond-async", + "type": "text" + } + ], + "url": { + "raw": "*populate with url from content-location header from previous request*", + "host": [ + "*populate with url from content-location header from previous request*" + ] + } + }, + "response": [] + }, + { + "name": "ndjson retrieval", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "*fill with urls from the body of a successful $export-poll-status status 200 request*", + "host": [ + "*fill with urls from the body of a successful $export-poll-status status 200 request*" + ] + } + }, + "response": [] + } + ], + "description": "These requests follow the Bulk Data workflow for the $export operation. The requests have been equipped with headers according to each step of the workflow, but you will need to populate the urls with results from previous requests as such\n\n1. Issue the $export request\n2. Check response for 202 (success) and pull the link from the `content-location` header.\n3. Paste that link in the url for the \"$export follow up\" request\n4. Send the request until you get a 200 and a body back\n5. Within the body, copy and paste any of the links to Binaries in the \"Binary read\" request." } ], "auth": { diff --git a/lib/davinci_pdex_test_kit/mock_server.rb b/lib/davinci_pdex_test_kit/mock_server.rb index b61d490..79660ee 100644 --- a/lib/davinci_pdex_test_kit/mock_server.rb +++ b/lib/davinci_pdex_test_kit/mock_server.rb @@ -17,7 +17,7 @@ def server_proxy @server_proxy ||= Faraday.new( url: ENV.fetch('FHIR_REFERENCE_SERVER'), params: {}, - headers: {'Content-Type' => 'application/json', 'Authorization' => 'Bearer SAMPLE_TOKEN'}, + headers: {'Content-Type' => 'application/json', 'Authorization' => 'Bearer SAMPLE_TOKEN', 'Host' => ENV.fetch('HOST_HEADER')}, ) end @@ -60,16 +60,38 @@ def everything_response(request, test = nil, test_result = nil) request.response_body = replace_bundle_urls(FHIR.from_contents(response.body)).to_json end - # def export_response(request, test = nil, test_result = nil) - # response = server_proxy.get do |req| - # req.url 'Group/pdex-Group/$export' #TODO: change from static response - # req.headers['Prefer'] = 'respond-async' - # req.headers['Accept'] = 'application/fhir+json' - # end - # request.status = response.status - # request.response_headers = response.env.response_headers - # request.response_body = response.body - # end + def export_response(request, test = nil, test_result = nil) + headers_as_hash = request.request_headers.map { |header| {"#{header.name}": header.value}}.reduce({}) { |reduced, curr| reduced.merge(curr)} + response = server_proxy.get do |req| + req.url 'Group/pdex-Group/$export' #TODO: change from static response + req.headers = headers_as_hash.merge(server_proxy.headers) + end + request.status = response.status + request.response_headers = response.env.response_headers + request.response_header("content-location").value.gsub!(/(.*)\?/, "#{new_link}/$export-poll-status?") + request.response_body = response.body + end + + def export_status_response(request, test = nil, test_result = nil) + headers_as_hash = request.request_headers.map { |header| {"#{header.name}": header.value}}.reduce({}) { |reduced, curr| reduced.merge(curr)} + response = server_proxy.get do |req| + req.url '$export-poll-status' + req.params = request.query_parameters + req.headers = headers_as_hash.merge(server_proxy.headers) + end + request.status = response.status + request.response_headers = response.env.response_headers + request.response_body = response.status.to_i == 200 ? replace_export_urls(JSON.parse(response.body)).to_json : response.body + request.response_header("content-length").value = request.response_body.length + end + + def binary_read_response(request, test = nil, test_result = nil) + binary_id = request.url.split('/').last + response = server_proxy.get('Binary/'+binary_id) + request.status = response.status + request.response_headers = response.headers + request.response_body = response.body + end def member_match_response(request, test = nil, test_result = nil) #remove token from request as well @@ -170,7 +192,7 @@ def mock_operation_outcome_resource def replace_bundle_urls(bundle) reference_server_base = ENV.fetch('FHIR_REFERENCE_SERVER') - bundle.link.map! {|link| {relation: link.relation, url: link.url.gsub(reference_server_base, new_link)}} + bundle&.link.map! {|link| {relation: link.relation, url: link.url.gsub(reference_server_base, new_link)}} bundle&.entry&.map! do |bundled_resource| {fullUrl: bundled_resource.fullUrl.gsub(reference_server_base, new_link), resource: bundled_resource.resource, @@ -180,8 +202,15 @@ def replace_bundle_urls(bundle) bundle end + def replace_export_urls(export_status_output) + reference_server_base = ENV.fetch('FHIR_REFERENCE_SERVER') + export_status_output['output'].map! { |binary| {type: binary["type"], url: binary["url"].gsub(reference_server_base, new_link)} } + export_status_output['request'] = new_link + '/Patient/$export' + export_status_output + end + def new_link - "#{Inferno::Application['base_url']}/custom/pdex_payer_client/fhir" + "#{Inferno::Application['base_url']}\/custom\/pdex_payer_client\/fhir" end # @private diff --git a/lib/davinci_pdex_test_kit/pdex_payer_client/client_validation_test.rb b/lib/davinci_pdex_test_kit/pdex_payer_client/client_validation_test.rb index 4c4103f..8c2c5f4 100644 --- a/lib/davinci_pdex_test_kit/pdex_payer_client/client_validation_test.rb +++ b/lib/davinci_pdex_test_kit/pdex_payer_client/client_validation_test.rb @@ -14,54 +14,13 @@ def previous_clinical_data_request_resources end end - # def collect_export_resources - # export_payload = collect_export_payload - # if export_payload - # ndjson_list = JSON.parse(export_payload) - # request_resources = ndjson_list['output'].map do |resource_binary| - # retrieved_resources = Faraday.new( - # url: resource_binary['url'], - # headers: {'Content-Type' => 'application/json', - # 'Authorization' => 'Bearer SAMPLE_TOKEN'} - # ).get - # connect_bundle(retrieved_resources.env.response_body) - # end - # puts request_resources - # request_resources.flatten - # else - # nil - # end - # end - - # def connect_bundle(export_binary) - # export_binary.split(/(?<=}\n)(?={)/).map { |str| FHIR.from_contents(str)} - # end - - # def collect_export_payload - # url = export_request&.response_header('content-location')&.value - # attempts = 0 - # return nil if url.nil? - # while attempts < 5 - # request_attempt = Faraday.new( - # url: url, - # headers: {'Content-Type' => 'application/json', - # 'Authorization' => 'Bearer SAMPLE_TOKEN', - # 'Prefer' => 'respond-async', - # 'Accept' => 'application/fhir+json'} - # ).get - # if request_attempt.status != 200 - # attempts += 1 - # sleep(2) - # else - # return request_attempt.env.response_body - # end - # end - # return nil - # end + def connect_bundle(export_binary) + export_binary.split(/(?<=}\n)(?={)/).map { |str| FHIR.from_contents(str)} + end - # def export_resources - # @export_resources ||= collect_export_resources - # end + def export_resources + @export_resources ||= (load_tagged_requests(BINARY_TAG).map { |binary_read| binary_read.response_body.split("\n") }.flatten).map { |resource_in_binary| FHIR.from_contents(resource_in_binary)} + end def previous_clinical_data_requests @previous_clinical_data_requests ||= load_tagged_requests(SUBMIT_TAG) + [everything_request].compact @@ -75,9 +34,13 @@ def member_match_request @member_match_request ||= load_tagged_requests(MEMBER_MATCH_TAG).first end - # def export_request - # @export_request ||= load_tagged_requests(EXPORT_TAG).first - # end + def export_request + @export_request ||= load_tagged_requests(EXPORT_TAG).first + end + + def export_status_request + @export_status_request ||= load_tagged_requests(EXPORT_STATUS_TAG).first + end # def patient_id_from_match_request # @patient_id_from_match_request ||= member_match_request ? "999" : nil #TODO: Change from static response diff --git a/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_scratch_storing.rb b/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_scratch_storing.rb index c9a6d19..31bb5ea 100644 --- a/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_scratch_storing.rb +++ b/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_scratch_storing.rb @@ -17,15 +17,13 @@ class PDexClientScratchStorage < Inferno::Test scratch[resource.resourceType.to_sym] |= [resource] end end - # if export_resources - # info "Attempted an $export request" - # export_resources.each do |resource| - # scratch[resource.resourceType.to_sym] ||= [] - # scratch[resource.resourceType.to_sym] |= [resource] - # end - # elsif export_request - # info "Found an $export request, but no resources found. It may not have had enough time to build" - # end + if !export_resources.empty? + info "Attempted an $export request" + export_resources.each do |resource| + scratch[resource.resourceType.to_sym] ||= [] + scratch[resource.resourceType.to_sym] |= [resource] + end + end if everything_request info "Attempted an $everything request" end diff --git a/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_wait_test.rb b/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_wait_test.rb index 4baeddc..c68588f 100644 --- a/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_wait_test.rb +++ b/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_wait_test.rb @@ -19,6 +19,8 @@ class PDexClientSubmitMustSupportTest < Inferno::Test Submit PDex requests via at least one of the following methods: * Single Resource API: `#{submit_url}`, with `:endpoint` replaced with the endpoint you want to reach * $everything method: `#{everything_url}`, with `:patient` replaced with the patient you are matching + * $export method: `#{export_url}`, see workflow process at the [Bulk Data IG](https://hl7.org/fhir/uv/bulkdata/STU2/) + * $export-poll-status: `#{export_status_url}`, then continue to make reads from the binaries if a payload is delivered and [click here](#{resume_clinical_data_url}?token=#{access_token}) when done. ) diff --git a/lib/davinci_pdex_test_kit/pdex_payer_client_suite.rb b/lib/davinci_pdex_test_kit/pdex_payer_client_suite.rb index 67b1860..d5f33e0 100644 --- a/lib/davinci_pdex_test_kit/pdex_payer_client_suite.rb +++ b/lib/davinci_pdex_test_kit/pdex_payer_client_suite.rb @@ -88,21 +88,31 @@ def self.test_resumes?(test) resumes: method(:test_resumes?) do |request| PDexPayerClientSuite.extract_bearer_token(request) end - + record_response_route :get, SUBMIT_PATH, SUBMIT_TAG, method(:claim_response), resumes: method(:test_resumes?) do |request| PDexPayerClientSuite.extract_bearer_token(request) end + record_response_route :get, BINARY_PATH, BINARY_TAG, method(:binary_read_response), + resumes: method(:test_resumes?) do |request| + PDexPayerClientSuite.extract_bearer_token(request) + end + record_response_route :get, EVERYTHING_PATH, EVERYTHING_TAG, method(:everything_response), resumes: method(:test_resumes?) do |request| PDexPayerClientSuite.extract_bearer_token(request) end - # record_response_route :get, EXPORT_PATH, EXPORT_TAG, method(:export_response), - # resumes: method(:test_resumes?) do |request| - # PDexPayerClientSuite.extract_bearer_token(request) - # end + record_response_route :get, EXPORT_PATH, EXPORT_TAG, method(:export_response), + resumes: method(:test_resumes?) do |request| + PDexPayerClientSuite.extract_bearer_token(request) + end + + record_response_route :get, EXPORT_STATUS_PATH, EXPORT_STATUS_TAG, method(:export_status_response), + resumes: method(:test_resumes?) do |request| + PDexPayerClientSuite.extract_bearer_token(request) + end record_response_route :post, MEMBER_MATCH_PATH, MEMBER_MATCH_TAG, method(:member_match_response), resumes: method(:test_resumes?) do |request| diff --git a/lib/davinci_pdex_test_kit/tags.rb b/lib/davinci_pdex_test_kit/tags.rb index 6001187..a08abdf 100644 --- a/lib/davinci_pdex_test_kit/tags.rb +++ b/lib/davinci_pdex_test_kit/tags.rb @@ -3,7 +3,9 @@ module DaVinciPDexTestKit AUTH_TAG = 'pdex_auth' SUBMIT_TAG = 'pdex_submit' - # EXPORT_TAG = 'pdex_export' + BINARY_TAG = 'pdex_binary' + EXPORT_TAG = 'pdex_export' + EXPORT_STATUS_TAG = 'pdex_export_status' EVERYTHING_TAG = 'pdex_everything' MEMBER_MATCH_TAG = 'pdex_member_match' end diff --git a/lib/davinci_pdex_test_kit/urls.rb b/lib/davinci_pdex_test_kit/urls.rb index 3433866..96d757d 100644 --- a/lib/davinci_pdex_test_kit/urls.rb +++ b/lib/davinci_pdex_test_kit/urls.rb @@ -2,10 +2,12 @@ module DaVinciPDexTestKit TOKEN_PATH = '/mock_auth/token' PATIENT_PATH = '/fhir/Patient' SUBMIT_PATH = '/fhir/:endpoint' + BINARY_PATH = '/fhir/Binary/:id' METADATA_PATH = '/fhir/metadata' EVERYTHING_PATH = '/fhir/Patient/:patient/$everything' MEMBER_MATCH_PATH = '/fhir/Patient/$member-match' - # EXPORT_PATH = '/fhir/Patient/$export' + EXPORT_PATH = '/fhir/Patient/$export' + EXPORT_STATUS_PATH = '/fhir/$export-poll-status' BASE_FHIR_PATH = '/fhir' RESUME_PASS_PATH = '/resume_pass' RESUME_CLINICAL_DATA_PATH = '/resume_clinical_data' @@ -32,6 +34,10 @@ def submit_url @submit_url ||= base_url + SUBMIT_PATH end + def binary_url + @binary_url ||= base_url + BINARY_PATH + end + def metadata_url @metadata_url ||= base_url + METADATA_PATH end @@ -44,9 +50,13 @@ def member_match_url @member_match_url ||= base_url + MEMBER_MATCH_PATH end - # def export_url - # @export_url ||= base_url + EXPORT_PATH - # end + def export_url + @export_url ||= base_url + EXPORT_PATH + end + + def export_status_url + @export_status_url ||= base_url + EXPORT_STATUS_PATH + end def resume_pass_url @resume_pass_url ||= base_url + RESUME_PASS_PATH