From f606451c6a76aef7abb93632f71fc6144aab2beb Mon Sep 17 00:00:00 2001 From: Ojuswi Rastogi Date: Thu, 5 Jun 2025 20:28:38 +0530 Subject: [PATCH 1/2] Refactor: Replace status codes with HTTP enums Signed-off-by: Ojuswi Rastogi --- operate/cli.py | 60 +++++++++++-------- .../contracts/foreign_omnibridge/contract.py | 2 +- .../foreign_omnibridge/contract.yaml | 2 +- operate/operate_http/__init__.py | 3 +- operate/operate_http/exceptions.py | 10 ++-- operate/quickstart/run_service.py | 3 +- operate/services/health_checker.py | 3 +- operate/services/manage.py | 3 +- operate/services/utils/tendermint.py | 35 ++++++++--- 9 files changed, 77 insertions(+), 44 deletions(-) diff --git a/operate/cli.py b/operate/cli.py index 87153adcd..f0cfb03bb 100644 --- a/operate/cli.py +++ b/operate/cli.py @@ -71,14 +71,15 @@ ).encode() DEFAULT_MAX_RETRIES = 3 USER_NOT_LOGGED_IN_ERROR = JSONResponse( - content={"error": "User not logged in!"}, status_code=401 + content={"error": "User not logged in!"}, status_code=HTTPStatus.UNAUTHORIZED ) def service_not_found_error(service_config_id: str) -> JSONResponse: """Service not found error response""" return JSONResponse( - content={"error": f"Service {service_config_id} not found"}, status_code=404 + content={"error": f"Service {service_config_id} not found"}, + status_code=HTTPStatus.NOT_FOUND, ) @@ -351,14 +352,19 @@ async def _call(request: Request) -> JSONResponse: else: error["error"] = str(e) errors.append(error) - return JSONResponse(content={"errors": errors}, status_code=500) + return JSONResponse( + content={"errors": errors}, + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + ) except Exception as e: # pylint: disable=broad-except errors.append( {"error": str(e.args[0]), "traceback": traceback.format_exc()} ) logger.error(f"Error {str(e.args[0])}\n{traceback.format_exc()}") retries += 1 - return JSONResponse(content={"errors": errors}, status_code=500) + return JSONResponse( + content={"errors": errors}, status_code=HTTPStatus.INTERNAL_SERVER_ERROR + ) return _call @@ -396,7 +402,7 @@ async def _setup_account(request: Request) -> t.Dict: if operate.user_account is not None: return JSONResponse( content={"error": "Account already exists"}, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) data = await request.json() @@ -414,7 +420,7 @@ async def _update_password( # pylint: disable=too-many-return-statements if operate.user_account is None: return JSONResponse( content={"error": "Account does not exist."}, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) data = await request.json() @@ -427,7 +433,7 @@ async def _update_password( # pylint: disable=too-many-return-statements content={ "error": "You must provide exactly one of 'old_password' or 'mnemonic' (seed phrase).", }, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) if old_password and mnemonic: @@ -435,7 +441,7 @@ async def _update_password( # pylint: disable=too-many-return-statements content={ "error": "You must provide exactly one of 'old_password' or 'mnemonic' (seed phrase), but not both.", }, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) try: @@ -457,11 +463,13 @@ async def _update_password( # pylint: disable=too-many-return-statements content={"error": None, "message": "Password not updated."} ) except ValueError as e: - return JSONResponse(content={"error": str(e)}, status_code=400) + return JSONResponse( + content={"error": str(e)}, status_code=HTTPStatus.BAD_REQUEST + ) except Exception as e: # pylint: disable=broad-except return JSONResponse( content={"error": str(e), "traceback": traceback.format_exc()}, - status_code=400, + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, ) @app.post("/api/account/login") @@ -471,20 +479,20 @@ async def _validate_password(request: Request) -> t.Dict: if operate.user_account is None: return JSONResponse( content={"error": "Account does not exist"}, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) data = await request.json() if not operate.user_account.is_valid(password=data["password"]): return JSONResponse( content={"error": "Password is not valid"}, - status_code=401, + status_code=HTTPStatus.UNAUTHORIZED, ) operate.password = data["password"] return JSONResponse( content={"message": "Login successful"}, - status_code=200, + status_code=HTTPStatus.OK, ) @app.get("/api/wallet") @@ -503,13 +511,13 @@ async def _create_wallet(request: Request) -> t.List[t.Dict]: if operate.user_account is None: return JSONResponse( content={"error": "Cannot create wallet; User account does not exist!"}, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) if operate.password is None: return JSONResponse( content={"error": "You need to login before creating a wallet"}, - status_code=401, + status_code=HTTPStatus.UNAUTHORIZED, ) data = await request.json() @@ -556,7 +564,7 @@ async def _get_safe(request: Request) -> t.List[t.Dict]: if not manager.exists(ledger_type=ledger_type): return JSONResponse( content={"error": "Wallet does not exist"}, - status_code=404, + status_code=HTTPStatus.NOT_FOUND, ) safes = manager.load(ledger_type=ledger_type).safes if safes is None or safes.get(chain) is None: @@ -576,13 +584,13 @@ async def _create_safe( # pylint: disable=too-many-return-statements if operate.user_account is None: return JSONResponse( content={"error": "Cannot create safe; User account does not exist!"}, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) if operate.password is None: return JSONResponse( content={"error": "You need to login before creating a safe"}, - status_code=401, + status_code=HTTPStatus.UNAUTHORIZED, ) data = await request.json() @@ -592,7 +600,7 @@ async def _create_safe( # pylint: disable=too-many-return-statements content={ "error": "You can only specify 'initial_funds' or 'transfer_excess_assets', but not both." }, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) logger.info(f"POST /api/wallet/safe {data=}") @@ -668,7 +676,7 @@ async def _create_safe( # pylint: disable=too-many-return-statements except Exception as e: # pylint: disable=broad-except logger.error(traceback.format_exc()) return JSONResponse( - status_code=500, + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, content={"error": str(e), "traceback": traceback.format_exc()}, ) @@ -680,13 +688,13 @@ async def _update_safe(request: Request) -> t.List[t.Dict]: if operate.user_account is None: return JSONResponse( content={"error": "Cannot update safe; User account does not exist!"}, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) if operate.password is None: return JSONResponse( content={"error": "You need to login before updating a safe."}, - status_code=401, + status_code=HTTPStatus.UNAUTHORIZED, ) data = await request.json() @@ -694,7 +702,7 @@ async def _update_safe(request: Request) -> t.List[t.Dict]: if "chain" not in data: return JSONResponse( content={"error": "You need to specify a chain to update a safe."}, - status_code=401, + status_code=HTTPStatus.BAD_REQUEST, ) chain = Chain(data["chain"]) @@ -703,7 +711,7 @@ async def _update_safe(request: Request) -> t.List[t.Dict]: if not manager.exists(ledger_type=ledger_type): return JSONResponse( content={"error": "Wallet does not exist"}, - status_code=401, + status_code=HTTPStatus.BAD_REQUEST, ) wallet = manager.load(ledger_type=ledger_type) @@ -906,7 +914,7 @@ async def _withdraw_onchain(request: Request) -> JSONResponse: if withdrawal_address is None: return JSONResponse( content={"error": "withdrawal_address is required"}, - status_code=400, + status_code=HTTPStatus.BAD_REQUEST, ) try: @@ -949,7 +957,7 @@ async def _withdraw_onchain(request: Request) -> JSONResponse: except Exception as e: # pylint: disable=broad-except logger.error(traceback.format_exc()) return JSONResponse( - status_code=500, + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, content={"error": str(e), "traceback": traceback.format_exc()}, ) diff --git a/operate/data/contracts/foreign_omnibridge/contract.py b/operate/data/contracts/foreign_omnibridge/contract.py index 7bd2337db..a1d8d354f 100644 --- a/operate/data/contracts/foreign_omnibridge/contract.py +++ b/operate/data/contracts/foreign_omnibridge/contract.py @@ -30,7 +30,7 @@ PLACEHOLDER_NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" # nosec -#DEFAULT_GAS_BRIDGE_ETH_TO = 800_000 +# DEFAULT_GAS_BRIDGE_ETH_TO = 800_000 DEFAULT_GAS_RELAY_TOKENS = 800_000 # By simulations, nonzero-ERC20-bridge gas ~ 1.05 zero-ERC20-bridge gas diff --git a/operate/data/contracts/foreign_omnibridge/contract.yaml b/operate/data/contracts/foreign_omnibridge/contract.yaml index 4e9eba2b4..2d3dcfedd 100644 --- a/operate/data/contracts/foreign_omnibridge/contract.yaml +++ b/operate/data/contracts/foreign_omnibridge/contract.yaml @@ -8,7 +8,7 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeibsmumov3s36vfo24xp2niilcp3ywju2d4yqfadllyjncqtgtndly build/ForeignOmnibridge.json: bafybeibmcflt7w5p5szgii7glbtrvjahweclowz2k7e6qjq7yfbvszy6em - contract.py: bafybeidjjdhjr2rj3q2q27fhm366kt4qpsvjqg3ytpa2titic5sari6r24 + contract.py: bafybeihywi67spsgsmcbqgjrcqlwgiq2e36ymssznqubfxwedforxek4ri fingerprint_ignore_patterns: [] contracts: [] class_name: ForeignOmnibridge diff --git a/operate/operate_http/__init__.py b/operate/operate_http/__init__.py index 817f6a713..10e80694e 100644 --- a/operate/operate_http/__init__.py +++ b/operate/operate_http/__init__.py @@ -23,6 +23,7 @@ import traceback import typing as t from abc import ABC +from http import HTTPStatus from starlette.requests import Request from starlette.responses import JSONResponse @@ -142,7 +143,7 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> t.Any: tb = traceback.format_exc() response = JSONResponse( content={"error": str(e), "traceback": tb}, - status_code=500, + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, ) print(tb) await response(scope=scope, receive=receive, send=send) diff --git a/operate/operate_http/exceptions.py b/operate/operate_http/exceptions.py index 162665ab0..b279bf860 100644 --- a/operate/operate_http/exceptions.py +++ b/operate/operate_http/exceptions.py @@ -19,6 +19,8 @@ """Exceptions.""" +from http import HTTPStatus + class ResourceException(Exception): """Base resource exceptio.""" @@ -29,22 +31,22 @@ class ResourceException(Exception): class BadRequest(ResourceException): """Bad request error.""" - code = 400 + code = HTTPStatus.BAD_REQUEST class ResourceAlreadyExists(ResourceException): """Bad request error.""" - code = 409 + code = HTTPStatus.CONFLICT class NotFound(ResourceException): """Not found error.""" - code = 404 + code = HTTPStatus.NOT_FOUND class NotAllowed(ResourceException): """Not allowed error.""" - code = 405 + code = HTTPStatus.METHOD_NOT_ALLOWED diff --git a/operate/quickstart/run_service.py b/operate/quickstart/run_service.py index 2157cb938..9ddbcf7a7 100644 --- a/operate/quickstart/run_service.py +++ b/operate/quickstart/run_service.py @@ -25,6 +25,7 @@ import time import typing as t import warnings +from http import HTTPStatus import requests from aea.crypto.registries import make_ledger_api @@ -247,7 +248,7 @@ def configure_local_config( metadata_hash = instance.functions.metadataHash().call().hex() ipfs_address = IPFS_ADDRESS.format(hash=metadata_hash) response = requests.get(ipfs_address) - if response.status_code != 200: + if response.status_code != HTTPStatus.OK: raise requests.RequestException( f"Failed to fetch data from {ipfs_address}: {response.status_code}" ) diff --git a/operate/services/health_checker.py b/operate/services/health_checker.py index ec8cb6a7c..e98a24d2c 100644 --- a/operate/services/health_checker.py +++ b/operate/services/health_checker.py @@ -22,6 +22,7 @@ import json import typing as t from concurrent.futures import ThreadPoolExecutor +from http import HTTPStatus from pathlib import Path from traceback import print_exc @@ -32,7 +33,7 @@ from operate.services.manage import ServiceManager # type: ignore -HTTP_OK = 200 +HTTP_OK = HTTPStatus.OK class HealthChecker: diff --git a/operate/services/manage.py b/operate/services/manage.py index 7b7be4bab..a39b3ec40 100644 --- a/operate/services/manage.py +++ b/operate/services/manage.py @@ -30,6 +30,7 @@ from collections import Counter, defaultdict from concurrent.futures import ThreadPoolExecutor from contextlib import suppress +from http import HTTPStatus from pathlib import Path import requests @@ -295,7 +296,7 @@ def _get_on_chain_metadata(self, chain_config: ChainConfig) -> t.Dict: url = f"{IPFS_GATEWAY}f01701220{config_hash}" self.logger.info(f"Fetching {url=}...") res = requests.get(url, timeout=30) - if res.status_code == 200: + if res.status_code == HTTPStatus.OK: return res.json() raise ValueError( f"Something went wrong while trying to get the on-chain metadata from IPFS: {res}" diff --git a/operate/services/utils/tendermint.py b/operate/services/utils/tendermint.py index a6276716f..a9f1b2793 100644 --- a/operate/services/utils/tendermint.py +++ b/operate/services/utils/tendermint.py @@ -31,6 +31,7 @@ import subprocess # nosec: import sys import traceback +from http import HTTPStatus from logging import Logger from pathlib import Path from threading import Event, Thread @@ -581,9 +582,15 @@ def gentle_reset() -> Tuple[Any, int]: try: tendermint_node.stop() tendermint_node.start() - return jsonify({"message": "Reset successful.", "status": True}), 200 + return ( + jsonify({"message": "Reset successful.", "status": True}), + HTTPStatus.OK, + ) except Exception as e: # pylint: disable=W0703 - return jsonify({"message": f"Reset failed: {e}", "status": False}), 200 + return ( + jsonify({"message": f"Reset failed: {e}", "status": False}), + HTTPStatus.OK, + ) @app.route("/app_hash") def app_hash() -> Tuple[Any, int]: @@ -624,21 +631,33 @@ def hard_reset() -> Tuple[Any, int]: request.args.get("period_count", "0"), ) tendermint_node.start() - return jsonify({"message": "Reset successful.", "status": True}), 200 + return ( + jsonify({"message": "Reset successful.", "status": True}), + HTTPStatus.OK, + ) except Exception as e: # pylint: disable=W0703 - return jsonify({"message": f"Reset failed: {e}", "status": False}), 200 + return ( + jsonify({"message": f"Reset failed: {e}", "status": False}), + HTTPStatus.OK, + ) - @app.errorhandler(404) # type: ignore + @app.errorhandler(HTTPStatus.NOT_FOUND) # type: ignore def handle_notfound(e: NotFound) -> Response: """Handle server error.""" cast(logging.Logger, app.logger).info(e) # pylint: disable=E - return Response("Not Found", status=404, mimetype="application/json") + return Response( + "Not Found", status=HTTPStatus.NOT_FOUND, mimetype="application/json" + ) - @app.errorhandler(500) # type: ignore + @app.errorhandler(HTTPStatus.INTERNAL_SERVER_ERROR) # type: ignore def handle_server_error(e: InternalServerError) -> Response: """Handle server error.""" cast(logging.Logger, app.logger).info(e) # pylint: disable=E - return Response("Error Closing Node", status=500, mimetype="application/json") + return Response( + "Error Closing Node", + status=HTTPStatus.INTERNAL_SERVER_ERROR, + mimetype="application/json", + ) return app, tendermint_node From 8b6c9dbd6f77a796c86205fa27cd1ec9c3af4358 Mon Sep 17 00:00:00 2001 From: Ojuswi Rastogi Date: Thu, 5 Jun 2025 21:10:11 +0530 Subject: [PATCH 2/2] Bump version to 0.6.0 in pyproject.toml Signed-off-by: Ojuswi Rastogi --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d4bdcf11c..fee0b6c7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "olas-operate-middleware" -version = "0.5.0" +version = "0.6.0" description = "" authors = ["David Vilela ", "Viraj Patel "] readme = "README.md"