Skip to content
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
21 changes: 11 additions & 10 deletions tableauserverclient/models/group_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,18 @@ def from_response(cls, resp, ns):
name = group_xml.get('name', None)
group_item = cls(name)
group_item._id = group_xml.get('id', None)
# AD groups have an extra element under this

# Domain name is returned in a domain element for some calls
domain_elem = group_xml.find('.//t:domain', namespaces=ns)
if domain_elem is not None:
group_item.domain_name = domain_elem.get('name', None)

# Import element is returned for both local and AD groups (2020.3+)
import_elem = group_xml.find('.//t:import', namespaces=ns)
if (import_elem is not None):
group_item.domain_name = import_elem.get('domainName')
group_item.license_mode = import_elem.get('grantLicenseMode')
group_item.minimum_site_role = import_elem.get('siteRole')
else:
# local group, we will just have two extra attributes here
group_item.domain_name = 'local'
group_item.license_mode = group_xml.get('grantLicenseMode')
group_item.minimum_site_role = group_xml.get('siteRole')
if import_elem is not None:
group_item.domain_name = import_elem.get('domainName', None)
group_item.license_mode = import_elem.get('grantLicenseMode', None)
group_item.minimum_site_role = import_elem.get('siteRole', None)

all_group_items.append(group_item)
return all_group_items
Expand Down
21 changes: 16 additions & 5 deletions tableauserverclient/server/endpoint/groups_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

logger = logging.getLogger('tableau.endpoint.groups')

UNLICENSED_USER = UserItem.Roles.Unlicensed


class Groups(Endpoint):
@property
Expand Down Expand Up @@ -58,15 +56,28 @@ def delete(self, group_id):
logger.info('Deleted single group (ID: {0})'.format(group_id))

@api(version="2.0")
def update(self, group_item, default_site_role=UNLICENSED_USER, as_job=False):
def update(self, group_item, default_site_role=None, as_job=False):
# (1/8/2021): Deprecated starting v0.15
if default_site_role is not None:
import warnings
warnings.simplefilter('always', DeprecationWarning)
warnings.warn('Groups.update(...default_site_role=""...) is deprecated, '
'please set the minimum_site_role field of GroupItem',
DeprecationWarning)
group_item.minimum_site_role = default_site_role

if not group_item.id:
error = "Group item missing ID."
raise MissingRequiredFieldError(error)
if as_job and (group_item.domain_name is None or group_item.domain_name == 'local'):
error = "Local groups cannot be updated asynchronously."
raise ValueError(error)

url = "{0}/{1}".format(self.baseurl, group_item.id)
update_req = RequestFactory.Group.update_req(group_item, default_site_role)
update_req = RequestFactory.Group.update_req(group_item, None)
server_response = self.put_request(url, update_req)
logger.info('Updated group item (ID: {0})'.format(group_item.id))
if (as_job):
if as_job:
return JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
else:
return GroupItem.from_response(server_response.content, self.parent_srv.namespace)[0]
Expand Down
25 changes: 19 additions & 6 deletions tableauserverclient/server/request_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,17 +299,30 @@ def create_ad_req(self, group_item):
return ET.tostring(xml_request)

def update_req(self, group_item, default_site_role=None):
# (1/8/2021): Deprecated starting v0.15
if default_site_role is not None:
import warnings
warnings.simplefilter('always', DeprecationWarning)
warnings.warn('RequestFactory.Group.update_req(...default_site_role="") is deprecated, '
'please set the minimum_site_role field of GroupItem',
DeprecationWarning)
group_item.minimum_site_role = default_site_role

xml_request = ET.Element('tsRequest')
group_element = ET.SubElement(xml_request, 'group')
group_element.attrib['name'] = group_item.name
if group_item.domain_name != 'local':
project_element = ET.SubElement(group_element, 'import')
project_element.attrib['source'] = "ActiveDirectory"
project_element.attrib['domainName'] = group_item.domain_name
project_element.attrib['siteRole'] = group_item.minimum_site_role
project_element.attrib['grantLicenseMode'] = group_item.license_mode
if group_item.domain_name is not None and group_item.domain_name != 'local':
# Import element is only accepted in the request for AD groups
import_element = ET.SubElement(group_element, 'import')
import_element.attrib['source'] = "ActiveDirectory"
import_element.attrib['domainName'] = group_item.domain_name
import_element.attrib['siteRole'] = group_item.minimum_site_role
if group_item.license_mode is not None:
import_element.attrib['grantLicenseMode'] = group_item.license_mode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we still have an underlying REST bug on changing the license mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is still that bug where you can't change license mode from onLogin -> onSync. Internal 1214605

else:
# Local group request does not accept an 'import' element
if group_item.minimum_site_role is not None:
group_element.attrib['minimumSiteRole'] = group_item.minimum_site_role

return ET.tostring(xml_request)

Expand Down
6 changes: 3 additions & 3 deletions test/assets/group_update.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<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">
<group id="ef8b19c0-43b6-11e6-af50-63f5805dbe3c" name="Group updated name"
siteRole="ExplorerCanPublish"
grantLicenseMode='onLogin'/>/>
<group id="ef8b19c0-43b6-11e6-af50-63f5805dbe3c" name="Group updated name">
<import domainName="local" siteRole="ExplorerCanPublish" grantLicenseMode="onLogin" />
</group>
</tsResponse>
10 changes: 10 additions & 0 deletions test/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,13 @@ def test_update(self):
self.assertEqual('Group updated name', group.name)
self.assertEqual('ExplorerCanPublish', group.minimum_site_role)
self.assertEqual('onLogin', group.license_mode)

# async update is not supported for local groups
def test_update_local_async(self):
group = TSC.GroupItem("myGroup")
group._id = 'ef8b19c0-43b6-11e6-af50-63f5805dbe3c'
self.assertRaises(ValueError, self.server.groups.update, group, as_job=True)

# mimic group returned from server where domain name is set to 'local'
group.domain_name = "local"
self.assertRaises(ValueError, self.server.groups.update, group, as_job=True)