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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions tableauserverclient/models/webhook_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ class WebhookItem:

owner_id : str | None
The identifier (luid) of the user who owns the webhook.

is_enabled : bool | None
Whether the webhook is enabled. Disabled webhooks do not fire.

status_change_reason : str | None
The reason the webhook status last changed (e.g. why it was disabled).
"""

def __init__(self):
Expand All @@ -52,8 +58,10 @@ def __init__(self):
self.url: str | None = None
self._event: str | None = None
self.owner_id: str | None = None
self.is_enabled: bool | None = None
self.status_change_reason: str | None = None

def _set_values(self, id, name, url, event, owner_id):
def _set_values(self, id, name, url, event, owner_id, is_enabled=None, status_change_reason=None):
if id is not None:
self._id = id
if name:
Expand All @@ -64,6 +72,10 @@ def _set_values(self, id, name, url, event, owner_id):
self.event = event
if owner_id:
self.owner_id = owner_id
if is_enabled is not None:
self.is_enabled = is_enabled
if status_change_reason is not None:
self.status_change_reason = status_change_reason

@property
def id(self) -> str | None:
Expand Down Expand Up @@ -97,6 +109,21 @@ def from_response(cls: type["WebhookItem"], resp: bytes, ns) -> list["WebhookIte
all_webhooks_items.append(webhook_item)
return all_webhooks_items

def _parse_common_tags(self, webhook_xml, ns) -> "WebhookItem":
"""Merge fields from a server response into this item.

Used by the update endpoint (matching the convention in users_endpoint
and datasources_endpoint) so that locally-set fields are preserved
when the server's response omits them.
"""
if not isinstance(webhook_xml, ET.Element):
parsed = fromstring(webhook_xml)
webhook_xml = parsed.find(".//t:webhook", namespaces=ns)
if webhook_xml is not None:
values = self._parse_element(webhook_xml, ns)
self._set_values(*values)
return self

@staticmethod
def _parse_element(webhook_xml: ET.Element, ns) -> tuple:
id = webhook_xml.get("id", None)
Expand All @@ -116,7 +143,14 @@ def _parse_element(webhook_xml: ET.Element, ns) -> tuple:
if owner_tag is not None:
owner_id = owner_tag.get("id", None)

return id, name, url, event, owner_id
is_enabled = None
is_enabled_str = webhook_xml.get("isEnabled", None)
if is_enabled_str is not None:
is_enabled = is_enabled_str.lower() == "true"

status_change_reason = webhook_xml.get("statusChangeReason", None)

return id, name, url, event, owner_id, is_enabled, status_change_reason

def __repr__(self) -> str:
return f"<Webhook id={self.id} name={self.name} url={self.url} event={self.event}>"
return f"<Webhook id={self.id} name={self.name} url={self.url} event={self.event} is_enabled={self.is_enabled}>"
32 changes: 32 additions & 0 deletions tableauserverclient/server/endpoint/webhooks_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import copy
import logging

from .endpoint import Endpoint, api
from .exceptions import MissingRequiredFieldError
from tableauserverclient.server import RequestFactory
from tableauserverclient.models import WebhookItem, PaginationItem

Expand Down Expand Up @@ -118,6 +120,36 @@ def create(self, webhook_item: WebhookItem) -> WebhookItem:
logger.info(f"Created new webhook (ID: {new_webhook.id})")
return new_webhook

@api(version="3.6")
def update(self, webhook_item: WebhookItem) -> WebhookItem:
"""
Modifies an existing webhook.

REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#update_webhook

Parameters
----------
webhook_item : WebhookItem
The webhook item to update. Must have a valid id.

Returns
-------
WebhookItem
An object containing information about the updated webhook.
"""
if not webhook_item.id:
error = (
"Webhook item missing ID. Set webhook_item.id directly, or fetch the "
"webhook via webhooks.get_by_id() / webhooks.get() before updating."
)
raise MissingRequiredFieldError(error)
Comment on lines +140 to +145
url = f"{self.baseurl}/{webhook_item.id}"
update_req = RequestFactory.Webhook.update_req(webhook_item)
server_response = self.put_request(url, update_req)
logger.info(f"Updated webhook (ID: {webhook_item.id})")
updated_webhook = copy.copy(webhook_item)
return updated_webhook._parse_common_tags(server_response.content, self.parent_srv.namespace)

@api(version="3.6")
def test(self, webhook_id: str):
"""
Expand Down
20 changes: 20 additions & 0 deletions tableauserverclient/server/request_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,26 @@ def create_req(self, xml_request: ET.Element, webhook_item: "WebhookItem") -> by

return ET.tostring(xml_request)

@_tsrequest_wrapped
def update_req(self, xml_request: ET.Element, webhook_item: "WebhookItem") -> bytes:
webhook = ET.SubElement(xml_request, "webhook")
if webhook_item.name is not None:
webhook.attrib["name"] = webhook_item.name
if webhook_item.is_enabled is not None:
webhook.attrib["isEnabled"] = str(webhook_item.is_enabled).lower()
Comment on lines +1417 to +1421

if webhook_item._event is not None:
source = ET.SubElement(webhook, "webhook-source")
ET.SubElement(source, webhook_item._event)
Comment on lines +1423 to +1425

if webhook_item.url is not None:
destination = ET.SubElement(webhook, "webhook-destination")
post = ET.SubElement(destination, "webhook-destination-http")
post.attrib["method"] = "POST"
post.attrib["url"] = webhook_item.url

return ET.tostring(xml_request)


class MetricRequest:
@_tsrequest_wrapped
Expand Down
12 changes: 12 additions & 0 deletions test/assets/webhook_update.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version='1.0' encoding='UTF-8'?>
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
<webhook id="webhook-id" name="webhook-name-updated" isEnabled="true" statusChangeReason="">
<webhook-source>
<webhook-source-event-datasource-created />
</webhook-source>
<webhook-destination>
<webhook-destination-http method="POST" url="https://updated-url.example.com/hook"/>
</webhook-destination>
<owner id="webhook_owner_luid" name="webhook_owner_name"/>
</webhook>
</tsResponse>
Loading
Loading