server-client-python icon indicating copy to clipboard operation
server-client-python copied to clipboard

Update databricks connection

Open ConstantinoSchillebeeckx opened this issue 2 years ago • 10 comments

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:

  • using Tableau Online
  • 3.8
  • 0.23.4

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

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.

williamdphillips avatar Feb 14 '23 03:02 williamdphillips

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

jacalata avatar Feb 14 '23 03:02 jacalata

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

williamdphillips avatar Feb 14 '23 04:02 williamdphillips

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)

@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.

williamdphillips avatar Feb 14 '23 22:02 williamdphillips

@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?

could you offer any help @williamdphillips please?

@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 avatar Sep 12 '23 04:09 yb-yu

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")

yb-yu avatar Nov 23 '23 08:11 yb-yu