Skip to content
Draft
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
29 changes: 28 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
[project]
name = "ssoready"
version = "1.1.0"
description = "Python client library for SSOReady"
readme = "README.md"
authors = []
keywords = []
license = "MIT"
classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Operating System :: MacOS",
"Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows",
"Topic :: Software Development :: Libraries :: Python Modules",
"Typing :: Typed",
"License :: OSI Approved :: MIT License"
]

[tool.poetry]
name = "ssoready"
version = "1.1.0"
description = ""
description = "Python client library for SSOReady"
readme = "README.md"
authors = []
keywords = []
Expand Down
5 changes: 3 additions & 2 deletions src/ssoready/core/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def __init__(self, *, httpx_client: httpx.Client):
def request(
self, *args: typing.Any, max_retries: int = 0, retries: int = 0, **kwargs: typing.Any
) -> httpx.Response:
"""Here retries is just a counter of times the request has been retried which is stored in request."""
response = self.httpx_client.request(*args, **kwargs)
if _should_retry(response=response):
if max_retries > retries:
Expand All @@ -100,7 +101,7 @@ def request(

@wraps(httpx.Client.stream)
@contextmanager
def stream(self, *args: typing.Any, max_retries: int = 0, retries: int = 0, **kwargs: typing.Any) -> typing.Any:
def stream(self, *args: typing.Any, **kwargs: typing.Any) -> typing.Any:
with self.httpx_client.stream(*args, **kwargs) as stream:
yield stream

Expand All @@ -124,7 +125,7 @@ async def request(
@wraps(httpx.AsyncClient.stream)
@asynccontextmanager
async def stream(
self, *args: typing.Any, max_retries: int = 0, retries: int = 0, **kwargs: typing.Any
self, *args: typing.Any, **kwargs: typing.Any
) -> typing.Any:
async with self.httpx_client.stream(*args, **kwargs) as stream:
yield stream
4 changes: 3 additions & 1 deletion src/ssoready/core/jsonable_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
https://github.com/tiangolo/fastapi/blob/master/fastapi/encoders.py
"""

from _typeshed import DataclassInstance
import dataclasses
import datetime as dt
from collections import defaultdict
from enum import Enum
from pathlib import PurePath
from types import GeneratorType
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union, cast

from .datetime_utils import serialize_datetime
from .pydantic_utilities import pydantic_v1
Expand Down Expand Up @@ -53,6 +54,7 @@ def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any]
obj_dict = obj_dict["__root__"]
return jsonable_encoder(obj_dict, custom_encoder=encoder)
if dataclasses.is_dataclass(obj):
obj = cast(DataclassInstance, obj)
obj_dict = dataclasses.asdict(obj)
return jsonable_encoder(obj_dict, custom_encoder=custom_encoder)
if isinstance(obj, Enum):
Expand Down
34 changes: 19 additions & 15 deletions src/ssoready/saml/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import urllib.parse
from json.decoder import JSONDecodeError

from ssoready.core.http_client import AsyncHttpClient, HttpClient

from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.jsonable_encoder import jsonable_encoder
Expand All @@ -20,7 +22,7 @@

class SamlClient:
def __init__(self, *, client_wrapper: SyncClientWrapper):
self._client_wrapper = client_wrapper
self._client_wrapper: SyncClientWrapper = client_wrapper

def redeem_saml_access_code(
self, *, saml_access_code: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None
Expand Down Expand Up @@ -52,8 +54,8 @@ def redeem_saml_access_code(
_request: typing.Dict[str, typing.Any] = {}
if saml_access_code is not OMIT:
_request["samlAccessCode"] = saml_access_code
_response = self._client_wrapper.httpx_client.request(
method="POST",
_response = HttpClient.request(
self._client_wrapper.httpx_client,
url=urllib.parse.urljoin(f"{self._client_wrapper.get_base_url()}/", "v1/saml/redeem"),
params=encode_query(
jsonable_encoder(
Expand All @@ -78,10 +80,10 @@ def redeem_saml_access_code(
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self._client_wrapper.get_timeout(),
retries=0,
max_retries=request_options.get("max_retries") if request_options is not None else 0, # type: ignore
max_retries=request_options.get("max_retries", 0) if request_options is not None else 0,
)
if 200 <= _response.status_code < 300:
return pydantic_v1.parse_obj_as(RedeemSamlAccessCodeResponse, _response.json()) # type: ignore
return pydantic_v1.parse_obj_as(RedeemSamlAccessCodeResponse, _response.json())
try:
_response_json = _response.json()
except JSONDecodeError:
Expand Down Expand Up @@ -136,8 +138,8 @@ def get_saml_redirect_url(
_request["organizationExternalId"] = organization_external_id
if state is not OMIT:
_request["state"] = state
_response = self._client_wrapper.httpx_client.request(
method="POST",
_response = HttpClient.request(
self._client_wrapper.httpx_client,
url=urllib.parse.urljoin(f"{self._client_wrapper.get_base_url()}/", "v1/saml/redirect"),
params=encode_query(
jsonable_encoder(
Expand All @@ -162,10 +164,10 @@ def get_saml_redirect_url(
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self._client_wrapper.get_timeout(),
retries=0,
max_retries=request_options.get("max_retries") if request_options is not None else 0, # type: ignore
max_retries=request_options.get("max_retries", 0) if request_options is not None else 0,
)
if 200 <= _response.status_code < 300:
return pydantic_v1.parse_obj_as(GetSamlRedirectUrlResponse, _response.json()) # type: ignore
return pydantic_v1.parse_obj_as(GetSamlRedirectUrlResponse, _response.json())
try:
_response_json = _response.json()
except JSONDecodeError:
Expand All @@ -175,7 +177,7 @@ def get_saml_redirect_url(

class AsyncSamlClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
self._client_wrapper = client_wrapper
self._client_wrapper: AsyncClientWrapper = client_wrapper

async def redeem_saml_access_code(
self, *, saml_access_code: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None
Expand Down Expand Up @@ -207,7 +209,8 @@ async def redeem_saml_access_code(
_request: typing.Dict[str, typing.Any] = {}
if saml_access_code is not OMIT:
_request["samlAccessCode"] = saml_access_code
_response = await self._client_wrapper.httpx_client.request(
_response = await AsyncHttpClient.request(
self._client_wrapper.httpx_client,
method="POST",
url=urllib.parse.urljoin(f"{self._client_wrapper.get_base_url()}/", "v1/saml/redeem"),
params=encode_query(
Expand All @@ -233,10 +236,10 @@ async def redeem_saml_access_code(
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self._client_wrapper.get_timeout(),
retries=0,
max_retries=request_options.get("max_retries") if request_options is not None else 0, # type: ignore
max_retries=request_options.get("max_retries", 0) if request_options is not None else 0,
)
if 200 <= _response.status_code < 300:
return pydantic_v1.parse_obj_as(RedeemSamlAccessCodeResponse, _response.json()) # type: ignore
return pydantic_v1.parse_obj_as(RedeemSamlAccessCodeResponse, _response.json())
try:
_response_json = _response.json()
except JSONDecodeError:
Expand Down Expand Up @@ -291,7 +294,8 @@ async def get_saml_redirect_url(
_request["organizationExternalId"] = organization_external_id
if state is not OMIT:
_request["state"] = state
_response = await self._client_wrapper.httpx_client.request(
_response = await AsyncHttpClient.request(
self._client_wrapper.httpx_client,
method="POST",
url=urllib.parse.urljoin(f"{self._client_wrapper.get_base_url()}/", "v1/saml/redirect"),
params=encode_query(
Expand All @@ -317,7 +321,7 @@ async def get_saml_redirect_url(
if request_options is not None and request_options.get("timeout_in_seconds") is not None
else self._client_wrapper.get_timeout(),
retries=0,
max_retries=request_options.get("max_retries") if request_options is not None else 0, # type: ignore
max_retries=request_options.get("max_retries", 0) if request_options is not None else 0,
)
if 200 <= _response.status_code < 300:
return pydantic_v1.parse_obj_as(GetSamlRedirectUrlResponse, _response.json()) # type: ignore
Expand Down