8000 Jorwoods/type hint permissions by jorwoods · Pull Request #940 · tableau/server-client-python · GitHub
[go: up one dir, main page]

Skip to content

Jorwoods/type hint permissions #940

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 28, 2022
Merged
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
13 changes: 9 additions & 4 deletions tableauserverclient/models/permissions_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

logger = logging.getLogger("tableau.models.permissions_item")

from typing import Dict, List, Optional, TYPE_CHECKING

if TYPE_CHECKING:
from .reference_item import ResourceReference


class Permission:
class Mode:
Expand Down Expand Up @@ -42,19 +47,19 @@ class Resource:


class PermissionsRule(object):
def __init__(self, grantee, capabilities):
def __init__(self, grantee: "ResourceReference", capabilities: Dict[Optional[str], Optional[str]]) -> None:
self.grantee = grantee
self.capabilities = capabilities

@classmethod
def from_response(cls, resp, ns=None):
def from_response(cls, resp, ns=None) -> List["PermissionsRule"]:
parsed_response = ET.fromstring(resp)

rules = []
permissions_rules_list_xml = parsed_response.findall(".//t:granteeCapabilities", namespaces=ns)

for grantee_capability_xml in permissions_rules_list_xml:
capability_dict = {}
capability_dict: Dict[Optional[str], Optional[str]] = {}

grantee = PermissionsRule._parse_grantee_element(grantee_capability_xml, ns)

Expand All @@ -70,7 +75,7 @@ def from_response(cls, resp, ns=None):
return rules

@staticmethod
def _parse_grantee_element(grantee_capability_xml, ns):
def _parse_grantee_element(grantee_capability_xml: ET.Element, ns: Optional[Dict[str, str]]) -> "ResourceReference":
"""Use Xpath magic and some string splitting to get the right object type from the xml"""

# Get the first element in the tree with an 'id' attribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@

logger = logging.getLogger(__name__)

from typing import TYPE_CHECKING, Callable, List, Optional, Sequence, Union

if TYPE_CHECKING:
from ...models import (
DatasourceItem,
FlowItem,
ProjectItem,
ViewItem,
WorkbookItem,
)

from ..server import Server
from ..request_options import RequestOptions

TableauItem = Union[DatasourceItem, FlowItem, ProjectItem, ViewItem, WorkbookItem]


class _DefaultPermissionsEndpoint(Endpoint):
"""Adds default-permission model to another endpoint
Expand All @@ -19,15 +35,17 @@ class _DefaultPermissionsEndpoint(Endpoint):
has these supported endpoints
"""

def __init__(self, parent_srv, owner_baseurl):
def __init__(self, parent_srv: "Server", owner_baseurl: Callable[[], str]) -> None:
super(_DefaultPermissionsEndpoint, self).__init__(parent_srv)

# owner_baseurl is the baseurl of the parent. The MUST be a lambda
# since we don't know the full site URL until we sign in. If
# populated without, we will get a sign-in error
self.owner_baseurl = owner_baseurl

def update_default_permissions(self, resource, permissions, content_type):
def update_default_permissions(
self, resource: "TableauItem", permissions: Sequence[PermissionsRule], content_type: str
) -> List[PermissionsRule]:
url = "{0}/{1}/default-permissions/{2}".format(self.owner_baseurl(), resource.id, content_type + "s")
update_req = RequestFactory.Permission.add_req(permissions)
response = self.put_request(url, update_req)
Expand All @@ -36,7 +54,7 @@ def update_default_permissions(self, resource, permissions, content_type):

return permissions

def delete_default_permission(self, resource, rule, content_type):
def delete_default_permission(self, resource: "TableauItem", rule: PermissionsRule, content_type: str) -> None:
for capability, mode in rule.capabilities.items():
# Made readability better but line is too long, will make this look better
url = (
Expand All @@ -60,18 +78,20 @@ def delete_default_permission(self, resource, rule, content_type):
"Deleted permission for {0} {1} item {2}".format(rule.grantee.tag_name, rule.grantee.id, resource.id)
)

def populate_default_permissions(self, item, content_type):
def populate_default_permissions(self, item: "ProjectItem", content_type: str) -> None:
if not item.id:
error = "Server item is missing ID. Item must be retrieved from server first."
raise MissingRequiredFieldError(error)

def permission_fetcher():
def permission_fetcher() -> List[PermissionsRule]:
return self._get_default_permissions(item, content_type)

item._set_default_permissions(permission_fetcher, content_type)
logger.info("Populated {0} permissions for item (ID: {1})".format(item.id, content_type))

def _get_default_permissions(self, item, content_type, req_options=None):
def _get_default_permissions(
self, item: "TableauItem", content_type: str, req_options: Optional["RequestOptions"] = None
) -> List[PermissionsRule]:
url = "{0}/{1}/default-permissions/{2}".format(self.owner_baseurl(), item.id, content_type + "s")
server_response = self.get_request(url, req_options)
permissions = PermissionsRule.from_response(server_response.content, self.parent_srv.namespace)
Expand Down
19 changes: 14 additions & 5 deletions tableauserverclient/server/endpoint/permissions_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@

logger = logging.getLogger(__name__)

from typing import Callable, TYPE_CHECKING, List, Union

if TYPE_CHECKING:
from ...models import DatasourceItem, ProjectItem, WorkbookItem, ViewItem
from ..server import Server
from ..request_options import RequestOptions

TableauItem = Union["DatasourceItem", "ProjectItem", "WorkbookItem", "ViewItem"]


class _PermissionsEndpoint(Endpoint):
"""Adds permission model to another endpoint
Expand All @@ -18,15 +27,15 @@ class _PermissionsEndpoint(Endpoint):
parent endpoint which has these supported endpoints
"""

def __init__(self, parent_srv, owner_baseurl):
def __init__(self, parent_srv: "Server", owner_baseurl: Callable[[], str]) -> None:
super(_PermissionsEndpoint, self).__init__(parent_srv)

# owner_baseurl is the baseurl of the parent. The MUST be a lambda
# since we don't know the full site URL until we sign in. If
# populated without, we will get a sign-in error
self.owner_baseurl = owner_baseurl

def update(self, resource, permissions):
def update(self, resource: TableauItem, permissions: List[PermissionsRule]) -> List[PermissionsRule]:
url = "{0}/{1}/permissions".format(self.owner_baseurl(), resource.id)
update_req = RequestFactory.Permission.add_req(permissions)
response = self.put_request(url, update_req)
Expand All @@ -35,7 +44,7 @@ def update(self, resource, permissions):

return permissions

def delete(self, resource, rules):
def delete(self, resource: TableauItem, rules: Union[PermissionsRule, List[PermissionsRule]]):
# Delete is the only endpoint that doesn't take a list of rules
# so let's fake it to keep it consistent
# TODO that means we need error handling around the call
Expand All @@ -62,7 +71,7 @@ def delete(self, resource, rules):
"Deleted permission for {0} {1} item {2}".format(rule.grantee.tag_name, rule.grantee.id, resource.id)
)

def populate(self, item):
def populate(self, item: TableauItem):
if not item.id:
error = "Server item is missing ID. Item must be retrieved from server first."
raise MissingRequiredFieldError(error)
Expand All @@ -73,7 +82,7 @@ def permission_fetcher():
item._set_permissions(permission_fetcher)
logger.info("Populated permissions for item (ID: {0})".format(item.id))

def _get_permissions(self, item, req_options=None):
def _get_permissions(self, item: TableauItem, req_options: "RequestOptions" = None):
url = "{0}/{1}/permissions".format(self.owner_baseurl(), item.id)
server_response = self.get_request(url, req_options)
permissions = PermissionsRule.from_response(server_response.content, self.parent_srv.namespace)
Expand Down
4 changes: 2 additions & 2 deletions tableauserverclient/server/request_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from ..models import TaskItem, UserItem, GroupItem, PermissionsRule, FavoriteItem

from typing import Any, Dict, List, Optional, TYPE_CHECKING, Tuple
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Tuple, Iterable

if TYPE_CHECKING:
from ..models import DataAlertItem
Expand Down Expand Up @@ -429,7 +429,7 @@ def update_req(self, group_item: GroupItem, default_site_role: Optional[str] = N


class PermissionRequest(object):
def add_req(self, rules):
def add_req(self, rules: Iterable[PermissionsRule]) -> bytes:
xml_request = ET.Element("tsRequest")
permissions_element = ET.SubElement(xml_request, "permissions")

Expand Down
0