| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- #!/usr/bin/env bash
- set -euo pipefail
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- REQUEST_DIR="${SCRIPT_DIR}/../src/test/resources/requests"
- VALID_REQUEST="${REQUEST_DIR}/exam-sprint-achievement-report-request.json"
- INVALID_REQUEST="${REQUEST_DIR}/exam-sprint-achievement-report-invalid-request.json"
- BASE_URL="${BASE_URL:-http://127.0.0.1:8080}"
- OUTPUT_PATH="${OUTPUT_PATH:-${PWD}/achievement-report-demo.pdf}"
- HTML_OUTPUT_PATH="${HTML_OUTPUT_PATH:-${PWD}/achievement-report-preview.html}"
- MAX_ATTEMPTS="${MAX_ATTEMPTS:-30}"
- POLL_INTERVAL_SECONDS="${POLL_INTERVAL_SECONDS:-1}"
- JSON_PARSER=""
- TMP_FILES=()
- usage() {
- cat <<EOF
- Usage:
- $(basename "$0") success
- $(basename "$0") invalid
- $(basename "$0") --help
- Modes:
- success Submit the valid sample request, poll report status, then download the PDF and preview HTML.
- invalid Submit the invalid sample request and print the validation error response.
- Environment variables:
- BASE_URL API base URL. Default: http://127.0.0.1:8080
- OUTPUT_PATH PDF output path for success mode.
- Default: current working directory/achievement-report-demo.pdf
- HTML_OUTPUT_PATH HTML preview output path for success mode.
- Default: current working directory/achievement-report-preview.html
- MAX_ATTEMPTS Polling attempts for success mode. Default: 30
- POLL_INTERVAL_SECONDS Seconds between polling attempts. Default: 1
- Examples:
- $(basename "$0") success
- BASE_URL=http://127.0.0.1:8081 $(basename "$0") invalid
- EOF
- }
- cleanup() {
- if [ "${#TMP_FILES[@]}" -gt 0 ]; then
- rm -f "${TMP_FILES[@]}"
- fi
- }
- trap cleanup EXIT
- die() {
- printf 'Error: %s\n' "$1" >&2
- exit 1
- }
- make_temp_file() {
- local file
- file="$(mktemp)"
- TMP_FILES+=("$file")
- printf '%s\n' "$file"
- }
- require_command() {
- command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
- }
- select_json_parser() {
- if command -v jq >/dev/null 2>&1; then
- JSON_PARSER="jq"
- elif command -v python3 >/dev/null 2>&1; then
- JSON_PARSER="python3"
- else
- die "JSON parsing requires jq or python3, but neither was found in PATH."
- fi
- }
- json_get() {
- local file="$1"
- local path="$2"
- if [ "$JSON_PARSER" = "jq" ]; then
- jq -er ".$path" "$file"
- else
- python3 - "$file" "$path" <<'PY'
- import json
- import sys
- from pathlib import Path
- file_path = Path(sys.argv[1])
- path = [part for part in sys.argv[2].split('.') if part]
- value = json.loads(file_path.read_text(encoding='utf-8'))
- for part in path:
- if isinstance(value, dict) and part in value:
- value = value[part]
- else:
- sys.exit(1)
- if value is None:
- sys.exit(1)
- if isinstance(value, (dict, list)):
- print(json.dumps(value, ensure_ascii=False))
- else:
- print(value)
- PY
- fi
- }
- pretty_print_json() {
- local file="$1"
- if [ "$JSON_PARSER" = "jq" ]; then
- jq . "$file"
- else
- python3 - "$file" <<'PY'
- import json
- import sys
- from pathlib import Path
- file_path = Path(sys.argv[1])
- data = json.loads(file_path.read_text(encoding='utf-8'))
- print(json.dumps(data, ensure_ascii=False, indent=2))
- PY
- fi
- }
- normalize_url() {
- local url="$1"
- if [[ "$url" =~ ^https?:// ]]; then
- printf '%s\n' "$url"
- elif [[ "$url" == /* ]]; then
- printf '%s%s\n' "${BASE_URL%/}" "$url"
- else
- printf '%s/%s\n' "${BASE_URL%/}" "$url"
- fi
- }
- post_json() {
- local url="$1"
- local payload="$2"
- local output="$3"
- curl -sS -o "$output" -w '%{http_code}' -X POST -H 'Content-Type: application/json' --data "@$payload" "$url"
- }
- get_json() {
- local url="$1"
- local output="$2"
- curl -sS -o "$output" -w '%{http_code}' "$url"
- }
- download_file() {
- local url="$1"
- local output="$2"
- curl -sS -L -o "$output" -w '%{http_code}' "$url"
- }
- ensure_request_files() {
- [ -f "$VALID_REQUEST" ] || die "Missing request fixture: $VALID_REQUEST"
- [ -f "$INVALID_REQUEST" ] || die "Missing request fixture: $INVALID_REQUEST"
- }
- run_success_mode() {
- local submit_url submit_body submit_status report_id query_url query_body query_status current_status
- local download_url download_target download_status preview_html_url preview_html_target preview_html_status attempt
- submit_url="${BASE_URL%/}/api/exam-sprint/reports"
- submit_body="$(make_temp_file)"
- submit_status="$(post_json "$submit_url" "$VALID_REQUEST" "$submit_body")" || die "Failed to submit request to ${submit_url}"
- if [ "$submit_status" != "202" ]; then
- printf 'Submit request failed with HTTP %s\n' "$submit_status" >&2
- pretty_print_json "$submit_body" >&2
- exit 1
- fi
- report_id="$(json_get "$submit_body" 'data.reportId')" || die "Accepted response did not contain data.reportId"
- printf 'Report created: %s\n' "$report_id"
- query_url="${BASE_URL%/}/api/exam-sprint/reports/${report_id}"
- query_body="$(make_temp_file)"
- for ((attempt = 1; attempt <= MAX_ATTEMPTS; attempt++)); do
- query_status="$(get_json "$query_url" "$query_body")" || die "Failed to query report status from ${query_url}"
- if [ "$query_status" != "200" ]; then
- printf 'Query report failed with HTTP %s\n' "$query_status" >&2
- pretty_print_json "$query_body" >&2
- exit 1
- fi
- current_status="$(json_get "$query_body" 'data.generationStatus')" || die "Report response did not contain data.generationStatus"
- printf 'Poll %d/%s: %s\n' "$attempt" "$MAX_ATTEMPTS" "$current_status"
- case "$current_status" in
- SUCCESS)
- download_url="$(json_get "$query_body" 'data.downloadUrl')" || die "Successful report did not contain data.downloadUrl"
- download_url="$(normalize_url "$download_url")"
- download_target="$(make_temp_file)"
- download_status="$(download_file "$download_url" "$download_target")" || die "Failed to download PDF from ${download_url}"
- if [ "$download_status" != "200" ]; then
- printf 'Download failed with HTTP %s\n' "$download_status" >&2
- pretty_print_json "$download_target" >&2
- exit 1
- fi
- preview_html_url="$(json_get "$query_body" 'data.previewHtmlUrl')" || die "Successful report did not contain data.previewHtmlUrl"
- preview_html_url="$(normalize_url "$preview_html_url")"
- preview_html_target="$(make_temp_file)"
- preview_html_status="$(download_file "$preview_html_url" "$preview_html_target")" || die "Failed to download preview HTML from ${preview_html_url}"
- if [ "$preview_html_status" != "200" ]; then
- printf 'Preview HTML download failed with HTTP %s\n' "$preview_html_status" >&2
- pretty_print_json "$preview_html_target" >&2
- exit 1
- fi
- mv "$download_target" "$OUTPUT_PATH"
- mv "$preview_html_target" "$HTML_OUTPUT_PATH"
- printf 'PDF downloaded to: %s\n' "$OUTPUT_PATH"
- printf 'Preview HTML downloaded to: %s\n' "$HTML_OUTPUT_PATH"
- return 0
- ;;
- FAILED|EXPIRED)
- printf 'Report ended with status %s\n' "$current_status" >&2
- pretty_print_json "$query_body" >&2
- exit 1
- ;;
- esac
- sleep "$POLL_INTERVAL_SECONDS"
- done
- printf 'Report did not reach SUCCESS within %s attempts\n' "$MAX_ATTEMPTS" >&2
- pretty_print_json "$query_body" >&2
- exit 1
- }
- run_invalid_mode() {
- local submit_url response_body response_status
- submit_url="${BASE_URL%/}/api/exam-sprint/reports"
- response_body="$(make_temp_file)"
- response_status="$(post_json "$submit_url" "$INVALID_REQUEST" "$response_body")" || die "Failed to submit invalid request to ${submit_url}"
- printf 'HTTP %s\n' "$response_status"
- pretty_print_json "$response_body"
- if [ "$response_status" != "400" ]; then
- die "Expected HTTP 400 for invalid request, got ${response_status}"
- fi
- }
- main() {
- local mode="${1:-}"
- if [ "$#" -gt 1 ]; then
- usage
- exit 1
- fi
- case "$mode" in
- ""|--help|-h|help)
- usage
- ;;
- success)
- require_command curl
- select_json_parser
- ensure_request_files
- run_success_mode
- ;;
- invalid)
- require_command curl
- select_json_parser
- ensure_request_files
- run_invalid_mode
- ;;
- *)
- usage
- exit 1
- ;;
- esac
- }
- main "$@"
|