mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-30 12:04:31 +00:00
refactor: Extract generic_cloud_api retry logic into helper functions (#78)
Split the 66-line generic_cloud_api function into focused helpers to reduce complexity and eliminate duplication: - _parse_api_response: Extracts HTTP code and response body (10 lines) - _make_api_request: Builds curl args and executes request (27 lines) - _handle_api_transient_error: Centralizes retry logic for all error types (24 lines) Main function reduced from 66 to 41 lines (38% reduction). Behavior unchanged: still retries on network errors and transient HTTP codes (429, 503), with exponential backoff. All test assertions pass. This extraction pattern makes it clearer how retry logic flows and easier to modify error handling in the future without duplicating patterns. Co-authored-by: A <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
dfaf9370d2
commit
6e47cb597f
1 changed files with 80 additions and 33 deletions
113
shared/common.sh
113
shared/common.sh
|
|
@ -995,6 +995,78 @@ _update_retry_interval() {
|
|||
eval "${interval_var}=${current_interval}"
|
||||
}
|
||||
|
||||
# Helper to extract HTTP status code and response body from curl output
|
||||
# Curl is called with "-w \n%{http_code}" so last line is the code
|
||||
# Returns: http_code on stdout, response_body via global variable
|
||||
_parse_api_response() {
|
||||
local response="${1}"
|
||||
local http_code
|
||||
http_code=$(echo "${response}" | tail -1)
|
||||
local response_body
|
||||
response_body=$(echo "${response}" | head -n -1)
|
||||
|
||||
API_HTTP_CODE="${http_code}"
|
||||
API_RESPONSE_BODY="${response_body}"
|
||||
}
|
||||
|
||||
# Helper to handle a single API request attempt - builds curl args and executes it
|
||||
# Returns: 0 on curl success, 1 on curl failure
|
||||
# Sets: API_HTTP_CODE and API_RESPONSE_BODY globals
|
||||
_make_api_request() {
|
||||
local base_url="${1}"
|
||||
local auth_token="${2}"
|
||||
local method="${3}"
|
||||
local endpoint="${4}"
|
||||
local body="${5:-}"
|
||||
|
||||
local args=(
|
||||
-s
|
||||
-w "\n%{http_code}"
|
||||
-X "${method}"
|
||||
-H "Authorization: Bearer ${auth_token}"
|
||||
-H "Content-Type: application/json"
|
||||
)
|
||||
|
||||
if [[ -n "${body}" ]]; then
|
||||
args+=(-d "${body}")
|
||||
fi
|
||||
|
||||
local response
|
||||
response=$(curl "${args[@]}" "${base_url}${endpoint}" 2>&1)
|
||||
local curl_exit_code=$?
|
||||
|
||||
_parse_api_response "${response}"
|
||||
|
||||
return ${curl_exit_code}
|
||||
}
|
||||
|
||||
# Helper to handle transient API error and decide whether to retry
|
||||
# Returns: 0 to continue, 1 to fail (response already echoed)
|
||||
_handle_api_transient_error() {
|
||||
local error_type="${1}" # "network" or HTTP_CODE like "429"
|
||||
local attempt="${2}"
|
||||
local max_retries="${3}"
|
||||
local interval_var="${4}"
|
||||
local max_interval_var="${5}"
|
||||
local response_body="${6}"
|
||||
|
||||
if [[ "${error_type}" == "network" ]]; then
|
||||
if ! _api_should_retry_on_error "network" "${attempt}" "${max_retries}" "${!interval_var}" "${!max_interval_var}" "Cloud API network error"; then
|
||||
log_error "Cloud API network error after ${max_retries} attempts"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
# HTTP error code (429 or 503)
|
||||
if ! _api_handle_transient_http_error "${error_type}" "${attempt}" "${max_retries}" "${!interval_var}" "${!max_interval_var}"; then
|
||||
echo "${response_body}"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
_update_retry_interval "${interval_var}" "${max_interval_var}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Generic cloud API wrapper - centralized curl wrapper for all cloud providers
|
||||
# Includes automatic retry logic with exponential backoff for transient failures
|
||||
# Usage: generic_cloud_api BASE_URL AUTH_TOKEN METHOD ENDPOINT [BODY] [MAX_RETRIES]
|
||||
|
|
@ -1015,52 +1087,27 @@ generic_cloud_api() {
|
|||
local max_interval=30
|
||||
|
||||
while [[ "${attempt}" -le "${max_retries}" ]]; do
|
||||
local args=(
|
||||
-s
|
||||
-w "\n%{http_code}"
|
||||
-X "${method}"
|
||||
-H "Authorization: Bearer ${auth_token}"
|
||||
-H "Content-Type: application/json"
|
||||
)
|
||||
|
||||
if [[ -n "${body}" ]]; then
|
||||
args+=(-d "${body}")
|
||||
fi
|
||||
|
||||
local response
|
||||
response=$(curl "${args[@]}" "${base_url}${endpoint}" 2>&1)
|
||||
local curl_exit_code=$?
|
||||
|
||||
# Extract HTTP status code (last line) and response body (everything else)
|
||||
local http_code
|
||||
http_code=$(echo "${response}" | tail -1)
|
||||
local response_body
|
||||
response_body=$(echo "${response}" | head -n -1)
|
||||
|
||||
# Check for network errors (curl exit code != 0)
|
||||
if [[ ${curl_exit_code} -ne 0 ]]; then
|
||||
if ! _api_should_retry_on_error "network" "${attempt}" "${max_retries}" "${interval}" "${max_interval}" "Cloud API network error"; then
|
||||
log_error "Cloud API network error after ${max_retries} attempts: curl exit code ${curl_exit_code}"
|
||||
# Make the API request
|
||||
if ! _make_api_request "${base_url}" "${auth_token}" "${method}" "${endpoint}" "${body}"; then
|
||||
# Network error - handle and retry
|
||||
if ! _handle_api_transient_error "network" "${attempt}" "${max_retries}" "interval" "max_interval" ""; then
|
||||
return 1
|
||||
fi
|
||||
_update_retry_interval "interval" "max_interval"
|
||||
attempt=$((attempt + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check for transient HTTP errors that should be retried
|
||||
if [[ "${http_code}" == "429" ]] || [[ "${http_code}" == "503" ]]; then
|
||||
if ! _api_handle_transient_http_error "${http_code}" "${attempt}" "${max_retries}" "${interval}" "${max_interval}"; then
|
||||
echo "${response_body}"
|
||||
# Check for transient HTTP errors (429 or 503)
|
||||
if [[ "${API_HTTP_CODE}" == "429" ]] || [[ "${API_HTTP_CODE}" == "503" ]]; then
|
||||
if ! _handle_api_transient_error "${API_HTTP_CODE}" "${attempt}" "${max_retries}" "interval" "max_interval" "${API_RESPONSE_BODY}"; then
|
||||
return 1
|
||||
fi
|
||||
_update_retry_interval "interval" "max_interval"
|
||||
attempt=$((attempt + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Success or non-retryable error - return response body
|
||||
echo "${response_body}"
|
||||
echo "${API_RESPONSE_BODY}"
|
||||
return 0
|
||||
done
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue