Files
odoo_source/addons/bus/controllers/websocket.py
T
tsm-odoo 0feaa4f650 [FIX] bus, website_livechat: prevent psycopg2 errors during tests
`psycopg2` errors sometimes happen during tests. This is caused by
race conditions between several `cr.execute()`/`cr.fetch*`.

This is even worse with websockets that are long lived connections
that sometimes open a cursor in the middle of the test.

Before websockets, longpolling was just disabled during tests. This
commits disable websockets as well in order to prevent those errors
as much as possible.

Some live chat tours were updated in [1] after the websockets intro-
duction due to some race conditions. This was reverted as the fix
relied on a hack on websockets that are now unavailable.

fixes runbot-25357,24809

[1]: https://github.com/odoo/odoo/pull/103521

closes odoo/odoo#143647

X-original-commit: e3b07a301036fcce76ebbbd856b507326b88160a
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Signed-off-by: Matthieu Stockbauer (tsm) <tsm@odoo.com>
2023-11-30 05:17:40 +00:00

65 lines
2.9 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
from odoo.http import Controller, request, route, SessionExpiredException
from odoo.addons.base.models.assetsbundle import AssetsBundle
from ..models.bus import channel_with_db
from ..websocket import WebsocketConnectionHandler
class WebsocketController(Controller):
@route('/websocket', type="http", auth="public", cors='*', websocket=True)
def websocket(self):
"""
Handle the websocket handshake, upgrade the connection if
successfull.
"""
return WebsocketConnectionHandler.open_connection(request)
@route('/websocket/health', type='http', auth='none', save_session=False)
def health(self):
data = json.dumps({
'status': 'pass',
})
headers = [('Content-Type', 'application/json'),
('Cache-Control', 'no-store')]
return request.make_response(data, headers)
@route('/websocket/peek_notifications', type='json', auth='public', cors='*')
def peek_notifications(self, channels, last, is_first_poll=False):
if not all(isinstance(c, str) for c in channels):
raise ValueError("bus.Bus only string channels are allowed.")
if is_first_poll:
# Used to detect when the current session is expired.
request.session['is_websocket_session'] = True
elif 'is_websocket_session' not in request.session:
raise SessionExpiredException()
channels = list(set(
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}
@route('/websocket/update_bus_presence', type='json', auth='public', cors='*')
def update_bus_presence(self, inactivity_period, im_status_ids_by_model):
if 'is_websocket_session' not in request.session:
raise SessionExpiredException()
request.env['ir.websocket']._update_bus_presence(int(inactivity_period), im_status_ids_by_model)
return {}
@route('/bus/websocket_worker_bundle', type='http', auth='public', cors='*')
def get_websocket_worker_bundle(self, v=None): # pylint: disable=unused-argument
"""
:param str v: Version of the worker, frontend only argument used to
prevent new worker versions to be loaded from the browser cache.
"""
bundle_name = 'bus.websocket_worker_assets'
bundle = request.env["ir.qweb"]._get_asset_bundle(bundle_name, debug_assets="assets" in request.session.debug)
stream = request.env['ir.binary']._get_stream_from(bundle.js())
return stream.get_response()