From f140f8a49f835602308cee8609c1f1474c87ea50 Mon Sep 17 00:00:00 2001 From: Russell Hay Date: Tue, 25 Oct 2016 09:44:18 -0700 Subject: [PATCH 01/11] Adding contributing doc that was missing from the repo (#74) --- contributing.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 contributing.md diff --git a/contributing.md b/contributing.md new file mode 100644 index 000000000..b1eda5b55 --- /dev/null +++ b/contributing.md @@ -0,0 +1,55 @@ +# Contributing + +We welcome contributions to this project! + +Contribution can include, but are not limited to, any of the following: + +* File an Issue +* Request a Feature +* Implement a Requested Feature +* Fix an Issue/Bug +* Add/Fix documentation + +Contributions must follow the guidelines outlined on the [Tableau Organization](http://tableau.github.io/) page, though filing an issue or requesting +a feature do not require the CLA. + +## Issues and Feature Requests + +To submit an issue/bug report, or to request a feature, please submit a [github issue](https://github.com/tableau/server-client-python/issues) to the repo. + +If you are submitting a bug report, please provide as much information as you can, including clear and concise repro steps, attaching any necessary +files to assist in the repro. **Be sure to scrub the files of any potentially sensitive information. Issues are public.** + +For a feature request, please try to describe the scenario you are trying to accomplish that requires the feature. This will help us understand +the limitations that you are running into, and provide us with a use case to know if we've satisfied your request. + +### Label usage on Issues + +The core team is responsible for assigning most labels to the issue. Labels +are used for prioritizing the core team's work, and use the following +definitions for labels. + +The following labels are only to be set or changed by the core team: + +* **bug** - A bug is an unintended behavior for existing functionality. It only relates to existing functionality and the behavior that is expected with that functionality. We do not use **bug** to indicate priority. +* **enhancement** - An enhancement is a new piece of functionality and is related to the fact that new code will need to be written in order to close this issue. We do not use **enhancement** to indicate priority. +* **CLARequired** - This label is used to indicate that the contribution will require that the CLA is signed before we can accept a PR. This label should not be used on Issues +* **CLANotRequired** - This label is used to indicate that the contribution does not require a CLA to be signed. This is used for minor fixes and usually around doc fixes or correcting strings. +* **help wanted** - This label on an issue indicates it's a good choice for external contributors to take on. It usually means it's an issue that can be tackled by first time contributors. + +The following labels can be used by the issue creator or anyone in the +community to help us prioritize enhancement and bug fixes that are +causing pain from our users. The short of it is, purple tags are ones that +anyone can add to an issue: + +* **Critical** - This means that you won't be able to use the library until the issues have been resolved. If an issue is already labeled as critical, but you want to show your support for it, add a +1 comment to the issue. This helps us know what issues are really impacting our users. +* **Nice To Have** - This means that the issue doesn't block your usage of the library, but would make your life easier. Like with critical, if the issue is already tagged with this, but you want to show your support, add a +1 comment to the issue. + +## Fixes, Implementations, and Documentation + +For all other things, please submit a PR that includes the fix, documentation, or new code that you are trying to contribute. More information on +creating a PR can be found in the [github documentation](https://help.github.com/articles/creating-a-pull-request/) + +If the feature is complex or has multiple solutions that could be equally appropriate approaches, it would be helpful to file an issue to discuss the +design trade-offs of each solution before implementing, to allow us to collectively arrive at the best solution, which most likely exists in the middle +somewhere. From b6121d174b7660cc891c0e1d71beae2c6f3a5eaa Mon Sep 17 00:00:00 2001 From: geordielad Date: Wed, 26 Oct 2016 19:17:05 -0400 Subject: [PATCH 02/11] Add Connection Credentials Allow users to add connection credentials to Datasources and workbook publish requests. Should work for smaller requests and chunked requests --- tableauserverclient/__init__.py | 4 +-- tableauserverclient/models/__init__.py | 1 + .../models/connection_credentials.py | 5 ++++ .../server/endpoint/datasources_endpoint.py | 7 +++-- .../server/endpoint/workbooks_endpoint.py | 7 +++-- tableauserverclient/server/request_factory.py | 30 ++++++++++++------- 6 files changed, 36 insertions(+), 18 deletions(-) create mode 100644 tableauserverclient/models/connection_credentials.py diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index 9e56919c6..5df3eacbc 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -1,5 +1,5 @@ from .namespace import NAMESPACE -from .models import ConnectionItem, DatasourceItem,\ +from .models import ConnectionCredentials, ConnectionItem, DatasourceItem,\ GroupItem, PaginationItem, ProjectItem, ScheduleItem, \ SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError, \ HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval, IntervalItem @@ -7,4 +7,4 @@ MissingRequiredFieldError, NotSignedInError __version__ = '0.0.1' -__VERSION__ = __version__ +__VERSION__ = __version__ \ No newline at end of file diff --git a/tableauserverclient/models/__init__.py b/tableauserverclient/models/__init__.py index 276684d66..415c84147 100644 --- a/tableauserverclient/models/__init__.py +++ b/tableauserverclient/models/__init__.py @@ -1,3 +1,4 @@ +from .connection_credentials import ConnectionCredentials from .connection_item import ConnectionItem from .datasource_item import DatasourceItem from .exceptions import UnpopulatedPropertyError diff --git a/tableauserverclient/models/connection_credentials.py b/tableauserverclient/models/connection_credentials.py new file mode 100644 index 000000000..931afdd52 --- /dev/null +++ b/tableauserverclient/models/connection_credentials.py @@ -0,0 +1,5 @@ +class ConnectionCredentials(object): + def __init__(self, name, password, embed=True): + self.password = password + self.embed = embed + self.name = name diff --git a/tableauserverclient/server/endpoint/datasources_endpoint.py b/tableauserverclient/server/endpoint/datasources_endpoint.py index ba49c768f..120aeb625 100644 --- a/tableauserverclient/server/endpoint/datasources_endpoint.py +++ b/tableauserverclient/server/endpoint/datasources_endpoint.py @@ -94,7 +94,7 @@ def update(self, datasource_item): return updated_datasource._parse_common_tags(server_response.content) # Publish datasource - def publish(self, datasource_item, file_path, mode): + def publish(self, datasource_item, file_path, mode, connection_credentials=None): if not os.path.isfile(file_path): error = "File path does not lead to an existing file." raise IOError(error) @@ -122,14 +122,15 @@ def publish(self, datasource_item, file_path, mode): logger.info('Publishing {0} to server with chunking method (datasource over 64MB)'.format(filename)) upload_session_id = Fileuploads.upload_chunks(self.parent_srv, file_path) url = "{0}&uploadSessionId={1}".format(url, upload_session_id) - xml_request, content_type = RequestFactory.Datasource.publish_req_chunked(datasource_item) + xml_request, content_type = RequestFactory.Datasource.publish_req_chunked(datasource_item, connection_credentials) else: logger.info('Publishing {0} to server'.format(filename)) with open(file_path, 'rb') as f: file_contents = f.read() xml_request, content_type = RequestFactory.Datasource.publish_req(datasource_item, filename, - file_contents) + file_contents, + connection_credentials) server_response = self.post_request(url, xml_request, content_type) new_datasource = DatasourceItem.from_response(server_response.content)[0] logger.info('Published {0} (ID: {1})'.format(filename, new_datasource.id)) diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index e60789f06..16d09eba1 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -140,7 +140,7 @@ def populate_preview_image(self, workbook_item): logger.info('Populated preview image for workbook (ID: {0})'.format(workbook_item.id)) # Publishes workbook. Chunking method if file over 64MB - def publish(self, workbook_item, file_path, mode): + def publish(self, workbook_item, file_path, mode, connection_credentials=None): if not os.path.isfile(file_path): error = "File path does not lead to an existing file." raise IOError(error) @@ -171,14 +171,15 @@ def publish(self, workbook_item, file_path, mode): logger.info('Publishing {0} to server with chunking method (workbook over 64MB)'.format(filename)) upload_session_id = Fileuploads.upload_chunks(self.parent_srv, file_path) url = "{0}&uploadSessionId={1}".format(url, upload_session_id) - xml_request, content_type = RequestFactory.Workbook.publish_req_chunked(workbook_item) + xml_request, content_type = RequestFactory.Workbook.publish_req_chunked(workbook_item, connection_credentials) else: logger.info('Publishing {0} to server'.format(filename)) with open(file_path, 'rb') as f: file_contents = f.read() xml_request, content_type = RequestFactory.Workbook.publish_req(workbook_item, filename, - file_contents) + file_contents, + connection_credentials) server_response = self.post_request(url, xml_request, content_type) new_workbook = WorkbookItem.from_response(server_response.content)[0] logger.info('Published {0} (ID: {1})'.format(filename, new_workbook.id)) diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index 8f20cfa36..c7ba536d1 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -30,12 +30,17 @@ def signin_req(self, auth_item): class DatasourceRequest(object): - def _generate_xml(self, datasource_item): + def _generate_xml(self, datasource_item, connection_credentials=None): xml_request = ET.Element('tsRequest') datasource_element = ET.SubElement(xml_request, 'datasource') datasource_element.attrib['name'] = datasource_item.name project_element = ET.SubElement(datasource_element, 'project') project_element.attrib['id'] = datasource_item.project_id + if connection_credentials: + credentials_element = ET.SubElement(datasource_element,'connectionCredentials') + credentials_element.attrib['name'] = connection_credentials.name + credentials_element.attrib['password'] = connection_credentials.password + credentials_element.attrib['embed'] = str(connection_credentials.embed).lower() return ET.tostring(xml_request) def update_req(self, datasource_item): @@ -49,15 +54,15 @@ def update_req(self, datasource_item): owner_element.attrib['id'] = datasource_item.owner_id return ET.tostring(xml_request) - def publish_req(self, datasource_item, filename, file_contents): - xml_request = self._generate_xml(datasource_item) + def publish_req(self, datasource_item, filename, file_contents, connection_credentials=None): + xml_request = self._generate_xml(datasource_item, connection_credentials) parts = {'request_payload': ('', xml_request, 'text/xml'), 'tableau_datasource': (filename, file_contents, 'application/octet-stream')} return _add_multipart(parts) - def publish_req_chunked(self, datasource_item): - xml_request = self._generate_xml(datasource_item) + def publish_req_chunked(self, datasource_item, connection_credentials=None): + xml_request = self._generate_xml(datasource_item, connection_credentials) parts = {'request_payload': ('', xml_request, 'text/xml')} return _add_multipart(parts) @@ -260,7 +265,7 @@ def add_req(self, user_item): class WorkbookRequest(object): - def _generate_xml(self, workbook_item): + def _generate_xml(self, workbook_item,connection_credentials=None): xml_request = ET.Element('tsRequest') workbook_element = ET.SubElement(xml_request, 'workbook') workbook_element.attrib['name'] = workbook_item.name @@ -268,6 +273,11 @@ def _generate_xml(self, workbook_item): workbook_element.attrib['showTabs'] = str(workbook_item.show_tabs).lower() project_element = ET.SubElement(workbook_element, 'project') project_element.attrib['id'] = workbook_item.project_id + if connection_credentials: + credentials_element = ET.SubElement(workbook_element,'connectionCredentials') + credentials_element.attrib['name'] = connection_credentials.name + credentials_element.attrib['password'] = connection_credentials.password + credentials_element.attrib['embed'] = str(connection_credentials.embed).lower() return ET.tostring(xml_request) def update_req(self, workbook_item): @@ -283,15 +293,15 @@ def update_req(self, workbook_item): owner_element.attrib['id'] = workbook_item.owner_id return ET.tostring(xml_request) - def publish_req(self, workbook_item, filename, file_contents): - xml_request = self._generate_xml(workbook_item) + def publish_req(self, workbook_item, filename, file_contents, connection_credentials=None): + xml_request = self._generate_xml(workbook_item, connection_credentials) parts = {'request_payload': ('', xml_request, 'text/xml'), 'tableau_workbook': (filename, file_contents, 'application/octet-stream')} return _add_multipart(parts) - def publish_req_chunked(self, workbook_item): - xml_request = self._generate_xml(workbook_item) + def publish_req_chunked(self, workbook_item, connection_credentials=None): + xml_request = self._generate_xml(workbook_item, connection_credentials) parts = {'request_payload': ('', xml_request, 'text/xml')} return _add_multipart(parts) From c4f2ebc6b7430b6cf82f093e2080f9f4ef972384 Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 14:21:53 -0400 Subject: [PATCH 03/11] Fixed coding style errors The command "python setup.py test" exited with 0. 1.27s$ pycodestyle . ./tableauserverclient/__init__.py:10:26: W292 no newline at end of file ./tableauserverclient/server/request_factory.py:40:67: E231 missing whitespace after ',' ./tableauserverclient/server/request_factory.py:268:42: E231 missing whitespace after ',' ./tableauserverclient/server/request_factory.py:277:65: E231 missing whitespace after ',' ./tableauserverclient/server/endpoint/datasources_endpoint.py:125:121: E501 line too long (126 > 120 characters) ./tableauserverclient/server/endpoint/workbooks_endpoint.py:174:121: E501 line too long (122 > 120 characters) --- tableauserverclient/__init__.py | 2 +- tableauserverclient/server/endpoint/datasources_endpoint.py | 3 ++- tableauserverclient/server/endpoint/workbooks_endpoint.py | 3 ++- tableauserverclient/server/request_factory.py | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index 5df3eacbc..107b6b2d9 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -7,4 +7,4 @@ MissingRequiredFieldError, NotSignedInError __version__ = '0.0.1' -__VERSION__ = __version__ \ No newline at end of file +__VERSION__ = __version__ diff --git a/tableauserverclient/server/endpoint/datasources_endpoint.py b/tableauserverclient/server/endpoint/datasources_endpoint.py index 120aeb625..ecb66724c 100644 --- a/tableauserverclient/server/endpoint/datasources_endpoint.py +++ b/tableauserverclient/server/endpoint/datasources_endpoint.py @@ -122,7 +122,8 @@ def publish(self, datasource_item, file_path, mode, connection_credentials=None) logger.info('Publishing {0} to server with chunking method (datasource over 64MB)'.format(filename)) upload_session_id = Fileuploads.upload_chunks(self.parent_srv, file_path) url = "{0}&uploadSessionId={1}".format(url, upload_session_id) - xml_request, content_type = RequestFactory.Datasource.publish_req_chunked(datasource_item, connection_credentials) + xml_request, content_type = RequestFactory.Datasource.publish_req_chunked(datasource_item, + connection_credentials) else: logger.info('Publishing {0} to server'.format(filename)) with open(file_path, 'rb') as f: diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py index 16d09eba1..46ec3c5ee 100644 --- a/tableauserverclient/server/endpoint/workbooks_endpoint.py +++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py @@ -171,7 +171,8 @@ def publish(self, workbook_item, file_path, mode, connection_credentials=None): logger.info('Publishing {0} to server with chunking method (workbook over 64MB)'.format(filename)) upload_session_id = Fileuploads.upload_chunks(self.parent_srv, file_path) url = "{0}&uploadSessionId={1}".format(url, upload_session_id) - xml_request, content_type = RequestFactory.Workbook.publish_req_chunked(workbook_item, connection_credentials) + xml_request, content_type = RequestFactory.Workbook.publish_req_chunked(workbook_item, + connection_credentials) else: logger.info('Publishing {0} to server'.format(filename)) with open(file_path, 'rb') as f: diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index c7ba536d1..4e06785b5 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -37,7 +37,7 @@ def _generate_xml(self, datasource_item, connection_credentials=None): project_element = ET.SubElement(datasource_element, 'project') project_element.attrib['id'] = datasource_item.project_id if connection_credentials: - credentials_element = ET.SubElement(datasource_element,'connectionCredentials') + credentials_element = ET.SubElement(datasource_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password credentials_element.attrib['embed'] = str(connection_credentials.embed).lower() @@ -265,7 +265,7 @@ def add_req(self, user_item): class WorkbookRequest(object): - def _generate_xml(self, workbook_item,connection_credentials=None): + def _generate_xml(self, workbook_item, connection_credentials=None): xml_request = ET.Element('tsRequest') workbook_element = ET.SubElement(xml_request, 'workbook') workbook_element.attrib['name'] = workbook_item.name @@ -274,7 +274,7 @@ def _generate_xml(self, workbook_item,connection_credentials=None): project_element = ET.SubElement(workbook_element, 'project') project_element.attrib['id'] = workbook_item.project_id if connection_credentials: - credentials_element = ET.SubElement(workbook_element,'connectionCredentials') + credentials_element = ET.SubElement(workbook_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password credentials_element.attrib['embed'] = str(connection_credentials.embed).lower() From c2f697b9af3a2d9d61749e85048db0fce9c1fbba Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 16:07:15 -0400 Subject: [PATCH 04/11] Making changes from PR Review Explicit convert of boolean to true/false string Ordering of class properties to match signature Docstring on connection credentials and auth classes --- tableauserverclient/models/connection_credentials.py | 9 ++++++++- tableauserverclient/models/tableau_auth.py | 12 +++++++++--- tableauserverclient/server/request_factory.py | 10 ++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/tableauserverclient/models/connection_credentials.py b/tableauserverclient/models/connection_credentials.py index 931afdd52..36b53be69 100644 --- a/tableauserverclient/models/connection_credentials.py +++ b/tableauserverclient/models/connection_credentials.py @@ -1,5 +1,12 @@ class ConnectionCredentials(object): + """Connection Credentials for Workbooks and Datasources publish request + + Consider removing this object and other variables holding secrets + as soon as poobible after use to avoid them hanging around in memory. + + """ def __init__(self, name, password, embed=True): + self.name = name self.password = password self.embed = embed - self.name = name + \ No newline at end of file diff --git a/tableauserverclient/models/tableau_auth.py b/tableauserverclient/models/tableau_auth.py index cdf0fb410..978710efa 100644 --- a/tableauserverclient/models/tableau_auth.py +++ b/tableauserverclient/models/tableau_auth.py @@ -1,6 +1,12 @@ class TableauAuth(object): - def __init__(self, username, password, site='', user_id_to_impersonate=None): - self.user_id_to_impersonate = user_id_to_impersonate + """Authentication attributes for signin request + + Consider removing this object and other variables holding secrets + as soon as possible after use to avoid them hanging around in memory. + + """ + def __init__(self, username, password, site='', user_id_to_impersonate=None): + self.username = username self.password = password self.site = site - self.username = username + self.user_id_to_impersonate = user_id_to_impersonate diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index 4e06785b5..e5120d83d 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -40,7 +40,10 @@ def _generate_xml(self, datasource_item, connection_credentials=None): credentials_element = ET.SubElement(datasource_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password - credentials_element.attrib['embed'] = str(connection_credentials.embed).lower() + if connection_credentials.embed: + credentials_element.attrib['embed'] = 'true' + else: + credentials_element.attrib['embed'] = 'false' return ET.tostring(xml_request) def update_req(self, datasource_item): @@ -277,7 +280,10 @@ def _generate_xml(self, workbook_item, connection_credentials=None): credentials_element = ET.SubElement(workbook_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password - credentials_element.attrib['embed'] = str(connection_credentials.embed).lower() + if connection_credentials.embed: + credentials_element.attrib['embed'] = 'true' + else: + credentials_element.attrib['embed'] = 'false' return ET.tostring(xml_request) def update_req(self, workbook_item): From 011a73aabdddc3ef07f6ca69672c81026515a595 Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 16:11:39 -0400 Subject: [PATCH 05/11] Fixed indenting --- tableauserverclient/models/tableau_auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tableauserverclient/models/tableau_auth.py b/tableauserverclient/models/tableau_auth.py index 978710efa..c4abd8242 100644 --- a/tableauserverclient/models/tableau_auth.py +++ b/tableauserverclient/models/tableau_auth.py @@ -5,7 +5,7 @@ class TableauAuth(object): as soon as possible after use to avoid them hanging around in memory. """ - def __init__(self, username, password, site='', user_id_to_impersonate=None): + def __init__(self, username, password, site='', user_id_to_impersonate=None): self.username = username self.password = password self.site = site From be85635a67d3faa499864289858358fd545d9ad7 Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 16:30:54 -0400 Subject: [PATCH 06/11] reverted tableau_auth.py --- tableauserverclient/models/tableau_auth.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tableauserverclient/models/tableau_auth.py b/tableauserverclient/models/tableau_auth.py index c4abd8242..cdf0fb410 100644 --- a/tableauserverclient/models/tableau_auth.py +++ b/tableauserverclient/models/tableau_auth.py @@ -1,12 +1,6 @@ class TableauAuth(object): - """Authentication attributes for signin request - - Consider removing this object and other variables holding secrets - as soon as possible after use to avoid them hanging around in memory. - - """ - def __init__(self, username, password, site='', user_id_to_impersonate=None): - self.username = username + def __init__(self, username, password, site='', user_id_to_impersonate=None): + self.user_id_to_impersonate = user_id_to_impersonate self.password = password self.site = site - self.user_id_to_impersonate = user_id_to_impersonate + self.username = username From 8d13cf43155e842b8fbbccba7abcb51aa736473e Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 16:34:53 -0400 Subject: [PATCH 07/11] attempting to fix codestyle errors --- tableauserverclient/models/connection_credentials.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tableauserverclient/models/connection_credentials.py b/tableauserverclient/models/connection_credentials.py index 36b53be69..33c709a96 100644 --- a/tableauserverclient/models/connection_credentials.py +++ b/tableauserverclient/models/connection_credentials.py @@ -1,7 +1,7 @@ class ConnectionCredentials(object): """Connection Credentials for Workbooks and Datasources publish request - Consider removing this object and other variables holding secrets + Consider removing this object and other variables holding secrets as soon as poobible after use to avoid them hanging around in memory. """ @@ -9,4 +9,3 @@ def __init__(self, name, password, embed=True): self.name = name self.password = password self.embed = embed - \ No newline at end of file From 5f25ef992ad234656394ebff541a198b85dc684d Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 16:41:56 -0400 Subject: [PATCH 08/11] fix indentrs and docstring --- tableauserverclient/models/connection_credentials.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tableauserverclient/models/connection_credentials.py b/tableauserverclient/models/connection_credentials.py index 33c709a96..9b0765520 100644 --- a/tableauserverclient/models/connection_credentials.py +++ b/tableauserverclient/models/connection_credentials.py @@ -1,11 +1,12 @@ class ConnectionCredentials(object): - """Connection Credentials for Workbooks and Datasources publish request + """Connection Credentials for Workbooks and Datasources publish request. - Consider removing this object and other variables holding secrets - as soon as poobible after use to avoid them hanging around in memory. + Consider removing this object and other variables holding secrets + as soon as poobible after use to avoid them hanging around in memory. + + """ - """ def __init__(self, name, password, embed=True): - self.name = name + self.name = name self.password = password self.embed = embed From 82e84a3d0472437a4531516c8081c5dca5e62391 Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 16:50:23 -0400 Subject: [PATCH 09/11] fix spelling error --- tableauserverclient/models/connection_credentials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tableauserverclient/models/connection_credentials.py b/tableauserverclient/models/connection_credentials.py index 9b0765520..d1bcde61a 100644 --- a/tableauserverclient/models/connection_credentials.py +++ b/tableauserverclient/models/connection_credentials.py @@ -2,7 +2,7 @@ class ConnectionCredentials(object): """Connection Credentials for Workbooks and Datasources publish request. Consider removing this object and other variables holding secrets - as soon as poobible after use to avoid them hanging around in memory. + as soon as possible after use to avoid them hanging around in memory. """ From c4d6cee6bf8a3fd08425c41d49c1cab1c06cf2a2 Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 19:09:04 -0400 Subject: [PATCH 10/11] Adding type safety on boolean embed property --- tableauserverclient/models/connection_credentials.py | 12 ++++++++++++ tableauserverclient/server/request_factory.py | 10 ++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tableauserverclient/models/connection_credentials.py b/tableauserverclient/models/connection_credentials.py index d1bcde61a..3a462e7d4 100644 --- a/tableauserverclient/models/connection_credentials.py +++ b/tableauserverclient/models/connection_credentials.py @@ -1,3 +1,6 @@ +from property_decorators import property_is_boolean + + class ConnectionCredentials(object): """Connection Credentials for Workbooks and Datasources publish request. @@ -10,3 +13,12 @@ def __init__(self, name, password, embed=True): self.name = name self.password = password self.embed = embed + + @property + def embed(self): + return self._embed + + @embed.setter + @property_is_boolean + def embed(self, value): + self._embed = value diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index e5120d83d..d8b4743fc 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -40,10 +40,7 @@ def _generate_xml(self, datasource_item, connection_credentials=None): credentials_element = ET.SubElement(datasource_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password - if connection_credentials.embed: - credentials_element.attrib['embed'] = 'true' - else: - credentials_element.attrib['embed'] = 'false' + credentials_element.attrib['embed'] = str(connection_credentials).lower() return ET.tostring(xml_request) def update_req(self, datasource_item): @@ -280,10 +277,7 @@ def _generate_xml(self, workbook_item, connection_credentials=None): credentials_element = ET.SubElement(workbook_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password - if connection_credentials.embed: - credentials_element.attrib['embed'] = 'true' - else: - credentials_element.attrib['embed'] = 'false' + credentials_element.attrib['embed'] = str(connection_credentials).lower() return ET.tostring(xml_request) def update_req(self, workbook_item): From ab478dc0f26ae82ce75f9886eba79fccdda8cd07 Mon Sep 17 00:00:00 2001 From: geordielad Date: Thu, 27 Oct 2016 19:28:33 -0400 Subject: [PATCH 11/11] make import python3 friendly and true/false test more pythonic --- tableauserverclient/models/connection_credentials.py | 2 +- tableauserverclient/server/request_factory.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tableauserverclient/models/connection_credentials.py b/tableauserverclient/models/connection_credentials.py index 3a462e7d4..d823b0b7f 100644 --- a/tableauserverclient/models/connection_credentials.py +++ b/tableauserverclient/models/connection_credentials.py @@ -1,4 +1,4 @@ -from property_decorators import property_is_boolean +from .property_decorators import property_is_boolean class ConnectionCredentials(object): diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py index d8b4743fc..25f06abad 100644 --- a/tableauserverclient/server/request_factory.py +++ b/tableauserverclient/server/request_factory.py @@ -40,7 +40,7 @@ def _generate_xml(self, datasource_item, connection_credentials=None): credentials_element = ET.SubElement(datasource_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password - credentials_element.attrib['embed'] = str(connection_credentials).lower() + credentials_element.attrib['embed'] = 'true' if connection_credentials.embed else 'false' return ET.tostring(xml_request) def update_req(self, datasource_item): @@ -277,7 +277,7 @@ def _generate_xml(self, workbook_item, connection_credentials=None): credentials_element = ET.SubElement(workbook_element, 'connectionCredentials') credentials_element.attrib['name'] = connection_credentials.name credentials_element.attrib['password'] = connection_credentials.password - credentials_element.attrib['embed'] = str(connection_credentials).lower() + credentials_element.attrib['embed'] = 'true' if connection_credentials.embed else 'false' return ET.tostring(xml_request) def update_req(self, workbook_item):