diff --git a/docs/docs/api-ref.md b/docs/docs/api-ref.md
index 7b22c3517..81d1211dd 100644
--- a/docs/docs/api-ref.md
+++ b/docs/docs/api-ref.md
@@ -849,6 +849,52 @@ Error | Description
+#### groups.update
+
+```py
+groups.update(group_item, default_site_role=UserItem.Roles.Unlicensed)
+```
+
+Updates the group on the site.
+If domain_name = 'local' then update only the name of the group.
+If not - update group from the Active Directory with domain_name.
+
+REST API: [Update Group](http://onlinehelp.tableau.com/current/api/rest_api/en-us/help.htm#REST/rest_api_ref.htm#Update_Group%3FTocPath%3DAPI%2520Reference%7C_____95){:target="_blank"}
+
+
+**Parameters**
+
+Name | Description
+:--- | :---
+`group_item` | the group_item specifies the group to update.
+`default_site_role` | if group updates from Active Directory then this is the default role for the new users.
+
+
+**Exceptions**
+
+Error | Description
+:--- | :---
+`Group item missing ID` | Raises an exception if a valid `group_item.id` is not provided.
+
+
+**Example**
+
+```py
+# Update a group
+
+# import tableauserverclient as TSC
+# tableau_auth = TSC.TableauAuth('USERNAME', 'PASSWORD')
+# server = TSC.Server('http://SERVERURL')
+
+ with server.auth.sign_in(tableau_auth):
+ all_groups, pagination_item = server.groups.get()
+
+ for group in all_groups:
+ server.groups.update(group)
+```
+
+
+
#### groups.get
```py
diff --git a/tableauserverclient/server/endpoint/groups_endpoint.py b/tableauserverclient/server/endpoint/groups_endpoint.py
index bee595b25..2428ff9be 100644
--- a/tableauserverclient/server/endpoint/groups_endpoint.py
+++ b/tableauserverclient/server/endpoint/groups_endpoint.py
@@ -7,6 +7,8 @@
logger = logging.getLogger('tableau.endpoint.groups')
+UNLICENSED_USER = UserItem.Roles.Unlicensed
+
class Groups(Endpoint):
@property
@@ -55,6 +57,18 @@ def delete(self, group_id):
self.delete_request(url)
logger.info('Deleted single group (ID: {0})'.format(group_id))
+ @api(version="2.0")
+ def update(self, group_item, default_site_role=UNLICENSED_USER):
+ if not group_item.id:
+ error = "Group item missing ID."
+ raise MissingRequiredFieldError(error)
+ url = "{0}/{1}".format(self.baseurl, group_item.id)
+ update_req = RequestFactory.Group.update_req(group_item, default_site_role)
+ server_response = self.put_request(url, update_req)
+ logger.info('Updated group item (ID: {0})'.format(group_item.id))
+ updated_group = GroupItem.from_response(server_response.content, self.parent_srv.namespace)[0]
+ return updated_group
+
# Create a 'local' Tableau group
@api(version="2.0")
def create(self, group_item):
diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py
index c4f10d731..d07a30362 100644
--- a/tableauserverclient/server/request_factory.py
+++ b/tableauserverclient/server/request_factory.py
@@ -107,6 +107,17 @@ def create_req(self, group_item):
group_element.attrib['name'] = group_item.name
return ET.tostring(xml_request)
+ def update_req(self, group_item, 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'] = default_site_role
+ return ET.tostring(xml_request)
+
class PermissionRequest(object):
def _add_capability(self, parent_element, capability_set, mode):
diff --git a/test/assets/group_update.xml b/test/assets/group_update.xml
new file mode 100644
index 000000000..b5dba4bc6
--- /dev/null
+++ b/test/assets/group_update.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/test/test_group.py b/test/test_group.py
index 244ba47b8..7096ca408 100644
--- a/test/test_group.py
+++ b/test/test_group.py
@@ -14,6 +14,7 @@
ADD_USER_POPULATE = os.path.join(TEST_ASSET_DIR, 'group_users_added.xml')
CREATE_GROUP = os.path.join(TEST_ASSET_DIR, 'group_create.xml')
CREATE_GROUP_ASYNC = os.path.join(TEST_ASSET_DIR, 'group_create_async.xml')
+UPDATE_XML = os.path.join(TEST_ASSET_DIR, 'group_update.xml')
class GroupTests(unittest.TestCase):
@@ -183,3 +184,16 @@ def test_create_group(self):
group = self.server.groups.create(group_to_create)
self.assertEqual(group.name, u'試供品')
self.assertEqual(group.id, '3e4a9ea0-a07a-4fe6-b50f-c345c8c81034')
+
+ def test_update(self):
+ with open(UPDATE_XML, 'rb') as f:
+ response_xml = f.read().decode('utf-8')
+ with requests_mock.mock() as m:
+ m.put(self.baseurl + '/ef8b19c0-43b6-11e6-af50-63f5805dbe3c', text=response_xml)
+ group = TSC.GroupItem(name='Test Group')
+ group._domain_name = 'local'
+ group._id = 'ef8b19c0-43b6-11e6-af50-63f5805dbe3c'
+ group = self.server.groups.update(group)
+
+ self.assertEqual('ef8b19c0-43b6-11e6-af50-63f5805dbe3c', group.id)
+ self.assertEqual('Group updated name', group.name)