From 40b7c878ce5a3678667385ac552abcfe7883bc44 Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Fri, 5 Sep 2025 13:32:56 +0200 Subject: [PATCH 1/6] RDBC-935 Add Connection String Operations --- .../GetConnectionStringOperation.py | 73 ++++++++++++++ .../PutConnectionStringOperation.py | 97 +++++++++++++++++++ .../RemoveConnectionStringOperation.py | 54 +++++++++++ .../operations/connection_string/__init__.py | 0 .../documents/operations/etl/configuration.py | 4 +- .../documents/operations/etl/olap/__init__.py | 4 +- .../documents/operations/etl/sql/__init__.py | 4 +- .../serverwide/server_operation_executor.py | 8 +- .../test_connection_string.py | 72 ++++++++++++++ 9 files changed, 306 insertions(+), 10 deletions(-) create mode 100644 ravendb/documents/operations/connection_string/GetConnectionStringOperation.py create mode 100644 ravendb/documents/operations/connection_string/PutConnectionStringOperation.py create mode 100644 ravendb/documents/operations/connection_string/RemoveConnectionStringOperation.py create mode 100644 ravendb/documents/operations/connection_string/__init__.py create mode 100644 ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py diff --git a/ravendb/documents/operations/connection_string/GetConnectionStringOperation.py b/ravendb/documents/operations/connection_string/GetConnectionStringOperation.py new file mode 100644 index 00000000..be9c57e3 --- /dev/null +++ b/ravendb/documents/operations/connection_string/GetConnectionStringOperation.py @@ -0,0 +1,73 @@ +import json +from typing import Dict, Optional + +import requests + +from ravendb import RavenCommand, ServerNode +from ravendb.documents.operations.definitions import MaintenanceOperation +from ravendb.documents.operations.etl.configuration import RavenConnectionString +from ravendb.documents.operations.etl.olap import OlapConnectionString +from ravendb.documents.operations.etl.sql import SqlConnectionString +from ravendb.serverwide.server_operation_executor import ConnectionStringType + + +class GetConnectionStringsResult: + def __init__( + self, + raven_connection_strings: Dict[str, RavenConnectionString] = None, + sql_connection_strings: Dict[str, SqlConnectionString] = None, + olap_connection_strings: Dict[str, OlapConnectionString] = None, + ): + self.raven_connection_strings = raven_connection_strings + self.sql_connection_strings = sql_connection_strings + self.olap_connection_strings = olap_connection_strings + + def to_json(self) -> Dict: + return { + "RavenConnectionStrings": self._raven_connection_strings, + "SqlConnectionStrings": self._sql_connection_strings, + "OlapConnectionStrings": self._olap_connection_strings, + } + + @classmethod + def from_json(cls, json_dict: Dict) -> "GetConnectionStringsResult": + return cls( + raven_connection_strings=json_dict["RavenConnectionStrings"], + sql_connection_strings=json_dict["SqlConnectionStrings"], + olap_connection_strings=json_dict["OlapConnectionStrings"], + ) + + +class GetConnectionStringsOperation(MaintenanceOperation[GetConnectionStringsResult]): + def __init__(self, connection_string_name: str = None, connection_string_type: ConnectionStringType = None): + self._connection_string_name = connection_string_name + self._type = connection_string_type + + def get_command(self, conventions: "DocumentConventions") -> "RavenCommand[GetConnectionStringsResult]": + return self.GetConnectionStringsCommand(self._connection_string_name, self._type) + + class GetConnectionStringsCommand(RavenCommand[GetConnectionStringsResult]): + def __init__(self, connection_string_name: str = None, connection_string_type: ConnectionStringType = None): + super().__init__(GetConnectionStringsResult) + self._connection_string_name = connection_string_name + self._type = connection_string_type + + def is_read_request(self) -> bool: + return True + + def create_request(self, node: ServerNode) -> requests.Request: + url = f"{node.url}/databases/{node.database}/admin/connection-strings" + + if self._connection_string_name: + url += f"?connectionStringName={self._connection_string_name}&type={self._type.value}" + + request = requests.Request("GET") + request.url = url + + return request + + def set_response(self, response: Optional[str], from_cache: bool) -> None: + if response is None: + self._throw_invalid_response() + + self.result = GetConnectionStringsResult.from_json(json.loads(response)) diff --git a/ravendb/documents/operations/connection_string/PutConnectionStringOperation.py b/ravendb/documents/operations/connection_string/PutConnectionStringOperation.py new file mode 100644 index 00000000..a8589006 --- /dev/null +++ b/ravendb/documents/operations/connection_string/PutConnectionStringOperation.py @@ -0,0 +1,97 @@ +import json +from typing import Dict + +import requests + +from ravendb import ConnectionString, RavenCommand, ServerNode, RaftCommand +from ravendb.documents.conventions import DocumentConventions +from ravendb.documents.operations.definitions import MaintenanceOperation +from ravendb.documents.operations.etl.configuration import RavenConnectionString +from ravendb.documents.operations.etl.olap import OlapConnectionString +from ravendb.documents.operations.etl.sql import SqlConnectionString +from ravendb.serverwide.server_operation_executor import ConnectionStringType +from ravendb.util.util import RaftIdGenerator + + +class PutConnectionStringResult: + def __init__(self, raft_command_index: int = None): + self.raft_command_index = raft_command_index + + def to_json(self) -> Dict: + return {"RaftCommandIndex": self.raft_command_index} + + @classmethod + def from_json(cls, json_dict: Dict) -> "PutConnectionStringResult": + return cls(json_dict["RaftCommandIndex"]) + + +class PutConnectionStringOperation(MaintenanceOperation[PutConnectionStringResult]): + def __init__(self, connection_string: ConnectionString = None): + self._connection_string = connection_string + + def get_command(self, conventions: "DocumentConventions") -> "RavenCommand[PutConnectionStringResult]": + return self.PutConnectionStringCommand(conventions, self._connection_string) + + class PutConnectionStringCommand(RavenCommand[PutConnectionStringResult], RaftCommand): + def __init__( + self, document_conventions: DocumentConventions = None, connection_string: ConnectionString = None + ): + super().__init__(PutConnectionStringResult) + + if connection_string is None: + raise ValueError("Connection string cannot be None") + + self._document_conventions = document_conventions + self._connection_string = connection_string + + def _to_data(self) -> Dict: + if isinstance(self._connection_string, RavenConnectionString): + return { + "Name": self._connection_string.name, + "Database": self._connection_string.database, + "TopologyDiscoveryUrls": self._connection_string.topology_discovery_urls, + "Type": ConnectionStringType.RAVEN, + } + + if isinstance(self._connection_string, SqlConnectionString): + return { + "Name": self._connection_string.name, + "ConnectionString": self._connection_string.connection_string, + "FactoryName": self._connection_string.factory_name, + "Type": ConnectionStringType.SQL, + } + + if isinstance(self._connection_string, OlapConnectionString): + return { + "Name": self._connection_string.name, + "LocalSettings": self._connection_string.local_settings, + "S3Settings": self._connection_string.s3_settings, + "AzureSettings": self._connection_string.azure_settings, + "GlacierSettings": self._connection_string.glacier_settings, + "GoogleCloudSettings": self._connection_string.google_cloud_settings, + "FtpSettings": self._connection_string.ftp_settings, + "Type": ConnectionStringType.OLAP, + } + + return None + + def is_read_request(self) -> bool: + return False + + def create_request(self, node: ServerNode) -> requests.Request: + url = f"{node.url}/databases/{node.database}/admin/connection-strings" + + request = requests.Request("PUT") + request.url = url + request.data = self._to_data() + + return request + + def set_response(self, response: str, from_cache: bool) -> None: + if response is None: + self._throw_invalid_response() + + self.result = PutConnectionStringResult.from_json(json.loads(response)) + + def get_raft_unique_request_id(self) -> str: + return RaftIdGenerator.new_id() diff --git a/ravendb/documents/operations/connection_string/RemoveConnectionStringOperation.py b/ravendb/documents/operations/connection_string/RemoveConnectionStringOperation.py new file mode 100644 index 00000000..dec9b0f8 --- /dev/null +++ b/ravendb/documents/operations/connection_string/RemoveConnectionStringOperation.py @@ -0,0 +1,54 @@ +import json +import urllib +from typing import Dict + +import requests + +from ravendb import RavenCommand, RaftCommand, ConnectionString, ServerNode +from ravendb.documents.operations.definitions import MaintenanceOperation +from ravendb.util.util import RaftIdGenerator + + +class RemoveConnectionStringResult: + def __init__(self, raft_command_index: int = None): + self.raft_command_index = raft_command_index + + def to_json(self) -> Dict: + return {"RaftCommandIndex": self.raft_command_index} + + @classmethod + def from_json(cls, json_dict: Dict) -> "PutConnectionStringResult": + return cls(json_dict["RaftCommandIndex"]) + + +class RemoveConnectionStringOperation(MaintenanceOperation[RemoveConnectionStringResult]): + def __init__(self, connection_string: ConnectionString = None): + self._connection_string = connection_string + + def get_command(self, conventions: "DocumentConventions") -> "RavenCommand[_T]": + return self.RemoveConnectionStringCommand(self._connection_string) + + class RemoveConnectionStringCommand(RavenCommand[RemoveConnectionStringResult], RaftCommand): + def __init__(self, connection_string: ConnectionString = None): + super().__init__(RemoveConnectionStringResult) + self._connection_string = connection_string + + def is_read_request(self) -> bool: + return False + + def create_request(self, node: ServerNode) -> requests.Request: + url = f"{node.url}/databases/{node.database}/admin/connection-strings?connectionString={urllib.parse.quote(self._connection_string.name)}&type={self._connection_string.get_type}" + + request = requests.Request("DELETE") + request.url = url + + return request + + def set_response(self, response: str, from_cache: bool) -> None: + if response is None: + self._throw_invalid_response() + + self.result = RemoveConnectionStringResult.from_json(json.loads(response)) + + def get_raft_unique_request_id(self) -> str: + return RaftIdGenerator.new_id() diff --git a/ravendb/documents/operations/connection_string/__init__.py b/ravendb/documents/operations/connection_string/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ravendb/documents/operations/etl/configuration.py b/ravendb/documents/operations/etl/configuration.py index 29670980..d7f75f5c 100644 --- a/ravendb/documents/operations/etl/configuration.py +++ b/ravendb/documents/operations/etl/configuration.py @@ -1,7 +1,7 @@ from typing import Optional, Generic, TypeVar, List from ravendb.documents.operations.connection_strings import ConnectionString -import ravendb.serverwide +import ravendb.serverwide.server_operation_executor _T = TypeVar("_T") @@ -14,7 +14,7 @@ def __init__(self, name: str, database: Optional[str] = None, topology_discovery @property def get_type(self): - return ravendb.serverwide.ConnectionStringType.RAVEN + return ravendb.serverwide.server_operation_executor.ConnectionStringType.RAVEN.value # todo: implement diff --git a/ravendb/documents/operations/etl/olap/__init__.py b/ravendb/documents/operations/etl/olap/__init__.py index 828ed5ea..e2e13f19 100644 --- a/ravendb/documents/operations/etl/olap/__init__.py +++ b/ravendb/documents/operations/etl/olap/__init__.py @@ -9,7 +9,7 @@ FtpSettings, ) from ravendb.documents.operations.connection_strings import ConnectionString -import ravendb.serverwide +import ravendb.serverwide.server_operation_executor from ravendb.documents.operations.etl.configuration import EtlConfiguration @@ -34,7 +34,7 @@ def __init__( @property def get_type(self): - return ravendb.serverwide.ConnectionStringType.OLAP + return ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP.value # todo: implement diff --git a/ravendb/documents/operations/etl/sql/__init__.py b/ravendb/documents/operations/etl/sql/__init__.py index a4137059..0bd9355e 100644 --- a/ravendb/documents/operations/etl/sql/__init__.py +++ b/ravendb/documents/operations/etl/sql/__init__.py @@ -1,7 +1,7 @@ from typing import Optional from ravendb.documents.operations.connection_strings import ConnectionString -import ravendb.serverwide +import ravendb.serverwide.server_operation_executor from ravendb.documents.operations.etl.configuration import EtlConfiguration @@ -13,7 +13,7 @@ def __init__(self, name: str, connection_string: Optional[str] = None, factory_n @property def get_type(self): - return ravendb.serverwide.ConnectionStringType.SQL + return ravendb.serverwide.server_operation_executor.ConnectionStringType.SQL.value # todo: implement diff --git a/ravendb/serverwide/server_operation_executor.py b/ravendb/serverwide/server_operation_executor.py index 20f00b01..c4365fbb 100644 --- a/ravendb/serverwide/server_operation_executor.py +++ b/ravendb/serverwide/server_operation_executor.py @@ -21,10 +21,10 @@ class ConnectionStringType(enum.Enum): - NONE = "NONE" - RAVEN = "RAVEN" - SQL = "SQL" - OLAP = "OLAP" + NONE = "None" + RAVEN = "Raven" + SQL = "Sql" + OLAP = "Olap" class ServerOperationExecutor: diff --git a/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py new file mode 100644 index 00000000..49af0efd --- /dev/null +++ b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py @@ -0,0 +1,72 @@ +from ravendb import FtpSettings +from ravendb.documents.operations.connection_string.GetConnectionStringOperation import GetConnectionStringsOperation +from ravendb.documents.operations.connection_string.PutConnectionStringOperation import ( + PutConnectionStringOperation, + PutConnectionStringResult, +) +from ravendb.documents.operations.connection_string.RemoveConnectionStringOperation import ( + RemoveConnectionStringOperation, +) +from ravendb.documents.operations.etl.configuration import RavenConnectionString +from ravendb.documents.operations.etl.olap import OlapConnectionString +from ravendb.documents.operations.etl.sql import SqlConnectionString +from ravendb.serverwide.server_operation_executor import ConnectionStringType +from ravendb.tests.test_base import TestBase + + +class TestConnectionString(TestBase): + def setUp(self): + super().setUp() + + def test_can_create_get_and_delete_connection_strings(self): + raven_connection_string_1 = RavenConnectionString("r1", "db1", ["http://localhost:8080"]) + sql_connection_string_1 = SqlConnectionString("s1", "test", "MySql.Data.MySqlClient") + olap_connection_string_1 = OlapConnectionString("o1", ftp_settings=FtpSettings(url="localhost:9090")) + + put_result: PutConnectionStringResult = self.store.maintenance.send( + PutConnectionStringOperation(raven_connection_string_1) + ) + self.assertGreater(put_result.raft_command_index, 0) + + put_result: PutConnectionStringResult = self.store.maintenance.send( + PutConnectionStringOperation(sql_connection_string_1) + ) + self.assertGreater(put_result.raft_command_index, 0) + + put_result: PutConnectionStringResult = self.store.maintenance.send( + PutConnectionStringOperation(olap_connection_string_1) + ) + self.assertGreater(put_result.raft_command_index, 0) + + connection_strings = self.store.maintenance.send(GetConnectionStringsOperation()) + + self.assertIn("r1", connection_strings.raven_connection_strings) + self.assertEqual(1, len(connection_strings.raven_connection_strings)) + + self.assertIn("s1", connection_strings.sql_connection_strings) + self.assertEqual(1, len(connection_strings.sql_connection_strings)) + + self.assertIn("o1", connection_strings.olap_connection_strings) + self.assertEqual(1, len(connection_strings.olap_connection_strings)) + + raven_only = self.store.maintenance.send(GetConnectionStringsOperation("r1", ConnectionStringType.RAVEN)) + self.assertIn("r1", raven_only.raven_connection_strings) + self.assertEqual(1, len(raven_only.raven_connection_strings)) + self.assertIsNone(raven_only.sql_connection_strings) + + sql_only = self.store.maintenance.send(GetConnectionStringsOperation("s1", ConnectionStringType.SQL)) + self.assertIn("s1", sql_only.sql_connection_strings) + self.assertEqual(1, len(sql_only.sql_connection_strings)) + self.assertIsNone(sql_only.raven_connection_strings) + + olap_only = self.store.maintenance.send(GetConnectionStringsOperation("o1", ConnectionStringType.OLAP)) + self.assertIn("o1", olap_only.olap_connection_strings) + self.assertEqual(1, len(olap_only.olap_connection_strings)) + self.assertIsNone(olap_only.raven_connection_strings) + + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(sql_connection_string_1)) + self.assertGreater(remove_result.raft_command_index, 0) + + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("s1", ConnectionStringType.SQL)) + self.assertIsNone(after_delete.raven_connection_strings) + self.assertIsNone(after_delete.sql_connection_strings) From 714cfb4f4ebf935bb168d5ee88defe257fe1439b Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Mon, 8 Sep 2025 12:05:36 +0200 Subject: [PATCH 2/6] RDBC-935 Use self.store database and urls in test --- .../operations_tests/test_connection_string.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py index 49af0efd..13ccc65f 100644 --- a/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py +++ b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py @@ -19,9 +19,9 @@ def setUp(self): super().setUp() def test_can_create_get_and_delete_connection_strings(self): - raven_connection_string_1 = RavenConnectionString("r1", "db1", ["http://localhost:8080"]) + raven_connection_string_1 = RavenConnectionString("r1", self.store.database, self.store.urls) sql_connection_string_1 = SqlConnectionString("s1", "test", "MySql.Data.MySqlClient") - olap_connection_string_1 = OlapConnectionString("o1", ftp_settings=FtpSettings(url="localhost:9090")) + olap_connection_string_1 = OlapConnectionString("o1", ftp_settings=FtpSettings(url=self.store.urls[0])) put_result: PutConnectionStringResult = self.store.maintenance.send( PutConnectionStringOperation(raven_connection_string_1) From d8f43a56a4cc0b513dd55b6ca1386e97fce334bf Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 9 Sep 2025 15:53:50 +0200 Subject: [PATCH 3/6] RDBC-935 Add missing connection strings --- .../operations/ai/abstract_ai_settings.py | 16 ++++ .../operations/ai/ai_connection_string.py | 62 +++++++++++++++ .../operations/ai/azure_open_ai_settings.py | 31 ++++++++ .../operations/ai/embedded_settings.py | 15 ++++ .../operations/ai/google_settings.py | 36 +++++++++ .../operations/ai/hugging_face_settings.py | 27 +++++++ .../operations/ai/mistral_ai_settings.py | 27 +++++++ .../operations/ai/ollama_settings.py | 28 +++++++ .../operations/ai/open_ai_base_settings.py | 13 ++++ .../operations/ai/open_ai_settings.py | 34 ++++++++ ....py => get_connection_string_operation.py} | 0 ....py => put_connection_string_operation.py} | 33 +------- ... => remove_connection_string_operation.py} | 0 .../operations/connection_strings.py | 5 ++ .../documents/operations/etl/configuration.py | 8 ++ .../operations/etl/elastic_search/__init__.py | 47 +++++++++++ .../documents/operations/etl/olap/__init__.py | 12 +++ .../operations/etl/queue/__init__.py | 50 ++++++++++++ .../queue/amazon_sqs_connection_settings.py | 58 ++++++++++++++ ...azure_queue_storage_connection_settings.py | 77 +++++++++++++++++++ .../etl/queue/kafka_connection_settings.py | 28 +++++++ .../queue/rabbit_mq_connection_settings.py | 13 ++++ .../operations/etl/snowflake/__init__.py | 21 +++++ .../documents/operations/etl/sql/__init__.py | 8 ++ .../serverwide/server_operation_executor.py | 4 + .../test_connection_string.py | 6 +- 26 files changed, 624 insertions(+), 35 deletions(-) create mode 100644 ravendb/documents/operations/ai/abstract_ai_settings.py create mode 100644 ravendb/documents/operations/ai/ai_connection_string.py create mode 100644 ravendb/documents/operations/ai/azure_open_ai_settings.py create mode 100644 ravendb/documents/operations/ai/embedded_settings.py create mode 100644 ravendb/documents/operations/ai/google_settings.py create mode 100644 ravendb/documents/operations/ai/hugging_face_settings.py create mode 100644 ravendb/documents/operations/ai/mistral_ai_settings.py create mode 100644 ravendb/documents/operations/ai/ollama_settings.py create mode 100644 ravendb/documents/operations/ai/open_ai_base_settings.py create mode 100644 ravendb/documents/operations/ai/open_ai_settings.py rename ravendb/documents/operations/connection_string/{GetConnectionStringOperation.py => get_connection_string_operation.py} (100%) rename ravendb/documents/operations/connection_string/{PutConnectionStringOperation.py => put_connection_string_operation.py} (62%) rename ravendb/documents/operations/connection_string/{RemoveConnectionStringOperation.py => remove_connection_string_operation.py} (100%) create mode 100644 ravendb/documents/operations/etl/elastic_search/__init__.py create mode 100644 ravendb/documents/operations/etl/queue/__init__.py create mode 100644 ravendb/documents/operations/etl/queue/amazon_sqs_connection_settings.py create mode 100644 ravendb/documents/operations/etl/queue/azure_queue_storage_connection_settings.py create mode 100644 ravendb/documents/operations/etl/queue/kafka_connection_settings.py create mode 100644 ravendb/documents/operations/etl/queue/rabbit_mq_connection_settings.py create mode 100644 ravendb/documents/operations/etl/snowflake/__init__.py diff --git a/ravendb/documents/operations/ai/abstract_ai_settings.py b/ravendb/documents/operations/ai/abstract_ai_settings.py new file mode 100644 index 00000000..ddb61efa --- /dev/null +++ b/ravendb/documents/operations/ai/abstract_ai_settings.py @@ -0,0 +1,16 @@ +from abc import ABC, abstractmethod +from typing import Dict, Any + + +class AbstractAiSettings(ABC): + def __init__(self): + self.embeddings_max_concurrent_batches = None + + @classmethod + @abstractmethod + def from_json(cls, json_dict: Dict[str, Any]) -> Any: + pass + + @abstractmethod + def to_json(self) -> Dict[str, Any]: + pass \ No newline at end of file diff --git a/ravendb/documents/operations/ai/ai_connection_string.py b/ravendb/documents/operations/ai/ai_connection_string.py new file mode 100644 index 00000000..952466d3 --- /dev/null +++ b/ravendb/documents/operations/ai/ai_connection_string.py @@ -0,0 +1,62 @@ +import enum +from typing import Optional, Dict, Any + +import ravendb.serverwide.server_operation_executor +from ravendb.documents.operations.ai.azure_open_ai_settings import AzureOpenAiSettings +from ravendb.documents.operations.ai.embedded_settings import EmbeddedSettings +from ravendb.documents.operations.ai.google_settings import GoogleSettings +from ravendb.documents.operations.ai.hugging_face_settings import HuggingFaceSettings +from ravendb.documents.operations.ai.mistral_ai_settings import MistralAiSettings +from ravendb.documents.operations.ai.ollama_settings import OllamaSettings +from ravendb.documents.operations.ai.open_ai_settings import OpenAiSettings +from ravendb.documents.operations.connection_strings import ConnectionString + + +class AiModelType(enum.Enum): + TEXT_EMBEDDINGS = "TextEmbeddings", + CHAT = "Chat" + + +class AiConnectionString(ConnectionString): # todo kuba + def __init__( + self, + name: str, + identifier: str, + openai_settings: Optional[OpenAiSettings] = None, + azure_openai_settings: Optional[AzureOpenAiSettings] = None, + ollama_settings: Optional[OllamaSettings] = None, + embedded_settings: Optional[EmbeddedSettings] = None, + google_settings: Optional[GoogleSettings] = None, + huggingface_settings: Optional[HuggingFaceSettings] = None, + mistral_ai_settings: Optional[MistralAiSettings] = None, + model_type: AiModelType = None + ): + super().__init__(name) + self.identifier = identifier + self.openai_settings = openai_settings + self.azure_openai_settings = azure_openai_settings + self.ollama_settings = ollama_settings + self.embedded_settings = embedded_settings + self.google_settings = google_settings + self.huggingface_settings = huggingface_settings + self.mistral_ai_settings = mistral_ai_settings + self.model_type = model_type + + @property + def get_type(self): + return ravendb.serverwide.server_operation_executor.ConnectionStringType.AI.value + + def to_json(self) -> Dict[str, Any]: + return { + "Name": self.name, + "Identifier": self.identifier, + "OpenaiSettings": self.openai_settings, + "AzureOpenaiSettings": self.azure_openai_settings, + "ollama_settings": self.ollama_settings, + "EmbeddedSettings": self.embedded_settings, + "GoogleSettings": self.google_settings, + "HuggingfaceSettings": self.huggingface_settings, + "MistralAiSettings": self.mistral_ai_settings, + "ModelType": self.model_type, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP, + } diff --git a/ravendb/documents/operations/ai/azure_open_ai_settings.py b/ravendb/documents/operations/ai/azure_open_ai_settings.py new file mode 100644 index 00000000..9de6b672 --- /dev/null +++ b/ravendb/documents/operations/ai/azure_open_ai_settings.py @@ -0,0 +1,31 @@ +from typing import Dict, Any + +from ravendb.documents.operations.ai.open_ai_base_settings import OpenAiBaseSettings + + +class AzureOpenAiSettings(OpenAiBaseSettings): + def __init__(self, api_key: str = None, endpoint: str = None, model: str = None, deployment_name: str = None, dimensions: int = None, temperature: float = None): + super().__init__(api_key, endpoint, model, dimensions, temperature) + self.deployment_name = deployment_name + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "AzureOpenAiSettings": + return cls( + api_key=json_dict["ApiKey"], + endpoint=json_dict["Endpoint"], + model=json_dict["Model"], + dimensions=json_dict["Dimensions"], + temperature=json_dict["Temperature"], + deployment_name=json_dict["DeploymentName"], + ) + + def to_json(self) -> Dict[str, Any]: + return { + "ApiKey": self.api_key, + "Endpoint": self.endpoint, + "Model": self.model, + "Dimensions": self.dimensions, + "Temperature": self.temperature, + "DeploymentName": self.deployment_name, + "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, + } \ No newline at end of file diff --git a/ravendb/documents/operations/ai/embedded_settings.py b/ravendb/documents/operations/ai/embedded_settings.py new file mode 100644 index 00000000..10f115cc --- /dev/null +++ b/ravendb/documents/operations/ai/embedded_settings.py @@ -0,0 +1,15 @@ +from typing import Dict, Any + +from ravendb.documents.operations.ai.abstract_ai_settings import AbstractAiSettings + + +class EmbeddedSettings(AbstractAiSettings): + def __init__(self): + super().__init__() + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "EmbeddedSettings": + return cls() + + def to_json(self) -> Dict[str, Any]: + return {"EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches} \ No newline at end of file diff --git a/ravendb/documents/operations/ai/google_settings.py b/ravendb/documents/operations/ai/google_settings.py new file mode 100644 index 00000000..31068134 --- /dev/null +++ b/ravendb/documents/operations/ai/google_settings.py @@ -0,0 +1,36 @@ +from enum import Enum +from typing import Dict, Any + +from ravendb.documents.operations.ai.abstract_ai_settings import AbstractAiSettings + + +class GoogleAiVersion(Enum): + V1 = "V1", + V1_Beta = "V1_Beta", + + +class GoogleSettings(AbstractAiSettings): + def __init__(self, model: str = None, api_key: str = None, ai_version: GoogleAiVersion = None, dimensions: int = None): + super().__init__() + self.model = model + self.api_key = api_key + self.ai_version = ai_version + self.dimensions = dimensions + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "GoogleSettings": + return cls( + model=json_dict["Model"], + api_key=json_dict["ApiKey"], + ai_version=GoogleAiVersion(json_dict["AiVersion"]), + dimensions=json_dict["Dimensions"], + ) + + def to_json(self) -> Dict[str, Any]: + return { + "Model": self.model, + "ApiKey": self.api_key, + "AiVersion": self.ai_version.value, + "Dimensions": self.dimensions, + "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, + } diff --git a/ravendb/documents/operations/ai/hugging_face_settings.py b/ravendb/documents/operations/ai/hugging_face_settings.py new file mode 100644 index 00000000..297ff7f6 --- /dev/null +++ b/ravendb/documents/operations/ai/hugging_face_settings.py @@ -0,0 +1,27 @@ +from typing import Dict, Any + +from ravendb.documents.operations.ai.abstract_ai_settings import AbstractAiSettings + + +class HuggingFaceSettings(AbstractAiSettings): + def __init__(self, api_key: str = None, model: str = None, endpoint: str = None): + super().__init__() + self.api_key = api_key + self.model = model + self.endpoint = endpoint + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "HuggingFaceSettings": + return cls( + api_key=json_dict["ApiKey"], + model=json_dict["Model"], + endpoint=json_dict["Endpoint"], + ) + + def to_json(self) -> Dict[str, Any]: + return { + "ApiKey": self.api_key, + "Model": self.model, + "Endpoint": self.endpoint, + "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, + } \ No newline at end of file diff --git a/ravendb/documents/operations/ai/mistral_ai_settings.py b/ravendb/documents/operations/ai/mistral_ai_settings.py new file mode 100644 index 00000000..216b254d --- /dev/null +++ b/ravendb/documents/operations/ai/mistral_ai_settings.py @@ -0,0 +1,27 @@ +from typing import Dict, Any + +from ravendb.documents.operations.ai.abstract_ai_settings import AbstractAiSettings + + +class MistralAiSettings(AbstractAiSettings): + def __init__(self, api_key: str = None, model: str = None, endpoint: str = None): + super().__init__() + self.api_key = api_key + self.model = model + self.endpoint = endpoint + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "MistralAiSettings": + return cls( + api_key=json_dict["ApiKey"], + model=json_dict["Model"], + endpoint=json_dict["Endpoint"], + ) + + def to_json(self) -> Dict[str, Any]: + return { + "ApiKey": self.api_key, + "Model": self.model, + "Endpoint": self.endpoint, + "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, + } \ No newline at end of file diff --git a/ravendb/documents/operations/ai/ollama_settings.py b/ravendb/documents/operations/ai/ollama_settings.py new file mode 100644 index 00000000..fdee2797 --- /dev/null +++ b/ravendb/documents/operations/ai/ollama_settings.py @@ -0,0 +1,28 @@ +from typing import Dict, Any + +from ravendb.documents.operations.ai.abstract_ai_settings import AbstractAiSettings + + +class OllamaSettings(AbstractAiSettings): + def __init__(self, uri: str = None, model: str = None): + super().__init__() + self.uri = uri + self.model = model + self.think: bool = None + self.temperature: float = None + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "OllamaSettings": + return cls( + uri=json_dict["Uri"], + model=json_dict["Model"], + ) + + def to_json(self) -> Dict[str, Any]: + return { + "Uri": self.uri, + "Model": self.model, + "Think": self.think, + "Temperature": self.temperature, + "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, + } \ No newline at end of file diff --git a/ravendb/documents/operations/ai/open_ai_base_settings.py b/ravendb/documents/operations/ai/open_ai_base_settings.py new file mode 100644 index 00000000..59112e06 --- /dev/null +++ b/ravendb/documents/operations/ai/open_ai_base_settings.py @@ -0,0 +1,13 @@ +from abc import ABC + +from ravendb.documents.operations.ai.abstract_ai_settings import AbstractAiSettings + + +class OpenAiBaseSettings(AbstractAiSettings, ABC): + def __init__(self, api_key: str = None, endpoint: str = None, model: str = None, dimensions: int = None, temperature: float = None): + super().__init__() + self.api_key = api_key + self.endpoint = endpoint + self.model = model + self.dimensions = dimensions + self.temperature = temperature diff --git a/ravendb/documents/operations/ai/open_ai_settings.py b/ravendb/documents/operations/ai/open_ai_settings.py new file mode 100644 index 00000000..4c338b98 --- /dev/null +++ b/ravendb/documents/operations/ai/open_ai_settings.py @@ -0,0 +1,34 @@ +from typing import Dict, Any + +from ravendb.documents.operations.ai.open_ai_base_settings import OpenAiBaseSettings + + +class OpenAiSettings(OpenAiBaseSettings): + def __init__(self, api_key: str = None, endpoint: str = None, model: str = None, organization_id: str = None, project_id: str = None, dimensions: int = None, temperature: float = None): + super().__init__(api_key, endpoint, model, dimensions, temperature) + self.organization_id = organization_id + self.project_id = project_id + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "OpenAiSettings": + return cls( + api_key=json_dict["ApiKey"], + endpoint=json_dict["Endpoint"], + model=json_dict["Model"], + dimensions=json_dict["Dimensions"], + temperature=json_dict["Temperature"], + organization_id=json_dict["OrganizationId"], + project_id=json_dict["ProjectId"], + ) + + def to_json(self) -> Dict[str, Any]: + return { + "ApiKey": self.api_key, + "Endpoint": self.endpoint, + "Model": self.model, + "Dimensions": self.dimensions, + "Temperature": self.temperature, + "OrganizationId": self.organization_id, + "ProjectId": self.project_id, + "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, + } \ No newline at end of file diff --git a/ravendb/documents/operations/connection_string/GetConnectionStringOperation.py b/ravendb/documents/operations/connection_string/get_connection_string_operation.py similarity index 100% rename from ravendb/documents/operations/connection_string/GetConnectionStringOperation.py rename to ravendb/documents/operations/connection_string/get_connection_string_operation.py diff --git a/ravendb/documents/operations/connection_string/PutConnectionStringOperation.py b/ravendb/documents/operations/connection_string/put_connection_string_operation.py similarity index 62% rename from ravendb/documents/operations/connection_string/PutConnectionStringOperation.py rename to ravendb/documents/operations/connection_string/put_connection_string_operation.py index a8589006..fff42bf0 100644 --- a/ravendb/documents/operations/connection_string/PutConnectionStringOperation.py +++ b/ravendb/documents/operations/connection_string/put_connection_string_operation.py @@ -44,37 +44,6 @@ def __init__( self._document_conventions = document_conventions self._connection_string = connection_string - def _to_data(self) -> Dict: - if isinstance(self._connection_string, RavenConnectionString): - return { - "Name": self._connection_string.name, - "Database": self._connection_string.database, - "TopologyDiscoveryUrls": self._connection_string.topology_discovery_urls, - "Type": ConnectionStringType.RAVEN, - } - - if isinstance(self._connection_string, SqlConnectionString): - return { - "Name": self._connection_string.name, - "ConnectionString": self._connection_string.connection_string, - "FactoryName": self._connection_string.factory_name, - "Type": ConnectionStringType.SQL, - } - - if isinstance(self._connection_string, OlapConnectionString): - return { - "Name": self._connection_string.name, - "LocalSettings": self._connection_string.local_settings, - "S3Settings": self._connection_string.s3_settings, - "AzureSettings": self._connection_string.azure_settings, - "GlacierSettings": self._connection_string.glacier_settings, - "GoogleCloudSettings": self._connection_string.google_cloud_settings, - "FtpSettings": self._connection_string.ftp_settings, - "Type": ConnectionStringType.OLAP, - } - - return None - def is_read_request(self) -> bool: return False @@ -83,7 +52,7 @@ def create_request(self, node: ServerNode) -> requests.Request: request = requests.Request("PUT") request.url = url - request.data = self._to_data() + request.data = self._connection_string.to_json() return request diff --git a/ravendb/documents/operations/connection_string/RemoveConnectionStringOperation.py b/ravendb/documents/operations/connection_string/remove_connection_string_operation.py similarity index 100% rename from ravendb/documents/operations/connection_string/RemoveConnectionStringOperation.py rename to ravendb/documents/operations/connection_string/remove_connection_string_operation.py diff --git a/ravendb/documents/operations/connection_strings.py b/ravendb/documents/operations/connection_strings.py index 48f429d4..4fbb1b41 100644 --- a/ravendb/documents/operations/connection_strings.py +++ b/ravendb/documents/operations/connection_strings.py @@ -1,4 +1,5 @@ from abc import abstractmethod +from typing import Dict, Any class ConnectionString: @@ -8,3 +9,7 @@ def __init__(self, name: str): @abstractmethod def get_type(self): pass + + @abstractmethod + def to_json(self) -> Dict[str, Any]: + pass diff --git a/ravendb/documents/operations/etl/configuration.py b/ravendb/documents/operations/etl/configuration.py index d7f75f5c..2eb00833 100644 --- a/ravendb/documents/operations/etl/configuration.py +++ b/ravendb/documents/operations/etl/configuration.py @@ -16,6 +16,14 @@ def __init__(self, name: str, database: Optional[str] = None, topology_discovery def get_type(self): return ravendb.serverwide.server_operation_executor.ConnectionStringType.RAVEN.value + def to_json(self): + return { + "Name": self.name, + "Database": self.database, + "TopologyDiscoveryUrls": self.topology_discovery_urls, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.RAVEN, + } + # todo: implement class EtlConfiguration(ConnectionString, Generic[_T]): diff --git a/ravendb/documents/operations/etl/elastic_search/__init__.py b/ravendb/documents/operations/etl/elastic_search/__init__.py new file mode 100644 index 00000000..f6d7f9ec --- /dev/null +++ b/ravendb/documents/operations/etl/elastic_search/__init__.py @@ -0,0 +1,47 @@ +from typing import List + +from ravendb.documents.operations.connection_strings import ConnectionString +import ravendb.serverwide.server_operation_executor + + +class ApiKeyAuthentication: + def __init__(self, api_key_id: str = None, api_key: str = None, encoded_api_key: str = None): + self.api_key_id = api_key_id + self.api_key = api_key + self.encoded_api_key = encoded_api_key + + +class BasicAuthentication: + def __init__(self, username: str = None, password: str = None): + self.username = username + self.password = password + + +class CertificateAuthentication: + def __init__(self, certificates_base64: List[str] = None): + self.certificates_base64 = certificates_base64 + + +class Authentication: + def __init__(self, api_key: ApiKeyAuthentication = None, basic: BasicAuthentication = None, certificate: CertificateAuthentication = None): + self.api_key = api_key + self.basic = basic + self.certificate = certificate + + +class ElasticSearchConnectionString(ConnectionString): + def __init__(self, name: str, nodes: List[str] = None, authentication: Authentication = None): + self.name = name + self.nodes = nodes + self.authentication = authentication + + def get_type(self): + return ravendb.serverwide.server_operation_executor.ConnectionStringType.ELASTIC_SEARCH.value + + def to_json(self): + return { + "Name": self.name, + "Nodes": self.nodes, + "Authentication": self.authentication, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.ELASTIC_SEARCH, + } \ No newline at end of file diff --git a/ravendb/documents/operations/etl/olap/__init__.py b/ravendb/documents/operations/etl/olap/__init__.py index e2e13f19..4372adc7 100644 --- a/ravendb/documents/operations/etl/olap/__init__.py +++ b/ravendb/documents/operations/etl/olap/__init__.py @@ -36,6 +36,18 @@ def __init__( def get_type(self): return ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP.value + def to_json(self): + return { + "Name": self.name, + "LocalSettings": self.local_settings, + "S3Settings": self.s3_settings, + "AzureSettings": self.azure_settings, + "GlacierSettings": self.glacier_settings, + "GoogleCloudSettings": self.google_cloud_settings, + "FtpSettings": self.ftp_settings, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP, + } + # todo: implement class OlapEtlConfiguration(EtlConfiguration[OlapConnectionString]): diff --git a/ravendb/documents/operations/etl/queue/__init__.py b/ravendb/documents/operations/etl/queue/__init__.py new file mode 100644 index 00000000..d0b5c908 --- /dev/null +++ b/ravendb/documents/operations/etl/queue/__init__.py @@ -0,0 +1,50 @@ +from enum import Enum + +from ravendb.documents.operations.connection_strings import ConnectionString +import ravendb.serverwide.server_operation_executor +from ravendb.documents.operations.etl.queue.amazon_sqs_connection_settings import AmazonSqsConnectionSettings +from ravendb.documents.operations.etl.queue.azure_queue_storage_connection_settings import \ + AzureQueueStorageConnectionSettings +from ravendb.documents.operations.etl.queue.kafka_connection_settings import KafkaConnectionSettings +from ravendb.documents.operations.etl.queue.rabbit_mq_connection_settings import RabbitMqConnectionSettings + + +class QueueBrokerType(Enum): + NONE = "None" + KAFKA = "Kafka" + RABBIT_MQ = "RabbitMq" + AZURE_QUEUE_STORAGE = "AzureQueueStorage" + AMAZON_SQS = "AmazonSqs" + + +class QueueConnectionString(ConnectionString): + def __init__( + self, + name: str = None, + broker_type: QueueBrokerType = None, + kafka_settings: KafkaConnectionSettings = None, + rabbit_mq_settings: RabbitMqConnectionSettings = None, + azure_queue_storage_settings: AzureQueueStorageConnectionSettings = None, + amazon_sqs_settings: AmazonSqsConnectionSettings = None, + ): + super().__init__() + self.name = name + self.broker_type = broker_type + self.kafka_settings = kafka_settings + self.rabbit_mq_settings = rabbit_mq_settings + self.azure_queue_storage_settings = azure_queue_storage_settings + self.amazon_sqs_settings = amazon_sqs_settings + + def get_type(self): + return ravendb.serverwide.server_operation_executor.ConnectionStringType.QUEUE.value + + def to_json(self): + return { + "Name": self.name, + "BrokerType": self.broker_type, + "KafkaConnectionSettings": self.kafka_settings, + "RabbitMqConnectionSettings": self.rabbit_mq_settings, + "AzureQueueStorageConnectionSettings": self.azure_queue_storage_settings, + "AmazonSqsConnectionSettings": self.amazon_sqs_settings, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.QUEUE, + } diff --git a/ravendb/documents/operations/etl/queue/amazon_sqs_connection_settings.py b/ravendb/documents/operations/etl/queue/amazon_sqs_connection_settings.py new file mode 100644 index 00000000..6a848517 --- /dev/null +++ b/ravendb/documents/operations/etl/queue/amazon_sqs_connection_settings.py @@ -0,0 +1,58 @@ +from typing import Optional, Dict, Any + + +class AmazonSqsCredentials: + def __init__( + self, + access_key: Optional[str] = None, + secret_key: Optional[str] = None, + region_name: Optional[str] = None, + ): + self.access_key = access_key + self.secret_key = secret_key + self.region_name = region_name + + def to_json(self) -> Dict[str, Any]: + return { + "AccessKey": self.access_key, + "SecretKey": self.secret_key, + "RegionName": self.region_name, + } + + @classmethod + def from_json(cls, json_dict: Optional[Dict[str, Any]]): + return cls( + access_key=json_dict.get("AccessKey"), + secret_key=json_dict.get("SecretKey"), + region_name=json_dict.get("RegionName"), + ) + + +class AmazonSqsConnectionSettings: + EMULATOR_URL_ENVIRONMENT_VARIABLE = "RAVEN_AMAZON_SQS_EMULATOR_URL" + + def __init__( + self, + basic: Optional[AmazonSqsCredentials] = None, + passwordless: Optional[bool] = None, + use_emulator: Optional[bool] = None, + ): + self.basic = basic + self.passwordless = passwordless + self.use_emulator = use_emulator + + def to_json(self) -> Dict[str, Any]: + return { + "Basic": self.basic.to_json() if self.basic else None, + "Passwordless": self.passwordless, + "UseEmulator": self.use_emulator, + } + + @classmethod + def from_json(cls, json_dict: Optional[Dict[str, Any]]): + basic_dict = json_dict.get("Basic") + return cls( + basic=AmazonSqsCredentials.from_json(basic_dict) if basic_dict else None, + passwordless=json_dict.get("Passwordless"), + use_emulator=json_dict.get("UseEmulator"), + ) diff --git a/ravendb/documents/operations/etl/queue/azure_queue_storage_connection_settings.py b/ravendb/documents/operations/etl/queue/azure_queue_storage_connection_settings.py new file mode 100644 index 00000000..d3f1c480 --- /dev/null +++ b/ravendb/documents/operations/etl/queue/azure_queue_storage_connection_settings.py @@ -0,0 +1,77 @@ +from typing import Optional, Dict, Any + + +class EntraId: + def __init__( + self, + storage_account_name: Optional[str] = None, + tenant_id: Optional[str] = None, + client_id: Optional[str] = None, + client_secret: Optional[str] = None, + ): + self.storage_account_name = storage_account_name + self.tenant_id = tenant_id + self.client_id = client_id + self.client_secret = client_secret + + def to_json(self) -> Dict[str, Any]: + return { + "StorageAccountName": self.storage_account_name, + "TenantId": self.tenant_id, + "ClientId": self.client_id, + "ClientSecret": self.client_secret, + } + + @classmethod + def from_json(cls, json_dict: Optional[Dict[str, Any]]): + return cls( + storage_account_name=json_dict.get("StorageAccountName"), + tenant_id=json_dict.get("TenantId"), + client_id=json_dict.get("ClientId"), + client_secret=json_dict.get("ClientSecret"), + ) + + +class Passwordless: + def __init__(self, storage_account_name: Optional[str] = None): + self.storage_account_name = storage_account_name + + def to_json(self) -> Dict[str, Any]: + return { + "StorageAccountName": self.storage_account_name, + } + + @classmethod + def from_json(cls, json_dict: Optional[Dict[str, Any]]): + return cls( + storage_account_name=json_dict.get("StorageAccountName"), + ) + + +class AzureQueueStorageConnectionSettings: + def __init__( + self, + entra_id: Optional[EntraId] = None, + connection_string: Optional[str] = None, + passwordless: Optional[Passwordless] = None, + ): + self.entra_id = entra_id + self.connection_string = connection_string + self.passwordless = passwordless + + def to_json(self) -> Dict[str, Any]: + return { + "EntraId": self.entra_id.to_json() if self.entra_id else None, + "ConnectionString": self.connection_string, + "Passwordless": self.passwordless.to_json() if self.passwordless else None, + } + + @classmethod + def from_json(cls, json_dict: Optional[Dict[str, Any]]): + entra_id_dict = json_dict.get("EntraId") + passwordless_dict = json_dict.get("Passwordless") + return cls( + entra_id=EntraId.from_json(entra_id_dict) if entra_id_dict else None, + connection_string=json_dict.get("ConnectionString"), + passwordless=Passwordless.from_json(passwordless_dict) if passwordless_dict else None, + ) diff --git a/ravendb/documents/operations/etl/queue/kafka_connection_settings.py b/ravendb/documents/operations/etl/queue/kafka_connection_settings.py new file mode 100644 index 00000000..493a3bec --- /dev/null +++ b/ravendb/documents/operations/etl/queue/kafka_connection_settings.py @@ -0,0 +1,28 @@ +from typing import Dict, Any + + +class KafkaConnectionSettings: + def __init__( + self, + bootstrap_servers: str = None, + connection_options: Dict[str, str] = None, + use_raven_certificate: bool = None, + ): + self.bootstrap_servers = bootstrap_servers + self.connection_options = connection_options + self.use_raven_certificate = use_raven_certificate + + def to_json(self): + return { + "BootstrapServers": self.bootstrap_servers, + "ConnectionOptions": self.connection_options, + "UseRavenCertificate": self.use_raven_certificate, + } + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]): + return cls( + bootstrap_servers=json_dict.get("BootstrapServers"), + connection_options=json_dict.get("ConnectionOptions"), + use_raven_certificate=json_dict.get("UseRavenCertificate"), + ) diff --git a/ravendb/documents/operations/etl/queue/rabbit_mq_connection_settings.py b/ravendb/documents/operations/etl/queue/rabbit_mq_connection_settings.py new file mode 100644 index 00000000..2ed7c08a --- /dev/null +++ b/ravendb/documents/operations/etl/queue/rabbit_mq_connection_settings.py @@ -0,0 +1,13 @@ +from typing import Dict, Any + + +class RabbitMqConnectionSettings: + def __init__(self, connection_string: str = None): + self.connection_string = connection_string + + def to_json(self): + return {"ConnectionString": self.connection_string} + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]): + return cls(connection_string=json_dict.get("ConnectionString")) diff --git a/ravendb/documents/operations/etl/snowflake/__init__.py b/ravendb/documents/operations/etl/snowflake/__init__.py new file mode 100644 index 00000000..3b538928 --- /dev/null +++ b/ravendb/documents/operations/etl/snowflake/__init__.py @@ -0,0 +1,21 @@ +from typing import Optional + +from ravendb.documents.operations.connection_strings import ConnectionString +import ravendb.serverwide.server_operation_executor + + +class SnowflakeConnectionString(ConnectionString): + def __init__(self, name: str, connection_string: Optional[str] = None): + super().__init__(name) + self.connection_string = connection_string + + @property + def get_type(self): + return ravendb.serverwide.server_operation_executor.ConnectionStringType.SNOWFLAKE.value + + def to_json(self): + return { + "Name": self.name, + "ConnectionString": self.connection_string, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.SNOWFLAKE, + } \ No newline at end of file diff --git a/ravendb/documents/operations/etl/sql/__init__.py b/ravendb/documents/operations/etl/sql/__init__.py index 0bd9355e..a0535984 100644 --- a/ravendb/documents/operations/etl/sql/__init__.py +++ b/ravendb/documents/operations/etl/sql/__init__.py @@ -15,6 +15,14 @@ def __init__(self, name: str, connection_string: Optional[str] = None, factory_n def get_type(self): return ravendb.serverwide.server_operation_executor.ConnectionStringType.SQL.value + def to_json(self): + return { + "Name": self.name, + "ConnectionString": self.connection_string, + "FactoryName": self.factory_name, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.SQL, + } + # todo: implement class SqlEtlConfiguration(EtlConfiguration[SqlConnectionString]): diff --git a/ravendb/serverwide/server_operation_executor.py b/ravendb/serverwide/server_operation_executor.py index c4365fbb..7c034866 100644 --- a/ravendb/serverwide/server_operation_executor.py +++ b/ravendb/serverwide/server_operation_executor.py @@ -25,6 +25,10 @@ class ConnectionStringType(enum.Enum): RAVEN = "Raven" SQL = "Sql" OLAP = "Olap" + AI = "Ai" + ELASTIC_SEARCH = "ElasticSearch" + QUEUE = "Queue" + SNOWFLAKE = "Snowflake" class ServerOperationExecutor: diff --git a/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py index 13ccc65f..7bfaa9f6 100644 --- a/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py +++ b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py @@ -1,10 +1,10 @@ from ravendb import FtpSettings -from ravendb.documents.operations.connection_string.GetConnectionStringOperation import GetConnectionStringsOperation -from ravendb.documents.operations.connection_string.PutConnectionStringOperation import ( +from ravendb.documents.operations.connection_string.get_connection_string_operation import GetConnectionStringsOperation +from ravendb.documents.operations.connection_string.put_connection_string_operation import ( PutConnectionStringOperation, PutConnectionStringResult, ) -from ravendb.documents.operations.connection_string.RemoveConnectionStringOperation import ( +from ravendb.documents.operations.connection_string.remove_connection_string_operation import ( RemoveConnectionStringOperation, ) from ravendb.documents.operations.etl.configuration import RavenConnectionString From 4ade5011ac65ce04fd754ed317966714ab41587e Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 9 Sep 2025 16:28:02 +0200 Subject: [PATCH 4/6] RDBC-935 to_json/from_json --- .../operations/ai/ai_connection_string.py | 15 ++++ .../get_connection_string_operation.py | 26 ++++++- .../operations/connection_strings.py | 5 ++ .../documents/operations/etl/configuration.py | 9 ++- .../operations/etl/elastic_search/__init__.py | 73 ++++++++++++++++++- .../documents/operations/etl/olap/__init__.py | 12 +++ .../operations/etl/queue/__init__.py | 11 +++ .../operations/etl/snowflake/__init__.py | 9 ++- .../documents/operations/etl/sql/__init__.py | 10 ++- 9 files changed, 162 insertions(+), 8 deletions(-) diff --git a/ravendb/documents/operations/ai/ai_connection_string.py b/ravendb/documents/operations/ai/ai_connection_string.py index 952466d3..2d955777 100644 --- a/ravendb/documents/operations/ai/ai_connection_string.py +++ b/ravendb/documents/operations/ai/ai_connection_string.py @@ -60,3 +60,18 @@ def to_json(self) -> Dict[str, Any]: "ModelType": self.model_type, "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP, } + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "AiConnectionString": + return cls( + name=json_dict["Name"], + identifier=json_dict["Identifier"], + openai_settings=OpenAiSettings.from_json(json_dict["OpenaiSettings"]), + azure_openai_settings=AzureOpenAiSettings.from_json(json_dict["AzureOpenaiSettings"]), + ollama_settings=OllamaSettings.from_json(json_dict["ollama_settings"]), + embedded_settings=EmbeddedSettings.from_json(json_dict["EmbeddedSettings"]), + google_settings=GoogleSettings.from_json(json_dict["GoogleSettings"]), + huggingface_settings=HuggingFaceSettings.from_json(json_dict["HuggingfaceSettings"]), + mistral_ai_settings=MistralAiSettings.from_json(json_dict["MistralAiSettings"]), + model_type=AiModelType(json_dict["ModelType"]) if json_dict["ModelType"] else None, + ) diff --git a/ravendb/documents/operations/connection_string/get_connection_string_operation.py b/ravendb/documents/operations/connection_string/get_connection_string_operation.py index be9c57e3..04109036 100644 --- a/ravendb/documents/operations/connection_string/get_connection_string_operation.py +++ b/ravendb/documents/operations/connection_string/get_connection_string_operation.py @@ -4,9 +4,13 @@ import requests from ravendb import RavenCommand, ServerNode +from ravendb.documents.operations.ai.ai_connection_string import AiConnectionString from ravendb.documents.operations.definitions import MaintenanceOperation from ravendb.documents.operations.etl.configuration import RavenConnectionString +from ravendb.documents.operations.etl.elastic_search import ElasticSearchConnectionString from ravendb.documents.operations.etl.olap import OlapConnectionString +from ravendb.documents.operations.etl.queue import QueueConnectionString +from ravendb.documents.operations.etl.snowflake import SnowflakeConnectionString from ravendb.documents.operations.etl.sql import SqlConnectionString from ravendb.serverwide.server_operation_executor import ConnectionStringType @@ -17,16 +21,28 @@ def __init__( raven_connection_strings: Dict[str, RavenConnectionString] = None, sql_connection_strings: Dict[str, SqlConnectionString] = None, olap_connection_strings: Dict[str, OlapConnectionString] = None, + ai_connection_strings: Dict[str, AiConnectionString] = None, + elastic_search_connection_strings: Dict[str, ElasticSearchConnectionString] = None, + queue_connection_strings: Dict[str, QueueConnectionString] = None, + snowflake_connection_strings: Dict[str, SnowflakeConnectionString] = None, ): self.raven_connection_strings = raven_connection_strings self.sql_connection_strings = sql_connection_strings self.olap_connection_strings = olap_connection_strings + self.ai_connection_strings = ai_connection_strings + self.elastic_search_connection_strings = elastic_search_connection_strings + self.queue_connection_strings = queue_connection_strings + self.snowflake_connection_strings = snowflake_connection_strings def to_json(self) -> Dict: return { - "RavenConnectionStrings": self._raven_connection_strings, - "SqlConnectionStrings": self._sql_connection_strings, - "OlapConnectionStrings": self._olap_connection_strings, + "RavenConnectionStrings": [x.to_json() for x in self.raven_connection_strings.values()], + "SqlConnectionStrings": [x.to_json() for x in self.sql_connection_strings.values()], + "OlapConnectionStrings": [x.to_json() for x in self.olap_connection_strings.values()], + "AiConnectionStrings": [x.to_json() for x in self.ai_connection_strings.values()], + "ElasticSearchConnectionStrings": [x.to_json() for x in self.elastic_search_connection_strings.values()], + "QueueConnectionStrings": [x.to_json() for x in self.queue_connection_strings.values()], + "SnowflakeConnectionStrings": [x.to_json() for x in self.snowflake_connection_strings.values()], } @classmethod @@ -35,6 +51,10 @@ def from_json(cls, json_dict: Dict) -> "GetConnectionStringsResult": raven_connection_strings=json_dict["RavenConnectionStrings"], sql_connection_strings=json_dict["SqlConnectionStrings"], olap_connection_strings=json_dict["OlapConnectionStrings"], + ai_connection_strings=json_dict["AiConnectionStrings"], + elastic_search_connection_strings=json_dict["ElasticSearchConnectionStrings"], + queue_connection_strings=json_dict["QueueConnectionStrings"], + snowflake_connection_strings=json_dict["SnowflakeConnectionStrings"], ) diff --git a/ravendb/documents/operations/connection_strings.py b/ravendb/documents/operations/connection_strings.py index 4fbb1b41..be5c9a44 100644 --- a/ravendb/documents/operations/connection_strings.py +++ b/ravendb/documents/operations/connection_strings.py @@ -13,3 +13,8 @@ def get_type(self): @abstractmethod def to_json(self) -> Dict[str, Any]: pass + + @classmethod + @abstractmethod + def from_json(cls, json_dict: Dict[str, Any]) -> Any: + pass diff --git a/ravendb/documents/operations/etl/configuration.py b/ravendb/documents/operations/etl/configuration.py index 2eb00833..a1c36db9 100644 --- a/ravendb/documents/operations/etl/configuration.py +++ b/ravendb/documents/operations/etl/configuration.py @@ -1,4 +1,4 @@ -from typing import Optional, Generic, TypeVar, List +from typing import Optional, Generic, TypeVar, List, Dict from ravendb.documents.operations.connection_strings import ConnectionString import ravendb.serverwide.server_operation_executor @@ -24,6 +24,13 @@ def to_json(self): "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.RAVEN, } + @classmethod + def from_json(cls, json_dict: Dict) -> "RavenConnectionString": + return cls( + name=json_dict["Name"], + database=json_dict["Database"], + topology_discovery_urls=json_dict["TopologyDiscoveryUrls"], + ) # todo: implement class EtlConfiguration(ConnectionString, Generic[_T]): diff --git a/ravendb/documents/operations/etl/elastic_search/__init__.py b/ravendb/documents/operations/etl/elastic_search/__init__.py index f6d7f9ec..7f6e315c 100644 --- a/ravendb/documents/operations/etl/elastic_search/__init__.py +++ b/ravendb/documents/operations/etl/elastic_search/__init__.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Dict, Any from ravendb.documents.operations.connection_strings import ConnectionString import ravendb.serverwide.server_operation_executor @@ -10,17 +10,57 @@ def __init__(self, api_key_id: str = None, api_key: str = None, encoded_api_key: self.api_key = api_key self.encoded_api_key = encoded_api_key + def to_json(self) -> Dict[str, Any]: + return { + "ApiKeyId": self.api_key_id, + "ApiKey": self.api_key, + "EncodedApiKey": self.encoded_api_key, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "ApiKeyAuthentication": + return cls( + api_key_id=data.get("ApiKeyId") or data.get("api_key_id"), + api_key=data.get("ApiKey") or data.get("api_key"), + encoded_api_key=data.get("EncodedApiKey") or data.get("encoded_api_key"), + ) + class BasicAuthentication: def __init__(self, username: str = None, password: str = None): self.username = username self.password = password + def to_json(self) -> Dict[str, Any]: + return { + "Username": self.username, + "Password": self.password, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "BasicAuthentication": + return cls( + username=data.get("Username") or data.get("username"), + password=data.get("Password") or data.get("password"), + ) + + class CertificateAuthentication: def __init__(self, certificates_base64: List[str] = None): self.certificates_base64 = certificates_base64 + def to_json(self) -> Dict[str, Any]: + return { + "CertificatesBase64": list(self.certificates_base64) if self.certificates_base64 is not None else None, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "CertificateAuthentication": + certs = data.get("CertificatesBase64") or data.get("certificates_base64") + return cls(certificates_base64=list(certs) if certs is not None else None) + + class Authentication: def __init__(self, api_key: ApiKeyAuthentication = None, basic: BasicAuthentication = None, certificate: CertificateAuthentication = None): @@ -28,9 +68,30 @@ def __init__(self, api_key: ApiKeyAuthentication = None, basic: BasicAuthenticat self.basic = basic self.certificate = certificate + def to_json(self) -> Dict[str, Any]: + return { + "ApiKey": self.api_key.to_json() if self.api_key is not None else None, + "Basic": self.basic.to_json() if self.basic is not None else None, + "Certificate": self.certificate.to_json() if self.certificate is not None else None, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "Authentication": + api_key_data = data.get("ApiKey") + basic_data = data.get("Basic") + certificate_data = data.get("Certificate") + + return cls( + api_key=ApiKeyAuthentication.from_json(api_key_data) if api_key_data else None, + basic=BasicAuthentication.from_json(basic_data) if basic_data else None, + certificate=CertificateAuthentication.from_json(certificate_data) if certificate_data else None, + ) + + class ElasticSearchConnectionString(ConnectionString): def __init__(self, name: str, nodes: List[str] = None, authentication: Authentication = None): + super().__init__() self.name = name self.nodes = nodes self.authentication = authentication @@ -44,4 +105,12 @@ def to_json(self): "Nodes": self.nodes, "Authentication": self.authentication, "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.ELASTIC_SEARCH, - } \ No newline at end of file + } + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> Any: + return cls( + name=json_dict["Name"], + nodes=json_dict["Nodes"], + authentication=Authentication.from_json(json_dict["Authentication"]), + ) \ No newline at end of file diff --git a/ravendb/documents/operations/etl/olap/__init__.py b/ravendb/documents/operations/etl/olap/__init__.py index 4372adc7..b6fbec40 100644 --- a/ravendb/documents/operations/etl/olap/__init__.py +++ b/ravendb/documents/operations/etl/olap/__init__.py @@ -48,6 +48,18 @@ def to_json(self): "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP, } + @classmethod + def from_json(cls, json_dict: dict) -> "OlapConnectionString": + return cls( + name=json_dict["Name"], + local_settings=LocalSettings.from_json(json_dict["LocalSettings"]), + s3_settings=S3Settings.from_json(json_dict["S3Settings"]), + azure_settings=AzureSettings.from_json(json_dict["AzureSettings"]), + glacier_settings=GlacierSettings.from_json(json_dict["GlacierSettings"]), + google_cloud_settings=GoogleCloudSettings.from_json(json_dict["GoogleCloudSettings"]), + ftp_settings=FtpSettings.from_json(json_dict["FtpSettings"]), + ) + # todo: implement class OlapEtlConfiguration(EtlConfiguration[OlapConnectionString]): diff --git a/ravendb/documents/operations/etl/queue/__init__.py b/ravendb/documents/operations/etl/queue/__init__.py index d0b5c908..611c1e65 100644 --- a/ravendb/documents/operations/etl/queue/__init__.py +++ b/ravendb/documents/operations/etl/queue/__init__.py @@ -48,3 +48,14 @@ def to_json(self): "AmazonSqsConnectionSettings": self.amazon_sqs_settings, "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.QUEUE, } + + @classmethod + def from_json(cls, json_dict: dict) -> "QueueConnectionString": + return cls( + name=json_dict["Name"], + broker_type=QueueBrokerType(json_dict["BrokerType"]), + kafka_settings=KafkaConnectionSettings.from_json(json_dict["KafkaConnectionSettings"]), + rabbit_mq_settings=RabbitMqConnectionSettings.from_json(json_dict["RabbitMqConnectionSettings"]), + azure_queue_storage_settings=AzureQueueStorageConnectionSettings.from_json(json_dict["AzureQueueStorageConnectionSettings"]), + amazon_sqs_settings=AmazonSqsConnectionSettings.from_json(json_dict["AmazonSqsConnectionSettings"]) + ) diff --git a/ravendb/documents/operations/etl/snowflake/__init__.py b/ravendb/documents/operations/etl/snowflake/__init__.py index 3b538928..ba4ceae9 100644 --- a/ravendb/documents/operations/etl/snowflake/__init__.py +++ b/ravendb/documents/operations/etl/snowflake/__init__.py @@ -18,4 +18,11 @@ def to_json(self): "Name": self.name, "ConnectionString": self.connection_string, "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.SNOWFLAKE, - } \ No newline at end of file + } + + @classmethod + def from_json(cls, json_dict: dict) -> "SnowflakeConnectionString": + return cls( + name=json_dict["Name"], + connection_string=json_dict["ConnectionString"], + ) \ No newline at end of file diff --git a/ravendb/documents/operations/etl/sql/__init__.py b/ravendb/documents/operations/etl/sql/__init__.py index a0535984..bcd5ec39 100644 --- a/ravendb/documents/operations/etl/sql/__init__.py +++ b/ravendb/documents/operations/etl/sql/__init__.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Dict, Any from ravendb.documents.operations.connection_strings import ConnectionString import ravendb.serverwide.server_operation_executor @@ -23,6 +23,14 @@ def to_json(self): "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.SQL, } + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> "SqlConnectionString": + return cls( + name=json_dict["Name"], + connection_string=json_dict["ConnectionString"], + factory_name=json_dict["FactoryName"], + ) + # todo: implement class SqlEtlConfiguration(EtlConfiguration[SqlConnectionString]): From 5323ddb455f8be94faa3fe0ef7b4b16683395e7c Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 9 Sep 2025 16:29:17 +0200 Subject: [PATCH 5/6] RDBC-935 black --- .../documents/operations/ai/abstract_ai_settings.py | 2 +- .../documents/operations/ai/ai_connection_string.py | 6 +++--- .../operations/ai/azure_open_ai_settings.py | 12 ++++++++++-- .../documents/operations/ai/embedded_settings.py | 2 +- ravendb/documents/operations/ai/google_settings.py | 8 +++++--- .../operations/ai/hugging_face_settings.py | 2 +- .../documents/operations/ai/mistral_ai_settings.py | 2 +- ravendb/documents/operations/ai/ollama_settings.py | 2 +- .../operations/ai/open_ai_base_settings.py | 9 ++++++++- ravendb/documents/operations/ai/open_ai_settings.py | 13 +++++++++++-- ravendb/documents/operations/etl/configuration.py | 1 + .../operations/etl/elastic_search/__init__.py | 12 +++++++----- ravendb/documents/operations/etl/queue/__init__.py | 11 +++++++---- .../documents/operations/etl/snowflake/__init__.py | 2 +- 14 files changed, 58 insertions(+), 26 deletions(-) diff --git a/ravendb/documents/operations/ai/abstract_ai_settings.py b/ravendb/documents/operations/ai/abstract_ai_settings.py index ddb61efa..b078f897 100644 --- a/ravendb/documents/operations/ai/abstract_ai_settings.py +++ b/ravendb/documents/operations/ai/abstract_ai_settings.py @@ -13,4 +13,4 @@ def from_json(cls, json_dict: Dict[str, Any]) -> Any: @abstractmethod def to_json(self) -> Dict[str, Any]: - pass \ No newline at end of file + pass diff --git a/ravendb/documents/operations/ai/ai_connection_string.py b/ravendb/documents/operations/ai/ai_connection_string.py index 2d955777..a742f226 100644 --- a/ravendb/documents/operations/ai/ai_connection_string.py +++ b/ravendb/documents/operations/ai/ai_connection_string.py @@ -13,11 +13,11 @@ class AiModelType(enum.Enum): - TEXT_EMBEDDINGS = "TextEmbeddings", + TEXT_EMBEDDINGS = ("TextEmbeddings",) CHAT = "Chat" -class AiConnectionString(ConnectionString): # todo kuba +class AiConnectionString(ConnectionString): # todo kuba def __init__( self, name: str, @@ -29,7 +29,7 @@ def __init__( google_settings: Optional[GoogleSettings] = None, huggingface_settings: Optional[HuggingFaceSettings] = None, mistral_ai_settings: Optional[MistralAiSettings] = None, - model_type: AiModelType = None + model_type: AiModelType = None, ): super().__init__(name) self.identifier = identifier diff --git a/ravendb/documents/operations/ai/azure_open_ai_settings.py b/ravendb/documents/operations/ai/azure_open_ai_settings.py index 9de6b672..baeedcda 100644 --- a/ravendb/documents/operations/ai/azure_open_ai_settings.py +++ b/ravendb/documents/operations/ai/azure_open_ai_settings.py @@ -4,7 +4,15 @@ class AzureOpenAiSettings(OpenAiBaseSettings): - def __init__(self, api_key: str = None, endpoint: str = None, model: str = None, deployment_name: str = None, dimensions: int = None, temperature: float = None): + def __init__( + self, + api_key: str = None, + endpoint: str = None, + model: str = None, + deployment_name: str = None, + dimensions: int = None, + temperature: float = None, + ): super().__init__(api_key, endpoint, model, dimensions, temperature) self.deployment_name = deployment_name @@ -28,4 +36,4 @@ def to_json(self) -> Dict[str, Any]: "Temperature": self.temperature, "DeploymentName": self.deployment_name, "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, - } \ No newline at end of file + } diff --git a/ravendb/documents/operations/ai/embedded_settings.py b/ravendb/documents/operations/ai/embedded_settings.py index 10f115cc..416dc63f 100644 --- a/ravendb/documents/operations/ai/embedded_settings.py +++ b/ravendb/documents/operations/ai/embedded_settings.py @@ -12,4 +12,4 @@ def from_json(cls, json_dict: Dict[str, Any]) -> "EmbeddedSettings": return cls() def to_json(self) -> Dict[str, Any]: - return {"EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches} \ No newline at end of file + return {"EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches} diff --git a/ravendb/documents/operations/ai/google_settings.py b/ravendb/documents/operations/ai/google_settings.py index 31068134..01711713 100644 --- a/ravendb/documents/operations/ai/google_settings.py +++ b/ravendb/documents/operations/ai/google_settings.py @@ -5,12 +5,14 @@ class GoogleAiVersion(Enum): - V1 = "V1", - V1_Beta = "V1_Beta", + V1 = ("V1",) + V1_Beta = ("V1_Beta",) class GoogleSettings(AbstractAiSettings): - def __init__(self, model: str = None, api_key: str = None, ai_version: GoogleAiVersion = None, dimensions: int = None): + def __init__( + self, model: str = None, api_key: str = None, ai_version: GoogleAiVersion = None, dimensions: int = None + ): super().__init__() self.model = model self.api_key = api_key diff --git a/ravendb/documents/operations/ai/hugging_face_settings.py b/ravendb/documents/operations/ai/hugging_face_settings.py index 297ff7f6..e34b1d1b 100644 --- a/ravendb/documents/operations/ai/hugging_face_settings.py +++ b/ravendb/documents/operations/ai/hugging_face_settings.py @@ -24,4 +24,4 @@ def to_json(self) -> Dict[str, Any]: "Model": self.model, "Endpoint": self.endpoint, "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, - } \ No newline at end of file + } diff --git a/ravendb/documents/operations/ai/mistral_ai_settings.py b/ravendb/documents/operations/ai/mistral_ai_settings.py index 216b254d..5719349e 100644 --- a/ravendb/documents/operations/ai/mistral_ai_settings.py +++ b/ravendb/documents/operations/ai/mistral_ai_settings.py @@ -24,4 +24,4 @@ def to_json(self) -> Dict[str, Any]: "Model": self.model, "Endpoint": self.endpoint, "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, - } \ No newline at end of file + } diff --git a/ravendb/documents/operations/ai/ollama_settings.py b/ravendb/documents/operations/ai/ollama_settings.py index fdee2797..69d366d9 100644 --- a/ravendb/documents/operations/ai/ollama_settings.py +++ b/ravendb/documents/operations/ai/ollama_settings.py @@ -25,4 +25,4 @@ def to_json(self) -> Dict[str, Any]: "Think": self.think, "Temperature": self.temperature, "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, - } \ No newline at end of file + } diff --git a/ravendb/documents/operations/ai/open_ai_base_settings.py b/ravendb/documents/operations/ai/open_ai_base_settings.py index 59112e06..abf70f53 100644 --- a/ravendb/documents/operations/ai/open_ai_base_settings.py +++ b/ravendb/documents/operations/ai/open_ai_base_settings.py @@ -4,7 +4,14 @@ class OpenAiBaseSettings(AbstractAiSettings, ABC): - def __init__(self, api_key: str = None, endpoint: str = None, model: str = None, dimensions: int = None, temperature: float = None): + def __init__( + self, + api_key: str = None, + endpoint: str = None, + model: str = None, + dimensions: int = None, + temperature: float = None, + ): super().__init__() self.api_key = api_key self.endpoint = endpoint diff --git a/ravendb/documents/operations/ai/open_ai_settings.py b/ravendb/documents/operations/ai/open_ai_settings.py index 4c338b98..97386de6 100644 --- a/ravendb/documents/operations/ai/open_ai_settings.py +++ b/ravendb/documents/operations/ai/open_ai_settings.py @@ -4,7 +4,16 @@ class OpenAiSettings(OpenAiBaseSettings): - def __init__(self, api_key: str = None, endpoint: str = None, model: str = None, organization_id: str = None, project_id: str = None, dimensions: int = None, temperature: float = None): + def __init__( + self, + api_key: str = None, + endpoint: str = None, + model: str = None, + organization_id: str = None, + project_id: str = None, + dimensions: int = None, + temperature: float = None, + ): super().__init__(api_key, endpoint, model, dimensions, temperature) self.organization_id = organization_id self.project_id = project_id @@ -31,4 +40,4 @@ def to_json(self) -> Dict[str, Any]: "OrganizationId": self.organization_id, "ProjectId": self.project_id, "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, - } \ No newline at end of file + } diff --git a/ravendb/documents/operations/etl/configuration.py b/ravendb/documents/operations/etl/configuration.py index a1c36db9..f01b1c0e 100644 --- a/ravendb/documents/operations/etl/configuration.py +++ b/ravendb/documents/operations/etl/configuration.py @@ -32,6 +32,7 @@ def from_json(cls, json_dict: Dict) -> "RavenConnectionString": topology_discovery_urls=json_dict["TopologyDiscoveryUrls"], ) + # todo: implement class EtlConfiguration(ConnectionString, Generic[_T]): pass diff --git a/ravendb/documents/operations/etl/elastic_search/__init__.py b/ravendb/documents/operations/etl/elastic_search/__init__.py index 7f6e315c..4a13f65c 100644 --- a/ravendb/documents/operations/etl/elastic_search/__init__.py +++ b/ravendb/documents/operations/etl/elastic_search/__init__.py @@ -45,7 +45,6 @@ def from_json(cls, data: Dict[str, Any]) -> "BasicAuthentication": ) - class CertificateAuthentication: def __init__(self, certificates_base64: List[str] = None): self.certificates_base64 = certificates_base64 @@ -61,9 +60,13 @@ def from_json(cls, data: Dict[str, Any]) -> "CertificateAuthentication": return cls(certificates_base64=list(certs) if certs is not None else None) - class Authentication: - def __init__(self, api_key: ApiKeyAuthentication = None, basic: BasicAuthentication = None, certificate: CertificateAuthentication = None): + def __init__( + self, + api_key: ApiKeyAuthentication = None, + basic: BasicAuthentication = None, + certificate: CertificateAuthentication = None, + ): self.api_key = api_key self.basic = basic self.certificate = certificate @@ -88,7 +91,6 @@ def from_json(cls, data: Dict[str, Any]) -> "Authentication": ) - class ElasticSearchConnectionString(ConnectionString): def __init__(self, name: str, nodes: List[str] = None, authentication: Authentication = None): super().__init__() @@ -113,4 +115,4 @@ def from_json(cls, json_dict: Dict[str, Any]) -> Any: name=json_dict["Name"], nodes=json_dict["Nodes"], authentication=Authentication.from_json(json_dict["Authentication"]), - ) \ No newline at end of file + ) diff --git a/ravendb/documents/operations/etl/queue/__init__.py b/ravendb/documents/operations/etl/queue/__init__.py index 611c1e65..599b7c60 100644 --- a/ravendb/documents/operations/etl/queue/__init__.py +++ b/ravendb/documents/operations/etl/queue/__init__.py @@ -3,8 +3,9 @@ from ravendb.documents.operations.connection_strings import ConnectionString import ravendb.serverwide.server_operation_executor from ravendb.documents.operations.etl.queue.amazon_sqs_connection_settings import AmazonSqsConnectionSettings -from ravendb.documents.operations.etl.queue.azure_queue_storage_connection_settings import \ - AzureQueueStorageConnectionSettings +from ravendb.documents.operations.etl.queue.azure_queue_storage_connection_settings import ( + AzureQueueStorageConnectionSettings, +) from ravendb.documents.operations.etl.queue.kafka_connection_settings import KafkaConnectionSettings from ravendb.documents.operations.etl.queue.rabbit_mq_connection_settings import RabbitMqConnectionSettings @@ -56,6 +57,8 @@ def from_json(cls, json_dict: dict) -> "QueueConnectionString": broker_type=QueueBrokerType(json_dict["BrokerType"]), kafka_settings=KafkaConnectionSettings.from_json(json_dict["KafkaConnectionSettings"]), rabbit_mq_settings=RabbitMqConnectionSettings.from_json(json_dict["RabbitMqConnectionSettings"]), - azure_queue_storage_settings=AzureQueueStorageConnectionSettings.from_json(json_dict["AzureQueueStorageConnectionSettings"]), - amazon_sqs_settings=AmazonSqsConnectionSettings.from_json(json_dict["AmazonSqsConnectionSettings"]) + azure_queue_storage_settings=AzureQueueStorageConnectionSettings.from_json( + json_dict["AzureQueueStorageConnectionSettings"] + ), + amazon_sqs_settings=AmazonSqsConnectionSettings.from_json(json_dict["AmazonSqsConnectionSettings"]), ) diff --git a/ravendb/documents/operations/etl/snowflake/__init__.py b/ravendb/documents/operations/etl/snowflake/__init__.py index ba4ceae9..b137fbb5 100644 --- a/ravendb/documents/operations/etl/snowflake/__init__.py +++ b/ravendb/documents/operations/etl/snowflake/__init__.py @@ -25,4 +25,4 @@ def from_json(cls, json_dict: dict) -> "SnowflakeConnectionString": return cls( name=json_dict["Name"], connection_string=json_dict["ConnectionString"], - ) \ No newline at end of file + ) From f925b75c3fc96f5aeffc46f391d04c68c829a370 Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Tue, 9 Sep 2025 21:16:45 +0200 Subject: [PATCH 6/6] RDBC-935 Finished the job, cleanup, tests, bugfixes --- ravendb/__init__.py | 2 +- .../operations/ai/ai_connection_string.py | 95 +- .../operations/ai/azure_open_ai_settings.py | 13 +- .../operations/ai/google_settings.py | 4 +- .../operations/ai/ollama_settings.py | 23 +- .../operations/ai/open_ai_settings.py | 8 +- .../documents/operations/backups/settings.py | 8 - .../get_connection_string_operation.py | 61 +- .../put_connection_string_operation.py | 4 - .../operations/etl/elastic_search/__init__.py | 119 +- .../etl/elastic_search/connection.py | 120 ++ .../documents/operations/etl/olap/__init__.py | 66 -- .../operations/etl/olap/connection.py | 72 ++ .../operations/etl/queue/__init__.py | 65 +- .../operations/etl/queue/connection.py | 80 ++ .../operations/etl/snowflake/__init__.py | 29 +- .../operations/etl/snowflake/connection.py | 28 + ravendb/documents/session/query.py | 2 +- ravendb/serverwide/database_record.py | 2 +- .../test_connection_string.py | 1020 ++++++++++++++++- 20 files changed, 1447 insertions(+), 374 deletions(-) create mode 100644 ravendb/documents/operations/etl/elastic_search/connection.py create mode 100644 ravendb/documents/operations/etl/olap/connection.py create mode 100644 ravendb/documents/operations/etl/queue/connection.py create mode 100644 ravendb/documents/operations/etl/snowflake/connection.py diff --git a/ravendb/__init__.py b/ravendb/__init__.py index ca90f736..323188af 100644 --- a/ravendb/__init__.py +++ b/ravendb/__init__.py @@ -108,7 +108,7 @@ ) from ravendb.documents.operations.etl.configuration import EtlConfiguration, RavenEtlConfiguration -from ravendb.documents.operations.etl.olap import OlapEtlConfiguration +from ravendb.documents.operations.etl.olap.connection import OlapEtlConfiguration from ravendb.documents.operations.etl.sql import SqlEtlConfiguration from ravendb.documents.operations.executor import MaintenanceOperationExecutor, SessionOperationExecutor from ravendb.documents.operations.expiration.configuration import ExpirationConfiguration diff --git a/ravendb/documents/operations/ai/ai_connection_string.py b/ravendb/documents/operations/ai/ai_connection_string.py index a742f226..e67baab1 100644 --- a/ravendb/documents/operations/ai/ai_connection_string.py +++ b/ravendb/documents/operations/ai/ai_connection_string.py @@ -1,7 +1,7 @@ import enum from typing import Optional, Dict, Any -import ravendb.serverwide.server_operation_executor +from ravendb.serverwide.server_operation_executor import ConnectionStringType from ravendb.documents.operations.ai.azure_open_ai_settings import AzureOpenAiSettings from ravendb.documents.operations.ai.embedded_settings import EmbeddedSettings from ravendb.documents.operations.ai.google_settings import GoogleSettings @@ -13,7 +13,7 @@ class AiModelType(enum.Enum): - TEXT_EMBEDDINGS = ("TextEmbeddings",) + TEXT_EMBEDDINGS = "TextEmbeddings" CHAT = "Chat" @@ -42,23 +42,58 @@ def __init__( self.mistral_ai_settings = mistral_ai_settings self.model_type = model_type + if not any( + [ + openai_settings, + azure_openai_settings, + ollama_settings, + embedded_settings, + google_settings, + huggingface_settings, + mistral_ai_settings, + ] + ): + raise ValueError( + "Please provide at least one of the following settings: openai_settings, azure_openai_settings, ollama_settings, embedded_settings, google_settings, huggingface_settings, mistral_ai_settings" + ) + + if model_type is None: + raise ValueError("Please provide a model type - AiModelType.TEXT_EMBEDDINGS or AiModelType.CHAT") + + settings_set_count = 0 + for setting in [ + openai_settings, + azure_openai_settings, + ollama_settings, + embedded_settings, + google_settings, + huggingface_settings, + mistral_ai_settings, + ]: + if setting: + settings_set_count += 1 if setting else 0 + if settings_set_count > 1: + raise ValueError( + "Please provide only one of the following settings: openai_settings, azure_openai_settings, ollama_settings, embedded_settings, google_settings, huggingface_settings, mistral_ai_settings" + ) + @property def get_type(self): - return ravendb.serverwide.server_operation_executor.ConnectionStringType.AI.value + return ConnectionStringType.AI.value def to_json(self) -> Dict[str, Any]: return { "Name": self.name, "Identifier": self.identifier, - "OpenaiSettings": self.openai_settings, - "AzureOpenaiSettings": self.azure_openai_settings, - "ollama_settings": self.ollama_settings, - "EmbeddedSettings": self.embedded_settings, - "GoogleSettings": self.google_settings, - "HuggingfaceSettings": self.huggingface_settings, - "MistralAiSettings": self.mistral_ai_settings, - "ModelType": self.model_type, - "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP, + "OpenAiSettings": self.openai_settings.to_json() if self.openai_settings else None, + "AzureOpenAiSettings": self.azure_openai_settings.to_json() if self.azure_openai_settings else None, + "OllamaSettings": self.ollama_settings.to_json() if self.ollama_settings else None, + "EmbeddedSettings": self.embedded_settings.to_json() if self.embedded_settings else None, + "GoogleSettings": self.google_settings.to_json() if self.google_settings else None, + "HuggingFaceSettings": self.huggingface_settings.to_json() if self.huggingface_settings else None, + "MistralAiSettings": self.mistral_ai_settings.to_json() if self.mistral_ai_settings else None, + "ModelType": self.model_type.value if self.model_type else None, + "Type": self.get_type, } @classmethod @@ -66,12 +101,32 @@ def from_json(cls, json_dict: Dict[str, Any]) -> "AiConnectionString": return cls( name=json_dict["Name"], identifier=json_dict["Identifier"], - openai_settings=OpenAiSettings.from_json(json_dict["OpenaiSettings"]), - azure_openai_settings=AzureOpenAiSettings.from_json(json_dict["AzureOpenaiSettings"]), - ollama_settings=OllamaSettings.from_json(json_dict["ollama_settings"]), - embedded_settings=EmbeddedSettings.from_json(json_dict["EmbeddedSettings"]), - google_settings=GoogleSettings.from_json(json_dict["GoogleSettings"]), - huggingface_settings=HuggingFaceSettings.from_json(json_dict["HuggingfaceSettings"]), - mistral_ai_settings=MistralAiSettings.from_json(json_dict["MistralAiSettings"]), - model_type=AiModelType(json_dict["ModelType"]) if json_dict["ModelType"] else None, + openai_settings=( + OpenAiSettings.from_json(json_dict["OpenAiSettings"]) if json_dict.get("OpenAiSettings") else None + ), + azure_openai_settings=( + AzureOpenAiSettings.from_json(json_dict["AzureOpenAiSettings"]) + if json_dict.get("AzureOpenAiSettings") + else None + ), + ollama_settings=( + OllamaSettings.from_json(json_dict["OllamaSettings"]) if json_dict.get("OllamaSettings") else None + ), + embedded_settings=( + EmbeddedSettings.from_json(json_dict["EmbeddedSettings"]) if json_dict.get("EmbeddedSettings") else None + ), + google_settings=( + GoogleSettings.from_json(json_dict["GoogleSettings"]) if json_dict.get("GoogleSettings") else None + ), + huggingface_settings=( + HuggingFaceSettings.from_json(json_dict["HuggingFaceSettings"]) + if json_dict.get("HuggingFaceSettings") + else None + ), + mistral_ai_settings=( + MistralAiSettings.from_json(json_dict["MistralAiSettings"]) + if json_dict.get("MistralAiSettings") + else None + ), + model_type=AiModelType(json_dict["ModelType"]) if json_dict.get("ModelType") else None, ) diff --git a/ravendb/documents/operations/ai/azure_open_ai_settings.py b/ravendb/documents/operations/ai/azure_open_ai_settings.py index baeedcda..b7e512f7 100644 --- a/ravendb/documents/operations/ai/azure_open_ai_settings.py +++ b/ravendb/documents/operations/ai/azure_open_ai_settings.py @@ -19,12 +19,12 @@ def __init__( @classmethod def from_json(cls, json_dict: Dict[str, Any]) -> "AzureOpenAiSettings": return cls( - api_key=json_dict["ApiKey"], - endpoint=json_dict["Endpoint"], - model=json_dict["Model"], - dimensions=json_dict["Dimensions"], - temperature=json_dict["Temperature"], - deployment_name=json_dict["DeploymentName"], + api_key=json_dict["ApiKey"] if "ApiKey" in json_dict else None, + endpoint=json_dict["Endpoint"] if "Endpoint" in json_dict else None, + model=json_dict["Model"] if "Model" in json_dict else None, + dimensions=json_dict["Dimensions"] if "Dimensions" in json_dict else None, + temperature=json_dict["Temperature"] if "Temperature" in json_dict else None, + deployment_name=json_dict["DeploymentName"] if "DeploymentName" in json_dict else None, ) def to_json(self) -> Dict[str, Any]: @@ -35,5 +35,4 @@ def to_json(self) -> Dict[str, Any]: "Dimensions": self.dimensions, "Temperature": self.temperature, "DeploymentName": self.deployment_name, - "EmbeddingsMaxConcurrentBatches": self.embeddings_max_concurrent_batches, } diff --git a/ravendb/documents/operations/ai/google_settings.py b/ravendb/documents/operations/ai/google_settings.py index 01711713..6aa72135 100644 --- a/ravendb/documents/operations/ai/google_settings.py +++ b/ravendb/documents/operations/ai/google_settings.py @@ -5,8 +5,8 @@ class GoogleAiVersion(Enum): - V1 = ("V1",) - V1_Beta = ("V1_Beta",) + V1 = "V1" + V1_Beta = "V1_Beta" class GoogleSettings(AbstractAiSettings): diff --git a/ravendb/documents/operations/ai/ollama_settings.py b/ravendb/documents/operations/ai/ollama_settings.py index 69d366d9..cb5e93e0 100644 --- a/ravendb/documents/operations/ai/ollama_settings.py +++ b/ravendb/documents/operations/ai/ollama_settings.py @@ -4,18 +4,31 @@ class OllamaSettings(AbstractAiSettings): - def __init__(self, uri: str = None, model: str = None): + def __init__( + self, + uri: str = None, + model: str = None, + think: bool = None, + temperature: float = None, + embeddings_max_concurrent_batches: int = None, + ): super().__init__() self.uri = uri self.model = model - self.think: bool = None - self.temperature: float = None + self.think = think + self.temperature = temperature + self.embeddings_max_concurrent_batches = embeddings_max_concurrent_batches @classmethod def from_json(cls, json_dict: Dict[str, Any]) -> "OllamaSettings": return cls( - uri=json_dict["Uri"], - model=json_dict["Model"], + uri=json_dict["Uri"] if "Uri" in json_dict else None, + model=json_dict["Model"] if "Model" in json_dict else None, + think=json_dict["Think"] if "Think" in json_dict else None, + temperature=json_dict["Temperature"] if "Temperature" in json_dict else None, + embeddings_max_concurrent_batches=( + json_dict["EmbeddingsMaxConcurrentBatches"] if "EmbeddingsMaxConcurrentBatches" in json_dict else None + ), ) def to_json(self) -> Dict[str, Any]: diff --git a/ravendb/documents/operations/ai/open_ai_settings.py b/ravendb/documents/operations/ai/open_ai_settings.py index 97386de6..d5974c98 100644 --- a/ravendb/documents/operations/ai/open_ai_settings.py +++ b/ravendb/documents/operations/ai/open_ai_settings.py @@ -24,10 +24,10 @@ def from_json(cls, json_dict: Dict[str, Any]) -> "OpenAiSettings": api_key=json_dict["ApiKey"], endpoint=json_dict["Endpoint"], model=json_dict["Model"], - dimensions=json_dict["Dimensions"], - temperature=json_dict["Temperature"], - organization_id=json_dict["OrganizationId"], - project_id=json_dict["ProjectId"], + dimensions=json_dict["Dimensions"] if "Dimensions" in json_dict else None, + temperature=json_dict["Temperature"] if "Temperature" in json_dict else None, + organization_id=json_dict["OrganizationId"] if "OrganizationId" in json_dict else None, + project_id=json_dict["ProjectId"] if "ProjectId" in json_dict else None, ) def to_json(self) -> Dict[str, Any]: diff --git a/ravendb/documents/operations/backups/settings.py b/ravendb/documents/operations/backups/settings.py index 88e4013c..a4392f5b 100644 --- a/ravendb/documents/operations/backups/settings.py +++ b/ravendb/documents/operations/backups/settings.py @@ -259,19 +259,15 @@ def __init__( disabled: bool = None, get_backup_configuration_script: GetBackupConfigurationScript = None, url: str = None, - port: int = None, user_name: str = None, password: str = None, certificate_as_base64: str = None, - certificate_file_name: str = None, ): super().__init__(disabled, get_backup_configuration_script) self.url = url - self.port = port self.user_name = user_name self.password = password self.certificate_as_base64 = certificate_as_base64 - self.certificate_file_name = certificate_file_name @classmethod def from_json(cls, json_dict: Dict[str, Any]) -> FtpSettings: @@ -283,11 +279,9 @@ def from_json(cls, json_dict: Dict[str, Any]) -> FtpSettings: else None ), json_dict["Url"], - json_dict["Port"] if "Port" in json_dict else None, json_dict["UserName"], json_dict["Password"], json_dict["CertificateAsBase64"], - json_dict["CertificateFileName"] if "CertificateFileName" in json_dict else None, ) def to_json(self) -> Dict[str, Any]: @@ -297,11 +291,9 @@ def to_json(self) -> Dict[str, Any]: self.get_backup_configuration_script.to_json() if self.get_backup_configuration_script else None ), "Url": self.url, - "Port": self.port, "UserName": self.user_name, "Password": self.password, "CertificateAsBase64": self.certificate_as_base64, - "CertificateFileName": self.certificate_file_name, } diff --git a/ravendb/documents/operations/connection_string/get_connection_string_operation.py b/ravendb/documents/operations/connection_string/get_connection_string_operation.py index 04109036..13dbec34 100644 --- a/ravendb/documents/operations/connection_string/get_connection_string_operation.py +++ b/ravendb/documents/operations/connection_string/get_connection_string_operation.py @@ -7,10 +7,10 @@ from ravendb.documents.operations.ai.ai_connection_string import AiConnectionString from ravendb.documents.operations.definitions import MaintenanceOperation from ravendb.documents.operations.etl.configuration import RavenConnectionString -from ravendb.documents.operations.etl.elastic_search import ElasticSearchConnectionString -from ravendb.documents.operations.etl.olap import OlapConnectionString -from ravendb.documents.operations.etl.queue import QueueConnectionString -from ravendb.documents.operations.etl.snowflake import SnowflakeConnectionString +from ravendb.documents.operations.etl.elastic_search.connection import ElasticSearchConnectionString +from ravendb.documents.operations.etl.olap.connection import OlapConnectionString +from ravendb.documents.operations.etl.queue.connection import QueueConnectionString +from ravendb.documents.operations.etl.snowflake.connection import SnowflakeConnectionString from ravendb.documents.operations.etl.sql import SqlConnectionString from ravendb.serverwide.server_operation_executor import ConnectionStringType @@ -46,15 +46,52 @@ def to_json(self) -> Dict: } @classmethod - def from_json(cls, json_dict: Dict) -> "GetConnectionStringsResult": + def from_json(cls, json_dict: Dict[str, Dict]) -> "GetConnectionStringsResult": return cls( - raven_connection_strings=json_dict["RavenConnectionStrings"], - sql_connection_strings=json_dict["SqlConnectionStrings"], - olap_connection_strings=json_dict["OlapConnectionStrings"], - ai_connection_strings=json_dict["AiConnectionStrings"], - elastic_search_connection_strings=json_dict["ElasticSearchConnectionStrings"], - queue_connection_strings=json_dict["QueueConnectionStrings"], - snowflake_connection_strings=json_dict["SnowflakeConnectionStrings"], + raven_connection_strings=( + {key: RavenConnectionString.from_json(rcs) for key, rcs in json_dict["RavenConnectionStrings"].items()} + if json_dict["RavenConnectionStrings"] + else None + ), + sql_connection_strings=( + {key: SqlConnectionString.from_json(sqlcs) for key, sqlcs in json_dict["SqlConnectionStrings"].items()} + if json_dict["SqlConnectionStrings"] + else None + ), + olap_connection_strings=( + { + key: OlapConnectionString.from_json(olapcs) + for key, olapcs in json_dict["OlapConnectionStrings"].items() + } + if json_dict["OlapConnectionStrings"] + else None + ), + ai_connection_strings=( + {key: AiConnectionString.from_json(aics) for key, aics in json_dict["AiConnectionStrings"].items()} + if json_dict["AiConnectionStrings"] + else None + ), + elastic_search_connection_strings=( + { + key: ElasticSearchConnectionString.from_json(escs) + for key, escs in json_dict["ElasticSearchConnectionStrings"].items() + } + if json_dict["ElasticSearchConnectionStrings"] + else None + ), + queue_connection_strings=( + {key: QueueConnectionString.from_json(qcs) for key, qcs in json_dict["QueueConnectionStrings"].items()} + if json_dict["QueueConnectionStrings"] + else None + ), + snowflake_connection_strings=( + { + key: SnowflakeConnectionString.from_json(scs) + for key, scs in json_dict["SnowflakeConnectionStrings"].items() + } + if json_dict["SnowflakeConnectionStrings"] + else None + ), ) diff --git a/ravendb/documents/operations/connection_string/put_connection_string_operation.py b/ravendb/documents/operations/connection_string/put_connection_string_operation.py index fff42bf0..c2bc1817 100644 --- a/ravendb/documents/operations/connection_string/put_connection_string_operation.py +++ b/ravendb/documents/operations/connection_string/put_connection_string_operation.py @@ -6,10 +6,6 @@ from ravendb import ConnectionString, RavenCommand, ServerNode, RaftCommand from ravendb.documents.conventions import DocumentConventions from ravendb.documents.operations.definitions import MaintenanceOperation -from ravendb.documents.operations.etl.configuration import RavenConnectionString -from ravendb.documents.operations.etl.olap import OlapConnectionString -from ravendb.documents.operations.etl.sql import SqlConnectionString -from ravendb.serverwide.server_operation_executor import ConnectionStringType from ravendb.util.util import RaftIdGenerator diff --git a/ravendb/documents/operations/etl/elastic_search/__init__.py b/ravendb/documents/operations/etl/elastic_search/__init__.py index 4a13f65c..5f282702 100644 --- a/ravendb/documents/operations/etl/elastic_search/__init__.py +++ b/ravendb/documents/operations/etl/elastic_search/__init__.py @@ -1,118 +1 @@ -from typing import List, Dict, Any - -from ravendb.documents.operations.connection_strings import ConnectionString -import ravendb.serverwide.server_operation_executor - - -class ApiKeyAuthentication: - def __init__(self, api_key_id: str = None, api_key: str = None, encoded_api_key: str = None): - self.api_key_id = api_key_id - self.api_key = api_key - self.encoded_api_key = encoded_api_key - - def to_json(self) -> Dict[str, Any]: - return { - "ApiKeyId": self.api_key_id, - "ApiKey": self.api_key, - "EncodedApiKey": self.encoded_api_key, - } - - @classmethod - def from_json(cls, data: Dict[str, Any]) -> "ApiKeyAuthentication": - return cls( - api_key_id=data.get("ApiKeyId") or data.get("api_key_id"), - api_key=data.get("ApiKey") or data.get("api_key"), - encoded_api_key=data.get("EncodedApiKey") or data.get("encoded_api_key"), - ) - - -class BasicAuthentication: - def __init__(self, username: str = None, password: str = None): - self.username = username - self.password = password - - def to_json(self) -> Dict[str, Any]: - return { - "Username": self.username, - "Password": self.password, - } - - @classmethod - def from_json(cls, data: Dict[str, Any]) -> "BasicAuthentication": - return cls( - username=data.get("Username") or data.get("username"), - password=data.get("Password") or data.get("password"), - ) - - -class CertificateAuthentication: - def __init__(self, certificates_base64: List[str] = None): - self.certificates_base64 = certificates_base64 - - def to_json(self) -> Dict[str, Any]: - return { - "CertificatesBase64": list(self.certificates_base64) if self.certificates_base64 is not None else None, - } - - @classmethod - def from_json(cls, data: Dict[str, Any]) -> "CertificateAuthentication": - certs = data.get("CertificatesBase64") or data.get("certificates_base64") - return cls(certificates_base64=list(certs) if certs is not None else None) - - -class Authentication: - def __init__( - self, - api_key: ApiKeyAuthentication = None, - basic: BasicAuthentication = None, - certificate: CertificateAuthentication = None, - ): - self.api_key = api_key - self.basic = basic - self.certificate = certificate - - def to_json(self) -> Dict[str, Any]: - return { - "ApiKey": self.api_key.to_json() if self.api_key is not None else None, - "Basic": self.basic.to_json() if self.basic is not None else None, - "Certificate": self.certificate.to_json() if self.certificate is not None else None, - } - - @classmethod - def from_json(cls, data: Dict[str, Any]) -> "Authentication": - api_key_data = data.get("ApiKey") - basic_data = data.get("Basic") - certificate_data = data.get("Certificate") - - return cls( - api_key=ApiKeyAuthentication.from_json(api_key_data) if api_key_data else None, - basic=BasicAuthentication.from_json(basic_data) if basic_data else None, - certificate=CertificateAuthentication.from_json(certificate_data) if certificate_data else None, - ) - - -class ElasticSearchConnectionString(ConnectionString): - def __init__(self, name: str, nodes: List[str] = None, authentication: Authentication = None): - super().__init__() - self.name = name - self.nodes = nodes - self.authentication = authentication - - def get_type(self): - return ravendb.serverwide.server_operation_executor.ConnectionStringType.ELASTIC_SEARCH.value - - def to_json(self): - return { - "Name": self.name, - "Nodes": self.nodes, - "Authentication": self.authentication, - "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.ELASTIC_SEARCH, - } - - @classmethod - def from_json(cls, json_dict: Dict[str, Any]) -> Any: - return cls( - name=json_dict["Name"], - nodes=json_dict["Nodes"], - authentication=Authentication.from_json(json_dict["Authentication"]), - ) + \ No newline at end of file diff --git a/ravendb/documents/operations/etl/elastic_search/connection.py b/ravendb/documents/operations/etl/elastic_search/connection.py new file mode 100644 index 00000000..aa464574 --- /dev/null +++ b/ravendb/documents/operations/etl/elastic_search/connection.py @@ -0,0 +1,120 @@ +from typing import List, Dict, Any + +from ravendb.documents.operations.connection_strings import ConnectionString +from ravendb.serverwide.server_operation_executor import ConnectionStringType + + +class ApiKeyAuthentication: + def __init__(self, api_key_id: str = None, api_key: str = None, encoded_api_key: str = None): + self.api_key_id = api_key_id + self.api_key = api_key + self.encoded_api_key = encoded_api_key + + def to_json(self) -> Dict[str, Any]: + return { + "ApiKeyId": self.api_key_id, + "ApiKey": self.api_key, + "EncodedApiKey": self.encoded_api_key, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "ApiKeyAuthentication": + return cls( + api_key_id=data.get("ApiKeyId") or data.get("api_key_id"), + api_key=data.get("ApiKey") or data.get("api_key"), + encoded_api_key=data.get("EncodedApiKey") or data.get("encoded_api_key"), + ) + + +class BasicAuthentication: + def __init__(self, username: str = None, password: str = None): + self.username = username + self.password = password + + def to_json(self) -> Dict[str, Any]: + return { + "Username": self.username, + "Password": self.password, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "BasicAuthentication": + return cls( + username=data.get("Username") or data.get("username"), + password=data.get("Password") or data.get("password"), + ) + + +class CertificateAuthentication: + def __init__(self, certificates_base64: List[str] = None): + self.certificates_base64 = certificates_base64 + + def to_json(self) -> Dict[str, Any]: + return { + "CertificatesBase64": list(self.certificates_base64) if self.certificates_base64 is not None else None, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "CertificateAuthentication": + certs = data.get("CertificatesBase64") or data.get("certificates_base64") + return cls(certificates_base64=list(certs) if certs is not None else None) + + +class Authentication: + def __init__( + self, + api_key: ApiKeyAuthentication = None, + basic: BasicAuthentication = None, + certificate: CertificateAuthentication = None, + ): + self.api_key = api_key + self.basic = basic + self.certificate = certificate + + def to_json(self) -> Dict[str, Any]: + return { + "ApiKey": self.api_key.to_json() if self.api_key is not None else None, + "Basic": self.basic.to_json() if self.basic is not None else None, + "Certificate": self.certificate.to_json() if self.certificate is not None else None, + } + + @classmethod + def from_json(cls, data: Dict[str, Any]) -> "Authentication": + api_key_data = data.get("ApiKey") + basic_data = data.get("Basic") + certificate_data = data.get("Certificate") + + return cls( + api_key=ApiKeyAuthentication.from_json(api_key_data) if api_key_data else None, + basic=BasicAuthentication.from_json(basic_data) if basic_data else None, + certificate=CertificateAuthentication.from_json(certificate_data) if certificate_data else None, + ) + + +class ElasticSearchConnectionString(ConnectionString): + def __init__(self, name: str, nodes: List[str] = None, authentication: Authentication = None): + super().__init__(name) + self.nodes = nodes + self.authentication = authentication + + @property + def get_type(self): + return ConnectionStringType.ELASTIC_SEARCH.value + + def to_json(self): + return { + "Name": self.name, + "Nodes": self.nodes, + "Authentication": self.authentication.to_json() if self.authentication else None, + "Type": ConnectionStringType.ELASTIC_SEARCH, + } + + @classmethod + def from_json(cls, json_dict: Dict[str, Any]) -> Any: + return cls( + name=json_dict["Name"], + nodes=json_dict["Nodes"], + authentication=( + Authentication.from_json(json_dict["Authentication"]) if json_dict["Authentication"] else None + ), + ) diff --git a/ravendb/documents/operations/etl/olap/__init__.py b/ravendb/documents/operations/etl/olap/__init__.py index b6fbec40..e69de29b 100644 --- a/ravendb/documents/operations/etl/olap/__init__.py +++ b/ravendb/documents/operations/etl/olap/__init__.py @@ -1,66 +0,0 @@ -from typing import Optional - -from ravendb.documents.operations.backups.settings import ( - LocalSettings, - S3Settings, - AzureSettings, - GlacierSettings, - GoogleCloudSettings, - FtpSettings, -) -from ravendb.documents.operations.connection_strings import ConnectionString -import ravendb.serverwide.server_operation_executor -from ravendb.documents.operations.etl.configuration import EtlConfiguration - - -class OlapConnectionString(ConnectionString): - def __init__( - self, - name: str, - local_settings: Optional[LocalSettings] = None, - s3_settings: Optional[S3Settings] = None, - azure_settings: Optional[AzureSettings] = None, - glacier_settings: Optional[GlacierSettings] = None, - google_cloud_settings: Optional[GoogleCloudSettings] = None, - ftp_settings: Optional[FtpSettings] = None, - ): - super().__init__(name) - self.local_settings = local_settings - self.s3_settings = s3_settings - self.azure_settings = azure_settings - self.glacier_settings = glacier_settings - self.google_cloud_settings = google_cloud_settings - self.ftp_settings = ftp_settings - - @property - def get_type(self): - return ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP.value - - def to_json(self): - return { - "Name": self.name, - "LocalSettings": self.local_settings, - "S3Settings": self.s3_settings, - "AzureSettings": self.azure_settings, - "GlacierSettings": self.glacier_settings, - "GoogleCloudSettings": self.google_cloud_settings, - "FtpSettings": self.ftp_settings, - "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP, - } - - @classmethod - def from_json(cls, json_dict: dict) -> "OlapConnectionString": - return cls( - name=json_dict["Name"], - local_settings=LocalSettings.from_json(json_dict["LocalSettings"]), - s3_settings=S3Settings.from_json(json_dict["S3Settings"]), - azure_settings=AzureSettings.from_json(json_dict["AzureSettings"]), - glacier_settings=GlacierSettings.from_json(json_dict["GlacierSettings"]), - google_cloud_settings=GoogleCloudSettings.from_json(json_dict["GoogleCloudSettings"]), - ftp_settings=FtpSettings.from_json(json_dict["FtpSettings"]), - ) - - -# todo: implement -class OlapEtlConfiguration(EtlConfiguration[OlapConnectionString]): - pass diff --git a/ravendb/documents/operations/etl/olap/connection.py b/ravendb/documents/operations/etl/olap/connection.py new file mode 100644 index 00000000..f6e7ea76 --- /dev/null +++ b/ravendb/documents/operations/etl/olap/connection.py @@ -0,0 +1,72 @@ +from typing import Optional + +from ravendb.documents.operations.backups.settings import ( + LocalSettings, + S3Settings, + AzureSettings, + GlacierSettings, + GoogleCloudSettings, + FtpSettings, +) +from ravendb.documents.operations.connection_strings import ConnectionString +import ravendb.serverwide.server_operation_executor +from ravendb.documents.operations.etl.configuration import EtlConfiguration + + +class OlapConnectionString(ConnectionString): + def __init__( + self, + name: str, + local_settings: Optional[LocalSettings] = None, + s3_settings: Optional[S3Settings] = None, + azure_settings: Optional[AzureSettings] = None, + glacier_settings: Optional[GlacierSettings] = None, + google_cloud_settings: Optional[GoogleCloudSettings] = None, + ftp_settings: Optional[FtpSettings] = None, + ): + super().__init__(name) + self.local_settings = local_settings + self.s3_settings = s3_settings + self.azure_settings = azure_settings + self.glacier_settings = glacier_settings + self.google_cloud_settings = google_cloud_settings + self.ftp_settings = ftp_settings + + @property + def get_type(self): + return ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP.value + + def to_json(self): + return { + "Name": self.name, + "LocalSettings": self.local_settings, + "S3Settings": self.s3_settings, + "AzureSettings": self.azure_settings, + "GlacierSettings": self.glacier_settings, + "GoogleCloudSettings": self.google_cloud_settings, + "FtpSettings": self.ftp_settings, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP, + } + + @classmethod + def from_json(cls, json_dict: dict) -> "OlapConnectionString": + return cls( + name=json_dict["Name"], + local_settings=LocalSettings.from_json(json_dict["LocalSettings"]) if json_dict["LocalSettings"] else None, + s3_settings=S3Settings.from_json(json_dict["S3Settings"]) if json_dict["S3Settings"] else None, + azure_settings=AzureSettings.from_json(json_dict["AzureSettings"]) if json_dict["AzureSettings"] else None, + glacier_settings=( + GlacierSettings.from_json(json_dict["GlacierSettings"]) if json_dict["GlacierSettings"] else None + ), + google_cloud_settings=( + GoogleCloudSettings.from_json(json_dict["GoogleCloudSettings"]) + if json_dict["GoogleCloudSettings"] + else None + ), + ftp_settings=FtpSettings.from_json(json_dict["FtpSettings"]) if json_dict["FtpSettings"] else None, + ) + + +# todo: implement +class OlapEtlConfiguration(EtlConfiguration[OlapConnectionString]): + pass diff --git a/ravendb/documents/operations/etl/queue/__init__.py b/ravendb/documents/operations/etl/queue/__init__.py index 599b7c60..5f282702 100644 --- a/ravendb/documents/operations/etl/queue/__init__.py +++ b/ravendb/documents/operations/etl/queue/__init__.py @@ -1,64 +1 @@ -from enum import Enum - -from ravendb.documents.operations.connection_strings import ConnectionString -import ravendb.serverwide.server_operation_executor -from ravendb.documents.operations.etl.queue.amazon_sqs_connection_settings import AmazonSqsConnectionSettings -from ravendb.documents.operations.etl.queue.azure_queue_storage_connection_settings import ( - AzureQueueStorageConnectionSettings, -) -from ravendb.documents.operations.etl.queue.kafka_connection_settings import KafkaConnectionSettings -from ravendb.documents.operations.etl.queue.rabbit_mq_connection_settings import RabbitMqConnectionSettings - - -class QueueBrokerType(Enum): - NONE = "None" - KAFKA = "Kafka" - RABBIT_MQ = "RabbitMq" - AZURE_QUEUE_STORAGE = "AzureQueueStorage" - AMAZON_SQS = "AmazonSqs" - - -class QueueConnectionString(ConnectionString): - def __init__( - self, - name: str = None, - broker_type: QueueBrokerType = None, - kafka_settings: KafkaConnectionSettings = None, - rabbit_mq_settings: RabbitMqConnectionSettings = None, - azure_queue_storage_settings: AzureQueueStorageConnectionSettings = None, - amazon_sqs_settings: AmazonSqsConnectionSettings = None, - ): - super().__init__() - self.name = name - self.broker_type = broker_type - self.kafka_settings = kafka_settings - self.rabbit_mq_settings = rabbit_mq_settings - self.azure_queue_storage_settings = azure_queue_storage_settings - self.amazon_sqs_settings = amazon_sqs_settings - - def get_type(self): - return ravendb.serverwide.server_operation_executor.ConnectionStringType.QUEUE.value - - def to_json(self): - return { - "Name": self.name, - "BrokerType": self.broker_type, - "KafkaConnectionSettings": self.kafka_settings, - "RabbitMqConnectionSettings": self.rabbit_mq_settings, - "AzureQueueStorageConnectionSettings": self.azure_queue_storage_settings, - "AmazonSqsConnectionSettings": self.amazon_sqs_settings, - "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.QUEUE, - } - - @classmethod - def from_json(cls, json_dict: dict) -> "QueueConnectionString": - return cls( - name=json_dict["Name"], - broker_type=QueueBrokerType(json_dict["BrokerType"]), - kafka_settings=KafkaConnectionSettings.from_json(json_dict["KafkaConnectionSettings"]), - rabbit_mq_settings=RabbitMqConnectionSettings.from_json(json_dict["RabbitMqConnectionSettings"]), - azure_queue_storage_settings=AzureQueueStorageConnectionSettings.from_json( - json_dict["AzureQueueStorageConnectionSettings"] - ), - amazon_sqs_settings=AmazonSqsConnectionSettings.from_json(json_dict["AmazonSqsConnectionSettings"]), - ) + \ No newline at end of file diff --git a/ravendb/documents/operations/etl/queue/connection.py b/ravendb/documents/operations/etl/queue/connection.py new file mode 100644 index 00000000..985d65d0 --- /dev/null +++ b/ravendb/documents/operations/etl/queue/connection.py @@ -0,0 +1,80 @@ +from enum import Enum + +from ravendb.documents.operations.connection_strings import ConnectionString +import ravendb.serverwide.server_operation_executor +from ravendb.documents.operations.etl.queue.amazon_sqs_connection_settings import AmazonSqsConnectionSettings +from ravendb.documents.operations.etl.queue.azure_queue_storage_connection_settings import ( + AzureQueueStorageConnectionSettings, +) +from ravendb.documents.operations.etl.queue.kafka_connection_settings import KafkaConnectionSettings +from ravendb.documents.operations.etl.queue.rabbit_mq_connection_settings import RabbitMqConnectionSettings + + +class QueueBrokerType(Enum): + NONE = "None" + KAFKA = "Kafka" + RABBIT_MQ = "RabbitMq" + AZURE_QUEUE_STORAGE = "AzureQueueStorage" + AMAZON_SQS = "AmazonSqs" + + +class QueueConnectionString(ConnectionString): + def __init__( + self, + name: str = None, + broker_type: QueueBrokerType = None, + kafka_settings: KafkaConnectionSettings = None, + rabbit_mq_settings: RabbitMqConnectionSettings = None, + azure_queue_storage_settings: AzureQueueStorageConnectionSettings = None, + amazon_sqs_settings: AmazonSqsConnectionSettings = None, + ): + super().__init__(name) + self.broker_type = broker_type + self.kafka_settings = kafka_settings + self.rabbit_mq_settings = rabbit_mq_settings + self.azure_queue_storage_settings = azure_queue_storage_settings + self.amazon_sqs_settings = amazon_sqs_settings + + @property + def get_type(self): + return ravendb.serverwide.server_operation_executor.ConnectionStringType.QUEUE.value + + def to_json(self): + return { + "Name": self.name, + "BrokerType": self.broker_type.value, + "KafkaConnectionSettings": self.kafka_settings.to_json() if self.kafka_settings else None, + "RabbitMqConnectionSettings": self.rabbit_mq_settings.to_json() if self.rabbit_mq_settings else None, + "AzureQueueStorageConnectionSettings": ( + self.azure_queue_storage_settings.to_json() if self.azure_queue_storage_settings else None + ), + "AmazonSqsConnectionSettings": self.amazon_sqs_settings.to_json() if self.amazon_sqs_settings else None, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.QUEUE, + } + + @classmethod + def from_json(cls, json_dict: dict) -> "QueueConnectionString": + return cls( + name=json_dict["Name"], + broker_type=QueueBrokerType(json_dict["BrokerType"]), + kafka_settings=( + KafkaConnectionSettings.from_json(json_dict["KafkaConnectionSettings"]) + if json_dict["KafkaConnectionSettings"] + else None + ), + rabbit_mq_settings=( + RabbitMqConnectionSettings.from_json(json_dict["RabbitMqConnectionSettings"]) + if json_dict["RabbitMqConnectionSettings"] + else None + ), + azure_queue_storage_settings=( + AzureQueueStorageConnectionSettings.from_json(json_dict["AzureQueueStorageConnectionSettings"]) + if json_dict["AzureQueueStorageConnectionSettings"] + else None + ), + amazon_sqs_settings=( + AmazonSqsConnectionSettings.from_json(json_dict["AmazonSqsConnectionSettings"]) + if json_dict["AmazonSqsConnectionSettings"] + else None + ), + ) diff --git a/ravendb/documents/operations/etl/snowflake/__init__.py b/ravendb/documents/operations/etl/snowflake/__init__.py index b137fbb5..5f282702 100644 --- a/ravendb/documents/operations/etl/snowflake/__init__.py +++ b/ravendb/documents/operations/etl/snowflake/__init__.py @@ -1,28 +1 @@ -from typing import Optional - -from ravendb.documents.operations.connection_strings import ConnectionString -import ravendb.serverwide.server_operation_executor - - -class SnowflakeConnectionString(ConnectionString): - def __init__(self, name: str, connection_string: Optional[str] = None): - super().__init__(name) - self.connection_string = connection_string - - @property - def get_type(self): - return ravendb.serverwide.server_operation_executor.ConnectionStringType.SNOWFLAKE.value - - def to_json(self): - return { - "Name": self.name, - "ConnectionString": self.connection_string, - "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.SNOWFLAKE, - } - - @classmethod - def from_json(cls, json_dict: dict) -> "SnowflakeConnectionString": - return cls( - name=json_dict["Name"], - connection_string=json_dict["ConnectionString"], - ) + \ No newline at end of file diff --git a/ravendb/documents/operations/etl/snowflake/connection.py b/ravendb/documents/operations/etl/snowflake/connection.py new file mode 100644 index 00000000..b2d0e87b --- /dev/null +++ b/ravendb/documents/operations/etl/snowflake/connection.py @@ -0,0 +1,28 @@ +from typing import Optional + +from ravendb.documents.operations.connection_strings import ConnectionString +import ravendb.serverwide.server_operation_executor + + +class SnowflakeConnectionString(ConnectionString): + def __init__(self, name: str, connection_string: Optional[str] = None): + super().__init__(name) + self.connection_string = connection_string + + @property + def get_type(self): + return ravendb.serverwide.server_operation_executor.ConnectionStringType.SNOWFLAKE.value + + def to_json(self): + return { + "Name": self.name, + "ConnectionString": self.connection_string, + "Type": ravendb.serverwide.server_operation_executor.ConnectionStringType.SNOWFLAKE, + } + + @classmethod + def from_json(cls, json_dict: dict) -> "SnowflakeConnectionString": + return cls( + name=json_dict["Name"], + connection_string=json_dict["ConnectionString"], + ) diff --git a/ravendb/documents/session/query.py b/ravendb/documents/session/query.py index b8e355f9..26e3fc99 100644 --- a/ravendb/documents/session/query.py +++ b/ravendb/documents/session/query.py @@ -216,7 +216,7 @@ def _to_string(self) -> str: return self.__to_string(False) def _using_default_operator(self, operator: QueryOperator) -> None: - if not self._where_tokens: + if self._where_tokens: raise RuntimeError("Default operator can only be set before any where clause is added") self._default_operator = operator diff --git a/ravendb/serverwide/database_record.py b/ravendb/serverwide/database_record.py index a6b46b8c..bb6f724c 100644 --- a/ravendb/serverwide/database_record.py +++ b/ravendb/serverwide/database_record.py @@ -12,7 +12,7 @@ ) from ravendb.documents.operations.backups.settings import PeriodicBackupConfiguration from ravendb.documents.operations.etl.configuration import RavenConnectionString, RavenEtlConfiguration -from ravendb.documents.operations.etl.olap import OlapConnectionString, OlapEtlConfiguration +from ravendb.documents.operations.etl.olap.connection import OlapConnectionString, OlapEtlConfiguration from ravendb.documents.operations.etl.sql import SqlConnectionString, SqlEtlConfiguration from ravendb.documents.operations.expiration.configuration import ExpirationConfiguration from ravendb.documents.operations.refresh.configuration import RefreshConfiguration diff --git a/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py index 7bfaa9f6..6d002458 100644 --- a/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py +++ b/ravendb/tests/jvm_migrated_tests/client_tests/documents_tests/operations_tests/test_connection_string.py @@ -1,14 +1,48 @@ from ravendb import FtpSettings +from ravendb.documents.operations.ai.ai_connection_string import AiConnectionString, AiModelType +from ravendb.documents.operations.ai.azure_open_ai_settings import AzureOpenAiSettings +from ravendb.documents.operations.ai.embedded_settings import EmbeddedSettings +from ravendb.documents.operations.ai.google_settings import GoogleSettings, GoogleAiVersion +from ravendb.documents.operations.ai.hugging_face_settings import HuggingFaceSettings +from ravendb.documents.operations.ai.mistral_ai_settings import MistralAiSettings +from ravendb.documents.operations.ai.ollama_settings import OllamaSettings +from ravendb.documents.operations.ai.open_ai_settings import OpenAiSettings +from ravendb.documents.operations.backups.settings import ( + LocalSettings, + S3Settings, + AzureSettings, + GlacierSettings, + GetBackupConfigurationScript, +) from ravendb.documents.operations.connection_string.get_connection_string_operation import GetConnectionStringsOperation from ravendb.documents.operations.connection_string.put_connection_string_operation import ( PutConnectionStringOperation, - PutConnectionStringResult, ) from ravendb.documents.operations.connection_string.remove_connection_string_operation import ( RemoveConnectionStringOperation, ) from ravendb.documents.operations.etl.configuration import RavenConnectionString -from ravendb.documents.operations.etl.olap import OlapConnectionString +from ravendb.documents.operations.etl.elastic_search.connection import ( + ElasticSearchConnectionString, + Authentication, + BasicAuthentication, + ApiKeyAuthentication, + CertificateAuthentication, +) +from ravendb.documents.operations.etl.olap.connection import OlapConnectionString +from ravendb.documents.operations.etl.queue.connection import QueueConnectionString, QueueBrokerType +from ravendb.documents.operations.etl.queue.amazon_sqs_connection_settings import ( + AmazonSqsConnectionSettings, + AmazonSqsCredentials, +) +from ravendb.documents.operations.etl.queue.azure_queue_storage_connection_settings import ( + AzureQueueStorageConnectionSettings, + EntraId, + Passwordless, +) +from ravendb.documents.operations.etl.queue.kafka_connection_settings import KafkaConnectionSettings +from ravendb.documents.operations.etl.queue.rabbit_mq_connection_settings import RabbitMqConnectionSettings +from ravendb.documents.operations.etl.snowflake.connection import SnowflakeConnectionString from ravendb.documents.operations.etl.sql import SqlConnectionString from ravendb.serverwide.server_operation_executor import ConnectionStringType from ravendb.tests.test_base import TestBase @@ -18,55 +52,975 @@ class TestConnectionString(TestBase): def setUp(self): super().setUp() - def test_can_create_get_and_delete_connection_strings(self): - raven_connection_string_1 = RavenConnectionString("r1", self.store.database, self.store.urls) - sql_connection_string_1 = SqlConnectionString("s1", "test", "MySql.Data.MySqlClient") - olap_connection_string_1 = OlapConnectionString("o1", ftp_settings=FtpSettings(url=self.store.urls[0])) + def test_full_lifecycle_for_all_connection_string_types(self): + # Initialize all connection string types + raven_connection_string = RavenConnectionString("raven1", self.store.database, self.store.urls) + sql_connection_string = SqlConnectionString("sql1", "test", "MySql.Data.MySqlClient") + olap_connection_string = OlapConnectionString("olap1", ftp_settings=FtpSettings(url=self.store.urls[0])) + ai_connection_string = AiConnectionString( + "ai1", + "test-identifier", + openai_settings=OpenAiSettings(api_key="test-key", endpoint="https://api.openai.com", model="gpt-4"), + model_type=AiModelType.CHAT, + ) + elastic_search_connection_string = ElasticSearchConnectionString("elastic1", nodes=["http://localhost:9200"]) + kafka_settings = KafkaConnectionSettings(bootstrap_servers="localhost:9092") + queue_connection_string = QueueConnectionString("queue1", QueueBrokerType.KAFKA, kafka_settings=kafka_settings) + snowflake_connection_string = SnowflakeConnectionString( + "snowflake1", "account=myaccount;user=myuser;password=mypassword" + ) + + # Test full lifecycle for RavenConnectionString + # 1. Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(raven_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) - put_result: PutConnectionStringResult = self.store.maintenance.send( - PutConnectionStringOperation(raven_connection_string_1) + # 2. Get and Assert + raven_get_result = self.store.maintenance.send( + GetConnectionStringsOperation("raven1", ConnectionStringType.RAVEN) ) + self.assertIn("raven1", raven_get_result.raven_connection_strings) + self.assertEqual(1, len(raven_get_result.raven_connection_strings)) + + # 3. Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(raven_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # 4. Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("raven1", ConnectionStringType.RAVEN)) + self.assertIsNone(after_delete.raven_connection_strings) + + # Test full lifecycle for SqlConnectionString + # 1. Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(sql_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # 2. Get and Assert + sql_get_result = self.store.maintenance.send(GetConnectionStringsOperation("sql1", ConnectionStringType.SQL)) + self.assertIn("sql1", sql_get_result.sql_connection_strings) + self.assertEqual(1, len(sql_get_result.sql_connection_strings)) + + # 3. Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(sql_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # 4. Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("sql1", ConnectionStringType.SQL)) + self.assertIsNone(after_delete.sql_connection_strings) + + # Test full lifecycle for OlapConnectionString + # 1. Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(olap_connection_string)) self.assertGreater(put_result.raft_command_index, 0) - put_result: PutConnectionStringResult = self.store.maintenance.send( - PutConnectionStringOperation(sql_connection_string_1) + # 2. Get and Assert + olap_get_result = self.store.maintenance.send(GetConnectionStringsOperation("olap1", ConnectionStringType.OLAP)) + self.assertIn("olap1", olap_get_result.olap_connection_strings) + self.assertEqual(1, len(olap_get_result.olap_connection_strings)) + + # 3. Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(olap_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # 4. Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("olap1", ConnectionStringType.OLAP)) + self.assertIsNone(after_delete.olap_connection_strings) + # Test full lifecycle for ElasticSearchConnectionString + # 1. Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(elastic_search_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # 2. Get and Assert + elastic_get_result = self.store.maintenance.send( + GetConnectionStringsOperation("elastic1", ConnectionStringType.ELASTIC_SEARCH) ) + self.assertIn("elastic1", elastic_get_result.elastic_search_connection_strings) + self.assertEqual(1, len(elastic_get_result.elastic_search_connection_strings)) + + # 3. Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(elastic_search_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # 4. Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("elastic1", ConnectionStringType.ELASTIC_SEARCH) + ) + self.assertIsNone(after_delete.elastic_search_connection_strings) + + # Test full lifecycle for QueueConnectionString + # 1. Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(queue_connection_string)) self.assertGreater(put_result.raft_command_index, 0) - put_result: PutConnectionStringResult = self.store.maintenance.send( - PutConnectionStringOperation(olap_connection_string_1) + # 2. Get and Assert + queue_get_result = self.store.maintenance.send( + GetConnectionStringsOperation("queue1", ConnectionStringType.QUEUE) ) + self.assertIn("queue1", queue_get_result.queue_connection_strings) + self.assertEqual(1, len(queue_get_result.queue_connection_strings)) + + # 3. Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(queue_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # 4. Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("queue1", ConnectionStringType.QUEUE)) + self.assertIsNone(after_delete.queue_connection_strings) + + # Test full lifecycle for SnowflakeConnectionString + # 1. Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(snowflake_connection_string)) self.assertGreater(put_result.raft_command_index, 0) - connection_strings = self.store.maintenance.send(GetConnectionStringsOperation()) + # 2. Get and Assert + snowflake_get_result = self.store.maintenance.send( + GetConnectionStringsOperation("snowflake1", ConnectionStringType.SNOWFLAKE) + ) + self.assertIn("snowflake1", snowflake_get_result.snowflake_connection_strings) + self.assertEqual(1, len(snowflake_get_result.snowflake_connection_strings)) - self.assertIn("r1", connection_strings.raven_connection_strings) - self.assertEqual(1, len(connection_strings.raven_connection_strings)) + # 3. Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(snowflake_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) - self.assertIn("s1", connection_strings.sql_connection_strings) - self.assertEqual(1, len(connection_strings.sql_connection_strings)) + # 4. Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("snowflake1", ConnectionStringType.SNOWFLAKE) + ) + self.assertIsNone(after_delete.snowflake_connection_strings) - self.assertIn("o1", connection_strings.olap_connection_strings) - self.assertEqual(1, len(connection_strings.olap_connection_strings)) + # Test full lifecycle for AiConnectionString + # 1. Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) - raven_only = self.store.maintenance.send(GetConnectionStringsOperation("r1", ConnectionStringType.RAVEN)) - self.assertIn("r1", raven_only.raven_connection_strings) - self.assertEqual(1, len(raven_only.raven_connection_strings)) - self.assertIsNone(raven_only.sql_connection_strings) + # 2. Get and Assert + ai_get_result = self.store.maintenance.send(GetConnectionStringsOperation("ai1", ConnectionStringType.AI)) + self.assertIn("ai1", ai_get_result.ai_connection_strings) + self.assertEqual(1, len(ai_get_result.ai_connection_strings)) - sql_only = self.store.maintenance.send(GetConnectionStringsOperation("s1", ConnectionStringType.SQL)) - self.assertIn("s1", sql_only.sql_connection_strings) - self.assertEqual(1, len(sql_only.sql_connection_strings)) - self.assertIsNone(sql_only.raven_connection_strings) + # 3. Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) - olap_only = self.store.maintenance.send(GetConnectionStringsOperation("o1", ConnectionStringType.OLAP)) - self.assertIn("o1", olap_only.olap_connection_strings) - self.assertEqual(1, len(olap_only.olap_connection_strings)) - self.assertIsNone(olap_only.raven_connection_strings) + # 4. Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("ai1", ConnectionStringType.AI)) + self.assertIsNone(after_delete.ai_connection_strings) + + def test_all_possible_fields_for_each_connection_string_type(self): + # Test RavenConnectionString with all possible fields + raven_connection_string = RavenConnectionString( + name="raven_all_fields", database=self.store.database, topology_discovery_urls=self.store.urls + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(raven_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) - remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(sql_connection_string_1)) + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("raven_all_fields", ConnectionStringType.RAVEN) + ) + self.assertIn("raven_all_fields", get_result.raven_connection_strings) + retrieved = get_result.raven_connection_strings["raven_all_fields"] + # Assert that all fields remain unchanged + self.assertEqual(raven_connection_string.name, retrieved.name) + self.assertEqual(raven_connection_string.database, retrieved.database) + self.assertEqual(raven_connection_string.topology_discovery_urls, retrieved.topology_discovery_urls) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(raven_connection_string)) self.assertGreater(remove_result.raft_command_index, 0) - after_delete = self.store.maintenance.send(GetConnectionStringsOperation("s1", ConnectionStringType.SQL)) + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("raven_all_fields", ConnectionStringType.RAVEN) + ) self.assertIsNone(after_delete.raven_connection_strings) + + # Test SqlConnectionString with all possible fields + sql_connection_string = SqlConnectionString( + name="sql_all_fields", + connection_string="Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;", + factory_name="System.Data.SqlClient", + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(sql_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("sql_all_fields", ConnectionStringType.SQL) + ) + self.assertIn("sql_all_fields", get_result.sql_connection_strings) + retrieved = get_result.sql_connection_strings["sql_all_fields"] + # Assert that all fields remain unchanged + self.assertEqual(sql_connection_string.name, retrieved.name) + self.assertEqual(sql_connection_string.connection_string, retrieved.connection_string) + self.assertEqual(sql_connection_string.factory_name, retrieved.factory_name) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(sql_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("sql_all_fields", ConnectionStringType.SQL) + ) self.assertIsNone(after_delete.sql_connection_strings) + + # Test OlapConnectionString with all possible fields + backup_script = GetBackupConfigurationScript(exec="test.exe", arguments="--test", timeout_in_ms=5000) + local_settings = LocalSettings( + disabled=False, get_backup_configuration_script=backup_script, folder_path="/path/to/folder" + ) + s3_settings = S3Settings( + disabled=False, + get_backup_configuration_script=backup_script, + aws_access_key="access_key", + aws_secret_key="secret_key", + aws_session_token="session_token", + aws_region_name="us-east-1", + remote_folder_name="remote_folder", + bucket_name="bucket", + custom_server_url="https://s3.custom.com", + force_path_style=True, + ) + azure_settings = AzureSettings( + disabled=False, + get_backup_configuration_script=backup_script, + storage_container="container", + remote_folder_name="remote_folder", + account_name="account", + account_key="key", + sas_token="sas_token", + ) + glacier_settings = GlacierSettings( + disabled=False, + get_backup_configuration_script=backup_script, + aws_access_key="access_key", + aws_secret_key="secret_key", + aws_session_token="session_token", + aws_region_name="us-east-1", + remote_folder_name="remote_folder", + vault_name="vault", + ) + ftp_settings = FtpSettings( + disabled=False, + get_backup_configuration_script=backup_script, + url="ftp://example.com", + user_name="user", + password="password", + certificate_as_base64="base64cert", + ) + + olap_connection_string = OlapConnectionString( + name="olap_all_fields", + local_settings=local_settings, + s3_settings=s3_settings, + azure_settings=azure_settings, + glacier_settings=glacier_settings, + ftp_settings=ftp_settings, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(olap_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("olap_all_fields", ConnectionStringType.OLAP) + ) + self.assertIn("olap_all_fields", get_result.olap_connection_strings) + retrieved = get_result.olap_connection_strings["olap_all_fields"] + # Assert that all fields remain unchanged + self.assertEqual(olap_connection_string.name, retrieved.name) + + # Verify local_settings + self.assertEqual(olap_connection_string.local_settings.disabled, retrieved.local_settings.disabled) + self.assertEqual(olap_connection_string.local_settings.folder_path, retrieved.local_settings.folder_path) + self.assertEqual( + olap_connection_string.local_settings.get_backup_configuration_script.exec, + retrieved.local_settings.get_backup_configuration_script.exec, + ) + self.assertEqual( + olap_connection_string.local_settings.get_backup_configuration_script.arguments, + retrieved.local_settings.get_backup_configuration_script.arguments, + ) + self.assertEqual( + olap_connection_string.local_settings.get_backup_configuration_script.timeout_in_ms, + retrieved.local_settings.get_backup_configuration_script.timeout_in_ms, + ) + + # Verify s3_settings + self.assertEqual(olap_connection_string.s3_settings.disabled, retrieved.s3_settings.disabled) + self.assertEqual(olap_connection_string.s3_settings.aws_access_key, retrieved.s3_settings.aws_access_key) + self.assertEqual(olap_connection_string.s3_settings.aws_secret_key, retrieved.s3_settings.aws_secret_key) + self.assertEqual(olap_connection_string.s3_settings.aws_session_token, retrieved.s3_settings.aws_session_token) + self.assertEqual(olap_connection_string.s3_settings.aws_region_name, retrieved.s3_settings.aws_region_name) + self.assertEqual( + olap_connection_string.s3_settings.remote_folder_name, retrieved.s3_settings.remote_folder_name + ) + self.assertEqual(olap_connection_string.s3_settings.bucket_name, retrieved.s3_settings.bucket_name) + self.assertEqual(olap_connection_string.s3_settings.custom_server_url, retrieved.s3_settings.custom_server_url) + self.assertEqual(olap_connection_string.s3_settings.force_path_style, retrieved.s3_settings.force_path_style) + self.assertEqual( + olap_connection_string.s3_settings.get_backup_configuration_script.exec, + retrieved.s3_settings.get_backup_configuration_script.exec, + ) + self.assertEqual( + olap_connection_string.s3_settings.get_backup_configuration_script.arguments, + retrieved.s3_settings.get_backup_configuration_script.arguments, + ) + self.assertEqual( + olap_connection_string.s3_settings.get_backup_configuration_script.timeout_in_ms, + retrieved.s3_settings.get_backup_configuration_script.timeout_in_ms, + ) + + # Verify azure_settings + self.assertEqual(olap_connection_string.azure_settings.disabled, retrieved.azure_settings.disabled) + self.assertEqual( + olap_connection_string.azure_settings.storage_container, retrieved.azure_settings.storage_container + ) + self.assertEqual( + olap_connection_string.azure_settings.remote_folder_name, retrieved.azure_settings.remote_folder_name + ) + self.assertEqual(olap_connection_string.azure_settings.account_name, retrieved.azure_settings.account_name) + self.assertEqual(olap_connection_string.azure_settings.account_key, retrieved.azure_settings.account_key) + self.assertEqual(olap_connection_string.azure_settings.sas_token, retrieved.azure_settings.sas_token) + self.assertEqual( + olap_connection_string.azure_settings.get_backup_configuration_script.exec, + retrieved.azure_settings.get_backup_configuration_script.exec, + ) + self.assertEqual( + olap_connection_string.azure_settings.get_backup_configuration_script.arguments, + retrieved.azure_settings.get_backup_configuration_script.arguments, + ) + self.assertEqual( + olap_connection_string.azure_settings.get_backup_configuration_script.timeout_in_ms, + retrieved.azure_settings.get_backup_configuration_script.timeout_in_ms, + ) + + # Verify glacier_settings + self.assertEqual(olap_connection_string.glacier_settings.disabled, retrieved.glacier_settings.disabled) + self.assertEqual( + olap_connection_string.glacier_settings.aws_access_key, retrieved.glacier_settings.aws_access_key + ) + self.assertEqual( + olap_connection_string.glacier_settings.aws_secret_key, retrieved.glacier_settings.aws_secret_key + ) + self.assertEqual( + olap_connection_string.glacier_settings.aws_session_token, retrieved.glacier_settings.aws_session_token + ) + self.assertEqual( + olap_connection_string.glacier_settings.aws_region_name, retrieved.glacier_settings.aws_region_name + ) + self.assertEqual( + olap_connection_string.glacier_settings.remote_folder_name, retrieved.glacier_settings.remote_folder_name + ) + self.assertEqual(olap_connection_string.glacier_settings.vault_name, retrieved.glacier_settings.vault_name) + self.assertEqual( + olap_connection_string.glacier_settings.get_backup_configuration_script.exec, + retrieved.glacier_settings.get_backup_configuration_script.exec, + ) + self.assertEqual( + olap_connection_string.glacier_settings.get_backup_configuration_script.arguments, + retrieved.glacier_settings.get_backup_configuration_script.arguments, + ) + self.assertEqual( + olap_connection_string.glacier_settings.get_backup_configuration_script.timeout_in_ms, + retrieved.glacier_settings.get_backup_configuration_script.timeout_in_ms, + ) + + # Verify ftp_settings + self.assertEqual(olap_connection_string.ftp_settings.disabled, retrieved.ftp_settings.disabled) + self.assertEqual(olap_connection_string.ftp_settings.url, retrieved.ftp_settings.url) + self.assertEqual(olap_connection_string.ftp_settings.user_name, retrieved.ftp_settings.user_name) + self.assertEqual(olap_connection_string.ftp_settings.password, retrieved.ftp_settings.password) + self.assertEqual( + olap_connection_string.ftp_settings.certificate_as_base64, retrieved.ftp_settings.certificate_as_base64 + ) + self.assertEqual( + olap_connection_string.ftp_settings.get_backup_configuration_script.exec, + retrieved.ftp_settings.get_backup_configuration_script.exec, + ) + self.assertEqual( + olap_connection_string.ftp_settings.get_backup_configuration_script.arguments, + retrieved.ftp_settings.get_backup_configuration_script.arguments, + ) + self.assertEqual( + olap_connection_string.ftp_settings.get_backup_configuration_script.timeout_in_ms, + retrieved.ftp_settings.get_backup_configuration_script.timeout_in_ms, + ) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(olap_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("olap_all_fields", ConnectionStringType.OLAP) + ) + self.assertIsNone(after_delete.olap_connection_strings) + + # Test AiConnectionString with each possible setting type separately + + # 1. Test with OpenAI settings - CHAT model type (without temperature and embeddings_max_concurrent_batches) + openai_chat_settings = OpenAiSettings( + api_key="openai_key", + endpoint="https://api.openai.com", + model="gpt-4", + organization_id="org_id", + project_id="proj_id", + dimensions=1536, + ) + + ai_openai_chat_connection_string = AiConnectionString( + name="ai_openai_chat", + identifier="test-ai-openai-chat", + openai_settings=openai_chat_settings, + model_type=AiModelType.CHAT, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_openai_chat_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("ai_openai_chat", ConnectionStringType.AI) + ) + self.assertIn("ai_openai_chat", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_openai_chat"] + self.assertEqual(ai_openai_chat_connection_string.name, retrieved.name) + self.assertEqual(ai_openai_chat_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.openai_settings) + self.assertEqual(openai_chat_settings.api_key, retrieved.openai_settings.api_key) + self.assertEqual(openai_chat_settings.endpoint, retrieved.openai_settings.endpoint) + self.assertEqual(openai_chat_settings.model, retrieved.openai_settings.model) + self.assertEqual(openai_chat_settings.organization_id, retrieved.openai_settings.organization_id) + self.assertEqual(openai_chat_settings.project_id, retrieved.openai_settings.project_id) + self.assertEqual(openai_chat_settings.dimensions, retrieved.openai_settings.dimensions) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_openai_chat_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("ai_openai_chat", ConnectionStringType.AI) + ) + self.assertIsNone(after_delete.ai_connection_strings) + + # 1.1 Test with OpenAI settings - TEXT_EMBEDDINGS model type (with temperature and embeddings_max_concurrent_batches) + openai_embeddings_settings = OpenAiSettings( + api_key="openai_key", + endpoint="https://api.openai.com", + model="text-embedding-ada-002", + organization_id="org_id", + project_id="proj_id", + dimensions=1536, + temperature=0.7, + ) + openai_embeddings_settings.embeddings_max_concurrent_batches = 5 + + ai_openai_embeddings_connection_string = AiConnectionString( + name="ai_openai_embeddings", + identifier="test-ai-openai-embeddings", + openai_settings=openai_embeddings_settings, + model_type=AiModelType.TEXT_EMBEDDINGS, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_openai_embeddings_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("ai_openai_embeddings", ConnectionStringType.AI) + ) + self.assertIn("ai_openai_embeddings", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_openai_embeddings"] + self.assertEqual(ai_openai_embeddings_connection_string.name, retrieved.name) + self.assertEqual(ai_openai_embeddings_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.openai_settings) + self.assertEqual(openai_embeddings_settings.api_key, retrieved.openai_settings.api_key) + self.assertEqual(openai_embeddings_settings.endpoint, retrieved.openai_settings.endpoint) + self.assertEqual(openai_embeddings_settings.model, retrieved.openai_settings.model) + self.assertEqual(openai_embeddings_settings.organization_id, retrieved.openai_settings.organization_id) + self.assertEqual(openai_embeddings_settings.project_id, retrieved.openai_settings.project_id) + self.assertEqual(openai_embeddings_settings.dimensions, retrieved.openai_settings.dimensions) + + # Delete + remove_result = self.store.maintenance.send( + RemoveConnectionStringOperation(ai_openai_embeddings_connection_string) + ) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("ai_openai_embeddings", ConnectionStringType.AI) + ) + self.assertIsNone(after_delete.ai_connection_strings) + + # 2. Test with Azure OpenAI settings + azure_openai_settings = AzureOpenAiSettings( + api_key="azure_key", + endpoint="https://azure-openai.com", + model="gpt-4", + deployment_name="deployment1", + dimensions=1536, + temperature=0.7, + ) + azure_openai_settings.embeddings_max_concurrent_batches = 5 + + ai_azure_openai_connection_string = AiConnectionString( + name="ai_azure_openai", + identifier="test-ai-azure-openai", + azure_openai_settings=azure_openai_settings, + model_type=AiModelType.CHAT, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_azure_openai_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("ai_azure_openai", ConnectionStringType.AI) + ) + self.assertIn("ai_azure_openai", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_azure_openai"] + self.assertEqual(ai_azure_openai_connection_string.name, retrieved.name) + self.assertEqual(ai_azure_openai_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.azure_openai_settings) + self.assertEqual(azure_openai_settings.api_key, retrieved.azure_openai_settings.api_key) + self.assertEqual(azure_openai_settings.endpoint, retrieved.azure_openai_settings.endpoint) + self.assertEqual(azure_openai_settings.model, retrieved.azure_openai_settings.model) + self.assertEqual(azure_openai_settings.deployment_name, retrieved.azure_openai_settings.deployment_name) + self.assertEqual(azure_openai_settings.dimensions, retrieved.azure_openai_settings.dimensions) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_azure_openai_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("ai_azure_openai", ConnectionStringType.AI) + ) + self.assertIsNone(after_delete.ai_connection_strings) + + # 3. Test with Ollama settings + ollama_settings = OllamaSettings( + uri="http://localhost:11434", + model="llama2", + think=True, + temperature=0.8, + embeddings_max_concurrent_batches=5, + ) + ollama_settings.think = True + ollama_settings.temperature = 0.8 + ollama_settings.embeddings_max_concurrent_batches = 5 + + ai_ollama_connection_string = AiConnectionString( + name="ai_ollama", identifier="test-ai-ollama", ollama_settings=ollama_settings, model_type=AiModelType.CHAT + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_ollama_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send(GetConnectionStringsOperation("ai_ollama", ConnectionStringType.AI)) + self.assertIn("ai_ollama", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_ollama"] + self.assertEqual(ai_ollama_connection_string.name, retrieved.name) + self.assertEqual(ai_ollama_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.ollama_settings) + self.assertEqual(ollama_settings.uri, retrieved.ollama_settings.uri) + self.assertEqual(ollama_settings.model, retrieved.ollama_settings.model) + self.assertEqual(ollama_settings.think, retrieved.ollama_settings.think) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_ollama_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("ai_ollama", ConnectionStringType.AI)) + self.assertIsNone(after_delete.ai_connection_strings) + + # 4. Test with Embedded settings + embedded_settings = EmbeddedSettings() + embedded_settings.embeddings_max_concurrent_batches = 5 + + ai_embedded_connection_string = AiConnectionString( + name="ai_embedded", + identifier="test-ai-embedded", + embedded_settings=embedded_settings, + model_type=AiModelType.CHAT, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_embedded_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send(GetConnectionStringsOperation("ai_embedded", ConnectionStringType.AI)) + self.assertIn("ai_embedded", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_embedded"] + self.assertEqual(ai_embedded_connection_string.name, retrieved.name) + self.assertEqual(ai_embedded_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.embedded_settings) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_embedded_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("ai_embedded", ConnectionStringType.AI) + ) + self.assertIsNone(after_delete.ai_connection_strings) + + # 5. Test with Google settings + google_settings = GoogleSettings( + model="gemini-pro", api_key="google_key", ai_version=GoogleAiVersion.V1, dimensions=768 + ) + google_settings.embeddings_max_concurrent_batches = 5 + + ai_google_connection_string = AiConnectionString( + name="ai_google", identifier="test-ai-google", google_settings=google_settings, model_type=AiModelType.CHAT + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_google_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send(GetConnectionStringsOperation("ai_google", ConnectionStringType.AI)) + self.assertIn("ai_google", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_google"] + self.assertEqual(ai_google_connection_string.name, retrieved.name) + self.assertEqual(ai_google_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.google_settings) + self.assertEqual(google_settings.model, retrieved.google_settings.model) + self.assertEqual(google_settings.api_key, retrieved.google_settings.api_key) + self.assertEqual(google_settings.ai_version, retrieved.google_settings.ai_version) + self.assertEqual(google_settings.dimensions, retrieved.google_settings.dimensions) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_google_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("ai_google", ConnectionStringType.AI)) + self.assertIsNone(after_delete.ai_connection_strings) + + # 6. Test with HuggingFace settings + huggingface_settings = HuggingFaceSettings( + api_key="hf_key", + model="mistral-7b", + endpoint="https://api-inference.huggingface.co/models/mistralai/Mistral-7B-v0.1", + ) + huggingface_settings.embeddings_max_concurrent_batches = 5 + + ai_huggingface_connection_string = AiConnectionString( + name="ai_huggingface", + identifier="test-ai-huggingface", + huggingface_settings=huggingface_settings, + model_type=AiModelType.CHAT, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_huggingface_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("ai_huggingface", ConnectionStringType.AI) + ) + self.assertIn("ai_huggingface", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_huggingface"] + self.assertEqual(ai_huggingface_connection_string.name, retrieved.name) + self.assertEqual(ai_huggingface_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.huggingface_settings) + self.assertEqual(huggingface_settings.api_key, retrieved.huggingface_settings.api_key) + self.assertEqual(huggingface_settings.model, retrieved.huggingface_settings.model) + self.assertEqual(huggingface_settings.endpoint, retrieved.huggingface_settings.endpoint) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_huggingface_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("ai_huggingface", ConnectionStringType.AI) + ) + self.assertIsNone(after_delete.ai_connection_strings) + + # 7. Test with MistralAI settings + mistral_ai_settings = MistralAiSettings( + api_key="mistral_key", model="mistral-medium", endpoint="https://api.mistral.ai" + ) + mistral_ai_settings.embeddings_max_concurrent_batches = 5 + + ai_mistral_connection_string = AiConnectionString( + name="ai_mistral", + identifier="test-ai-mistral", + mistral_ai_settings=mistral_ai_settings, + model_type=AiModelType.CHAT, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(ai_mistral_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send(GetConnectionStringsOperation("ai_mistral", ConnectionStringType.AI)) + self.assertIn("ai_mistral", get_result.ai_connection_strings) + retrieved = get_result.ai_connection_strings["ai_mistral"] + self.assertEqual(ai_mistral_connection_string.name, retrieved.name) + self.assertEqual(ai_mistral_connection_string.identifier, retrieved.identifier) + self.assertIsNotNone(retrieved.mistral_ai_settings) + self.assertEqual(mistral_ai_settings.api_key, retrieved.mistral_ai_settings.api_key) + self.assertEqual(mistral_ai_settings.model, retrieved.mistral_ai_settings.model) + self.assertEqual(mistral_ai_settings.endpoint, retrieved.mistral_ai_settings.endpoint) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(ai_mistral_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send(GetConnectionStringsOperation("ai_mistral", ConnectionStringType.AI)) + self.assertIsNone(after_delete.ai_connection_strings) + + # Test ElasticSearchConnectionString with all possible fields + api_key_auth = ApiKeyAuthentication( + api_key_id="api_key_id", api_key="api_key", encoded_api_key="encoded_api_key" + ) + + basic_auth = BasicAuthentication(username="elastic", password="password") + + cert_auth = CertificateAuthentication(certificates_base64=["base64cert1", "base64cert2"]) + + auth = Authentication(api_key=api_key_auth, basic=basic_auth, certificate=cert_auth) + + elastic_search_connection_string = ElasticSearchConnectionString( + name="elastic_all_fields", nodes=["http://localhost:9200", "http://localhost:9201"], authentication=auth + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(elastic_search_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("elastic_all_fields", ConnectionStringType.ELASTIC_SEARCH) + ) + self.assertIn("elastic_all_fields", get_result.elastic_search_connection_strings) + retrieved = get_result.elastic_search_connection_strings["elastic_all_fields"] + # Assert that all fields remain unchanged + self.assertEqual(elastic_search_connection_string.name, retrieved.name) + self.assertEqual(elastic_search_connection_string.nodes, retrieved.nodes) + + # Verify authentication details + # API Key Authentication + self.assertEqual( + elastic_search_connection_string.authentication.api_key.api_key_id, + retrieved.authentication.api_key.api_key_id, + ) + self.assertEqual( + elastic_search_connection_string.authentication.api_key.api_key, retrieved.authentication.api_key.api_key + ) + self.assertEqual( + elastic_search_connection_string.authentication.api_key.encoded_api_key, + retrieved.authentication.api_key.encoded_api_key, + ) + + # Basic Authentication + self.assertEqual( + elastic_search_connection_string.authentication.basic.username, retrieved.authentication.basic.username + ) + self.assertEqual( + elastic_search_connection_string.authentication.basic.password, retrieved.authentication.basic.password + ) + + # Certificate Authentication + self.assertEqual( + elastic_search_connection_string.authentication.certificate.certificates_base64, + retrieved.authentication.certificate.certificates_base64, + ) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(elastic_search_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("elastic_all_fields", ConnectionStringType.ELASTIC_SEARCH) + ) + self.assertIsNone(after_delete.elastic_search_connection_strings) + + # Test QueueConnectionString with all possible fields + kafka_settings = KafkaConnectionSettings( + bootstrap_servers="localhost:9092", + connection_options={"acks": "all", "batch.size": "16384"}, + use_raven_certificate=True, + ) + + rabbit_mq_settings = RabbitMqConnectionSettings(connection_string="amqp://guest:guest@localhost:5672/") + + entra_id = EntraId( + storage_account_name="storage_account", + tenant_id="tenant_id", + client_id="client_id", + client_secret="client_secret", + ) + + passwordless = Passwordless(storage_account_name="storage_account") + + azure_queue_storage_settings = AzureQueueStorageConnectionSettings( + entra_id=entra_id, + connection_string="DefaultEndpointsProtocol=https;AccountName=account;AccountKey=key;EndpointSuffix=core.windows.net", + passwordless=passwordless, + ) + + amazon_sqs_credentials = AmazonSqsCredentials( + access_key="access_key", secret_key="secret_key", region_name="us-east-1" + ) + + amazon_sqs_settings = AmazonSqsConnectionSettings( + basic=amazon_sqs_credentials, passwordless=True, use_emulator=True + ) + + queue_connection_string = QueueConnectionString( + name="queue_all_fields", + broker_type=QueueBrokerType.KAFKA, + kafka_settings=kafka_settings, + rabbit_mq_settings=rabbit_mq_settings, + azure_queue_storage_settings=azure_queue_storage_settings, + amazon_sqs_settings=amazon_sqs_settings, + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(queue_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("queue_all_fields", ConnectionStringType.QUEUE) + ) + self.assertIn("queue_all_fields", get_result.queue_connection_strings) + retrieved = get_result.queue_connection_strings["queue_all_fields"] + # Assert that all fields remain unchanged + self.assertEqual(queue_connection_string.name, retrieved.name) + self.assertEqual(queue_connection_string.broker_type, retrieved.broker_type) + + # Verify Kafka settings + self.assertEqual( + queue_connection_string.kafka_settings.bootstrap_servers, retrieved.kafka_settings.bootstrap_servers + ) + self.assertEqual( + queue_connection_string.kafka_settings.connection_options, retrieved.kafka_settings.connection_options + ) + self.assertEqual( + queue_connection_string.kafka_settings.use_raven_certificate, retrieved.kafka_settings.use_raven_certificate + ) + + # Verify RabbitMQ settings + self.assertEqual( + queue_connection_string.rabbit_mq_settings.connection_string, retrieved.rabbit_mq_settings.connection_string + ) + + # Verify Azure Queue Storage settings + # EntraId + self.assertEqual( + queue_connection_string.azure_queue_storage_settings.entra_id.storage_account_name, + retrieved.azure_queue_storage_settings.entra_id.storage_account_name, + ) + self.assertEqual( + queue_connection_string.azure_queue_storage_settings.entra_id.tenant_id, + retrieved.azure_queue_storage_settings.entra_id.tenant_id, + ) + self.assertEqual( + queue_connection_string.azure_queue_storage_settings.entra_id.client_id, + retrieved.azure_queue_storage_settings.entra_id.client_id, + ) + self.assertEqual( + queue_connection_string.azure_queue_storage_settings.entra_id.client_secret, + retrieved.azure_queue_storage_settings.entra_id.client_secret, + ) + + # Connection string + self.assertEqual( + queue_connection_string.azure_queue_storage_settings.connection_string, + retrieved.azure_queue_storage_settings.connection_string, + ) + + # Passwordless + self.assertEqual( + queue_connection_string.azure_queue_storage_settings.passwordless.storage_account_name, + retrieved.azure_queue_storage_settings.passwordless.storage_account_name, + ) + + # Verify Amazon SQS settings + # Basic credentials + self.assertEqual( + queue_connection_string.amazon_sqs_settings.basic.access_key, retrieved.amazon_sqs_settings.basic.access_key + ) + self.assertEqual( + queue_connection_string.amazon_sqs_settings.basic.secret_key, retrieved.amazon_sqs_settings.basic.secret_key + ) + self.assertEqual( + queue_connection_string.amazon_sqs_settings.basic.region_name, + retrieved.amazon_sqs_settings.basic.region_name, + ) + + # Other settings + self.assertEqual( + queue_connection_string.amazon_sqs_settings.passwordless, retrieved.amazon_sqs_settings.passwordless + ) + self.assertEqual( + queue_connection_string.amazon_sqs_settings.use_emulator, retrieved.amazon_sqs_settings.use_emulator + ) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(queue_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("queue_all_fields", ConnectionStringType.QUEUE) + ) + self.assertIsNone(after_delete.queue_connection_strings) + + # Test SnowflakeConnectionString with all possible fields + snowflake_connection_string = SnowflakeConnectionString( + name="snowflake_all_fields", + connection_string="account=myaccount;user=myuser;password=mypassword;warehouse=mywarehouse;database=mydatabase;schema=myschema", + ) + + # Create + put_result = self.store.maintenance.send(PutConnectionStringOperation(snowflake_connection_string)) + self.assertGreater(put_result.raft_command_index, 0) + + # Get and Assert + get_result = self.store.maintenance.send( + GetConnectionStringsOperation("snowflake_all_fields", ConnectionStringType.SNOWFLAKE) + ) + self.assertIn("snowflake_all_fields", get_result.snowflake_connection_strings) + retrieved = get_result.snowflake_connection_strings["snowflake_all_fields"] + # Assert that all fields remain unchanged + self.assertEqual(snowflake_connection_string.name, retrieved.name) + self.assertEqual(snowflake_connection_string.connection_string, retrieved.connection_string) + + # Delete + remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(snowflake_connection_string)) + self.assertGreater(remove_result.raft_command_index, 0) + + # Get and Assert None + after_delete = self.store.maintenance.send( + GetConnectionStringsOperation("snowflake_all_fields", ConnectionStringType.SNOWFLAKE) + ) + self.assertIsNone(after_delete.snowflake_connection_strings)