Skip to content

Commit dbf0975

Browse files
committed
refactor(application): rename storage envelope key aws_error to provider_error with read-side alias
Outer envelope key in error_details is renamed to be provider-neutral. The read side accepts both old and new key names so persisted records written before this change keep working. Inner AWS-specific dict keys (aws_request_id, aws_error_code, aws_error_message) remain — they are AWS exception field names, not shared types.
1 parent 7862dfc commit dbf0975

3 files changed

Lines changed: 18 additions & 13 deletions

File tree

src/orb/application/request/dto.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ class RequestDTO(BaseDTO):
117117
version: int = 0
118118
resource_ids: list[str] = Field(default_factory=list)
119119
# Structured error block surfaced when status is failed or partial.
120-
# Populated from error_details["aws_error"] when an AWS API error was captured.
120+
# Populated from error_details["provider_error"] (or legacy "aws_error") when
121+
# an AWS API error was captured.
121122
error: Optional[dict[str, Any]] = None
122123
# Capacity fields from ProviderFulfilment — present when the provider emits them.
123124
# For weighted fleets target_units and fulfilled_units reflect capacity units;
@@ -170,9 +171,13 @@ def from_domain(
170171
resolved_fulfilment = None
171172

172173
# Build structured error block from error_details when available.
173-
error_block: Optional[dict[str, Any]] = (
174-
request.error_details.get("aws_error") if request.error_details else None
175-
)
174+
# Accept both "provider_error" (new) and legacy "aws_error" so that
175+
# records persisted before this deploy continue to surface the error block.
176+
error_block: Optional[dict[str, Any]] = None
177+
if request.error_details:
178+
error_block = request.error_details.get("provider_error") or request.error_details.get(
179+
"aws_error"
180+
)
176181

177182
# Create the DTO with all available fields
178183
return cls(

src/orb/application/services/request_status_management_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def _handle_provisioning_failure(self, request: Any, provisioning_result: Any) -
117117

118118
# Merge into error_details so it survives serialization / persistence.
119119
current = dict(request.error_details) if request.error_details else {}
120-
current["aws_error"] = aws_error_block
120+
current["provider_error"] = aws_error_block
121121
# Pydantic freeze-safe: use model_copy for the error_details field.
122122
from orb.domain.request.aggregate import Request as RequestAggregate
123123

tests/providers/aws/unit/test_aws_error_propagation.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
and returns None values for non-AWSError exceptions.
99
- ProvisioningResult carries AWS error fields through from the exception.
1010
- RequestStatusManagementService._handle_provisioning_failure writes
11-
error_details["aws_error"] onto the Request aggregate.
12-
- RequestDTO.from_domain exposes error_details["aws_error"] as the top-level
13-
error field; to_dict includes the error block only when present.
11+
error_details["provider_error"] onto the Request aggregate.
12+
- RequestDTO.from_domain exposes error_details["provider_error"] (or legacy
13+
"aws_error") as the top-level error field; to_dict includes it only when present.
1414
"""
1515

1616
from __future__ import annotations
@@ -207,7 +207,7 @@ def test_partial_aws_error_attrs(self):
207207

208208

209209
class TestHandleProvisioningFailure:
210-
"""error_details["aws_error"] is written when provisioning fails with AWS context."""
210+
"""error_details["provider_error"] is written when provisioning fails with AWS context."""
211211

212212
def _make_service(self):
213213
from orb.application.services.request_status_management_service import (
@@ -265,10 +265,10 @@ def test_no_aws_fields_leaves_error_details_clean(self):
265265
result = self._make_provisioning_result(error_message="generic error")
266266
updated = svc._handle_provisioning_failure(request, result)
267267
# aws_error key should be absent when no AWS details are available
268-
assert "aws_error" not in updated.error_details
268+
assert "provider_error" not in updated.error_details
269269

270270
def test_aws_error_code_written_to_error_details(self):
271-
"""UnauthorizedOperation is stored in error_details["aws_error"]["code"]."""
271+
"""UnauthorizedOperation is stored in error_details["provider_error"]["code"]."""
272272
svc = self._make_service()
273273
request = self._make_real_request()
274274
result = self._make_provisioning_result(
@@ -279,7 +279,7 @@ def test_aws_error_code_written_to_error_details(self):
279279
error_source="aws.ec2.run_instances",
280280
)
281281
updated = svc._handle_provisioning_failure(request, result)
282-
aws_err = updated.error_details.get("aws_error", {})
282+
aws_err = updated.error_details.get("provider_error", {})
283283
assert aws_err["code"] == "UnauthorizedOperation"
284284
assert aws_err["message"] == "You are not authorized."
285285
assert aws_err["aws_request_id"] == "rid-aws-001"
@@ -295,7 +295,7 @@ def test_partial_aws_fields_stored(self):
295295
# aws_error_message, aws_request_id, error_source intentionally absent
296296
)
297297
updated = svc._handle_provisioning_failure(request, result)
298-
aws_err = updated.error_details.get("aws_error", {})
298+
aws_err = updated.error_details.get("provider_error", {})
299299
assert aws_err["code"] == "InsufficientInstanceCapacity"
300300
assert "message" not in aws_err
301301
assert "aws_request_id" not in aws_err

0 commit comments

Comments
 (0)