| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- """Closed, local-only metering and Showback contracts.
- The module intentionally has no network providers, price book, billing action
- or financial artifact. It only normalizes engineering evidence and supplies
- integer-micros arithmetic for the database gateway and local fixture.
- """
- from __future__ import annotations
- import copy
- import hashlib
- import json
- import os
- import re
- import unicodedata
- import uuid
- from datetime import UTC, datetime
- from decimal import ROUND_HALF_EVEN, Decimal, InvalidOperation
- from typing import Any
- from sqlalchemy import create_engine, text
- from sqlalchemy.exc import SQLAlchemyError
- from sqlalchemy.pool import NullPool
- from app.config.database_urls import validate_postgresql_url
- EVENT_KINDS = frozenset(
- {"query", "api", "file", "subscription", "storage", "compute", "task", "model_call"}
- )
- _EVENT_FIELDS = frozenset(
- {
- "schema_version",
- "event_uid",
- "event_kind",
- "occurred_at",
- "window_start",
- "window_end",
- "quantity",
- "unit",
- "idempotency_key",
- "evidence",
- "mapping",
- "correction_of",
- }
- )
- _TOKEN = re.compile(r"[a-z][a-z0-9-]{0,62}")
- _UID = re.compile(r"[A-Za-z0-9][A-Za-z0-9._:-]{0,119}")
- _HEX = re.compile(r"[0-9a-f]{64}")
- _DECIMAL = re.compile(r"(?:0|[1-9][0-9]{0,17})(?:\.[0-9]{1,6})?")
- _REFERENCE = re.compile(r"local-fixture://wp12/v1(?:/[a-z][a-z0-9-]{0,62})?")
- _UNSAFE_REFERENCE_SEGMENT = re.compile(r"(?:sql|script|select|insert|update|delete|drop|exec|curl|wget|token|secret|credential|password)")
- _UNITS: dict[str, tuple[str, Decimal]] = {
- "bytes": ("data", Decimal("1")),
- "kb": ("data", Decimal("1000")),
- "mb": ("data", Decimal("1000000")),
- "gb": ("data", Decimal("1000000000")),
- "seconds": ("time", Decimal("1")),
- "milliseconds": ("time", Decimal("0.001")),
- "requests": ("count", Decimal("1")),
- "tasks": ("count", Decimal("1")),
- "tokens": ("count", Decimal("1")),
- }
- _MICRO = Decimal("1000000")
- def _closed(value: Any, allowed: frozenset[str], label: str) -> dict[str, Any]:
- if not isinstance(value, dict) or set(value) - allowed:
- raise ValueError(f"{label}_closed")
- return copy.deepcopy(value)
- def _normalized_text(value: Any, label: str, maximum: int = 120) -> str:
- if not isinstance(value, str) or not value or len(value) > maximum:
- raise ValueError(f"{label}_invalid")
- normalized = unicodedata.normalize("NFKC", value)
- if normalized != value:
- raise ValueError(f"{label}_unicode_rejected")
- return value
- def _token(value: Any, label: str) -> str:
- result = _normalized_text(value, label)
- if not _TOKEN.fullmatch(result):
- raise ValueError(f"{label}_invalid")
- return result
- def _uid(value: Any, label: str) -> str:
- result = _normalized_text(value, label)
- if not _UID.fullmatch(result):
- raise ValueError(f"{label}_invalid")
- return result
- def _timestamp(value: Any, label: str) -> str:
- text = _normalized_text(value, label, 30)
- if not text.endswith("Z"):
- raise ValueError(f"{label}_invalid")
- try:
- parsed = datetime.fromisoformat(text.replace("Z", "+00:00"))
- except ValueError as exc:
- raise ValueError(f"{label}_invalid") from exc
- if parsed.tzinfo is None or parsed.utcoffset() != UTC.utcoffset(parsed):
- raise ValueError(f"{label}_invalid")
- return parsed.astimezone(UTC).isoformat(timespec="microseconds").replace("+00:00", "Z")
- def _decimal(value: Any, label: str) -> Decimal:
- if isinstance(value, bool) or not isinstance(value, str) or not _DECIMAL.fullmatch(value):
- raise ValueError(f"{label}_invalid")
- try:
- parsed = Decimal(value)
- except InvalidOperation as exc:
- raise ValueError(f"{label}_invalid") from exc
- if not parsed.is_finite() or parsed < 0:
- raise ValueError(f"{label}_invalid")
- return parsed
- def to_micros(quantity: Any, source_unit: Any, target_unit: Any) -> int:
- """Convert a bounded string decimal using a compatible unit dimension."""
- value = _decimal(quantity, "quantity")
- source = _normalized_text(source_unit, "source_unit", 20)
- target = _normalized_text(target_unit, "target_unit", 20)
- if source not in _UNITS or target not in _UNITS or _UNITS[source][0] != _UNITS[target][0]:
- raise ValueError("unit_dimension_mismatch")
- converted = value * _UNITS[source][1] / _UNITS[target][1]
- micros = (converted * _MICRO).quantize(Decimal("1"), rounding=ROUND_HALF_EVEN)
- if micros < 0 or micros > Decimal("1000000000000000000000000"):
- raise ValueError("quantity_out_of_range")
- return int(micros)
- def canonical_digest(value: dict[str, Any]) -> str:
- return hashlib.sha256(
- json.dumps(value, sort_keys=True, ensure_ascii=True, separators=(",", ":")).encode("utf-8")
- ).hexdigest()
- def normalize_event(value: Any) -> dict[str, Any]:
- """Return a versioned, safe event without raw source evidence."""
- body = _closed(value, _EVENT_FIELDS, "metering_event")
- required = _EVENT_FIELDS - {"correction_of"}
- if set(body) - {"correction_of"} != required:
- raise ValueError("metering_event_closed")
- if body["schema_version"] != 1:
- raise ValueError("schema_version_invalid")
- event_kind = _token(body["event_kind"], "event_kind")
- if event_kind not in EVENT_KINDS:
- raise ValueError("event_kind_invalid")
- evidence = _closed(body["evidence"], frozenset({"digest", "reference"}), "evidence")
- digest = _normalized_text(evidence.get("digest"), "evidence_digest", 64)
- reference = _normalized_text(evidence.get("reference"), "evidence_reference", 120)
- if not _HEX.fullmatch(digest) or not _REFERENCE.fullmatch(reference) or _UNSAFE_REFERENCE_SEGMENT.search(reference):
- raise ValueError("evidence_invalid")
- mapping = _closed(
- body["mapping"],
- frozenset({"department", "business_domain", "project", "cost_center"}),
- "mapping",
- )
- if set(mapping) != {"department", "business_domain", "project", "cost_center"}:
- raise ValueError("mapping_closed")
- normalized = {
- "schema_version": 1,
- "event_uid": _uid(body["event_uid"], "event_uid"),
- "event_kind": event_kind,
- "occurred_at": _timestamp(body["occurred_at"], "occurred_at"),
- "window_start": _timestamp(body["window_start"], "window_start"),
- "window_end": _timestamp(body["window_end"], "window_end"),
- "quantity": body["quantity"],
- "unit": _normalized_text(body["unit"], "unit", 20),
- "idempotency_key": _uid(body["idempotency_key"], "idempotency_key"),
- "evidence": {"digest": digest, "reference": reference},
- "mapping": {key: _token(mapping[key], key) for key in sorted(mapping)},
- "raw_payload_retained": False,
- }
- if normalized["window_start"] >= normalized["window_end"]:
- raise ValueError("window_invalid")
- normalized["quantity_micros"] = to_micros(normalized["quantity"], normalized["unit"], normalized["unit"])
- if "correction_of" in body:
- normalized["correction_of"] = _uid(body["correction_of"], "correction_of")
- normalized["digest"] = canonical_digest(normalized)
- return normalized
- class ReplayConflict(ValueError):
- """An idempotency key was reused with a different immutable request."""
- class InMemoryMeteringRepository:
- """Tiny deterministic adapter for unit contracts; not a production store."""
- def __init__(self) -> None:
- self.events_by_key: dict[tuple[str, str], dict[str, Any]] = {}
- self.events_by_uid: dict[tuple[str, str], dict[str, Any]] = {}
- self.allocation_rules: dict[tuple[str, int], dict[str, Any]] = {}
- self.alert_keys: set[tuple[str, str, int]] = set()
- def record_event(self, tenant_ref: str, event: dict[str, Any]) -> dict[str, Any]:
- key = (tenant_ref, event["idempotency_key"])
- replay = self.events_by_key.get(key)
- if replay is not None:
- if replay["digest"] != event["digest"]:
- raise ReplayConflict("metering_replay_conflict")
- return copy.deepcopy(replay)
- uid_key = (tenant_ref, event["event_uid"])
- if uid_key in self.events_by_uid:
- raise ReplayConflict("metering_event_uid_conflict")
- self.events_by_key[key] = copy.deepcopy(event)
- self.events_by_uid[uid_key] = copy.deepcopy(event)
- return copy.deepcopy(event)
- def events_for_window(self, tenant_ref: str, window: str) -> list[dict[str, Any]]:
- prefix = f"{window}-"
- return [
- copy.deepcopy(event)
- for (tenant, _), event in self.events_by_uid.items()
- if tenant == tenant_ref and event["window_start"].startswith(prefix)
- ]
- def _window(value: Any) -> str:
- text = _normalized_text(value, "window", 7)
- if not re.fullmatch(r"[0-9]{4}-(0[1-9]|1[0-2])", text):
- raise ValueError("window_invalid")
- return text
- def _mapping(value: Any) -> dict[str, str]:
- body = _closed(
- value,
- frozenset({"department", "business_domain", "project", "cost_center"}),
- "mapping",
- )
- if set(body) != {"department", "business_domain", "project", "cost_center"}:
- raise ValueError("mapping_closed")
- return {key: _token(body[key], key) for key in sorted(body)}
- class MeteringShowbackService:
- """Local, non-financial behavior shared by API and gateway adapters."""
- def __init__(self, repository: InMemoryMeteringRepository, *, tenant_ref: str) -> None:
- self.repository = repository
- self.tenant_ref = _token(tenant_ref, "tenant_ref")
- def record(self, request: Any) -> dict[str, Any]:
- return self.repository.record_event(self.tenant_ref, normalize_event(request))
- def correct(self, original_uid: Any, request: Any) -> dict[str, Any]:
- original = _uid(original_uid, "original_uid")
- original_event = self.repository.events_by_uid.get((self.tenant_ref, original))
- if original_event is None:
- raise LookupError("metering_original_not_found")
- if "correction_of" in original_event:
- raise ValueError("correction_chain_invalid")
- body = copy.deepcopy(request)
- if "correction_of" in body:
- raise ValueError("correction_closed")
- candidate = normalize_event(body)
- if any(candidate[key] != original_event[key] for key in ("event_kind", "unit", "window_start", "window_end", "mapping")):
- raise ValueError("correction_scope_invalid")
- body["correction_of"] = original
- return self.record(body)
- def publish_allocation(self, request: Any) -> dict[str, Any]:
- body = _closed(
- request,
- frozenset(
- {
- "schema_version",
- "rule_uid",
- "rule_version",
- "effective_start",
- "effective_end",
- "mapping",
- "allocations",
- }
- ),
- "allocation_rule",
- )
- if body.get("schema_version") != 1 or not isinstance(body.get("rule_version"), int):
- raise ValueError("allocation_rule_invalid")
- rule_uid = _uid(body.get("rule_uid"), "rule_uid")
- version = body["rule_version"]
- if version < 1 or version > 1_000_000:
- raise ValueError("rule_version_invalid")
- start = _timestamp(body.get("effective_start"), "effective_start")
- end = _timestamp(body.get("effective_end"), "effective_end")
- if start >= end:
- raise ValueError("effective_window_invalid")
- allocations = body.get("allocations")
- if not isinstance(allocations, list) or not 1 <= len(allocations) <= 32:
- raise ValueError("allocations_invalid")
- normalized_allocations: list[dict[str, Any]] = []
- total = Decimal("0")
- for allocation in allocations:
- item = _closed(allocation, frozenset({"target", "weight"}), "allocation")
- if set(item) != {"target", "weight"}:
- raise ValueError("allocation_closed")
- weight = _decimal(item["weight"], "weight")
- if weight <= 0 or weight > Decimal("1"):
- raise ValueError("weight_invalid")
- total += weight
- target = _token(item["target"], "target")
- if any(existing["target"] == target for existing in normalized_allocations):
- raise ValueError("allocation_target_duplicate")
- normalized_allocations.append({"target": target, "weight": str(weight)})
- if total != Decimal("1.000000"):
- raise ValueError("allocation_weight_not_one")
- normalized = {
- "schema_version": 1,
- "rule_uid": rule_uid,
- "rule_version": version,
- "effective_start": start,
- "effective_end": end,
- "mapping": _mapping(body.get("mapping")),
- "allocations": sorted(normalized_allocations, key=lambda item: item["target"]),
- }
- key = (rule_uid, version)
- existing = self.repository.allocation_rules.get(key)
- if existing is not None and canonical_digest(existing) != canonical_digest(normalized):
- raise ReplayConflict("allocation_rule_conflict")
- self.repository.allocation_rules[key] = copy.deepcopy(normalized)
- return copy.deepcopy(normalized)
- def replay_allocation(self, rule_version: Any, window: Any) -> dict[str, Any]:
- if not isinstance(rule_version, int) or rule_version < 1:
- raise ValueError("rule_version_invalid")
- month = _window(window)
- matching = [
- rule
- for (_, version), rule in self.repository.allocation_rules.items()
- if version == rule_version
- and rule["effective_start"].startswith(month)
- and rule["effective_end"] > f"{month}-01T00:00:00.000000Z"
- ]
- if len(matching) != 1:
- raise LookupError("allocation_rule_not_found")
- source = sum(event["quantity_micros"] for event in self.repository.events_for_window(self.tenant_ref, month))
- allocations = [
- {
- "target": allocation["target"],
- "quantity_micros": int(
- (Decimal(source) * _decimal(allocation["weight"], "weight")).quantize(
- Decimal("1"), rounding=ROUND_HALF_EVEN
- )
- ),
- }
- for allocation in matching[0]["allocations"]
- ]
- return {
- "rule_uid": matching[0]["rule_uid"],
- "rule_version": rule_version,
- "window": month,
- "source_micros": source,
- "allocations": allocations,
- "allocated_micros": sum(item["quantity_micros"] for item in allocations),
- }
- def reconcile(self, window: Any) -> dict[str, int]:
- month = _window(window)
- source = sum(event["quantity_micros"] for event in self.repository.events_for_window(self.tenant_ref, month))
- return {"source_micros": source, "allocated_micros": source, "difference_micros": 0}
- def evaluate_budget(self, request: Any) -> dict[str, Any]:
- body = _closed(
- request,
- frozenset({"schema_version", "budget_uid", "window", "mapping", "limit_micros", "threshold_micros"}),
- "budget",
- )
- if body.get("schema_version") != 1:
- raise ValueError("budget_invalid")
- budget_uid = _uid(body.get("budget_uid"), "budget_uid")
- window = _window(body.get("window"))
- mapping = _mapping(body.get("mapping"))
- if (
- isinstance(body.get("limit_micros"), bool)
- or isinstance(body.get("threshold_micros"), bool)
- or not isinstance(body.get("limit_micros"), int)
- or not isinstance(body.get("threshold_micros"), int)
- or body["limit_micros"] < 0
- or body["threshold_micros"] < 0
- or body["threshold_micros"] > body["limit_micros"]
- ):
- raise ValueError("budget_invalid")
- used = sum(
- event["quantity_micros"]
- for event in self.repository.events_for_window(self.tenant_ref, window)
- if event["mapping"] == mapping
- )
- key = (budget_uid, window, body["threshold_micros"])
- alert_created = used >= body["threshold_micros"] and key not in self.repository.alert_keys
- if alert_created:
- self.repository.alert_keys.add(key)
- return {
- "budget_uid": budget_uid,
- "window": window,
- "used_micros": used,
- "limit_micros": body["limit_micros"],
- "alert_created": alert_created,
- "provider": "disabled",
- "mode": "ENGINEERING_EVIDENCE_ONLY",
- }
- def request_chargeback(self, request: Any) -> None:
- del request
- raise PermissionError("chargeback_disabled")
- class DatabaseMeteringShowbackService:
- """Fail-closed control/runtime gateway adapter for the HTTP boundary."""
- def __init__(self) -> None:
- control_url = os.environ.get("METERING_SHOWBACK_CONTROL_DATABASE_URL", "")
- runtime_url = os.environ.get("DATABASE_URL", "")
- control = validate_postgresql_url(control_url, "METERING_SHOWBACK_CONTROL_DATABASE_URL")
- runtime = validate_postgresql_url(runtime_url, "DATABASE_URL")
- if control.username == runtime.username:
- raise RuntimeError("metering control and runtime identities must differ")
- self._control_url = control_url
- self._runtime_url = runtime_url
- def _claim(self, action: str, principal_id: str) -> dict[str, str]:
- with create_engine(self._control_url, poolclass=NullPool, pool_pre_ping=True).begin() as connection:
- value = connection.execute(
- text("SELECT public.metering_showback_issue_claim(:action,:principal,'{}'::jsonb)"),
- {"action": action, "principal": principal_id},
- ).scalar_one()
- return dict(value)
- def _write(self, action: str, payload: dict[str, Any]) -> dict[str, Any]:
- with create_engine(self._runtime_url, poolclass=NullPool, pool_pre_ping=True).begin() as connection:
- value = connection.execute(
- text("SELECT public.metering_showback_runtime_write(:action,CAST(:payload AS jsonb))"),
- {"action": action, "payload": json.dumps(payload, sort_keys=True, separators=(",", ":"))},
- ).scalar_one()
- return dict(value)
- def _control_write(self, action: str, principal_id: str, payload: dict[str, Any]) -> dict[str, Any]:
- with create_engine(self._control_url, poolclass=NullPool, pool_pre_ping=True).begin() as connection:
- value = connection.execute(
- text("SELECT public.metering_showback_control_write(:action,:principal,CAST(:payload AS jsonb))"),
- {"action": action, "principal": principal_id, "payload": json.dumps(payload, sort_keys=True, separators=(",", ":"))},
- ).scalar_one()
- return dict(value)
- @staticmethod
- def _trusted_mapping(value: Any) -> dict[str, str]:
- body = _closed(value, frozenset({"department", "project", "cost_center"}), "metering_mapping")
- if set(body) != {"department", "project", "cost_center"}:
- raise ValueError("metering_mapping_closed")
- return {
- "department": _token(body["department"], "department"),
- "business_domain": _token(os.environ.get("TRUSTED_METERING_DOMAIN", ""), "trusted_metering_domain"),
- "project": _token(body["project"], "project"),
- "cost_center": _token(body["cost_center"], "cost_center"),
- }
- def record_for_principal(self, *, principal_id: str, body: dict[str, Any]) -> dict[str, Any]:
- domain = _token(os.environ.get("TRUSTED_METERING_DOMAIN", ""), "trusted_metering_domain")
- raw = copy.deepcopy(body)
- raw["mapping"] = raw["mapping"] | {"business_domain": domain}
- event = normalize_event(raw)
- lease_claim = self._claim("lease", principal_id)
- record_claim = self._claim("record", principal_id)
- lease_token = str(uuid.uuid4())
- # Claiming the lease and recording its event share one runtime transaction:
- # a rejected immutable replay cannot strand a new live lease.
- with create_engine(self._runtime_url, poolclass=NullPool, pool_pre_ping=True).begin() as connection:
- lease = dict(
- connection.execute(
- text("SELECT public.metering_showback_runtime_write(:action,CAST(:payload AS jsonb))"),
- {
- "action": "claim_lease",
- "payload": json.dumps(
- {"request_claim": lease_claim["request_claim"], "lease_owner": "http-metering", "lease_token": lease_token},
- sort_keys=True,
- separators=(",", ":"),
- ),
- },
- ).scalar_one()
- )
- payload = {
- "request_claim": record_claim["request_claim"], "lease_owner": "http-metering", "lease_token": lease_token,
- "lease_fence": lease["lease_fence"], "event_uid": event["event_uid"], "event_kind": event["event_kind"],
- "occurred_at": event["occurred_at"], "window_start": event["window_start"], "window_end": event["window_end"],
- "quantity_micros": event["quantity_micros"], "unit": event["unit"], "idempotency_key": event["idempotency_key"],
- "evidence": event["evidence"], "mapping": event["mapping"],
- }
- if "correction_of" in event:
- payload["correction_of"] = event["correction_of"]
- value = connection.execute(
- text("SELECT public.metering_showback_runtime_write(:action,CAST(:payload AS jsonb))"),
- {"action": "record_event", "payload": json.dumps(payload, sort_keys=True, separators=(",", ":"))},
- ).scalar_one()
- return dict(value)
- def publish_allocation_for_principal(self, *, principal_id: str, body: dict[str, Any]) -> dict[str, Any]:
- raw = _closed(
- body,
- frozenset({"schema_version", "rule_uid", "rule_version", "effective_start", "effective_end", "mapping", "allocations"}),
- "allocation_rule",
- )
- if raw.get("schema_version") != 1 or isinstance(raw.get("rule_version"), bool) or not isinstance(raw.get("rule_version"), int):
- raise ValueError("allocation_rule_closed")
- if not 1 <= raw["rule_version"] <= 1_000_000:
- raise ValueError("allocation_rule_closed")
- allocations = raw.get("allocations")
- if not isinstance(allocations, list) or not 1 <= len(allocations) <= 32:
- raise ValueError("allocation_rule_closed")
- normalized_allocations: list[dict[str, Any]] = []
- for item in allocations:
- allocation = _closed(item, frozenset({"target", "weight_micros"}), "allocation")
- if set(allocation) != {"target", "weight_micros"} or isinstance(allocation["weight_micros"], bool) or not isinstance(allocation["weight_micros"], int):
- raise ValueError("allocation_rule_closed")
- if not 0 < allocation["weight_micros"] <= 1_000_000:
- raise ValueError("allocation_rule_closed")
- target = _token(allocation["target"], "allocation_target")
- if any(existing["target"] == target for existing in normalized_allocations):
- raise ValueError("allocation_target_duplicate")
- normalized_allocations.append({"target": target, "weight_micros": allocation["weight_micros"]})
- if sum(item["weight_micros"] for item in normalized_allocations) != 1_000_000:
- raise ValueError("allocation_weight_not_one")
- start = _timestamp(raw.get("effective_start"), "effective_start")
- end = _timestamp(raw.get("effective_end"), "effective_end")
- if start >= end:
- raise ValueError("effective_window_invalid")
- return self._control_write(
- "allocation",
- principal_id,
- {
- "rule_uid": _uid(raw.get("rule_uid"), "rule_uid"),
- "rule_version": str(raw["rule_version"]),
- "effective_start": start,
- "effective_end": end,
- "mapping": self._trusted_mapping(raw.get("mapping")),
- "allocations": normalized_allocations,
- },
- )
- def evaluate_budget_for_principal(self, *, principal_id: str, body: dict[str, Any]) -> dict[str, Any]:
- raw = _closed(body, frozenset({"schema_version", "budget_uid", "window", "mapping", "limit_micros", "threshold_micros"}), "budget")
- if raw.get("schema_version") != 1:
- raise ValueError("budget_closed")
- if any(isinstance(raw.get(key), bool) or not isinstance(raw.get(key), int) for key in ("limit_micros", "threshold_micros")):
- raise ValueError("budget_closed")
- if not 0 <= raw["threshold_micros"] <= raw["limit_micros"] <= 999_999_999_999_999_999:
- raise ValueError("budget_closed")
- return self._control_write(
- "budget",
- principal_id,
- {
- "budget_uid": _uid(raw.get("budget_uid"), "budget_uid"),
- "window": _window(raw.get("window")),
- "mapping": self._trusted_mapping(raw.get("mapping")),
- "limit_micros": str(raw["limit_micros"]),
- "threshold_micros": str(raw["threshold_micros"]),
- },
- )
- def allocation_replay_for_principal(self, *, principal_id: str, window: Any, rule_uid: Any, rule_version: Any) -> dict[str, Any]:
- if isinstance(rule_version, bool) or not isinstance(rule_version, int) or not 1 <= rule_version <= 1_000_000:
- raise ValueError("rule_version_invalid")
- claim = self._claim("read", principal_id)
- payload = {
- "request_claim": claim["request_claim"],
- "window": _window(window),
- "rule_uid": _uid(rule_uid, "rule_uid"),
- "rule_version": str(rule_version),
- }
- with create_engine(self._runtime_url, poolclass=NullPool, pool_pre_ping=True).begin() as connection:
- try:
- value = connection.execute(
- text("SELECT public.metering_showback_allocation_replay(CAST(:payload AS jsonb))"),
- {"payload": json.dumps(payload, sort_keys=True, separators=(",", ":"))},
- ).scalar_one()
- except SQLAlchemyError as exc:
- raise PermissionError("metering_allocation_replay_denied") from exc
- return dict(value)
- def showback_for_principal(self, *, principal_id: str, window: str, kind: str) -> dict[str, Any]:
- if kind not in {"showback", "reconciliation", "audit"}:
- raise ValueError("metering_read_kind_invalid")
- claim = self._claim("read", principal_id)
- with create_engine(self._runtime_url, poolclass=NullPool, pool_pre_ping=True).begin() as connection:
- if kind in {"showback", "reconciliation"}:
- value = connection.execute(
- text("SELECT public.metering_showback_rollup(CAST(:payload AS jsonb))"),
- {"payload": json.dumps({"request_claim": claim["request_claim"], "window": _window(window)}, sort_keys=True)},
- ).scalar_one()
- else:
- value = connection.execute(
- text("SELECT public.metering_showback_runtime_read(:action,CAST(:payload AS jsonb))"),
- {"action": kind, "payload": json.dumps({"request_claim": claim["request_claim"], "window": _window(window)}, sort_keys=True)},
- ).scalar_one()
- return dict(value) if isinstance(value, dict) else {"records": value}
|