diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 734c893..0bc6f73 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,7 +6,7 @@ ## Upgrading - +* Listing gridpool orders that do not contain a price or quantity no longer raises an exception. Users must validate orders themselves. ## New Features diff --git a/src/frequenz/client/electricity_trading/_types.py b/src/frequenz/client/electricity_trading/_types.py index 0fff50c..3fcd5ee 100644 --- a/src/frequenz/client/electricity_trading/_types.py +++ b/src/frequenz/client/electricity_trading/_types.py @@ -1316,9 +1316,6 @@ def from_pb(cls, order_detail: electricity_trading_pb2.OrderDetail) -> Self: Returns: OrderDetail object corresponding to the protobuf message. - - Raises: - ValueError: If the order price and quantity are not specified for a non-canceled order. """ od = cls( order_id=order_detail.order_id, @@ -1332,18 +1329,24 @@ def from_pb(cls, order_detail: electricity_trading_pb2.OrderDetail) -> Self: ), ) + return od + + @property + def is_valid(self) -> bool: + """Check if the order detail is valid. + + Returns: + True if the order detail is valid, False otherwise. + """ # Only cancelled orders are allowed to have missing price or quantity - missing_price_or_quantity = ( - od.order.price.amount.is_nan() - or od.order.price.currency == Currency.UNSPECIFIED - or od.order.quantity.mw.is_nan() - ) - if missing_price_or_quantity and od.state_detail.state != OrderState.CANCELED: - raise ValueError( - f"Price and quantity must be specified for a non-canceled order (`{od}`)." - ) + if self.state_detail.state == OrderState.CANCELED: + return True - return od + return not ( + self.order.price.amount.is_nan() + or self.order.price.currency == Currency.UNSPECIFIED + or self.order.quantity.mw.is_nan() + ) def to_pb(self) -> electricity_trading_pb2.OrderDetail: """Convert an OrderDetail object to protobuf OrderDetail. diff --git a/tests/test_types.py b/tests/test_types.py index 7924557..0689ec3 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -551,25 +551,32 @@ def test_order_detail_from_pb() -> None: def test_order_detail_from_pb_missing_fields() -> None: """Test the client order detail type conversion from protobuf with missing fields.""" + # Previously missing price or quantity raised an error, now it is handled gracefully. # Missing price od_pb1 = electricity_trading_pb2.OrderDetail() od_pb1.CopyFrom(ORDER_DETAIL_PB) + OrderDetail.from_pb(od_pb1) + assert OrderDetail.from_pb(od_pb1).is_valid od_pb1.order.ClearField("price") + OrderDetail.from_pb(od_pb1) # Not allowed for active orders - with pytest.raises(ValueError): - OrderDetail.from_pb(od_pb1) - # But allowed for canceled orders + assert not OrderDetail.from_pb(od_pb1).is_valid od_pb1.state_detail.state = electricity_trading_pb2.OrderState.ORDER_STATE_CANCELED OrderDetail.from_pb(od_pb1) + # But allowed for canceled orders + assert OrderDetail.from_pb(od_pb1).is_valid # Missing quantity (same logic as above) od_pb2 = electricity_trading_pb2.OrderDetail() od_pb2.CopyFrom(ORDER_DETAIL_PB) + OrderDetail.from_pb(od_pb2) + assert OrderDetail.from_pb(od_pb2).is_valid od_pb2.order.ClearField("quantity") - with pytest.raises(ValueError): - OrderDetail.from_pb(od_pb2) + OrderDetail.from_pb(od_pb2) + assert not OrderDetail.from_pb(od_pb2).is_valid od_pb2.state_detail.state = electricity_trading_pb2.OrderState.ORDER_STATE_CANCELED OrderDetail.from_pb(od_pb2) + assert OrderDetail.from_pb(od_pb2).is_valid def test_order_detail_no_timezone_error() -> None: