Skip to content

Commit 7d81e26

Browse files
committed
Updates with various exceptions
1 parent 4e90d73 commit 7d81e26

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

simplefin4py/exceptions.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
11
"""SimpleFIN Exceptions."""
22

33

4-
class ClaimError(Exception):
4+
class SimpleFinInvalidClaimTokenError(Exception):
5+
"""Invalid claim token error."""
6+
7+
def __init__(self) -> None:
8+
"""Initialize the exception."""
9+
self.message = "The claim token is invalid and could not be decoded."
10+
super().__init__(self.message)
11+
12+
13+
class SimpleFinClaimError(Exception):
514
"""Exception raised for errors in the claim process."""
615

716
def __init__(self) -> None:
817
"""Initialize the exception."""
918
self.message = "The claim token either does not exist or has already been used claimed by someone "
1019
"else. Receiving this could mean that the user’s transaction information has been compromised."
1120
super().__init__(self.message)
21+
22+
23+
class SimpleFinAuthError(Exception):
24+
"""Authentication Error (403) on Account endpoint."""
25+
26+
def __init__(self) -> None:
27+
"""Initialize the exception."""
28+
self.message = (
29+
"Authentication failed. This could be because access has been revoked or if the credentials "
30+
"are incorrect."
31+
)
32+
super().__init__(self.message)
33+
34+
35+
class SimpleFinPaymentError(Exception):
36+
"""A 402 error will raise a Payment Required."""
37+
38+
def __init__(self) -> None:
39+
"""Initialize the exception."""
40+
self.message = "Payment Required"
41+
super().__init__(self.message)

simplefin4py/simplefin.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
"""SimpleFin Class."""
22
from __future__ import annotations
33

4+
import binascii
5+
46
import aiohttp
57
import base64
68

79
from aiohttp import BasicAuth, ClientConnectorError, ClientConnectorSSLError
810

11+
from .exceptions import SimpleFinClaimError, SimpleFinInvalidClaimTokenError
912
from .model import FinancialData
1013
from .const import LOGGER
1114

@@ -25,7 +28,11 @@ async def claim_setup_token(
2528
cls, setup_token: str, verify_ssl: bool = True, proxy: str | None = None
2629
) -> str:
2730
"""Exchanges a 1-time setup token for an access token."""
28-
claim_url = base64.b64decode(setup_token).decode("utf-8")
31+
try:
32+
claim_url = base64.b64decode(setup_token).decode("utf-8")
33+
except binascii.Error as err:
34+
raise SimpleFinInvalidClaimTokenError from err
35+
2936
auth = BasicAuth(
3037
login="", password=""
3138
) # Replace with appropriate auth if needed
@@ -39,6 +46,9 @@ async def claim_setup_token(
3946

4047
async with aiohttp.ClientSession(auth=auth, connector=connector) as session:
4148
response = await session.post(claim_url, **request_params)
49+
if response.status == 403:
50+
# Claim issue
51+
raise SimpleFinClaimError()
4252
access_url: str = await response.text()
4353
return access_url
4454

tests/conftest.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
current_dir = os.path.dirname(__file__)
1212

13-
1413
MOCK_CLAIM_TOKEN = "https://claim.url" # noqa: S105
1514
MOCK_ACCESS_URL = "https://user:passwords@access_url"
1615

@@ -57,6 +56,16 @@ def mock_claim_token_success() -> Generator[aioresponses, None, None]:
5756
yield m
5857

5958

59+
@pytest.fixture # type: ignore
60+
def mock_claim_token_403() -> Generator[aioresponses, None, None]:
61+
"""Fixture for mocking a login process with bad credentials."""
62+
# Using 'aioresponses' to mock asynchronous HTTP responses.
63+
with aioresponses() as m:
64+
# Mock a failed login attempt due to bad credentials.
65+
m.post(MOCK_CLAIM_TOKEN, status=403, body="")
66+
yield m
67+
68+
6069
@pytest.fixture # type: ignore
6170
def mock_claim_token() -> str:
6271
"""Fixture for the claim URL."""

tests/test_simplefin.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from aioresponses import aioresponses
66

77
from simplefin4py import SimpleFin
8+
from simplefin4py.exceptions import SimpleFinInvalidClaimTokenError, SimpleFinClaimError
89
from tests.conftest import MOCK_ACCESS_URL
910

1011

@@ -16,6 +17,25 @@ async def test_claim_token_good(mock_claim_token_success, mock_claim_token):
1617
assert access_url == MOCK_ACCESS_URL
1718

1819

20+
@pytest.mark.asyncio # type: ignore
21+
async def test_claim_token_bad():
22+
"""Test a successful claim token."""
23+
# Claim the token.
24+
with pytest.raises(SimpleFinInvalidClaimTokenError):
25+
access_url = await SimpleFin.claim_setup_token(
26+
"bad token of crap", proxy="localhost"
27+
)
28+
assert access_url == MOCK_ACCESS_URL
29+
30+
31+
@pytest.mark.asyncio # type: ignore
32+
async def test_claim_token_403(mock_claim_token_403, mock_claim_token):
33+
"""Test a successful claim token."""
34+
# Claim the token.
35+
with pytest.raises(SimpleFinClaimError):
36+
await SimpleFin.claim_setup_token(mock_claim_token, proxy="localhost")
37+
38+
1939
@pytest.mark.asyncio # type: ignore
2040
async def test_access_402(mock_get_data_402) -> None:
2141
"""Test a failed access token."""

0 commit comments

Comments
 (0)