8000 Merge pull request #672 from tableau/extracts · tableau/server-client-python@b355c8f · GitHub
[go: up one dir, main page]

Skip to content

Commit b355c8f

Browse files
authored
Merge pull request #672 from tableau/extracts
Extract operations to match tabcmd
2 parents 972f10a + 81c795b commit b355c8f

File tree

8 files changed

+153
-1
lines changed

8 files changed

+153
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 0.13 (19 Aug 2020)
2+
* Add support for basic Extract operations - Create, Delete, en/re/decrypt for site
3+
14
## 0.12.1 (22 July 2020)
25

36
* Fixed login.py sample to properly handle sitename (#652)

tableauserverclient/server/endpoint/datasources_endpoint.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ def refresh(self, datasource_item):
152152
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
153153
return new_job
154154

155+
@api(version='3.5')
156+
def create_extract(self, datasource_item, encrypt=False):
157+
id_ = getattr(datasource_item, 'id', datasource_item)
158+
url = "{0}/{1}/createExtract?encrypt={2}".format(self.baseurl, id_, encrypt)
159+
empty_req = RequestFactory.Empty.empty_req()
160+
server_response = self.post_request(url, empty_req)
161+
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
162+
return new_job
163+
164+
@api(version='3.5')
165+
def delete_extract(self, datasource_item):
166+
id_ = getattr(datasource_item, 'id', datasource_item)
167+
url = "{0}/{1}/deleteExtract".format(self.baseurl, id_)
168+
empty_req = RequestFactory.Empty.empty_req()
169+
self.post_request(url, empty_req)
170+
155171
# Publish datasource
156172
@api(version="2.0")
157173
@parameter_added_in(connections="2.8")

tableauserverclient/server/endpoint/sites_endpoint.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,31 @@ def create(self, site_item):
103103
new_site = SiteItem.from_response(server_response.content, self.parent_srv.namespace)[0]
104104
logger.info('Created new site (ID: {0})'.format(new_site.id))
105105
return new_site
106+
107+
@api(version="3.5")
108+
def encrypt_extracts(self, site_id):
109+
if not site_id:
110+
error = "Site ID undefined."
111+
raise ValueError(error)
112+
url = "{0}/{1}/encrypt-extracts".format(self.baseurl, site_id)
113+
empty_req = RequestFactory.Empty.empty_req()
114+
self.post_request(url, empty_req)
115+
116+
@api(version="3.5")
117+
def decrypt_extracts(self, site_id):
118+
if not site_id:
119+
error = "Site ID undefined."
120+
raise ValueError(error)
121+
url = "{0}/{1}/decrypt-extracts".format(self.baseurl, site_id)
122+
empty_req = RequestFactory.Empty.empty_req()
123+
self.post_request(url, empty_req)
124+
125+
@api(version="3.5")
126+
def re_encrypt_extracts(self, site_id):
127+
if not site_id:
128+
error = "Site ID undefined."
129+
raise ValueError(error)
130+
url = "{0}/{1}/reencrypt-extracts".format(self.baseurl, site_id)
131+
132+
empty_req = RequestFactory.Empty.empty_req()
133+
self.post_request(url, empty_req)

tableauserverclient/server/endpoint/workbooks_endpoint.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,25 @@ def refresh(self, workbook_id):
6161
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
6262
return new_job
6363

64+
# create one or more extracts on 1 workbook, optionally encrypted
65+
@api(version='3.5')
66+
def create_extract(self, workbook_item, encrypt=False, includeAll=True, datasources=None):
67+
id_ = getattr(workbook_item, 'id', workbook_item)
68+
url = "{0}/{1}/createExtract?encrypt={2}".format(self.baseurl, id_, encrypt)
69+
70+
datasource_req = RequestFactory.Workbook.embedded_extract_req(includeAll, datasources)
71+
server_response = self.post_request(url, datasource_req)
72+
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
73+
return new_job
74+
75+
# delete all the extracts on 1 workbook
76+
@api(version='3.5')
77+
def delete_extract(self, workbook_item):
78+
id_ = getattr(workbook_item, 'id', workbook_item)
79+
url = "{0}/{1}/deleteExtract".format(self.baseurl, id_)
80+
empty_req = RequestFactory.Empty.empty_req()
81+
server_response = self.post_request(url, empty_req)
82+
6483
# Delete 1 workbook by id
6584
@api(version="2.0")
6685
def delete(self, workbook_id):

tableauserverclient/server/request_factory.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,16 @@ def publish_req_chunked(
568568
parts = {'request_payload': ('', xml_request, 'text/xml')}
569569
return _add_multipart(parts)
570570

571+
@_tsrequest_wrapped
572+
def embedded_extract_req(self, xml_request, include_all=True, datasources=None):
573+
list_element = ET.SubElement(xml_request, 'datasources')
574+
if include_all:
575+
list_element.attrib['includeAll'] = "true"
576+
else:
577+
for datasource_item in datasources:
578+
datasource_element = list_element.SubElement(xml_request, 'datasource')
579+
datasource_element.attrib['id'] = datasource_item.id
580+
571581

572582
class Connection(object):
573583
@_tsrequest_wrapped
@@ -623,7 +633,7 @@ def create_req(self, xml_request, webhook_item):
623633
webhook.attrib['name'] = webhook_item.name
624634

625635
source = ET.SubElement(webhook, 'webhook-source')
626-
event = ET.SubElement(source, webhook_item._event)
636+
ET.SubElement(source, webhook_item._event)
627637

628638
destination = ET.SubElement(webhook, 'webhook-destination')
629639
post = ET.SubElement(destination, 'webhook-destination-http')

test/test_datasource.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,3 +381,30 @@ def test_synchronous_publish_timeout_error(self):
381381
self.assertRaisesRegex(InternalServerError, 'Please use asynchronous publishing to avoid timeouts.',
382382
self.server.datasources.publish, new_datasource,
383383
asset('SampleDS.tds'), publish_mode)
384+
385+
def test_delete_extracts(self):
386+
self.server.version = "3.10"
387+
self.baseurl = self.server.datasources.baseurl
388+
with requests_mock.mock() as m:
389+
m.post(self.baseurl + '/3cc6cd06-89ce-4fdc-b935-5294135d6d42/deleteExtract', status_code=200)
390+
self.server.datasources.delete_extract('3cc6cd06-89ce-4fdc-b935-5294135d6d42')
391+
392+
def test_create_extracts(self):
393+
self.server.version = "3.10"
394+
self.baseurl = self.server.datasources.baseurl
395+
396+
response_xml = read_xml_asset(PUBLISH_XML_ASYNC)
397+
with requests_mock.mock() as m:
398+
m.post(self.baseurl + '/3cc6cd06-89ce-4fdc-b935-5294135d6d42/createExtract',
399+
status_code=200, text=response_xml)
400+
self.server.datasources.create_extract('3cc6cd06-89ce-4fdc-b935-5294135d6d42')
401+
402+
def test_create_extracts_encrypted(self):
403+
self.server.version = "3.10"
404+
self.baseurl = self.server.datasources.baseurl
405+
406+
response_xml = read_xml_asset(PUBLISH_XML_ASYNC)
407+
with requests_mock.mock() as m:
408+
m.post(self.baseurl + '/3cc6cd06-89ce-4fdc-b935-5294135d6d42/createExtract',
409+
status_code=200, text=response_xml)
410+
self.server.datasources.create_extract('3cc6cd06-89ce-4fdc-b935-5294135d6d42', True)

test/test_site.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
class SiteTests(unittest.TestCase):
1616
def setUp(self):
1717
self.server = TSC.Server('http://test')
18+
self.server.version = "3.10"
1819

1920
# Fake signin
2021
self.server._auth_token = 'j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM'
@@ -140,3 +141,18 @@ def test_delete(self):
140141

141142
def test_delete_missing_id(self):
142143
self.assertRaises(ValueError, self.server.sites.delete, '')
144+
145+
def test_encrypt(self):
146+
with requests_mock.mock() as m:
147+
m.post(self.baseurl + '/0626857c-1def-4503-a7d8-7907c3ff9d9f/encrypt-extracts', status_code=200)
148+
self.server.sites.encrypt_extracts('0626857c-1def-4503-a7d8-7907c3ff9d9f')
149+
150+
def test_recrypt(self):
151+
with requests_mock.mock() as m:
152+
m.post(self.baseurl + '/0626857c-1def-4503-a7d8-7907c3ff9d9f/reencrypt-extracts', status_code=200)
153+
self.server.sites.re_encrypt_extracts('0626857c-1def-4503-a7d8-7907c3ff9d9f')
154+
155+
def test_decrypt(self):
156+
with requests_mock.mock() as m:
157+
m.post(self.baseurl + '/0626857c-1def-4503-a7d8-7907c3ff9d9f/decrypt-extracts', status_code=200)
158+
self.server.sites.decrypt_extracts('0626857c-1def-4503-a7d8-7907c3ff9d9f')

test/test_workbook.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ def test_publish_with_hidden_view(self):
470470
hidden_views=['GDP per capita'])
471471

472472
request_body = m._adapter.request_history[0]._request.body
473+
# order of attributes in xml is unspecified
473474
self.assertTrue(re.search(rb'<views><view.*?hidden=\"true\".*?\/><\/views>', request_body))
474475
self.assertTrue(re.search(rb'<views><view.*?name=\"GDP per capita\".*?\/><\/views>', request_body))
475476

@@ -566,3 +567,35 @@ def test_synchronous_publish_timeout_error(self):
566567

567568
self.assertRaisesRegex(InternalServerError, 'Please use asynchronous publishing to avoid timeouts',
568569
self.server.workbooks.publish, new_workbook, asset('SampleWB.twbx'), publish_mode)
570+
571+
def test_delete_extracts_all(self):
572+
self.server.version = "3.10"
573+
self.baseurl = self.server.workbooks.baseurl
574+
with requests_mock.mock() as m:
575+
m.post(self.baseurl + '/3cc6cd06-89ce-4fdc-b935-5294135d6d42/deleteExtract', status_code=200)
576+
self.server.workbooks.delete_extract('3cc6cd06-89ce-4fdc-b935-5294135d6d42')
577+
578+
def test_create_extracts_all(self):
579+
self.server.version = "3.10"
580+
self.baseurl = self.server.workbooks.baseurl
581+
582+
with open(PUBLISH_ASYNC_XML, 'rb') as f:
583+
response_xml = f.read().decode('utf-8')
584+
with requests_mock.mock() as m:
585+
m.post(self.baseurl + '/3cc6cd06-89ce-4fdc-b935-5294135d6d42/createExtract',
586+
status_code=200, text=response_xml)
587+
self.server.workbooks.create_extract('3cc6cd06-89ce-4fdc-b935-5294135d6d42')
588+
589+
def test_create_extracts_one(self):
590+
self.server.version = "3.10"
591+
self.baseurl = self.server.workbooks.baseurl
592+
593+
datasource = TSC.DatasourceItem('test')
594+
datasource._id = '1f951daf-4061-451a-9df1-69a8062664f2'
595+
596+
with open(PUBLISH_ASYNC_XML, 'rb') as f:
597+
response_xml = f.read().decode('utf-8')
598+
with requests_mock.mock() as m:
599+
m.post(self.baseurl + '/3cc6cd06-89ce-4fdc-b935-5294135d6d42/createExtract',
600+
status_code=200, text=response_xml)
601+
self.server.workbooks.create_extract('3cc6cd06-89ce-4fdc-b935-5294135d6d42', False, datasource)

0 commit comments

Comments
 (0)
0