Skip to content

Commit

Permalink
Build out http helper class (#36)
Browse files Browse the repository at this point in the history
An abstraction on the requests library
  • Loading branch information
mbiannaccone authored May 13, 2024
1 parent 035cc17 commit b4bc617
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 3 deletions.
3 changes: 3 additions & 0 deletions canvas_sdk/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from canvas_sdk.utils.http import Http

__all__ = ("Http",)
73 changes: 73 additions & 0 deletions canvas_sdk/utils/http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import time
from functools import wraps
from typing import Any, Callable, TypeVar

import requests
import statsd

F = TypeVar("F", bound=Callable)


class Http:
"""A helper class for completing HTTP calls with metrics tracking."""

def __init__(self) -> None:
self.session = requests.Session()
self.statsd_client = statsd.StatsClient()

@staticmethod
def measure_time(fn: F | None = None) -> Callable[[F], F] | F:
"""A decorator to store timing of HTTP calls."""

def _decorator(fn: F) -> F:
@wraps(fn)
def wrapper(self: "Http", *args: Any, **kwargs: Any) -> Any:
print(fn.__name__)
start_time = time.time()
result = fn(self, *args, **kwargs)
end_time = time.time()
timing = int((end_time - start_time) * 1000)
self.statsd_client.timing(f"http_{fn.__name__}", timing)
return result

return wrapper

return _decorator(fn) if fn else _decorator

@measure_time
def get(self, url: str, headers: dict = {}) -> requests.Response:
"""Sends a GET request."""
return self.session.get(url, headers=headers)

@measure_time
def post(
self,
url: str,
json: dict | None = None,
data: dict | str | list | bytes | None = None,
headers: dict = {},
) -> requests.Response:
"""Sends a POST request."""
return self.session.post(url, json=json, data=data, headers=headers)

@measure_time
def put(
self,
url: str,
json: dict | None = None,
data: dict | str | list | bytes | None = None,
headers: dict = {},
) -> requests.Response:
"""Sends a PUT request."""
return self.session.put(url, json=json, data=data, headers=headers)

@measure_time
def patch(
self,
url: str,
json: dict | None = None,
data: dict | str | list | bytes | None = None,
headers: dict = {},
) -> requests.Response:
"""Sends a PATCH request."""
return self.session.patch(url, json=json, data=data, headers=headers)
46 changes: 46 additions & 0 deletions canvas_sdk/utils/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from unittest.mock import MagicMock, patch

from canvas_sdk.utils import Http


@patch("requests.Session.get")
def test_http_get(mock_get: MagicMock) -> None:
http = Http()
http.get("https://www.canvasmedical.com/", headers={"Authorization": "Bearer as;ldkfjdkj"})
mock_get.assert_called_once()


@patch("requests.Session.post")
def test_http_post(mock_post: MagicMock) -> None:
http = Http()
http.post(
"https://www.canvasmedical.com/",
json={"hey": "hi"},
data="grant-type=client_credentials",
headers={"Content-type": "application/json"},
)
mock_post.assert_called_once()


@patch("requests.Session.put")
def test_http_put(mock_put: MagicMock) -> None:
http = Http()
http.put(
"https://www.canvasmedical.com/",
json={"hey": "hi"},
data="grant-type=client_credentials",
headers={"Content-type": "application/json"},
)
mock_put.assert_called_once()


@patch("requests.Session.patch")
def test_http_patch(mock_patch: MagicMock) -> None:
http = Http()
http.patch(
"https://www.canvasmedical.com/",
json={"hey": "hi"},
data="grant-type=client_credentials",
headers={"Content-type": "application/json"},
)
mock_patch.assert_called_once()
15 changes: 13 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ pydantic = "^2.6.1"
python = ">=3.11,<3.13"
python-dotenv = "^1.0.1"
requests = "*"
restrictedpython = "^7.1"
statsd = "^4.0.1"
typer = {extras = ["all"], version = "*"}
websocket-client = "^1.7.0"
restrictedpython = "^7.1"

[tool.poetry.group.dev.dependencies]
grpcio-tools = "^1.60.1"
Expand Down

0 comments on commit b4bc617

Please sign in to comment.