mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-31 10:31:35 +00:00
Add cancel_status to POST /api/order
This commit is contained in:
parent
6b8b694d07
commit
99ecf54b18
@ -996,7 +996,17 @@ class Logics:
|
|||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cancel_order(cls, order, user, state=None):
|
def cancel_order(cls, order, user, cancel_status=None):
|
||||||
|
# If cancel 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 cancel_status is not None:
|
||||||
|
if order.status != cancel_status:
|
||||||
|
return False, {
|
||||||
|
"bad_request": f"Current order status is {order.status}, not {cancel_status}."
|
||||||
|
}
|
||||||
|
|
||||||
# Do not change order status if an is in order
|
# Do not change order status if an is in order
|
||||||
# any of these status
|
# any of these status
|
||||||
do_not_cancel = [
|
do_not_cancel = [
|
||||||
|
@ -245,6 +245,10 @@ class OrderViewSchema:
|
|||||||
- `17` - Maker lost dispute
|
- `17` - Maker lost dispute
|
||||||
- `18` - Taker lost dispute
|
- `18` - Taker lost dispute
|
||||||
|
|
||||||
|
The client can use `cancel_status` to cancel the order only
|
||||||
|
if it is in the specified status. The server will
|
||||||
|
return an error without cancelling the trade otherwise.
|
||||||
|
|
||||||
Note that there are penalties involved for cancelling a order
|
Note that there are penalties involved for cancelling a order
|
||||||
mid-trade so use this action carefully:
|
mid-trade so use this action carefully:
|
||||||
|
|
||||||
|
@ -641,6 +641,13 @@ class UpdateOrderSerializer(serializers.Serializer):
|
|||||||
mining_fee_rate = serializers.DecimalField(
|
mining_fee_rate = serializers.DecimalField(
|
||||||
max_digits=6, decimal_places=3, allow_null=True, required=False, default=None
|
max_digits=6, decimal_places=3, allow_null=True, required=False, default=None
|
||||||
)
|
)
|
||||||
|
cancel_status = serializers.ChoiceField(
|
||||||
|
choices=Order.Status.choices,
|
||||||
|
allow_null=True,
|
||||||
|
allow_blank=True,
|
||||||
|
default=None,
|
||||||
|
help_text="Status the order should have for it to be cancelled.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClaimRewardSerializer(serializers.Serializer):
|
class ClaimRewardSerializer(serializers.Serializer):
|
||||||
|
@ -518,6 +518,7 @@ class OrderView(viewsets.ViewSet):
|
|||||||
mining_fee_rate = serializer.data.get("mining_fee_rate")
|
mining_fee_rate = serializer.data.get("mining_fee_rate")
|
||||||
statement = serializer.data.get("statement")
|
statement = serializer.data.get("statement")
|
||||||
rating = serializer.data.get("rating")
|
rating = serializer.data.get("rating")
|
||||||
|
cancel_status = serializer.data.get("cancel_status")
|
||||||
|
|
||||||
# 1) If action is take, it is a taker request!
|
# 1) If action is take, it is a taker request!
|
||||||
if action == "take":
|
if action == "take":
|
||||||
@ -593,7 +594,7 @@ class OrderView(viewsets.ViewSet):
|
|||||||
|
|
||||||
# 3) If action is cancel
|
# 3) If action is cancel
|
||||||
elif action == "cancel":
|
elif action == "cancel":
|
||||||
valid, context = Logics.cancel_order(order, request.user)
|
valid, context = Logics.cancel_order(order, request.user, cancel_status)
|
||||||
if not valid:
|
if not valid:
|
||||||
return Response(context, status.HTTP_400_BAD_REQUEST)
|
return Response(context, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
@ -1996,6 +1996,9 @@ components:
|
|||||||
format: decimal
|
format: decimal
|
||||||
pattern: ^-?\d{0,3}(?:\.\d{0,3})?$
|
pattern: ^-?\d{0,3}(?:\.\d{0,3})?$
|
||||||
nullable: true
|
nullable: true
|
||||||
|
cancel_status:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/StatusEnum'
|
||||||
required:
|
required:
|
||||||
- action
|
- action
|
||||||
Version:
|
Version:
|
||||||
|
@ -150,6 +150,7 @@ const TradeBox = ({ currentOrder, onStartAgain }: TradeBoxProps): JSX.Element =>
|
|||||||
mining_fee_rate?: number;
|
mining_fee_rate?: number;
|
||||||
statement?: string;
|
statement?: string;
|
||||||
rating?: number;
|
rating?: number;
|
||||||
|
cancel_status?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renewOrder = function (): void {
|
const renewOrder = function (): void {
|
||||||
@ -188,6 +189,7 @@ const TradeBox = ({ currentOrder, onStartAgain }: TradeBoxProps): JSX.Element =>
|
|||||||
mining_fee_rate,
|
mining_fee_rate,
|
||||||
statement,
|
statement,
|
||||||
rating,
|
rating,
|
||||||
|
cancel_status
|
||||||
}: SubmitActionProps): void {
|
}: SubmitActionProps): void {
|
||||||
const slot = garage.getSlot();
|
const slot = garage.getSlot();
|
||||||
|
|
||||||
@ -201,6 +203,7 @@ const TradeBox = ({ currentOrder, onStartAgain }: TradeBoxProps): JSX.Element =>
|
|||||||
mining_fee_rate,
|
mining_fee_rate,
|
||||||
statement,
|
statement,
|
||||||
rating,
|
rating,
|
||||||
|
cancel_status
|
||||||
})
|
})
|
||||||
.then((data: Order) => {
|
.then((data: Order) => {
|
||||||
setOpen(closeAll);
|
setOpen(closeAll);
|
||||||
@ -222,8 +225,14 @@ const TradeBox = ({ currentOrder, onStartAgain }: TradeBoxProps): JSX.Element =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cancel = function (): void {
|
const cancel = function (): void {
|
||||||
|
const order = garage.getSlot()?.order;
|
||||||
|
const noConfirmation = Boolean(order?.is_maker && [0, 1, 2].includes(order?.status));
|
||||||
|
|
||||||
setLoadingButtons({ ...noLoadingButtons, cancel: true });
|
setLoadingButtons({ ...noLoadingButtons, cancel: true });
|
||||||
submitAction({ action: 'cancel' });
|
submitAction({
|
||||||
|
action: 'cancel',
|
||||||
|
cancel_status: noConfirmation ? order?.status : undefined
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const openDispute = function (): void {
|
const openDispute = function (): void {
|
||||||
|
@ -21,6 +21,7 @@ export interface SubmitActionProps {
|
|||||||
statement?: string;
|
statement?: string;
|
||||||
rating?: number;
|
rating?: number;
|
||||||
amount?: number;
|
amount?: number;
|
||||||
|
cancel_status?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TradeRobotSummary {
|
export interface TradeRobotSummary {
|
||||||
|
@ -143,6 +143,9 @@ SPECTACULAR_SETTINGS = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"REDOC_DIST": "SIDECAR",
|
"REDOC_DIST": "SIDECAR",
|
||||||
|
"ENUM_NAME_OVERRIDES": {
|
||||||
|
"StatusEnum": "api.models.order.Order.Status",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -760,6 +760,58 @@ class TradeTest(BaseAPITestCase):
|
|||||||
f"❌ Hey {maker_nick}, you have cancelled your public order with ID {trade.order_id}.",
|
f"❌ Hey {maker_nick}, you have cancelled your public order with ID {trade.order_id}.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_cancel_order_cancel_status(self):
|
||||||
|
"""
|
||||||
|
Tests the cancellation of a public order using cancel_status.
|
||||||
|
"""
|
||||||
|
trade = Trade(self.client)
|
||||||
|
trade.publish_order()
|
||||||
|
data = trade.response.json()
|
||||||
|
|
||||||
|
self.assertEqual(trade.response.status_code, 200)
|
||||||
|
self.assertResponse(trade.response)
|
||||||
|
|
||||||
|
self.assertEqual(data["status_message"], Order.Status(Order.Status.WFB).label)
|
||||||
|
|
||||||
|
# Cancel order if the order status is waiting for maker bond
|
||||||
|
trade.cancel_order(cancel_status=Order.Status.WFB)
|
||||||
|
data = trade.response.json()
|
||||||
|
|
||||||
|
self.assertEqual(trade.response.status_code, 400)
|
||||||
|
self.assertResponse(trade.response)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
data["bad_request"], "This order has been cancelled by the maker"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_cancel_order_different_cancel_status(self):
|
||||||
|
"""
|
||||||
|
Tests the cancellation of a paused order with a different cancel_status.
|
||||||
|
"""
|
||||||
|
trade = Trade(self.client)
|
||||||
|
trade.get_order()
|
||||||
|
data = trade.response.json()
|
||||||
|
|
||||||
|
self.assertEqual(trade.response.status_code, 200)
|
||||||
|
self.assertResponse(trade.response)
|
||||||
|
|
||||||
|
self.assertEqual(data["status_message"], Order.Status(Order.Status.WFB).label)
|
||||||
|
|
||||||
|
# Try to cancel order if it is public
|
||||||
|
trade.cancel_order(cancel_status=Order.Status.PUB)
|
||||||
|
data = trade.response.json()
|
||||||
|
|
||||||
|
self.assertEqual(trade.response.status_code, 400)
|
||||||
|
self.assertResponse(trade.response)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
data["bad_request"],
|
||||||
|
f"Current order status is {Order.Status.WFB}, not {Order.Status.PUB}."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Cancel order to avoid leaving pending HTLCs after a successful test
|
||||||
|
trade.cancel_order()
|
||||||
|
|
||||||
def test_collaborative_cancel_order_in_chat(self):
|
def test_collaborative_cancel_order_in_chat(self):
|
||||||
"""
|
"""
|
||||||
Tests the collaborative cancellation of an order in the chat state
|
Tests the collaborative cancellation of an order in the chat state
|
||||||
|
@ -112,11 +112,11 @@ class Trade:
|
|||||||
self.response = self.client.get(path + params, **headers)
|
self.response = self.client.get(path + params, **headers)
|
||||||
|
|
||||||
@patch("api.tasks.send_notification.delay", send_notification)
|
@patch("api.tasks.send_notification.delay", send_notification)
|
||||||
def cancel_order(self, robot_index=1):
|
def cancel_order(self, robot_index=1, cancel_status=None):
|
||||||
path = reverse("order")
|
path = reverse("order")
|
||||||
params = f"?order_id={self.order_id}"
|
params = f"?order_id={self.order_id}"
|
||||||
headers = self.get_robot_auth(robot_index)
|
headers = self.get_robot_auth(robot_index)
|
||||||
body = {"action": "cancel"}
|
body = {"action": "cancel", "cancel_status": cancel_status}
|
||||||
self.response = self.client.post(path + params, body, **headers)
|
self.response = self.client.post(path + params, body, **headers)
|
||||||
|
|
||||||
@patch("api.tasks.send_notification.delay", send_notification)
|
@patch("api.tasks.send_notification.delay", send_notification)
|
||||||
|
Loading…
Reference in New Issue
Block a user