import hashlib import json import os import time import uuid import boto3 _dynamodb = boto3.resource("dynamodb") def handler(event, context): path = _path(event) if path not in ("/api/v1/query", "/api/v1/query_range"): return _json_response(404, {"status": "error", "errorType": "not_found", "error": "unsupported path"}) if not _authorized(event): return _json_response(401, {"status": "error", "errorType": "unauthorized", "error": "missing or invalid token"}) query = _query_text(event) script = _script() sequence = _sequence(script, query) count = _next_count(query) index = min(max(count - 1, 0), len(sequence) - 1) response = dict(sequence[index]) response.setdefault("status", "success") response.setdefault("data", _empty_data(path)) fixture = f"{query}#{index}" if query else f"default#{index}" _record_evidence(event, path, query, fixture, response) return _json_response(200, response) def _path(event): return event.get("rawPath") or event.get("path") or "" def _headers(event): return {str(k).lower(): v for k, v in (event.get("headers") or {}).items()} def _authorized(event): token = os.environ.get("CP9_O11Y_TOKEN", "").strip() if not token: return False auth = str(_headers(event).get("authorization", "")).strip() return auth == f"Bearer {token}" def _query_text(event): params = event.get("queryStringParameters") or {} if params.get("query"): return str(params["query"]) body = event.get("body") if not body: return "" try: parsed = json.loads(body) except json.JSONDecodeError: return "" return str(parsed.get("query") or "") def _script(): raw = os.environ.get("CP9_O11Y_SCRIPT", "").strip() if not raw: return {"default": [{"status": "success", "data": _empty_data("/api/v1/query")}]} return json.loads(raw) def _sequence(script, query): queries = script.get("queries") or {} selected = queries.get(query) if not selected: selected = script.get("default") if not selected: selected = [{"status": "success", "data": _empty_data("/api/v1/query")}] return selected def _next_count(query): table = _table() key = _query_key(query) result = table.update_item( Key={"pk": f"counter#{key}", "sk": "state"}, UpdateExpression="ADD request_count :one", ExpressionAttributeValues={":one": 1}, ReturnValues="UPDATED_NEW", ) return int(result.get("Attributes", {}).get("request_count", 1)) def _record_evidence(event, path, query, fixture, response): headers = _headers(event) request_id = headers.get("x-request-id") or headers.get("x-amzn-trace-id") or str(uuid.uuid4()) request_context = event.get("requestContext") or {} http_context = request_context.get("http") or {} timestamp = int(time.time() * 1000) _table().put_item( Item={ "pk": f"request#{request_id}", "sk": str(timestamp), "request_id": request_id, "timestamp": timestamp, "path": path, "method": http_context.get("method") or "", "source_ip": http_context.get("sourceIp") or "", "query": query, "query_hash": _query_key(query), "fixture": fixture, "response_status": response.get("status", ""), } ) def _table(): return _dynamodb.Table(os.environ["CP9_O11Y_EVIDENCE_TABLE"]) def _query_key(query): return hashlib.sha256(query.encode("utf-8")).hexdigest()[:24] def _empty_data(path): result_type = "matrix" if path == "/api/v1/query_range" else "vector" return {"resultType": result_type, "result": []} def _json_response(status_code, payload): return { "statusCode": status_code, "headers": {"Content-Type": "application/json"}, "body": json.dumps(payload, separators=(",", ":")), }