diff --git a/tableauserverclient/models/webhook_item.py b/tableauserverclient/models/webhook_item.py index 5fc5c5749..8479a2922 100644 --- a/tableauserverclient/models/webhook_item.py +++ b/tableauserverclient/models/webhook_item.py @@ -2,6 +2,7 @@ import re +from typing import List, Optional, TYPE_CHECKING, Tuple, Type NAMESPACE_RE = re.compile(r"^{.*}") @@ -14,11 +15,11 @@ def _parse_event(events): class WebhookItem(object): def __init__(self): - self._id = None - self.name = None - self.url = None - self._event = None - self.owner_id = None + self._id: Optional[str] = None + self.name: Optional[str] = None + self.url: Optional[str] = None + self._event: Optional[str] = None + self.owner_id: Optional[str] = None def _set_values(self, id, name, url, event, owner_id): if id is not None: @@ -33,21 +34,21 @@ def _set_values(self, id, name, url, event, owner_id): self.owner_id = owner_id @property - def id(self): + def id(self) -> Optional[str]: return self._id @property - def event(self): + def event(self) -> Optional[str]: if self._event: return self._event.replace("webhook-source-event-", "") return None @event.setter - def event(self, value): + def event(self, value: str) -> None: self._event = "webhook-source-event-{}".format(value) @classmethod - def from_response(cls, resp, ns): + def from_response(cls: Type["WebhookItem"], resp: bytes, ns) -> List["WebhookItem"]: all_webhooks_items = list() parsed_response = ET.fromstring(resp) all_webhooks_xml = parsed_response.findall(".//t:webhook", namespaces=ns) @@ -60,7 +61,7 @@ def from_response(cls, resp, ns): return all_webhooks_items @staticmethod - def _parse_element(webhook_xml, ns): + def _parse_element(webhook_xml: ET.Element, ns) -> Tuple: id = webhook_xml.get("id", None) name = webhook_xml.get("name", None) @@ -80,5 +81,5 @@ def _parse_element(webhook_xml, ns): return id, name, url, event, owner_id - def __repr__(self): + def __repr__(self) -> str: return "".format(self.id, self.name, self.url, self.event) diff --git a/tableauserverclient/server/endpoint/webhooks_endpoint.py b/tableauserverclient/server/endpoint/webhooks_endpoint.py index 6f5135ac1..773f06ed8 100644 --- a/tableauserverclient/server/endpoint/webhooks_endpoint.py +++ b/tableauserverclient/server/endpoint/webhooks_endpoint.py @@ -6,17 +6,23 @@ logger = logging.getLogger("tableau.endpoint.webhooks") +from typing import List, Optional, TYPE_CHECKING, Tuple + +if TYPE_CHECKING: + from ..server import Server + from ..request_options import RequestOptions + class Webhooks(Endpoint): - def __init__(self, parent_srv): + def __init__(self, parent_srv: "Server") -> None: super(Webhooks, self).__init__(parent_srv) @property - def baseurl(self): + def baseurl(self) -> str: return "{0}/sites/{1}/webhooks".format(self.parent_srv.baseurl, self.parent_srv.site_id) @api(version="3.6") - def get(self, req_options=None): + def get(self, req_options: Optional["RequestOptions"] = None) -> Tuple[List[WebhookItem], PaginationItem]: logger.info("Querying all Webhooks on site") url = self.baseurl server_response = self.get_request(url, req_options) @@ -25,7 +31,7 @@ def get(self, req_options=None): return all_webhook_items, pagination_item @api(version="3.6") - def get_by_id(self, webhook_id): + def get_by_id(self, webhook_id: str) -> WebhookItem: if not webhook_id: error = "Webhook ID undefined." raise ValueError(error) @@ -35,7 +41,7 @@ def get_by_id(self, webhook_id): return WebhookItem.from_response(server_response.content, self.parent_srv.namespace)[0] @api(version="3.6") - def delete(self, webhook_id): + def delete(self, webhook_id: str) -> None: if not webhook_id: error = "Webhook ID undefined." raise ValueError(error) @@ -44,7 +50,7 @@ def delete(self, webhook_id): logger.info("Deleted single webhook (ID: {0})".format(webhook_id)) @api(version="3.6") - def create(self, webhook_item): + def create(self, webhook_item: WebhookItem) -> WebhookItem: url = self.baseurl create_req = RequestFactory.Webhook.create_req(webhook_item) server_response = self.post_request(url, create_req) @@ -54,7 +60,7 @@ def create(self, webhook_item): return new_webhook @api(version="3.6") - def test(self, webhook_id): + def test(self, webhook_id: str): if not webhook_id: error = "Webhook ID undefined." raise ValueError(error) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 93cf1bb90..293f2166d 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -307,6 +307,7 @@ def publish( connection_credentials: Optional["ConnectionCredentials"] = None, connections: Optional[Sequence[ConnectionItem]] = None, as_job: bool = False, + hidden_views: Optional[Sequence[str]] = None, skip_connection_check: bool = False, ): diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index 914bfe4c7..2a16f5a8f 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -2,18 +2,17 @@ from requests.packages.urllib3.fields import RequestField from requests.packages.urllib3.filepost import encode_multipart_formdata +from typing import Any, Dict, List, Optional, TYPE_CHECKING, Tuple, Iterable from ..models import TaskItem, UserItem, GroupItem, PermissionsRule, FavoriteItem -from typing import Any, Dict, List, Optional, TYPE_CHECKING, Tuple, Iterable - -if TYPE_CHECKING: - from ..models import SubscriptionItem - from ..models import DataAlertItem - from ..models import FlowItem - from ..models import ConnectionItem - from ..models import SiteItem - from ..models import ProjectItem +from ..models import SubscriptionItem +from ..models import DataAlertItem +from ..models import FlowItem +from ..models import ConnectionItem +from ..models import SiteItem +from ..models import ProjectItem +from ..models import WebhookItem def _add_multipart(parts: Dict) -> Tuple[Any, str]: @@ -1031,17 +1030,26 @@ def empty_req(self, xml_request): class WebhookRequest(object): @_tsrequest_wrapped - def create_req(self, xml_request, webhook_item): + def create_req(self, xml_request: ET.Element, webhook_item: "WebhookItem") -> bytes: webhook = ET.SubElement(xml_request, "webhook") - webhook.attrib["name"] = webhook_item.name + if isinstance(webhook_item.name, str): + webhook.attrib["name"] = webhook_item.name + else: + raise ValueError(f"Name must be provided for {webhook_item}") source = ET.SubElement(webhook, "webhook-source") - ET.SubElement(source, webhook_item._event) + if isinstance(webhook_item._event, str): + ET.SubElement(source, webhook_item._event) + else: + raise ValueError(f"_event for Webhook must be provided. {webhook_item}") destination = ET.SubElement(webhook, "webhook-destination") post = ET.SubElement(destination, "webhook-destination-http") post.attrib["method"] = "POST" - post.attrib["url"] = webhook_item.url + if isinstance(webhook_item.url, str): + post.attrib["url"] = webhook_item.url + else: + raise ValueError(f"URL must be provided on {webhook_item}") return ET.tostring(xml_request) diff --git a/test/test_schedule.py b/test/test_schedule.py index 73f61008e..5b3c1177e 100644 --- a/test/test_schedule.py +++ b/test/test_schedule.py @@ -167,9 +167,7 @@ def test_create_weekly(self) -> None: self.assertEqual("2016-09-16T16:15:00Z", format_datetime(new_schedule.next_run_at)) self.assertEqual(TSC.ScheduleItem.ExecutionOrder.Parallel, new_schedule.execution_order) self.assertEqual(time(9, 15), new_schedule.interval_item.start_time) - self.assertEqual( - ("Monday", "Wednesday", "Friday"), new_schedule.interval_item.interval - ) # type: ignore[union-attr] + self.assertEqual(("Monday", "Wednesday", "Friday"), new_schedule.interval_item.interval) self.assertEqual(2, len(new_schedule.warnings)) self.assertEqual("warning 1", new_schedule.warnings[0]) self.assertEqual("warning 2", new_schedule.warnings[1]) diff --git a/test/test_webhook.py b/test/test_webhook.py index 64c201af5..c5f089204 100644 --- a/test/test_webhook.py +++ b/test/test_webhook.py @@ -14,7 +14,7 @@ class WebhookTests(unittest.TestCase): - def setUp(self): + def setUp(self) -> None: self.server = TSC.Server("http://test") self.server.version = "3.6" @@ -24,7 +24,7 @@ def setUp(self): self.baseurl = self.server.webhooks.baseurl - def test_get(self): + def test_get(self) -> None: with open(GET_XML, "rb") as f: response_xml = f.read().decode("utf-8") with requests_mock.mock() as m: @@ -39,24 +39,24 @@ def test_get(self): self.assertEqual(webhook.name, "webhook-name") self.assertEqual(webhook.id, "webhook-id") - def test_get_before_signin(self): + def test_get_before_signin(self) -> None: self.server._auth_token = None self.assertRaises(TSC.NotSignedInError, self.server.webhooks.get) - def test_delete(self): + def test_delete(self) -> None: with requests_mock.mock() as m: m.delete(self.baseurl + "/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", status_code=204) self.server.webhooks.delete("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760") - def test_delete_missing_id(self): + def test_delete_missing_id(self) -> None: self.assertRaises(ValueError, self.server.webhooks.delete, "") - def test_test(self): + def test_test(self) -> None: with requests_mock.mock() as m: m.get(self.baseurl + "/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760/test", status_code=200) self.server.webhooks.test("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760") - def test_create(self): + def test_create(self) -> None: with open(CREATE_XML, "rb") as f: response_xml = f.read().decode("utf-8") with requests_mock.mock() as m: