Google Apps Script UrlFetchApp.fetch converts integers to floats? - google-apps-script

I am writing a Google App Script that accesses several APIs using UrlFetchApp
One of my API POST requests expects an integer value in the payload, but UrlFetchApp.fetch changes the integer value (i.e. 0) to a float (i.e. 0.0)
My options look something like this:
const options = { method: 'POST', headers: {...}, payload: { someStringA: "foo", someStringB: "bar", App: 0 } }
And the error msg returned from the API is:
{"message":"The request is invalid.","modelState":{"request.App":["The value '0.0' is not valid for App.","The App field is required."]}}
UrlFetchApp.fetch has converted my App value of: 0 to: 0.0 which this API endpoint does not accept.
I tried forcing the request value to a string "0" instead, but the API does not accept a string type for that property.
Does anyone know of a workaround for this? How can I tell UrlFetchApp.fetch to keep integer values "as is" and not convert them to floats?
ps. the same request works ok for me via cURL like this:
curl --location --request POST "https://$url" \
--header 'Content-Type: application/json' \
--header "Authorization: Bearer $token" \
--data "{\"someStringA\":\"$foo\",\"someStringB\":\"$bar\",\"app\":0}"
Thanks in advance for any assistance
UPDATE: apologies, but the problem here was not with UrlFetchApp after all. My problem was that the response coming back from the api was an empty object and I was trying to log it like this: JSON.parse(response); resulting in the errors mentioned.

From your question, unfortunately, I'm not sure about the specification of the API you want to use, your request header and your curl command you confirmed. So I would like to propose the following modification patterns as my guess.
From:
const options = { method: 'POST', headers: {...}, payload: { someStringA: "foo", someStringB: "bar", App: 0 } }
To: pattern 1
const options = { method: 'POST', headers: {...}, contentType: "application/json", payload: JSON.stringify({ someStringA: "foo", someStringB: "bar", App: 0 }) };
In this case, the data is sent as JSON.
If you are using Content-Type in headers, please remove it.
To: pattern 2
const options = { method: 'POST', headers: {...}, payload: JSON.stringify({ someStringA: "foo", someStringB: "bar", App: 0 }) };
In this case, the data is sent as form data of the string value.
To: pattern 3
const options = { method: 'POST', headers: {...}, payload: { someStringA: "foo", someStringB: "bar", App: "0" } };
In this case, "0" is sent as a string value of form data in the parsed data.
Note:
If above modification patterns were not the solution of your issue, can you provide your sample curl command that you confirmed? By this, I would like to modify my answer.
Reference:
Class UrlFetchApp
Added:
From your curl command, I understood that my pattern 1 is the same request with your curl command. But from Unexpected end of JSON input of your replying, unfortunately, although I'm not sure about your current script using my pattern 1, when your curl command is converted to Google Apps Script, it becomes as follows. Can you test it again?
const token = "###"; // Please use your token.
const data = { someStringA: "foo", someStringB: "bar", App: 0 };
const options = {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
contentType: "application/json",
payload: JSON.stringify(data)
};
const res = UrlFetchApp.fetch("your URL", options);
console.log(res.getContentText())

Related

How to turn Postman API's request into Apps Script code?

I'm currently using Postman to do an API post request from a CRM software called Intercom.
I followed the below documentation to do this:
https://developers.intercom.com/intercom-api-reference/v0/reference/creating-an-export-job
My purpose is to create a script via Google Apps Script to automate the API request.
I need to give the following elements:
Method: Post
URL: https://api.intercom.io/export/content/data
Headers: Authorization : Bearer 123456789, Accept : application/json, Content-Type: application/json
Body: "created_at_after": 1654041600, "created_at_before": 1656547200 (this is the date in Unix Timestamp)
The only parameter that will change is the body ("created_at_after" and "created_at_before"). Everything else will remain the same.
Below is the script I've created, that currently does not work.
Any help on how to fix this would be appreciated. I'm quite a beginner programmer so apologies in advance if the problem is quite obvious.
function exportjob() {
var url = 'https://api.intercom.io/export/content/data';
var options = {
"Method": "post",
"Headers": {
"Authorization": "Bearer 123456789",
"Accept": "application/json",
"Content-Type": "application/json"
},
"Body": {
"created_at_after": 1654041600,
"created_at_before": 1656547200}
}
var response = UrlFetchApp.fetch(url, options);
}
From your showing document, I believe your goal is as follows.
You want to convert the following curl command to Google Apps Script.
curl https://api.intercom.io/export/content/data \
-X POST \
-H 'Authorization:Bearer <Your access token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' -d '
{
"created_at_after": 1527811200,
"created_at_before": 1530316800
}'
Modification points:
At params of fetch(url, params) of Class UrlFetchApp, there are no properties of Method, Headers, Body.
In your situation, it seems that it is required to be sent the data as the string. And, by the content type of application/json, the data is parsed at the server side.
When these points are reflected in your script, how about the following modification?
Modified script:
function exportjob() {
var url = 'https://api.intercom.io/export/content/data';
var options = {
"method": "post",
"headers": {
"Authorization": "Bearer 123456789",
"Accept": "application/json",
},
"contentType": "application/json",
"payload": JSON.stringify({
"created_at_after": 1654041600,
"created_at_before": 1656547200
})
}
var response = UrlFetchApp.fetch(url, options);
console.log(response.getContentText())
}
Note:
If an error occurs for the request, please confirm your access token and the data again. And, please provide the error message.
Reference:
fetch(url, params)

how can i send a array in json i have used json.stringify

How can i send this array in json format it going as {'test_ids[]' : '[value]'}
i want it as {'test_ids[]' : 'value'} can anyone help me with this please
const [multitest, setTests] = React.useState([]);
.post(url,
{'test_ids[]': JSON.stringify(multitest)},
{
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
})
If the array - multitest - has only one element, then simply extract it as such
JSON.stringify(multitest[0]);

Authentication error when attempting to fetch google analytics 4 with app script

I would like to connect a community connector to a google analytics 4 account so that I can easily modify the data and send it to data studio. However, My code is returning an authentication error:
{ error:
{ code: 401,
message: 'Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.',
status: 'UNAUTHENTICATED' } }
I have included the token, but I am unsure if I am making the correct url call or if there is some other issue that I am unaware of. I don't believe I need an API key to connect from community connector to a google API, but I may be wrong. I did create an API key but the result was the same.
function testFetch(){
var url = "https://analyticsdata.googleapis.com/v1alpha:runReport"
var token = ScriptApp.getOAuthToken();
var options = {
"method" : 'POST',
"entity": { "propertyId": "263290444" },
"dateRanges": [{ "startDate": "2020-12-01", "endDate": "2021-03-01" }],
"dimensions": [{ "name": "country" }],
"metrics": [{ "name": "activeUsers" }],
'muteHttpExceptions': true,
headers: {
Authorization: 'Bearer' + token,
},
};
var response = UrlFetchApp.fetch(url, options);
var result = JSON.parse(response.getContentText());
}
Here is a small guide on how to do what you are trying to achieve:
Set explicit OAuth scopes (see documentation) to your Apps Script project manifest (appsscript.json). In this case you need to add the following:
{
...
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/analytics.readonly"
}
}
Then you need to separate the method parameters from the fetch options. The fetch options need to be stringified and added to payload. You also need to set the contentType to JSON.
const options = {
entry: { propertyId: "263290444"},
// etc.
}
const response = UrlFetchApp.fetch(
'https://analyticsdata.googleapis.com/v1alpha:runReport',
{
method: 'POST',
muteHttpExceptions: true,
headers: {
'Authorization': `Bearer ${ScriptApp.getOAuthToken()}`
},
contentType: 'application/json; charset=utf-8',
payload: JSON.stringify(options)
}
)
After that, you may use the response as you were doing before.
Note that Bearer and the token need to be separated by a space, which your code does not have. It's hard to see because of the concatenation and that why I usually use template literals (see documentation).
References
Authorization scopes | Set explicit scopes (Google Apps Script Guides)
UrlFetchApp.fetch(url, params) (Google Apps Script Reference)
Template literals (MDN)

GAS: Error 401 while connecting to an API

I'm trying to connect to Cloud Waitress API, a solution for restaurants,
Documentation: https://apidocs.cloudwaitress.com/#orderpromos
It's documentation gives an example on how to connect to the API:
curl https://api.cloudwaitress.com/v1/orders \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: YOUR_API_KEY" \
-d `
{
"restaurant_id": "xxxxxxx",
"limit": 10,
"page": 1,
"sort": { "created": -1 },
}
I tried to create a Script in GAS to get the information back to a Spreadsheet.
Based on this question:
How curl maps onto Google App Script URLFetchApp
I have changed my code as follows:
function getAuthHeader(){
var apiKey = "SOME-API-KEY";
var authHeader = Utilities.base64Encode(apiKey);
return {
headers: {Authorization: authHeader}
}
}
function GetOrders(){
var url = "https://api.cloudwaitress.com/v1/orders";
var data = {
"restaurant_id":"SOME-RESTAURANT-ID",
"limit": 10,
"page": 1,
"sort": { "created": 1 }
};
var result = goPost(url ,data);
}
function goPost(url,data){
var options = {
'method' : 'post',
'payload' : data,
'headers': getAuthHeader()['headers']
};
var response;
try{
response = JSON.parse(UrlFetchApp.fetch(url,options).getContentText());
}catch(err){
Logger.log(err);
}
return response;
}
Right now the new error that I'm getting is:
Exception: Request failed for https://api.cloudwaitress.com returned
code 401. Truncated server response: {"outcome":1,"message":"Invalid
Authentication"} (use muteHttpExceptions option to examine full
response)
Which I believe is quite a progress since I was getting an 500 error before.
I have asked the cloudwaitress team their assistance, however I wonder if there is something else I can try.
How about this modification?
Modification points:
At your sample curl, "Content-Type: application/json" is used, and the JSON object is converted to the string.
The thread you refered uses the basic authorization like -u testtoken123:. By this, Utilities.base64Encode is required be used. But in your sample curl, the API key is directly used.
I thought that this might be the reason of the error message.
When above points are reflected to your script, it becomes as follows.
Modified script:
From:
var options = {
'method' : 'post',
'payload' : data,
'headers': getAuthHeader()['headers']
};
To:
var apiKey = "SOME-API-KEY";
var options = {
'method' : 'post',
'payload' : JSON.stringify(data),
'headers': {Authorization: apiKey},
'contentType': "application/json",
};
In this case, getAuthHeader() is not used.
Note:
The request of this modified script is the same with your sample curl command. But in this modified script, it supposes that your apiKey and data can be used for this API. Please be careful this.
Reference:
Class UrlFetchApp

Slack API call to postMessage not working

I'm just trying to make a simple postMessage call from a google apps script with an image attached, but I get the following response:
"{"ok":false,"error":"invalid_arg_name"}"
Here is the function that creates the payload:
function getPostMessagePayload(fileUrl) {
var content = {
"channel":"#data-vis",
"token": ACCESS_TOKEN,
"text":"Chart update:",
"attachments": [
{
"title": "Chart",
"fallback": "Fallback",
"text": "Testing chart",
"image_url": fileUrl
}
]
};
return content;
}
And here is where I make the request:
var POST_MESSAGE_ENDPOINT = 'https://slack.com/api/chat.postMessage';
function performPostMessage(payload) {
var res = UrlFetchApp.fetch(
POST_MESSAGE_ENDPOINT,
{
method: "post",
payload: JSON.stringify(payload),
muteHttpExceptions: true,
}).getContentText();
return res;
}
It's impossible to tell what the actual problem is. I've tried making my token obviously incorrect, the URL obviously incorrect, and deleting/adding random args and it gives the same response every time.
When I use the webhook to do this rather than the API, it works fine.
My app has the following permissions in Slack:
chat:write:bot
incoming-webhook
Problem
You are sending a JSON object as payload with your POST request, whilst the contentType parameter of the fetch() method is defaulted to application/x-www-form-urlencoded.
Solution 1
In addition to JSON.stringify(), to ensure the payload is sent correctly, wrap it in an encodeURIComponent() built-in function. If the issue persists, continue to solution 2.
Update to solution 1
Nearly forgot how fetch() method treats objects passed to payload with default x-www-form-urlencoded content type. Remove the JSON.stringify() entirely (and add encodeURI() / encodeURIComponent() if needed).
Solution 2
Slack API supports application/json content type of POST requests. In your case it might be easier to send the request with contentType parameter set to application.json (note that you will have to move authorization from payload to headers):
//fetch part;
var res = UrlFetchApp.fetch(
POST_MESSAGE_ENDPOINT,
{
method : 'post',
contentType : 'application/json',
headers : {
Authorization : 'Bearer ' + ACCESS_TOKEN
},
payload : JSON.stringify(payload),
muteHttpExceptions : true,
})
//payload part;
var payload = {
"channel" : "#data-vis",
"text" : "Chart update:",
"attachments" : [
{
"title" : "Chart",
"fallback" : "Fallback",
"text" : "Testing chart",
"image_url" : fileUrl
}
]
};
Useful links
fetch() method reference;
postMessage method reference (Slack API);