8000 Support for Certified Data Sources in the REST API by t8y8 · Pull Request #189 · tableau/server-client-python · GitHub
[go: up one dir, main page]

Skip to content

Support for Certified Data Sources in the REST API #189

New issue 8000

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 5 commits into from
Jun 27, 2017
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
51 changes: 40 additions & 11 deletions tableauserverclient/models/datasource_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import xml.etree.ElementTree as ET
from .exceptions import UnpopulatedPropertyError
from .property_decorators import property_not_nullable
from .property_decorators import property_not_nullable, property_is_boolean
from .tag_item import TagItem
from .. import NAMESPACE
from ..datetime_helpers import parse_datetime
Expand All @@ -17,6 +17,8 @@ def __init__(self, project_id, name=None):
self._ 8000 initial_tags = set()
self._project_name = None
self._updated_at = None
self._certified = None
self._certification_note = None
self.name = name
self.owner_id = None
self.project_id = project_id
Expand All @@ -37,6 +39,24 @@ def content_url(self):
def created_at(self):
return self._created_at

@property
def certified(self):
return self._certified

@certified.setter
@property_not_nullable
@property_is_boolean
def certified(self, value):
self._certified = value

@property
def certification_note(self):
return self._certification_note

@certification_note.setter
def certification_note(self, value):
self._certification_note = value

@property
def id(self):
return self._id
Expand Down Expand Up @@ -65,16 +85,18 @@ def updated_at(self):
def _set_connections(self, connections):
self._connections = connections

def _parse_common_tags(self, datasource_xml):
def _parse_common_elements(self, datasource_xml):
if not isinstance(datasource_xml, ET.Element):
datasource_xml = ET.fromstring(datasource_xml).find('.//t:datasource', namespaces=NAMESPACE)
if datasource_xml is not None:
(_, _, _, _, _, updated_at, _, project_id, project_name, owner_id) = self._parse_element(datasource_xml)
self._set_values(None, None, None, None, None, updated_at, None, project_id, project_name, owner_id)
(_, _, _, _, _, updated_at, _, project_id, project_name, owner_id,
certified, certification_note) = self._parse_element(datasource_xml)
self._set_values(None, None, None, None, None, updated_at, None, project_id,
project_name, owner_id, certified, certification_note)
return self

def _set_values(self, id, name, datasource_type, content_url, created_at,
updated_at, tags, project_id, project_name, owner_id):
updated_at, tags, project_id, project_name, owner_id, certified, certification_note):
if id is not None:
self._id = id
if name:
Expand All @@ -96,6 +118,9 @@ def _set_values(self, id, name, datasource_type, content_url, created_at,
self._project_name = project_name
if owner_id:
self.owner_id = owner_id
if certification_note:
self.certification_note = certification_note
self.certified = certified # Always True/False, not conditional

@classmethod
def from_response(cls, resp):
Expand All @@ -104,22 +129,25 @@ def from_response(cls, resp):
all_datasource_xml = parsed_response.findall('.//t:datasource', namespaces=NAMESPACE)

for datasource_xml in all_datasource_xml:
(id, name, datasource_type, content_url, created_at, updated_at,
tags, project_id, project_name, owner_id) = cls._parse_element(datasource_xml)
(id_, name, datasource_type, content_url, created_at, updated_at,
tags, project_id, project_name, owner_id,
certified, certification_note) = cls._parse_element(datasource_xml)
datasource_item = cls(project_id)
datasource_item._set_values(id, name, datasource_type, content_url, created_at, updated_at,
tags, None, project_name, owner_id)
datasource_item._set_values(id_, name, datasource_type, content_url, created_at, updated_at,
tags, None, project_name, owner_id, certified, certification_note)
all_datasource_items.append(datasource_item)
return all_datasource_items

@staticmethod
def _parse_element(datasource_xml):
id = datasource_xml.get('id', None)
id_ = datasource_xml.get('id', None)
name = datasource_xml.get('name', None)
datasource_type = datasource_xml.get('type', None)
content_url = datasource_xml.get('contentUrl', None)
created_at = parse_datetime(datasource_xml.get('createdAt', None))
updated_at = parse_datetime(datasource_xml.get('updatedAt', None))
certification_note = datasource_xml.get('certificationNote', None)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you have a certification note without being certified? Just curious?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Technically yes. The UI may choose to not display them (yet) but it's not expressly forbidden. We may also add UI support for this anywho.

certified = str(datasource_xml.get('isCertified', None)).lower() == 'true'

tags = None
tags_elem = datasource_xml.find('.//t:tags', namespaces=NAMESPACE)
Expand All @@ -138,4 +166,5 @@ def _parse_element(datasource_xml):
if owner_elem is not None:
owner_id = owner_elem.get('id', None)

return id, name, datasource_type, content_url, created_at, updated_at, tags, project_id, project_name, owner_id
return (id_, name, datasource_type, content_url, created_at, updated_at, tags, project_id,
project_name, owner_id, certified, certification_note)
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def update(self, datasource_item):
server_response = self.put_request(url, update_req)
logger.info('Updated datasource item (ID: {0})'.format(datasource_item.id))
updated_datasource = copy.copy(datasource_item)
return updated_datasource._parse_common_tags(server_response.content)
return updated_datasource._parse_common_elements(server_response.content)

# Publish datasource
@api(version="2.0")
Expand Down
6 changes: 6 additions & 0 deletions tableauserverclient/server/request_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ def update_req(self, datasource_item):
if datasource_item.owner_id:
owner_element = ET.SubElement(datasource_element, 'owner')
owner_element.attrib['id'] = datasource_item.owner_id

datasource_element.attrib['isCertified'] = str(datasource_item.certified).lower()

if datasource_item.certification_note:
datasource_element.attrib['certificationNote'] = str(datasource_item.certification_note)

return ET.tostring(xml_request)

def publish_req(self, datasource_item, filename, file_contents, connection_credentials=None):
Expand Down
2 changes: 1 addition & 1 deletion test/assets/datasource_update.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<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">
<datasource id="9dbd2263-16b5-46e1-9c43-a76bb8ab65fb" name="Sample datasource" contentUrl="Sampledatasource" type="dataengine" createdAt="2016-08-04T21:31:55Z" updatedAt="2016-08-17T20:13:13Z">
<datasource id="9dbd2263-16b5-46e1-9c43-a76bb8ab65fb" name="Sample datasource" contentUrl="Sampledatasource" type="dataengine" createdAt="2016-08-04T21:31:55Z" updatedAt="2016-08-17T20:13:13Z" isCertified="True" certificationNote="Warning, here be dragons.">
<project id="1d0304cd-3796-429f-b815-7258370b9b74" />
<owner id="dd2239f6-ddf1-4107-981a-4cf94e415794" />
</datasource>
Expand Down
4 changes: 4 additions & 0 deletions test/test_datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,15 @@ def test_update(self):
single_datasource = TSC.DatasourceItem('test', '1d0304cd-3796-429f-b815-7258370b9b74')
single_datasource.owner_id = 'dd2239f6-ddf1-4107-981a-4cf94e415794'
single_datasource._id = '9dbd2263-16b5-46e1-9c43-a76bb8ab65fb'
single_datasource.certified = True
single_datasource.certification_note = "Warning, here be dragons."
single_datasource = self.server.datasources.update(single_datasource)

self.assertEqual('9dbd2263-16b5-46e1-9c43-a76bb8ab65fb', single_datasource.id)
self.assertEqual('1d0304cd-3796-429f-b815-7258370b9b74', single_datasource.project_id)
self.assertEqual('dd2239f6-ddf1-4107-981a-4cf94e415794', single_datasource.owner_id)
self.assertEqual(True, single_datasource.certified)
self.assertEqual("Warning, here be dragons.", single_datasource.certification_note)

def test_update_copy_fields(self):
with open(UPDATE_XML, 'rb') as f:
Expand Down
0