tableau / server-client-python

A Python library for the Tableau Server REST API
https://tableau.github.io/server-client-python/
MIT License
649 stars 419 forks source link

Update databricks connection #1187

Open ConstantinoSchillebeeckx opened 1 year ago

ConstantinoSchillebeeckx commented 1 year ago

Describe the bug I need to update various Databricks connections; the docs are unclear about connection attributes like http_path or authentication method (i.e. token vs user & password). How do I do this?

image

FWIW, when I call the REST API and inspect the response for the connection details all I get is (NOTE the xml response has been JSONified)

{'tsResponse': {'@xmlns': 'http://tableau.com/api',
  '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
  '@xsi:schemaLocation': 'http://tableau.com/api https://help.tableau.com/samples/en-us/rest_api/ts-api_3_18.xsd',
  'connections': {'connection': {'@id': 'some-id,
    '@type': 'databricks',
    '@embedPassword': 'true',
    '@serverAddress': 'super secret',
    '@userName': '',
    '@queryTaggingEnabled': 'false'}}}}

i.e. the server doesn't give me any hints on any undocumented attributes

Versions Details of your environment, including:

ConstantinoSchillebeeckx commented 1 year ago

any thoughts? I'd love to get some help on this.

williamdphillips commented 1 year ago

Hi @ConstantinoSchillebeeckx

I was able to successfully update the connection details of a Databricks source like this using Tableau Document API tool:

for connection in data_source.connections:
  connection.server = server_name
  connection._server = server_name
  connection._connectionXML.set('server', server_name)
  connection._connectionXML.set('_.fcp.DatabricksCatalog.false...dbname', http_path)
  connection._connectionXML.set('_.fcp.DatabricksCatalog.true...dbname', 'hive_metastore')
  connection._connectionXML.set('_.fcp.DatabricksCatalog.true...v-http-path', http_path)
  connection._connectionXML.set('schema', database_name)
  connection._schema = database_name

  dconnection.username = sql_username
  connection.password = sql_password

You should be able to figure out how to update nearly any data source by checking out the XML directly. Please note that Tableau Desktop sometimes changes the xml amongst versions, so the version of Tableau Desktop this is specific to would be 2022.1.10. Also, note I have tested only with PAT-based notebooks and not with OAUTH as the xml changes may be slightly different for OAUTH.

jacalata commented 1 year ago

Thanks William!

Connection attributes vary a lot and I'm not super familiar with them. It can be easier to update them by downloading the workbook and using the Document API tool - https://tableau.github.io/document-api-python/docs/api-ref#connections

williamdphillips commented 1 year ago

@jacalata Ah I missed adding that in my comment. The code I pasted is actually using the Tableau Document API Tool. Updated comment accordingly.

ConstantinoSchillebeeckx commented 1 year ago

hi @williamdphillips thanks for the info! what exactly do you mean by:

You should be able to figure out how to update nearly any data source by checking out the XML directly.

XML from where exactly?

also, could you update your example please? those ellipses make it seem incompleted, e.g. data_source.connections[0].connectionXML.set('.fcp.DatabricksCatalog.false...dbname', http_path)

williamdphillips commented 1 year ago

@ConstantinoSchillebeeckx a Tableau Workbook (.twb file) is nothing more than an XML file with a different extension. If you try to view a Tableau Workbook in a text editor you will be able to see it's XML content. Also, the example is complete - those ellipses are actually how Tableau defines the xml tags within the workbook.

ConstantinoSchillebeeckx commented 1 year ago

@williamdphillips

I believe the example you've shown me is for a published data source; instead, I'd like to update a data source thats associated with a workbook (i.e. not published). I'm seeing:

with server.auth.sign_in(tableau_auth):
    wb = server.workbooks.get_by_id("81870d1b-05aa-48ba-9032-b6e63441054e")
    server.workbooks.populate_connections(wb)

    conn = wb.connections[0]

    http_path = "sql/protocolv1/o/0/0630-065022-123123"
    host = "foo.cloud.databricks.com"
    user = "token"
    token = "NEW E2 TOKEN"

    conn.password = token
    conn.user = user
    conn.server = host 
    conn.connectionXML.set('.fcp.DatabricksCatalog.false...dbname', http_path)
    conn.connectionXML.set('.fcp.DatabricksCatalog.true...dbname', 'hive_metastore')
    conn.connectionXML.set('.fcp.DatabricksCatalog.true...v-http-path', http_path)

results in

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[42], line 20
     18 conn.user = user
     19 conn.server = host 
---> 20 conn.connectionXML.set('.fcp.DatabricksCatalog.false...dbname', http_path)
     21 conn.connectionXML.set('.fcp.DatabricksCatalog.true...dbname', 'hive_metastore')
     22 conn.connectionXML.set('.fcp.DatabricksCatalog.true...v-http-path', http_path)

AttributeError: 'ConnectionItem' object has no attribute 'connectionXML'

I noticed your example also uses ._connectionXML (with a prefixed underscore) which also doesn't work. What am I doing wrong?

ConstantinoSchillebeeckx commented 1 year ago

could you offer any help @williamdphillips please?

yb-yu commented 10 months ago

@ConstantinoSchillebeeckx Hello, I'm facing the same issue. If you've found a workaround, can you let me know how you did it?

yb-yu commented 8 months ago

For anyone else who might come here:

I was using Tableau Cloud with Databricks and had to change the target catalog in many data source connections. While this can't be solved with the Tableau Server Client alone, by downloading the datasource locally and using it along with the Tableau Document Client for parsing, you can change more attributes including dbname.

The general flow of the code is as follows, and you can also change all data sources using pagination.

import tableauserverclient as TSC
from tableauserverclient import RequestOptions, ConnectionItem

tableau_auth = TSC.PersonalAccessTokenAuth(TOKEN_NAME, TOKEN_VALUE, SITE_ID)
server = TSC.Server(SERVER_URL)
server.auth.sign_in(tableau_auth)

datasource_item = server.datasources.get_by_id(DATASOURCE_ID)
server.datasources.download(DATASOURCE_ID, FILE_PATH)

from tableaudocumentapi import Workbook, Datasource

dd = Datasource.from_file(FILE_PATH)
print(dd.connections[0]._connectionXML.items())

dd.connections[0]._connectionXML.set("schema", "NEW_DB")
dd.save()

datasources.publish(datasource_item, FILE_PATH, "overwrite")