From 551537ae05ab2a51d074fd3a142ef1c7010ea190 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:47:42 -0700 Subject: [PATCH 01/12] Moved and renamed hrl script to simplify and clean up file org --- scripts/hurl/README.md | 119 +++++++++++++++++++++++ scripts/hurl/{epic/hrl => epic.sh} | 2 +- scripts/hurl/epic/README.md | 10 -- scripts/hurl/message_submission_utils.sh | 17 +--- scripts/hurl/{rs/hrl => rs.sh} | 4 +- scripts/hurl/rs/README.md | 51 ---------- scripts/hurl/{ti/hrl => ti.sh} | 4 +- scripts/hurl/ti/README.md | 55 ----------- 8 files changed, 127 insertions(+), 135 deletions(-) rename scripts/hurl/{epic/hrl => epic.sh} (94%) delete mode 100644 scripts/hurl/epic/README.md rename scripts/hurl/{rs/hrl => rs.sh} (96%) delete mode 100644 scripts/hurl/rs/README.md rename scripts/hurl/{ti/hrl => ti.sh} (95%) delete mode 100644 scripts/hurl/ti/README.md diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index aba1fb1ca..fc208f968 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -13,6 +13,125 @@ - [CDC Intermediary](./ti/): scripts to send requests to the CDC Intermediary's endpoints - [Epic/UCSD](./epic/): scripts to send requests to Epic endpoints for UCSD +### ReportStream + +#### Usage + +``` +Usage: ./rs.sh [OPTIONS] + +Options: + -f The path to the hl7/fhir file to submit, relative the root path (Required for waters API) + -r The root path to the hl7/fhir files (Default: $CDCTI_HOME/examples/) + -t The content type for the message (e.g. 'application/hl7-v2' or 'application/fhir+ndjson') (Default: application/hl7-v2) + -e [local | staging | production ] The environment to run the test in (Default: local) + -c The client id to use (Default: flexion) + -s The client sender to use (Default: simulated-sender) + -x The path to the client private key for the environment + -i The submissionId to call the history API with (Required for history API) + -v Verbose mode + -h Display this help and exit +``` + +#### Examples + +Sending an order to local environment + +``` +./rs.sh waters.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 +``` + +Sending a result to local environment + +``` +./rs.sh waters.hurl -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_0_initial_message.hl7 +``` + +Sending an order to staging + +``` +./rs.sh waters.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -x /path/to/staging/private/key +``` + +Checking the history in local environment for a submission id + +``` +./rs.sh history.hurl -i 100 +``` + +Checking the history in staging for a submission id + +``` +./rs.sh history.hurl -i 100 -e staging -x /path/to/staging/private/key +``` + +### CDC Intermediary + +#### Usage + +``` +Usage: ti.rs [OPTIONS] + +Options: + -f The path to the hl7/fhir file to submit, relative the root path (Required for orders and results APIs) + -r The root path to the hl7/fhir files (Default: $CDCTI_HOME/examples/) + -e [local | staging] The environment to run the test in (Default: local) + -c The client id to use (Default: report-stream) + -j The JWT to use for authentication + -i The submissionId to call the metadata API with (Required for metadata API) + -v Verbose mode + -h Display this help and exit +``` + +#### Examples + +Submit an order to local environment: +``` +./ti.rs orders.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 +``` + +Submit an order to staging: +``` +./ti.rs orders.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -j eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ + +``` + +Submit a result to local environment: +``` +./ti.rs results.hurl -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 +``` + +Get metadata from local environment: +``` +./ti.rs metadata -i 100 +``` + +Authenticate to local environment: +``` +./ti.rs auth.hurl +``` + +Get OpenAPI docs from local environment: +``` +./ti.rs openapi.hurl +``` + +Get Health info from local environment: +``` +./ti.rs health.hurl +``` + +### Epic/UCSD + +#### Before running the script + +- Add the `client` id to `epic.rs` +- Update the `secret` variable path + +#### Usage + +`./epic.rs ` + ## Local Submission Scripts - `submit_message.sh`: sends a HL7 message to a locally running RS instance. It also grabs the snapshots of the file in azurite after converting to FHIR, after applying transformations in TI, and after converting back to HL7. It copies these files to the same folder where the submitted file is diff --git a/scripts/hurl/epic/hrl b/scripts/hurl/epic.sh similarity index 94% rename from scripts/hurl/epic/hrl rename to scripts/hurl/epic.sh index 06c0cb53a..29d4f2e3b 100755 --- a/scripts/hurl/epic/hrl +++ b/scripts/hurl/epic.sh @@ -9,5 +9,5 @@ hurl \ --variable fpath=$fpath \ --file-root $root \ --variable jwt=$(jwt encode --exp='+5min' --jti $(uuidgen) --alg RS256 -k $client -i $client -s $client -a $audience --no-iat -S @$secret) \ - results.hurl \ + epic/results.hurl \ $@ diff --git a/scripts/hurl/epic/README.md b/scripts/hurl/epic/README.md deleted file mode 100644 index c66c84aa8..000000000 --- a/scripts/hurl/epic/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Epic/UCSD Hurl Script - -## Before running the script - -- Add the `client` id to `.hrl` -- Update the `secret` variable path - -## Usage - -`./hrl ` diff --git a/scripts/hurl/message_submission_utils.sh b/scripts/hurl/message_submission_utils.sh index ea5a0abab..1e5708cd1 100755 --- a/scripts/hurl/message_submission_utils.sh +++ b/scripts/hurl/message_submission_utils.sh @@ -1,7 +1,5 @@ #!/bin/bash -RS_HRL_SCRIPT_PATH="$CDCTI_HOME/scripts/hurl/rs" -TI_HRL_SCRIPT_PATH="$CDCTI_HOME/scripts/hurl/ti" FILE_NAME_SUFFIX_STEP_0="_0_initial_message" FILE_NAME_SUFFIX_STEP_1="_1_hl7_translation" FILE_NAME_SUFFIX_STEP_2="_2_fhir_transformation" @@ -40,10 +38,7 @@ check_submission_status() { start_time=$(date +%s) while true; do - history_response=$( - cd "$RS_HRL_SCRIPT_PATH" || exit 1 - ./hrl history.hurl -i "$submission_id" - ) + history_response=$(./rs.sh history.hurl -i "$submission_id") overall_status=$(echo "$history_response" | jq -r '.overallStatus') echo -n " Status: $overall_status" @@ -118,10 +113,7 @@ submit_message() { echo "Assuming receivers are '$first_leg_receiver' and '$second_leg_receiver' because of MSH-9 value '$msh9'" - waters_response=$( - cd "$RS_HRL_SCRIPT_PATH" || exit 1 - ./hrl waters.hurl -f "$message_file_name" -r "$message_file_path" - ) + waters_response=$(./rs.sh waters.hurl -f "$message_file_name" -r "$message_file_path") submission_id=$(echo "$waters_response" | jq -r '.id') echo "[First leg] Checking submission status for ID: $submission_id" @@ -135,10 +127,7 @@ submit_message() { translated_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_1.fhir" download_from_azurite "$translated_blob_name" "$translated_file_path" - metadata_response=$( - cd "$TI_HRL_SCRIPT_PATH" || exit 1 - ./hrl metadata.hurl -i "$inbound_submission_id" - ) + metadata_response=$(./ti.sh metadata.hurl -i "$inbound_submission_id") outbound_submission_id=$(echo "$metadata_response" | jq -r '.issue[] | select(.details.text == "outbound submission id") | .diagnostics') transformed_blob_name="receive/flexion.etor-service-sender/$outbound_submission_id.fhir" diff --git a/scripts/hurl/rs/hrl b/scripts/hurl/rs.sh similarity index 96% rename from scripts/hurl/rs/hrl rename to scripts/hurl/rs.sh index 6383a4103..15515d6af 100755 --- a/scripts/hurl/rs/hrl +++ b/scripts/hurl/rs.sh @@ -44,8 +44,8 @@ if [ "$1" = "-h" ]; then exit 0 fi -hurl_file="$1" # Assign the first argument to hurl_file -shift # Remove the first argument from the list of arguments +hurl_file=rs/"$1" # Assign the first argument to hurl_file +shift # Remove the first argument from the list of arguments while getopts ':f:r:t:e:c:s:x:i:vh' opt; do case "$opt" in diff --git a/scripts/hurl/rs/README.md b/scripts/hurl/rs/README.md deleted file mode 100644 index 98d39c9c9..000000000 --- a/scripts/hurl/rs/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# ReportStream Hurl Script - -## Usage - -``` -Usage: ./hrl [OPTIONS] - -Options: - -f The path to the hl7/fhir file to submit, relative the root path (Required for waters API) - -r The root path to the hl7/fhir files (Default: $CDCTI_HOME/examples/) - -t The content type for the message (e.g. 'application/hl7-v2' or 'application/fhir+ndjson') (Default: application/hl7-v2) - -e [local | staging | production ] The environment to run the test in (Default: local) - -c The client id to use (Default: flexion) - -s The client sender to use (Default: simulated-sender) - -x The path to the client private key for the environment - -i The submissionId to call the history API with (Required for history API) - -v Verbose mode - -h Display this help and exit -``` - -## Examples - -Sending an order to local environment - -``` -./hrl waters.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -``` - -Sending a result to local environment - -``` -./hrl waters.hurl -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_0_initial_message.hl7 -``` - -Sending an order to staging - -``` -./hrl waters.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -x /path/to/staging/private/key -``` - -Checking the history in local environment for a submission id - -``` -./hrl history.hurl -i 100 -``` - -Checking the history in staging for a submission id - -``` -./hrl history.hurl -i 100 -e staging -x /path/to/staging/private/key -``` diff --git a/scripts/hurl/ti/hrl b/scripts/hurl/ti.sh similarity index 95% rename from scripts/hurl/ti/hrl rename to scripts/hurl/ti.sh index 465ff3c92..e38d7a506 100755 --- a/scripts/hurl/ti/hrl +++ b/scripts/hurl/ti.sh @@ -39,8 +39,8 @@ if [ "$1" = "-h" ]; then exit 0 fi -hurl_file="$1" # Assign the first argument to hurl_file -shift # Remove the first argument from the list of arguments +hurl_file=ti/"$1" # Assign the first argument to hurl_file +shift # Remove the first argument from the list of arguments while getopts ':f:r:e:c:j:i:vh' opt; do case "$opt" in diff --git a/scripts/hurl/ti/README.md b/scripts/hurl/ti/README.md deleted file mode 100644 index 704cb30b0..000000000 --- a/scripts/hurl/ti/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# CDC Intermediary Hurl Script - -## Usage - -``` -Usage: hrl [OPTIONS] - -Options: - -f The path to the hl7/fhir file to submit, relative the root path (Required for orders and results APIs) - -r The root path to the hl7/fhir files (Default: $CDCTI_HOME/examples/) - -e [local | staging] The environment to run the test in (Default: local) - -c The client id to use (Default: report-stream) - -j The JWT to use for authentication - -i The submissionId to call the metadata API with (Required for metadata API) - -v Verbose mode - -h Display this help and exit -``` - -## Examples - -Submit an order to local environment: -``` -./hrl orders.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 -``` - -Submit an order to staging: -``` -./hrl orders.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -j eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ - -``` - -Submit a result to local environment: -``` -./hrl results.hurl -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 -``` - -Get metadata from local environment: -``` -./hrl metadata -i 100 -``` - -Authenticate to local environment: -``` -./hrl auth.hurl -``` - -Get OpenAPI docs from local environment: -``` -./hrl openapi.hurl -``` - -Get Health info from local environment: -``` -./hrl health.hurl -``` From 14793a80f22321a54ab90d84ee7433ea37f18642 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:00:36 -0700 Subject: [PATCH 02/12] Simplify command hurl parameter + cleanup --- scripts/hurl/README.md | 32 +++++++++++------------- scripts/hurl/message_submission_utils.sh | 6 ++--- scripts/hurl/rs.sh | 12 ++++----- scripts/hurl/ti.sh | 12 ++++----- 4 files changed, 29 insertions(+), 33 deletions(-) diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index fc208f968..cb84ee95f 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -9,16 +9,12 @@ ## Available Hurl Scripts -- [ReportStream](./rs/): scripts to send requests to ReportStream's endpoints -- [CDC Intermediary](./ti/): scripts to send requests to the CDC Intermediary's endpoints -- [Epic/UCSD](./epic/): scripts to send requests to Epic endpoints for UCSD - ### ReportStream #### Usage ``` -Usage: ./rs.sh [OPTIONS] +Usage: ./rs.sh [OPTIONS] Options: -f The path to the hl7/fhir file to submit, relative the root path (Required for waters API) @@ -38,31 +34,31 @@ Options: Sending an order to local environment ``` -./rs.sh waters.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 +./rs.sh waters -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 ``` Sending a result to local environment ``` -./rs.sh waters.hurl -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_0_initial_message.hl7 +./rs.sh waters -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_0_initial_message.hl7 ``` Sending an order to staging ``` -./rs.sh waters.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -x /path/to/staging/private/key +./rs.sh waters -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -x /path/to/staging/private/key ``` Checking the history in local environment for a submission id ``` -./rs.sh history.hurl -i 100 +./rs.sh history -i 100 ``` Checking the history in staging for a submission id ``` -./rs.sh history.hurl -i 100 -e staging -x /path/to/staging/private/key +./rs.sh history -i 100 -e staging -x /path/to/staging/private/key ``` ### CDC Intermediary @@ -70,7 +66,7 @@ Checking the history in staging for a submission id #### Usage ``` -Usage: ti.rs [OPTIONS] +Usage: ti.rs [OPTIONS] Options: -f The path to the hl7/fhir file to submit, relative the root path (Required for orders and results APIs) @@ -87,18 +83,18 @@ Options: Submit an order to local environment: ``` -./ti.rs orders.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 +./ti.rs orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 ``` Submit an order to staging: ``` -./ti.rs orders.hurl -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -j eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ +./ti.rs orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -j eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ ``` Submit a result to local environment: ``` -./ti.rs results.hurl -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 +./ti.rs results -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 ``` Get metadata from local environment: @@ -108,17 +104,17 @@ Get metadata from local environment: Authenticate to local environment: ``` -./ti.rs auth.hurl +./ti.rs auth ``` Get OpenAPI docs from local environment: ``` -./ti.rs openapi.hurl +./ti.rs openapi ``` Get Health info from local environment: ``` -./ti.rs health.hurl +./ti.rs health ``` ### Epic/UCSD @@ -130,7 +126,7 @@ Get Health info from local environment: #### Usage -`./epic.rs ` +`./epic.rs results` ## Local Submission Scripts diff --git a/scripts/hurl/message_submission_utils.sh b/scripts/hurl/message_submission_utils.sh index 1e5708cd1..fd28c599b 100755 --- a/scripts/hurl/message_submission_utils.sh +++ b/scripts/hurl/message_submission_utils.sh @@ -38,7 +38,7 @@ check_submission_status() { start_time=$(date +%s) while true; do - history_response=$(./rs.sh history.hurl -i "$submission_id") + history_response=$(./rs.sh history -i "$submission_id") overall_status=$(echo "$history_response" | jq -r '.overallStatus') echo -n " Status: $overall_status" @@ -113,7 +113,7 @@ submit_message() { echo "Assuming receivers are '$first_leg_receiver' and '$second_leg_receiver' because of MSH-9 value '$msh9'" - waters_response=$(./rs.sh waters.hurl -f "$message_file_name" -r "$message_file_path") + waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path") submission_id=$(echo "$waters_response" | jq -r '.id') echo "[First leg] Checking submission status for ID: $submission_id" @@ -127,7 +127,7 @@ submit_message() { translated_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_1.fhir" download_from_azurite "$translated_blob_name" "$translated_file_path" - metadata_response=$(./ti.sh metadata.hurl -i "$inbound_submission_id") + metadata_response=$(./ti.sh metadata -i "$inbound_submission_id") outbound_submission_id=$(echo "$metadata_response" | jq -r '.issue[] | select(.details.text == "outbound submission id") | .diagnostics') transformed_blob_name="receive/flexion.etor-service-sender/$outbound_submission_id.fhir" diff --git a/scripts/hurl/rs.sh b/scripts/hurl/rs.sh index 15515d6af..60ea8e00d 100755 --- a/scripts/hurl/rs.sh +++ b/scripts/hurl/rs.sh @@ -16,7 +16,7 @@ verbose="" submission_id="" show_help() { - echo "Usage: $(basename $0) [OPTIONS]" + echo "Usage: $(basename $0) [OPTIONS]" echo echo "Options:" echo " -f The path to the hl7/fhir file to submit, relative the root path (Required for waters API)" @@ -31,9 +31,9 @@ show_help() { echo " -h Display this help and exit" } -# Check if required HURL_FILE is provided +# Check if required ENDPOINT_NAME is provided if [ $# -eq 0 ]; then - echo "Error: Missing required argument " + echo "Error: Missing required argument " show_help exit 1 fi @@ -44,8 +44,8 @@ if [ "$1" = "-h" ]; then exit 0 fi -hurl_file=rs/"$1" # Assign the first argument to hurl_file -shift # Remove the first argument from the list of arguments +endpoint_name=rs/"$1".hurl # Assign the first argument to endpoint_name +shift # Remove the first argument from the list of arguments while getopts ':f:r:t:e:c:s:x:i:vh' opt; do case "$opt" in @@ -127,5 +127,5 @@ hurl \ --variable jwt=$(jwt encode --exp='+5min' --jti $(uuidgen) --alg RS256 -k $client_id.$client_sender -i $client_id.$client_sender -s $client_id.$client_sender -a $host --no-iat -S @$secret) \ $submission_id \ $verbose \ - $hurl_file \ + $endpoint_name \ $@ diff --git a/scripts/hurl/ti.sh b/scripts/hurl/ti.sh index e38d7a506..a798d43b5 100755 --- a/scripts/hurl/ti.sh +++ b/scripts/hurl/ti.sh @@ -13,7 +13,7 @@ client=report-stream verbose="" show_help() { - echo "Usage: $(basename $0) [OPTIONS]" + echo "Usage: $(basename $0) [OPTIONS]" echo echo "Options:" echo " -f The path to the hl7/fhir file to submit, relative the root path (Required for orders and results APIs)" @@ -26,9 +26,9 @@ show_help() { echo " -h Display this help and exit" } -# Check if required HURL_FILE is provided +# Check if required ENDPOINT_NAME is provided if [ $# -eq 0 ]; then - echo "Error: Missing required argument " + echo "Error: Missing required argument " show_help exit 1 fi @@ -39,8 +39,8 @@ if [ "$1" = "-h" ]; then exit 0 fi -hurl_file=ti/"$1" # Assign the first argument to hurl_file -shift # Remove the first argument from the list of arguments +endpoint_name=ti/"$1.hurl" # Assign the first argument to endpoint_name +shift # Remove the first argument from the list of arguments while getopts ':f:r:e:c:j:i:vh' opt; do case "$opt" in @@ -111,5 +111,5 @@ hurl \ --variable jwt=$jwt \ $submission_id \ $verbose \ - $hurl_file \ + $endpoint_name \ $@ From 4edbce5443b269e3c6d4a78ab565706e1f9303da Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Wed, 23 Oct 2024 07:55:44 -0700 Subject: [PATCH 03/12] Refactored to make more generic and reusable functions --- scripts/hurl/message_submission_utils.sh | 89 +++++++++++++++++++++--- scripts/hurl/submit_message.sh | 37 ++++++++-- scripts/hurl/update_examples.sh | 7 +- 3 files changed, 117 insertions(+), 16 deletions(-) diff --git a/scripts/hurl/message_submission_utils.sh b/scripts/hurl/message_submission_utils.sh index fd28c599b..a6fd2383c 100755 --- a/scripts/hurl/message_submission_utils.sh +++ b/scripts/hurl/message_submission_utils.sh @@ -7,30 +7,94 @@ FILE_NAME_SUFFIX_STEP_3="_3_hl7_translation_final" AZURITE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;" # pragma: allowlist secret -RS_LOCAL_API="http://localhost:7071" -TI_LOCAL_API="http://localhost:8080" +LOCAL_HOST="localhost" +RS_STG_HOST="staging.prime.cdc.gov" +RS_PRD_HOST="prime.cdc.gov" +RS_LOCAL_PORT=7071 +TI_STG_HOST="cdcti-stg-api.azurewebsites.net" +TI_PRD_HOST="cdcti-prd-api.azurewebsites.net" +TI_LOCAL_PORT=8080 + +check_installed_commands() { + for cmd in "$@"; do + if ! command -v "$cmd" &>/dev/null; then + echo "$cmd could not be found. Please install $cmd to proceed." + exit 1 + fi + done +} -check_prerequisites() { - # Check if the RS and TO local APIs are reachable - local apis=("$RS_LOCAL_API" "$TI_LOCAL_API") - for service in "${apis[@]}"; do +check_apis() { + for service in "$@"; do if ! curl -s --head --fail "$service" | grep "200 OK" >/dev/null; then echo "The service at $service is not reachable" exit 1 fi done +} - # Check required CLI tools - local required_commands=("hurl" "jq" "az") - for cmd in "${required_commands[@]}"; do - if ! command -v "$cmd" &>/dev/null; then - echo "$cmd could not be found. Please install $cmd to proceed." +check_env_vars() { + local env_vars=("$@") + for var in "${env_vars[@]}"; do + if [ -z "${!var}" ]; then + echo "Error: Environment variable '$var' is not set" exit 1 fi done } +get_endpoint_url() { + local type=$1 + local env=$2 + local protocol host port + + case "$env" in + local) + protocol="http" + host="$LOCAL_HOST" + port=$([ "$type" = "rs" ] && echo "$RS_LOCAL_PORT" || echo "$TI_LOCAL_PORT") + ;; + staging) + protocol="https" + host=$([ "$type" = "rs" ] && echo "$RS_STG_HOST" || echo "$TI_STG_HOST") + port=443 + ;; + production) + protocol="https" + host=$([ "$type" = "rs" ] && echo "$RS_PRD_HOST" || echo "$TI_PRD_HOST") + port=443 + ;; + *) + echo "Error: Invalid environment '$env'" + show_help + exit 1 + ;; + esac + + echo "$protocol://$host:$port" +} + +generate_jwt() { + # requires: jwt-cli + local client_id=$1 + local client_sender=$2 + local host=$3 + local secret_path=$4 + + jwt encode \ + --exp='+5min' \ + --jti "$(uuidgen)" \ + --alg RS256 \ + -k "$client_id.$client_sender" \ + -i "$client_id.$client_sender" \ + -s "$client_id.$client_sender" \ + -a "$host" \ + --no-iat \ + -S "@$secret_path" +} + check_submission_status() { + # requires: hurl, jq local submission_id=$1 local timeout=180 # 3 minutes local retry_interval=10 # Retry every 10 seconds @@ -62,11 +126,13 @@ check_submission_status() { } extract_submission_id() { + # requires: jq local history_response=$1 echo "$history_response" | jq '.destinations[0].sentReports[0].externalName' | sed 's/.*-\([0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}\)-.*/\1/' } download_from_azurite() { + # requires: jq, az local blob_name=$1 local file_path=$2 @@ -89,6 +155,7 @@ download_from_azurite() { } submit_message() { + # requires: hurl, jq, az local file=$1 local message_file_path=$(dirname "$file") local message_file_name=$(basename "$file") diff --git a/scripts/hurl/submit_message.sh b/scripts/hurl/submit_message.sh index 83045be57..1f113629a 100755 --- a/scripts/hurl/submit_message.sh +++ b/scripts/hurl/submit_message.sh @@ -2,11 +2,40 @@ source ./message_submission_utils.sh -check_prerequisites +env="local" +file="" -if [ $# -eq 0 ]; then - echo "Usage: $0 /path/to/message.hl7" +show_usage() { + echo "Usage: $0 -f [-e ]" + echo "Options:" + echo " -f Message file path (required)" + echo " -e Environment (default: $env)" exit 1 +} + +# Parse command line arguments +while getopts "e:f:h" opt; do + case $opt in + e) env="$OPTARG" ;; + f) file="$OPTARG" ;; + h) show_usage ;; + \?) + echo "Invalid option: -$OPTARG" + show_usage + ;; + esac +done + +# Check if file was provided +if [ -z "$file" ]; then + show_usage fi -submit_message "$1" +rs_url=$(get_endpoint_url "rs" "$env") +ti_url=$(get_endpoint_url "ti" "$env") + +check_env_vars CDCTI_HOME +check_installed_commands hurl jq az +check_apis "$rs_url" "$ti_url" + +submit_message "$2" diff --git a/scripts/hurl/update_examples.sh b/scripts/hurl/update_examples.sh index f0c6ac3da..577f72c10 100755 --- a/scripts/hurl/update_examples.sh +++ b/scripts/hurl/update_examples.sh @@ -2,7 +2,12 @@ source ./message_submission_utils.sh -check_prerequisites +env=local +rs_url=$(get_endpoint_url "rs" "$env") +ti_url=$(get_endpoint_url "ti" "$env") + +check_installed_commands hurl jq az +check_apis "$rs_url" "$ti_url" find "$CDCTI_HOME/examples" -type f -name "*$FILE_NAME_SUFFIX_STEP_0.hl7" | while read -r file; do echo "-----------------------------------------------------------------------------------------------------------" From 6fef7377aa2c71481fedeed3000d1ddeec0ba7a2 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Wed, 23 Oct 2024 08:05:32 -0700 Subject: [PATCH 04/12] Renamed message_submission_utils.sh => utils.sh --- scripts/hurl/submit_message.sh | 2 +- scripts/hurl/update_examples.sh | 2 +- scripts/hurl/{message_submission_utils.sh => utils.sh} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename scripts/hurl/{message_submission_utils.sh => utils.sh} (100%) diff --git a/scripts/hurl/submit_message.sh b/scripts/hurl/submit_message.sh index 1f113629a..e77d2ea4e 100755 --- a/scripts/hurl/submit_message.sh +++ b/scripts/hurl/submit_message.sh @@ -1,6 +1,6 @@ #!/bin/bash -source ./message_submission_utils.sh +source ./utils.sh env="local" file="" diff --git a/scripts/hurl/update_examples.sh b/scripts/hurl/update_examples.sh index 577f72c10..6672cb58b 100755 --- a/scripts/hurl/update_examples.sh +++ b/scripts/hurl/update_examples.sh @@ -1,6 +1,6 @@ #!/bin/bash -source ./message_submission_utils.sh +source ./utils.sh env=local rs_url=$(get_endpoint_url "rs" "$env") diff --git a/scripts/hurl/message_submission_utils.sh b/scripts/hurl/utils.sh similarity index 100% rename from scripts/hurl/message_submission_utils.sh rename to scripts/hurl/utils.sh From 75b6555c4d1214bf32d8bb63b2a4b67606e23518 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Wed, 23 Oct 2024 08:05:58 -0700 Subject: [PATCH 05/12] Renamed message_submission_utils.sh => utils.sh --- scripts/hurl/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index cb84ee95f..b16ec993b 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -5,6 +5,7 @@ - [hurl](https://hurl.dev/) - [jq](https://jqlang.github.io/jq/) - [azure-cli](https://learn.microsoft.com/en-us/cli/azure/) +- [jwt-cli](https://github.com/mike-engel/jwt-cli) - `CDCTI_HOME` environment variable ([see here](../README.md)) ## Available Hurl Scripts @@ -138,6 +139,6 @@ Get Health info from local environment: ``` ./update_examples.sh ``` -- `message_submission_utils.sh`: utility functions for the previous scripts. It has functions to submit requests to RS, check the submission status throughout the whole flow, and downloading snapshots from azurite +- `utils.sh`: utility functions for the previous scripts. It has functions to submit requests to RS, check the submission status throughout the whole flow, and downloading snapshots from azurite **Note**: these scripts require both RS and TI to be running locally From bf580ceb3fec36e20df01cf799b937970ba80a0b Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:04:10 -0700 Subject: [PATCH 06/12] Refactored to extract functions and reuse utils + clean up --- scripts/hurl/README.md | 68 +++++++---- scripts/hurl/epic.sh | 12 +- scripts/hurl/rs.sh | 207 ++++++++++++++------------------ scripts/hurl/submit_message.sh | 59 ++++----- scripts/hurl/ti.sh | 182 ++++++++++++---------------- scripts/hurl/update_examples.sh | 4 +- scripts/hurl/utils.sh | 87 ++++++++------ 7 files changed, 298 insertions(+), 321 deletions(-) diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index b16ec993b..396f2ffe6 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -15,19 +15,25 @@ #### Usage ``` -Usage: ./rs.sh [OPTIONS] +Usage: rs.sh [OPTIONS] + +ENDPOINT_NAME: + The name of the endpoint to call (required) Options: - -f The path to the hl7/fhir file to submit, relative the root path (Required for waters API) - -r The root path to the hl7/fhir files (Default: $CDCTI_HOME/examples/) - -t The content type for the message (e.g. 'application/hl7-v2' or 'application/fhir+ndjson') (Default: application/hl7-v2) - -e [local | staging | production ] The environment to run the test in (Default: local) - -c The client id to use (Default: flexion) - -s The client sender to use (Default: simulated-sender) - -x The path to the client private key for the environment - -i The submissionId to call the history API with (Required for history API) - -v Verbose mode - -h Display this help and exit + -f Path to the hl7/fhir file to submit (Required for waters API) + -r Root path to the hl7/fhir files (Default: /Users/bbogado/Code/Flexion/CDC-TI/trusted-intermediary/examples/) + -t Content type for the message (Default: application/hl7-v2) + -e Environment: local|staging|production (Default: local) + -c Client ID (Default: flexion) + -s Client sender (Default: simulated-sender) + -x Path to the client private key + -i SubmissionId for history API + -v Verbose mode + -h Display this help and exit + +Environment Variables: + CDCTI_HOME Base directory for CDC TI repository (Required) ``` #### Examples @@ -67,45 +73,50 @@ Checking the history in staging for a submission id #### Usage ``` -Usage: ti.rs [OPTIONS] +Usage: ti.sh [OPTIONS] + +ENDPOINT_NAME: + The name of the endpoint to call (required) Options: - -f The path to the hl7/fhir file to submit, relative the root path (Required for orders and results APIs) - -r The root path to the hl7/fhir files (Default: $CDCTI_HOME/examples/) - -e [local | staging] The environment to run the test in (Default: local) - -c The client id to use (Default: report-stream) - -j The JWT to use for authentication - -i The submissionId to call the metadata API with (Required for metadata API) + -f Path to the hl7/fhir file to submit (Required for orders and results APIs) + -i Submission ID for metadata API (Required for orders, results and metadata API) + -r Root path to the hl7/fhir files (Default: /Users/bbogado/Code/Flexion/CDC-TI/trusted-intermediary/examples/) + -e Environment: local|staging (Default: local) + -j JWT token for authentication -v Verbose mode -h Display this help and exit + +Environment Variables: + CDCTI_HOME Base directory for CDC TI repository (Required) ``` #### Examples Submit an order to local environment: ``` -./ti.rs orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 +./ti.sh orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 ``` Submit an order to staging: ``` -./ti.rs orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -j eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ +./ti.sh orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -j eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ ``` Submit a result to local environment: ``` -./ti.rs results -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 +./ti.sh results -f Test/Results/002_AL_ORU_R01_NBS_Fully_Populated_1_hl7_translation.fhir -i 100 ``` Get metadata from local environment: ``` -./ti.rs metadata -i 100 +./ti.sh metadata -i 100 ``` Authenticate to local environment: ``` -./ti.rs auth +./ti.sh auth ``` Get OpenAPI docs from local environment: @@ -115,7 +126,7 @@ Get OpenAPI docs from local environment: Get Health info from local environment: ``` -./ti.rs health +./ti.sh health ``` ### Epic/UCSD @@ -127,13 +138,18 @@ Get Health info from local environment: #### Usage -`./epic.rs results` +`./epic.sh results` ## Local Submission Scripts - `submit_message.sh`: sends a HL7 message to a locally running RS instance. It also grabs the snapshots of the file in azurite after converting to FHIR, after applying transformations in TI, and after converting back to HL7. It copies these files to the same folder where the submitted file is ``` - ./submit_message.sh /path/to/message.hl7 + Usage: submit_message.sh -f [-e ] + + Options: + -f Message file path (required) + -e Environment: local|staging|production (Default: ) + -h Display this help and exit ``` - `update_examples.sh`: sends all the HL7 files with `_0_initial_message.hl7` suffix in the `/examples` folder to a locally running RS instance. As the previous script, it copies the snapshots at each stage ``` diff --git a/scripts/hurl/epic.sh b/scripts/hurl/epic.sh index 29d4f2e3b..57835798d 100755 --- a/scripts/hurl/epic.sh +++ b/scripts/hurl/epic.sh @@ -1,3 +1,7 @@ +#!/bin/bash + +source ./utils.sh + client= audience=https://epicproxy-np.et0502.epichosted.com/FhirProxy/oauth2/token secret=/path/to/ucsd-epic-private-key.pem @@ -5,9 +9,11 @@ root=$CDCTI_HOME/examples/CA/ fpath="$1" shift +jwt_token=$(generate_jwt "$client" "$audience" "$secret") || fail "Failed to generate JWT token" + hurl \ - --variable fpath=$fpath \ - --file-root $root \ - --variable jwt=$(jwt encode --exp='+5min' --jti $(uuidgen) --alg RS256 -k $client -i $client -s $client -a $audience --no-iat -S @$secret) \ + --variable "fpath=$fpath" \ + --file-root "$root" \ + --variable "jwt=$jwt_token" \ epic/results.hurl \ $@ diff --git a/scripts/hurl/rs.sh b/scripts/hurl/rs.sh index 60ea8e00d..e04ccb447 100755 --- a/scripts/hurl/rs.sh +++ b/scripts/hurl/rs.sh @@ -1,10 +1,8 @@ #!/bin/bash -# Check if $CDCTI_HOME is set -if [ -z "$CDCTI_HOME" ]; then - echo "Error: CDCTI_HOME is not set. Please set this environment variable before running the script." - exit 1 -fi +source ./utils.sh + +LOCAL_KEY_PATH="$CDCTI_HOME/mock_credentials/organization-trusted-intermediary-private-key-local.pem" # default values env=local @@ -12,120 +10,93 @@ root=$CDCTI_HOME/examples/ content_type=application/hl7-v2 client_id=flexion client_sender=simulated-sender -verbose="" -submission_id="" - -show_help() { - echo "Usage: $(basename $0) [OPTIONS]" - echo - echo "Options:" - echo " -f The path to the hl7/fhir file to submit, relative the root path (Required for waters API)" - echo " -r The root path to the hl7/fhir files (Default: $root)" - echo " -t The content type for the message (e.g. 'application/hl7-v2' or 'application/fhir+ndjson') (Default: $content_type)" - echo " -e [local | staging | production ] The environment to run the test in (Default: $env)" - echo " -c The client id to use (Default: $client_id)" - echo " -s The client sender to use (Default: $client_sender)" - echo " -x The path to the client private key for the environment" - echo " -i The submissionId to call the history API with (Required for history API)" - echo " -v Verbose mode" - echo " -h Display this help and exit" + +show_usage() { + cat < [OPTIONS] + +ENDPOINT_NAME: + The name of the endpoint to call (required) + +Options: + -f Path to the hl7/fhir file to submit (Required for waters API) + -r Root path to the hl7/fhir files (Default: $root) + -t Content type for the message (Default: $content_type) + -e Environment: local|staging|production (Default: $env) + -c Client ID (Default: $client_id) + -s Client sender (Default: $client_sender) + -x Path to the client private key + -i Submission ID for history API + -v Verbose mode + -h Display this help and exit + +Environment Variables: + CDCTI_HOME Base directory for CDC TI repository (Required) +EOF } -# Check if required ENDPOINT_NAME is provided -if [ $# -eq 0 ]; then - echo "Error: Missing required argument " - show_help - exit 1 -fi - -# Check if first argument is -h -if [ "$1" = "-h" ]; then - show_help - exit 0 -fi - -endpoint_name=rs/"$1".hurl # Assign the first argument to endpoint_name -shift # Remove the first argument from the list of arguments - -while getopts ':f:r:t:e:c:s:x:i:vh' opt; do - case "$opt" in - f) - fpath="$OPTARG" - ;; - r) - root="$OPTARG" - ;; - t) - content_type="$OPTARG" - ;; - e) - env="$OPTARG" - ;; - c) - client_id="$OPTARG" - ;; - s) - client_sender="$OPTARG" - ;; - x) - secret="$OPTARG" - ;; - i) - submission_id="--variable submissionid=$OPTARG" - ;; - v) - verbose="--verbose" - ;; - h) - show_help +parse_arguments() { + if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + show_usage exit 0 - ;; - :) - echo -e "Option requires an argument" - show_help - exit 1 - ;; - ?) - echo -e "Invalid command option" - show_help - exit 1 - ;; - esac -done -shift "$(($OPTIND - 1))" - -if [ "$env" = "local" ]; then - host=localhost - url=http://$host:7071 - if [ -z "$secret" ] && [ "$client_id" = "flexion" ]; then - secret="$CDCTI_HOME/mock_credentials/organization-trusted-intermediary-private-key-local.pem" fi -elif [ "$env" = "staging" ]; then - host=staging.prime.cdc.gov - url=https://$host:443 -elif [ "$env" = "production" ]; then - host=prime.cdc.gov - url=https://$host:443 -else - echo "Error: Invalid environment $env" - show_help - exit 1 -fi - -if [ -z "$secret" ]; then - echo "Error: Please provide the private key for $client_id" - exit 1 -fi - -hurl \ - --variable fpath=$fpath \ - --file-root $root \ - --variable url=$url \ - --variable content-type=$content_type \ - --variable client-id=$client_id \ - --variable client-sender=$client_sender \ - --variable jwt=$(jwt encode --exp='+5min' --jti $(uuidgen) --alg RS256 -k $client_id.$client_sender -i $client_id.$client_sender -s $client_id.$client_sender -a $host --no-iat -S @$secret) \ - $submission_id \ - $verbose \ - $endpoint_name \ - $@ + + [ $# -eq 0 ] && fail "Missing required argument " + endpoint_name="rs/$1.hurl" + shift # Remove endpoint name from args + + while getopts ':f:r:t:e:c:s:x:i:v' opt; do + case "$opt" in + f) fpath="$OPTARG" ;; + r) root="$OPTARG" ;; + t) content_type="$OPTARG" ;; + e) env="$OPTARG" ;; + c) client_id="$OPTARG" ;; + s) client_sender="$OPTARG" ;; + x) secret="$OPTARG" ;; + i) submission_id="--variable submissionid=$OPTARG" ;; + v) verbose="--verbose" ;; + ?) fail "Invalid option -$OPTARG" ;; + esac + done + + shift "$(($OPTIND - 1))" + remaining_args="$*" +} + +setup_credentials() { + if [ -z "$secret" ] && [ "$client_id" = "flexion" ] && [ "$env" = "local" ]; then + if [ -f "$LOCAL_KEY_PATH" ]; then + secret="$LOCAL_KEY_PATH" + else + fail "Local environment key not found at: $LOCAL_KEY_PATH" + fi + fi + + [ -n "$secret" ] || fail "Please provide the private key for $client_id" + [ -f "$secret" ] || fail "Private key file not found: $secret" +} + +run_hurl_command() { + url=$(get_api_url "$env" "rs") + host=$(extract_host_from_url "$url") + jwt_token=$(generate_jwt "$client_id.$client_sender" "$host" "$secret") || fail "Failed to generate JWT token" + + hurl \ + --variable "fpath=$fpath" \ + --file-root "$root" \ + --variable "url=$url" \ + --variable "content-type=$content_type" \ + --variable "client-id=$client_id" \ + --variable "client-sender=$client_sender" \ + --variable "jwt=$jwt_token" \ + ${submission_id:-} \ + ${verbose:-} \ + "$endpoint_name" \ + ${remaining_args:+$remaining_args} +} + +check_env_vars CDCTI_HOME +parse_arguments "$@" +setup_credentials +run_hurl_command diff --git a/scripts/hurl/submit_message.sh b/scripts/hurl/submit_message.sh index e77d2ea4e..a9f34bac3 100755 --- a/scripts/hurl/submit_message.sh +++ b/scripts/hurl/submit_message.sh @@ -6,36 +6,41 @@ env="local" file="" show_usage() { - echo "Usage: $0 -f [-e ]" - echo "Options:" - echo " -f Message file path (required)" - echo " -e Environment (default: $env)" + cat < [-e ] + +Options: + -f Message file path (required) + -e Environment: local|staging|production (Default: $DEFAULT_ENV) + -h Display this help and exit +EOF exit 1 } -# Parse command line arguments -while getopts "e:f:h" opt; do - case $opt in - e) env="$OPTARG" ;; - f) file="$OPTARG" ;; - h) show_usage ;; - \?) - echo "Invalid option: -$OPTARG" - show_usage - ;; +parse_arguments() { + # Show help if no arguments + [ $# -eq 0 ] && show_usage + + while getopts "e:f:h" opt; do + case $opt in + e) env="$OPTARG" ;; + f) file="$OPTARG" ;; + h) show_usage ;; + ?) fail "Invalid option: -$OPTARG" ;; + esac + done + + [ -z "$file" ] && fail "File (-f) is required" + [ -f "$file" ] || fail "File not found: $file" + + # Validate environment + case "$env" in + local | staging | production) ;; + *) fail "Invalid environment '$env'. Must be local, staging, or production" ;; esac -done - -# Check if file was provided -if [ -z "$file" ]; then - show_usage -fi - -rs_url=$(get_endpoint_url "rs" "$env") -ti_url=$(get_endpoint_url "ti" "$env") +} -check_env_vars CDCTI_HOME check_installed_commands hurl jq az -check_apis "$rs_url" "$ti_url" - -submit_message "$2" +check_apis "$(get_api_url "$env" "rs")" "$(get_api_url "$env" "ti")" +parse_arguments "$@" +submit_message "$file" diff --git a/scripts/hurl/ti.sh b/scripts/hurl/ti.sh index a798d43b5..2ad86704c 100755 --- a/scripts/hurl/ti.sh +++ b/scripts/hurl/ti.sh @@ -1,115 +1,89 @@ #!/bin/bash -# Check if $CDCTI_HOME is set -if [ -z "$CDCTI_HOME" ]; then - echo "Error: CDCTI_HOME is not set. Please set this environment variable before running the script." - exit 1 -fi +source ./utils.sh + +LOCAL_JWT_PATH="$CDCTI_HOME/mock_credentials/report-stream-valid-token.jwt" # default values env=local root=$CDCTI_HOME/examples/ client=report-stream -verbose="" - -show_help() { - echo "Usage: $(basename $0) [OPTIONS]" - echo - echo "Options:" - echo " -f The path to the hl7/fhir file to submit, relative the root path (Required for orders and results APIs)" - echo " -i The submissionId to call the metadata API with (Required for orders, results and metadata API)" - echo " -r The root path to the hl7/fhir files (Default: $root)" - echo " -e [local | staging] The environment to run the test in (Default: $env)" - echo " -c The client id to use (Default: $client)" - echo " -j The JWT to use for authentication" - echo " -v Verbose mode" - echo " -h Display this help and exit" + +show_usage() { + cat < [OPTIONS] + +ENDPOINT_NAME: + The name of the endpoint to call (required) + +Options: + -f Path to the hl7/fhir file to submit (Required for orders and results APIs) + -i Submission ID for metadata API (Required for orders, results and metadata API) + -r Root path to the hl7/fhir files (Default: $root) + -e Environment: local|staging (Default: $env) + -j JWT token for authentication + -v Verbose mode + -h Display this help and exit + +Environment Variables: + CDCTI_HOME Base directory for CDC TI repository (Required) +EOF } -# Check if required ENDPOINT_NAME is provided -if [ $# -eq 0 ]; then - echo "Error: Missing required argument " - show_help - exit 1 -fi - -# Check if first argument is -h -if [ "$1" = "-h" ]; then - show_help - exit 0 -fi - -endpoint_name=ti/"$1.hurl" # Assign the first argument to endpoint_name -shift # Remove the first argument from the list of arguments - -while getopts ':f:r:e:c:j:i:vh' opt; do - case "$opt" in - f) - fpath="$OPTARG" - ;; - i) - submission_id="--variable submissionid=$OPTARG" - ;; - r) - root="$OPTARG" - ;; - e) - env="$OPTARG" - ;; - c) - client="$OPTARG" - ;; - j) - jwt="$OPTARG" - ;; - v) - verbose="--verbose" - ;; - h) - show_help +parse_arguments() { + if [ $# -eq 0 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + show_usage exit 0 - ;; - :) - echo -e "Option requires an argument" - show_help - exit 1 - ;; - ?) - echo -e "Invalid command option" - show_help - exit 1 - ;; - esac -done -shift "$(($OPTIND - 1))" - -if [ "$env" = "local" ]; then - host=localhost - url=http://$host:8080 - if [ -z "$jwt" ] && [ "$client" = "report-stream" ]; then - jwt=$(cat "$CDCTI_HOME/mock_credentials/report-stream-valid-token.jwt") fi -elif [ "$env" = "staging" ]; then - host=cdcti-stg-api.azurewebsites.net - url=https://$host:443 -else - echo "Error: Invalid environment $env" - show_help - exit 1 -fi - -if [ -z "$jwt" ]; then - echo "Error: Please provide the JWT for $client" - exit 1 -fi - -hurl \ - --variable fpath=$fpath \ - --file-root $root \ - --variable url=$url \ - --variable client=$client \ - --variable jwt=$jwt \ - $submission_id \ - $verbose \ - $endpoint_name \ - $@ + + [ $# -eq 0 ] && fail "Missing required argument " + endpoint_name="ti/$1.hurl" + shift # Remove endpoint name from args + + while getopts ':f:r:e:j:i:v' opt; do + case "$opt" in + f) fpath="$OPTARG" ;; + i) submission_id="--variable submissionid=$OPTARG" ;; + r) root="$OPTARG" ;; + e) env="$OPTARG" ;; + j) jwt="$OPTARG" ;; + v) verbose="--verbose" ;; + ?) fail "Invalid option -$OPTARG" ;; + esac + done + + shift "$(($OPTIND - 1))" + remaining_args="$*" +} + +setup_credentials() { + if [ -z "$jwt" ] && [ "$client" = "report-stream" ] && [ "$env" = "local" ]; then + if [ -f "$LOCAL_JWT_PATH" ]; then + jwt=$(cat "$LOCAL_JWT_PATH") + else + fail "Local JWT file not found at: $LOCAL_JWT_PATH" + fi + fi + + [ -n "$jwt" ] || fail "Please provide the JWT for $client" +} + +run_hurl_command() { + url=$(get_api_url "$env" "ti") + + hurl \ + --variable "fpath=$fpath" \ + --file-root "$root" \ + --variable "url=$url" \ + --variable "client=$client" \ + --variable "jwt=$jwt" \ + ${submission_id:-} \ + ${verbose:-} \ + "$endpoint_name" \ + ${remaining_args:+$remaining_args} +} + +check_env_vars CDCTI_HOME +parse_arguments "$@" +setup_credentials +run_hurl_command diff --git a/scripts/hurl/update_examples.sh b/scripts/hurl/update_examples.sh index 6672cb58b..ef9aea775 100755 --- a/scripts/hurl/update_examples.sh +++ b/scripts/hurl/update_examples.sh @@ -3,11 +3,9 @@ source ./utils.sh env=local -rs_url=$(get_endpoint_url "rs" "$env") -ti_url=$(get_endpoint_url "ti" "$env") check_installed_commands hurl jq az -check_apis "$rs_url" "$ti_url" +check_apis "$(get_api_url "$env" "rs")" "$(get_api_url "$env" "ti")" find "$CDCTI_HOME/examples" -type f -name "*$FILE_NAME_SUFFIX_STEP_0.hl7" | while read -r file; do echo "-----------------------------------------------------------------------------------------------------------" diff --git a/scripts/hurl/utils.sh b/scripts/hurl/utils.sh index a6fd2383c..1ceb295d3 100755 --- a/scripts/hurl/utils.sh +++ b/scripts/hurl/utils.sh @@ -7,13 +7,17 @@ FILE_NAME_SUFFIX_STEP_3="_3_hl7_translation_final" AZURITE_CONNECTION_STRING="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;" # pragma: allowlist secret -LOCAL_HOST="localhost" -RS_STG_HOST="staging.prime.cdc.gov" -RS_PRD_HOST="prime.cdc.gov" -RS_LOCAL_PORT=7071 -TI_STG_HOST="cdcti-stg-api.azurewebsites.net" -TI_PRD_HOST="cdcti-prd-api.azurewebsites.net" -TI_LOCAL_PORT=8080 +RS_API_LCL_URL="http://localhost:7071" +RS_API_STG_URL="https://staging.prime.cdc.gov:443" +RS_API_PRD_URL="https://prime.cdc.gov:443" +TI_API_LCL_URL="http://localhost:8080" +TI_API_STG_URL="https://cdcti-stg-api.azurewebsites.net:443" +TI_API_PRD_URL="https://cdcti-prd-api.azurewebsites.net:443" + +fail() { + echo "Error: $1" >&2 + exit 1 +} check_installed_commands() { for cmd in "$@"; do @@ -43,52 +47,55 @@ check_env_vars() { done } -get_endpoint_url() { - local type=$1 - local env=$2 - local protocol host port - - case "$env" in - local) - protocol="http" - host="$LOCAL_HOST" - port=$([ "$type" = "rs" ] && echo "$RS_LOCAL_PORT" || echo "$TI_LOCAL_PORT") - ;; - staging) - protocol="https" - host=$([ "$type" = "rs" ] && echo "$RS_STG_HOST" || echo "$TI_STG_HOST") - port=443 - ;; - production) - protocol="https" - host=$([ "$type" = "rs" ] && echo "$RS_PRD_HOST" || echo "$TI_PRD_HOST") - port=443 +get_api_url() { + local env=$1 + local type=$2 + + case "$type" in + "rs") + case "$env" in + "local") echo $RS_API_LCL_URL ;; + "staging") echo $RS_API_STG_URL ;; + "production") echo $RS_API_PRD_URL ;; + *) + echo "Invalid environment: $env" >&2 + exit 1 + ;; + esac ;; - *) - echo "Error: Invalid environment '$env'" - show_help - exit 1 + "ti") + case "$env" in + "local") echo $TI_API_LCL_URL ;; + "staging") echo $TI_API_STG_URL ;; + "production") echo $TI_API_PRD_URL ;; + *) + echo "Invalid environment: $env" >&2 + exit 1 + ;; + esac ;; esac +} - echo "$protocol://$host:$port" +extract_host_from_url() { + local url=$1 + echo "$url" | sed 's|^.*://\([^/:]*\)[:/].*|\1|' } generate_jwt() { # requires: jwt-cli - local client_id=$1 - local client_sender=$2 - local host=$3 - local secret_path=$4 + local client=$1 + local audience=$2 + local secret_path=$3 jwt encode \ --exp='+5min' \ --jti "$(uuidgen)" \ --alg RS256 \ - -k "$client_id.$client_sender" \ - -i "$client_id.$client_sender" \ - -s "$client_id.$client_sender" \ - -a "$host" \ + -k "$client" \ + -i "$client" \ + -s "$client" \ + -a "$audience" \ --no-iat \ -S "@$secret_path" } From 35e2a9595dacb6f2cc4f675266edf4866970cde2 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:22:07 -0700 Subject: [PATCH 07/12] [WIP] Added handling of deployed environments for submit_message.sh --- scripts/hurl/rs.sh | 8 +-- scripts/hurl/submit_message.sh | 36 +++++++++- scripts/hurl/ti.sh | 8 +-- scripts/hurl/update_examples.sh | 2 +- scripts/hurl/utils.sh | 114 +++++++++++++++++++------------- 5 files changed, 109 insertions(+), 59 deletions(-) diff --git a/scripts/hurl/rs.sh b/scripts/hurl/rs.sh index e04ccb447..4755163a4 100755 --- a/scripts/hurl/rs.sh +++ b/scripts/hurl/rs.sh @@ -2,8 +2,6 @@ source ./utils.sh -LOCAL_KEY_PATH="$CDCTI_HOME/mock_credentials/organization-trusted-intermediary-private-key-local.pem" - # default values env=local root=$CDCTI_HOME/examples/ @@ -66,10 +64,10 @@ parse_arguments() { setup_credentials() { if [ -z "$secret" ] && [ "$client_id" = "flexion" ] && [ "$env" = "local" ]; then - if [ -f "$LOCAL_KEY_PATH" ]; then - secret="$LOCAL_KEY_PATH" + if [ -f "$TI_LOCAL_PRIVATE_KEY_PATH" ]; then + secret="$TI_LOCAL_PRIVATE_KEY_PATH" else - fail "Local environment key not found at: $LOCAL_KEY_PATH" + fail "Local environment key not found at: $TI_LOCAL_PRIVATE_KEY_PATH" fi fi diff --git a/scripts/hurl/submit_message.sh b/scripts/hurl/submit_message.sh index a9f34bac3..9c5c7318c 100755 --- a/scripts/hurl/submit_message.sh +++ b/scripts/hurl/submit_message.sh @@ -12,6 +12,8 @@ Usage: $(basename "$0") -f [-e ] Options: -f Message file path (required) -e Environment: local|staging|production (Default: $DEFAULT_ENV) + -x Path to the client private key for authentication with RS API + -j JWT token for authentication with TI API -h Display this help and exit EOF exit 1 @@ -21,10 +23,12 @@ parse_arguments() { # Show help if no arguments [ $# -eq 0 ] && show_usage - while getopts "e:f:h" opt; do + while getopts "e:f:x:j:h" opt; do case $opt in e) env="$OPTARG" ;; f) file="$OPTARG" ;; + x) private_key="$OPTARG" ;; + j) jwt="$OPTARG" ;; h) show_usage ;; ?) fail "Invalid option: -$OPTARG" ;; esac @@ -40,7 +44,35 @@ parse_arguments() { esac } +setup_credentials() { + # Set default credentials for local environment + if [ "$env" = "local" ] && [ -z "$jwt" ]; then + if [ -f "$RS_LOCAL_JWT_PATH" ]; then + jwt=$(cat "$RS_LOCAL_JWT_PATH") + else + fail "Local JWT file not found at: $RS_LOCAL_JWT_PATH" + fi + fi + + if [ "$env" = "local" ] && [ -z "$private_key" ]; then + if [ -f "$TI_LOCAL_PRIVATE_KEY_PATH" ]; then + private_key="$TI_LOCAL_PRIVATE_KEY_PATH" + else + fail "Local environment key not found at: $TI_LOCAL_PRIVATE_KEY_PATH" + fi + fi + + # Validate credentials if not local environment + if [ "$env" != "local" ]; then + [ -z "$jwt" ] && fail "JWT (-j) is required for non-local environments" + [ -z "$private_key" ] && fail "Private key (-x) is required for non-local environments" + fi + + [ -n "$private_key" ] && [ ! -f "$private_key" ] && fail "Private key file not found: $private_key" +} + check_installed_commands hurl jq az check_apis "$(get_api_url "$env" "rs")" "$(get_api_url "$env" "ti")" parse_arguments "$@" -submit_message "$file" +setup_credentials +submit_message "$env" "$file" "$private_key" "$jwt" diff --git a/scripts/hurl/ti.sh b/scripts/hurl/ti.sh index 2ad86704c..dffe48617 100755 --- a/scripts/hurl/ti.sh +++ b/scripts/hurl/ti.sh @@ -2,8 +2,6 @@ source ./utils.sh -LOCAL_JWT_PATH="$CDCTI_HOME/mock_credentials/report-stream-valid-token.jwt" - # default values env=local root=$CDCTI_HOME/examples/ @@ -58,10 +56,10 @@ parse_arguments() { setup_credentials() { if [ -z "$jwt" ] && [ "$client" = "report-stream" ] && [ "$env" = "local" ]; then - if [ -f "$LOCAL_JWT_PATH" ]; then - jwt=$(cat "$LOCAL_JWT_PATH") + if [ -f "$RS_LOCAL_JWT_PATH" ]; then + jwt=$(cat "$RS_LOCAL_JWT_PATH") else - fail "Local JWT file not found at: $LOCAL_JWT_PATH" + fail "Local JWT file not found at: $RS_LOCAL_JWT_PATH" fi fi diff --git a/scripts/hurl/update_examples.sh b/scripts/hurl/update_examples.sh index ef9aea775..cae907567 100755 --- a/scripts/hurl/update_examples.sh +++ b/scripts/hurl/update_examples.sh @@ -10,5 +10,5 @@ check_apis "$(get_api_url "$env" "rs")" "$(get_api_url "$env" "ti")" find "$CDCTI_HOME/examples" -type f -name "*$FILE_NAME_SUFFIX_STEP_0.hl7" | while read -r file; do echo "-----------------------------------------------------------------------------------------------------------" echo "Submitting message: $file" - submit_message "$file" + submit_message "$env" "$file" "$TI_LOCAL_PRIVATE_KEY_PATH" "$(cat "$RS_LOCAL_JWT_PATH")" done diff --git a/scripts/hurl/utils.sh b/scripts/hurl/utils.sh index 1ceb295d3..bd7652aff 100755 --- a/scripts/hurl/utils.sh +++ b/scripts/hurl/utils.sh @@ -14,6 +14,9 @@ TI_API_LCL_URL="http://localhost:8080" TI_API_STG_URL="https://cdcti-stg-api.azurewebsites.net:443" TI_API_PRD_URL="https://cdcti-prd-api.azurewebsites.net:443" +TI_LOCAL_PRIVATE_KEY_PATH="$CDCTI_HOME/mock_credentials/organization-trusted-intermediary-private-key-local.pem" +RS_LOCAL_JWT_PATH="$CDCTI_HOME/mock_credentials/report-stream-valid-token.jwt" + fail() { echo "Error: $1" >&2 exit 1 @@ -100,16 +103,48 @@ generate_jwt() { -S "@$secret_path" } +extract_submission_id() { + # requires: jq + local history_response=$1 + echo "$history_response" | jq '.destinations[0].sentReports[0].externalName' | sed 's/.*-\([0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}\)-.*/\1/' +} + +download_from_azurite() { + # requires: jq, az + local blob_name=$1 + local file_path=$2 + + echo " Downloading from blob storage '$blob_name' to '$file_path'" + az storage blob download \ + --container-name "reports" \ + --name "$blob_name" \ + --file "$file_path" \ + --connection-string "$AZURITE_CONNECTION_STRING" \ + --output none || { + echo "Download failed for blob '$blob_name'." + exit 1 + } + + if [[ "$file_path" == *.fhir ]]; then + echo " Formatting the content for: $file_path" + formatted_content=$(jq '.' "$file_path") + echo "$formatted_content" >"$file_path" + fi +} + check_submission_status() { # requires: hurl, jq - local submission_id=$1 + local env=$1 + local submission_id=$2 + local private_key=$3 + local timeout=180 # 3 minutes local retry_interval=10 # Retry every 10 seconds start_time=$(date +%s) while true; do - history_response=$(./rs.sh history -i "$submission_id") + history_response=$(./rs.sh history -i "$submission_id" -e "$env" -x "$private_key") overall_status=$(echo "$history_response" | jq -r '.overallStatus') echo -n " Status: $overall_status" @@ -132,43 +167,20 @@ check_submission_status() { done } -extract_submission_id() { - # requires: jq - local history_response=$1 - echo "$history_response" | jq '.destinations[0].sentReports[0].externalName' | sed 's/.*-\([0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}\)-.*/\1/' -} - -download_from_azurite() { - # requires: jq, az - local blob_name=$1 - local file_path=$2 - - echo " Downloading from blob storage '$blob_name' to '$file_path'" - az storage blob download \ - --container-name "reports" \ - --name "$blob_name" \ - --file "$file_path" \ - --connection-string "$AZURITE_CONNECTION_STRING" \ - --output none || { - echo "Download failed for blob '$blob_name'." - exit 1 - } - - if [[ "$file_path" == *.fhir ]]; then - echo " Formatting the content for: $file_path" - formatted_content=$(jq '.' "$file_path") - echo "$formatted_content" >"$file_path" - fi -} - submit_message() { # requires: hurl, jq, az - local file=$1 + local env=$1 + local file=$2 + local private_key=$3 + local jwt=$4 + local message_file_path=$(dirname "$file") local message_file_name=$(basename "$file") local message_base_name="${message_file_name%.hl7}" message_base_name="${message_base_name%"$FILE_NAME_SUFFIX_STEP_0"}" + echo "Debug: Processing file '$file' in environment '$env'" + msh9=$(awk -F'|' '/^MSH/ { print $9 }' "$file") case "$msh9" in "ORU^R01^ORU_R01") @@ -187,35 +199,45 @@ submit_message() { echo "Assuming receivers are '$first_leg_receiver' and '$second_leg_receiver' because of MSH-9 value '$msh9'" - waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path") + waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path" -e "$env" -x "$private_key") submission_id=$(echo "$waters_response" | jq -r '.id') echo "[First leg] Checking submission status for ID: $submission_id" - if ! check_submission_status "$submission_id"; then + if ! check_submission_status "$env" "$submission_id" "$private_key"; then echo "Failed to deliver the first leg of the message. Skipping the next steps." return fi inbound_submission_id=$(extract_submission_id "$history_response") - translated_blob_name="ready/$first_leg_receiver/$inbound_submission_id.fhir" - translated_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_1.fhir" - download_from_azurite "$translated_blob_name" "$translated_file_path" - metadata_response=$(./ti.sh metadata -i "$inbound_submission_id") + if [ "$env" = "local" ]; then + translated_blob_name="ready/$first_leg_receiver/$inbound_submission_id.fhir" + translated_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_1.fhir" + echo "Debug: Downloading translated file in local environment" + download_from_azurite "$translated_blob_name" "$translated_file_path" + fi + + metadata_response=$(./ti.sh metadata -i "$inbound_submission_id" -e "$env" -j "$jwt") outbound_submission_id=$(echo "$metadata_response" | jq -r '.issue[] | select(.details.text == "outbound submission id") | .diagnostics') - transformed_blob_name="receive/flexion.etor-service-sender/$outbound_submission_id.fhir" - transformed_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_2.fhir" - download_from_azurite "$transformed_blob_name" "$transformed_file_path" + if [ "$env" = "local" ]; then + transformed_blob_name="receive/flexion.etor-service-sender/$outbound_submission_id.fhir" + transformed_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_2.fhir" + echo "Debug: Downloading transformed file in local environment" + download_from_azurite "$transformed_blob_name" "$transformed_file_path" + fi echo "[Second leg] Checking submission status for ID: $outbound_submission_id" - if ! check_submission_status "$outbound_submission_id"; then + if ! check_submission_status "$env" "$outbound_submission_id" "$private_key"; then echo "Failed to deliver the second leg of the message. Skipping the next steps." return fi - final_submission_id=$(extract_submission_id "$history_response") - final_blob_name="ready/$second_leg_receiver/$final_submission_id.hl7" - final_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_3.hl7" - download_from_azurite "$final_blob_name" "$final_file_path" + if [ "$env" = "local" ]; then + final_submission_id=$(extract_submission_id "$history_response") + final_blob_name="ready/$second_leg_receiver/$final_submission_id.hl7" + final_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_3.hl7" + echo "Debug: Downloading final file in local environment" + download_from_azurite "$final_blob_name" "$final_file_path" + fi } From 9eb9621bce665cf59373556a4cbf339721906622 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:17:06 -0700 Subject: [PATCH 08/12] Refactored to handle deployed environments, change parameters and variable naming for consistency, and clean up --- scripts/hurl/README.md | 41 ++++++++++++++++--------------- scripts/hurl/rs.sh | 23 ++++++++++-------- scripts/hurl/submit_message.sh | 43 ++++++++++++++++----------------- scripts/hurl/ti.sh | 42 +++++++++++++++++++------------- scripts/hurl/update_examples.sh | 2 +- scripts/hurl/utils.sh | 25 +++++++++---------- 6 files changed, 93 insertions(+), 83 deletions(-) diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index 396f2ffe6..0c9436461 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -15,7 +15,7 @@ #### Usage ``` -Usage: rs.sh [OPTIONS] +Usage: ./rs.sh [OPTIONS] ENDPOINT_NAME: The name of the endpoint to call (required) @@ -27,8 +27,8 @@ Options: -e Environment: local|staging|production (Default: local) -c Client ID (Default: flexion) -s Client sender (Default: simulated-sender) - -x Path to the client private key - -i SubmissionId for history API + -k Path to the client private key + -i Submission ID for history API -v Verbose mode -h Display this help and exit @@ -53,7 +53,7 @@ Sending a result to local environment Sending an order to staging ``` -./rs.sh waters -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -x /path/to/staging/private/key +./rs.sh waters -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -k /path/to/client/staging/private/key ``` Checking the history in local environment for a submission id @@ -65,7 +65,7 @@ Checking the history in local environment for a submission id Checking the history in staging for a submission id ``` -./rs.sh history -i 100 -e staging -x /path/to/staging/private/key +./rs.sh history -i 100 -e staging -k /path/to/client/staging/private/key ``` ### CDC Intermediary @@ -73,22 +73,23 @@ Checking the history in staging for a submission id #### Usage ``` -Usage: ti.sh [OPTIONS] +Usage: ./ti.sh [OPTIONS] ENDPOINT_NAME: The name of the endpoint to call (required) Options: - -f Path to the hl7/fhir file to submit (Required for orders and results APIs) - -i Submission ID for metadata API (Required for orders, results and metadata API) - -r Root path to the hl7/fhir files (Default: /Users/bbogado/Code/Flexion/CDC-TI/trusted-intermediary/examples/) - -e Environment: local|staging (Default: local) - -j JWT token for authentication - -v Verbose mode - -h Display this help and exit + -f Path to the hl7/fhir file to submit (Required for orders and results APIs) + -r Root path to the hl7/fhir files (Default: /Users/bbogado/Code/Flexion/CDC-TI/trusted-intermediary/examples/) + -e Environment: local|staging (Default: local) + -c Client ID to create JWT with (Default: report-stream) + -k Path to the client private key + -i Submission ID for metadata API (Required for orders, results and metadata API) + -v Verbose mode + -h Display this help and exit Environment Variables: - CDCTI_HOME Base directory for CDC TI repository (Required) + CDCTI_HOME Base directory for CDC TI repository (Required) ``` #### Examples @@ -100,7 +101,7 @@ Submit an order to local environment: Submit an order to staging: ``` -./ti.sh orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -j eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ +./ti.sh orders -f Test/Orders/003_AL_ORM_O01_NBS_Fully_Populated_0_initial_message.hl7 -e staging -k /path/to/client/staging/private/key ``` @@ -140,16 +141,18 @@ Get Health info from local environment: `./epic.sh results` -## Local Submission Scripts +## Submission Scripts - `submit_message.sh`: sends a HL7 message to a locally running RS instance. It also grabs the snapshots of the file in azurite after converting to FHIR, after applying transformations in TI, and after converting back to HL7. It copies these files to the same folder where the submitted file is ``` Usage: submit_message.sh -f [-e ] Options: - -f Message file path (required) - -e Environment: local|staging|production (Default: ) - -h Display this help and exit + -f Message file path (required) + -e Environment: local|staging|production (Default: ) + -x Path to the client private key for authentication with RS API + -z Path to the client private key authentication with TI API + -h Display this help and exit ``` - `update_examples.sh`: sends all the HL7 files with `_0_initial_message.hl7` suffix in the `/examples` folder to a locally running RS instance. As the previous script, it copies the snapshots at each stage ``` diff --git a/scripts/hurl/rs.sh b/scripts/hurl/rs.sh index 4755163a4..c8933f3f7 100755 --- a/scripts/hurl/rs.sh +++ b/scripts/hurl/rs.sh @@ -23,7 +23,7 @@ Options: -e Environment: local|staging|production (Default: $env) -c Client ID (Default: $client_id) -s Client sender (Default: $client_sender) - -x Path to the client private key + -k Path to the client private key -i Submission ID for history API -v Verbose mode -h Display this help and exit @@ -43,7 +43,7 @@ parse_arguments() { endpoint_name="rs/$1.hurl" shift # Remove endpoint name from args - while getopts ':f:r:t:e:c:s:x:i:v' opt; do + while getopts ':f:r:t:e:c:s:k:i:v' opt; do case "$opt" in f) fpath="$OPTARG" ;; r) root="$OPTARG" ;; @@ -51,7 +51,7 @@ parse_arguments() { e) env="$OPTARG" ;; c) client_id="$OPTARG" ;; s) client_sender="$OPTARG" ;; - x) secret="$OPTARG" ;; + k) private_key="$OPTARG" ;; i) submission_id="--variable submissionid=$OPTARG" ;; v) verbose="--verbose" ;; ?) fail "Invalid option -$OPTARG" ;; @@ -63,22 +63,25 @@ parse_arguments() { } setup_credentials() { - if [ -z "$secret" ] && [ "$client_id" = "flexion" ] && [ "$env" = "local" ]; then - if [ -f "$TI_LOCAL_PRIVATE_KEY_PATH" ]; then - secret="$TI_LOCAL_PRIVATE_KEY_PATH" + if [ -z "$private_key" ] && [ "$client_id" = "flexion" ] && [ "$env" = "local" ]; then + if [ -f "$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" ]; then + private_key="$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" else - fail "Local environment key not found at: $TI_LOCAL_PRIVATE_KEY_PATH" + fail "Local environment client private key not found at: $RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" fi fi - [ -n "$secret" ] || fail "Please provide the private key for $client_id" - [ -f "$secret" ] || fail "Private key file not found: $secret" + if [ "$env" != "local" ]; then + [ -z "$private_key" ] && fail "Client private key (-k) is required for non-local environments" + fi + + [ ! -f "$private_key" ] && fail "Client private key file not found: $private_key" } run_hurl_command() { url=$(get_api_url "$env" "rs") host=$(extract_host_from_url "$url") - jwt_token=$(generate_jwt "$client_id.$client_sender" "$host" "$secret") || fail "Failed to generate JWT token" + jwt_token=$(generate_jwt "$client_id.$client_sender" "$host" "$private_key") || fail "Failed to generate JWT token" hurl \ --variable "fpath=$fpath" \ diff --git a/scripts/hurl/submit_message.sh b/scripts/hurl/submit_message.sh index 9c5c7318c..24f5512de 100755 --- a/scripts/hurl/submit_message.sh +++ b/scripts/hurl/submit_message.sh @@ -3,18 +3,17 @@ source ./utils.sh env="local" -file="" show_usage() { cat < [-e ] Options: - -f Message file path (required) - -e Environment: local|staging|production (Default: $DEFAULT_ENV) - -x Path to the client private key for authentication with RS API - -j JWT token for authentication with TI API - -h Display this help and exit + -f Message file path (required) + -e Environment: local|staging|production (Default: $DEFAULT_ENV) + -x Path to the client private key for authentication with RS API + -z Path to the client private key authentication with TI API + -h Display this help and exit EOF exit 1 } @@ -23,12 +22,12 @@ parse_arguments() { # Show help if no arguments [ $# -eq 0 ] && show_usage - while getopts "e:f:x:j:h" opt; do + while getopts "e:f:x:z:h" opt; do case $opt in e) env="$OPTARG" ;; f) file="$OPTARG" ;; - x) private_key="$OPTARG" ;; - j) jwt="$OPTARG" ;; + x) rs_client_private_key="$OPTARG" ;; + z) ti_client_private_key="$OPTARG" ;; h) show_usage ;; ?) fail "Invalid option: -$OPTARG" ;; esac @@ -46,33 +45,33 @@ parse_arguments() { setup_credentials() { # Set default credentials for local environment - if [ "$env" = "local" ] && [ -z "$jwt" ]; then - if [ -f "$RS_LOCAL_JWT_PATH" ]; then - jwt=$(cat "$RS_LOCAL_JWT_PATH") + if [ "$env" = "local" ] && [ -z "$rs_client_private_key" ]; then + if [ -f "$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" ]; then + rs_client_private_key="$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" else - fail "Local JWT file not found at: $RS_LOCAL_JWT_PATH" + fail "Local environment RS client private key not found at: $RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" fi fi - if [ "$env" = "local" ] && [ -z "$private_key" ]; then - if [ -f "$TI_LOCAL_PRIVATE_KEY_PATH" ]; then - private_key="$TI_LOCAL_PRIVATE_KEY_PATH" + if [ "$env" = "local" ] && [ -z "$ti_client_private_key" ]; then + if [ -f "$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" ]; then + ti_client_private_key="$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" else - fail "Local environment key not found at: $TI_LOCAL_PRIVATE_KEY_PATH" + fail "Local environment TI client private key not found at: $TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" fi fi - # Validate credentials if not local environment if [ "$env" != "local" ]; then - [ -z "$jwt" ] && fail "JWT (-j) is required for non-local environments" - [ -z "$private_key" ] && fail "Private key (-x) is required for non-local environments" + [ -z "$rs_client_private_key" ] && fail "RS client private key (-x) is required for non-local environments" + [ -z "$ti_client_private_key" ] && fail "TI client private key (-z) is required for non-local environments" fi - [ -n "$private_key" ] && [ ! -f "$private_key" ] && fail "Private key file not found: $private_key" + [ ! -f "$rs_client_private_key" ] && fail "RS client private key file not found: $rs_client_private_key" + [ ! -f "$ti_client_private_key" ] && fail "TI client private key file not found: $ti_client_private_key" } check_installed_commands hurl jq az check_apis "$(get_api_url "$env" "rs")" "$(get_api_url "$env" "ti")" parse_arguments "$@" setup_credentials -submit_message "$env" "$file" "$private_key" "$jwt" +submit_message "$env" "$file" "$rs_client_private_key" "$ti_client_private_key" diff --git a/scripts/hurl/ti.sh b/scripts/hurl/ti.sh index dffe48617..9a0207f53 100755 --- a/scripts/hurl/ti.sh +++ b/scripts/hurl/ti.sh @@ -15,16 +15,17 @@ ENDPOINT_NAME: The name of the endpoint to call (required) Options: - -f Path to the hl7/fhir file to submit (Required for orders and results APIs) - -i Submission ID for metadata API (Required for orders, results and metadata API) - -r Root path to the hl7/fhir files (Default: $root) - -e Environment: local|staging (Default: $env) - -j JWT token for authentication - -v Verbose mode - -h Display this help and exit + -f Path to the hl7/fhir file to submit (Required for orders and results APIs) + -r Root path to the hl7/fhir files (Default: $root) + -e Environment: local|staging (Default: $env) + -c Client ID to create JWT with (Default: $client) + -k Path to the client private key + -i Submission ID for metadata API (Required for orders, results and metadata API) + -v Verbose mode + -h Display this help and exit Environment Variables: - CDCTI_HOME Base directory for CDC TI repository (Required) + CDCTI_HOME Base directory for CDC TI repository (Required) EOF } @@ -38,13 +39,14 @@ parse_arguments() { endpoint_name="ti/$1.hurl" shift # Remove endpoint name from args - while getopts ':f:r:e:j:i:v' opt; do + while getopts ':f:r:e:c:k:i:v' opt; do case "$opt" in f) fpath="$OPTARG" ;; - i) submission_id="--variable submissionid=$OPTARG" ;; r) root="$OPTARG" ;; e) env="$OPTARG" ;; - j) jwt="$OPTARG" ;; + c) client="$OPTARG" ;; + k) private_key="$OPTARG" ;; + i) submission_id="--variable submissionid=$OPTARG" ;; v) verbose="--verbose" ;; ?) fail "Invalid option -$OPTARG" ;; esac @@ -55,26 +57,32 @@ parse_arguments() { } setup_credentials() { - if [ -z "$jwt" ] && [ "$client" = "report-stream" ] && [ "$env" = "local" ]; then - if [ -f "$RS_LOCAL_JWT_PATH" ]; then - jwt=$(cat "$RS_LOCAL_JWT_PATH") + if [ -z "$private_key" ] && [ "$client" = "report-stream" ] && [ "$env" = "local" ]; then + if [ -f "$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" ]; then + private_key="$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" else - fail "Local JWT file not found at: $RS_LOCAL_JWT_PATH" + fail "Local environment client private key not found at: $TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" fi fi - [ -n "$jwt" ] || fail "Please provide the JWT for $client" + if [ "$env" != "local" ]; then + [ -z "$private_key" ] && fail "Client private key (-k) is required for non-local environments" + fi + + [ ! -f "$private_key" ] && fail "Client private key file not found: $private_key" } run_hurl_command() { url=$(get_api_url "$env" "ti") + host=$(extract_host_from_url "$url") + jwt_token=$(generate_jwt "$client" "$host" "$private_key") || fail "Failed to generate JWT token" hurl \ --variable "fpath=$fpath" \ --file-root "$root" \ --variable "url=$url" \ --variable "client=$client" \ - --variable "jwt=$jwt" \ + --variable "jwt=$jwt_token" \ ${submission_id:-} \ ${verbose:-} \ "$endpoint_name" \ diff --git a/scripts/hurl/update_examples.sh b/scripts/hurl/update_examples.sh index cae907567..d140ebfab 100755 --- a/scripts/hurl/update_examples.sh +++ b/scripts/hurl/update_examples.sh @@ -10,5 +10,5 @@ check_apis "$(get_api_url "$env" "rs")" "$(get_api_url "$env" "ti")" find "$CDCTI_HOME/examples" -type f -name "*$FILE_NAME_SUFFIX_STEP_0.hl7" | while read -r file; do echo "-----------------------------------------------------------------------------------------------------------" echo "Submitting message: $file" - submit_message "$env" "$file" "$TI_LOCAL_PRIVATE_KEY_PATH" "$(cat "$RS_LOCAL_JWT_PATH")" + submit_message "$env" "$file" "$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" "$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" done diff --git a/scripts/hurl/utils.sh b/scripts/hurl/utils.sh index bd7652aff..26c68dfc5 100755 --- a/scripts/hurl/utils.sh +++ b/scripts/hurl/utils.sh @@ -14,8 +14,8 @@ TI_API_LCL_URL="http://localhost:8080" TI_API_STG_URL="https://cdcti-stg-api.azurewebsites.net:443" TI_API_PRD_URL="https://cdcti-prd-api.azurewebsites.net:443" -TI_LOCAL_PRIVATE_KEY_PATH="$CDCTI_HOME/mock_credentials/organization-trusted-intermediary-private-key-local.pem" -RS_LOCAL_JWT_PATH="$CDCTI_HOME/mock_credentials/report-stream-valid-token.jwt" +RS_CLIENT_LOCAL_PRIVATE_KEY_PATH="$CDCTI_HOME/mock_credentials/organization-trusted-intermediary-private-key-local.pem" +TI_CLIENT_LOCAL_PRIVATE_KEY_PATH="$CDCTI_HOME/mock_credentials/organization-report-stream-private-key-local.pem" fail() { echo "Error: $1" >&2 @@ -144,7 +144,7 @@ check_submission_status() { start_time=$(date +%s) while true; do - history_response=$(./rs.sh history -i "$submission_id" -e "$env" -x "$private_key") + history_response=$(./rs.sh history -i "$submission_id" -e "$env" -k "$private_key") overall_status=$(echo "$history_response" | jq -r '.overallStatus') echo -n " Status: $overall_status" @@ -172,15 +172,14 @@ submit_message() { local env=$1 local file=$2 local private_key=$3 - local jwt=$4 + local public_key=$4 - local message_file_path=$(dirname "$file") - local message_file_name=$(basename "$file") - local message_base_name="${message_file_name%.hl7}" + local message_file_path message_file_name message_base_name + message_file_path="$(dirname "${file}")" + message_file_name="$(basename "${file}")" + message_base_name="${message_file_name%.hl7}" message_base_name="${message_base_name%"$FILE_NAME_SUFFIX_STEP_0"}" - echo "Debug: Processing file '$file' in environment '$env'" - msh9=$(awk -F'|' '/^MSH/ { print $9 }' "$file") case "$msh9" in "ORU^R01^ORU_R01") @@ -199,7 +198,7 @@ submit_message() { echo "Assuming receivers are '$first_leg_receiver' and '$second_leg_receiver' because of MSH-9 value '$msh9'" - waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path" -e "$env" -x "$private_key") + waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path" -e "$env" -k "$private_key") submission_id=$(echo "$waters_response" | jq -r '.id') echo "[First leg] Checking submission status for ID: $submission_id" @@ -213,17 +212,16 @@ submit_message() { if [ "$env" = "local" ]; then translated_blob_name="ready/$first_leg_receiver/$inbound_submission_id.fhir" translated_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_1.fhir" - echo "Debug: Downloading translated file in local environment" download_from_azurite "$translated_blob_name" "$translated_file_path" fi - metadata_response=$(./ti.sh metadata -i "$inbound_submission_id" -e "$env" -j "$jwt") + echo "[Intermediary] Getting outbound submission ID from TI's metadata API" + metadata_response=$(./ti.sh metadata -i "$inbound_submission_id" -e "$env" -k "$public_key") outbound_submission_id=$(echo "$metadata_response" | jq -r '.issue[] | select(.details.text == "outbound submission id") | .diagnostics') if [ "$env" = "local" ]; then transformed_blob_name="receive/flexion.etor-service-sender/$outbound_submission_id.fhir" transformed_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_2.fhir" - echo "Debug: Downloading transformed file in local environment" download_from_azurite "$transformed_blob_name" "$transformed_file_path" fi @@ -237,7 +235,6 @@ submit_message() { final_submission_id=$(extract_submission_id "$history_response") final_blob_name="ready/$second_leg_receiver/$final_submission_id.hl7" final_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_3.hl7" - echo "Debug: Downloading final file in local environment" download_from_azurite "$final_blob_name" "$final_file_path" fi } From 5b7130fdbfb5ee4884611db12357e1cced836ec4 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:57:29 -0700 Subject: [PATCH 09/12] Added error handling for hurl calls, add user prompt for outbound submission id when not possible to get, plus clean up --- scripts/hurl/utils.sh | 66 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/scripts/hurl/utils.sh b/scripts/hurl/utils.sh index 26c68dfc5..62eb88d27 100755 --- a/scripts/hurl/utils.sh +++ b/scripts/hurl/utils.sh @@ -103,10 +103,19 @@ generate_jwt() { -S "@$secret_path" } -extract_submission_id() { +extract_rs_history_submission_id() { # requires: jq local history_response=$1 - echo "$history_response" | jq '.destinations[0].sentReports[0].externalName' | sed 's/.*-\([0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}\)-.*/\1/' + local externalName + externalName=$(echo "$history_response" | jq -r '.destinations[0].sentReports[0].externalName') || return 1 + [[ -z "$externalName" || "$externalName" == "null" ]] && return 1 + echo "$externalName" | sed 's/.*-\([0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}\)-.*/\1/' +} + +extract_ti_metadata_submission_id() { + # requires: jq + local metadata_response=$1 + echo "$metadata_response" | jq -r '.issue[] | select(.details.text == "outbound submission id") | .diagnostics' } download_from_azurite() { @@ -144,7 +153,12 @@ check_submission_status() { start_time=$(date +%s) while true; do - history_response=$(./rs.sh history -i "$submission_id" -e "$env" -k "$private_key") + history_response=$(./rs.sh history -i "$submission_id" -e "$env" -k "$private_key") || { + exit_code=$? + if [ $exit_code -ne 0 ]; then + fail "Expected exit code 0 but got $exit_code for RS history API call" + fi + } overall_status=$(echo "$history_response" | jq -r '.overallStatus') echo -n " Status: $overall_status" @@ -198,7 +212,12 @@ submit_message() { echo "Assuming receivers are '$first_leg_receiver' and '$second_leg_receiver' because of MSH-9 value '$msh9'" - waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path" -e "$env" -k "$private_key") + waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path" -e "$env" -k "$private_key") || { + exit_code=$? + if [ $exit_code -ne 0 ]; then + fail "Expected exit code 0 but got $exit_code for RS waters API call" + fi + } submission_id=$(echo "$waters_response" | jq -r '.id') echo "[First leg] Checking submission status for ID: $submission_id" @@ -207,22 +226,39 @@ submit_message() { return fi - inbound_submission_id=$(extract_submission_id "$history_response") + inbound_submission_id=$(extract_rs_history_submission_id "$history_response") + translated_blob_name="ready/$first_leg_receiver/$inbound_submission_id.fhir" + translated_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_1.fhir" if [ "$env" = "local" ]; then - translated_blob_name="ready/$first_leg_receiver/$inbound_submission_id.fhir" - translated_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_1.fhir" download_from_azurite "$translated_blob_name" "$translated_file_path" + else + echo " Snapshot for translated message in blob storage at: $translated_blob_name" fi echo "[Intermediary] Getting outbound submission ID from TI's metadata API" - metadata_response=$(./ti.sh metadata -i "$inbound_submission_id" -e "$env" -k "$public_key") - outbound_submission_id=$(echo "$metadata_response" | jq -r '.issue[] | select(.details.text == "outbound submission id") | .diagnostics') + metadata_response=$(./ti.sh metadata -i "$inbound_submission_id" -e "$env" -k "$public_key") || { + exit_code=$? + if [ $exit_code -ne 0 ]; then + echo "Failed to get metadata for inbound submission ID: $inbound_submission_id" + echo -n "Please enter the outbound submission ID manually (you may find it in the TI $env logs): " + read -r outbound_submission_id + if [ -z "$outbound_submission_id" ]; then + fail "No outbound submission ID provided" + fi + fi + } + + if [ -z "$outbound_submission_id" ] || [ "$outbound_submission_id" = "null" ]; then + outbound_submission_id=$(extract_ti_metadata_submission_id "$metadata_response") + fi + transformed_blob_name="receive/flexion.etor-service-sender/$outbound_submission_id.fhir" + transformed_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_2.fhir" if [ "$env" = "local" ]; then - transformed_blob_name="receive/flexion.etor-service-sender/$outbound_submission_id.fhir" - transformed_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_2.fhir" download_from_azurite "$transformed_blob_name" "$transformed_file_path" + else + echo " Snapshot for transformed message in blob storage at: $translated_blob_name" fi echo "[Second leg] Checking submission status for ID: $outbound_submission_id" @@ -231,10 +267,12 @@ submit_message() { return fi + final_submission_id=$(extract_rs_history_submission_id "$history_response") + final_blob_name="ready/$second_leg_receiver/$final_submission_id.hl7" + final_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_3.hl7" if [ "$env" = "local" ]; then - final_submission_id=$(extract_submission_id "$history_response") - final_blob_name="ready/$second_leg_receiver/$final_submission_id.hl7" - final_file_path="$message_file_path/$message_base_name$FILE_NAME_SUFFIX_STEP_3.hl7" download_from_azurite "$final_blob_name" "$final_file_path" + else + echo " Snapshot for final message in blob storage at: $translated_blob_name" fi } From ce3c19170f86efa22fedbb76eda949931a6acb7b Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 24 Oct 2024 12:04:47 -0700 Subject: [PATCH 10/12] Typo fix --- scripts/hurl/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index 0c9436461..6dc654766 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -145,7 +145,7 @@ Get Health info from local environment: - `submit_message.sh`: sends a HL7 message to a locally running RS instance. It also grabs the snapshots of the file in azurite after converting to FHIR, after applying transformations in TI, and after converting back to HL7. It copies these files to the same folder where the submitted file is ``` - Usage: submit_message.sh -f [-e ] + Usage: ./submit_message.sh -f [-e ] Options: -f Message file path (required) From 0d1cdc3387d9054d86e75f11c070243a37aace05 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:05:36 -0700 Subject: [PATCH 11/12] Updated readme to add deployed env options --- scripts/hurl/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index 6dc654766..cb62aaaea 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -141,9 +141,9 @@ Get Health info from local environment: `./epic.sh results` -## Submission Scripts +## High Level Scripts -- `submit_message.sh`: sends a HL7 message to a locally running RS instance. It also grabs the snapshots of the file in azurite after converting to FHIR, after applying transformations in TI, and after converting back to HL7. It copies these files to the same folder where the submitted file is +- `submit_message.sh`: sends a HL7 message to RS and tracks its status throughout the flow until final delivery. When running locally, it grabs the snapshots of the file in azurite after converting to FHIR, after applying transformations in TI, and after converting back to HL7; and it copies those files to the same folder where the submitted file is. If running in a deployed environment we currently don't have a way to download the files from Azure, but the script will print the relative path for the files in the blob storage container. ``` Usage: ./submit_message.sh -f [-e ] @@ -159,5 +159,3 @@ Get Health info from local environment: ./update_examples.sh ``` - `utils.sh`: utility functions for the previous scripts. It has functions to submit requests to RS, check the submission status throughout the whole flow, and downloading snapshots from azurite - -**Note**: these scripts require both RS and TI to be running locally From 676a2eedf2cd40170cc37f244e6bc522701a7980 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:28:04 -0700 Subject: [PATCH 12/12] Added handling of empty ti client private key and improved help messages --- scripts/hurl/README.md | 14 ++++++------- scripts/hurl/rs.sh | 4 ++-- scripts/hurl/submit_message.sh | 33 ++++++++++++------------------- scripts/hurl/ti.sh | 2 +- scripts/hurl/utils.sh | 36 ++++++++++++++++++++-------------- 5 files changed, 43 insertions(+), 46 deletions(-) diff --git a/scripts/hurl/README.md b/scripts/hurl/README.md index cb62aaaea..576a9fb75 100644 --- a/scripts/hurl/README.md +++ b/scripts/hurl/README.md @@ -27,8 +27,8 @@ Options: -e Environment: local|staging|production (Default: local) -c Client ID (Default: flexion) -s Client sender (Default: simulated-sender) - -k Path to the client private key - -i Submission ID for history API + -k Path to the client private key (Required for non-local environments) + -i Submission ID for history API (Required for history API) -v Verbose mode -h Display this help and exit @@ -83,7 +83,7 @@ Options: -r Root path to the hl7/fhir files (Default: /Users/bbogado/Code/Flexion/CDC-TI/trusted-intermediary/examples/) -e Environment: local|staging (Default: local) -c Client ID to create JWT with (Default: report-stream) - -k Path to the client private key + -k Path to the client private key (Required for non-local environments) -i Submission ID for metadata API (Required for orders, results and metadata API) -v Verbose mode -h Display this help and exit @@ -145,13 +145,13 @@ Get Health info from local environment: - `submit_message.sh`: sends a HL7 message to RS and tracks its status throughout the flow until final delivery. When running locally, it grabs the snapshots of the file in azurite after converting to FHIR, after applying transformations in TI, and after converting back to HL7; and it copies those files to the same folder where the submitted file is. If running in a deployed environment we currently don't have a way to download the files from Azure, but the script will print the relative path for the files in the blob storage container. ``` - Usage: ./submit_message.sh -f [-e ] + Usage: submit_message.sh -f [-e ] Options: - -f Message file path (required) + -f Message file path (Required) -e Environment: local|staging|production (Default: ) - -x Path to the client private key for authentication with RS API - -z Path to the client private key authentication with TI API + -x Path to the client private key for authentication with RS API (Required for non-local environments) + -z Path to the client private key for authentication with TI API (Optional for all environments) -h Display this help and exit ``` - `update_examples.sh`: sends all the HL7 files with `_0_initial_message.hl7` suffix in the `/examples` folder to a locally running RS instance. As the previous script, it copies the snapshots at each stage diff --git a/scripts/hurl/rs.sh b/scripts/hurl/rs.sh index c8933f3f7..39a65d669 100755 --- a/scripts/hurl/rs.sh +++ b/scripts/hurl/rs.sh @@ -23,8 +23,8 @@ Options: -e Environment: local|staging|production (Default: $env) -c Client ID (Default: $client_id) -s Client sender (Default: $client_sender) - -k Path to the client private key - -i Submission ID for history API + -k Path to the client private key (Required for non-local environments) + -i Submission ID for history API (Required for history API) -v Verbose mode -h Display this help and exit diff --git a/scripts/hurl/submit_message.sh b/scripts/hurl/submit_message.sh index 24f5512de..35c7ccfdb 100755 --- a/scripts/hurl/submit_message.sh +++ b/scripts/hurl/submit_message.sh @@ -9,10 +9,10 @@ show_usage() { Usage: $(basename "$0") -f [-e ] Options: - -f Message file path (required) + -f Message file path (Required) -e Environment: local|staging|production (Default: $DEFAULT_ENV) - -x Path to the client private key for authentication with RS API - -z Path to the client private key authentication with TI API + -x Path to the client private key for authentication with RS API (Required for non-local environments) + -z Path to the client private key for authentication with TI API (Optional for all environments) -h Display this help and exit EOF exit 1 @@ -44,30 +44,21 @@ parse_arguments() { } setup_credentials() { - # Set default credentials for local environment + # Handle RS client key if [ "$env" = "local" ] && [ -z "$rs_client_private_key" ]; then - if [ -f "$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" ]; then - rs_client_private_key="$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" - else - fail "Local environment RS client private key not found at: $RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" - fi + rs_client_private_key="$RS_CLIENT_LOCAL_PRIVATE_KEY_PATH" fi - if [ "$env" = "local" ] && [ -z "$ti_client_private_key" ]; then - if [ -f "$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" ]; then - ti_client_private_key="$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" - else - fail "Local environment TI client private key not found at: $TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" - fi - fi + [ "$env" != "local" ] && [ -z "$rs_client_private_key" ] && fail "RS client private key (-x) is required for non-local environments" + [ ! -f "$rs_client_private_key" ] && fail "RS client private key file not found: $rs_client_private_key" - if [ "$env" != "local" ]; then - [ -z "$rs_client_private_key" ] && fail "RS client private key (-x) is required for non-local environments" - [ -z "$ti_client_private_key" ] && fail "TI client private key (-z) is required for non-local environments" + # Handle optional TI client key + if [ "$env" = "local" ] && [ -z "$ti_client_private_key" ]; then + ti_client_private_key="$TI_CLIENT_LOCAL_PRIVATE_KEY_PATH" fi - [ ! -f "$rs_client_private_key" ] && fail "RS client private key file not found: $rs_client_private_key" - [ ! -f "$ti_client_private_key" ] && fail "TI client private key file not found: $ti_client_private_key" + # Only verify TI key if provided + [ -n "$ti_client_private_key" ] && [ ! -f "$ti_client_private_key" ] && fail "TI client private key file not found: $ti_client_private_key" } check_installed_commands hurl jq az diff --git a/scripts/hurl/ti.sh b/scripts/hurl/ti.sh index 9a0207f53..9278dc147 100755 --- a/scripts/hurl/ti.sh +++ b/scripts/hurl/ti.sh @@ -19,7 +19,7 @@ Options: -r Root path to the hl7/fhir files (Default: $root) -e Environment: local|staging (Default: $env) -c Client ID to create JWT with (Default: $client) - -k Path to the client private key + -k Path to the client private key (Required for non-local environments) -i Submission ID for metadata API (Required for orders, results and metadata API) -v Verbose mode -h Display this help and exit diff --git a/scripts/hurl/utils.sh b/scripts/hurl/utils.sh index 62eb88d27..b9d8903f7 100755 --- a/scripts/hurl/utils.sh +++ b/scripts/hurl/utils.sh @@ -185,8 +185,8 @@ submit_message() { # requires: hurl, jq, az local env=$1 local file=$2 - local private_key=$3 - local public_key=$4 + local rs_client_private_key=$3 + local ti_client_private_key=$4 local message_file_path message_file_name message_base_name message_file_path="$(dirname "${file}")" @@ -212,7 +212,7 @@ submit_message() { echo "Assuming receivers are '$first_leg_receiver' and '$second_leg_receiver' because of MSH-9 value '$msh9'" - waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path" -e "$env" -k "$private_key") || { + waters_response=$(./rs.sh waters -f "$message_file_name" -r "$message_file_path" -e "$env" -k "$rs_client_private_key") || { exit_code=$? if [ $exit_code -ne 0 ]; then fail "Expected exit code 0 but got $exit_code for RS waters API call" @@ -221,7 +221,7 @@ submit_message() { submission_id=$(echo "$waters_response" | jq -r '.id') echo "[First leg] Checking submission status for ID: $submission_id" - if ! check_submission_status "$env" "$submission_id" "$private_key"; then + if ! check_submission_status "$env" "$submission_id" "$rs_client_private_key"; then echo "Failed to deliver the first leg of the message. Skipping the next steps." return fi @@ -236,18 +236,24 @@ submit_message() { echo " Snapshot for translated message in blob storage at: $translated_blob_name" fi - echo "[Intermediary] Getting outbound submission ID from TI's metadata API" - metadata_response=$(./ti.sh metadata -i "$inbound_submission_id" -e "$env" -k "$public_key") || { - exit_code=$? - if [ $exit_code -ne 0 ]; then + echo "[Intermediary] Getting outbound submission ID" + if [ -n "$ti_client_private_key" ]; then + echo " Attempting to get outbound submission ID from TI's metadata API..." + metadata_response=$(./ti.sh metadata -i "$inbound_submission_id" -e "$env" -k "$ti_client_private_key") || { echo "Failed to get metadata for inbound submission ID: $inbound_submission_id" - echo -n "Please enter the outbound submission ID manually (you may find it in the TI $env logs): " - read -r outbound_submission_id - if [ -z "$outbound_submission_id" ]; then - fail "No outbound submission ID provided" - fi + outbound_submission_id="" + } + if [ -n "$metadata_response" ]; then + outbound_submission_id=$(extract_ti_metadata_submission_id "$metadata_response") fi - } + fi + if [ -z "$outbound_submission_id" ] || [ "$outbound_submission_id" = "null" ]; then + echo -n " Please enter the outbound submission ID manually (you may find it in the TI $env logs): " + read -r outbound_submission_id + if [ -z "$outbound_submission_id" ]; then + fail "No outbound submission ID provided" + fi + fi if [ -z "$outbound_submission_id" ] || [ "$outbound_submission_id" = "null" ]; then outbound_submission_id=$(extract_ti_metadata_submission_id "$metadata_response") @@ -262,7 +268,7 @@ submit_message() { fi echo "[Second leg] Checking submission status for ID: $outbound_submission_id" - if ! check_submission_status "$env" "$outbound_submission_id" "$private_key"; then + if ! check_submission_status "$env" "$outbound_submission_id" "$rs_client_private_key"; then echo "Failed to deliver the second leg of the message. Skipping the next steps." return fi