Google Spreadsheet JSON request - 400 status - json

Hi I have a public google spreadsheet at Google Docs. However when i request the JSON with:
$.getJSON( "https://spreadsheets.google.com/feeds/list/0Ak0qDiMLT3XddHlNempadUs1djdkQ0tFLWF6ci1rUUE/od6/public/values?alt=json-in-script&callback=?&gid=0",
function (data) { console.log(data) })
I get a HTTP 400 request error. Strangely the same exact code works for this spreadsheet.
What's going on here? Thanks a lot.

I was looking for a solution to the same problem. It led me to this.
Just want to share the fix:
"Warning: API requests using the public visiblity for feeds on spreadsheets that are not "Published to the Web" yield an HTTP 400 Bad Request response with the message The spreadsheet at this URL could not be found. Make sure that you have the right URL and that the owner of the spreadsheet hasn't deleted it."

From my experience, I guess you maybe had forgotten to share it or forgotten to make it public. I just tried it and it is working fine. By entering this link in a browser:
https://spreadsheets.google.com/feeds/list/0Ak0qDiMLT3XddHlNempadUs1djdkQ0tFLWF6ci1rUUE/od6/public/values?alt=json-in-script&gid=0

Related

Problem with UrlFetchApp.fetch in Google Apps Script

I am trying to use the ImportJSON.gs script to import data from my WooCommerce Web site directly into a Google Sheet. I have enabled the REST API in WooCommerce and am able to enter the URL directly into Chrome and get an accurate JSON response. The problem I am having is that when using UrlFetchApp.fetch, the data I get back suggests everything in the URL following a ? or & seems to be either truncated or reencoded to something else, but I haven't quite figured out what URL would give this response.
I have created this simple script to test it:
function myFunction() {
var response = UrlFetchApp.fetch("https://kidswonder.net/wp-json/wc-bookings/v1/products/slots?min_date=2023-01-30&max_date=2023-01-31&product_ids=16304");
Logger.log(response.getContentText());
}
The expected JSON response, which I get by entering the URL directly into a browser is:
{"records":[{"date":"2023-01-30T09:00","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T09:30","duration":30,"available":47,"booked":3,"product_id":16304},{"date":"2023-01-30T10:00","duration":30,"available":48,"booked":2,"product_id":16304},{"date":"2023-01-30T10:30","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T11:00","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T11:30","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T12:00","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T12:30","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T13:00","duration":30,"available":45,"booked":5,"product_id":16304},{"date":"2023-01-30T13:30","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T14:00","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T14:30","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T15:00","duration":30,"available":50,"booked":0,"product_id":16304},{"date":"2023-01-30T15:30","duration":30,"available":50,"booked":0,"product_id":16304}],"count":14}
But the actual response I get, which is the same as if I had is:
{"records":[{"date":"2023-02-02T09:00","duration":30,"available":1,"booked":0,"product_id":140},{"date":"2023-02-02T10:00","duration":30,"available":1,"booked":0,"product_id":140},{"date":"2023-02-02T14:00","duration":30,"available":1,"booked":0,"product_id":140},{"date":"2023-02-02T15:00","duration":30,"available":1,"booked":0,"product_id":140},{"date":"2023-02-05T17:00","duration":30,"available":1,"booked":0,"product_id":12314},{"date":"2023-02-05T17:00","duration":30,"available":1,"booked":0,"product_id":12312},{"date":"2023-02-06T10:00","duration":30,"available":1,"booked":0,"product_id":103024},{"date":"2023-02-06T10:00","duration":30,"available":1,"booked":0,"product_id":12312},{"date":"2023-02-06T11:00","duration":30,"available":1,"booked":0,"product_id":103024},{"date":"2023-02-06T11:00","duration":30,"available":1,"booked":0,"product_id":12312},{"date":"2023-02-06T12:00","duration":30,"available":1,"booked":0,"product_id":103024},{"date":"2023-02-06T12:00","duration":30,"available":1,"booked":0,"product_id":12312},{"date":"2023-02-06T13:00","duration":30,"available":1,"booked":0,"product_id":103024},{"date":"2023-02-06T13:00","duration":30,"available":1,"booked":0,"product_id":12312},{"date":"2023-02-06T14:00","duration":30,"available":1,"booked":0,"product_id":103024},{"date":"2023-02-06T14:00","duration":30,"available":1,"booked":0,"product_id":12312},{"date":"2023-02-06T15:00","duration":30,"available":1,"booked":0,"product_id":103024},{"date":"2023-02-06T15:00","duration":30,"available":1,"booked":0,"product_id":12312}],"count":18}

How do I close modals in a Slack App using Google Apps Script?

I am attempting to close a modal, I've just started working on a Slack App in Google Apps Script and have tried numerous combinations to attempt to close it.
The documentation says I can respond with
{ "response_action":"clear" }
and I tried that many times with no success.
I've sent this alongside my slack token to the views.update endpoint with no luck. I get a response back that says a view_id is required. And then when I send it with the view_id the modal still does not close.
When I send the request to the view.update url I send it as following:
var options = {
method :"post",
contentType: 'application/json',
headers:{Authorization: "Bearer " + SLACK_ACCESS_TOKEN},
status: 200,
payload:{"view_id":id, "response_action":"clear"}
}
var val = UrlFetchApp.fetch(endPoint,options)
The documentation also says I can send a HTTP 200 ok response. But I'm not sure how exactly to do that, and I haven't seen what that looks like when using Google Apps Script.
Thanks for your help!
I figured this out.
In order to return an HTTP 200 OK response I just had to do the following:
return ContentService.createTextOutput();
from the doPost() method.

How to add authentication to a Google apps script without it expiring?

I have a Google apps script project which I use as a web application to save some data gathered from a web page (JavaScript only) to My Google sheets (think of it as a simple database for just me).
It's no need to use auth for anyone else other than my account, because, I don't use their accounts/data for anything at all. I just need to use my account only, so, when I deploy it, I make it execute as me, and accessible to anyone:
.
When I click deploy after previous screen, it asks for my permission (the consent dialogue) to access my account's data, which I grant it, and after that everything is good, and the HTTP requests to this script works just fine.
.
The problem is:
This authentication expires after maybe 7 days (I'm not sure) and the script stops working, I discover that when the HTTP requests to it return error 403
To fix that and make it work again, I need to run the script again from the Google apps script editor which asks for the permissions (consent) again:
.
I can't use it like that and the web page stop working when the authentication gets revoked!
I'm not publishing the script (I don't want/don't need to). Do I?
My question is, how can I add the authentication in a way that makes it lasts and stops asking me for it over and over again?
The script on Google apps script works like this:
function doPost(request) {
return checkRequest(request);
}
function checkRequest(request) {
//check the request & save the sent data to a google sheet here;
//...
return sendResponse({
success: true,
data: {result: 'Saved the data!' }
});
}
function sendResponse(response) {
return ContentService
.createTextOutput(JSON.stringify(response))
.setMimeType(ContentService.MimeType.JSON);
}
And I call it from the web page using Ajax HTTP POST request, like this:
jQuery.ajax({
url: 'https://script.google.com/macros/s/{script-id}/exec',
method: 'POST',
dataType: "json",
data: {key: 'value'},
success: function (response) {
console.log(response);
},
error: function (response) {
console.error(response);
}
});
And this is the response the script returns after few days when the authentication expires:
This has been reported to Google
There is already a report on Google's Issue Tracker which detail the same kind of behaviour:
Random Deauthorizations for script since Editor Update with no changes to code
Google does seem to know about this issue. From the issue tracker link, a response was given:
[...] reviewing the documentation, it reads:
Authorizations by a test user will expire seven days from the time of consent.
Source
So I'm not sure we should expect anything different from these tests.
Also re-reading this thread, in your first comment you said that this was affecting projects that already published. Though I understand that you fixed the original projects that were having issues by un-linking the GCP projects.
A possible fix would be filling the scopes manually in the manifest for these types of issues. This is because Apps Script tries to infer the scopes needed at runtime, and sometimes this can result in Apps Script trying to gain more permissive scope than what is authorized.
See Setting Explicit Scopes
However, token expiry in around 7 days is to be expected for projects in "testing" state.
Which seems to be the case for the users in the report thread.
There are multiple reasons that this expiration may occur, as explained in the Using OAuth 2.0 to Access Google APIs page.
That being said, if it's causing problems you can file your own bug about it here in the Google Apps Script component.
References:
Random Deauthorizations for script since Editor Update with no changes to code
Using OAuth 2.0 to Access Google APIs | Google Identity
Setting up your OAuth consent screen - Google Cloud Platform Console Help
Authorization Scopes - Setting explicit scopes | Apps Script | Google Developers

Google Apps Script/Sheet Returns Bad Request 400

I have a fairly simple script running on a Google Sheet.
The script accepts a webhook POST and writes the content of the webhook as a new row in the spreadsheet.
The script does function as expected and it writes a new row when it receives data.
However, the app from where I'm sending the webhook receives back "Bad Request 400" error for some reason. Even though the script worked and it did what it was supposed to do.
This is a fiddle of the script I'm using.
If I call this manually, I do get a 200 response:
var response = UrlFetchApp.fetch("https://script.google.com/macros/s/AKfycbx5ubu78yi5sj2D9z-2m3Wqog604wY1ENwP3pZstnB95Mc5_N3b/exec", options);
This is a landing page with a form on it. When you submit the form, the form data gets pushed through a webhook to a Google Sheet. (Click on "Click Me To Test This Script" to see the form and if submit it, you'll see the actual sheet.)
You can solve this by using the fetch(url, params) method of the UrlFetchApp class with the muteHttpExceptions option set to true. Below is an example of this for a post request.
var options = {
'method': 'post',
'contentType': 'application/json',
'payload': JSON.stringify(ObjectToSend),
'muteHttpExceptions':true,
};
var noMancoResponse = UrlFetchApp.fetch('URL', options);
console.log("Status Code: " + String(noMancoResponse.getResponseCode()));
Hope this helps!
I had the same problem and after a lot of resending of the HTTP request, i realized it was because of Content Security Policy header. If it is set by the server and your origin doesn't satisfy its rules, then the browser will show error 400.
It would be great if there was an option in UrlFetchApp to suppress this error.
I haven't found any solution to fix it. please let me know if you do so.

Failure in Fetching Google Script's URL

URL: https://script.google.com/a/macros/MyDomain.com/s/MyProjectId/exec
To trigger the doGet() inside my Google Script, with URL:
Success: Manually Input above Url in the browser.
Failure: Fetch by Google Script UrlFetchApp.fetch(URL)
Is Fetch by program not allowed for Google Script's URL? Or have I made any stupid mistake?
I have tried to fetch by post method also to test doPost(), but got same result.
N.B.I have added my domain & script.google.com into APIs & auth/Push, and handled OAuth. No more authorization popup. So suppose not these issues.
~~~~~~~~~~~~~~~~~~~~~~~~~
Some people say this problem come from Google Apps's security in front of doGet() & doPost(). But after testing, it is failure no matter I run it on personal Gmail account or Google Apps account, and no matter run it as Developer, Domain users or Anonymous Anyone. Below is the demo code, without including the lengthy standard authorization code.
var REDIRECT_URL = 'https://script.google.com/macros/s/.../exec';
var SysEmail = "EmailAddress";
function doGet(e) {
MailApp.sendEmail(SysEmail, 'Test', 'doGet has received Push Notification');
}
//Running below function should trigger above doGet() theoretically, but not actually.
function TestGetByManual(){
var payload =
{
"kind" : "Test",
};
var options =
{
"method" : "get",
"followRedirects" : true,
"payload" : payload
};
var vResult = UrlFetchApp.fetch(REDIRECT_URL, options);
//vResult is Google's Login page.
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After latest testings, I find that Google behave like this:
Since Google's security is so strong, setting parameter "followRedirects" to true is helpless. When Fetch meet Google before doGet() or doPost(), it will be stopped due to Authority reviewing. If it is fetched by manual (e.g. users' installation through browser), an authorization window will be pop-up (esp. if doGet() include authorization code inside). But if it is fetched by another App like my case, everything seems stop and doGet() and its authorization code will never be called!
The only workaround is: If I can set the App to execute as me and accessible by Anonymous, then there will be no Authority checking and everything go directly. Unluckily, even ignoring security issue, only personal Gmail account can make this setting. I, using Google Apps account, cannot make this "Anonymous" setting.
If authorization code will be called, I think there is still hope. But if my testing result is correct and no authorization code will be called before stop, I believe it is a dead corner.
The case here is still better. If the caller is not a controllable custom function, but an uncontrollable system function like Drive.changes.watch(), everything will be further helpless. (See my another post, which is my ultimate goal: Use Google Script's Web App as Webhook to receive Push Notification directly)
If any senior here can contact Google's Arun Nagarajan, really please help get a confirmation from him.
Works fine for me with the following code:
var REDIRECT_URL = 'https://script.google.com/macros/s/AKf.../exec';
var SysEmail = "anemail";
function doGet(e) {
MailApp.sendEmail(SysEmail, 'Test', 'doGet has received Push Notification');
}
function TestGetByManual(){
var vResult = UrlFetchApp.fetch(REDIRECT_URL);
Logger.log(vResult);
}
Make sure you save a new version and re-publish the webapp with that version and using the correct published url.