@@ -459,7 +459,7 @@ async def a_raw_get(self, path: str, **kwargs: Any) -> AsyncResponse: # noqa: A
459459 try :
460460 return await self .async_s .get (
461461 urljoin (self .base_url , path ),
462- params = self ._filter_query_params (kwargs ),
462+ params = self ._filter_none_values (kwargs ),
463463 headers = self .headers ,
464464 timeout = self .timeout ,
465465 )
@@ -494,7 +494,7 @@ async def a_raw_post(
494494 return await self .async_s .request (
495495 method = "POST" ,
496496 url = urljoin (self .base_url , path ),
497- params = self ._filter_query_params (kwargs ),
497+ params = self ._filter_none_values (kwargs ),
498498 ** self ._prepare_httpx_request_content (data ),
499499 headers = self .headers ,
500500 timeout = self .timeout ,
@@ -529,7 +529,7 @@ async def a_raw_put(
529529 try :
530530 return await self .async_s .put (
531531 urljoin (self .base_url , path ),
532- params = self ._filter_query_params (kwargs ),
532+ params = self ._filter_none_values (kwargs ),
533533 ** self ._prepare_httpx_request_content (data ),
534534 headers = self .headers ,
535535 timeout = self .timeout ,
@@ -566,16 +566,16 @@ async def a_raw_delete(
566566 method = "DELETE" ,
567567 url = urljoin (self .base_url , path ),
568568 ** self ._prepare_httpx_request_content (data or {}),
569- params = self ._filter_query_params (kwargs ),
569+ params = self ._filter_none_values (kwargs ),
570570 headers = self .headers ,
571571 timeout = self .timeout ,
572572 )
573573 except Exception as e :
574574 msg = "Can't connect to server"
575575 raise KeycloakConnectionError (msg ) from e
576576
577- @staticmethod
578- def _prepare_httpx_request_content (data : dict | str | None | MultipartEncoder ) -> dict :
577+ @classmethod
578+ def _prepare_httpx_request_content (cls , data : dict | str | None | MultipartEncoder ) -> dict :
579579 """
580580 Create the correct request content kwarg to `httpx.AsyncClient.request()`.
581581
@@ -593,19 +593,23 @@ def _prepare_httpx_request_content(data: dict | str | None | MultipartEncoder) -
593593 # Note: this could also accept bytes, Iterable[bytes], or AsyncIterable[bytes]
594594 return {"content" : data }
595595
596+ if isinstance (data , dict ):
597+ return {"data" : cls ._filter_none_values (data )}
598+
596599 return {"data" : data }
597600
598- @staticmethod
599- def _filter_query_params ( query_params : dict ) -> dict :
601+ @classmethod
602+ def _filter_none_values ( cls , data : dict ) -> dict :
600603 """
601- Explicitly filter query params with None values for compatibility.
604+ Explicitly filter items with None values for compatibility.
602605
603- Httpx and requests differ in the way they handle query params with the value None,
604- requests does not include params with the value None while httpx includes them as-is.
606+ Httpx and requests differ in the way they handle None values:
607+ requests silently drops keys with None values from both query params and form-encoded bodies, while
608+ httpx serializes them as empty strings.
605609
606- :param query_params : the query params
607- :type query_params : dict
608- :returns: the filtered query params
610+ :param data : the dict to filter
611+ :type data : dict
612+ :returns: the filtered dict
609613 :rtype: dict
610614 """
611- return {k : v for k , v in query_params .items () if v is not None }
615+ return {k : v for k , v in data .items () if v is not None }
0 commit comments