Skip to content

Commit 26b8046

Browse files
authored
Add type annotations for BankID API responses (#78)
* Add TypedDict implementations of API responses. * Set asyncio_default_fixture_loop_scope. This will silence a deprecation warning from pytest-asyncio. pytest-dev/pytest-asyncio#924 * Use session scope for cert_and_key fixture.
1 parent 806ccc7 commit 26b8046

File tree

6 files changed

+120
-32
lines changed

6 files changed

+120
-32
lines changed

bankid/asyncclient.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

55
from bankid.baseclient import BankIDClientBaseclass
66
from bankid.exceptions import get_json_error_class
7+
from bankid.responses import (
8+
AuthenticateResponse,
9+
CollectCompleteResponse,
10+
CollectFailedResponse,
11+
CollectPendingResponse,
12+
PhoneAuthenticateResponse,
13+
PhoneSignResponse,
14+
SignResponse,
15+
)
716

817

918
class BankIDAsyncClient(BankIDClientBaseclass[httpx.AsyncClient]):
@@ -32,7 +41,7 @@ async def authenticate(
3241
user_visible_data: Union[str, None] = None,
3342
user_non_visible_data: Union[str, None] = None,
3443
user_visible_data_format: Union[str, None] = None,
35-
) -> Dict[str, str]:
44+
) -> AuthenticateResponse:
3645
"""Request an authentication order. The :py:meth:`collect` method
3746
is used to query the status of the order.
3847
@@ -62,8 +71,8 @@ async def authenticate(
6271
this parameter indicates that userVisibleData holds formatting characters which
6372
potentially make for a more pleasant user experience.
6473
:type user_visible_data_format: str
65-
:return: The order response.
66-
:rtype: dict
74+
:return: The order response parsed as a dict.
75+
:rtype: AuthenticateResponse
6776
:raises BankIDError: raises a subclass of this error
6877
when error has been returned from server.
6978
@@ -91,7 +100,7 @@ async def phone_authenticate(
91100
user_visible_data: Union[str, None] = None,
92101
user_non_visible_data: Union[str, None] = None,
93102
user_visible_data_format: Union[str, None] = None,
94-
) -> Dict[str, str]:
103+
) -> PhoneAuthenticateResponse:
95104
"""Initiates an authentication order when the user is talking
96105
to the RP over the phone. The :py:meth:`collect` method
97106
is used to query the status of the order.
@@ -123,8 +132,8 @@ async def phone_authenticate(
123132
this parameter indicates that userVisibleData holds formatting characters which
124133
potentially make for a more pleasant user experience.
125134
:type user_visible_data_format: str
126-
:return: The order response.
127-
:rtype: dict
135+
:return: The order response parsed as a dict.
136+
:rtype: PhoneAuthenticateResponse
128137
:raises BankIDError: raises a subclass of this error
129138
when error has been returned from server.
130139
@@ -155,7 +164,7 @@ async def sign(
155164
requirement: Union[Dict[str, Any], None] = None,
156165
user_non_visible_data: Union[str, None] = None,
157166
user_visible_data_format: Union[str, None] = None,
158-
) -> Dict[str, str]:
167+
) -> SignResponse:
159168
"""Request a signing order. The :py:meth:`collect` method
160169
is used to query the status of the order.
161170
@@ -183,8 +192,8 @@ async def sign(
183192
this parameter indicates that userVisibleData holds formatting characters which
184193
potentially make for a more pleasant user experience.
185194
:type user_visible_data_format: str
186-
:return: The order response.
187-
:rtype: dict
195+
:return: The order response parsed as a dict.
196+
:rtype: SignResponse
188197
:raises BankIDError: raises a subclass of this error
189198
when error has been returned from server.
190199
@@ -212,7 +221,7 @@ async def phone_sign(
212221
requirement: Union[Dict[str, Any], None] = None,
213222
user_non_visible_data: Union[str, None] = None,
214223
user_visible_data_format: Union[str, None] = None,
215-
) -> Dict[str, str]:
224+
) -> PhoneSignResponse:
216225
"""Initiates an authentication order when the user is talking to
217226
the RP over the phone. The :py:meth:`collect` method
218227
is used to query the status of the order.
@@ -242,8 +251,8 @@ async def phone_sign(
242251
this parameter indicates that userVisibleData holds formatting characters which
243252
potentially make for a more pleasant user experience.
244253
:type user_visible_data_format: str
245-
:return: The order response.
246-
:rtype: dict
254+
:return: The order response parsed as a dict.
255+
:rtype: PhoneSignResponse
247256
:raises BankIDError: raises a subclass of this error
248257
when error has been returned from server.
249258
@@ -267,7 +276,7 @@ async def phone_sign(
267276
else:
268277
raise get_json_error_class(response)
269278

270-
async def collect(self, order_ref: str) -> dict:
279+
async def collect(self, order_ref: str) -> Union[CollectPendingResponse, CollectCompleteResponse, CollectFailedResponse]:
271280
"""Collects the result of a sign or auth order using the
272281
``orderRef`` as reference.
273282
@@ -326,8 +335,8 @@ async def collect(self, order_ref: str) -> dict:
326335
327336
:param order_ref: The ``orderRef`` UUID returned from auth or sign.
328337
:type order_ref: str
329-
:return: The CollectResponse parsed to a dictionary.
330-
:rtype: dict
338+
:return: The order response parsed to a dict.
339+
:rtype: Union[CollectPendingResponse, CollectCompleteResponse, CollectFailedResponse]
331340
:raises BankIDError: raises a subclass of this error
332341
when error has been returned from server.
333342

bankid/baseclient.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import base64
22
import ssl
33
from datetime import datetime
4-
from typing import Tuple, Dict, Any, Union, TypeVar, Generic
4+
from typing import Any, Dict, Generic, Tuple, TypeVar, Union
55
from urllib.parse import urljoin
66

77
from bankid.qr import generate_qr_code_content

bankid/responses.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from typing_extensions import Literal, NotRequired, TypedDict
2+
3+
class SignResponse(TypedDict):
4+
orderRef: str
5+
autoStartToken: str
6+
qrStartToken: str
7+
qrStartSecret: str
8+
9+
10+
class PhoneSignResponse(TypedDict):
11+
orderRef: str
12+
13+
14+
class AuthenticateResponse(TypedDict):
15+
orderRef: str
16+
autoStartToken: str
17+
qrStartToken: str
18+
qrStartSecret: str
19+
20+
21+
class PhoneAuthenticateResponse(TypedDict):
22+
orderRef: str
23+
24+
25+
class Device(TypedDict):
26+
ipAddress: str
27+
uhi: str
28+
29+
30+
class StepUp(TypedDict):
31+
mrtd: bool
32+
33+
34+
class User(TypedDict):
35+
personalNumber: str
36+
name: str
37+
givenName: str
38+
surname: str
39+
40+
41+
class CompletionData(TypedDict):
42+
user: User
43+
device: Device
44+
stepUp: StepUp
45+
bankIdIssueDate: str
46+
signature: str
47+
ocspResponse: str
48+
risk: NotRequired[str]
49+
50+
51+
class _CollectResponse(TypedDict):
52+
orderRef: str
53+
54+
55+
class CollectPendingResponse(_CollectResponse):
56+
status: Literal["pending"]
57+
hintCode: str
58+
59+
60+
class CollectCompleteResponse(_CollectResponse):
61+
status: Literal["complete"]
62+
completionData: CompletionData
63+
64+
65+
class CollectFailedResponse(_CollectResponse):
66+
status: Literal["failed"]
67+
hintCode: str

bankid/syncclient.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44

55
from bankid.baseclient import BankIDClientBaseclass
66
from bankid.exceptions import get_json_error_class
7+
from bankid.responses import (
8+
AuthenticateResponse,
9+
CollectCompleteResponse,
10+
CollectFailedResponse,
11+
CollectPendingResponse,
12+
PhoneAuthenticateResponse,
13+
PhoneSignResponse,
14+
SignResponse,
15+
)
716

817

918
class BankIDClient(BankIDClientBaseclass[httpx.Client]):
@@ -32,7 +41,7 @@ def authenticate(
3241
user_visible_data: Union[str, None] = None,
3342
user_non_visible_data: Union[str, None] = None,
3443
user_visible_data_format: Union[str, None] = None,
35-
) -> Dict[str, str]:
44+
) -> AuthenticateResponse:
3645
"""Request an authentication order. The :py:meth:`collect` method
3746
is used to query the status of the order.
3847
@@ -62,8 +71,8 @@ def authenticate(
6271
this parameter indicates that userVisibleData holds formatting characters which
6372
potentially make for a more pleasant user experience.
6473
:type user_visible_data_format: str
65-
:return: The order response.
66-
:rtype: dict
74+
:return: The order response parsed to a dict.
75+
:rtype: AuthenticateResponse
6776
:raises BankIDError: raises a subclass of this error
6877
when error has been returned from server.
6978
@@ -91,7 +100,7 @@ def phone_authenticate(
91100
user_visible_data: Union[str, None] = None,
92101
user_non_visible_data: Union[str, None] = None,
93102
user_visible_data_format: Union[str, None] = None,
94-
) -> Dict[str, str]:
103+
) -> PhoneAuthenticateResponse:
95104
"""Initiates an authentication order when the user is talking
96105
to the RP over the phone. The :py:meth:`collect` method
97106
is used to query the status of the order.
@@ -123,8 +132,8 @@ def phone_authenticate(
123132
this parameter indicates that userVisibleData holds formatting characters which
124133
potentially make for a more pleasant user experience.
125134
:type user_visible_data_format: str
126-
:return: The order response.
127-
:rtype: dict
135+
:return: The order response parsed to a dict.
136+
:rtype: PhoneAuthenticateResponse
128137
:raises BankIDError: raises a subclass of this error
129138
when error has been returned from server.
130139
@@ -155,7 +164,7 @@ def sign(
155164
requirement: Union[Dict[str, Any], None] = None,
156165
user_non_visible_data: Union[str, None] = None,
157166
user_visible_data_format: Union[str, None] = None,
158-
) -> Dict[str, str]:
167+
) -> SignResponse:
159168
"""Request a signing order. The :py:meth:`collect` method
160169
is used to query the status of the order.
161170
@@ -183,8 +192,8 @@ def sign(
183192
this parameter indicates that userVisibleData holds formatting characters which
184193
potentially make for a more pleasant user experience.
185194
:type user_visible_data_format: str
186-
:return: The order response.
187-
:rtype: dict
195+
:return: The order response parsed to a dict.
196+
:rtype: SignResponse
188197
:raises BankIDError: raises a subclass of this error
189198
when error has been returned from server.
190199
@@ -211,7 +220,7 @@ def phone_sign(
211220
requirement: Union[Dict[str, Any], None] = None,
212221
user_non_visible_data: Union[str, None] = None,
213222
user_visible_data_format: Union[str, None] = None,
214-
) -> Dict[str, str]:
223+
) -> PhoneSignResponse:
215224
"""Initiates an authentication order when the user is talking to
216225
the RP over the phone. The :py:meth:`collect` method
217226
is used to query the status of the order.
@@ -241,8 +250,8 @@ def phone_sign(
241250
this parameter indicates that userVisibleData holds formatting characters which
242251
potentially make for a more pleasant user experience.
243252
:type user_visible_data_format: str
244-
:return: The order response.
245-
:rtype: dict
253+
:return: The order response parsed to a dict.
254+
:rtype: PhoneSignResponse
246255
:raises BankIDError: raises a subclass of this error
247256
when error has been returned from server.
248257
@@ -266,7 +275,7 @@ def phone_sign(
266275
else:
267276
raise get_json_error_class(response)
268277

269-
def collect(self, order_ref: str) -> dict:
278+
def collect(self, order_ref: str) -> Union[CollectPendingResponse, CollectCompleteResponse, CollectFailedResponse]:
270279
"""Collects the result of a sign or auth order using the
271280
``orderRef`` as reference.
272281
@@ -325,8 +334,8 @@ def collect(self, order_ref: str) -> dict:
325334
326335
:param order_ref: The ``orderRef`` UUID returned from auth or sign.
327336
:type order_ref: str
328-
:return: The CollectResponse parsed to a dictionary.
329-
:rtype: dict
337+
:return: The order response parsed to a dict.
338+
:rtype: Union[CollectPendingResponse, CollectCompleteResponse, CollectFailedResponse]
330339
:raises BankIDError: raises a subclass of this error
331340
when error has been returned from server.
332341

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
[wheel]
22
universal=1
3+
4+
[tool:pytest]
5+
asyncio_default_fixture_loop_scope = session

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def ip_address() -> str:
1111
return "127.0.0.1"
1212

1313

14-
@pytest.fixture()
14+
@pytest.fixture(scope="session")
1515
def cert_and_key() -> Tuple[str, str]:
1616
cert, key = get_test_cert_and_key()
1717
return str(cert), str(key)

0 commit comments

Comments
 (0)