10000 Add new fields to ProjectItem and clean up parsing logic · gmiretti/server-client-python@b3d740c · GitHub
[go: up one dir, main page]

Skip to content

Commit b3d740c

Browse files
author
Chris Shin
committed
Add new fields to ProjectItem and clean up parsing logic
1 parent cacc4fb commit b3d740c

File tree

5 files changed

+134
-75
lines changed

5 files changed

+134
-75
lines changed
Lines changed: 87 additions & 34 deletions
< 10000 td data-grid-cell-id="diff-fe12d749387d81c5a1c5384eeae35f39dca342f118f939d14e3154b761146f49-62-110-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import xml.etree.ElementTree as ET
2-
from .property_decorators import property_is_enum, property_not_empty
2+
from .property_decorators import property_is_enum, property_not_empty, property_not_nullable
3+
from ..datetime_helpers import parse_datetime
34

45

56
class ProjectItem(object):
@@ -8,11 +9,16 @@ class ContentPermissions:
89
ManagedByOwner = 'ManagedByOwner'
910

1011
def __init__(self, name, description=None, content_permissions=None, parent_id=None):
11-
self._content_permissions = None
12+
self._created_at = None
1213
self._id = None
14+
self._owner_id = None
15+
self._owner_name = None
16+
self._top_level_project = None
17+
self._updated_at = None
18+
19+
self.content_permissions = content_permissions
1320
self.description = description
1421
self.name = name
15-
self.content_permissions = content_permissions
1622
self.parent_id = parent_id
1723

1824
@property
@@ -24,6 +30,18 @@ def content_permissions(self):
2430
def content_permissions(self, value):
2531
self._content_permissions = value
2632

33+
@property
34+
def created_at(self):
35+
return self._created_at
36+
37+
@property
38+
def description(self):
39+
return self._description
40+
41+
@description.setter
42+
def description(self, value):
43+
self._description = value
44+
2745
@property
2846
def id(self):
2947
return self._id
@@ -34,32 +52,62 @@ def name(self):
3452

3553
@name.setter
3654
@property_not_empty
55+
@property_not_nullable
3756
def name(self, value):
3857
self._name = value
3958

59+
@property
60+
def owner_id(self):
61+
return self._owner_id
62+
63+
@property
64+
def owner_name(self):
65+
return self._owner_name
66+
67+
@property
68+
def parent_id(self):
69+
return self._parent_id
70+
71+
@parent_id.setter
72+
def parent_id(self, value):
73+
self._parent_id = value
74+
75+
@property
76+
def top_level_project(self):
77+
return self._top_level_project
78+
79+
@property
80+
def updated_at(self):
81+
return self._updated_at
82+
4083
def is_default(self):
4184
return self.name.lower() == 'default'
4285

43-
def _parse_common_tags(self, project_xml):
44-
if not isinstance(project_xml, ET.Element):
45-
project_xml = ET.fromstring(project_xml).find('.//t:project', namespaces=NAMESPACE)
46-
47-
if project_xml is not None:
48-
(_, name, description, content_permissions, parent_id) = self._parse_element(project_xml)
49-
self._set_values(None, name, description, content_permissions, parent_id)
50-
return self
51-
52-
def _set_values(self, project_id, name, description, content_permissions, parent_id):
53-
if project_id is not None:
54-
self._id = project_id
55-
if name:
56-
self._name = name
57-
if description:
58-
self.description = description
59-
if content_permissions:
60-
self._content_permissions = content_permissions
61-
if parent_id:
62-
self.parent_id = parent_id
86+
def _set_values(self, project_fields):
87+
if 'contentPermissions' in project_fields:
88+
self._content_permissions = project_fields['contentPermissions']
89+
if 'createdAt' in project_fields:
90+
self._created_at = parse_datetime(project_fields['createdAt'])
91+
if 'description' in project_fields:
92+
self._description = project_fields['description']
93+
if 'id' in project_fields:
94+
self._id = project_fields['id']
95+
if 'name' in project_fields:
96+
self._name = project_fields['name']
97+
if 'parentProjectId' in project_fields:
98+
self._parent_id = project_fields['parentProjectId']
99+
if 'topLevelProject' in project_fields:
100+
self._top_level_project = string_to_bool(project_fields['topLevelProject'])
101+
if 'updatedAt' in project_fields:
102+
self._updated_at = parse_datetime(project_fields['updatedAt'])
103+
if 'owner' in project_fields:
104+
owner_fields = project_fields['owner']
105+
if 'id' in owner_fields:
106+
self._owner_id = owner_fields['id']
107+
if 'name' in owner_fields:
108+
self._owner_name = owner_fields['name']
109+
if self.parent_id is not None:
110+
self._top_level_project = False
63111

64112
@classmethod
65113
def from_response(cls, resp, ns):
@@ -68,18 +116,23 @@ def from_response(cls, resp, ns):
68116
all_project_xml = parsed_response.findall('.//t:project', namespaces=ns)
69117

70118
for project_xml in all_project_xml:
71-
(id, name, description, content_permissions, parent_id) = cls._parse_element(project_xml)
72-
project_item = cls(name)
73-
project_item._set_values(id, name, description, content_permissions, parent_id)
119+
project_fields = cls._parse_element(project_xml, ns)
120+
project_item = cls(project_fields['name'])
121+
project_item._set_values(project_fields)
74122
all_project_items.append(project_item)
75123
return all_project_items
76124

77125
@staticmethod
78-
def _parse_element(project_xml):
79-
id = project_xml.get('id', None)
80-
name = project_xml.get('name', None)
81-
description = project_xml.get('description', None)
82-
content_permissions = project_xml.get('contentPermissions', None)
83-
parent_id = project_xml.get('parentProjectId', None)
84-
85-
return id, name, description, content_permissions, parent_id
126+
def _parse_element(project_xml, ns):
127+
project_fields = project_xml.attrib
128+
owner_elem = project_xml.find('.//t:owner', namespaces=ns)
129+
if owner_elem is not None:
130+
owner_fields = owner_elem.attrib
131+
project_fields['owner'] = owner_fields
132+
133+
return project_fields
134+
135+
136+
# Used to convert string represented boolean to a boolean type
137+
def string_to_bool(s):
138+
return s.lower() == 'true'

tableauserverclient/server/endpoint/projects_endpoint.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .endpoint import Endpoint, api
1+
from .endpoint import Endpoint, api, parameter_added_in
22
from .exceptions import MissingRequiredFieldError
33
from .. import RequestFactory, ProjectItem, PaginationItem
44
import logging
@@ -14,7 +14,7 @@ def baseurl(self):
1414
@api(version="2.0")
1515
def get(self, req_options=None):
1616
logger.info('Querying all projects on site')
17-
url = self.baseurl
17+
url = self.baseurl + '?fields=_all_'
1818
server_response = self.get_request(url, req_options)
1919
pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
2020
all_project_items = ProjectItem.from_response(server_response.content, self.parent_srv.namespace)
@@ -30,22 +30,28 @@ def delete(self, project_id):
3030
logger.info('Deleted single project (ID: {0})'.format(project_id))
3131

3232
@api(version="2.0")
33-
def update(self, project_item):
33+
@parameter_added_in(publish_samples="2.5")
34+
def update(self, project_item, publish_samples=False):
3435
if not project_item.id:
3536
error = "Project item missing ID."
3637
raise MissingRequiredFieldError(error)
3738

3839
url = "{0}/{1}".format(self.baseurl, project_item.id)
39-
update_req = RequestFactory.Project.update_req(project_item)
40+
if publish_samples:
41+
url += '?publishSamples=true'
42+
update_req = RequestFactory.Project.common_req(project_item)
4043
server_response = self.put_request(url, update_req)
4144
logger.info('Updated project item (ID: {0})'.format(project_item.id))
4245
updated_project = ProjectItem.from_response(server_response.content, self.parent_srv.namespace)[0]
4346
return updated_project
4447

4548
@api(version="2.0")
46-
def create(self, project_item):
49+
@parameter_added_in(publish_samples="2.5")
50+
def create(self, project_item, publish_samples=False):
4751
url = self.baseurl
48-
create_req = RequestFactory.Project.create_req(project_item)
52+
if publish_samples:
53+
url += '?publishSamples=true'
54+
create_req = RequestFactory.Project.common_req(project_item)
4955
server_response = self.post_request(url, create_req)
5056
new_project = ProjectItem.from_response(server_response.content, self.parent_srv.namespace)[0]
5157
logger.info('Created new project (ID: {0})'.format(new_project.id))

tableauserverclient/server/request_factory.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,18 +170,7 @@ def add_req(self, permission_item):
170170

171171

172172
class ProjectRequest(object):
173-
def update_req(self, project_item):
174-
xml_request = ET.Element('tsRequest')
175-
project_element = ET.SubElement(xml_request, 'project')
176-
if project_item.name:
177-
project_element.attrib['name'] = project_item.name
178-
if project_item.description:
179-
project_element.attrib['description'] = project_item.description
180-
if project_item.content_permissions:
181-
project_element.attrib['contentPermissions'] = project_item.content_permissions
182-
return ET.tostring(xml_request)
183-
184-
def create_req(self, project_item):
173+
def common_req(self, project_item):
185174
xml_request = ET.Element('tsRequest')
186175
project_element = ET.SubElement(xml_request, 'project')
187176
project_element.attrib['name'] = project_item.name

test/assets/project_get.xml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
<?xml version='1.0' encoding='UTF-8'?>
2-
<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">
3-
<pagination pageNumber="1" pageSize="100" totalAvailable="3" />
2+
<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-3.4.xsd">
3+
<pagination pageNumber="1" pageSize="100" totalAvailable="2"/>
44
<projects>
5-
<project id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760" name="default" description="The default project that was automatically created by Tableau." contentPermissions="ManagedByOwner" />
6-
<project id="1d0304cd-3796-429f-b815-7258370b9b74" name="Tableau" description="" contentPermissions="ManagedByOwner" />
7-
<project id="4cc52973-5e3a-4d1f-a4fb-5b5f73796edf" name="Tableau > Child 1" description="" contentPermissions="ManagedByOwner" parentProjectId="1d0304cd-3796-429f-b815-7258370b9b74" />
5+
<project id="bdd975c6-4042-11e9-a712-975dc31937aa" name="Default" description="The default project that was automatically created by Tableau." topLevelProject="true" createdAt="2019-03-06T19:04:57Z" updatedAt="2019-03-06T19:04:58Z" contentPermissions="ManagedByOwner">
6+
<owner id="f9e32d4b-ca36-43bb-bc58-29ad45b10be5" name="_system" siteRole="Unlicensed"/>
7+
</project>
8+
<project id="7e593a18-c6e2-469c-9aca-4b2782693777" name="update" description="upd" topLevelProject="false" parentProjectId="bdd975c6-4042-11e9-a712-975dc31937aa" createdAt="2019-03-13T22:18:18Z" updatedAt="2019-03-14T17:13:40Z" contentPermissions="ManagedByOwner">
9+
<owner id="344356bd-a847-4d6c-8370-8b2821498cdb" name="testadmin" fullName="testadmin" siteRole="ServerAdministrator" lastLogin="2019-03-14T17:12:50Z"/>
10+
</project>
811
</projects>
9-
</tsResponse>
12+
</tsResponse>

test/test_project.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import requests_mock
44
import tableauserverclient as TSC
5+
from tableauserverclient.datetime_helpers import format_datetime
56

67
TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), 'assets')
78

@@ -27,23 +28,30 @@ def test_get(self):
2728
m.get(self.baseurl, text=response_xml)
2829
all_projects, pagination_item = self.server.projects.get()
2930

30-
self.assertEqual(3, pagination_item.total_available)
31-
self.assertEqual('ee8c6e70-43b6-11e6-af4f-f7b0d8e20760', all_projects[0].id)
32-
self.assertEqual('default', all_projects[0].name)
33-
self.assertEqual('The default project that was automatically created by Tableau.',
34-
all_projects[0].description)
35-
self.assertEqual('ManagedByOwner', all_projects[0].content_permissions)
36-
self.assertEqual(None, all_projects[0].parent_id)
37-
38-
self.assertEqual('1d0304cd-3796-429f-b815-7258370b9b74', all_projects[1].id)
39-
self.assertEqual('Tableau', all_projects[1].name)
40-
self.assertEqual('ManagedByOwner', all_projects[1].content_permissions)
41-
self.assertEqual(None, all_projects[1].parent_id)
42-
43-
self.assertEqual('4cc52973-5e3a-4d1f-a4fb-5b5f73796edf', all_projects[2].id)
44-
self.assertEqual('Tableau > Child 1', all_projects[2].name)
45-
self.assertEqual('ManagedByOwner', all_projects[2].content_permissions)
46-
self.assertEqual('1d0304cd-3796-429f-b815-7258370b9b74', all_projects[2].parent_id)
31+
self.assertEqual(2, pagination_item.total_available)
32+
pr1 = all_projects[0]
33+
pr2 = all_projects[1]
34+
35+
self.assertEqual('bdd975c6-4042-11e9-a712-975dc31937aa', pr1.id)
36+
self.assertEqual('Default', pr1.name)
37+
self.assertEqual('The default project that was automatically created by Tableau.', pr1.description)
38+
self.assertEqual(True, pr1.top_level_project)
39+
self.assertEqual('2019-03-06T19:04:57Z', format_datetime(pr1.created_at))
40+
self.assertEqual('2019-03-06T19:04:58Z', format_datetime(pr1.updated_at))
41+
self.assertEqual('ManagedByOwner', pr1.content_permissions)
42+
self.assertEqual('f9e32d4b-ca36-43bb-bc58-29ad45b10be5', pr1.owner_id)
43+
self.assertEqual('_system', pr1.owner_name)
44+
45+
self.assertEqual('7e593a18-c6e2-469c-9aca-4b2782693777', pr2.id)
46+
self.assertEqual('update', pr2.name)
47+
self.assertEqual('upd', pr2.description)
48+
self.assertEqual(False, pr2.top_level_project)
49+
self.assertEqual('bdd975c6-4042-11e9-a712-975dc31937aa', pr2.parent_id)
50+
self.assertEqual('2019-03-13T22:18:18Z', format_datetime(pr2.created_at))
51+
self.assertEqual('2019-03-14T17:13:40Z', format_datetime(pr2.updated_at))
52+
self.assertEqual('ManagedByOwner', pr2.content_permissions)
53+
self.assertEqual('344356bd-a847-4d6c-8370-8b2821498cdb', pr2.owner_id)
54+
self.assertEqual('testadmin', pr2.owner_name)
4755

4856
def test_get_before_signin(self):
4957
self.server._auth_token = None

0 commit comments

Comments
 (0)
0