How do I determine what caused my script to run?

Viewed 243

Whenever I attempt to display a UI dialog (e.g. msgBox or alert) it works fine when invoked via a menu item (e.g. from Google Sheets), but it hangs my script if I try to invoke it from the Google Apps Script editor (e.g. via Run > Run function).

enter image description here

My guess is it's because the Google Apps Script editor can't display any UI. To resolve this, I'd like to create a wrapper function that checks how the script was run, and not present UI depending on the source.

The "Executions" screen has the notion of Type (Editor, Standalone, Trigger):

enter image description here

This makes me think there is a way to get this type in code somehow.

Psuedo code of what the function might look like:

function showMessage(message) {
  var scriptSource = ???;
  if (scriptSource === "Standalone") {
    Browser.msgBox(message);
  } else {
    console.log(message);
  }
}

How would I get the scriptSource?

The closest thing I can find is TriggerSource, but that is missing the enum values 'Editor' and 'Trigger'. Furthermore, it's a property only available on a Trigger. I don't know how to access the current trigger. From my understanding, that's only available via the event object (e.g. via triggerUid) on functions acting as triggers. This method I'm running in the apps script editor doesn't have access to an event object.

3 Answers
  • You want to know whether the current project is the container-bound script type or the standalone script type.
  • You want to use Browser.msgBox().

I could understand about your question as above. In order to achieve it, as a workaround,I would like to propose to use Apps Script API. The flow of sample script is as follows. I think that there are several workarounds for your situation. So please think of this as one of them.

  1. Retrieve the parent ID of the project using the method of projects.get in Apps Script API. The parent ID means that the file ID of Google Docs.
    • When the parent ID is returned, it is found that the project is the container-bound script type.
    • When the parent ID is NOT returned, it is found that the project is the standalone script type.
  2. When the mimeType of parent ID is Google Form, Browser.msgBox() cannot be used. So the if statement is used for this.

Sample script:

This is a sample script. In this sample script, the script ID of current project is used. Of course, you can also manually give the script ID.

var id = ScriptApp.getScriptId(); // Retrieve scriptId of current project.
var url = "https://script.googleapis.com/v1/projects/" + id + "?fields=parentId";
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
res = JSON.parse(res.getContentText());
if ("parentId" in res) {
  Logger.log("Container-bound script type.")
  var mimeType = DriveApp.getFileById(res.parentId).getMimeType();
  if (mimeType === MimeType.GOOGLE_FORMS) {
    Logger.log("Browser.msgBox() cannot be used at Google Form.");
  } else {
    Browser.msgBox("Hello world");
  }
} else {
  Logger.log("Standalone script type.")
  Logger.log("Hello world");
}

Note:

  • When you use this script, please do the following flow.
    1. Enable Apps Script API at API console.
    2. At least, add the following scopes to the manifests.
      • https://www.googleapis.com/auth/drive
      • https://www.googleapis.com/auth/script.external_request
      • https://www.googleapis.com/auth/script.projects.readonly
  • If in your script, other scopes are required to be added, please add them. And if you want to use the automatically installer of scopes with the script editor, you can achieve it using a library. You can see the detail information at here.

References:

If I misunderstand your question, I'm sorry.

Edit:

  • You want to confirm whether the function is called from the script editor or the custom menu.

If my understanding is correct, how about this sample script? This is a sample script. The process list can be retrieved by giving the script ID and function name. In this sample script, using "ProcessType" of processes.listScriptProcesses in Apps Script API, it confirms whether the function is called from the script editor or the custom menu.

Sample script:

This is a sample script. The process list can be retrieved by giving the script ID and function name.

When you use this script, please enable Apps Script API at API console, and add a scope of https://www.googleapis.com/auth/script.processes to the manifests.

The how to use this script is as follows.

  1. Run addCustomMenu().
  2. Run sampleFunction at the custom menu.
    • By this, Call from custom menu is shown in log.
  3. Run sampleFunction at the script editor.
    • By this, Call from script editor is shown in log.
Script:
function addCustomMenu() {
  SpreadsheetApp.getUi().createMenu('sampleCustomMenu').addItem('sample', 'sampleFunction').addToUi();
}

function sampleFunction() {
  var scriptId = ScriptApp.getScriptId();
  var functionName = "sampleFunction";
  var url = "https://script.googleapis.com/v1/processes:listScriptProcesses?scriptId=" + scriptId + "&scriptProcessFilter.functionName=" + functionName;
  var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}, muteHttpExceptions: true});
  res = JSON.parse(res);
  if (!("processType" in res.processes[0])) {
    Logger.log("Call from custom menu")
  } else if (res.processes[0].processType == "EDITOR") {
    Logger.log("Call from script editor")
  }
}

References:

Not the best solution, but my current workaround is to create 3 versions of each function, and append how it was invoked to the name.

For example, if there was a "Hello World" function:

function onOpen() {
  var menu = [
    {name: 'Hello World', functionName: 'helloWorldViaMenu_'},
  ];
  SpreadsheetApp.getActive().addMenu('Custom', menu);
}

function helloWorldViaMenu_() {
  helloWorld_(false);
}

function helloWorldViaEditor() {
  helloWorld_(true);
}

function helloWorld_(invokedFromEditor) {
  if (invokedFromEditor) {
    Logger.log("Hello world");
  } else {
    Browser.msgBox("Hello world");
  }
}

helloWorldViaEditor is the only that doesn't have a _ at the end so it can be selected via the "Select function" Editor UI dropdown.

Making Dialogs

You can run them from the menu or the script editor. They work the same.

function makeAmenu(){
  SpreadsheetApp.getUi().createMenu('A Menu')
  .addItem('Run my Dialogs', 'showMyDialogs')
  .addToUi();
}

function showMyDialogs(){
  var ui=SpreadsheetApp.getUi();
  ui.alert('This is an alert');
  ui.prompt('This is a prompt');
  var html=HtmlService.createHtmlOutput('<p>This is a modeless dialog</p><input type="button" value="Close" onClick="google.script.host.close();" />');
  ui.showModelessDialog(html, 'Dialog');
}

If you run a script from here:

enter image description here

The you have to go here to see it:

enter image description here

Related