diff --git a/batch/batch/front_end/front_end.py b/batch/batch/front_end/front_end.py index d5348e5364a..7cc6121b187 100644 --- a/batch/batch/front_end/front_end.py +++ b/batch/batch/front_end/front_end.py @@ -219,6 +219,18 @@ def cast_query_param_to_bool(param: Optional[str]) -> bool: return True +def deprecated(fun): + @wraps(fun) + async def wrapped(request, *args, **kwargs): + response = await fun(request, *args, **kwargs) + response.headers["X-Hail-Deprecated"] = ( + f'The endpoint "{request.url.path}" is deprecated. Please upgrade your Hail version to the latest release.' + ) + return response + + return wrapped + + @routes.get('/healthcheck') async def get_healthcheck(_) -> web.Response: return web.Response() @@ -651,10 +663,10 @@ async def _get_full_job_status(app, record): raise -# deprecated @routes.get('/api/v1alpha/batches/{batch_id}/jobs/{job_id}/log') @billing_project_users_only() @add_metadata_to_request +@deprecated async def get_job_log(request: web.Request, _, batch_id: int) -> web.Response: job_id = int(request.match_info['job_id']) job_log_bytes = await _get_job_log(request.app, batch_id, job_id) @@ -836,10 +848,10 @@ def check_service_account_permissions(user, sa): raise web.HTTPBadRequest(reason=f'unauthorized service account {(sa["namespace"], sa["name"])} for user {user}') -# Deprecated. Use create_jobs_for_update instead @routes.post('/api/v1alpha/batches/{batch_id}/jobs/create') @auth.authenticated_users_only() @add_metadata_to_request +@deprecated # Use create_jobs_for_update instead async def create_jobs(request: web.Request, userdata: UserData) -> web.Response: app = request.app batch_id = int(request.match_info['batch_id']) @@ -2055,10 +2067,10 @@ async def cancel_job_group(request: web.Request, _, batch_id: int) -> web.Respon return web.Response() -# deprecated @routes.patch('/api/v1alpha/batches/{batch_id}/close') @auth.authenticated_users_only() @add_metadata_to_request +@deprecated async def close_batch(request, userdata): batch_id = int(request.match_info['batch_id']) user = userdata['username'] diff --git a/gear/gear/http_server_utils.py b/gear/gear/http_server_utils.py index e9f3cd7723f..8a873865fdd 100644 --- a/gear/gear/http_server_utils.py +++ b/gear/gear/http_server_utils.py @@ -1,3 +1,4 @@ +from collections.abc import Mapping from typing import Any, Callable, Optional import orjson @@ -8,5 +9,7 @@ async def json_request(request: web.Request) -> Any: return orjson.loads(await request.read()) -def json_response(data: Any, fallback_serializer: Optional[Callable[[Any], Any]] = None) -> web.Response: - return web.json_response(body=orjson.dumps(data, default=fallback_serializer)) +def json_response( + data: Any, fallback_serializer: Optional[Callable[[Any], Any]] = None, headers: Optional[Mapping] = None +) -> web.Response: + return web.json_response(body=orjson.dumps(data, default=fallback_serializer), headers=headers) diff --git a/hail/python/hailtop/batch_client/aioclient.py b/hail/python/hailtop/batch_client/aioclient.py index fdec4c2767c..4c9b626861d 100644 --- a/hail/python/hailtop/batch_client/aioclient.py +++ b/hail/python/hailtop/batch_client/aioclient.py @@ -5,6 +5,7 @@ import math import random import secrets +import warnings from enum import Enum from typing import Any, AsyncIterator, Dict, List, Optional, Tuple, TypedDict, Union, cast @@ -1303,17 +1304,27 @@ def __init__(self, billing_project: str, url: str, session: Session, headers: Di self._session: Session = session self._headers = headers + async def _warn_if_deprecated(self, response: aiohttp.ClientResponse) -> aiohttp.ClientResponse: + deprecation_message = response.headers.get("X-Hail-Deprecated") + if deprecation_message is not None: + warnings.warn(f"DEPRECATED: {deprecation_message}") + return response + async def _get(self, path, params=None) -> aiohttp.ClientResponse: - return await self._session.get(self.url + path, params=params, headers=self._headers) + return await self._warn_if_deprecated( + await self._session.get(self.url + path, params=params, headers=self._headers) + ) async def _post(self, path, data=None, json=None) -> aiohttp.ClientResponse: - return await self._session.post(self.url + path, data=data, json=json, headers=self._headers) + return await self._warn_if_deprecated( + await self._session.post(self.url + path, data=data, json=json, headers=self._headers) + ) async def _patch(self, path) -> aiohttp.ClientResponse: - return await self._session.patch(self.url + path, headers=self._headers) + return await self._warn_if_deprecated(await self._session.patch(self.url + path, headers=self._headers)) async def _delete(self, path) -> aiohttp.ClientResponse: - return await self._session.delete(self.url + path, headers=self._headers) + return await self._warn_if_deprecated(await self._session.delete(self.url + path, headers=self._headers)) def reset_billing_project(self, billing_project): self.billing_project = billing_project