Skip to content

Commit 020d245

Browse files
chore: Add Correlation ID to API Error Response (#35)
* chore: Add Correlation ID to API Error Response * release 0.0.8 * changelog
1 parent 5ad49e0 commit 020d245

File tree

11 files changed

+90
-43
lines changed

11 files changed

+90
-43
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## Unreleased
44

5+
## [0.0.8] - 2024-10-28
6+
7+
### Added
8+
9+
- Include correlation ID in API Errors.
10+
511
## [0.0.7] - 2024-10-25
612

713
### Added

cdp/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.7"
1+
__version__ = "0.0.8"

cdp/errors.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ def __init__(
1212
err: ApiException,
1313
code: str | None = None,
1414
message: str | None = None,
15+
correlation_id: str | None = None,
1516
unhandled: bool = False,
1617
) -> None:
1718
self._http_code = err.status
1819
self._api_code = code
1920
self._api_message = message
21+
self._correlation_id = correlation_id
2022
self._handled = bool(code and message and not unhandled)
2123
super().__init__(str(err))
2224

@@ -47,11 +49,16 @@ def from_error(cls, err: ApiException) -> "ApiError":
4749

4850
message = body.get("message")
4951
code = body.get("code")
52+
correlation_id = body.get("correlation_id")
5053

5154
if code in ERROR_CODE_TO_ERROR_CLASS:
52-
return ERROR_CODE_TO_ERROR_CLASS[code](err, code=code, message=message)
55+
return ERROR_CODE_TO_ERROR_CLASS[code](
56+
err, code=code, message=message, correlation_id=correlation_id
57+
)
5358
else:
54-
return cls(err, code=code, message=message, unhandled=True)
59+
return cls(
60+
err, code=code, message=message, correlation_id=correlation_id, unhandled=True
61+
)
5562

5663
@property
5764
def http_code(self) -> int:
@@ -83,6 +90,16 @@ def api_message(self) -> str | None:
8390
"""
8491
return self._api_message
8592

93+
@property
94+
def correlation_id(self) -> str | None:
95+
"""Get the correlation ID.
96+
97+
Returns:
98+
str | None: The correlation ID.
99+
100+
"""
101+
return self._correlation_id
102+
86103
@property
87104
def handled(self) -> bool:
88105
"""Get whether the error is handled.
@@ -101,9 +118,9 @@ def __str__(self) -> str:
101118
102119
"""
103120
if self.handled:
104-
return f"ApiError(http_code={self.http_code}, api_code={self.api_code}, api_message={self.api_message})"
121+
return f"ApiError(http_code={self.http_code}, api_code={self.api_code}, api_message={self.api_message}, correlation_id={self.correlation_id})"
105122
else:
106-
return f"ApiError(http_code={self.http_code}, api_code={self.api_code}, api_message={self.api_message}, unhandled=True)"
123+
return f"ApiError(http_code={self.http_code}, api_code={self.api_code}, api_message={self.api_message}, correlation_id={self.correlation_id}, unhandled={self.handled})"
107124

108125

109126
class InvalidConfigurationError(Exception):

cdp/wallet.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,9 @@ def create_webhook(self, notification_uri: str) -> "Webhook":
301301
Exception: If there's an error creating the webhook.
302302
303303
"""
304-
create_wallet_webhook_request = CreateWalletWebhookRequest(notification_uri = notification_uri)
304+
create_wallet_webhook_request = CreateWalletWebhookRequest(
305+
notification_uri=notification_uri
306+
)
305307
model = Cdp.api_clients.webhooks.create_wallet_webhook(
306308
wallet_id=self.id, create_wallet_webhook_request=create_wallet_webhook_request
307309
)

cdp/webhook.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ def event_filters(self) -> list[WebhookEventFilter]:
8282

8383
@classmethod
8484
def create(
85-
cls,
86-
notification_uri: str,
87-
event_type: WebhookEventType,
88-
event_type_filter: WebhookEventTypeFilter | None = None,
89-
event_filters: list[WebhookEventFilter] | None = None,
90-
network_id: str = "base-sepolia",
85+
cls,
86+
notification_uri: str,
87+
event_type: WebhookEventType,
88+
event_type_filter: WebhookEventTypeFilter | None = None,
89+
event_filters: list[WebhookEventFilter] | None = None,
90+
network_id: str = "base-sepolia",
9191
) -> "Webhook":
9292
"""Create a new webhook.
9393
@@ -103,11 +103,11 @@ def create(
103103
104104
"""
105105
create_webhook_request = CreateWebhookRequest(
106-
network_id=network_id,
107-
event_type=event_type,
108-
event_type_filter=event_type_filter,
109-
event_filters=event_filters,
110-
notification_uri=notification_uri,
106+
network_id=network_id,
107+
event_type=event_type,
108+
event_type_filter=event_type_filter,
109+
event_filters=event_filters,
110+
notification_uri=notification_uri,
111111
)
112112

113113
model = Cdp.api_clients.webhooks.create_webhook(create_webhook_request)
@@ -149,7 +149,7 @@ def delete(webhook_id: str) -> None:
149149
def update(
150150
self,
151151
notification_uri: str | None = None,
152-
event_type_filter: WebhookEventTypeFilter | None = None
152+
event_type_filter: WebhookEventTypeFilter | None = None,
153153
) -> "Webhook":
154154
"""Update the webhook with a new notification URI, and/or a new list of addresses to monitor.
155155

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
project = 'CDP SDK'
1616
author = 'Coinbase Developer Platform'
17-
release = '0.0.7'
17+
release = '0.0.8'
1818

1919
# -- General configuration ---------------------------------------------------
2020
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "cdp-sdk"
3-
version = "0.0.7"
3+
version = "0.0.8"
44
description = "CDP Python SDK"
55
readme = "README.md"
66
authors = [{name = "John Peterson", email = "[email protected]"}]

tests/factories/webhook_factory.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,22 @@
66
@pytest.fixture
77
def webhook_factory():
88
"""Create and return a factory for Webhook fixtures."""
9+
910
def _create_webhook(
1011
webhook_id="webhook-123",
1112
network_id="base-sepolia",
1213
notification_uri="https://example.com/webhook",
1314
event_type=WebhookEventType.WALLET_ACTIVITY,
1415
event_type_filter=None,
15-
event_filters=None
16+
event_filters=None,
1617
):
1718
model = WebhookModel(
1819
id=webhook_id,
1920
network_id=network_id,
2021
notification_uri=notification_uri,
2122
event_type=event_type,
2223
event_type_filter=event_type_filter,
23-
event_filters=event_filters or []
24+
event_filters=event_filters or [],
2425
)
2526
return Webhook(model)
2627

tests/test_errors.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,33 @@
1919
def test_api_error_init():
2020
"""Test API error initialization."""
2121
err = ApiException(400, "Bad Request")
22-
api_error = ApiError(err, code="test_code", message="Test message")
22+
api_error = ApiError(
23+
err, code="test_code", message="Test message", correlation_id="test-correlation-id"
24+
)
2325

2426
assert api_error.http_code == 400
2527
assert api_error.api_code == "test_code"
2628
assert api_error.api_message == "Test message"
29+
assert api_error.correlation_id == "test-correlation-id"
2730
assert api_error.handled is True
2831

2932

3033
def test_api_error_from_error_with_valid_json():
3134
"""Test API error from error with valid JSON."""
3235
err = ApiException(400, "Bad Request")
33-
err.body = json.dumps({"code": "invalid_wallet_id", "message": "Invalid wallet ID"})
36+
err.body = json.dumps(
37+
{
38+
"code": "invalid_wallet_id",
39+
"message": "Invalid wallet ID",
40+
"correlation_id": "test-correlation-id",
41+
}
42+
)
3443
api_error = ApiError.from_error(err)
3544

3645
assert isinstance(api_error, ERROR_CODE_TO_ERROR_CLASS["invalid_wallet_id"])
3746
assert api_error.api_code == "invalid_wallet_id"
3847
assert api_error.api_message == "Invalid wallet ID"
48+
assert api_error.correlation_id == "test-correlation-id"
3949

4050

4151
def test_api_error_from_error_with_invalid_json():
@@ -47,26 +57,39 @@ def test_api_error_from_error_with_invalid_json():
4757
assert isinstance(api_error, ApiError)
4858
assert api_error.api_code is None
4959
assert api_error.api_message is None
60+
assert api_error.correlation_id is None
5061

5162

5263
def test_api_error_from_error_with_unknown_code():
5364
"""Test API error from error with unknown code."""
5465
err = ApiException(400, "Bad Request")
55-
err.body = json.dumps({"code": "unknown_code", "message": "Unknown error"})
66+
err.body = json.dumps(
67+
{
68+
"code": "unknown_code",
69+
"message": "Unknown error",
70+
"correlation_id": "test-correlation-id",
71+
}
72+
)
5673
api_error = ApiError.from_error(err)
5774

5875
assert isinstance(api_error, ApiError)
5976
assert api_error.api_code == "unknown_code"
6077
assert api_error.api_message == "Unknown error"
78+
assert api_error.correlation_id == "test-correlation-id"
6179
assert api_error.handled is False
6280

6381

6482
def test_api_error_str_representation():
6583
"""Test API error string representation."""
6684
err = ApiException(400, "Bad Request")
67-
api_error = ApiError(err, code="test_code", message="Test message")
68-
69-
assert str(api_error) == "ApiError(http_code=400, api_code=test_code, api_message=Test message)"
85+
api_error = ApiError(
86+
err, code="test_code", message="Test message", correlation_id="test-correlation-id"
87+
)
88+
89+
assert (
90+
str(api_error)
91+
== "ApiError(http_code=400, api_code=test_code, api_message=Test message, correlation_id=test-correlation-id)"
92+
)
7093

7194

7295
def test_invalid_configuration_error():

tests/test_wallet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ def test_wallet_deploy_multi_token_with_server_signer(wallet_factory):
617617
"https://example.com/multi-token/{id}.json"
618618
)
619619

620+
620621
@patch("cdp.Cdp.api_clients")
621622
def test_create_webhook(mock_api_clients, wallet_factory, webhook_factory):
622623
"""Test Wallet create_webhook method."""
@@ -636,8 +637,7 @@ def test_create_webhook(mock_api_clients, wallet_factory, webhook_factory):
636637

637638
# Assert that the API client was called with the correct parameters
638639
mock_api_clients.webhooks.create_wallet_webhook.assert_called_once_with(
639-
wallet_id=wallet.id,
640-
create_wallet_webhook_request=expected_request
640+
wallet_id=wallet.id, create_wallet_webhook_request=expected_request
641641
)
642642

643643
# Assert that the returned webhook is an instance of Webhook

0 commit comments

Comments
 (0)