STATE-WP-0069 T04: Sunset headers and POST /progress body metering
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Add Sunset to legacy compat responses (Jun 2027 planning horizon).
Meter POST /progress/ bodies that use workstream_id; wire hub-core body
hook. Consolidate workstreams deprecation headers via legacy_compat.
This commit is contained in:
tegwick 2026-07-08 23:06:45 +02:00
parent 05a138bc1c
commit 6e5e150803
8 changed files with 126 additions and 14 deletions

View file

@ -11,12 +11,16 @@ from api.services.legacy_meter import identity_from_request, record_legacy_usage
logger = logging.getLogger(__name__)
_LEGACY_OWNER = "state-hub.api"
# Planning horizon for legacy workstream interfaces; actual removal remains
# legacy-meter-gated per docs/workplan-terminology-transition.md.
_LEGACY_WORKSTREAM_SUNSET = "Wed, 30 Jun 2027 23:59:59 GMT"
def mark_legacy_response(response: Response | None, replacement_ref: str) -> None:
if response is None:
return
response.headers["Deprecation"] = "true"
response.headers["Sunset"] = _LEGACY_WORKSTREAM_SUNSET
response.headers["X-StateHub-Replacement"] = replacement_ref
response.headers.append("Link", f'<{replacement_ref}>; rel="successor-version"')
@ -37,6 +41,37 @@ async def meter_legacy_query_param(
) -> None:
interface_key = legacy_query_param_key(method, route, param)
mark_legacy_response(response, replacement_ref)
try:
await record_legacy_usage(
session,
interface_key=interface_key,
interface_kind="rest_api",
replacement_ref=replacement_ref,
owner_component=_LEGACY_OWNER,
replacement_verified=True,
identity=identity_from_request(request),
)
except Exception:
await session.rollback()
logger.warning("legacy-meter failed to record %s", interface_key, exc_info=True)
def legacy_body_field_key(method: str, route: str, field: str = "workstream_id") -> str:
return f"rest_api:{method} {route} {field}"
async def meter_legacy_body_field(
*,
session: AsyncSession,
request: Request | None,
response: Response | None,
method: str,
route: str,
replacement_ref: str,
field: str = "workstream_id",
) -> None:
interface_key = legacy_body_field_key(method, route, field)
mark_legacy_response(response, replacement_ref)
try:
await record_legacy_usage(
session,