#!/bin/bash # find-zone-by-name - SFCC SCAPI helper to find a CDN zone by name substring # # Usage: # find-zone-by-name [options] # find-zone-by-name -h # # Options: # -j, --jwt JWT bearer token (required unless J_JWT is set) # -s, --shortcode SCAPI shortcode (required unless J_SHORTCODE is set) # -r, --realm Realm ID (required unless J_REALM is set) # -i, --instance Instance name (required unless J_INSTANCE is set) # -n, --name Target zone name substring (required unless J_TARGET is set) # -h, --help Show this help message # # Environment: # J_JWT JWT bearer token (e.g. ) # J_SHORTCODE SCAPI shortcode (e.g. ) # J_REALM Realm ID (e.g. ) # J_INSTANCE Instance name (e.g. ) # J_TARGET Zone name substring (e.g. ) _find_zone_by_name() ( local SCRIPT_NAME; SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")" case "${BASH_SOURCE[0]}" in /dev/*|/proc/*) SCRIPT_NAME="" ;; esac case "$SCRIPT_NAME" in ""|bash|sh|zsh|dash) SCRIPT_NAME="find-zone-by-name" ;; esac local MAX_RESULTS_PER_REQUEST=50 # Limit determined by the API local COUNT_HEADER_NAME="sfdc-pagination-result-count" local TOTAL_HEADER_NAME="sfdc-pagination-total-count" _error() { echo "[ERR][$SCRIPT_NAME] $*" >&2; } _show_help() { local s; [ -t 1 ] && s=$'\033[4m' local r; [ -t 1 ] && r=$'\033[24m' echo "NAME" echo " $SCRIPT_NAME - Salesforce Commerce Cloud SCAPI helper for finding a CDN zone by name" echo "SYNOPSIS" echo " $SCRIPT_NAME -j ${s}jwt${r} -s ${s}shortcode${r} -r ${s}realm${r} -i ${s}instance${r} -n ${s}name${r}" echo " J_JWT=... J_SHORTCODE=... J_REALM=... J_INSTANCE=... J_TARGET=... $SCRIPT_NAME" echo " $SCRIPT_NAME -h" echo "DESCRIPTION" echo " Queries the SFCC CDN Zones API, paging through" echo " GET /cdn/zones/v1/organizations/f_ecom_/zones/info until it" echo " finds a zone whose name contains the given substring. The matching" echo " zone's JSON object is printed to stdout." echo " Prefixed environment variables (J_*) are honored as defaults for flags." echo "OPTIONS" echo " -j, --jwt ${s}jwt${r} JWT bearer token (required unless J_JWT set)" echo " -s, --shortcode ${s}shortcode${r} SCAPI shortcode (required unless J_SHORTCODE set)" echo " -r, --realm ${s}realm${r} Realm ID (required unless J_REALM set)" echo " -i, --instance ${s}instance${r} Instance name (required unless J_INSTANCE set)" echo " -n, --name ${s}name${r} Target zone name substring (required unless J_TARGET set)" echo " -t, --target ${s}name${r} Alias for -n/--name" echo " -h, --help Show this help message" echo "ENVIRONMENT" echo " J_JWT JWT bearer token (default for -j/--jwt)" echo " J_SHORTCODE SCAPI shortcode (default for -s/--shortcode)" echo " J_REALM Realm ID (default for -r/--realm)" echo " J_INSTANCE Instance name (default for -i/--instance)" echo " J_TARGET Zone name substring (default for -n/--name)" echo "DEPENDENCIES" echo " curl, jq" echo "EXIT STATUS" echo " 0 Zone found and printed" echo " 1 No matching zone found, or API request failed" } _expand_short_opts() { # $1 = string of short-opt letters that take a value (e.g. "nXHd"); "" for flag-only scripts # $2..$N = "$@" # Populates _EXPANDED; caller does: set -- "${_EXPANDED[@]}"; unset _EXPANDED local value_opts="$1"; shift _EXPANDED=() local passthru="" local arg local rest local c for arg in "$@"; do if [ -n "$passthru" ]; then _EXPANDED+=("$arg"); continue; fi case "$arg" in --) passthru=1; _EXPANDED+=("$arg") ;; --*|-|"") _EXPANDED+=("$arg") ;; -[a-zA-Z]?*) rest="${arg#-}" while [ -n "$rest" ]; do c="${rest%"${rest#?}"}"; rest="${rest#?}" _EXPANDED+=("-$c") case "$value_opts" in *"$c"*) [ -n "$rest" ] && _EXPANDED+=("$rest") rest="" ;; esac done ;; *) _EXPANDED+=("$arg") ;; esac done } # Initialize local variables with default values from environment variables local jwt="$J_JWT" local shortcode="$J_SHORTCODE" local realm="$J_REALM" local instance="$J_INSTANCE" local target="$J_TARGET" _expand_short_opts "jsrint" "$@" set -- "${_EXPANDED[@]}"; unset _EXPANDED # Parse command-line options while [ $# -gt 0 ]; do case "$1" in -j|--jwt|--token) [ -n "${2-}" ] || { _error "-j|--jwt requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } jwt="$2"; shift 2 ;; --jwt=*|--token=*) jwt="${1#*=}" [ -n "$jwt" ] || { _error "-j|--jwt requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } shift ;; -s|--shortcode) [ -n "${2-}" ] || { _error "-s|--shortcode requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } shortcode="$2"; shift 2 ;; --shortcode=*) shortcode="${1#*=}" [ -n "$shortcode" ] || { _error "-s|--shortcode requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } shift ;; -r|--realm) [ -n "${2-}" ] || { _error "-r|--realm requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } realm="$2"; shift 2 ;; --realm=*) realm="${1#*=}" [ -n "$realm" ] || { _error "-r|--realm requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } shift ;; -i|--instance) [ -n "${2-}" ] || { _error "-i|--instance requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } instance="$2"; shift 2 ;; --instance=*) instance="${1#*=}" [ -n "$instance" ] || { _error "-i|--instance requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } shift ;; -n|--name|-t|--target) [ -n "${2-}" ] || { _error "-n|--name requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } target="$2"; shift 2 ;; --name=*|--target=*) target="${1#*=}" [ -n "$target" ] || { _error "-n|--name requires a value. Run \`$SCRIPT_NAME -h\` for usage"; return 1; } shift ;; -h|--help) _show_help return 0 ;; *) _error "Unknown argument '$1'. Run \`$SCRIPT_NAME -h\` for usage"; return 1 ;; esac done # Check each parameter and assign to missingParam if empty local missingParam='' [ "$jwt" ] || missingParam='jwt: -j ([A-Za-z0-9]+\.[A-Za-z0-9]+\.[A-Za-z0-9]+)' [ "$shortcode" ] || missingParam='shortcode: -s ([a-z0-9]{8})' [ "$realm" ] || missingParam='realm ID: -r ([a-z]{4})' [ "$instance" ] || missingParam='instance name: -i (prd|dev|stg|s01-s99|001-999)' [ "$target" ] || missingParam='zone name: -n (.*)' # Check if any parameter is missing if [ "$missingParam" ]; then _error "Missing required option: $missingParam. Run \`$SCRIPT_NAME -h\` for usage" return 1 fi # Derive other variables local tenant="${realm}_$instance" local organization="f_ecom_$tenant" local path="/cdn/zones/v1/organizations/$organization/zones/info" local hostname="$shortcode.api.commercecloud.salesforce.com" local baseUrl="https://$hostname$path" local limit="$MAX_RESULTS_PER_REQUEST" local offset=0 local response local response_headers local pagination_result_count local pagination_total_count local response_body local target_object while true; do # Call GET /zones/info response="$(curl -fSsiL -X GET "$baseUrl?offset=$offset&limit=$limit" -H 'User-Agent: sfcc-getzone' -H "Authorization: Bearer $jwt" | tr -d '\r')" # Extract response headers response_headers="$(echo "$response" | sed -n '/^HTTP\//,/^$/p' | sed '1d;$d')" # Extract pagination header values using awk pagination_result_count="$(echo "$response_headers" | grep -i "$COUNT_HEADER_NAME" | awk -F': ' '{print $2}')" # Alternatively: cut -d ': ' -f 2 pagination_total_count="$(echo "$response_headers" | grep -i "$TOTAL_HEADER_NAME" | awk -F': ' '{print $2}')" # Alternatively: cut -d ': ' -f 2 # Extract response body response_body="$(echo "$response" | sed -n '/^$/,$p' | sed '1d')" # Use jq to search for the target JSON object target_object="$(echo "$response_body" | jq --arg target "$target" '.data[] | select(.name | contains($target))')" # Check if the target object was found if [ "$target_object" ]; then echo "$target_object" return 0 fi # Check if there are more pages to fetch if [ "$pagination_result_count" -lt "$pagination_total_count" ]; then # Calculate the next offset offset=$((offset + limit)) else _error "Reached the end of pagination without finding the requested zone" _error "Searched for: $target" return 1 fi done ) _find_zone_by_name "$@" __find_zone_by_name_rc=$? unset -f _find_zone_by_name if [ -n "${BASH_SOURCE[0]}" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then eval "unset __find_zone_by_name_rc; return $__find_zone_by_name_rc" fi eval "unset __find_zone_by_name_rc; exit $__find_zone_by_name_rc"