M Language - How to get JSON in HTTP Request Body? (Vimeo API Unsupported Grant Type Error) - json

I am attempting to create my first PowerBI Custom Connecter to connect to the Vimeo API. I am stuck on the final step of the authorization flow - getting back an access token. When trying out the Connecter in PowerBI, it seems to authenticate properly when I hit the access token endpoint, but I get back a warning "[unsupported_grant_type] Unsupported grant type"
It appears I am not sending the grant_type properly in the request. Here are Vimeo's requirements of what is sent along in the header and body of the request:
Header
Set value to
Authorization
basic base64_encode(x:y), where x is the client identifier and y is the client secret
Content-Type
application/json
Accept
application/vnd.vimeo.*+json;version=3.4
"In the request body, send the grant_type field with the value authorization_code. You must also set code to the authorization code string that you just received and redirect_uri to the redirect URI that you specified previously — don't use a different redirect URI."
{
"grant_type": "authorization_code",
"code": "{code}",
"redirect_uri": "{redirect_uri}"
}
Here is a snippet of code from the Customer Connector I am building. It is within this TokenMethod function that I am trying to fulfill the requirements of the table above. I am getting the sense I am not correctly placing the JSON in the body of the request, but I am stuck on what to try next:
TokenMethod = (grantType, tokenField, code) =>
let
queryString = [
grant_type = "authorization_code",
redirect_uri = redirect_uri,
client_id = client_id,
client_secret = client_secret
],
queryWithCode = Record.AddField(queryString, tokenField, code),
authKey = "Basic " & Binary.ToText(Text.ToBinary("client_id:client_secret"),BinaryEncoding.Base64),
tokenResponse = Web.Contents(token_uri, [
Content = Text.ToBinary(Uri.BuildQueryString(queryWithCode)),
Headers = [
#"Authorization" = authKey,
#"Content-type" = "application/json",
#"Accept" = "application/vnd.vimeo.*+json;version=3.4"
],
ManualStatusHandling = {400}
]),
body = Json.Document(tokenResponse),
result = if (Record.HasFields(body, {"error", "error_description"})) then
error Error.Record(body[error], body[error_description], body)
else
body
in
result;
I'm wondering if someone could please point out where I might be going astray in the code and why I am receiving the [unsupported_grant_type] error.
Many thanks for your time!

I changed Content-Type to "application/x-www-form-urlencoded" and it worked!

Related

HTTP Request to Discord API Through Google Apps Script Results in 403 Error With Response "Error Code 1020"

I'm having trouble getting guild channels with this code in Google Apps Script.
var token = "MY_TOKEN"
var url = "https://discord.com/api/v10/guilds/[GUILD_ID]/channels"
var header = {
Authorization: "Bot " + token
}
function myFunction(){
var params = {
method: "get",
headers: header,
muteHttpExceptions: false
}
var response = UrlFetchApp.fetch(url,params)
Logger.log(response.getContentText())
}
Running myFunction() results in this error message:
Exception: Request failed for https://discord.com returned code 403. Truncated server response: error code: 1020 (use muteHttpExceptions option to examine full response)
or just this if the muteHttpExceptions value is true:
error code: 1020
Some information that may be useful to diagnose my problem:
My bot is in my server, but it's shown to be offline in the member list (does it matter?)
My bot is a private bot
I have made sure that the bot token and guild ID are correct
I invited the bot to my server via a generated link in OAuth2 URL generator in Discord Developer Portal. I checked the bot scope only with administrator permission. (Additional info: The first time I tried generating URL, it didn't need a redirect URL. But for some reason, it requires me to select one from a dropdown list now, but when I click on the dropdown, the list is empty as shown in the screenshot, so I can't select one and can't generate URL from there anymore.)
Get Current User Guild (/users/#me/guilds) runs perfectly fine. It returns this array:
[
{
"id": "[GUILD_ID]",
"name": "[GUILD_NAME]",
"icon": null,
"owner": false,
"permissions": "4398046511103",
"features": [
"APPLICATION_COMMAND_PERMISSIONS_V2"
]
}
]
I think my problem has something to do with the bot permissions, but I'm just lost.

how to make a correct HTTP request to BigQuery from google script

I am working in google script API trying to get a schema of a table from BiqQuery... not sure why it is so troublesome.
I am sending a request like this :
let url = 'https://bigquery.googleapis.com/bigquery/v2/projects/'+ projectId +'/datasets/'+ datasetId +'/tables/' +tableId;
var response = UrlFetchApp.fetch(url)
I am getting this response:
Exception: Request failed for https://bigquery.googleapis.com returned code 401. Truncated server response: { "error": { "code": 401, "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie ... (use muteHttpExceptions option to examine full response) (line 68, file "bigQuery")
I have been able to load data to bigQuery alright... not sure why this does not work. I have looked at the OAuth fields in manifest and the script does have access to bigQuery...
no success also when adding this to the options field of the UrlFetch request
var authHeader = 'Basic ' + Utilities.base64Encode(USERNAME + ':' + PASSWORD);
var options = {
headers: {Authorization: authHeader}
}
Use bearer tokens
The reason why the BigQuery API rejects your requests is that the endpoint requires one of the following scopes to be provided with the access token to work, and it is missing from the request:
https://www.googleapis.com/auth/bigquery
https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/bigquery.readonly
https://www.googleapis.com/auth/cloud-platform.read-only
The actual issue here is that the basic authorization scheme lacks info about any claims, only sending over correct credentials. Since you are requesting the endpoint directly with UrlFetch service, despite correctly specifying the scopes in the manifest, they will not be sent over.
ScriptApp service now provides an easy method to get a valid bearer token without using an OAuth 2.0 library or building the flow from scratch: getOAuthToken. Pass it to an Authorization header as bearer token, and you should be all set:
const token = ScriptApp.getOAuthToken();
const options = {
headers : {
Authorization : `Bearer ${token}`
}
};
Use Advanced Service
As an alternative, there is an official advanced service as a wrapper around BigQuery REST API that will manage authentication and response parsing for you.
You must enable the BigQuery advanced service before using it
Also, note that the advanced service identifier is configurable, so you have to reference the identifier you chose.
In your case, the service can be used as follows (assuming you used the default BigQuery identifier). There is also the 4th argument of type object that contains optional arguments (not shown here):
Bigquery.Tables.get("projectId","datasetId", "tableId");
The method chain above corresponds to tables.get method of the BigQuery API.

How to pull data from Toggl API with Power Query?

First timer when it comes to connecting to API. I'm trying to pull data from Toggl using my API token but I can't get credentials working. I tried to replicate the method by Chris Webb (https://blog.crossjoin.co.uk/2014/03/26/working-with-web-services-in-power-query/) but I can't get it working. Here's my M code:
let
Source = Web.Contents(
"https://toggl.com/reports/api/v2/details?workspace_id=xxxxx&client=xxxxxx6&billable=yes&user_agent=xxxxxxx",
[
Query=[ #"filter"="", #"orderBy"=""],
ApiKeyName="api-token"
])
in
Source
After that I'm inputting my API Token into Web API method in Access Web content windows but I get an error that credentials could not be authenticated. Here's Toggl API specification:
https://github.com/toggl/toggl_api_docs/blob/master/reports.md
Web.Contents function receives two parameters: url + options
Inside options, you define the headers and the api_key, and other queryable properties, such as:
let
baseUrl = "https://toggl.com/",
// the token part can vary depending on the requisites of the API
accessToken = "Bearer" & "insert api token here"
options = [
Headers = [Authorization = accessToken, #"Content-Type" =
"application/Json"], RelativePath ="reports/api/v2/details", Query =
[workspace_id=xxxxx, client=xxxxxx6 , billable=yes, user_agent=xxxxxxx]
]
Source = Web.Contents(baseUrl, options)
// since Web.Contents() doesn't parse the binaries it fetches, you must use another
// function to see if the data was retreived, based on the datatype of the data
parsedData = Json.Document(Source)
in
parsedData
The baseUrl is the smallest url that works and never changes;
The RelativePath is the next part of the url before the first "?".
The Query record is where you define all the attributes to query as a record.
This is usually the format, but check the documentation of the API you're querying to see if it is similar.

POST request not able to find url

I am new to nodejs as well as developing.
I am trying to get a set of data bat from a nutrition site in JSON format. If I formulate the url with my app and api keys along with criteria to paste into the browser I get a JSON data ok. When I try to send a POST request as the site asks for when the request comes back it says it cannot find the url. What it is doing is attaching ':443' to the end of the host url and like I said coming back as an error:
Error: getaddrinfo ENOTFOUND https://api.nutritionix.com/v1_1/search https://api.nutritionix.com/v1_1/search:443
What I would like to do is after the end of the url is append the 'postData'.
Here is my code:
var https = require('https');
var querystring = require('querystring');
var postData = { // Nutrionix required JSON formt
"appId":"MY_APP_KEY",
"appKey":"MY_API_KEY",
"query": "Lentils",
"fields": ["item_name", "nf_calories", "nf_serving_size_qty", "nf_serving_size_unit"],
"sort":{
"field":"score",
"order":"desc"
},
"filters":{
"item_type":"2"
}
};
console.log("This is header dta" + postData);
postBody = querystring.stringify(postData);
var post_options = {
host:"https://api.nutritionix.com/v1_1/search",
"port":"443",
method:"post",
"path":"/",
headers:{"Content-Type":"application/json",
'Content-Length': postBody.length
}
}
console.log(post_options);
var request = https.request(post_options,function(response){
return response;
});
I also am passing this data into the dev HTTP add-on in Chrome and getting back the proper response.
Any help would be appreciated.
Can you please take a look at this documentation?
It seems that you don't need to mention HTTPS
Take the port off, 443 is the default for HTTPS.

Third Party SMS with google apps script

I would like to use a mail list to send SMS through a third party provider. Here is the code samples that they provide:
<%
' This simple ASP Classic code sample is provided as a starting point. Please extend your
' actual production code to properly check the response for any error statuses that may be
' returned (as documented for the send_sms API call).
username = "your_username"
password = "your_password"
recipient = "44123123123"
message = "This is a test SMS from ASP"
postBody = "username=" & Server.URLEncode(username) & "&password=" & Server.URLEncode(password) & "&msisdn=" & recipient & "&message=" & Server.URLEncode(message)
set httpRequest = CreateObject("MSXML2.ServerXMLHTTP")
httpRequest.open "POST", "http://bulksms.2way.co.za/eapi/submission/send_sms/2/2.0", false
httpRequest.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
httpRequest.send postBody
Response.Write (httpRequest.responseText)
%>
I am not sure how to do this in GAS (I am an amateur programmer really...). From googling it seems that I will need to use something like "UrlFetchApp.fetch". Any help or relevant links would be appreciated. Thanks in advance.
The function below creates a properly formatted POST. Without valid credentials, I can confirm that it gets a 200 OK HTTP response, and the server reports 23|invalid credentials (username was: your_username)|. So it looks like it should work, with the right details filled in.
I've included application/x-www-form-urlencoded for the contentType, although this is not needed because it's the default.
If you get this working with a set of test values, then the next step would be to change it to accept and use parameters - I'll leave that to you.
/*
* Sends an HTTP POST to provider, to send a SMS.
*
* #param {tbd} paramName To be determined.
*
* #return {object} Results of POST, as an object. Result.rc is the
* HTTP result, an integer, and Result.serverResponse
* is the SMS Server response, a string.
*/
function sendSMS() {
var url = "http://bulksms.2way.co.za/eapi/submission/send_sms/2/2.0";
var username = "your_username";
var password = "your_password";
var recipient = "44123123123";
var message = "This is a test SMS from ASP";
var postBody = {
"username" : encodeURIComponent(username),
"password" : encodeURIComponent(password),
"msisdn" : encodeURIComponent(recipient),
"message" : encodeURIComponent(message)
};
var options =
{
"method" : "post",
"contentType" : "application/x-www-form-urlencoded",
"payload" : postBody
};
// Fetch the data and collect results.
var result = UrlFetchApp.fetch(url,options);
var rc = result.getResponseCode(); // HTTP Response code, e.g. 200 (Ok)
var serverResponse = result.getContentText(); // SMS Server response, e.g. Invalid Credentials
debugger; // Pause if running in debugger
return({"rc" : rc, "serverResponse" : serverResponse});
}
References
Google Apps Script documentation for UrlFetchApp and HTTPResponse
When are you supposed to use escape instead of encodeURI / encodeURIComponent?