how to make a correct HTTP request to BigQuery from google script - google-apps-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.

Related

Why am I getting an empty response back from UrlFetchApp in Google Apps Script?

I am trying to make a GET request to an external API from a Google Apps Script using UrlFetchApp. When I make this request with Postman or curl, I get back the expected response. However, when I try it with UrlFetchApp, I get back an empty response, {}.
I have tried using Basic Auth and OAuth 2, as well as explicitly setting the oauthScopes property in the manifest as described here.
I have confirmed with the API team that they are indeed sending back a full response when I hit the endpoint, but all I receive is {}. My problem seems similar to this StackOverflow question which went unanswered.
var headers = {
"X-Client-Key": "KEY",
"Authorization": "Bearer TOKEN"
};
var options = {
method: "get",
headers: headers,
}
var response = UrlFetchApp.fetch(ENDPOINT, options);
console.log(JSON.stringify(response)); // returns {}
Do not take what you see in logs at face value. fetch method of the UrlFetchApp service always returns an instance of HTTPResponse which is an object first and foremost. This is what the logs show you (I am assuming you are logging the response because this is the only context I am aware of where {} is displayed).
To extract useful information from the response, use the appropriate methods exposed on HTTPResponse instances, like getResponseCode or getContentText.

Google Chat API: Delete message from bot gives a 401 error

This is the code i'm trying to use for deleting messages posted through a webhook to a chat room (I grab messageID when I create It with this same request but POST method and sending message in a text variable):
var WEBHOOK_URL2 = "https://chat.googleapis.com/v1/spaces/<SPACE>/messages/<MESSAGEID>.<MESSAGEID>?key=<KEY>&token=<TOKEN>%3D";
var options = {
'method' : 'delete',
'muteHttpExceptions' : true,
};
var response = UrlFetchApp.fetch(WEBHOOK_URL2, options);
Logger.log (response);
The response is
"error": {
"code": 401,
"message": "Request is missing required authentication credential. 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"
}
As commented, creating messages works with those credentials (key / token from chatroom webhook) Maybe i am missing something from the documentation at https://developers.google.com/hangouts/chat/reference/rest/v1/spaces.messages/delete ?
Thank you in advance,
That error usually occurs because the request lacks the access token or that access token wasn't validated. I assume that you are using Apps Script for this task. If my assumption is true, you'll find this reference useful; it summarizes the OAuth 2.0 protocol for Google APIs using JavaScript. For obtaining access tokens, please refer to this other guide for a complete step-by-step. Please, follow those steps on your request routine and don't hesitate to write back if you need more help.

"Insufficient Permission" when trying to authenticate to cloud-storage via apps-script

I am about to give up on this as I can't find out what I am doing wrong.
I have a cloud-storage bucket with our companies billing data (json file objects written by google) that I am supposed to process into spreadsheets.
As there is no apps script API for oauth2, I am using the custom OAuth2 library provided by google with the key "1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF", and have setup the auth request as shown in this example for service accounts:https://github.com/googlesamples/apps-script-oauth2/blob/master/samples/GoogleServiceAccount.gs
The token is being created and put into the scripts property store, where I can view it. So far so good.
I have this code for requesting the token and then I am trying to list the contents of the bucket in the function "getFilesList()":
function getService() {
return OAuth2.createService('CloudStoreGrab-Service')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(creds_private_key)
.setIssuer(creds_client_email)
.setSubject(creds_user_email)
.setPropertyStore(PropertiesService.getScriptProperties())
.setScope(['https://www.googleapis.com/auth/drive','https://www.googleapis.com/auth/script.external_request','https://www.googleapis.com/auth/script.storage','https://www.googleapis.com/auth/spreadsheets']);
}
function getFilesList() {
var service = getService();
service.reset();
if (service.hasAccess()) {
var url = 'https://www.googleapis.com/storage/v1/b/'+bucket+'/o';
var response = UrlFetchApp.fetch(url, {
method: "GET",
muteHttpExceptions: true,
headers: {
Authorization: 'Bearer ' + service.getAccessToken()
}
});
}
Logger.log("Response:", response.getContentText())
}
No matter what I seem to try, the request always returns "403 Insufficient Permission". The service account has all necessary roles and permissions activated though (DwD, Storage-Administrator, Project-Owner). When I authenticate with the same credentials from gcloud and then browse the bucket with gsutils I can see the listing. This leads me to believe, that I am still requesting the auth token incorrectly. I tried using the generated token with curl and am getting the same Insufficient Permission response.
What am I doing wrong, while requesting the token?
Are the requested scopes too narrow?
Are the requested scopes too narrow?
That they are. You can find the OAuth scopes for Google's Cloud Storage API listed below (you won't need to use all of them, pick the ones best suited to your use-case, the 1st and 5th scopes in the list should be sufficient):
https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/cloud-platform.read-only
https://www.googleapis.com/auth/devstorage.full_control
https://www.googleapis.com/auth/devstorage.read_only
https://www.googleapis.com/auth/devstorage.read_write
In future, you can find the required OAuth scopes for any Google API you need at the following link:
https://developers.google.com/identity/protocols/googlescopes

How to turn on email forwarding with Google Script

A brief description of the project: I am looking to toggle the email forwarding option in the settings of one of my gmail accounts through a google script. This will be a function I would like to call every night between certain hours forwarding my mail from main_email#gmail to secondary_email#gmail.
I am having a difficult time finding the easiest way to toggle this through a google script. The simplest solution seems to be described here where they use an HTTP request. However in all honesty I don't completely understand how it all works, much less if it is the simplest way.
https://developers.google.com/gmail/api/v1/reference/users/settings/updateAutoForwarding
The code that I try and run on the gmail account to enable/disable email forwarding is the following:
function updateForwarding() {
var userID = "main_email#gmail.com"
var response = UrlFetchApp.fetch("https://www.googleapis.com/gmail/v1/users/" + userID + "/settings/autoForwarding", {
method: 'put',
enabled: true,
emailAddress: "secondary_email#gmail.com",
disposition: "leaveInInbox"
});
Logger.log(response.getContentText());
}
However I get the following error:
Request failed for
https://www.googleapis.com/gmail/v1/users/main_email#gmail.com/settings/autoForwarding
returned code 401. Truncated server response: { "error": { "errors": [
{ "domain": "global", "reason": "required", "message": "Login
Required", "locationType": "header", ... (use muteHttpExceptions
option to examine full response) (line 4, file "Code")
I recognize this is shows I need to provide credentials for making the request, but I don't understand how I would do that. I read on the tutorial (https://developers.google.com/gmail/api/auth/about-auth) I need to authorize my app with gmail and get an API key, so I have gone to the google developers console to create that. However, I have no idea how to authenticate or make the call through a Google script after a few hours of google.
Here are the key and secret I was given:
Is this the easiest solution to toggle gmail forwarding? If so, how do I authenticate my call? If not, what is the easiest solution to being able to toggle my gmail forwarding off/on?
You need to pass oAuth token in header information
function updateForwarding() {
var userID = "main_email#gmail.com";
var header = {
Authorization: 'Bearer ' + ScriptApp.getOAuthToken(),
}
var response = UrlFetchApp.fetch("https://www.googleapis.com/gmail/v1/users/" + userID + "/settings/autoForwarding", {
method: 'put',
enabled: true,
headers: header,
emailAddress: "secondary_email#gmail.com",
disposition: "leaveInInbox"
});
Logger.log(response.getContentText());
}
As noted in the authorization section of https://developers.google.com/gmail/api/v1/reference/users/settings/updateAutoForwarding, you need to use OAuth with the given scopes to make that call, not just an API key. You appear to have a client id, but you need to plug this into a library to handle the OAuth process for you. The OAuth process will then give you a Bearer token to add to your request (although most OAuth libraries will handle this for you).
It looks like https://github.com/googlesamples/apps-script-oauth2 is the current recommened way to do this if you're using UrlFetchApp (based on https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app).

StackExchange API authentication in Google Apps Script

I'm trying to use the V2.2 of StackExchange API in Google Apps Script.
The problem comes in the last step of the explicit OAuth 2.0 flow, when I try to send the POST request to obtain the access token. I receive a 404 error, but making the same request manually (using postman extension) everything is ok.
To simplify the problem, if I send this POST request with no payload I receive the same 404
var response = UrlFetchApp.fetch("https://stackexchange.com/oauth/access_token", {
method: 'post',
muteHttpExceptions: true
});
Logger.log(response);
while in postman I receive this 400:
{
"error": {
"type": "invalid_request",
"message": "client_id not provided"
}
}
I guess this will be a problem with UrlFetchApp, but does anyone know how to solve it? Thanks!
The problem is related with the Origin header.
You cannot remove from the header directly but you can perform the call via a proxy :)
You need to provide the data for the post by adding an 'option' object to the call.
For example:
var options = { "method" : "post", "payload" : payload };
UrlFetchApp.fetch("https://stackexchange.com/oauth/access_token", options);
Btw, have you tried you use the OAuth that UrlFetch got: https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#addOAuthService(String) - It might be better way.