Skip to content

Commit 4da78fa

Browse files
committed
Merge branch 'development' into jorwoods/vf_params
2 parents 4493665 + b4ef4a8 commit 4da78fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+5451
-5238
lines changed

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ repository = "https://github.com/tableau/server-client-python"
3333

3434
[project.optional-dependencies]
3535
test = ["black==24.8", "build", "mypy==1.4", "pytest>=7.0", "pytest-cov", "pytest-subtests",
36-
"requests-mock>=1.0,<2.0"]
37-
36+
"requests-mock>=1.0,<2.0", "types-requests>=2.32.4.20250913"]
3837
[tool.black]
3938
line-length = 120
4039
target-version = ['py39', 'py310', 'py311', 'py312', 'py313']

tableauserverclient/server/endpoint/flow_task_endpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def baseurl(self) -> str:
1818
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/tasks/flows"
1919

2020
@api(version="3.22")
21-
def create(self, flow_item: TaskItem) -> TaskItem:
21+
def create(self, flow_item: TaskItem) -> bytes:
2222
if not flow_item:
2323
error = "No flow provided"
2424
raise ValueError(error)

test/http/test_http_requests.py

Lines changed: 101 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import pytest
12
import tableauserverclient as TSC
23
import unittest
34
import requests
@@ -27,91 +28,103 @@ def __init__(self, status_code):
2728
return MockResponse(200)
2829

2930

30-
class ServerTests(unittest.TestCase):
31-
def test_init_server_model_empty_throws(self):
32-
with self.assertRaises(TypeError):
33-
server = TSC.Server()
34-
35-
def test_init_server_model_no_protocol_defaults_htt(self):
36-
server = TSC.Server("fake-url")
37-
38-
def test_init_server_model_valid_server_name_works(self):
39-
server = TSC.Server("http://fake-url")
40-
41-
def test_init_server_model_valid_https_server_name_works(self):
42-
# by default, it will just set the version to 2.3
43-
server = TSC.Server("https://fake-url")
44-
45-
def test_init_server_model_bad_server_name_not_version_check(self):
46-
server = TSC.Server("fake-url", use_server_version=False)
47-
48-
@mock.patch("requests.sessions.Session.get", side_effect=mocked_requests_get)
49-
def test_init_server_model_bad_server_name_do_version_check(self, mock_get):
50-
server = TSC.Server("fake-url", use_server_version=True)
51-
52-
def test_init_server_model_bad_server_name_not_version_check_random_options(self):
53-
# with self.assertRaises(MissingSchema):
54-
server = TSC.Server("fake-url", use_server_version=False, http_options={"foo": 1})
55-
56-
def test_init_server_model_bad_server_name_not_version_check_real_options(self):
57-
# with self.assertRaises(ValueError):
58-
server = TSC.Server("fake-url", use_server_version=False, http_options={"verify": False})
59-
60-
def test_http_options_skip_ssl_works(self):
61-
http_options = {"verify": False}
62-
server = TSC.Server("http://fake-url")
63-
server.add_http_options(http_options)
64-
65-
def test_http_options_multiple_options_works(self):
66-
http_options = {"verify": False, "birdname": "Parrot"}
67-
server = TSC.Server("http://fake-url")
68-
server.add_http_options(http_options)
69-
70-
# ValueError: dictionary update sequence element #0 has length 1; 2 is required
71-
def test_http_options_multiple_dicts_fails(self):
72-
http_options_1 = {"verify": False}
73-
http_options_2 = {"birdname": "Parrot"}
74-
server = TSC.Server("http://fake-url")
75-
with self.assertRaises(ValueError):
76-
server.add_http_options([http_options_1, http_options_2])
77-
78-
# TypeError: cannot convert dictionary update sequence element #0 to a sequence
79-
def test_http_options_not_sequence_fails(self):
80-
server = TSC.Server("http://fake-url")
81-
with self.assertRaises(ValueError):
82-
server.add_http_options({1, 2, 3})
83-
84-
def test_validate_connection_http(self):
85-
url = "http://cookies.com"
86-
server = TSC.Server(url)
87-
server.validate_connection_settings()
88-
self.assertEqual(url, server.server_address)
89-
90-
def test_validate_connection_https(self):
91-
url = "https://cookies.com"
92-
server = TSC.Server(url)
93-
server.validate_connection_settings()
94-
self.assertEqual(url, server.server_address)
95-
96-
def test_validate_connection_no_protocol(self):
97-
url = "cookies.com"
98-
fixed_url = "http://cookies.com"
99-
server = TSC.Server(url)
100-
server.validate_connection_settings()
101-
self.assertEqual(fixed_url, server.server_address)
102-
103-
104-
class SessionTests(unittest.TestCase):
105-
test_header = {"x-test": "true"}
106-
107-
@staticmethod
108-
def session_factory():
109-
session = requests.session()
110-
session.headers.update(SessionTests.test_header)
111-
return session
112-
113-
def test_session_factory_adds_headers(self):
114-
test_request_bin = "http://capture-this-with-mock.com"
115-
with requests_mock.mock() as m:
116-
m.get(url="http://capture-this-with-mock.com/api/2.4/serverInfo", request_headers=SessionTests.test_header)
117-
server = TSC.Server(test_request_bin, use_server_version=True, session_factory=SessionTests.session_factory)
31+
def test_init_server_model_empty_throws():
32+
with pytest.raises(TypeError):
33+
server = TSC.Server()
34+
35+
36+
def test_init_server_model_no_protocol_defaults_htt():
37+
server = TSC.Server("fake-url")
38+
39+
40+
def test_init_server_model_valid_server_name_works():
41+
server = TSC.Server("http://fake-url")
42+
43+
44+
def test_init_server_model_valid_https_server_name_works():
45+
# by default, it will just set the version to 2.3
46+
server = TSC.Server("https://fake-url")
47+
48+
49+
def test_init_server_model_bad_server_name_not_version_check():
50+
server = TSC.Server("fake-url", use_server_version=False)
51+
52+
53+
@mock.patch("requests.sessions.Session.get", side_effect=mocked_requests_get)
54+
def test_init_server_model_bad_server_name_do_version_check(mock_get):
55+
server = TSC.Server("fake-url", use_server_version=True)
56+
57+
58+
def test_init_server_model_bad_server_name_not_version_check_random_options():
59+
server = TSC.Server("fake-url", use_server_version=False, http_options={"foo": 1})
60+
61+
62+
def test_init_server_model_bad_server_name_not_version_check_real_options():
63+
server = TSC.Server("fake-url", use_server_version=False, http_options={"verify": False})
64+
65+
66+
def test_http_options_skip_ssl_works():
67+
http_options = {"verify": False}
68+
server = TSC.Server("http://fake-url")
69+
server.add_http_options(http_options)
70+
71+
72+
def test_http_options_multiple_options_works():
73+
http_options = {"verify": False, "birdname": "Parrot"}
74+
server = TSC.Server("http://fake-url")
75+
server.add_http_options(http_options)
76+
77+
78+
# ValueError: dictionary update sequence element #0 has length 1; 2 is required
79+
def test_http_options_multiple_dicts_fails():
80+
http_options_1 = {"verify": False}
81+
http_options_2 = {"birdname": "Parrot"}
82+
server = TSC.Server("http://fake-url")
83+
with pytest.raises(ValueError):
84+
server.add_http_options([http_options_1, http_options_2])
85+
86+
87+
# TypeError: cannot convert dictionary update sequence element #0 to a sequence
88+
def test_http_options_not_sequence_fails():
89+
server = TSC.Server("http://fake-url")
90+
with pytest.raises(ValueError):
91+
server.add_http_options({1, 2, 3})
92+
93+
94+
def test_validate_connection_http():
95+
url = "http://cookies.com"
96+
server = TSC.Server(url)
97+
server.validate_connection_settings()
98+
assert url == server.server_address
99+
100+
101+
def test_validate_connection_https():
102+
url = "https://cookies.com"
103+
server = TSC.Server(url)
104+
server.validate_connection_settings()
105+
assert url == server.server_address
106+
107+
108+
def test_validate_connection_no_protocol():
109+
url = "cookies.com"
110+
fixed_url = "http://cookies.com"
111+
server = TSC.Server(url)
112+
server.validate_connection_settings()
113+
assert fixed_url == server.server_address
114+
115+
116+
test_header = {"x-test": "true"}
117+
118+
119+
@pytest.fixture
120+
def session_factory() -> requests.Session:
121+
session = requests.session()
122+
session.headers.update(test_header)
123+
return session
124+
125+
126+
def test_session_factory_adds_headers(session_factory):
127+
test_request_bin = "http://capture-this-with-mock.com"
128+
with requests_mock.mock() as m:
129+
m.get(url="http://capture-this-with-mock.com/api/2.4/serverInfo", request_headers=test_header)
130+
server = TSC.Server(test_request_bin, use_server_version=True, session_factory=lambda: session_factory)

test/models/test_repr.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import inspect
2+
from typing import Any
23

3-
from unittest import TestCase
44
import _models # type: ignore # did not set types for this
55
import tableauserverclient as TSC
66

7-
from typing import Any
7+
import pytest
88

99

1010
# ensure that all models that don't need parameters can be instantiated
@@ -31,21 +31,16 @@ def instantiate_class(name: str, obj: Any):
3131
print(f"Class '{name}' does not have a constructor (__init__ method).")
3232

3333

34-
class TestAllModels(TestCase):
35-
# not all models have __repr__ yet: see above list
36-
def test_repr_is_implemented(self):
37-
m = _models.get_defined_models()
38-
for model in m:
39-
with self.subTest(model.__name__, model=model):
40-
print(model.__name__, type(model.__repr__).__name__)
41-
self.assertEqual(type(model.__repr__).__name__, "function")
34+
def is_concrete(obj: Any):
35+
return inspect.isclass(obj) and not inspect.isabstract(obj)
4236

43-
# 2 - Iterate through the objects in the module
44-
def test_by_reflection(self):
45-
for class_name, obj in inspect.getmembers(TSC, is_concrete):
46-
with self.subTest(class_name, obj=obj):
47-
instantiate_class(class_name, obj)
4837

38+
@pytest.mark.parametrize("class_name, obj", inspect.getmembers(TSC, is_concrete))
39+
def test_by_reflection(class_name, obj):
40+
instantiate_class(class_name, obj)
4941

50-
def is_concrete(obj: Any):
51-
return inspect.isclass(obj) and not inspect.isabstract(obj)
42+
43+
@pytest.mark.parametrize("model", _models.get_defined_models())
44+
def test_repr_is_implemented(model):
45+
print(model.__name__, type(model.__repr__).__name__)
46+
assert type(model.__repr__).__name__ == "function"
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import unittest
21
import tableauserverclient as TSC
32
import tableauserverclient.server.request_factory as TSC_RF
43
from tableauserverclient import DatasourceItem
54

65

7-
class DatasourceRequestTests(unittest.TestCase):
8-
def test_generate_xml(self):
9-
datasource_item: TSC.DatasourceItem = TSC.DatasourceItem("name")
10-
datasource_item.name = "a ds"
11-
datasource_item.description = "described"
12-
datasource_item.use_remote_query_agent = False
13-
datasource_item.ask_data_enablement = DatasourceItem.AskDataEnablement.Enabled
14-
datasource_item.project_id = "testval"
15-
TSC_RF.RequestFactory.Datasource._generate_xml(datasource_item)
6+
def test_generate_xml():
7+
datasource_item: TSC.DatasourceItem = TSC.DatasourceItem("name")
8+
datasource_item.name = "a ds"
9+
datasource_item.description = "described"
10+
datasource_item.use_remote_query_agent = False
11+
datasource_item.ask_data_enablement = DatasourceItem.AskDataEnablement.Enabled
12+
datasource_item.project_id = "testval"
13+
TSC_RF.RequestFactory.Datasource._generate_xml(datasource_item)
Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,58 @@
1-
import unittest
21
import tableauserverclient as TSC
32
import tableauserverclient.server.request_factory as TSC_RF
43
from tableauserverclient.helpers.strings import redact_xml
54
import pytest
65
import sys
76

87

9-
class WorkbookRequestTests(unittest.TestCase):
10-
def test_embedded_extract_req(self):
11-
include_all = True
12-
embedded_datasources = None
13-
xml_result = TSC_RF.RequestFactory.Workbook.embedded_extract_req(include_all, embedded_datasources)
14-
15-
def test_generate_xml(self):
16-
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
17-
TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item)
18-
19-
def test_generate_xml_invalid_connection(self):
20-
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
21-
conn = TSC.ConnectionItem()
22-
with self.assertRaises(ValueError):
23-
request = TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item, connections=[conn])
24-
25-
def test_generate_xml_invalid_connection_credentials(self):
26-
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
27-
conn = TSC.ConnectionItem()
28-
conn.server_address = "address"
29-
creds = TSC.ConnectionCredentials("username", "password")
30-
creds.name = None
31-
conn.connection_credentials = creds
32-
with self.assertRaises(ValueError):
33-
request = TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item, connections=[conn])
34-
35-
def test_generate_xml_valid_connection_credentials(self):
36-
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
37-
conn = TSC.ConnectionItem()
38-
conn.server_address = "address"
39-
creds = TSC.ConnectionCredentials("username", "DELETEME")
40-
conn.connection_credentials = creds
8+
def test_embedded_extract_req() -> None:
9+
include_all = True
10+
embedded_datasources = None
11+
xml_result = TSC_RF.RequestFactory.Workbook.embedded_extract_req(include_all, embedded_datasources)
12+
13+
14+
def test_generate_xml() -> None:
15+
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
16+
TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item)
17+
18+
19+
def test_generate_xml_invalid_connection() -> None:
20+
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
21+
conn = TSC.ConnectionItem()
22+
with pytest.raises(ValueError):
4123
request = TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item, connections=[conn])
42-
assert request.find(b"DELETEME") > 0
43-
44-
def test_redact_passwords_in_xml(self):
45-
if sys.version_info < (3, 7):
46-
pytest.skip("Redaction is only implemented for 3.7+.")
47-
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
48-
conn = TSC.ConnectionItem()
49-
conn.server_address = "address"
50-
creds = TSC.ConnectionCredentials("username", "DELETEME")
51-
conn.connection_credentials = creds
24+
25+
26+
def test_generate_xml_invalid_connection_credentials() -> None:
27+
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
28+
conn = TSC.ConnectionItem()
29+
conn.server_address = "address"
30+
creds = TSC.ConnectionCredentials("username", "password")
31+
creds.name = None
32+
conn.connection_credentials = creds
33+
with pytest.raises(ValueError):
5234
request = TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item, connections=[conn])
53-
redacted = redact_xml(request)
54-
assert request.find(b"DELETEME") > 0, request
55-
assert redacted.find(b"DELETEME") == -1, redacted
35+
36+
37+
def test_generate_xml_valid_connection_credentials() -> None:
38+
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
39+
conn = TSC.ConnectionItem()
40+
conn.server_address = "address"
41+
creds = TSC.ConnectionCredentials("username", "DELETEME")
42+
conn.connection_credentials = creds
43+
request = TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item, connections=[conn])
44+
assert request.find(b"DELETEME") > 0
45+
46+
47+
def test_redact_passwords_in_xml() -> None:
48+
if sys.version_info < (3, 7):
49+
pytest.skip("Redaction is only implemented for 3.7+.")
50+
workbook_item: TSC.WorkbookItem = TSC.WorkbookItem("name", "project_id")
51+
conn = TSC.ConnectionItem()
52+
conn.server_address = "address"
53+
creds = TSC.ConnectionCredentials("username", "DELETEME")
54+
conn.connection_credentials = creds
55+
request = TSC_RF.RequestFactory.Workbook._generate_xml(workbook_item, connections=[conn])
56+
redacted = redact_xml(request)
57+
assert request.find(b"DELETEME") > 0, request
58+
assert redacted.find(b"DELETEME") == -1, redacted

0 commit comments

Comments
 (0)