#!/usr/bin/env bash set -euo pipefail # Script Name: api # Description: API testing helper with saved requests and response management # Usage: api save login "POST https://api.com/login" -d '{"user":"test"}' # api run login # api list # api history login VERSION="1.0.0" API_DIR="$HOME/.api" REQUESTS_DIR="$API_DIR/requests" RESPONSES_DIR="$API_DIR/responses" TOKENS_FILE="$API_DIR/tokens.json" # Colors readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly BLUE='\033[0;34m' readonly RED='\033[0;31m' readonly CYAN='\033[0;36m' readonly BOLD='\033[1m' readonly NC='\033[0m' # Initialize API directory structure init_api() { mkdir -p "$REQUESTS_DIR" "$RESPONSES_DIR" if [[ ! -f "$TOKENS_FILE" ]]; then echo '{}' > "$TOKENS_FILE" fi } show_help() { echo -e "${BOLD}api${NC} - API Testing Helper v${VERSION}" echo echo -e "${BOLD}USAGE:${NC}" echo " api [args]" echo echo -e "${BOLD}COMMANDS:${NC}" echo -e " ${CYAN}save NAME CURL_ARGS${NC} Save HTTP request" echo -e " ${CYAN}run NAME [VARS]${NC} Run saved request" echo -e " ${CYAN}list${NC} List saved requests" echo -e " ${CYAN}show NAME${NC} Show request details" echo -e " ${CYAN}delete NAME${NC} Delete saved request" echo -e " ${CYAN}history NAME${NC} Show response history" echo -e " ${CYAN}diff NAME${NC} Diff last two responses" echo -e " ${CYAN}token set KEY VAL${NC} Save auth token" echo -e " ${CYAN}token get KEY${NC} Get auth token" echo -e " ${CYAN}export NAME curl${NC} Export as curl command" echo echo -e "${BOLD}EXAMPLES:${NC}" echo " # Save a login request" echo " api save login \"POST https://api.example.com/login\" \\" echo " -H \"Content-Type: application/json\" \\" echo " -d '{\"user\":\"test\",\"pass\":\"\${PASSWORD}\"}'" echo echo " # Run with variable substitution" echo " api run login PASSWORD=secret123" echo echo " # Save auth token from response" echo " api token set AUTH_TOKEN \"Bearer abc123\"" echo echo " # Use token in request" echo " api save profile \"GET https://api.example.com/profile\" \\" echo " -H \"Authorization: \${AUTH_TOKEN}\"" echo echo -e "${BOLD}FEATURES:${NC}" echo " - Variable substitution (\${VAR})" echo " - Response history" echo " - Pretty-print JSON" echo " - Diff responses" echo " - Token management" echo echo -e "${BOLD}NOTES:${NC}" echo " Requests: $REQUESTS_DIR" echo " Responses: $RESPONSES_DIR" echo " Tokens: $TOKENS_FILE" } # Save request save_request() { local name=$1 shift local request_file="$REQUESTS_DIR/$name.sh" # Save the curl command cat > "$request_file" << EOF #!/usr/bin/env bash # API Request: $name # Saved: $(date) curl -w "\\n\\nStatus: %{http_code}\\nTime: %{time_total}s\\n" \\ $@ EOF chmod +x "$request_file" echo -e "${GREEN}✓${NC} Saved request: $name" echo -e "${CYAN}File:${NC} $request_file" } # Run saved request run_request() { local name=$1 shift local request_file="$REQUESTS_DIR/$name.sh" if [[ ! -f "$request_file" ]]; then echo -e "${RED}Error:${NC} Request not found: $name" >&2 echo "Use 'api list' to see available requests" >&2 exit 1 fi # Parse variables (KEY=VALUE format) declare -A vars for arg in "$@"; do if [[ "$arg" =~ ^([A-Z_]+)=(.+)$ ]]; then vars[${BASH_REMATCH[1]}]="${BASH_REMATCH[2]}" fi done # Read request, substitute variables request_content=$(cat "$request_file") for var in "${!vars[@]}"; do request_content="${request_content//\$\{$var\}/${vars[$var]}}" done # Also substitute from tokens file if command -v jq &>/dev/null && [[ -f "$TOKENS_FILE" ]]; then while IFS= read -r line; do key=$(echo "$line" | jq -r '.key') val=$(echo "$line" | jq -r '.value') request_content="${request_content//\$\{$key\}/$val}" done < <(jq -c 'to_entries[]' "$TOKENS_FILE") fi # Save response with timestamp timestamp=$(date '+%Y%m%d-%H%M%S') response_file="$RESPONSES_DIR/${name}_${timestamp}.txt" echo -e "${BOLD}${CYAN}Running: $name${NC}" echo # Execute and save response echo "$request_content" | bash | tee "$response_file" echo echo -e "${GREEN}✓${NC} Response saved: $response_file" # Pretty-print JSON if possible if command -v jq &>/dev/null; then if head -1 "$response_file" | jq empty 2>/dev/null; then echo echo -e "${BOLD}${CYAN}JSON Response:${NC}" head -n -3 "$response_file" | jq . fi fi } # List saved requests list_requests() { if [[ ! -d "$REQUESTS_DIR" ]] || [[ -z "$(ls -A "$REQUESTS_DIR" 2>/dev/null)" ]]; then echo -e "${YELLOW}No saved requests${NC}" exit 0 fi echo -e "${BOLD}${CYAN}Saved Requests:${NC}" echo for file in "$REQUESTS_DIR"/*.sh; do name=$(basename "$file" .sh) method=$(grep -oP 'POST|GET|PUT|DELETE|PATCH' "$file" | head -1 || echo "?") url=$(grep -oP 'https?://[^\s"]+' "$file" | head -1 || echo "?") printf " %-20s ${CYAN}%-7s${NC} %s\n" "$name" "$method" "$url" done } # Show request details show_request() { local name=$1 local request_file="$REQUESTS_DIR/$name.sh" if [[ ! -f "$request_file" ]]; then echo -e "${RED}Error:${NC} Request not found: $name" >&2 exit 1 fi echo -e "${BOLD}${CYAN}Request: $name${NC}" echo # Use bat if available for syntax highlighting if command -v bat &>/dev/null; then bat "$request_file" else cat "$request_file" fi } # Delete request delete_request() { local name=$1 local request_file="$REQUESTS_DIR/$name.sh" if [[ ! -f "$request_file" ]]; then echo -e "${RED}Error:${NC} Request not found: $name" >&2 exit 1 fi echo -n "Delete request '$name'? (y/N) " read -r response if [[ "$response" =~ ^[Yy]$ ]]; then rm "$request_file" echo -e "${GREEN}✓${NC} Deleted: $name" else echo "Cancelled" fi } # Show response history show_history() { local name=$1 responses=$(find "$RESPONSES_DIR" -name "${name}_*.txt" 2>/dev/null | sort -r) if [[ -z "$responses" ]]; then echo -e "${YELLOW}No response history for: $name${NC}" exit 0 fi echo -e "${BOLD}${CYAN}Response History: $name${NC}" echo echo "$responses" | while read -r file; do timestamp=$(basename "$file" | sed "s/${name}_//" | sed 's/.txt//') status=$(tail -3 "$file" | grep "Status:" | awk '{print $2}') time=$(tail -3 "$file" | grep "Time:" | awk '{print $2}') # Color status if [[ "$status" =~ ^2 ]]; then status_colored="${GREEN}$status${NC}" elif [[ "$status" =~ ^4 ]]; then status_colored="${YELLOW}$status${NC}" elif [[ "$status" =~ ^5 ]]; then status_colored="${RED}$status${NC}" else status_colored="$status" fi echo -e " $timestamp - Status: $status_colored - Time: $time" done echo echo -e "${CYAN}Tip:${NC} Use 'api diff $name' to compare last two responses" } # Diff last two responses diff_responses() { local name=$1 responses=$(find "$RESPONSES_DIR" -name "${name}_*.txt" 2>/dev/null | sort -r | head -2) count=$(echo "$responses" | wc -l) if [[ $count -lt 2 ]]; then echo -e "${YELLOW}Need at least 2 responses to diff${NC}" exit 0 fi file1=$(echo "$responses" | sed -n 1p) file2=$(echo "$responses" | sed -n 2p) echo -e "${BOLD}${CYAN}Diff: $name${NC}" echo -e "${CYAN}Latest:${NC} $(basename "$file1")" echo -e "${CYAN}Previous:${NC} $(basename "$file2")" echo # Remove status/time lines before diff diff -u <(head -n -3 "$file2") <(head -n -3 "$file1") || true } # Token management manage_token() { local action=$1 shift case "$action" in set) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api token set KEY VALUE" >&2 exit 1 fi key=$1 value=$2 # Update JSON file if command -v jq &>/dev/null; then jq --arg k "$key" --arg v "$value" '. + {($k): $v}' "$TOKENS_FILE" > "$TOKENS_FILE.tmp" mv "$TOKENS_FILE.tmp" "$TOKENS_FILE" echo -e "${GREEN}✓${NC} Token saved: $key" else echo -e "${RED}Error:${NC} jq required for token management" >&2 exit 1 fi ;; get) if [[ $# -lt 1 ]]; then echo -e "${RED}Error:${NC} Usage: api token get KEY" >&2 exit 1 fi key=$1 if command -v jq &>/dev/null; then value=$(jq -r ".$key // empty" "$TOKENS_FILE") if [[ -n "$value" ]]; then echo "$value" else echo -e "${YELLOW}Token not found: $key${NC}" >&2 exit 1 fi fi ;; list) if command -v jq &>/dev/null; then echo -e "${BOLD}${CYAN}Saved Tokens:${NC}" jq -r 'keys[]' "$TOKENS_FILE" fi ;; *) echo -e "${RED}Error:${NC} Unknown token action: $action" >&2 echo "Use: set, get, list" >&2 exit 1 ;; esac } # Export request export_request() { local name=$1 local format=${2:-curl} local request_file="$REQUESTS_DIR/$name.sh" if [[ ! -f "$request_file" ]]; then echo -e "${RED}Error:${NC} Request not found: $name" >&2 exit 1 fi case "$format" in curl) # Extract the curl command grep -A 999 'curl' "$request_file" | grep -v '^#' ;; *) echo -e "${RED}Error:${NC} Unknown format: $format" >&2 echo "Supported: curl" >&2 exit 1 ;; esac } # Initialize init_api # Parse command if [[ $# -eq 0 ]]; then show_help exit 0 fi case $1 in -h|--help|help) show_help ;; save) if [[ $# -lt 3 ]]; then echo -e "${RED}Error:${NC} Usage: api save NAME CURL_ARGS" >&2 exit 1 fi shift save_request "$@" ;; run) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api run NAME [VARS]" >&2 exit 1 fi shift run_request "$@" ;; list|ls) list_requests ;; show) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api show NAME" >&2 exit 1 fi show_request "$2" ;; delete|rm) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api delete NAME" >&2 exit 1 fi delete_request "$2" ;; history) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api history NAME" >&2 exit 1 fi show_history "$2" ;; diff) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api diff NAME" >&2 exit 1 fi diff_responses "$2" ;; token) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api token ..." >&2 exit 1 fi shift manage_token "$@" ;; export) if [[ $# -lt 2 ]]; then echo -e "${RED}Error:${NC} Usage: api export NAME [FORMAT]" >&2 exit 1 fi export_request "$2" "${3:-curl}" ;; *) echo -e "${RED}Error:${NC} Unknown command: $1" >&2 echo "Run 'api --help' for usage" >&2 exit 1 ;; esac