Check if user is running

I run a Google Apps script that uploads a file to a Google Drive file:

function doGet(e) { var blob = UrlFetchApp.fetch(e.parameters.url).getBlob(); DriveApp.createFile(blob); return HtmlService.createHtmlOutput("DONE!"); } 

My site loads a pop-up window in which the Google Apps script runs with this code. It works great.

Now, how do I get back to my site so that it successfully uploads the file? Like in, how can I contact the server on which the user executed doGet() ? `

Should there be some type of response processing?

Full working code (check it on JSBin ):

 <!DOCTYPE html> <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.js"></script> </head> <body> <div class="google-upload" data-url="https://calibre-ebook.com/downloads/demos/demo.docx"> <span style="background-color: #ddd">Upload</span> </div> <script> $(function() { $(".google-upload").click(function() { var url = "https://script.google.com/macros/s/AKfycbwsuIcO5R86Xgv4E1k1ZtgtfKaENaKq2ZfsLGWZ4aqR0d9WBYc/exec"; // Please input the URL here. var withQuery = url + "?url="; window.open(withQuery + $('.google-upload').attr("data-url"), "_blank", "width=600,height=600,scrollbars=1"); }); }); </script> </body> </html> 

So, to clarify, I want to find out if the user really uploaded the file successfully. Sort of:

 request.execute(function(response) { if (response.code == 'uploaded') { // uploaded, do stuff } else { // you get the idea... } }); 

Adding generosity to completely solve this problem.

+5
source share
1 answer

Instead of returning an HtmlService object, you can pass data using the jQuery $.getJSON method and retrieve data from the doGet function using ContentService . Google Apps Script does not accept CORS, so using JSONP is the best way to get data from your script as well. See this post for more details .

CodePen working example

I share your HTML and scripts for clarity. None of the HTML has changed from your original example.

Code.gs

 function doGet(e) { var returnValue; // Set the callback param. See https://stackoverflow.com/questions/29525860/ var callback = e.parameter.callback; // Get the file and create it in Drive try { var blob = UrlFetchApp.fetch(e.parameters.url).getBlob(); DriveApp.createFile(blob); // If successful, return okay // Structure this JSON however you want. Parsing happens on the client side. returnValue = {status: 'okay'}; } catch(e) { Logger.log(e); // If a failure, return error message to the client returnValue = {status: e.message} } // Returning as JSONP allows for crossorigin requests return ContentService.createTextOutput(callback +'(' + JSON.stringify(returnValue) + ')').setMimeType(ContentService.MimeType.JAVASCRIPT); } 

JS Client

 $(function() { $(".google-upload").click(function() { var appUrl = "https://script.google.com/macros/s/AKfycbyUvgKdhubzlpYmO3Marv7iFOZwJNJZaZrFTXCksxtl2kqW7vg/exec"; var query = appUrl + "?url="; var popupUrl = query + $('.google-upload').attr("data-url") + "&callback=?"; console.log(popupUrl) // Open this to start authentication. // If already authenticated, the window will close on its own. var popup = window.open(popupUrl, "_blank", "width=600,height=600,scrollbars=1"); $.getJSON(popupUrl, function(returnValue) { // Log the value from the script console.log(returnValue.status); if(returnValue.status == "okay") { // Do stuff, like notify the user, close the window popup.close(); $("#result").html("Document successfully uploaded"); } else { $("#result").html(returnValue); } }) }); }); 

You can check the error message by passing an empty string in the data-url parameter. The message is returned to the console as well as to the page for the user.

Change 3.7.18

The above solution has problems with authorization flow control. After researching and discussing with the drive engineer ( see the section here ), I reworked this in a self-serving example based on the Script API Application and launching the project as an API executable, not a Script application for web applications. This will allow you to access the [run](https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run) method [run](https://developers.google.com/apps-script/api/reference/rest/v1/scripts/run) outside the Apps Script web application.

Customization

Follow the instructions in the Google Apps Script for JavaScript . The Apps Script project must be self-contained (not document-related) and published as an executable API. You will need to open the Cloud Console and create the OAuth credentials and API key.

The instructions use the Python server on your computer. I use Node JS server, http-server , but you can also put it online and check from there. You will need to whiten your source to the cloud console.

Customer

Since this is self-service, you will need a simple HTML page that authorizes the user through the OAuth2 API via JavaScript. This is preferable because it supports user login, allowing you to use multiple API calls for your Script without re-authorization. The code below works for this application and uses the authorization flow from the Google Quick Launch guides.

index.html

  <body> <!--Add buttons to initiate auth sequence and sign out--> <button id="authorize-button" style="display: none;">Authorize</button> <button id="signout-button" style="display: none;">Sign Out</button> <button onclick="uploadDoc()" style="margin: 10px;" id="google-upload" data-url="https://calibre-ebook.com/downloads/demos/demo.docx">Upload doc</button> <pre id="content"></pre> </body> 

index.js

 // Client ID and API key from the Developer Console var CLIENT_ID = 'YOUR_CLIENT_ID'; var API_KEY = 'YOUR_API_KEY'; var SCRIPT_ID = 'YOUR_SCRIPT_ID'; // Array of API discovery doc URLs for APIs used by the quickstart var DISCOVERY_DOCS = ["https://script.googleapis.com/$discovery/rest?version=v1"]; // Authorization scopes required by the API; multiple scopes can be // included, separated by spaces. var SCOPES = 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/script.external_request'; var authorizeButton = document.getElementById('authorize-button'); var signoutButton = document.getElementById('signout-button'); var uploadButton = document.getElementById('google-upload'); var docUrl = uploadButton.getAttribute('data-url').value; // Set the global variable for user authentication var isAuth = false; /** * On load, called to load the auth2 library and API client library. */ function handleClientLoad() { gapi.load('client:auth2', initClient); } /** * Initializes the API client library and sets up sign-in state * listeners. */ function initClient() { gapi.client.init({ apiKey: API_KEY, clientId: CLIENT_ID, discoveryDocs: DISCOVERY_DOCS, scope: SCOPES }).then(function () { // Listen for sign-in state changes. gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus); // Handle the initial sign-in state. updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get()); authorizeButton.onclick = handleAuthClick; signoutButton.onclick = handleSignoutClick; // uploadButton.onclick = uploadDoc; }); } /** * Called when the Upload button is clicked. Reset the * global variable to `true` and upload the document. * Thanks to @JackBrown for the logic. */ function updateSigninStatus(isSignedIn) { if (isSignedIn && !isAuth) { authorizeButton.style.display = 'none'; signoutButton.style.display = 'block'; uploadButton.style.display = 'block' uploadButton.onclick = uploadDoc; } else if (isSignedIn && isAuth) { authorizeButton.style.display = 'none'; signoutButton.style.display = 'block'; uploadButton.style.display = 'block'; uploadDoc(); } else { authorizeButton.style.display = 'block'; signoutButton.style.display = 'none'; uploadButton.style.display = 'none'; isAuth = false; } } /** * Sign in the user upon button click. */ function handleAuthClick(event) { gapi.auth2.getAuthInstance().signIn(); isAuth = true; // Update the global variable } /** * Sign out the user upon button click. */ function handleSignoutClick(event) { gapi.auth2.getAuthInstance().signOut(); isAuth = false; // update the global variable } /** * Append a pre element to the body containing the given message * as its text node. Used to display the results of the API call. * * @param {string} message Text to be placed in pre element. */ function appendPre(message) { var pre = document.getElementById('content'); var textContent = document.createTextNode(message + '\n'); pre.appendChild(textContent); } /** * Handle the login if signed out, return a Promise * to call the upload Docs function after signin. **/ function uploadDoc() { console.log("clicked!") var docUrl = document.getElementById('google-upload').getAttribute('data-url'); gapi.client.script.scripts.run({ 'scriptId':SCRIPT_ID, 'function':'uploadDoc', 'parameters': [ docUrl ] }).then(function(resp) { var result = resp.result; if(result.error && result.error.status) { // Error before the script was Called appendPre('Error calling API'); appendPre(JSON.parse(result, null, 2)); } else if(result.error) { // The API executed, but the script returned an error. // Extract the first (and only) set of error details. // The values of this object are the script 'errorMessage' and // 'errorType', and an array of stack trace elements. var error = result.error.details[0]; appendPre('Script error message: ' + error.errorMessage); if (error.scriptStackTraceElements) { // There may not be a stacktrace if the script didn't start // executing. appendPre('Script error stacktrace:'); for (var i = 0; i < error.scriptStackTraceElements.length; i++) { var trace = error.scriptStackTraceElements[i]; appendPre('\t' + trace.function + ':' + trace.lineNumber); } } } else { // The structure of the result will depend upon what the Apps // Script function returns. Here, the function returns an Apps // Script Object with String keys and values, and so the result // is treated as a JavaScript object (folderSet). console.log(resp.result) var msg = resp.result.response.result; appendPre(msg); // do more stuff with the response code } }) } 

Script Applications

Script application code does not need to be significantly changed. Instead of returning using the ContentService we can return simple JSON objects that will be used by the client.

 function uploadDoc(e) { Logger.log(e); var returnValue = {}; // Set the callback URL. See https://stackoverflow.com/questions/29525860/ Logger.log("Uploading the document..."); try { // Get the file and create it in Drive var blob = UrlFetchApp.fetch(e).getBlob(); DriveApp.createFile(blob); // If successful, return okay var msg = "The document was successfully uploaded!"; return msg; } catch(e) { Logger.log(e); // If a failure, return error message to the client return e.message } } 

It was difficult for me to get the code name CodePen, so I have an example that is safely hosted on my own site using the code above. Feel free to inspect the source and see the live Apps Script project.

Please note that the user must re-authorize when adding or changing areas in the Apps Script project.

+6
source

Source: https://habr.com/ru/post/1275663/


All Articles