Skip to content

Commit 40b7c87

Browse files
committed
RDBC-935 Add Connection String Operations
1 parent dc6b24e commit 40b7c87

9 files changed

Lines changed: 306 additions & 10 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import json
2+
from typing import Dict, Optional
3+
4+
import requests
5+
6+
from ravendb import RavenCommand, ServerNode
7+
from ravendb.documents.operations.definitions import MaintenanceOperation
8+
from ravendb.documents.operations.etl.configuration import RavenConnectionString
9+
from ravendb.documents.operations.etl.olap import OlapConnectionString
10+
from ravendb.documents.operations.etl.sql import SqlConnectionString
11+
from ravendb.serverwide.server_operation_executor import ConnectionStringType
12+
13+
14+
class GetConnectionStringsResult:
15+
def __init__(
16+
self,
17+
raven_connection_strings: Dict[str, RavenConnectionString] = None,
18+
sql_connection_strings: Dict[str, SqlConnectionString] = None,
19+
olap_connection_strings: Dict[str, OlapConnectionString] = None,
20+
):
21+
self.raven_connection_strings = raven_connection_strings
22+
self.sql_connection_strings = sql_connection_strings
23+
self.olap_connection_strings = olap_connection_strings
24+
25+
def to_json(self) -> Dict:
26+
return {
27+
"RavenConnectionStrings": self._raven_connection_strings,
28+
"SqlConnectionStrings": self._sql_connection_strings,
29+
"OlapConnectionStrings": self._olap_connection_strings,
30+
}
31+
32+
@classmethod
33+
def from_json(cls, json_dict: Dict) -> "GetConnectionStringsResult":
34+
return cls(
35+
raven_connection_strings=json_dict["RavenConnectionStrings"],
36+
sql_connection_strings=json_dict["SqlConnectionStrings"],
37+
olap_connection_strings=json_dict["OlapConnectionStrings"],
38+
)
39+
40+
41+
class GetConnectionStringsOperation(MaintenanceOperation[GetConnectionStringsResult]):
42+
def __init__(self, connection_string_name: str = None, connection_string_type: ConnectionStringType = None):
43+
self._connection_string_name = connection_string_name
44+
self._type = connection_string_type
45+
46+
def get_command(self, conventions: "DocumentConventions") -> "RavenCommand[GetConnectionStringsResult]":
47+
return self.GetConnectionStringsCommand(self._connection_string_name, self._type)
48+
49+
class GetConnectionStringsCommand(RavenCommand[GetConnectionStringsResult]):
50+
def __init__(self, connection_string_name: str = None, connection_string_type: ConnectionStringType = None):
51+
super().__init__(GetConnectionStringsResult)
52+
self._connection_string_name = connection_string_name
53+
self._type = connection_string_type
54+
55+
def is_read_request(self) -> bool:
56+
return True
57+
58+
def create_request(self, node: ServerNode) -> requests.Request:
59+
url = f"{node.url}/databases/{node.database}/admin/connection-strings"
60+
61+
if self._connection_string_name:
62+
url += f"?connectionStringName={self._connection_string_name}&type={self._type.value}"
63+
64+
request = requests.Request("GET")
65+
request.url = url
66+
67+
return request
68+
69+
def set_response(self, response: Optional[str], from_cache: bool) -> None:
70+
if response is None:
71+
self._throw_invalid_response()
72+
73+
self.result = GetConnectionStringsResult.from_json(json.loads(response))
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import json
2+
from typing import Dict
3+
4+
import requests
5+
6+
from ravendb import ConnectionString, RavenCommand, ServerNode, RaftCommand
7+
from ravendb.documents.conventions import DocumentConventions
8+
from ravendb.documents.operations.definitions import MaintenanceOperation
9+
from ravendb.documents.operations.etl.configuration import RavenConnectionString
10+
from ravendb.documents.operations.etl.olap import OlapConnectionString
11+
from ravendb.documents.operations.etl.sql import SqlConnectionString
12+
from ravendb.serverwide.server_operation_executor import ConnectionStringType
13+
from ravendb.util.util import RaftIdGenerator
14+
15+
16+
class PutConnectionStringResult:
17+
def __init__(self, raft_command_index: int = None):
18+
self.raft_command_index = raft_command_index
19+
20+
def to_json(self) -> Dict:
21+
return {"RaftCommandIndex": self.raft_command_index}
22+
23+
@classmethod
24+
def from_json(cls, json_dict: Dict) -> "PutConnectionStringResult":
25+
return cls(json_dict["RaftCommandIndex"])
26+
27+
28+
class PutConnectionStringOperation(MaintenanceOperation[PutConnectionStringResult]):
29+
def __init__(self, connection_string: ConnectionString = None):
30+
self._connection_string = connection_string
31+
32+
def get_command(self, conventions: "DocumentConventions") -> "RavenCommand[PutConnectionStringResult]":
33+
return self.PutConnectionStringCommand(conventions, self._connection_string)
34+
35+
class PutConnectionStringCommand(RavenCommand[PutConnectionStringResult], RaftCommand):
36+
def __init__(
37+
self, document_conventions: DocumentConventions = None, connection_string: ConnectionString = None
38+
):
39+
super().__init__(PutConnectionStringResult)
40+
41+
if connection_string is None:
42+
raise ValueError("Connection string cannot be None")
43+
44+
self._document_conventions = document_conventions
45+
self._connection_string = connection_string
46+
47+
def _to_data(self) -> Dict:
48+
if isinstance(self._connection_string, RavenConnectionString):
49+
return {
50+
"Name": self._connection_string.name,
51+
"Database": self._connection_string.database,
52+
"TopologyDiscoveryUrls": self._connection_string.topology_discovery_urls,
53+
"Type": ConnectionStringType.RAVEN,
54+
}
55+
56+
if isinstance(self._connection_string, SqlConnectionString):
57+
return {
58+
"Name": self._connection_string.name,
59+
"ConnectionString": self._connection_string.connection_string,
60+
"FactoryName": self._connection_string.factory_name,
61+
"Type": ConnectionStringType.SQL,
62+
}
63+
64+
if isinstance(self._connection_string, OlapConnectionString):
65+
return {
66+
"Name": self._connection_string.name,
67+
"LocalSettings": self._connection_string.local_settings,
68+
"S3Settings": self._connection_string.s3_settings,
69+
"AzureSettings": self._connection_string.azure_settings,
70+
"GlacierSettings": self._connection_string.glacier_settings,
71+
"GoogleCloudSettings": self._connection_string.google_cloud_settings,
72+
"FtpSettings": self._connection_string.ftp_settings,
73+
"Type": ConnectionStringType.OLAP,
74+
}
75+
76+
return None
77+
78+
def is_read_request(self) -> bool:
79+
return False
80+
81+
def create_request(self, node: ServerNode) -> requests.Request:
82+
url = f"{node.url}/databases/{node.database}/admin/connection-strings"
83+
84+
request = requests.Request("PUT")
85+
request.url = url
86+
request.data = self._to_data()
87+
88+
return request
89+
90+
def set_response(self, response: str, from_cache: bool) -> None:
91+
if response is None:
92+
self._throw_invalid_response()
93+
94+
self.result = PutConnectionStringResult.from_json(json.loads(response))
95+
96+
def get_raft_unique_request_id(self) -> str:
97+
return RaftIdGenerator.new_id()
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import json
2+
import urllib
3+
from typing import Dict
4+
5+
import requests
6+
7+
from ravendb import RavenCommand, RaftCommand, ConnectionString, ServerNode
8+
from ravendb.documents.operations.definitions import MaintenanceOperation
9+
from ravendb.util.util import RaftIdGenerator
10+
11+
12+
class RemoveConnectionStringResult:
13+
def __init__(self, raft_command_index: int = None):
14+
self.raft_command_index = raft_command_index
15+
16+
def to_json(self) -> Dict:
17+
return {"RaftCommandIndex": self.raft_command_index}
18+
19+
@classmethod
20+
def from_json(cls, json_dict: Dict) -> "PutConnectionStringResult":
21+
return cls(json_dict["RaftCommandIndex"])
22+
23+
24+
class RemoveConnectionStringOperation(MaintenanceOperation[RemoveConnectionStringResult]):
25+
def __init__(self, connection_string: ConnectionString = None):
26+
self._connection_string = connection_string
27+
28+
def get_command(self, conventions: "DocumentConventions") -> "RavenCommand[_T]":
29+
return self.RemoveConnectionStringCommand(self._connection_string)
30+
31+
class RemoveConnectionStringCommand(RavenCommand[RemoveConnectionStringResult], RaftCommand):
32+
def __init__(self, connection_string: ConnectionString = None):
33+
super().__init__(RemoveConnectionStringResult)
34+
self._connection_string = connection_string
35+
36+
def is_read_request(self) -> bool:
37+
return False
38+
39+
def create_request(self, node: ServerNode) -> requests.Request:
40+
url = f"{node.url}/databases/{node.database}/admin/connection-strings?connectionString={urllib.parse.quote(self._connection_string.name)}&type={self._connection_string.get_type}"
41+
42+
request = requests.Request("DELETE")
43+
request.url = url
44+
45+
return request
46+
47+
def set_response(self, response: str, from_cache: bool) -> None:
48+
if response is None:
49+
self._throw_invalid_response()
50+
51+
self.result = RemoveConnectionStringResult.from_json(json.loads(response))
52+
53+
def get_raft_unique_request_id(self) -> str:
54+
return RaftIdGenerator.new_id()

ravendb/documents/operations/connection_string/__init__.py

Whitespace-only changes.

ravendb/documents/operations/etl/configuration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Optional, Generic, TypeVar, List
22

33
from ravendb.documents.operations.connection_strings import ConnectionString
4-
import ravendb.serverwide
4+
import ravendb.serverwide.server_operation_executor
55

66
_T = TypeVar("_T")
77

@@ -14,7 +14,7 @@ def __init__(self, name: str, database: Optional[str] = None, topology_discovery
1414

1515
@property
1616
def get_type(self):
17-
return ravendb.serverwide.ConnectionStringType.RAVEN
17+
return ravendb.serverwide.server_operation_executor.ConnectionStringType.RAVEN.value
1818

1919

2020
# todo: implement

ravendb/documents/operations/etl/olap/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
FtpSettings,
1010
)
1111
from ravendb.documents.operations.connection_strings import ConnectionString
12-
import ravendb.serverwide
12+
import ravendb.serverwide.server_operation_executor
1313
from ravendb.documents.operations.etl.configuration import EtlConfiguration
1414

1515

@@ -34,7 +34,7 @@ def __init__(
3434

3535
@property
3636
def get_type(self):
37-
return ravendb.serverwide.ConnectionStringType.OLAP
37+
return ravendb.serverwide.server_operation_executor.ConnectionStringType.OLAP.value
3838

3939

4040
# todo: implement

ravendb/documents/operations/etl/sql/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Optional
22

33
from ravendb.documents.operations.connection_strings import ConnectionString
4-
import ravendb.serverwide
4+
import ravendb.serverwide.server_operation_executor
55
from ravendb.documents.operations.etl.configuration import EtlConfiguration
66

77

@@ -13,7 +13,7 @@ def __init__(self, name: str, connection_string: Optional[str] = None, factory_n
1313

1414
@property
1515
def get_type(self):
16-
return ravendb.serverwide.ConnectionStringType.SQL
16+
return ravendb.serverwide.server_operation_executor.ConnectionStringType.SQL.value
1717

1818

1919
# todo: implement

ravendb/serverwide/server_operation_executor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121

2222

2323
class ConnectionStringType(enum.Enum):
24-
NONE = "NONE"
25-
RAVEN = "RAVEN"
26-
SQL = "SQL"
27-
OLAP = "OLAP"
24+
NONE = "None"
25+
RAVEN = "Raven"
26+
SQL = "Sql"
27+
OLAP = "Olap"
2828

2929

3030
class ServerOperationExecutor:
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from ravendb import FtpSettings
2+
from ravendb.documents.operations.connection_string.GetConnectionStringOperation import GetConnectionStringsOperation
3+
from ravendb.documents.operations.connection_string.PutConnectionStringOperation import (
4+
PutConnectionStringOperation,
5+
PutConnectionStringResult,
6+
)
7+
from ravendb.documents.operations.connection_string.RemoveConnectionStringOperation import (
8+
RemoveConnectionStringOperation,
9+
)
10+
from ravendb.documents.operations.etl.configuration import RavenConnectionString
11+
from ravendb.documents.operations.etl.olap import OlapConnectionString
12+
from ravendb.documents.operations.etl.sql import SqlConnectionString
13+
from ravendb.serverwide.server_operation_executor import ConnectionStringType
14+
from ravendb.tests.test_base import TestBase
15+
16+
17+
class TestConnectionString(TestBase):
18+
def setUp(self):
19+
super().setUp()
20+
21+
def test_can_create_get_and_delete_connection_strings(self):
22+
raven_connection_string_1 = RavenConnectionString("r1", "db1", ["http://localhost:8080"])
23+
sql_connection_string_1 = SqlConnectionString("s1", "test", "MySql.Data.MySqlClient")
24+
olap_connection_string_1 = OlapConnectionString("o1", ftp_settings=FtpSettings(url="localhost:9090"))
25+
26+
put_result: PutConnectionStringResult = self.store.maintenance.send(
27+
PutConnectionStringOperation(raven_connection_string_1)
28+
)
29+
self.assertGreater(put_result.raft_command_index, 0)
30+
31+
put_result: PutConnectionStringResult = self.store.maintenance.send(
32+
PutConnectionStringOperation(sql_connection_string_1)
33+
)
34+
self.assertGreater(put_result.raft_command_index, 0)
35+
36+
put_result: PutConnectionStringResult = self.store.maintenance.send(
37+
PutConnectionStringOperation(olap_connection_string_1)
38+
)
39+
self.assertGreater(put_result.raft_command_index, 0)
40+
41+
connection_strings = self.store.maintenance.send(GetConnectionStringsOperation())
42+
43+
self.assertIn("r1", connection_strings.raven_connection_strings)
44+
self.assertEqual(1, len(connection_strings.raven_connection_strings))
45+
46+
self.assertIn("s1", connection_strings.sql_connection_strings)
47+
self.assertEqual(1, len(connection_strings.sql_connection_strings))
48+
49+
self.assertIn("o1", connection_strings.olap_connection_strings)
50+
self.assertEqual(1, len(connection_strings.olap_connection_strings))
51+
52+
raven_only = self.store.maintenance.send(GetConnectionStringsOperation("r1", ConnectionStringType.RAVEN))
53+
self.assertIn("r1", raven_only.raven_connection_strings)
54+
self.assertEqual(1, len(raven_only.raven_connection_strings))
55+
self.assertIsNone(raven_only.sql_connection_strings)
56+
57+
sql_only = self.store.maintenance.send(GetConnectionStringsOperation("s1", ConnectionStringType.SQL))
58+
self.assertIn("s1", sql_only.sql_connection_strings)
59+
self.assertEqual(1, len(sql_only.sql_connection_strings))
60+
self.assertIsNone(sql_only.raven_connection_strings)
61+
62+
olap_only = self.store.maintenance.send(GetConnectionStringsOperation("o1", ConnectionStringType.OLAP))
63+
self.assertIn("o1", olap_only.olap_connection_strings)
64+
self.assertEqual(1, len(olap_only.olap_connection_strings))
65+
self.assertIsNone(olap_only.raven_connection_strings)
66+
67+
remove_result = self.store.maintenance.send(RemoveConnectionStringOperation(sql_connection_string_1))
68+
self.assertGreater(remove_result.raft_command_index, 0)
69+
70+
after_delete = self.store.maintenance.send(GetConnectionStringsOperation("s1", ConnectionStringType.SQL))
71+
self.assertIsNone(after_delete.raven_connection_strings)
72+
self.assertIsNone(after_delete.sql_connection_strings)

0 commit comments

Comments
 (0)