Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ravendb/exceptions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ArgumentOutOfRangeException(Exception):
pass


class DatabaseDoesNotExistException(Exception):
class DatabaseDoesNotExistException(RuntimeError):
pass


Expand Down
13 changes: 11 additions & 2 deletions ravendb/http/request_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import logging
import os
from concurrent.futures import ThreadPoolExecutor, Future, FIRST_COMPLETED, wait, ALL_COMPLETED
from urllib.parse import unquote
import uuid
from json import JSONDecodeError
from threading import Timer, Semaphore, Lock
Expand Down Expand Up @@ -564,7 +565,7 @@ def execute(
):
db_missing_header = response.headers.get("Database-Missing", None)
if db_missing_header is not None:
raise DatabaseDoesNotExistException(db_missing_header)
raise DatabaseDoesNotExistException(unquote(db_missing_header))
self._throw_failed_to_contact_all_nodes(command, request)
return # we either handled this already in the unsuccessful response or we are throwing
self._on_succeed_request_invoke(self._database_name, url, response, request, attempt_num)
Expand Down Expand Up @@ -1114,8 +1115,16 @@ def _handle_unsuccessful_response(
return True
else:
command.on_response_failure(response)
db_missing_header = response.headers.get("Database-Missing", None)
if db_missing_header is not None:
raise DatabaseDoesNotExistException(unquote(db_missing_header))
try: # todo: exception dispatcher
raise RuntimeError(json.loads(response.text).get("Message", "Missing message"))
data = json.loads(response.text)
err_type = data.get("Type", "")
message = data.get("Message", "Missing message")
if err_type.endswith("DatabaseDoesNotExistException"):
raise DatabaseDoesNotExistException(message)
raise RuntimeError(message)
except JSONDecodeError as e:
raise RuntimeError(f"Failed to parse response: {response.text}") from e

Expand Down
46 changes: 46 additions & 0 deletions ravendb/tests/issue_tests/test_RDBC_1042.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
RDBC-1042: Database-Missing header is URL-decoded; raises DatabaseDoesNotExistException.
C# reference: RavenDB-24435
"""

import unittest

from ravendb.exceptions.exceptions import DatabaseDoesNotExistException
from ravendb.tests.test_base import TestBase


class TestUnicodeDatabaseExceptionUnit(unittest.TestCase):
"""Unit tests that do not require a live server."""

def test_database_does_not_exist_exception_importable(self):
exc = DatabaseDoesNotExistException("my database")
self.assertIn("my database", str(exc))

def test_header_unquote_applied(self):
# Simulate the server-set header value (percent-encoded) being decoded
from urllib.parse import unquote

encoded = "my%20db%20with%20spaces"
exc = DatabaseDoesNotExistException(unquote(encoded))
self.assertIn("my db with spaces", str(exc))
self.assertNotIn("%20", str(exc))


class TestUnicodeDatabaseExceptionIntegration(TestBase):
def test_missing_unicode_db_raises_correct_exception(self):
from ravendb.documents.store.definition import DocumentStore

base_urls = self.get_document_store().urls
with DocumentStore(base_urls, "my db with spaces") as store:
store.initialize()
with self.assertRaises(DatabaseDoesNotExistException) as ctx:
with store.open_session() as session:
session.load("docs/1")
# Exception message should be decoded (no %20)
msg = str(ctx.exception)
self.assertNotIn("%20", msg)


if __name__ == "__main__":
unittest.main()