Skip to content

Commit 2d01721

Browse files
committed
chore: typing
1 parent cb355e7 commit 2d01721

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

airbyte_cdk/sources/declarative/auth/jwt.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,27 @@
66
import json
77
from dataclasses import InitVar, dataclass
88
from datetime import datetime
9-
from typing import Any, Mapping, Optional, Union
9+
from typing import Any, Mapping, Optional, Union, cast
1010

1111
import jwt
1212
from cryptography.hazmat.backends import default_backend
1313
from cryptography.hazmat.primitives import serialization
14+
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
15+
from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey
16+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
17+
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
1418
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
1519

1620
from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator
1721
from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
1822
from airbyte_cdk.sources.declarative.interpolation.interpolated_mapping import InterpolatedMapping
1923
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
2024

25+
# Type alias for keys that JWT library accepts
26+
JwtKeyTypes = Union[
27+
RSAPrivateKey, EllipticCurvePrivateKey, Ed25519PrivateKey, Ed448PrivateKey, str, bytes
28+
]
29+
2130

2231
class JwtAlgorithm(str):
2332
"""
@@ -158,18 +167,21 @@ def _get_jwt_payload(self) -> dict[str, Any]:
158167
payload["nbf"] = nbf
159168
return payload
160169

161-
def _get_secret_key(self) -> PrivateKeyTypes | str | bytes:
170+
def _get_secret_key(self) -> JwtKeyTypes:
162171
"""
163172
Returns the secret key used to sign the JWT.
164173
"""
165174
secret_key: str = self._secret_key.eval(self.config, json_loads=json.loads)
166175

167176
if self._passphrase:
168-
return serialization.load_pem_private_key(
177+
# Load encrypted private key and cast to JWT-compatible type
178+
# The JWT algorithms we support (RSA, ECDSA, EdDSA) use compatible key types
179+
private_key = serialization.load_pem_private_key(
169180
secret_key.encode(),
170181
password=self._passphrase.eval(self.config, json_loads=json.loads).encode(),
171182
backend=default_backend(),
172183
)
184+
return cast(JwtKeyTypes, private_key)
173185
else:
174186
return (
175187
base64.b64encode(secret_key.encode()).decode()

airbyte_cdk/sources/declarative/interpolation/macros.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,12 @@ def camel_case_to_snake_case(value: str) -> str:
208208
return re.sub(r"(?<!^)(?=[A-Z])", "_", value).lower()
209209

210210

211-
def random_uuid() -> str:
211+
def generate_uuid() -> str:
212212
"""
213213
Generates a UUID4
214+
215+
Usage:
216+
`"{{ generate_uuid() }}"`
214217
"""
215218
return str(uuid.uuid4())
216219

@@ -228,5 +231,6 @@ def random_uuid() -> str:
228231
str_to_datetime,
229232
sanitize_url,
230233
camel_case_to_snake_case,
234+
generate_uuid,
231235
]
232236
macros = {f.__name__: f for f in _macros_list}

unit_tests/sources/declarative/interpolation/test_macros.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#
44

55
import datetime
6+
import uuid
67

78
import pytest
89

@@ -20,6 +21,7 @@
2021
("test_format_datetime", "format_datetime", True),
2122
("test_duration", "duration", True),
2223
("test_camel_case_to_snake_case", "camel_case_to_snake_case", True),
24+
("test_generate_uuid", "generate_uuid", True),
2325
("test_not_a_macro", "thisisnotavalidmacro", False),
2426
],
2527
)
@@ -275,3 +277,26 @@ def test_sanitize_url(test_name, input_value, expected_output):
275277
)
276278
def test_camel_case_to_snake_case(value, expected_value):
277279
assert macros["camel_case_to_snake_case"](value) == expected_value
280+
281+
282+
def test_generate_uuid():
283+
"""Test uuid macro generates valid UUID4 strings."""
284+
uuid_fn = macros["generate_uuid"]
285+
286+
# Test that uuid function returns a string
287+
result = uuid_fn()
288+
assert isinstance(result, str)
289+
290+
# Test that the result is a valid UUID format
291+
# This will raise ValueError if not a valid UUID
292+
parsed_uuid = uuid.UUID(result)
293+
294+
# Test that it's specifically a UUID4 (version 4)
295+
assert parsed_uuid.version == 4
296+
297+
# Test that multiple calls return different UUIDs
298+
result2 = uuid_fn()
299+
assert result != result2
300+
301+
# Test that both results are valid UUIDs
302+
uuid.UUID(result2) # Will raise ValueError if invalid

0 commit comments

Comments
 (0)