Unable to use DriveApp.getFileById(id).getLastUpdated(); - google-apps-script

I want to know the latest updated date of a file at location "https://drive.google.com/open?id=XYZ". This address is present in A1 cell of my sheet.
But every time it throws this error: "You do not have permission to call DriveApp.getFileById. Required permissions: (https://www.googleapis.com/auth/drive.readonly || https://www.googleapis.com/auth/drive) (line 3)."
I have made changes in appScript.jason file also.
This is the code that I have written in script editor and call from B1 cell:
function useThis(url){
var id = getIdFromUrl(url);
return DriveApp.getFileById(id).getLastUpdated();
}
function getIdFromUrl(url) {
return url.match(/[-\w]{25,}/);
}
Also, my appscript.json looks like this:
{
"timeZone": "Asia/Kolkata",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/userinfo.email"
]
}
Is there a way I can resolve this. Or any other easier way to get last updated date of a file in google drive?

Custom functions are not applicable to your use case as they can only call services that do not require access to personal data. See GAS documentation on custom functions
If your custom function throws the error message You do not have
permission to call X service., the service requires user authorization
and thus cannot be used in a custom function.

Related

GoogleJsonResponseException: Field name is required

I'm working with the Google Analytics API for the first time and I'm trying to create a new property. I wrote a JS function in Google App Script:
function insertProperty() {
var resource =
{
// "accountId": "177832616",
"resource":{
"name": "Test Property 7",
// "dataRetentionResetOnNewActivity": false,
"websiteUrl": "https://www.test.com"
}
}
var accountID = '177832616';
var request = Analytics.Management.Webproperties.insert(resource, accountID);
// request.execute(function (response) { console.log(property.id) });
}
This is the error the API throws:
GoogleJsonResponseException: API call to analytics.management.webproperties.insert failed with error: Field name is required. (line 56, file "Code")
The insert() method seems to take two parameters: insert(Webproperty resource, string accountId);
Since it's not recognizing the name key/value I added to resource, my guess is I haven't declared the variable as a Webproperty type and I'm not sure how to do this. I assumed Webproperty was a { } variable type, but at this point I'm not sure what to try next. Doing research online, I'm not able to find anything regarding the API's Webproperty so any context/info is helpful.
From your question, I could understand that Google Analytics API is used with Advanced Google services of Google Apps Script. In this case, resource of Analytics.Management.Webproperties.insert(resource, accountId) can directly use the request body of the method of "Web Properties: insert". I think that the reason of your error is due to this. So please modify as follows and test it again.
From:
var resource =
{
// "accountId": "177832616",
"resource":{
"name": "Test Property 7",
// "dataRetentionResetOnNewActivity": false,
"websiteUrl": "https://www.test.com"
}
}
To:
var resource = {
"name": "Test Property 7",
"websiteUrl": "https://www.test.com"
}
Note:
When accountId is not correct, an error occurs. Please be careful this.
From iansedano's comment, in this case, request of var request = Analytics.Management.Webproperties.insert(resource, accountID); directly returns the values. So you can see the value like console.log(request) and console.log(request.toString()).
Reference:
Web Properties: insert

Error while creating document in Firestore through Google App Script

I am following grahamearley's guide in setting up my google sheets to read/write to my firestore database. I have created a service account assigned to be the project owner, generated the key and stored the relevant strings in my testFunction as follows:
function testFunction() {
var key = "-----BEGIN PRIVATE KEY-----\n<my private key>\n-----END PRIVATE KEY-----\n";
var email = "xxxx#<projectId>.iam.gserviceaccount.com";
var projectId = "<projectId>";
var firestore = FirestoreApp.getFirestore(email, key, projectId);
const data = {
"name": "test!!"
};
firestore.createDocument("FirstCollection", data);
}
However, when I try running this, I obtain the following error:
Error: Missing or insufficient permissions. (line 33, file "Util",
project "FirestoreApp")
I tried setting my database rule as follows, but the function still returned an error:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if false;
}
}
}
I have searched here for questions with the same error of insufficient permissions, but have not found anything that helps me.
Could someone please advise me on finding out the issue and fixing it? Thank you.
Manifest:
{
"timeZone": "Europe/Paris",
"dependencies": {
"libraries": [{
"userSymbol": "FirestoreApp",
"libraryId": "1VUSl4b1r1eoNcRWotZM3e87ygkxvXltOgyDZhixqncz9lQ3MjfT1iKFw",
"version": "22"
}]
},
"exceptionLogging": "STACKDRIVER"
}
EDIT1: Added manifest file
EDIT2: Added db rules
I have since resolved this issue. It seems the problem was with my service account. I had selected Project>Owner instead of Datastore>Cloud Datastore Owner.
Thank you for all your help rendered.

Using custom libraries from apps script in App Maker: Authorization problem

I am using this code in Apps script
function getUserObjByEmail(email){
// Same as using AdminDirectory class.
var apiUrl = "https://www.googleapis.com/admin/directory/v1/users/"+email+"?fields=id";
var token = ScriptApp.getOAuthToken();
var header = {"Authorization":"Bearer " + token};
var options = {
"method": "GET",
"headers": header
};
var response = JSON.parse(UrlFetchApp.fetch(apiUrl, options));
return response;
}
which I run as a function from App Maker project. Things go smoothly when I use the app since I have an admin role( I guess, not sure ) but the problem arises when other normal users in our domain start using the deployed app maker app. I checked the server logs and its full of this message:
Exception: Request failed for
https://www.googleapis.com/admin/directory/v1/users/email#domain.com?fields=id
returned code 403.
Truncated server response: { "error": { "errors": [ { "domain": "global",
"reason": "forbidden", "message": "Not Authorized to access this
resource/api" ... (use muteHttpExceptions option to examine full response)
Any idea how to fix this? I have manually added the required scopes for the apps script library, I added the following:
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/admin.directory.user"
The reason this happens is because YOU have admin rights, otherwise you'd be getting the same error message. The other users don't have admin rights hence they get the error. To solve this problem, you can either deploy the application running it as the developer or you can use a service account to impersonate an admin and do the process.
Regarding the first approach, you can find more info here https://developers.google.com/appmaker/security/identity.
Regarding the second approach, you can use the following app script library https://github.com/gsuitedevs/apps-script-oauth2#using-service-accounts
Moreover, if you do not require to get custom schemas information, then you can simply use a directory model and that should work for all users. Check the reference here: https://developers.google.com/appmaker/models/directory

Gmail.Users.Threads.get with metadata scope and metadata format not working

I'm sure it's a pretty stupid thing I'm missing, but I can't quite see it.
My Google Apps Script only needs mail headers so it has a very restrictive scope:
"https://www.googleapis.com/auth/gmail.metadata". I really don't want to change this scope because it provides just what I need.
Because of this restrictive scope, many API calls will force you to select the METADATA format instead of the default FULL. This can be checked using the API explorer (https://developers.google.com/gmail/api/v1/reference/users/threads/get). Remember to Select JUST the metadata scope, otherwise it will work! :-)
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Metadata scope doesn't allow format FULL"
}
],
"code": 403,
"message": "Metadata scope doesn't allow format FULL"
}
}
Then, from the format drop down menu select "metadata" run again and it will work.
This simple code is enough to replicate the issue:
Code.gs
function getThread() {
// get the most recent thread
var thread = Gmail.Users.Threads.list('me', {maxResults: 1});
Logger.log('thread: %s', thread);
thread = JSON.parse(thread);
var threadId = thread.threads[0].id;
Logger.log('threadId: %s', threadId);
// scope "https://www.googleapis.com/auth/gmail.metadata"
// requires me to specify the metadata format
// next line will error: Metadata scope doesn't allow format FULL
var thread = Gmail.Users.Threads.get('me', {id: threadId, format: 'metadata'});
}
appsscript.json (View -> Show manifest file)
{
"oauthScopes": ["https://www.googleapis.com/auth/gmail.metadata"],
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "Gmail",
"serviceId": "gmail",
"version": "v1"
}]
},
"exceptionLogging": "STACKDRIVER"
}
When getThread() function is run, it produces this error:
Metadata scope doesn't allow format FULL (line 10, file "Code.gs")
Gmail API, in particular Gmail.Users.Thread.get documentation (https://developers.google.com/gmail/api/v1/reference/users/threads/get) states:
Optional query parameters
format string The format to return the messages in.
Acceptable values are:
"full": Returns the parsed email message content in the payload field and the raw field is not used. (default)
"metadata": Returns email headers with message metadata such as identifiers and labels.
"minimal": Only returns email message metadata such as identifiers and labels, it does not return the email headers, body, or payload.
So it's not clear to me how this API call should be written:
var thread = Gmail.Users.Threads.get('me', {id: threadId, format: 'metadata'});
I've tried all the possible combinations of quotes (single, double and no quotes) with case (upper and lower) and nothing has worked. It seems it's being ignored... :-(
I'm stumped... please help! :-)
Thanks!!
Per the API documentation, Gmail.Users.Threads.get() requires 2 parameters, and has an optional 2 (the format object you indicate):
Path parameters
id string The ID of the thread to retrieve.
userId string The user's email address. The special value me can be used to indicate the authenticated user.
Optional query parameters
format string The format to return the messages in. Acceptable values are:
- "full": Returns the parsed email message content in the payload field and the raw field is not used. (default)
- "metadata": Returns email headers with message metadata such as identifiers and labels.
- "minimal": Only returns email message metadata such as identifiers and labels, it does not return the email headers, body, or payload.
metadataHeaders[] list When given and format is METADATA, only include headers specified.
In Apps Script's Advanced Services Gmail client library, 2 signatures are provided:
The parameters here should be:
userId - the user to request (for your case, "me")
id - the ID of the thread you want to obtain
your optional parameter object

Access back-end messages (not the one from the event)

I once wrote a GMail widget that I had to convert to a Chrome add-on when Google removed support for the GMail widget, and I'm now trying to convert it to a Gmail Add-on.
My issue is that my add-on loops thru a series of messages under a label and tries to reply to them, but I get a error trying to access them:
ERROR: Exception: Access denied: : Missing access token for per message scope authorization.
So my question is, how can I access those messages? The only access token I have is the one that triggered the add-on and that won't do.
Or asking from a different point of view, are there more events that can trigger the add-on besides opening a message?
Thanks in advance.
BTW, the Chrome extension that I'm trying to convert to a Gmail add-on can be seen here:
https://sites.google.com/site/replytomany/home
https://chrome.google.com/webstore/detail/reply-to-many/gpmpcjkhfjflmjpjjmdegpkgginijbin?hl=en
[EDIT] This is what my appsscript.json looks like:
{
"timeZone": "Europe/Dublin",
"dependencies": {
"enabledAdvancedServices": [{
"userSymbol": "Gmail",
"serviceId": "gmail",
"version": "v1"
}]
},
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.send"
],
"gmail": {
"name": "Reply To Many",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "getContextualAddOn"
}],
"primaryColor": "#41f470",
"secondaryColor": "#94f441",
"version": "TRUSTED_TESTER_V2"
}
}
Did you try implementing the access token? I meant for you to try that first as I believe that's your issue if you don't have it.
// Activate temporary Gmail access token. Where 'e' is the function argument
var accessToken = e.messageMetadata.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
if you have "mail.google.com/", then you don't have a scope problem, as that gives you full access. The only other security issue is the access token.
Try this scope:
https://www.googleapis.com/auth/userinfo.email
I believe that's the one you need if you're calling the Session object, 'getActiveUser'
if this doesn't fix your issue, I don't believe I can help without seeing what you're trying to do.