I am attempting to build a web app that will create a Google Doc from a template and populate it with user provided data. Using Google's quickstart example in the documentation, I can successfully authorize and access the Google Drive file system. Now I need to programmatically open a template Google Doc (or even create one from scratch) and add the data.
This is rather easily done using App Script's Document Service (the DocumentAppclass). So I can do something like:
function createDoc(contentArray) {
var doc = DocumentApp.create('Sample Document');
var body = doc.getBody();
var rowsData = contentArray; // data submitted with HTML form passed as arg
body.insertParagraph(0, doc.getName())
.setHeading(DocumentApp.ParagraphHeading.HEADING1);
table = body.appendTable(rowsData);
table.getRow(0).editAsText().setBold(true);
}
in a standalone App Script and successfully create the new Google Doc on Google Drive. I can't figure out how to execute this App Script from my external web app. Is there a way to do this or do I need to find a different way to create Google Docs (and add content) using just the Drive API?
EDIT:
here is the GET request from my web app:
var gurl = "https://script.google.com/macros/s/AKfycbwMHKzfZr1X06zP2iEB4E8Vh-U1vGahaLjXZA1tk49tBNf0xk4/exec";
$.get(
gurl,
{ name: "john", time: "2pm",},
function(data) {
console.log(data);
},
"jsonp"
)
and here is my doGet():
function doGet(e) {
var result = "";
var name = e.parameter.name;
Logger.log(name);
try {
result = "Hello " + name;
} catch (f) {
result = "Error: " + f.toString();
}
result = JSON.stringify({
"result": result
});
var doc = DocumentApp.create('ballz3');
var body = doc.getBody();
var rowsData = [['Plants', 'Animals'], ['Ficus', 'Goat'], ['Basil', 'Cat'], ['Moss', 'Frog']];
body.insertParagraph(0, doc.getName())
.setHeading(DocumentApp.ParagraphHeading.HEADING1);
table = body.appendTable(rowsData);
table.getRow(0).editAsText().setBold(true);
Logger.log('DOc Name: ' + doc.getName());
return ContentService
.createTextOutput(e.parameter.callback + "(" + result + ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
In order to run a script from an external location like your web app you need to follow some set-up and use the Script API. The documentation provided below has a great example on how to run your scripts from outside.
Additionally, you can use the APIs directly, with services like OAuth to use the APIs directly in a way that can save you some time and make your code more simple. Using OAuth can provide you with simple API requests. To use it:
Go to the link provided below, select the desired scope (Drive for this example).
Exchange the authorization token for refresh/access tokens.
Proceed to configure the request. Here you can set all the parameters for the request, and even select from the existing operations the scope has available (“List possible operations” button).
The resulting request will look like the one below:
GET /drive/v3/files HTTP/1.1 Host: www.googleapis.com Content-length:
0 Authorization: Bearer [YOUR-TOKEN]
Beneath it you will see the server response to the request.
Documentation URL: https://developers.google.com/apps-script/api/how-tos/execute
OAuth Playground: https://developers.google.com/oauthplayground/
Related
I am trying to use Google Ads on Google Apps Script using this guide. I created client id and client secret on Google Console's API & Services.
Not too sure if this configuration is correct but the account is linked to Google Apps Script as I have pagespeed insights as well and there are some requests on the dashboard. I added https://www.googleapis.com/auth/drive as the scope. Again not too sure if I should add Google Ads to the scope. Lastly, got my refresh token from Google Auth playground. When I run the script above I got the following error:
Error: No access token received: {
"error": "invalid_client",
"error_description": "Unauthorized"
}
authenticate_ # test.gs:120
withRefreshToken # test.gs:144
initializeOAuthClient # test.gs:28
Honestly not too sure what I am doing wrong here so any help would be very much appreciated. Thank you.
Edit
Codes:
//From Google Console API & Services
var CLIENT_ID = '"MY_CLIENT_ID';
var CLIENT_SECRET = 'MY_CLIENT_SECRET';
//From Google Authplayground
var REFRESH_TOKEN = 'REFRESH_TOKEN';
// Enter scopes which should match scopes in File > Project properties
// For this project, e.g.: https://www.googleapis.com/auth/drive
var SCOPES = "https://www.googleapis.com/auth/adwords";
// Script ID taken from 'File > Project Properties'
var SCRIPT_ID = 'MY_SCRIPT_ID';
var authUrlFetch;
// Call this function just once, to initialize the OAuth client.
function initializeOAuthClient() {
if (typeof OAuth2 === 'undefined') {
var libUrl = 'https://developers.google.com/google-ads/scripts/docs/examples/oauth20-library';
throw Error('OAuth2 library not found. Please take a copy of the OAuth2 ' +
'library from ' + libUrl + ' and append to the bottom of this script.');
}
var tokenUrl = 'https://accounts.google.com/o/oauth2/token';
authUrlFetch = OAuth2.withRefreshToken(tokenUrl, CLIENT_ID, CLIENT_SECRET,
REFRESH_TOKEN, SCOPES);
}
/**
* Execute a remote function.
* #param {string} remoteFunctionName The name of the function to execute.
* #param {Object[]} functionParams An array of JSON objects to pass to the
* remote function.
* #return {?Object} The return value from the function.
*/
function executeRemoteFunction(remoteFunctionName, functionParams) {
var apiParams = {
'function': remoteFunctionName,
'parameters': functionParams
};
var httpOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
payload: JSON.stringify(apiParams)
};
var url = 'https://script.googleapis.com/v1/scripts/' + SCRIPT_ID + ':run';
var response = authUrlFetch.fetch(url, httpOptions);
var data = JSON.parse(response.getContentText());
// Retrieve the value that has been returned from the execution.
if (data.error) {
throw Error('There was an error: ' + response.getContentText());
}
return data.response.result;
}
// Paste in OAuth2 library here, from:
// https://developers.google.com/google-ads/scripts/docs/examples/oauth20-library
I have pasted the oauth2.0 library under the codes above.
Edit 2
I fixed the part of function initializeOAuthClient. It now shows execution complete, but when I try to run function executeRemoteFunction I am getting TypeError: Cannot read property 'fetch' of undefined. I am guessing I have to input remoteFunctionName and functionParams but where do I find them?
Simply trying to convert doc to docx. Success with my google account is saved authorization header are there on browser. However since apps script can't reach this info, we are not squared.
var options = {
muteHttpExceptions: false,
}
};
var blb = UrlFetchApp.fetch('https://docs.google.com/feeds/download/documents/export/Export?id=' + docurl + '&exportFormat=docx',
options).getBlob();
There should be new and easy way, if I mutehttperrors, I can create one docx with the error sayıng the file can't be found. The file is there but there is this error:
Error
Exception: Request failed for https://docs.google.com returned code 404.
If you are the owner of the Google Document and/or you have the permission for reading or writing to the Google Document, you can use the following script.
Modified script:
var docurl = "###"; // Please set the Document ID which is not URL.
var options = {
muteHttpExceptions: true,
headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() }
};
var blb = UrlFetchApp.fetch('https://docs.google.com/feeds/download/documents/export/Export?id=' + docurl + '&exportFormat=docx', options).getBlob();
DriveApp.createFile(blb); // When you use this script, the converted DOCX is created as a file on the root folder of your Google Drive.
In this case, you can use the access token by ScriptApp.getOAuthToken(). When DriveApp is used in your script, I think that the scope can be used for using the endpoint.
Note:
From the error message of Request failed for https://docs.google.com returned code 404., in your script, I'm worry that docurl might be not the Google Document ID. In that case, an error occurs. In that case, please use the Google Document ID. It's like ### of https://docs.google.com/document/d/###/edit.
Reference:
fetch(url, params)
I am trying to use the new Google Docs API using Google Apps Script. Since new API is not yet available as an extended service, I am trying to do it using UrlFetchApp() but failing.
Apologies for my naive attempt here:
function apiCall(){
var API_KEY = 'YOUR_API_KEY';
var username = 'YOUR_USERNAME';
var password = 'YOU_PASSWORD';
var DOC_ID = 'YOUR_DOC_ID';
var root = 'https://docs.googleapis.com/v1/documents/';
var endpoint = DOC_ID;
var query = '?key=' + API_KEY;
var params = {
'method': 'GET',
'muteHttpExceptions': true,
'headers': {
'Authorization': 'Basic ' + Utilities.base64Encode(username + ':' + password)
}
};
var response = UrlFetchApp.fetch(root + endpoint + query, params);
var data = response.getContentText();
var json = JSON.parse(data);
Logger.log(json);
}
I get the following response:
{error={code=401, message=Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or another valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project., status=UNAUTHENTICATED}}
Can someone point to the right direction, where I can find some documentation how to use Google Docs API in Google Apps Script.
If you own the document then you don't need to leverage an API key. Also, instead of using Basic authentication you can leverage the built-in Bearer OAuth token as follows:
/**
* Get `Document` resource object from Google Docs REST API.
*
* #param {String} docId - A Google Document Id
*
* #return {Document} A Document resource object.
*/
function getDocumentResouce(docId) {
return JSON.parse(UrlFetchApp.fetch(
"https://docs.googleapis.com/v1/documents/" + docId,
{
"headers": {
"Authorization":"Bearer " + ScriptApp.getOAuthToken()
}
}
)
);
}
Note: GET is the default HTTP request method used by UrlFetchApp.fetch() so you don't need to define it in the options object.
ADDENDUM
As Tanaike stated in the comments you'll need to manually add the relevant scopes (in addition to the ones you already have enabled) to your manifest JSON.
First check your project properties to get the list of existing scopes via the menu
File > Project Properties > Scopes. You need to add those scopes, as well as one of the relevant document scopes (listed in the documentation) to your manifest.
The following links provide the information you'll need to manage your manifest and scopes:
https://developers.google.com/apps-script/concepts/manifests
https://developers.google.com/apps-script/concepts/scopes
I have two scripts in different domains, I would like to fetch data from one to the other.
I've found Script Execution API to be useful, however they don't provide any example GAS to GAS. https://developers.google.com/apps-script/guides/rest/quickstart/target-script
Any suggestion/guidance on how to accomplish such thing?
This answer might be helpful. what i have done is that created a cloud platform project and created two standalone project, one contains the calling script and other has the method you want to call and must be deployed as a API Executable.
IMPORTANT: Both these project must be associated with the Cloud Platform Project.
For associating the projects to Cloud Platform's project , in script editor do this, Resources --> Cloud Platform Project. You'll see the dialog box which has input box having placeholder Enter Project Number Here, here put the Cloud Platform's project number and do the same for other project as well.
Now as i have followed google's quick start tutorial for EXECUTION API. i have a target script (Script containing your method) as follows:
/**
* The function in this script will be called by the Apps Script Execution API.
*/
/**
* Return the set of folder names contained in the user's root folder as an
* object (with folder IDs as keys).
* #return {Object} A set of folder names keyed by folder ID.
*/
function getFoldersUnderRoot() {
var root = DriveApp.getRootFolder();
var folders = root.getFolders();
var folderSet = {};
while (folders.hasNext()) {
var folder = folders.next();
folderSet[folder.getId()] = folder.getName();
}
return folderSet;
}
NOTE: This script must be deployed as a API Executable. In project you must have enabled the Google Execution API.
Now the calling script,
function makeRequest(){
// DriveApp.getRootFolder();
var access_token=ScriptApp.getOAuthToken();
var url = "https://script.googleapis.com/v1/scripts/{YourScriptId}:run";
var headers = {
"Authorization": "Bearer "+access_token,
"Content-Type": "application/json"
};
var payload = {
'function': 'getFoldersUnderRoot',
devMode: true
};
var res = UrlFetchApp.fetch(url, {
method: "post",
headers: headers,
payload: JSON.stringify(payload),
muteHttpExceptions: true
});
Logger.log(res);
}
On first line inside function you can see i have commented out the Drive API call. I did that because the calling script and target script must have the same scope.
Yes you can use Execution API to fetch data from one domain to other. From the documentation, it consists of a single scripts resource, which has a single method, run, that makes calls to specific Apps Script functions.
The run method must be given the following information when called:
The ID of the script being called.
The name of the function within the script to execute.
The list of parameters the function requires (if any).
Here is an example from GitHub:
var script = google.script('v1');
var key = require('./creds.json');
var jwtClient = new google.auth.JWT(key.client_email, null, key.private_key, ['https://www.googleapis.com/auth/spreadsheets'], null);
jwtClient.authorize(function(err, tokens) {
if (err) {
console.log(err);
return;
}
script.scripts.run({
auth: jwtClient,
scriptId: 'script-id',
resource: {
function: 'test',
parameters: []
}
}, function(err, response) {
if (err) {
throw err;
}
console.log('response', response);
})
});
You can also check this related SO question on how to use Google Apps Script Execution API and Javascript to make the interaction between website and Google Drive.
I am fairly new to Google Apps Script. I am using Google's functionality to access the DFA/DCM Trafficking and Reporting API through App Scripts without having to use OAuth.
When I run the DCM Report to then convert into google sheets, I am not able to figure out how to use either urls i'm supplied with to download the CSV.
Here is the code i'm using.
var file = DCM.Reports.run(profile.profileId,30792432);
var file2 = DCM.Files.get(30792432, file.id);
//wait till running of the report is complete.
file2 = DCM.Files.get(30792432, file.id);
var response = UrlFetchApp.fetch(file2.urls.browserUrl);
I also try using:
file2.urls.apiUrl();
for the UrlFetchApp service, but that didn't work either.
Any help on how to execute the url to download the file as an object where I can paste into google sheets would be greatly appreciated.
Add the ScriptApp authorization bearer as a header in the parameters while using the apiurl call. Something like:
var token = ScriptApp.getOAuthToken();
var headersOptions = {
Authorization : 'Bearer ' + token
};
var options = {
headers : headersOptions
};
var csvDoc = UrlFetchApp.fetch(file2.url.apiurl, options);