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

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

Open avijit3660 opened this issue 5 years ago • 20 comments

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.

avijit3660 avatar Aug 07 '20 08:08 avijit3660

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.

jorwoods avatar Aug 08 '20 13:08 jorwoods

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'<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.7.xsd">

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

  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

avijit3660 avatar Aug 10 '20 13:08 avijit3660

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

jorwoods avatar Aug 10 '20 20:08 jorwoods

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

avijit3660 avatar Aug 15 '20 06:08 avijit3660

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

hectorlavalle avatar Oct 28 '20 18:10 hectorlavalle

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?

endureit avatar Jan 06 '21 20:01 endureit

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>

andymiller-og avatar Jan 28 '21 16:01 andymiller-og

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

jorwoods avatar Jan 28 '21 16:01 jorwoods

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

andymiller-og avatar Jan 28 '21 20:01 andymiller-og

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

jorwoods avatar Jan 28 '21 23:01 jorwoods

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 avatar Jan 29 '21 11:01 andymiller-og

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.

andymiller-og avatar Jan 29 '21 13:01 andymiller-og

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 avatar Feb 05 '21 02:02 endureit

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

endureit avatar Feb 08 '21 22:02 endureit

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

kkrav3ts avatar Jan 28 '22 14:01 kkrav3ts

@kkrav3ts Were you able to figure out the error?

jorwoods avatar May 23 '22 14:05 jorwoods

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

kkrav3ts avatar May 23 '22 16:05 kkrav3ts

@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

riyadfebrian avatar Aug 27 '22 00:08 riyadfebrian

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 avatar Jul 09 '24 05:07 MeghanaShivamurthy

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.' 3. 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?

MeghanaShivamurthy avatar Jul 09 '24 05:07 MeghanaShivamurthy

I'm going to close this old issue. If anyone is still having a problem like this, please open a new issue with details.

bcantoni avatar Sep 19 '24 21:09 bcantoni