unrhodecode/rhodecode/lib/svn_txn_utils.py
2025-12-03 13:39:14 +01:00

136 lines
4.2 KiB
Python

# Copyright (C) 2010-2024 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
import redis
from ..lib import rc_cache
from ..lib.ext_json import json
log = logging.getLogger(__name__)
redis_client = None
class RedisTxnClient:
def __init__(self, url):
self.url = url
self._create_client(url)
def _create_client(self, url):
connection_pool = redis.ConnectionPool.from_url(url)
self.writer_client = redis.StrictRedis(connection_pool=connection_pool)
self.reader_client = self.writer_client
def set(self, key, value, expire=24 * 60000):
self.writer_client.set(key, value, ex=expire)
def get(self, key):
return self.reader_client.get(key)
def delete(self, key):
self.writer_client.delete(key)
def get_redis_client(url=""):
global redis_client
if redis_client is not None:
return redis_client
if not url:
from rhodecode import CONFIG
url = CONFIG["vcs.svn.redis_conn"]
redis_client = RedisTxnClient(url)
return redis_client
def extract_svn_txn_id(data: bytes):
"""
Helper method for extraction of svn txn_id from submitted XML data during
POST operations
"""
import re
from lxml import etree
try:
root = etree.fromstring(data)
pat = re.compile(r"/txn/(?P<txn_id>.*)")
for el in root:
if el.tag == "{DAV:}source":
for sub_el in el:
if sub_el.tag == "{DAV:}href":
match = pat.search(sub_el.text)
if match:
svn_tx_id = match.groupdict()["txn_id"]
return svn_tx_id
except Exception:
log.exception("Failed to extract txn_id")
def get_txn_id_data_key(repo_path, svn_txn_id):
log.debug("svn-txn-id: %s, obtaining data path", svn_txn_id)
repo_key = rc_cache.utils.compute_key_from_params(repo_path)
final_key = f"{repo_key}.{svn_txn_id}.svn_txn_id"
log.debug("computed final key: %s", final_key)
return final_key
def store_txn_id_data(repo_path, svn_txn_id, data_dict):
log.debug("svn-txn-id: %s, storing data", svn_txn_id)
if not svn_txn_id:
log.warning("Cannot store txn_id because it is empty")
return
redis_conn = get_redis_client()
store_key = get_txn_id_data_key(repo_path, svn_txn_id)
store_data = json.dumps(data_dict)
redis_conn.set(store_key, store_data)
def get_txn_id_from_store(repo_path, svn_txn_id, rm_on_read=False):
"""
Reads txn_id from store and if present returns the data for callback manager
"""
log.debug("svn-txn-id: %s, retrieving data", svn_txn_id)
redis_conn = get_redis_client()
store_key = get_txn_id_data_key(repo_path, svn_txn_id)
data = {}
redis_conn.get(store_key)
raw_data = "not-set"
try:
raw_data = redis_conn.get(store_key)
if raw_data:
data = json.loads(raw_data)
except Exception:
log.exception("Failed to get txn_id metadata: redis_conn=%s raw_data=%s", redis_conn, raw_data)
if not data:
log.exception("Failed to get txn_id metadata: redis_conn=%s raw_data=%s", redis_conn, raw_data)
raise ValueError(f"Failed to get txn_id metadata, from store: {store_key}")
if rm_on_read:
log.debug("Cleaning up txn_id at %s", store_key)
redis_conn.delete(store_key)
return data