Esivaatimukset ja turvallisuus
Käytä sandbox-avainta, jolla on catalog:read- ja watchlists:write-oikeudet, eristetyssä työtilassa ilman aiempia vahteja. Tarvitset bash-, curl- ja jq-ohjelmat. Aseta hyväksytty WATCH_EMAIL-osoitteesi ja yksilöllinen WATCH_REQUEST_KEY yksityisesti. Käytä samaa avainta vain saman luontipyynnön uudelleenyritykseen. Sandbox-vahdit eivät lähetä sähköpostia. Harjoitus testaa tarkoituksella vanhentunutta revisiota; älä suorita sitä tuotantovahdeille.
Suorita eristetty harjoitus
- 1Hae tuotetunnisteet
- 2Luo synteettinen vahti
- 3Muuta tavoitehintaa
- 4Tunnista vanhentunut revisio
- 5Yritä nykyisellä revisiolla
- 6Poista vahti
- 7Varmista siivous
Suorita omalta palvelimeltasi
set -euo pipefail
# Requires bash, curl and jq. Use an isolated sandbox API key.
DOCS_BASE_URL="${DOCS_BASE_URL:-https://hintadata.fi}"
: "${HINTADATA_API_KEY:?Set a sandbox API key privately}"
: "${WATCH_EMAIL:?Set WATCH_EMAIL privately before running}"
: "${WATCH_REQUEST_KEY:?Set WATCH_REQUEST_KEY privately before running}"
DOCS_VARIABLES=$(jq -cn --arg watchEmail "$WATCH_EMAIL" --arg requestKey "$WATCH_REQUEST_KEY" '{watchEmail:$watchEmail,requestKey:$requestKey}')
DOCS_RESPONSE_FILE=$(mktemp)
trap 'rm -f -- "$DOCS_RESPONSE_FILE"' EXIT
# search: expected HTTP 200
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/search?q=sony&limit=2"')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'GET' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY")
[[ "$DOCS_STATUS" == '200' ]] || { printf '%s\n' 'Unexpected HTTP status at search' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("query") else false end))) and ($response["query"] == ("sony"))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("items") else false end) and ($response["items"] | if type == "object" or type == "array" then has(0) else false end) and ($response["items"][0] | if type == "object" or type == "array" then has("canonical_product_id") else false end)) == true)' >/dev/null
DOCS_VARIABLES=$(jq -cn --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" '$vars + {"productId":$response["items"][0]["canonical_product_id"],"secondId":$response["items"][1]["canonical_product_id"]}')
printf '%s\n' 'search: verified'
# create: expected HTTP 201
jq -e 'has("requestKey")' <<< "$DOCS_VARIABLES" >/dev/null
jq -e 'has("productId")' <<< "$DOCS_VARIABLES" >/dev/null
jq -e 'has("watchEmail")' <<< "$DOCS_VARIABLES" >/dev/null
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/watchlists"')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'POST' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY" -H "Idempotency-Key: $(jq -cnr --argjson vars "$DOCS_VARIABLES" '$vars["requestKey"]')" -H 'Content-Type: application/json' --data "$(jq -cnr --argjson vars "$DOCS_VARIABLES" '{"canonical_product_id":$vars["productId"],"condition":"target_price","target_price":199,"channel":"email","destination":$vars["watchEmail"]}')")
[[ "$DOCS_STATUS" == '201' ]] || { printf '%s\n' 'Unexpected HTTP status at create' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("revision") else false end))) and ($response["revision"] == (1))' >/dev/null
DOCS_VARIABLES=$(jq -cn --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" '$vars + {"watchId":$response["id"],"revision":$response["revision"]}')
printf '%s\n' 'create: verified'
# advance: expected HTTP 200
jq -e 'has("watchId")' <<< "$DOCS_VARIABLES" >/dev/null
jq -e 'has("revision")' <<< "$DOCS_VARIABLES" >/dev/null
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/watchlists/" + ($vars["watchId"] | tostring | @uri) + ""')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'PATCH' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY" -H 'Content-Type: application/json' --data "$(jq -cnr --argjson vars "$DOCS_VARIABLES" '{"expected_revision":$vars["revision"],"target_price":198}')")
[[ "$DOCS_STATUS" == '200' ]] || { printf '%s\n' 'Unexpected HTTP status at advance' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("revision") else false end))) and ($response["revision"] == (2))' >/dev/null
DOCS_VARIABLES=$(jq -cn --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" '$vars + {"revision":$response["revision"]}')
printf '%s\n' 'advance: verified'
# conflict: expected HTTP 409
jq -e 'has("watchId")' <<< "$DOCS_VARIABLES" >/dev/null
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/watchlists/" + ($vars["watchId"] | tostring | @uri) + ""')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'PATCH' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY" -H 'Content-Type: application/json' --data "$(jq -cnr --argjson vars "$DOCS_VARIABLES" '{"expected_revision":1,"status":"paused"}')")
[[ "$DOCS_STATUS" == '409' ]] || { printf '%s\n' 'Unexpected HTTP status at conflict' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("error") else false end) and ($response["error"] | if type == "object" or type == "array" then has("code") else false end))) and ($response["error"]["code"] == ("revision_conflict"))' >/dev/null
printf '%s\n' 'conflict: verified'
# pause: expected HTTP 200
jq -e 'has("watchId")' <<< "$DOCS_VARIABLES" >/dev/null
jq -e 'has("revision")' <<< "$DOCS_VARIABLES" >/dev/null
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/watchlists/" + ($vars["watchId"] | tostring | @uri) + ""')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'PATCH' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY" -H 'Content-Type: application/json' --data "$(jq -cnr --argjson vars "$DOCS_VARIABLES" '{"expected_revision":$vars["revision"],"status":"paused"}')")
[[ "$DOCS_STATUS" == '200' ]] || { printf '%s\n' 'Unexpected HTTP status at pause' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("status") else false end))) and ($response["status"] == ("paused"))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("revision") else false end))) and ($response["revision"] == (3))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("status") else false end))) and ($response["status"] == ("paused"))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("revision") else false end))) and ($response["revision"] == (3))' >/dev/null
printf '%s\n' 'pause: verified'
# delete: expected HTTP 200
jq -e 'has("watchId")' <<< "$DOCS_VARIABLES" >/dev/null
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/watchlists/" + ($vars["watchId"] | tostring | @uri) + ""')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'DELETE' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY")
[[ "$DOCS_STATUS" == '200' ]] || { printf '%s\n' 'Unexpected HTTP status at delete' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("removed") else false end))) and ($response["removed"] == (true))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("id") else false end))) and ($response["id"] == ($vars["watchId"]))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("removed") else false end))) and ($response["removed"] == (true))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("id") else false end))) and ($response["id"] == ($vars["watchId"]))' >/dev/null
printf '%s\n' 'delete: verified'
# verify: expected HTTP 200
DOCS_PATH="$(jq -cnr --argjson vars "$DOCS_VARIABLES" '"/v1/watchlists"')"
DOCS_STATUS=$(curl --silent --show-error --max-time 20 --output "$DOCS_RESPONSE_FILE" --write-out '%{http_code}' -X 'GET' "$DOCS_BASE_URL$DOCS_PATH" -H "Authorization: Bearer $HINTADATA_API_KEY")
[[ "$DOCS_STATUS" == '200' ]] || { printf '%s\n' 'Unexpected HTTP status at verify' >&2; exit 1; }
DOCS_RESPONSE=$(jq -c . "$DOCS_RESPONSE_FILE")
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("total") else false end))) and ($response["total"] == (0))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("total") else false end))) and ($response["total"] == (0))' >/dev/null
jq -en --argjson vars "$DOCS_VARIABLES" --argjson response "$DOCS_RESPONSE" --argjson status "$DOCS_STATUS" '((($response | if type == "object" or type == "array" then has("items") else false end))) and ($response["items"] == ([]))' >/dev/null
printf '%s\n' 'verify: verified'Varmista tulos
POST palauttaa 201, id:n ja revision 1. Tavoitehinnan päivitys nostaa revision arvoon 2; vanhentunut päivitys palauttaa 409. Korjattu keskeytys palauttaa 200 ja revision 3. DELETE palauttaa 200 ja removed: true; eristetty lista on lopuksi tyhjä. Omistajuus tulee tunnistetusta työtilasta ja ympäristöstä, ei asiakkaan antamasta omistajaotsakkeesta.
Palautuminen ja siivous
409 revision_conflict: listaa vahdit, lue nykyinen revisio, tarkista rinnakkaiset muutokset ja yritä tarkoitettua päivitystä uudelleen. 409 idempotency_conflict: älä muuta runkoa samalla avaimella; aidosti uusi luonti tarvitsee uuden avaimen. 503 delivery_unavailable: määritä tuotannon toimituspalvelu tai käytä sandbox-avainta. Jos suoritus keskeytyy, listaa vahdit ja poista vain itse luomasi tunniste.
Noudata 429- ja tilapäisissä 5xx-virheissä Retry-After-ohjetta ja rajaa uudelleenyritykset. Älä vie API-avaimia tai vastausrunkoja lokeihin tai tukipyyntöihin.