From 6647e164c35e38aed2692af6b8179fcaa5a98813 Mon Sep 17 00:00:00 2001 From: jerryfletcher21 Date: Sat, 15 Jun 2024 04:07:18 +0200 Subject: [PATCH] add new param current_status to /api/order cancel --- api/logics.py | 12 +++++++++++- api/oas_schemas.py | 5 +++++ api/serializers.py | 7 +++++++ api/views.py | 15 ++++++++++++++- docs/assets/schemas/api-latest.yaml | 11 +++++++++++ tests/test_trade_pipeline.py | 14 +++++++++++++- tests/utils/trade.py | 4 +++- 7 files changed, 64 insertions(+), 4 deletions(-) diff --git a/api/logics.py b/api/logics.py index 7049e21c..70645383 100644 --- a/api/logics.py +++ b/api/logics.py @@ -991,7 +991,17 @@ class Logics: return False, None @classmethod - def cancel_order(cls, order, user, state=None): + def cancel_order(cls, order, user, current_status=None): + # If current status is specified, do no cancel the order + # if it is not the correct one. + # This prevents the client from cancelling an order that + # recently changed status. + if current_status is not None: + if order.status != current_status: + return False, { + "bad_request": f"Wrong status, current one is {order.status}" + } + # Do not change order status if an is in order # any of these status do_not_cancel = [ diff --git a/api/oas_schemas.py b/api/oas_schemas.py index 8b6412e6..5263456c 100644 --- a/api/oas_schemas.py +++ b/api/oas_schemas.py @@ -245,6 +245,11 @@ class OrderViewSchema: - `17` - Maker lost dispute - `18` - Taker lost dispute + The client can specify `current_status` to make sure that the + order is cancelled just if it is in right status. + If the order is not in the specified status, the server will + return an error without cancelling the trade. + Note that there are penalties involved for cancelling a order mid-trade so use this action carefully: diff --git a/api/serializers.py b/api/serializers.py index 4bd41fd2..9ed0d782 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -628,6 +628,13 @@ class UpdateOrderSerializer(serializers.Serializer): mining_fee_rate = serializers.DecimalField( max_digits=6, decimal_places=3, allow_null=True, required=False, default=None ) + current_status = serializers.IntegerField( + min_value=Decimal(0), + max_value=18, + allow_null=True, + required=False, + help_text="Current order status for the client", + ) class ClaimRewardSerializer(serializers.Serializer): diff --git a/api/views.py b/api/views.py index 78bd275c..b5045259 100644 --- a/api/views.py +++ b/api/views.py @@ -511,6 +511,7 @@ class OrderView(viewsets.ViewSet): mining_fee_rate = serializer.data.get("mining_fee_rate") statement = serializer.data.get("statement") rating = serializer.data.get("rating") + current_status_int = serializer.data.get("current_status", None) # 1) If action is take, it is a taker request! if action == "take": @@ -586,7 +587,19 @@ class OrderView(viewsets.ViewSet): # 3) If action is cancel elif action == "cancel": - valid, context = Logics.cancel_order(order, request.user) + if current_status_int is None: + current_status = None + else: + if current_status_int < 0 or current_status_int >= len(Order.Status): + return Response( + {"bad_request": "current_status is not in the correct range"}, + status.HTTP_400_BAD_REQUEST + ) + current_status = Order.Status(current_status_int) + + valid, context = Logics.cancel_order( + order, request.user, current_status=current_status + ) if not valid: return Response(context, status.HTTP_400_BAD_REQUEST) diff --git a/docs/assets/schemas/api-latest.yaml b/docs/assets/schemas/api-latest.yaml index afdd9436..1ab048b4 100644 --- a/docs/assets/schemas/api-latest.yaml +++ b/docs/assets/schemas/api-latest.yaml @@ -469,6 +469,11 @@ paths: - `17` - Maker lost dispute - `18` - Taker lost dispute + The client can specify `current_status` to make sure that the + order is cancelled just if it is in right status. + If the order is not in the specified status, the server will + return an error without cancelling the trade. + Note that there are penalties involved for cancelling a order mid-trade so use this action carefully: @@ -1950,6 +1955,12 @@ components: format: decimal pattern: ^-?\d{0,3}(?:\.\d{0,3})?$ nullable: true + current_status: + type: integer + maximum: 18 + minimum: 0 + nullable: true + description: Current order status for the client required: - action Version: diff --git a/tests/test_trade_pipeline.py b/tests/test_trade_pipeline.py index 32a39c9e..db0715a4 100644 --- a/tests/test_trade_pipeline.py +++ b/tests/test_trade_pipeline.py @@ -659,7 +659,19 @@ class TradeTest(BaseAPITestCase): """ trade = Trade(self.client) trade.publish_order() - trade.cancel_order() + + trade.cancel_order(current_status_int=6) + + data = trade.response.json() + + self.assertEqual(trade.response.status_code, 400) + self.assertResponse(trade.response) + + self.assertEqual( + data["bad_request"], "Wrong status, current one is 1" + ) + + trade.cancel_order(current_status_int=1) data = trade.response.json() diff --git a/tests/utils/trade.py b/tests/utils/trade.py index 16b9b89d..a77b5393 100644 --- a/tests/utils/trade.py +++ b/tests/utils/trade.py @@ -112,11 +112,13 @@ class Trade: self.response = self.client.get(path + params, **headers) @patch("api.tasks.send_notification.delay", send_notification) - def cancel_order(self, robot_index=1): + def cancel_order(self, robot_index=1, current_status_int=None): path = reverse("order") params = f"?order_id={self.order_id}" headers = self.get_robot_auth(robot_index) body = {"action": "cancel"} + if current_status_int is not None: + body.update({"current_status": current_status_int}) self.response = self.client.post(path + params, body, **headers) def pause_order(self, robot_index=1):