tableau / server-client-python

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

Help Request! Publish Workbook with multiple connections; Already Published DataSource in Tableau Server and Live connection #660

Open avijit3660 opened 4 years ago

avijit3660 commented 4 years ago

Hello All,

I have just started to explore the tableauserverclient functionalities and was working on a scenario to publish Workbook with multiple datasource connection. With Live and Extract it all works seamlessly as mentioned in the publish_workbook.py. Also I am able to publish a workbook with only the published Datasource by following below code snippet. new_job = server.workbooks.publish(new_workbook, file_path, overwrite_true, connections=None) I am facing issue when trying to use the same approach to publish a Workbook with both a database connection and a tableau server connection. As I am passing 'None' to the connections previously, here I tried to put the "None" into the connection list along with the relevant database connections as needed but it did not work. It gives error "'NoneType' object has no attribute 'server_address'". Also I tried to only pass the Tableau server address with credentials however that also is not going through most probably because again our server is SAML protected, Thanks as always to the community for the help and guidance.

jorwoods commented 4 years ago

connections expects an iterable of TSC.ConnectionItem. Try getting an existing workbook and populating its connections to get an idea of what the ConnectionItems look like.

avijit3660 commented 4 years ago

Hi @jorwoods ,

Thanks so much for your response and suggestion.

Yes as you said I have been passing the TSC.ConnectionItems for Live and Extract connections like below :

connection1 = ConnectionItem()

connection1.server_address = server_address

connection1.connection_credentials = ConnectionCredentials(username, password, True)

all_connections.append(connection1)

all_connections

new_job = server.workbooks.publish(new_workbook, file_path,

overwrite_true, connections=all_connections, as_job='--as-job')

However when I wanted to do the same for Published Data source I used below approaches :

  1. Tableau Server credentials and Tableau Server address. It did not work. I felt it might be for our SAML Protection in Tableau Server. Hence I tried to do the same approach in my own Tableau Online site without SAML Protection that also did not work.

  2. I fetched the corresponding datasource which was used in the workbook,did populate_connections and fetched the connection_item. Then appended the connection item to the iterable list all_connections. It also failed with the below error.

Error status code: 500

b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>

Internal Server ErrorThe server encountered an error and cannot complete your request. Contact your server administrator.'

  1. Next I tried as I told above with connections=None that worked fine.

The challenge happens when I need to publish a workbook with multiple connection, like live connection Data Source along with the published data source. Then I need that all_connection iterable list and it keeps failing as when I pass a "None" into that along with the connection_item. It fails with error "'NoneType' object has no attribute 'server_address'".

Thanks again for your response, please let me know if you need further details on this.

Thank you Tableau Community for giving us the podium to interact with experts and get so much knowledge.

Regards Avijit

jorwoods commented 4 years ago

I tested as well, and cannot get the publish to function with a connection to a published data source.

avijit3660 commented 4 years ago

Thanks @jorwoods for confirming. I am trying to understand the source code now and checking to understand the dependencies and thus trying to get a workaround. Please do suggest if any of you have any hints or suggestions which can help me to quickly debug. Thanks as always for all of your guidance.

Regards Avijit

hectorlavalle commented 3 years ago

has anybody found a solution to publish a workbook with a connection to a published data source?

endureit commented 3 years ago

has anybody found a solution to publish a workbook with a connection to a published data source?

Hello, have you found a way to publish the dashboard with embedded passwords to data sources published to Tableau Server?

andymiller-og commented 3 years ago

Thanks @jorwoods for confirming. I am trying to understand the source code now and checking to understand the dependencies and thus trying to get a workaround. Please do suggest if any of you have any hints or suggestions which can help me to quickly debug. Thanks as always for all of your guidance.

Regards Avijit

Avijit - did you find any solution? I am simply trying to publish a local datasource to server (datasource has two postgres connections to different dbs) and I cannot get the following to work.

server.datasources.publish(new_datasource, args.filepath, publish_mode, connections=conn_creds)

conn_creds here is an iterable of ConnectionItem().

Here is the snippet of what im running:

conn_creds = []
conn1 = ConnectionItem()
conn1.server_address = 'address1.rds.amazonaws.com'
conn1.embed_password = True
conn1.connection_credentials = ConnectionCredentials('user1', 'password1', embed=True)
conn_creds.append(conn1)

conn2 = ConnectionItem()
conn2.server_address = 'address2.eu-west-1.rds.amazonaws.com'
conn2.embed_password = True
conn2.connection_credentials = ConnectionCredentials('user2', 'password2', embed=True)
conn_creds.append(conn2)

publish_mode = TSC.Server.PublishMode.Overwrite

if args.async_:
    # Async publishing, returns a job_item
    new_job = server.datasources.publish(new_datasource, args.filepath, publish_mode,
                                         connections=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,
                                                connections=conn_creds)
    print("Datasource published. Datasource ID: {0}".format(new_datasource.id))

Getting this response from the server:

<?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-3.8.xsd"><error code="400000"><summary>Bad Request</summary><detail>Deserialization problem: unexpected element (uri:"", local:"connections"). Expected elements are &lt;{}owner>,&lt;{}site>,&lt;{}project>,&lt;{}connectionCredentials>,&lt;{}askData>,&lt;{}tags>; </detail></error></tsResponse>
jorwoods commented 3 years ago

@psuanm5030 I have a script that did similar and it failed until I included the server_port and _connection_type attributes on the ConnectionItems.

andymiller-og commented 3 years ago

@jorwoods Thanks for the idea... I was hoping that was it... I feel i am close becuase im getting the server to return a job with details but it still has finish code 1 (error) but no details of the error.

result: <Job#319f20dd-7bdf-4db3-a1e8-b91eff3bb14e PublishDatasource created_at(2021-01-28 20:44:46+00:00) started_at(None) completed_at(2021-01-28 20:44:47+00:00) progress (100) finish_code(1)>

code:


# 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="3c808ebb-ec93-4f16-9e9d-bc1e4e176a50", name='requests_pls')

    conn_creds = []
    conn1 = ConnectionItem()
    conn1.server_address = 'xxxxx.eu-west-1.rds.amazonaws.com'
    conn1.server_port = '5432'
    conn1.username = 'dev'
    conn1._connection_type = 'postgres'
    conn1.embed_password = True
    conn1.connection_credentials = ConnectionCredentials('dev', 'password', embed=True)
    conn_creds.append(conn1)

    conn2 = ConnectionItem()
    conn2.server_address = 'xxxxxx2.eu-west-1.rds.amazonaws.com'
    conn2.server_port = '5432'
    conn2.username = 'other'
    conn2._connection_type = 'postgres'
    conn2.embed_password = True
    conn2.connection_credentials = ConnectionCredentials('other', 'password', embed=True)
    conn_creds.append(conn2)

    # Define publish mode - Overwrite, Append, or CreateNew
    publish_mode = TSC.Server.PublishMode.CreateNew

    # Publish datasource
    if args.async_:
        # Async publishing, returns a job_item
        new_job = server.datasources.publish(new_datasource, args.filepath, publish_mode, connection_credentials = None, connections=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 = None, connections=conn_creds)
        print("Datasource published. Datasource ID: {0}".format(new_datasource.id))

    jobinfo = server.jobs.get(new_job.id)
    print(jobinfo)

I feel so close!!!

PS - im on the latest tsc (0.14.1) and python 3.7 with server 2020.2

EDIT: I guess when i run is NOT as a job, i get some obscure 400011: Bad Request There was a problem publishing the file 'requests copy 1.hyper'. error. UGGHHHHHH. No clue.

jorwoods commented 3 years ago

Try also assigning to ConectionItem._datasource_name . I think that is the only other thing that I am assigning that you are not.

andymiller-og commented 3 years ago

Try also assigning to ConectionItem._datasource_name . I think that is the only other thing that I am assigning that you are not.

Thanks @jorwoods for helping out. I just found a workaround. If i publish it to Tableau Server, download it and than use that file for future publishing programmatically, it works. So I dont know if it just doesnt like .hyper and prefers .tdsx. I dont know if this means that I need to change something about how I setup the new DatasourceItem new_datasource = TSC.DatasourceItem(project_id="3c808ebb-ec93-4f16-9e9d-bc1e4e176a50", name='requests_pls'). But I have not figured out .hyper.

Also, I tested this with the datasource name and it appears is not required.

EDIT: Still trying to overcome an issue where the published DS does not have the password embedded DESPITE setting embed=True on the ConnectionItem() and ConnectionCredentials(). Very frustrating.

andymiller-og commented 3 years ago

Also have found that with the publishing of the .tdsx, the connection details im supplying in connections=conn_creds is not taking. When i change the connection details, its taking the creds that are in .tdsx. This is despite SEEING the post_request in publish in venv/lib/python3.7/site-packages/tableauserverclient/server/endpoint/datasources_endpoint.py. I can see the NEW user/password, but yet after successfully publishing to server, the original .tdsx connection details are there.

Therefore, i cannot understand how to overtake the existing creds.

endureit commented 3 years ago

Also have found that with the publishing of the .tdsx, the connection details im supplying in connections=conn_creds is not taking. When i change the connection details, its taking the creds that are in .tdsx. This is despite SEEING the post_request in publish in venv/lib/python3.7/site-packages/tableauserverclient/server/endpoint/datasources_endpoint.py. I can see the NEW user/password, but yet after successfully publishing to server, the original .tdsx connection details are there.

Therefore, i cannot understand how to overtake the existing creds.

Does your dashboard happen to have multiple data sources with at least 1 data source with multiple connections? Looks like you are publishing a data source with 2 connections?

I'm trying to publish a workbook that has multiple data source and the data sources have multiple connections. Thanks in advance!

endureit commented 3 years ago

Also have found that with the publishing of the .tdsx, the connection details im supplying in connections=conn_creds is not taking. When i change the connection details, its taking the creds that are in .tdsx. This is despite SEEING the post_request in publish in venv/lib/python3.7/site-packages/tableauserverclient/server/endpoint/datasources_endpoint.py. I can see the NEW user/password, but yet after successfully publishing to server, the original .tdsx connection details are there.

Therefore, i cannot understand how to overtake the existing creds.

bumping

kkrav3ts commented 2 years ago

@jorwoods Thanks for the idea... I was hoping that was it... I feel i am close becuase im getting the server to return a job with details but it still has finish code 1 (error) but no details of the error.

result: <Job#319f20dd-7bdf-4db3-a1e8-b91eff3bb14e PublishDatasource created_at(2021-01-28 20:44:46+00:00) started_at(None) completed_at(2021-01-28 20:44:47+00:00) progress (100) finish_code(1)>

code:


# 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="3c808ebb-ec93-4f16-9e9d-bc1e4e176a50", name='requests_pls')

    conn_creds = []
    conn1 = ConnectionItem()
    conn1.server_address = 'xxxxx.eu-west-1.rds.amazonaws.com'
    conn1.server_port = '5432'
    conn1.username = 'dev'
    conn1._connection_type = 'postgres'
    conn1.embed_password = True
    conn1.connection_credentials = ConnectionCredentials('dev', 'password', embed=True)
    conn_creds.append(conn1)

    conn2 = ConnectionItem()
    conn2.server_address = 'xxxxxx2.eu-west-1.rds.amazonaws.com'
    conn2.server_port = '5432'
    conn2.username = 'other'
    conn2._connection_type = 'postgres'
    conn2.embed_password = True
    conn2.connection_credentials = ConnectionCredentials('other', 'password', embed=True)
    conn_creds.append(conn2)

    # Define publish mode - Overwrite, Append, or CreateNew
    publish_mode = TSC.Server.PublishMode.CreateNew

    # Publish datasource
    if args.async_:
        # Async publishing, returns a job_item
        new_job = server.datasources.publish(new_datasource, args.filepath, publish_mode, connection_credentials = None, connections=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 = None, connections=conn_creds)
        print("Datasource published. Datasource ID: {0}".format(new_datasource.id))

    jobinfo = server.jobs.get(new_job.id)
    print(jobinfo)

I feel so close!!!

PS - im on the latest tsc (0.14.1) and python 3.7 with server 2020.2

EDIT: I guess when i run is NOT as a job, i get some obscure 400011: Bad Request There was a problem publishing the file 'requests copy 1.hyper'. error. UGGHHHHHH. No clue.

@jorwoods could you please advise how to get rid of such error after trying this option?

400000: Bad Request Deserialization problem: unexpected element (uri:"", local:"connections"). Expected elements are <{}owner>,<{}site>,<{}project>,<{}connectionCredentials>,<{}askData>,<{}tags>;

jorwoods commented 2 years ago

@kkrav3ts Were you able to figure out the error?

kkrav3ts commented 2 years ago

@jorwoods alas, no. I had to workaround it on the sources' side.

riyadfebrian commented 2 years ago

@jorwoods Thanks for the idea... I was hoping that was it... I feel i am close becuase im getting the server to return a job with details but it still has finish code 1 (error) but no details of the error. result: <Job#319f20dd-7bdf-4db3-a1e8-b91eff3bb14e PublishDatasource created_at(2021-01-28 20:44:46+00:00) started_at(None) completed_at(2021-01-28 20:44:47+00:00) progress (100) finish_code(1)> code:


# 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="3c808ebb-ec93-4f16-9e9d-bc1e4e176a50", name='requests_pls')

    conn_creds = []
    conn1 = ConnectionItem()
    conn1.server_address = 'xxxxx.eu-west-1.rds.amazonaws.com'
    conn1.server_port = '5432'
    conn1.username = 'dev'
    conn1._connection_type = 'postgres'
    conn1.embed_password = True
    conn1.connection_credentials = ConnectionCredentials('dev', 'password', embed=True)
    conn_creds.append(conn1)

    conn2 = ConnectionItem()
    conn2.server_address = 'xxxxxx2.eu-west-1.rds.amazonaws.com'
    conn2.server_port = '5432'
    conn2.username = 'other'
    conn2._connection_type = 'postgres'
    conn2.embed_password = True
    conn2.connection_credentials = ConnectionCredentials('other', 'password', embed=True)
    conn_creds.append(conn2)

    # Define publish mode - Overwrite, Append, or CreateNew
    publish_mode = TSC.Server.PublishMode.CreateNew

    # Publish datasource
    if args.async_:
        # Async publishing, returns a job_item
        new_job = server.datasources.publish(new_datasource, args.filepath, publish_mode, connection_credentials = None, connections=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 = None, connections=conn_creds)
        print("Datasource published. Datasource ID: {0}".format(new_datasource.id))

    jobinfo = server.jobs.get(new_job.id)
    print(jobinfo)

I feel so close!!! PS - im on the latest tsc (0.14.1) and python 3.7 with server 2020.2 EDIT: I guess when i run is NOT as a job, i get some obscure 400011: Bad Request There was a problem publishing the file 'requests copy 1.hyper'. error. UGGHHHHHH. No clue.

@jorwoods could you please advise how to get rid of such error after trying this option?

400000: Bad Request Deserialization problem: unexpected element (uri:"", local:"connections"). Expected elements are <{}owner>,<{}site>,<{}project>,<{}connectionCredentials>,<{}askData>,<{}tags>;

you can't put parameters connections inside server.datasources.publish. it's only accepting connection_credentials. however if you want to put connections, you can add in DatasourceItem

MeghanaShivamurthy commented 2 months ago

Hi @jorwoods I am trying to publish workbook which contains multiple data sources and using the same logic mentioned above I am facing “none type” error while trying to publish the workbook what’s the way out of this?

MeghanaShivamurthy commented 2 months ago

Hi @jorwoods ,

Thanks so much for your response and suggestion.

Yes as you said I have been passing the TSC.ConnectionItems for Live and Extract connections like below :

connection1 = ConnectionItem()

connection1.server_address = server_address

connection1.connection_credentials = ConnectionCredentials(username, password, True)

all_connections.append(connection1)

all_connections

new_job = server.workbooks.publish(new_workbook, file_path,

overwrite_true, connections=all_connections, as_job='--as-job')

However when I wanted to do the same for Published Data source I used below approaches :

  1. Tableau Server credentials and Tableau Server address. It did not work. I felt it might be for our SAML Protection in Tableau Server. Hence I tried to do the same approach in my own Tableau Online site without SAML Protection that also did not work.
  2. I fetched the corresponding datasource which was used in the workbook,did populate_connections and fetched the connection_item. Then appended the connection item to the iterable list all_connections. It also failed with the below error.

Error status code: 500

b'

Internal Server Error The server encountered an error and cannot complete your request. Contact your server administrator.'

  1. Next I tried as I told above with connections=None that worked fine.

The challenge happens when I need to publish a workbook with multiple connection, like live connection Data Source along with the published data source. Then I need that all_connection iterable list and it keeps failing as when I pass a "None" into that along with the connection_item. It fails with error "'NoneType' object has no attribute 'server_address'".

Thanks again for your response, please let me know if you need further details on this.

Thank you Tableau Community for giving us the podium to interact with experts and get so much knowledge.

Regards Avijit

Hey @avijit3660 did you find a solution to get past the nonetype error? If yes what changes needs to be done?