def test_publish_multi_connection(self): new_datasource = TSC.DatasourceItem( name='Sample', project_id='ee8c6e70-43b6-11e6-af4f-f7b0d8e20760') connection1 = TSC.ConnectionItem() connection1.server_address = 'mysql.test.com' connection1.connection_credentials = TSC.ConnectionCredentials( 'test', 'secret', True) connection2 = TSC.ConnectionItem() connection2.server_address = 'pgsql.test.com' connection2.connection_credentials = TSC.ConnectionCredentials( 'test', 'secret', True) response = RequestFactory.Datasource._generate_xml( new_datasource, connections=[connection1, connection2]) # Can't use ConnectionItem parser due to xml namespace problems connection_results = ET.fromstring(response).findall('.//connection') self.assertEqual(connection_results[0].get('serverAddress', None), 'mysql.test.com') self.assertEqual( connection_results[0].find('connectionCredentials').get( 'name', None), 'test') self.assertEqual(connection_results[1].get('serverAddress', None), 'pgsql.test.com') self.assertEqual( connection_results[1].find('connectionCredentials').get( 'password', None), 'secret')
def test_publish_multi_connection(self) -> None: new_datasource = TSC.DatasourceItem( name="Sample", project_id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760") connection1 = TSC.ConnectionItem() connection1.server_address = "mysql.test.com" connection1.connection_credentials = TSC.ConnectionCredentials( "test", "secret", True) connection2 = TSC.ConnectionItem() connection2.server_address = "pgsql.test.com" connection2.connection_credentials = TSC.ConnectionCredentials( "test", "secret", True) response = RequestFactory.Datasource._generate_xml( new_datasource, connections=[connection1, connection2]) # Can't use ConnectionItem parser due to xml namespace problems connection_results = fromstring(response).findall(".//connection") self.assertEqual(connection_results[0].get("serverAddress", None), "mysql.test.com") self.assertEqual( connection_results[0].find("connectionCredentials").get( "name", None), "test") # type: ignore[union-attr] self.assertEqual(connection_results[1].get("serverAddress", None), "pgsql.test.com") self.assertEqual( connection_results[1].find("connectionCredentials").get( "password", None), "secret") # type: ignore[union-attr]
def test_credentials_and_multi_connect_raises_exception(self): new_datasource = TSC.DatasourceItem(name='Sample', project_id='ee8c6e70-43b6-11e6-af4f-f7b0d8e20760') connection_creds = TSC.ConnectionCredentials('test', 'secret', True) connection1 = TSC.ConnectionItem() connection1.server_address = 'mysql.test.com' connection1.connection_credentials = TSC.ConnectionCredentials('test', 'secret', True) with self.assertRaises(RuntimeError): response = RequestFactory.Datasource._generate_xml(new_datasource, connection_credentials=connection_creds, connections=[connection1])
def test_credentials_and_multi_connect_raises_exception(self) -> None: new_workbook = TSC.WorkbookItem( name="Sample", show_tabs=False, project_id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760" ) connection_creds = TSC.ConnectionCredentials("test", "secret", True) connection1 = TSC.ConnectionItem() connection1.server_address = "mysql.test.com" connection1.connection_credentials = TSC.ConnectionCredentials("test", "secret", True) with self.assertRaises(RuntimeError): response = RequestFactory.Workbook._generate_xml( new_workbook, connection_credentials=connection_creds, connections=[connection1] )
def publishDataSource(filePath, projectId): tableauAuth = TSC.TableauAuth(tableauUserId, tableauPassword) server = TSC.Server(tableauServerAddress) if tableauIgnoreSslCert: server.add_http_options({'verify': False}) server.auth.sign_in(tableauAuth) datasourceCredentials = TSC.ConnectionCredentials(name=pgUser, password=pgPassword) datasource = TSC.DatasourceItem(projectId) if useCustomSQL: datasource.name = dataSourceName datasource = server.datasources.publish(datasource, filePath, 'CreateNew', datasourceCredentials) if verbose: print('Published datasource {} to Tableau Server running at {}'.format( datasource.name, tableauServerAddress)) lineClear() server.auth.sign_out()
def migrate_datasource(datasource, input="in", output="out"): try: os.mkdir(os.path.join(ipath, datasource.project_id)) except: pass tdsx = os.path.join(ipath, datasource.project_id, datasource.id + '.tdsx') print("datasource: {} > {}".format(datasource.name, tdsx)) datasource.project_id = pmap[datasource.project_id] tableau_servers[input]["server"].datasources.download(datasource.id, filepath=tdsx) print('patching {} to output server'.format(tdsx)) updateTDSX(tdsx, input, output) tableau_servers[input]["server"].datasources.populate_connections( datasource) cc = TSC.ConnectionCredentials( datasource.connections[0].username, passwords[datasource.connections[0].username], embed=True, oauth=False) in_id = datasource.id out_id = tableau_servers[output]["server"].datasources.publish( datasource, tdsx, TSC.Server.PublishMode.Overwrite, connection_credentials=cc).id dmap[in_id] = out_id
def test_generate_xml_valid_connection_credentials(self): workbook_item: TSC.WorkbookItem = TSC.WorkbookItem( "name", "project_id") conn = TSC.ConnectionItem() conn.server_address = "address" creds = TSC.ConnectionCredentials("username", "DELETEME") conn.connection_credentials = creds request = TSC_RF.RequestFactory.Workbook._generate_xml( workbook_item, connections=[conn]) assert request.find(b"DELETEME") > 0
def test_generate_xml_invalid_connection_credentials(self): workbook_item: TSC.WorkbookItem = TSC.WorkbookItem( "name", "project_id") conn = TSC.ConnectionItem() conn.server_address = "address" creds = TSC.ConnectionCredentials("username", "password") creds.name = None conn.connection_credentials = creds with self.assertRaises(ValueError): request = TSC_RF.RequestFactory.Workbook._generate_xml( workbook_item, connections=[conn])
def test_publish_single_connection(self): new_workbook = TSC.WorkbookItem(name='Sample', show_tabs=False, project_id='ee8c6e70-43b6-11e6-af4f-f7b0d8e20760') connection_creds = TSC.ConnectionCredentials('test', 'secret', True) response = RequestFactory.Workbook._generate_xml(new_workbook, connection_credentials=connection_creds) # Can't use ConnectionItem parser due to xml namespace problems credentials = ET.fromstring(response).findall('.//connectionCredentials') self.assertEqual(len(credentials), 1) self.assertEqual(credentials[0].get('name', None), 'test') self.assertEqual(credentials[0].get('password', None), 'secret') self.assertEqual(credentials[0].get('embed', None), 'true')
def test_publish_single_connection(self) -> None: new_workbook = TSC.WorkbookItem( name="Sample", show_tabs=False, project_id="ee8c6e70-43b6-11e6-af4f-f7b0d8e20760" ) connection_creds = TSC.ConnectionCredentials("test", "secret", True) response = RequestFactory.Workbook._generate_xml(new_workbook, connection_credentials=connection_creds) # Can't use ConnectionItem parser due to xml namespace problems credentials = fromstring(response).findall(".//connectionCredentials") self.assertEqual(len(credentials), 1) self.assertEqual(credentials[0].get("name", None), "test") self.assertEqual(credentials[0].get("password", None), "secret") self.assertEqual(credentials[0].get("embed", None), "true")
def test_redact_passwords_in_xml(self): if sys.version_info < (3, 7): pytest.skip("Redaction is only implemented for 3.7+.") workbook_item: TSC.WorkbookItem = TSC.WorkbookItem( "name", "project_id") conn = TSC.ConnectionItem() conn.server_address = "address" creds = TSC.ConnectionCredentials("username", "DELETEME") conn.connection_credentials = creds request = TSC_RF.RequestFactory.Workbook._generate_xml( workbook_item, connections=[conn]) redacted = redact_xml(request) assert request.find(b"DELETEME") > 0, request assert redacted.find(b"DELETEME") == -1, redacted
def publish_hyper(hyper_file, credential_path, flow_path=None, **kwargs): """ publishes local extract.hyper file to tableau server, main method is datasources.publish(datasource_item, file_path, mode, connection_credentials=None) https://tableau.github.io/server-client-python/docs/api-ref#data-sources Arguments: hyper_file {str} -- filepath of extract.hyper or .tde to be published credential_path {str} -- credential.json file per tableau prep cli specification, must contain `outputConnections` credentails for Tableau server Keyword Arguments: project {str} -- tableau server folder/project where to publish (Defaults: {Defaults}) mode {str} -- publish mode CreateNew|Overwrite|Append (Defaults: Overwrite) embed {bool} -- if True, embeds credentails in data source (Defaults: True) flow_path {str} -- flow file path for infering extract.hyper path and name (default: {None}) name {str} -- name of extract file on server to be replaced or appended to """ with open(credential_path, 'r') as file: credentials = json.load(file).get('outputConnections')[0] server_address = credentials.get('serverUrl') username = credentials.get('username') password = credentials.get('password') logging.info(f"singing into {server_address} as {username}") server = TSC.Server(server_address) tableau_auth = TSC.TableauAuth( username, password, credentials.get('contentUrl')) # server.auth.sign_in(tableau_auth) logging.debug(f'supplied keyword args: {kwargs}') with server.auth.sign_in(tableau_auth): assert server.is_signed_in() == True if 'project' in kwargs.keys(): project = kwargs.get('project') else: project = 'Default' all_project_items, pagination_item = server.projects.get() try: project_id = [ item.id for item in all_project_items if item.name == project][0] except: project_id = [ item.id for item in all_project_items if item.name == 'Default'][0] logging.debug(f"project_id for {project} is : {project_id}") if not hyper_file: if flow_path: flow_name = os.path.basename(flow_path) project_dir = os.path.dirname(flow_path) os.chdir(project_dir) hyper_list = get_files_in_folder( folderpath=project_dir, file_extenion="hyper") if len(hyper_list) > 1: name = hyper_list[0] logging.warning( f'found more that one hyper files in tableau prep flow folder ``{project_dir}``, publising ``{name}`` to server. explicitly provide hyper_path to publish desired extract file') elif len(hyper_list) == 1: name = hyper_list[0] else: logging.error( f'no extract.hyper file was found in tableau prep flow folder ``{project_dir}``, check that flow outputs file in hyper format or explicitly provide hyper_path to publish desired extract file') else: logging.error( f'could not find extract.hyper file to publish, explicitly provide hyper_path to publish desired extract file') hyper_filepath = os.path.join(project_dir, name) elif os.path.isfile(hyper_file) == True: hyper_filepath = hyper_file name = os.path.basename(hyper_file) else: # when only filename is passed which is in project folder name = hyper_file hyper_filepath = os.path.join(project_dir, name) assert os.path.isfile(hyper_filepath) == True logging.debug( f'publishing extract file ``{hyper_filepath}`` to ``{project}`` project') name = name.split('.')[0] # remove file extension if 'mode' in kwargs.keys(): mode = kwargs.get('mode').title() if mode == 'Createnew': mode = 'CreateNew' if mode == 'Append': all_datasources, pagination_item = server.datasources.get() if 'name' in kwargs.keys(): name = kwargs.get('name') datasource_id = [ datasource.id for datasource in all_datasources if datasource.name == name][0] data_source_item = server.datasources.get_by_id(datasource_id) logging.debug(f"appending {hyper_filepath} to {name}") else: data_source_item = TSC.DatasourceItem( project_id=project_id, name=name) logging.debug( f"publishing {hyper_filepath} as {name} in {mode} mode") else: mode = 'Overwrite' data_source_item = TSC.DatasourceItem( project_id=project_id, name=name) logging.debug( f"publishing {hyper_filepath} as ``{name}`` in {mode} mode to ``{server_address}`` in ``{project}`` project") if 'embed' in kwargs.keys(): embedded_credential = TSC.ConnectionCredentials( username, password, embed=embed, oauth=False) server.datasources.publish( data_source_item, hyper_filepath, mode, connection_credentials=embedded_credential) else: server.datasources.publish( data_source_item, hyper_filepath, mode) logging.info(f'{hyper_filepath} was successfully published in {mode} mode to {project} project!'))
with server.auth.sign_in(tableau_auth): user = TSC.UserItem(server_username[i], 'SiteAdministrator') user = server.users.add(user) user = server.users.update(user, 'password') #Getting the default project and publishing workbook and datasource### project_id = '' time.sleep(1) with server.auth.sign_in(tableau_auth): all_projects, pagination_item = server.projects.get() for project in all_projects: if project.name == 'Default': project_id = project.id new_workbook = TSC.WorkbookItem(project_id) new_datasource = TSC.DatasourceItem(project_id) credentials = TSC.ConnectionCredentials(db_username[i], db_password[i], embed=True) with server.auth.sign_in(tableau_auth): server.datasources.publish(new_datasource, uniqueField[i] + ".tdsx", TSC.Server.PublishMode.Overwrite, credentials) server.workbooks.publish(new_workbook, uniqueField[i] + ".twbx", TSC.Server.PublishMode.Overwrite, credentials) site_check = 0 # print "The workbook connections have been updated. Please open the newly created workbook to test."
import tableauserverclient as TSC import config import os import pandas as pd auth = config.TableauAuth cred = config.Credentials #Import of credentials and logins from config file server = TSC.Server(auth["server"], use_server_version=True) credentials = TSC.ConnectionCredentials(cred["user"], cred["password"], embed=True) def sign_in(site_id, option): if option == True: tableau_auth = TSC.TableauAuth(auth["username"], auth["password"], site_id) server.auth.sign_in(tableau_auth) #Signs into the site using the tableau authentification information else: server.auth.sign_out() class server_con(): def __init__(self, site, type, name): self.site = site self.projs = list(TSC.Pager(server.projects, TSC.RequestOptions(pagenumber=1))) self.type = type self.name = name request_options = TSC.RequestOptions(pagenumber=1) if type == "ds": self.item_list = list(TSC.Pager(server.datasources, request_options)) #Gets list of datasource items in site elif type =="wb":
def main(): parser = argparse.ArgumentParser( description="Publish a datasource to server.") # Common options; please keep those in sync across all samples parser.add_argument("--server", "-s", required=True, help="server address") parser.add_argument("--site", "-S", help="site name") parser.add_argument( "--token-name", "-p", required=True, help="name of the personal access token used to sign into the server") parser.add_argument( "--token-value", "-v", required=True, help="value of the personal access token used to sign into the server") parser.add_argument( "--logging-level", "-l", choices=["debug", "info", "error"], default="error", help="desired logging level (set to error by default)", ) # Options specific to this sample parser.add_argument("--file", "-f", required=True, help="filepath to the datasource to publish") parser.add_argument("--project", help="Project within which to publish the datasource") parser.add_argument("--async", "-a", help="Publishing asynchronously", dest="async_", action="store_true") parser.add_argument("--conn-username", help="connection username") parser.add_argument("--conn-password", help="connection password") parser.add_argument("--conn-embed", help="embed connection password to datasource", action="store_true") parser.add_argument("--conn-oauth", help="connection is configured to use oAuth", action="store_true") args = parser.parse_args() # Ensure that both the connection username and password are provided, or none at all if (args.conn_username and not args.conn_password) or (not args.conn_username and args.conn_password): parser.error( "Both the connection username and password must be provided") # Set logging level based on user input, or error by default logging_level = getattr(logging, args.logging_level.upper()) logging.basicConfig(level=logging_level) # Sign in to server tableau_auth = TSC.PersonalAccessTokenAuth(args.token_name, args.token_value, site_id=args.site) server = TSC.Server(args.server, use_server_version=True) with server.auth.sign_in(tableau_auth): # Empty project_id field will default the publish to the site's default project project_id = "" # Retrieve the project id, if a project name was passed if args.project is not None: req_options = TSC.RequestOptions() req_options.filter.add( TSC.Filter(TSC.RequestOptions.Field.Name, TSC.RequestOptions.Operator.Equals, args.project)) projects = list(TSC.Pager(server.projects, req_options)) if len(projects) > 1: raise ValueError("The project name is not unique") project_id = projects[0].id # Create a new datasource item to publish new_datasource = TSC.DatasourceItem(project_id=project_id) # Create a connection_credentials item if connection details are provided new_conn_creds = None if args.conn_username: new_conn_creds = TSC.ConnectionCredentials(args.conn_username, args.conn_password, embed=args.conn_embed, oauth=args.conn_oauth) # Define publish mode - Overwrite, Append, or CreateNew publish_mode = TSC.Server.PublishMode.Overwrite # Publish datasource if args.async_: # Async publishing, returns a job_item new_job = server.datasources.publish( new_datasource, args.filepath, publish_mode, connection_credentials=new_conn_creds, as_job=True) print("Datasource published asynchronously. Job ID: {0}".format( new_job.id)) else: # Normal publishing, returns a datasource_item new_datasource = server.datasources.publish( new_datasource, args.filepath, publish_mode, connection_credentials=new_conn_creds) print("Datasource published. Datasource ID: {0}".format( new_datasource.id))
def main(): parser = argparse.ArgumentParser( description='Publish a datasource to server.') parser.add_argument('--server', '-s', required=True, help='server address') parser.add_argument('--site', '-i', help='site name') parser.add_argument( '--token-name', '-p', required=True, help='name of the personal access token used to sign into the server') parser.add_argument( '--token-value', '-v', required=True, help='value of the personal access token used to sign into the server') parser.add_argument('--filepath', '-f', required=True, help='filepath to the datasource to publish') parser.add_argument('--logging-level', '-l', choices=['debug', 'info', 'error'], default='error', help='desired logging level (set to error by default)') parser.add_argument('--async', '-a', help='Publishing asynchronously', dest='async_', action='store_true') parser.add_argument('--conn-username', help='connection username') parser.add_argument('--conn-password', help='connection password') parser.add_argument('--conn-embed', help='embed connection password to datasource', action='store_true') parser.add_argument('--conn-oauth', help='connection is configured to use oAuth', action='store_true') args = parser.parse_args() # Ensure that both the connection username and password are provided, or none at all if (args.conn_username and not args.conn_password) or (not args.conn_username and args.conn_password): parser.error( "Both the connection username and password must be provided") # Set logging level based on user input, or error by default logging_level = getattr(logging, args.logging_level.upper()) logging.basicConfig(level=logging_level) # Sign in to server tableau_auth = TSC.PersonalAccessTokenAuth(args.token_name, args.token_value, site_id=args.site) server = TSC.Server(args.server, use_server_version=True) with server.auth.sign_in(tableau_auth): # Create a new datasource item to publish - empty project_id field # will default the publish to the site's default project new_datasource = TSC.DatasourceItem(project_id="") # Create a connection_credentials item if connection details are provided new_conn_creds = None if args.conn_username: new_conn_creds = TSC.ConnectionCredentials(args.conn_username, args.conn_password, embed=args.conn_embed, oauth=args.conn_oauth) # Define publish mode - Overwrite, Append, or CreateNew publish_mode = TSC.Server.PublishMode.Overwrite # Publish datasource if args.async_: # Async publishing, returns a job_item new_job = server.datasources.publish( new_datasource, args.filepath, publish_mode, connection_credentials=new_conn_creds, as_job=True) print("Datasource published asynchronously. Job ID: {0}".format( new_job.id)) else: # Normal publishing, returns a datasource_item new_datasource = server.datasources.publish( new_datasource, args.filepath, publish_mode, connection_credentials=new_conn_creds) print("Datasource published. Datasource ID: {0}".format( new_datasource.id))