diff --git a/addons/bus/controllers/websocket.py b/addons/bus/controllers/websocket.py index 4f1aea1edf1..c3840370a86 100644 --- a/addons/bus/controllers/websocket.py +++ b/addons/bus/controllers/websocket.py @@ -44,6 +44,9 @@ class WebsocketController(Controller): channel_with_db(request.db, c) for c in request.env['ir.websocket']._build_bus_channel_list(channels) )) + last_known_notification_id = request.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + if last > last_known_notification_id: + last = 0 notifications = request.env['bus.bus']._poll(channels, last) return {'channels': channels, 'notifications': notifications} diff --git a/addons/bus/models/ir_websocket.py b/addons/bus/models/ir_websocket.py index 5c1b6b3ffd0..68d187ad974 100644 --- a/addons/bus/models/ir_websocket.py +++ b/addons/bus/models/ir_websocket.py @@ -32,6 +32,9 @@ class IrWebsocket(models.AbstractModel): def _subscribe(self, data): if not all(isinstance(c, str) for c in data['channels']): raise ValueError("bus.Bus only string channels are allowed.") + last_known_notification_id = self.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + if data['last'] > last_known_notification_id: + data['last'] = 0 channels = set(self._build_bus_channel_list(data['channels'])) dispatch.subscribe(channels, data['last'], self.env.registry.db_name, wsrequest.ws) diff --git a/addons/bus/tests/test_websocket_caryall.py b/addons/bus/tests/test_websocket_caryall.py index fcb02ebf076..07bdc5740a2 100644 --- a/addons/bus/tests/test_websocket_caryall.py +++ b/addons/bus/tests/test_websocket_caryall.py @@ -248,3 +248,39 @@ class TestWebsocketCaryall(WebsocketCase): # But ChromeHeadless still can. headers = ['User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/102.0.0.0 Safari/537.36'] self.websocket_connect() + + def test_subscribe_higher_last_notification_id(self): + subscribe_done_event = Event() + server_last_notification_id = self.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + client_last_notification_id = server_last_notification_id + 1 + + def subscribe_side_effect(_, last): + # Last notification id given by the client is higher than + # the one known by the server, should default to 0. + self.assertEqual(last, 0) + subscribe_done_event.set() + + with patch.object(Websocket, 'subscribe', side_effect=subscribe_side_effect): + websocket = self.websocket_connect() + websocket.send(json.dumps({ + 'event_name': 'subscribe', + 'data': {'channels': ['my_channel'], 'last': client_last_notification_id} + })) + subscribe_done_event.wait() + + def test_subscribe_lower_last_notification_id(self): + subscribe_done_event = Event() + server_last_notification_id = self.env['bus.bus'].sudo().search([], limit=1, order='id desc').id or 0 + client_last_notification_id = server_last_notification_id - 1 + + def subscribe_side_effect(_, last): + self.assertEqual(last, client_last_notification_id) + subscribe_done_event.set() + + with patch.object(Websocket, 'subscribe', side_effect=subscribe_side_effect): + websocket = self.websocket_connect() + websocket.send(json.dumps({ + 'event_name': 'subscribe', + 'data': {'channels': ['my_channel'], 'last': client_last_notification_id} + })) + subscribe_done_event.wait()