Skip to content

Commit 5aebb45

Browse files
committed
added headers to exceptions
1 parent 12e1bd5 commit 5aebb45

File tree

2 files changed

+77
-59
lines changed

2 files changed

+77
-59
lines changed

socketdev/core/api.py

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22
import requests
33
from socketdev.core.classes import Response
44
from socketdev.exceptions import (
5-
APIKeyMissing, APIFailure, APIAccessDenied, APIInsufficientQuota,
6-
APIResourceNotFound, APITimeout, APIConnectionError, APIBadGateway,
7-
APIInsufficientPermissions, APIOrganizationNotAllowed
5+
APIKeyMissing,
6+
APIFailure,
7+
APIAccessDenied,
8+
APIInsufficientQuota,
9+
APIResourceNotFound,
10+
APITimeout,
11+
APIConnectionError,
12+
APIBadGateway,
13+
APIInsufficientPermissions,
14+
APIOrganizationNotAllowed,
815
)
916
from socketdev.version import __version__
1017
from requests.exceptions import Timeout, ConnectionError
@@ -24,7 +31,12 @@ def set_timeout(self, timeout: int):
2431
self.request_timeout = timeout
2532

2633
def do_request(
27-
self, path: str, headers: dict | None = None, payload: [dict, str] = None, files: list = None, method: str = "GET"
34+
self,
35+
path: str,
36+
headers: dict | None = None,
37+
payload: [dict, str] = None,
38+
files: list = None,
39+
method: str = "GET",
2840
) -> Response:
2941
if self.encoded_key is None or self.encoded_key == "":
3042
raise APIKeyMissing
@@ -36,33 +48,38 @@ def do_request(
3648
"accept": "application/json",
3749
}
3850
url = f"{self.api_url}/{path}"
51+
52+
def format_headers(headers_dict):
53+
return "\n".join(f"{k}: {v}" for k, v in headers_dict.items())
54+
3955
try:
4056
start_time = time.time()
4157
response = requests.request(
4258
method.upper(), url, headers=headers, data=payload, files=files, timeout=self.request_timeout
4359
)
4460
request_duration = time.time() - start_time
45-
61+
62+
headers_str = f"\n\nHeaders:\n{format_headers(response.headers)}" if response.headers else ""
63+
4664
if response.status_code == 401:
47-
raise APIAccessDenied("Unauthorized")
65+
raise APIAccessDenied(f"Unauthorized{headers_str}")
4866
if response.status_code == 403:
4967
try:
50-
error_message = response.json().get('error', {}).get('message', '')
68+
error_message = response.json().get("error", {}).get("message", "")
5169
if "Insufficient permissions for API method" in error_message:
52-
raise APIInsufficientPermissions(error_message)
70+
raise APIInsufficientPermissions(f"{error_message}{headers_str}")
5371
elif "Organization not allowed" in error_message:
54-
raise APIOrganizationNotAllowed(error_message)
72+
raise APIOrganizationNotAllowed(f"{error_message}{headers_str}")
5573
elif "Insufficient max quota" in error_message:
56-
raise APIInsufficientQuota(error_message)
74+
raise APIInsufficientQuota(f"{error_message}{headers_str}")
5775
else:
58-
raise APIAccessDenied(error_message or "Access denied")
76+
raise APIAccessDenied(f"{error_message or 'Access denied'}{headers_str}")
5977
except ValueError:
60-
# If JSON parsing fails
61-
raise APIAccessDenied("Access denied")
78+
raise APIAccessDenied(f"Access denied{headers_str}")
6279
if response.status_code == 404:
63-
raise APIResourceNotFound(f"Path not found {path}")
80+
raise APIResourceNotFound(f"Path not found {path}{headers_str}")
6481
if response.status_code == 429:
65-
retry_after = response.headers.get('retry-after')
82+
retry_after = response.headers.get("retry-after")
6683
if retry_after:
6784
try:
6885
seconds = int(retry_after)
@@ -73,23 +90,31 @@ def do_request(
7390
time_msg = f" Retry after: {retry_after}"
7491
else:
7592
time_msg = ""
76-
raise APIInsufficientQuota(f"Insufficient quota for API route.{time_msg}")
93+
raise APIInsufficientQuota(f"Insufficient quota for API route.{time_msg}{headers_str}")
7794
if response.status_code == 502:
7895
raise APIBadGateway("Upstream server error")
7996
if response.status_code >= 400:
80-
raise APIFailure(f"Bad Request: HTTP {response.status_code}")
81-
97+
raise APIFailure(f"Bad Request: HTTP {response.status_code}{headers_str}")
98+
8299
return response
83-
100+
84101
except Timeout:
85102
request_duration = time.time() - start_time
86103
raise APITimeout(f"Request timed out after {request_duration:.2f} seconds")
87104
except ConnectionError as error:
88105
request_duration = time.time() - start_time
89106
raise APIConnectionError(f"Connection error after {request_duration:.2f} seconds: {error}")
90-
except (APIAccessDenied, APIInsufficientQuota, APIResourceNotFound, APIFailure,
91-
APITimeout, APIConnectionError, APIBadGateway, APIInsufficientPermissions,
92-
APIOrganizationNotAllowed):
107+
except (
108+
APIAccessDenied,
109+
APIInsufficientQuota,
110+
APIResourceNotFound,
111+
APIFailure,
112+
APITimeout,
113+
APIConnectionError,
114+
APIBadGateway,
115+
APIInsufficientPermissions,
116+
APIOrganizationNotAllowed,
117+
):
93118
# Let all our custom exceptions propagate up unchanged
94119
raise
95120
except Exception as error:

socketdev/repos/__init__.py

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
log = logging.getLogger("socketdev")
77

8+
89
@dataclass
910
class RepositoryInfo:
1011
id: str
@@ -19,8 +20,11 @@ class RepositoryInfo:
1920
default_branch: str
2021
slug: Optional[str] = None
2122

22-
def __getitem__(self, key): return getattr(self, key)
23-
def to_dict(self): return asdict(self)
23+
def __getitem__(self, key):
24+
return getattr(self, key)
25+
26+
def to_dict(self):
27+
return asdict(self)
2428

2529
@classmethod
2630
def from_dict(cls, data: dict) -> "RepositoryInfo":
@@ -35,56 +39,53 @@ def from_dict(cls, data: dict) -> "RepositoryInfo":
3539
visibility=data["visibility"],
3640
archived=data["archived"],
3741
default_branch=data["default_branch"],
38-
slug=data.get("slug")
42+
slug=data.get("slug"),
3943
)
4044

45+
4146
@dataclass
4247
class GetRepoResponse:
4348
success: bool
4449
status: int
4550
data: Optional[RepositoryInfo] = None
4651
message: Optional[str] = None
4752

48-
def __getitem__(self, key): return getattr(self, key)
49-
def to_dict(self): return asdict(self)
53+
def __getitem__(self, key):
54+
return getattr(self, key)
55+
56+
def to_dict(self):
57+
return asdict(self)
5058

5159
@classmethod
5260
def from_dict(cls, data: dict) -> "GetRepoResponse":
5361
return cls(
5462
success=data["success"],
5563
status=data["status"],
5664
message=data.get("message"),
57-
data=RepositoryInfo.from_dict(data.get("data")) if data.get("data") else None
65+
data=RepositoryInfo.from_dict(data.get("data")) if data.get("data") else None,
5866
)
5967

68+
6069
class Repos:
6170
def __init__(self, api):
6271
self.api = api
6372

6473
def get(self, org_slug: str, **kwargs) -> dict[str, List[RepositoryInfo]]:
65-
query_params = {}
66-
if kwargs:
67-
for key, val in kwargs.items():
68-
query_params[key] = val
69-
if len(query_params) == 0:
70-
return {}
71-
74+
query_params = kwargs
7275
path = "orgs/" + org_slug + "/repos"
73-
if query_params is not None:
76+
77+
if query_params: # Only add query string if we have parameters
7478
path += "?"
7579
for param in query_params:
7680
value = query_params[param]
7781
path += f"{param}={value}&"
7882
path = path.rstrip("&")
79-
83+
8084
response = self.api.do_request(path=path)
81-
85+
8286
if response.status_code == 200:
8387
raw_result = response.json()
84-
result = {
85-
key: [RepositoryInfo.from_dict(repo) for repo in repos]
86-
for key, repos in raw_result.items()
87-
}
88+
result = {key: [RepositoryInfo.from_dict(repo) for repo in repos] for key, repos in raw_result.items()}
8889
return result
8990

9091
error_message = response.json().get("error", {}).get("message", "Unknown error")
@@ -94,27 +95,19 @@ def get(self, org_slug: str, **kwargs) -> dict[str, List[RepositoryInfo]]:
9495
def repo(self, org_slug: str, repo_name: str) -> GetRepoResponse:
9596
path = f"orgs/{org_slug}/repos/{repo_name}"
9697
response = self.api.do_request(path=path)
97-
98+
9899
if response.status_code == 200:
99100
result = response.json()
100-
return GetRepoResponse.from_dict({
101-
"success": True,
102-
"status": 200,
103-
"data": result
104-
})
105-
101+
return GetRepoResponse.from_dict({"success": True, "status": 200, "data": result})
102+
106103
error_message = response.json().get("error", {}).get("message", "Unknown error")
107104
log.error(f"Failed to get repository: {response.status_code}, message: {error_message}")
108-
return GetRepoResponse.from_dict({
109-
"success": False,
110-
"status": response.status_code,
111-
"message": error_message
112-
})
105+
return GetRepoResponse.from_dict({"success": False, "status": response.status_code, "message": error_message})
113106

114107
def delete(self, org_slug: str, name: str) -> dict:
115108
path = f"orgs/{org_slug}/repos/{name}"
116109
response = self.api.do_request(path=path, method="DELETE")
117-
110+
118111
if response.status_code == 200:
119112
result = response.json()
120113
return result
@@ -130,11 +123,11 @@ def post(self, org_slug: str, **kwargs) -> dict:
130123
params[key] = val
131124
if len(params) == 0:
132125
return {}
133-
126+
134127
path = "orgs/" + org_slug + "/repos"
135128
payload = json.dumps(params)
136129
response = self.api.do_request(path=path, method="POST", payload=payload)
137-
130+
138131
if response.status_code == 201:
139132
result = response.json()
140133
return result
@@ -150,11 +143,11 @@ def update(self, org_slug: str, repo_name: str, **kwargs) -> dict:
150143
params[key] = val
151144
if len(params) == 0:
152145
return {}
153-
146+
154147
path = f"orgs/{org_slug}/repos/{repo_name}"
155148
payload = json.dumps(params)
156149
response = self.api.do_request(path=path, method="POST", payload=payload)
157-
150+
158151
if response.status_code == 200:
159152
result = response.json()
160153
return result

0 commit comments

Comments
 (0)