Tableau: Unable to upload Data Source using REST. [NodeJS] - node.js

I am trying to upload a data source to my tableau site using the following code:
const request = require('request')
const fs = require('fs')
const options = {
url: 'https://prod-apnortheast-a.online.tableau.com/api/3.12/sites/578341b7-325c-4dda-be08-ff0b10c4b18c/datasources',
headers: {
Authorization: 'Bearer xxxxxxxxxxxxxx',
'Content-Type': 'application/xml;charset=UTF-8',
boundary: 'boundary-string'
},
body: `--boundary-string
Content-Disposition: name="request_payload"
Content-Type: text/xml
<tsRequest>
<datasource name="datasource-name"
description="datasource-description">
<connectionCredentials name="xxxx#abc.com"
password="xxxxxx"/>
<project id="96ec15ee-eef1-41ac-beda-1f03d9209305" />
</datasource>
</tsRequest>
--boundary-string
Content-Disposition: name="tableau_datasource"; filename="datasource-file-name"
Content-Type: application/octet-stream
This is the content of data source file.
Hello from Mayank
--boundary-string--`
}
function callback (error, response, body) {
if (error) {
console.log(error)
}
console.log( body, response.headers, response.statusCode)
}
request.post(options, callback)
But instead of a successful upload, I get the following error:
Response Body:
<?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.12.xsd"><error code="406000"><summary>Bad Request</summary><detail>Content type 'application/xml;charset=UTF-8' not supported</detail></error></tsResponse>
Response Headers:
{
'content-type': 'application/xml;charset=UTF-8',
date: 'Mon, 30 Aug 2021 08:17:57 GMT',
p3p: 'CP="NON"',
'referrer-policy': 'strict-origin-when-cross-origin',
server: 'Tableau',
'set-cookie': [
'hid=pdanaa-hap01; domain=.prod-apnortheast-a.online.tableau.com; path=/; HttpOnly; Secure; SameSite=None',
'AWSELB=05DBF7950E7E74D8AC3E3765F2EF65B6BB96F639EDB7A6D781435ACF3E27CEC2643898FB33239EFFBCA90E45D6EE0951AC6ECA4251ACA4E386D74627D58239403899B395F5F04C31144F69D44D5789C3FA7D6D9DC6;PATH=/;DOMAIN=.prod-apnortheast-a.online.tableau.com;SECURE;HTTPONLY;SAMESITE=None'
],
'strict-transport-security': 'max-age=31536000; includeSubDomains',
tableau_error_code: '0xE3C7443A',
tableau_error_source: 'NeedsClassification',
tableau_service_name: 'vizportal',
tableau_status_code: '2',
'x-content-type-options': 'nosniff',
'x-tableau': 'Tableau Server',
'x-ua-compatible': 'IE=Edge',
'x-xss-protection': '1; mode=block',
'content-length': '365',
connection: 'Close'
}
Response Status Code: 406
References:
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_publishing.htm#publish_data_source
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm

According to Tableau's documentation here
The Content-Type header for the request must be set to multipart/mixed; boundary=boundary-string.
Although I'm using PHP, I had this exact same error as you while trying to publish flows using the API. Changing to "multipart/mixed" in the Content-Type fixed it for me (wasn't the end of my problems though). So maybe changing your code to
...
headers: {
Authorization: 'Bearer xxxxxxxxxxxxxx',
'Content-Type': 'multipart/mixed; boundary=boundary-string'
},
...
could do the trick.
Please notice the content type change and that boundary is not a header by itself like you did.
Just note, your example code as it is might still generate 400 errors since that's not a valid data source file. Since I run Tableau Server I had to do a lot of troubleshooting by tailing the logs at the server and try to figure what was wrong.

Related

Remix cannot decode endpoint data using gzip

I am working on a remix project and have gotten into an issue where a loader requesting data from a foreign endpoint encoded with gzip do not seem to be decoded.
The remix loader is fairly simple, with some simplification it looks like this:
export const loader = async () => {
try {
const [
encodedData,
[... <other responses>]
] = await Promise.all([
gzippedEndpoint(),
[... <other requests>]
]).catch((e) => {
console.error(e);
});
return json([<loader data>]);
} catch (error) {
console.log("ERROR:", error);
return json({});
}
};
It's the gzippedEndpoint() that fails, where the error stack claims that the returned data is not valid json. I figured compression should not be a problem, but it seems like the fetch requests on the remix server side cannot correctly decode the gzipped data. I also see no option to enable decoding explicitly on remix. When I disable gzip on the foreign endpoint everything works fine for the remix server making the request and parsing the response.
Here is an example of the headers from a returned response (with some obfuscation):
200 GET https://dev.server.com/public/v1/endpoint {
'cache-control': 'no-store, must-revalidate, no-cache',
connection: 'close',
'content-encoding': 'gzip',
'content-type': 'application/json',
date: 'Mon, 12 Sep 2022 06:51:41 GMT',
expires: 'Mon, 12 Sep 2022 06:51:41 GMT',
pragma: 'no-cache',
'referrer-policy': 'no-referrer',
'strict-transport-security': 'max-age=31536000 ; includeSubDomains',
'transfer-encoding': 'chunked',
}
Is there some remix option or request header that I am missing here?

How to delete multiple files on azure blob storage through rest api batch delete

I was trying to delete the multiple files from the container using the API provided by azure name as blob batch. So i followed the same instruction explained in this doc
I am getting the following body in response
'--batchresponse_ffab056e-336a-49e9-a878-371bd69b15f6
Content-Type: application/http
HTTP/1.1 400 One of the request inputs is not valid.
x-ms-error-code: InvalidInput
x-ms-request-id: <RequestId>
x-ms-version: 2020-04-08
Content-Length: 221
Content-Type: application/xml
Server: Windows-Azure-Blob/1.0
<?xml version="1.0" encoding="utf-8"?>
<Error><Code>InvalidInput</Code><Message>One of the request inputs is not valid.
RequestId: <RequestId>
Time:2021-11-23T13:01:31.5627264Z</Message></Error>
--batchresponse_ffab056e-336a-49e9-a878-371bd69b15f6--'
This was my request options and subrequest in batch
const boundary = "batch_357de4f7-6d0b-4e02-8cd2-6361411a9525";
const separation = "--"+boundary + "\n";
const ending = "--" + boundary + "--";
const fileList = ['sample1.json','sample2.json']
const subRequest = `--batch_357de4f7-6d0b-4e02-8cd2-6361411a9525\r
Content-Type: application/http\r
Content-Transfer-Encoding: binary\r
\r
DELETE /${azureDirectoryName}/${fileList[0].fileName} HTTP/1.1\r
Host: https://<accountName>.blob.core.windows.net\r
x-ms-date: ${strTime}\r
Authorization: Bearer ${authToken}\r
Content-Length: 0\r
--batch_357de4f7-6d0b-4e02-8cd2-6361411a9525--\r`
const requestObj = {
'Host': '<accountName>.blob.core.windows.net',
url: 'https://<accountName>.blob.core.windows.net/?comp=batch',
method: 'POST',
headers: {
'Content-Type': 'multipart/mixed; boundary=' + boundary,
"x-ms-date": new Date(),
"x-ms-version": "2020-04-08",
"Content-Length": subRequest.length,
Authorization: 'Bearer ' + authToken
},
body: subRequest
}
request(requestObj, (err, resp) => {
//internal logic
})
RequestObj JSON
'{"url":"https://<accountName>.blob.core.windows.net/?comp=batch","method":"POST","headers":{"Content-Type":"multipart/mixed; boundary=batch_357de4f7-6d0b-4e02-8cd2-6361411a9525","x-ms-date":"Wed, 24 Nov 2021 04:45:21 GMT","x-ms-version":"2018-11-09","Content-Length":2166,"Authorization":"Bearer <authtoken>"},"body":"--batch_357de4f7-6d0b-4e02-8cd2-6361411a9525\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nDELETE /containerName/test.json HTTP/1.1\r\nHost: https://<accountName>.blob.core.windows.net\r\nx-ms-date: Wed, 24 Nov 2021 04:45:21 GMT\r\nAuthorization: Bearer <authToken>\r\nContent-Length: 0\r\n--batch_357de4f7-6d0b-4e02-8cd2-6361411a9525--\r"}

Request returns Invalid or nonexistent Content-Type even if I put content-type in header

I'm trying to create an app-managed bucket but am encountering the error: Invalid or nonexistent Content-Type, accepted values are {text/json, application/json}
I'm using node.js and request-promise package. The error is confusing to me because I am setting my content-type within the headers of the request to application/json.
Here's my function which makes the request:
let globalOptions = {
resolveWithFullResponse: true
};
function createAppManagedBucket(){
let forgeToken = "eyJhb..."
const options = Object.assign({}, globalOptions, {
method: 'POST',
uri: `https://developer.api.autodesk.com/oss/v2/buckets`,
headers: {
"Content-Type": "application/json",
'User-Agent': 'Request-Promise'
},
form: {
"bucketKey": `someTestBucket`,
"policyKey": `transient`
},
auth: {
'bearer': forgeToken
},
json: true
})
return rp(options)
.then((response) => {
return response.body
}).catch((err) => {
return err
})
}
It seems like even though I've set Content-Type: application/json within the header my request is being forced to have Content-Type: application/x-www-form-urlencoded. If I log the response of this, then I get the error and it looks like my request is actually correct since these are my headers:
rawHeaders:
[ 'Access-Control-Allow-Headers',
'Authorization, Accept-Encoding, Range, Content-Type',
'Access-Control-Allow-Methods',
'GET',
'Access-Control-Allow-Origin',
'*',
'Content-Type',
'application/json; charset=utf-8',
'Date',
'Tue, 09 Apr 2019 15:58:07 GMT',
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains',
'Content-Length',
'99',
'Connection',
'Close' ],
But further down in the request I see
_header: 'POST /oss/v2/buckets HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nUser
e\r\nhost: developer.api.autodesk.com\r\nauthorization: Bearer eyJhb...\r\naccept: application/json\r\ncontent-length: 43\r\nConnection: close\r\n\r\n'
Where I can see that the content-type is actually changing to application/form_urlencodedSo it looks like my content-type is being forced to something other than what I set in the header. Has anyone encountered something like this before?
From the request options documentation,
form - when passed an object or a querystring, this sets body to a querystring representation of value, and adds Content-type: application/x-www-form-urlencoded header. When passed no options, a FormData instance is returned (and is piped to request). See "Forms" section above.
You can't mix the json and form request options like you are doing without the Content Type being ambiguous

Trello responds 'invalid key'

I am trying to get JSON data for a Trello board using the following URL, using Node.js's https module:
https://trello.com/b/nC8QJJoZ.json
Here's my code:
var https = require('https');
https.get('https://trello.com/b/nC8QJJoZ.json', function (res) {
console.log('statusCode:', res.statusCode);
console.log('headers:');
console.log(res.headers);
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log(chunk);
});
}).on('error', function (e) {
console.log('ERROR: ' + e);
});
Although the URL works perfectly in browser, It returns a body containing the string "invalid key", with a 401 status. Following is the output:
statusCode: 401
headers:
{ 'cache-control': 'max-age=0, must-revalidate, no-cache, no-store',
'x-content-type-options': 'nosniff',
'strict-transport-security': 'max-age=15768000',
'x-xss-protection': '1; mode=block',
'x-frame-options': 'DENY',
'x-trello-version': '1.430.0',
'x-trello-environment': 'Production',
'set-cookie':
[ 'dsc=ae78a354044f982079cd2b5d8adc4f334cda679656b3539ee0adaaf019aee48e; Path=
'visid_incap_168551=/NYMaLRtR+qQu/H8GYry1BCKl1UAAAAAQUIPAAAAAAC1zWDD1JLPowdC
'incap_ses_218_168551=+/2JSB4Vz0XJO/pWbX4GAxCKl1UAAAAA0pAbbN5Mbs4tFgbYuskVPw
expires: 'Thu, 01 Jan 1970 00:00:00',
'content-type': 'text/plain; charset=utf-8',
'content-length': '12',
etag: 'W/"c-b1ec112"',
vary: 'Accept-Encoding',
date: 'Sat, 04 Jul 2015 07:24:00 GMT',
'x-iinfo': '1-11281210-11279245 PNNN RT(1435994639565 404) q(0 0 0 -1) r(3 3) U
'x-cdn': 'Incapsula' }
invalid key
What am I doing wrong?
Well, it turns out that we need to provide a Trello API application key (generated from here) with our request.
var https = require('https');
var KEY = '<replace this with your app key>';
https.get('https://trello.com/b/nC8QJJoZ.json?key=' + KEY, function (res) {
...
});
This seems to me a weird requirement because we are not using Trello's API endpoint. (Even if I solved the problem, I would still like to know why a browser can access the resource, but a server side script cannot.)

Node.js http.get request gets a totally different set of headers from a curl request

Okay I'm trying to get the headers of this specific URL and node.js behaviour baffles me.
My code :
var http = require('http');
var req = http.get("http://listen.radionomy.com/abc-lounge", function(res) {
console.log("headers: ", res.headers);
});
Prints out :
headers: { 'cache-control': 'private', 'content-type': 'text/html;
charset=utf-8', server: 'Microsoft-IIS/7.5', 'x-aspnet-version':
'4.0.30319', 'x-powered-by': 'ASP.NET', date: 'Tue, 28 Jan 2014
14:18:27 GMT', 'content-length': '8309' }
Now I tried out the command line curl with headers :
curl -I http://listen.radionomy.com/abc-lounge
This prints out exactly what I'm looking for (The redirect url) :
HTTP/1.1 302 Found Cache-Control: private Content-Length:
0 Content-Type: application/octet-stream Location:
http://streaming.radionomy.com/ABC-Lounge Server:
Microsoft-IIS/7.5 X-AspNetMvc-Version: 5.0 X-AspNet-Version:
4.0.30319 X-Powered-By: ASP.NET Date: Tue, 28 Jan 2014 14:19:20 GMT
I don't understand why node is getting a different set of headers. It should not follow redirects by default. I don't even think this is a redirect issue.
This is because http.get actually follows the redirect
I fake a curl request by adding some headers to your http.get like this:
var http = require('http');
var options = {
host: 'listen.radionomy.com',
path: '/abc-lounge',
headers: {
'user-agent': 'curl/7.31.0',
'accept': '*/*'
}
};
var req = http.get(options, function(res) {
console.log('status:', res.statusCode)
console.log("headers: ", res.headers);
});
Output will be:
status: 302
headers: { 'cache-control': 'private',
'content-type': 'application/octet-stream',
location: 'http://streaming.radionomy.com/ABC-Lounge',
server: 'Microsoft-IIS/7.5',
'x-aspnetmvc-version': '5.0',
'x-aspnet-version': '4.0.30319',
'x-powered-by': 'ASP.NET',
date: 'Tue, 28 Jan 2014 15:02:16 GMT',
'content-length': '0' }

Resources