Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Presto web connector for Tableau
- Loading branch information
1 parent
02c8f9f
commit 0f50a33
Showing
7 changed files
with
665 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,4 +15,5 @@ Presto Documentation | |
sql | ||
migration | ||
develop | ||
tableau | ||
release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
************************* | ||
Web Connector for Tableau | ||
************************* | ||
|
||
Presto web connector for Tableau implements the functions in the Tableau web | ||
connector API and lets users run queries from Tableau against Presto. You can | ||
get more info about the Tableau web connector API at | ||
`<http://community.tableau.com/community/developers/web-data-connectors>`_. | ||
|
||
When creating a new web data source Tableau will ask for the URL of the web | ||
connector, which is | ||
``http://[presto_coordinator_host]:[port]/tableu/presto-connector.html`` | ||
where ``presto_coordinator_host`` is the hostname that Presto coordinator is | ||
running on, ``port`` is 8080 by default. When Tableau first loads the Presto | ||
web connector it will render an HTML form. In this form you need to fill in | ||
details such as your user name, the catalog and the schema you want to query, | ||
the data source name, and finally the SQL query to run. After you click | ||
``Submit`` the query will be submitted to the Presto coordinator and Tableau | ||
will then create an extract out of the results retrieved from the coordinator | ||
page by page. After Tableau is done extracting the results of your query you | ||
can then use this extract for further analysis with Tableau. | ||
|
||
.. note:: | ||
With Presto web connector you can only create Tableau extracts as the web | ||
connector API currently doesn't support the live mode. | ||
|
||
|
102 changes: 102 additions & 0 deletions
102
presto-main/src/main/resources/webapp/tableu/presto-client.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/** | ||
Tableau web connector doc says that it supports: bool, date, datetime, float, int, and string | ||
So, fields with complex types are converted to json strings. | ||
*/ | ||
function StatementClient(connectionData, headerCallback, dataCallback, errorCallback) { | ||
this.query = connectionData.query; | ||
this.catalog = connectionData.catalog; | ||
this.schema = connectionData.schema; | ||
this.source = 'Tableau Web Connector'; | ||
this.user = connectionData.userName; | ||
this.sessionParameters = []; | ||
|
||
this.headerCallback = headerCallback; | ||
this.dataCallback = dataCallback; | ||
this.errorCallback = errorCallback; | ||
|
||
this.currentResults = null; | ||
this.valid = true; | ||
|
||
if (!(connectionData.sessionParameters === undefined)) { | ||
var parameterMap = JSON.parse(connectionData.sessionParameters); | ||
for (var name in parameterMap) { | ||
var value = parameterMap[name]; | ||
this.sessionParameters.push(name + '=' + value); | ||
} | ||
} | ||
|
||
this.headers = { | ||
"X-Presto-User": this.user ? this.user : 'N/A', | ||
"X-Presto-Source": this.source, | ||
"X-Presto-Catalog": this.catalog, | ||
"X-Presto-Schema": this.schema, | ||
"X-Presto-Session": this.sessionParameters | ||
}; | ||
|
||
// lastRecordNumber starts with 0 according to Tableau web connector docs | ||
this.submitQuery(0); | ||
} | ||
|
||
StatementClient.prototype.submitQuery = function(lastRecordNumber) { | ||
var statementClient = this; | ||
$.ajax({ | ||
type: "POST", | ||
url: '/v1/statement', | ||
headers: this.headers, | ||
data: this.query, | ||
dataType: 'json', | ||
// FIXME having problems when async: true | ||
async: false, | ||
error: function(xhr, statusStr, errorStr) { | ||
statementClient.errorCallback(xhr.responseText, errorStr); | ||
}, | ||
success: function(response) { | ||
statementClient.currentResults = response; | ||
statementClient.responseHandler(response, lastRecordNumber); | ||
} | ||
}); | ||
}; | ||
|
||
StatementClient.prototype.advance = function(lastRecordNumber) { | ||
if (!this.currentResults || !this.currentResults.nextUri) { | ||
this.valid = false; | ||
return; | ||
} | ||
|
||
var statementClient = this; | ||
$.ajax({ | ||
type: "GET", | ||
url: this.currentResults.nextUri, | ||
headers: this.headers, | ||
dataType: 'json', | ||
// FIXME having problems when async: true | ||
async: false, | ||
error: function(xhr, statusStr, errorStr) { | ||
statementClient.errorCallback(xhr.responseText, errorStr); | ||
}, | ||
success: function(response) { | ||
statementClient.currentResults = response; | ||
statementClient.responseHandler(response, lastRecordNumber); | ||
if (!(response.data || response.error)) { | ||
// no data in this batch, schedule another GET | ||
statementClient.advance(lastRecordNumber); | ||
} | ||
} | ||
}); | ||
}; | ||
|
||
StatementClient.prototype.responseHandler = function(response, lastRecordNumber) { | ||
if (response.error) { | ||
this.errorCallback(response.error.errorName, response.error.message); | ||
} | ||
|
||
if (response.columns) { | ||
this.headerCallback(response.columns); | ||
} | ||
|
||
if (response.data) { | ||
// push the columns first in case we didn't get columns in previous requests | ||
this.headerCallback(response.columns); | ||
this.dataCallback(response.data, response.columns, lastRecordNumber); | ||
} | ||
} |
Oops, something went wrong.