Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions backend/python/app/sources/client/http/http_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from typing import Optional

import httpx # type: ignore

from app.sources.client.http.http_request import HTTPRequest
from app.sources.client.http.http_response import HTTPResponse
from app.sources.client.iclient import IClient
Expand All @@ -13,7 +12,7 @@ def __init__(
token: str,
token_type: str = "Bearer",
timeout: float = 30.0,
follow_redirects: bool = True
follow_redirects: bool = True,
) -> None:
self.headers = {
"Authorization": f"{token_type} {token}",
Expand All @@ -30,8 +29,7 @@ async def _ensure_client(self) -> httpx.AsyncClient:
"""Ensure client is created and available"""
if self.client is None:
self.client = httpx.AsyncClient(
timeout=self.timeout,
follow_redirects=self.follow_redirects
timeout=self.timeout, follow_redirects=self.follow_redirects
)
return self.client

Expand All @@ -43,28 +41,40 @@ async def execute(self, request: HTTPRequest, **kwargs) -> HTTPResponse:
Returns:
A HTTPResponse object containing the response from the server
"""
url = f"{request.url.format(**request.path_params)}"
# Use local variable lookups; format URL only if path_params present and url has braces
url = request.url
if request.path_params and "{" in url:
url = url.format(**request.path_params)
client = await self._ensure_client()

# Merge client headers with request headers (request headers take precedence)
merged_headers = {**self.headers, **request.headers}
merged_headers = (
{**self.headers, **request.headers}
if request.headers
else dict(self.headers)
)
request_kwargs = {
"params": request.query_params,
"headers": merged_headers,
**kwargs
**kwargs,
}

if isinstance(request.body, dict):
body = request.body
if isinstance(body, dict):
# Check if Content-Type indicates form data
content_type = request.headers.get("Content-Type", "").lower()
content_type = (
request.headers.get("Content-Type", "").lower()
if request.headers
else ""
)
if "application/x-www-form-urlencoded" in content_type:
# Send as form data
request_kwargs["data"] = request.body
request_kwargs["data"] = body
else:
# Send as JSON (default behavior)
request_kwargs["json"] = request.body
elif isinstance(request.body, bytes):
request_kwargs["content"] = request.body
request_kwargs["json"] = body
elif isinstance(body, bytes):
request_kwargs["content"] = body

response = await client.request(request.method, url, **request_kwargs)
return HTTPResponse(response)
Expand Down
67 changes: 49 additions & 18 deletions backend/python/app/sources/external/jira/jira.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from app.sources.client.http.http_request import HTTPRequest
from app.sources.client.http.http_response import HTTPResponse
from app.sources.client.jira.jira import JiraClient
from codeflash.code_utils.codeflash_wrap_decorator import \
codeflash_performance_async


class JiraDataSource:
Expand Down Expand Up @@ -6463,6 +6465,7 @@ async def get_issue_property_keys(
resp = await self._client.execute(req)
return resp

@codeflash_performance_async
async def delete_issue_property(
self,
issueIdOrKey: str,
Expand Down Expand Up @@ -8238,29 +8241,41 @@ async def remove_level(
replaceWith: Optional[str] = None,
headers: Optional[Dict[str, Any]] = None
) -> HTTPResponse:
"""Auto-generated from OpenAPI: Remove issue security level\n\nHTTP DELETE /rest/api/3/issuesecurityschemes/{schemeId}/level/{levelId}\nPath params:\n - schemeId (str)\n - levelId (str)\nQuery params:\n - replaceWith (str, optional)"""
if self._client is None:
"""Auto-generated from OpenAPI: Remove issue security level

HTTP DELETE /rest/api/3/issuesecurityschemes/{schemeId}/level/{levelId}
Path params:
- schemeId (str)
- levelId (str)
Query params:
- replaceWith (str, optional)"""
client = self._client # local var lookup is faster than attribute access
if client is None:
raise ValueError('HTTP client is not initialized')
_headers: Dict[str, Any] = dict(headers or {})
_path: Dict[str, Any] = {
_headers = dict(headers) if headers else {}
_path = {
'schemeId': schemeId,
'levelId': levelId,
}
_query: Dict[str, Any] = {}
_query = {}
if replaceWith is not None:
_query['replaceWith'] = replaceWith
_body = None
rel_path = '/rest/api/3/issuesecurityschemes/{schemeId}/level/{levelId}'
url = self.base_url + _safe_format_url(rel_path, _path)
# Convert all dicts just once before passing to HTTPRequest
str_headers = _as_str_dict(_headers)
str_path = _as_str_dict(_path)
str_query = _as_str_dict(_query)
req = HTTPRequest(
method='DELETE',
url=url,
headers=_as_str_dict(_headers),
path_params=_as_str_dict(_path),
query_params=_as_str_dict(_query),
headers=str_headers,
path_params=str_path,
query_params=str_query,
body=_body,
)
resp = await self._client.execute(req)
resp = await client.execute(req)
return resp

async def update_security_level(
Expand Down Expand Up @@ -9979,19 +9994,25 @@ async def set_locale(
resp = await self._client.execute(req)
return resp

@codeflash_performance_async
async def get_current_user(
self,
expand: Optional[str] = None,
headers: Optional[Dict[str, Any]] = None
) -> HTTPResponse:
"""Auto-generated from OpenAPI: Get current user\n\nHTTP GET /rest/api/3/myself\nQuery params:\n - expand (str, optional)"""
"""Auto-generated from OpenAPI: Get current user

HTTP GET /rest/api/3/myself
Query params:
- expand (str, optional)"""
if self._client is None:
raise ValueError('HTTP client is not initialized')
_headers: Dict[str, Any] = dict(headers or {})

# Use headers as-is if not None, else an empty dict (no mutation, safe).
_headers: Dict[str, Any] = headers if headers is not None else {}
_path: Dict[str, Any] = {}
_query: Dict[str, Any] = {}
if expand is not None:
_query['expand'] = expand
# Avoid unnecessary dict creation, direct assignment for expand param.
_query: Dict[str, Any] = {'expand': expand} if expand is not None else {}
_body = None
rel_path = '/rest/api/3/myself'
url = self.base_url + _safe_format_url(rel_path, _path)
Expand Down Expand Up @@ -20081,9 +20102,9 @@ async def put_forge_app_property(

# ---- Helpers used by generated methods ----
def _safe_format_url(template: str, params: Dict[str, object]) -> str:
class _SafeDict(dict):
def __missing__(self, key: str) -> str:
return '{' + key + '}'
# Fast path: If there are no placeholders, return as-is.
if '{' not in template:
return template
try:
return template.format_map(_SafeDict(params))
except Exception:
Expand All @@ -20102,4 +20123,14 @@ def _serialize_value(v: Union[bool, str, int, float, list, tuple, set, None]) ->
return _to_bool_str(v)

def _as_str_dict(d: Dict[str, Any]) -> Dict[str, str]:
return {str(k): _serialize_value(v) for k, v in (d or {}).items()}
# Avoids unnecessary dict allocation/copy; only convert if key/value not already string
# Fast path for empty dicts
if not d:
return {}
# If all keys and values are already strings, avoid conversion (most likely for headers).
# This check is much faster than doing conversion for large dicts.
if all(isinstance(k, str) and (isinstance(v, str) or v is None) for k, v in d.items()):
# Return with None converted to ''
return {k: (v if v is not None else '') for k, v in d.items()}
# Otherwise, do full conversion
return {str(k): _serialize_value(v) for k, v in d.items()}