I am developing a Gmail App Scripts Ad-On. I am able to create cards, and child cards, and add controls to the cards, and it works well. However, I need to add functionality to make an oData call to a web service in another application. As soon as I add the following line in my code.gs file I get the error shown below when I run my add-on in Gmail.
var req = new XMLHttpRequest();
ReferenceError: "XMLHttpRequest" is not defined. [line: 187, function: gotoChildCard, file: Code]
It never even gets beyond that line, so it is not any problem with oData structure or anything, it does not like that line. I have tried to find some posts, but I cannot find anything (I did find some people getting this error when they develop a Chrome Extension, but that is different - I am creating a Gmail App Scripts Add-On). I was thinking that maybe I need to add something to my manifest file, but if so, I do not know what it is.
below is my appscript.json file:
`{
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.readonly"
],
"gmail": {
"name": "Dean's Gmail Add-On: Multiple Cards for Set ",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/2x/bookmark_black_24dp.png",
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "createNavigationCard"
}],
"openLinkUrlPrefixes": [
"https://mail.google.com/"
],
"primaryColor": "#4285F4",
"secondaryColor": "#4285F4",
"version": "TRUSTED_TESTER_V2"
}
}
You can only make HTTP requests using the UrlFetchApp in Apps Scripts
Related
Our team is working with Autodesk Construction Cloud and it's Docs module quite heavily. Because of that we're trying to develop some internal tools which would automate some work that they do, mostly around copying files.
In ACC Docs you can easily copy files from one folder to another. From our investigation it looks like the ACC internally uses an endpoint like this to copy the files:
https://developer.api.autodesk.com/dm/v3/projects/{{projectId}}/documents:copy?targetFolder={{targetFolder}}
By using the authentication token from ACC requests (obtained from the browser), we can easily use this call from Postman or even an AWS Lambda function. But when we're using the auth tokens obtained from 3-legged auth process as described in the documentation, the same API call fails.
Is it even possible right now to obtain an auth token which works with that endpoint for copying ACC Docs files? Or is this not available right now as this API is still not really "public"?
Happy New Year!
Those APIs are internal APIs. Please do not try to use them. even if it may work, you would use it on your own risk..
Regarding with copy files files from one folder to the other, you can use the public API. After you get the version urn of the original file, call the endpoint below with the parameter
POST https://developer.api.autodesk.com/data/v1/projects/{PROJECT_ID}/items?copyFrom=urn%3Aadsk.wipprod%3Afs.file%3Avf.rH_L4XJsTmeiYA4ixCVNAA%3Fversion%3D1
with the payload, in which specify the target folder.
{
"jsonapi": {
"version": "1.0"
},
"data": {
"type": "items",
"relationships": {
"tip":{
"data":{
"type":"versions",
"id":"1"
}
},
"parent": {
"data": {
"type": "folders",
"id": "urn:adsk.wipprod:fs.folder:co.0xaYa2rVTJuFiz7rxLCOQQ"
//!<<< The folder we want to put this file
}
}
}
},
"included":[
{
"type":"versions",
"id":"1",
"attributes":{
"name":"rac_basic_sample_project.rvt" //!<<< Version name
}
}
]
}
I'm able to give writer permissions using the Google Drive API (v3). This is the endpoint:
POST https://www.googleapis.com/drive/v3/files/FILE_ID/permissions"
The API reference says that I should use sendNotificationEmail: false (note, this is a boolean not a string), which is what I'm doing (in v2 this was called sendNotificationEmails).
However, after the call I still get the email from Google saying I've been invited to edit a file. 🤔
sendNotificationEmail: false works as intended, there is one small detail:
It is a request parameter that goes outside of the resource body.
I am not sure which language your are using, if e.g. Javascript, the request would be:
gapi.client.drive.permissions.create({
"fileId": "XXX",
"sendNotificationEmail": false,
"resource": {
"role": "writer",
"type": "user",
"emailAddress": "test#gmail.com"
}
})
Question: how to remove an application logo.
Solution: previous solution from this answer, https://stackoverflow.com/a/57168008/1992004, is no longer working.
Google changed the format of "iconUrl" to "icon", and uses now the Base64-encoded data stream, like "icon":"iVBORw0KGgoAAAAN..., instead of the image URL, previously written as "iconUrl":"https://...".
I've tried "icon":"" and many Base64-encoded values like "icon":"IA", "icon":"Lw", and some of other - no success. I get console messages like
for "icon":""
{
"error": {
"code": 400,
"message": "The request failed because one of the field of the resource is invalid.",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.rpc.PreconditionFailure",
"violations": [
{
"type": "client_auth_config",
"subject": "?error_code=9&error_field_name=UpdateIconRequest.icon&error_field_value=%3CByteString#3eeee81e+size%3D0+contents%3D%22%22%3E"
}
]
}
]
}
}
or
{
"error": {
"code": 400,
"message": "Request contains an invalid argument.",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.identity.clientauthconfig.v1.ClientAuthConfigError",
"code": "ICON_STORAGE_FAILURE"
},
{
"#type": "type.googleapis.com/google.identity.clientauthconfig.v1.IconStorageError",
"reason": "INVALID_IMAGE"
}
]
}
}
or
{
"error": {
"code": 400,
"message": "Invalid value at 'icon' (TYPE_BYTES), Base64 decoding failed for \" \"",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "icon",
"description": "Invalid value at 'icon' (TYPE_BYTES), Base64 decoding failed for \" \""
}
]
}
]
}
}
Does somebody know, what should be inserted here to remove the logo image from the app?
Answer:
Unfortunately, there is no way for this to be done.
More Information:
Once an OAuth Application Logo has been uploaded there isn't a supported way of removing it - in the question that you linked the way that this was done is a bit hacky, inspecting the network requests and building a new request from the previous JSON object sent via the UI really shows this.
As the icon URL has changed to need a Base-64 encoded value this has been deprecated. Whether this was intentional by Google or not is hard to say, but now an empty value will always return INVALID_ARGUMENT. Any data in the value for icon will also just replace the image data and so this isn't a viable workaround, as as far as the validation process goes, image data exists and so will need to be verified.
If it's not too much of a arduous process, the only workaround here is to create a new GCP project with a new OAuth consent screen without uploading an image. Of course, you will need to reactivate all the relevant APIs and link the relevant scripts and projects to the new set-up.
Feature Request:
You can however let Google know that this is a feature that is important and that you would like to request they implement it. Google's Issue Tracker is a place for developers to report issues and make feature requests for their development services. I would suggest using the feature request template for G Suite Add-ons as this is a component for which GCP Projects could be used.
Update: The feature request for this is viewable here, to increase visibility on this, hit the ☆ at the top of the page.
Relevant Questions:
OAuth consent screen - ability to remove application logo [Obsolete]
May 2021 - It is still possible to completely delete the consent screen (and thus allowing to create it again). See my updated answer in https://stackoverflow.com/a/57168008/1992004
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.
I did write an chrome extension that calls this connect() function to connect to a local C++ program:
function connect() {
console.log("test1");
//port = chrome.extension.connectNative('com.a.chrome_interface');
port = chrome.runtime.connectNative('com.a.chrome_interface');
port.onMessage.addListener(onNativeMessage);
port.onDisconnect.addListener(onDisconnected);
console.log("test5");
}
I can see the test1 in the Console, but afterwards I got the error
Uncaught TypeError: undefined is not a function
in the line
port = chrome.runtime.connectNative('com.a.chrome_interface');
My extensions manifest file is here:
{
"name": "CPP_Connect",
"version": "1.0",
"description": "Send data to CPP program",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentscript.js"]
}
],
"permissions": ["contextMenus", "tabs", "nativeMessaging", "<all_urls>"],
"manifest_version": 2
}
My com.a.chrome_interface.json looks like this:
{
"name": "com.a.chrome_interface",
"description": "Chrome Native Messaging API Example Host",
"path": "com.a.chrome_interface",
"type": "stdio",
"allowed_origins": [
"chrome-extension://abc.../"
]
}
and com.a.chrome_interface is a linux executable C++ file that generates a file, if it is called and this file is never created.
I did put both files in
/etc/opt/chrome/native-messaging-hosts/
So I guess, I did register my C++ correctly but I also guess, if I would register it wrong, I should get a different error.
If I use chrome.extension.connect() the script runs trough and the error message disapear but no data arrive in my C++ program.
I did read and try to follow instructions on
https://developer.chrome.com/extensions/messaging#native-messaging
and googled a lot but I could find out the reason of my problem.
I'm using Chromium 34 on Ubuntu 12.04.
As I'm writing an extension, do I have to use chrome.runtime.connectNative() or chrome.extension.connectNative()?
How can I connect and send data to my C++ program?
connectNative() is not available in a content scripts.
To connect to a local program the content script must send the data e.g. to the background script of the extension and in the background script,
port = chrome.extension.connectNative
can be used.
So here a solution:
contentscript.js:
....
// send data to background script
chrome.extension.sendRequest("Some Data");
....
background.js:
function connect() {
// connect to local program com.a.chrome_interface
port = chrome.extension.connectNative('com.a.chrome_interface');
port.onMessage.addListener(onNativeMessage);
port.onDisconnect.addListener(onDisconnected);
}
chrome.extension.onRequest.addListener(function(data, sender) {
if (data.length > 0) {
connect();
sendNativeMessage(data);
}
});
manifest.json as above in my question but additionaly:
...
"background": {
"scripts": ["background.js"]
},
...
com.a.chrome_interface.json is unchange as in the question above.