Triggering Google Script function with REST API request - google-apps-script

I am running into trouble to trigger a function on edit when REST API software called Workato receives data from Quick Base and inputting in Google Spreadsheet.
Following codes auto sort stated tabs in Google Spreadsheet.
function onPost(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ApprovedTab = ss.getSheetByName("APPROVED");
var CollateralPending = ss.getSheetByName("COLLATERAL PENDING");
var InProcessing = ss.getSheetByName("IN PROCESSING");
var InClosing = ss.getSheetByName("IN CLOSING");
var funded = ss.getSheetByName("FUNDED");
var ApprovedTabRange = ApprovedTab.getRange("A2:T99");
var CollateralPendingRange = CollateralPending.getRange("A2:T99");
var InProcessingRange = InProcessing.getRange("A2:T99");
var InClosingRange = InClosing.getRange("A2:T99");
var fundedRange = funded.getRange("A2:T99");
ApprovedTabRange.sort( { column : 1, ascending: true } );
CollateralPendingRange.sort( { column : 1, ascending: true } );
InProcessingRange.sort( { column : 1, ascending: true } );
InClosingRange.sort( { column : 1, ascending: true } );
fundedRange.sort( { column : 1, ascending: true } );
}
When i try using onEdit instead of onPost and manually update a row in spreadsheet, it sorts rows by ID column.
When i try onPost and send a update request from Workato, Google Script function does not run and as result it is not sorting rows.
Any help would be appreciated.
Thank You

If I understand correctly, you want to have the spreadsheet automatically call the sorting function after Workato edits data in the sheet.
Since edits via scripts or add-ons don't generate an OnEdit trigger, you'll need to send a separate POST request to trigger a Google Apps Script function in your spreadsheet after Workato updates the data.
In order to call a function via a POST request, you must name the function "doPost()" rather than "onPost()", and you then must Publish the script as a web-app, from the Publish menu.
When publishing the script you will want to "execute as" you, and be accessible to "anyone, even anonymous".
Publishing the script as a web app allows it to receive an incoming GET or POST request, via functions named doGet() or doPost().
See the documentation here:
https://developers.google.com/apps-script/guides/web

Related

If LockService works for a common script for multiple users, is there an option when dealing with multiple scripts sending data to the same column?

I have 4 different scripts linked to 4 different accounts, which do their functions and in the end send a value from a specific cell to a final spreadsheet and in that final spreadsheet , to the same column of data:
Script Spreadsheet 1:
function Spreadsheet1() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 1');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
Script Spreadsheet 2:
function Spreadsheet2() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 2');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
Script Spreadsheet 3:
function Spreadsheet3() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 3');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
Script Spreadsheet 4:
function Spreadsheet4() {
HIDDEN CODE LINES → THEY ARE COLLECT DATA FROM AN API
var first_sheet_id = SpreadsheetApp.openById('SPREADSHEET 4');
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet_id = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS');
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r=1;
while(second_sheet_page.getRange(r,1).getValue()) {
r++;
}
second_sheet_page.getRange(r,1).setValue(first_sheet_page.getRange(1,1).getValue());
}
LockService would work if it was the same script with multiple users trying to use it at the same time.
But in my case, there are four scripts with auto trigger (every 5 minutes) running and sending to the same column of the same spreadsheet.
Is there any way to be able to avoid having the risk of them meeting and putting values on the same lines?
If there is any way, please create a visual example of how to use it
so that I understand the method as I believe it is not as simple as my
knowledge limit.
From your updated question, in your situation, how about using Web Apps with LockService? From your question, I confirmed that 4 accesses are run simultaneously. In this case, in my benchmark for writing a Spreadsheet using Web Apps, it has already been found that 4 workers can be used. Ref From this result, I proposed to use Web Apps as a workaround for achieving your goal.
Usage:
1. Prepare script for Web Apps.
As a sample, please copy and paste the following script to a new Google Apps Script project. This script is used as Web Apps. In this case, you can also put this script in one of 4 scripts. But as a sample, I separated 4 clients and a server of Web Apps.
Please set your destination Spreadsheet ID.
function doGet(e) {
const lock = LockService.getDocumentLock();
if (lock.tryLock(350000)) {
try {
var ssId = e.parameter.spreadsheetId;
if (!ssId) return ContentService.createTextOutput("No spreadsheet ID.");
var first_sheet = SpreadsheetApp.openById(ssId);
var first_sheet_page = first_sheet.getSheetByName('Sheet86');
var second_sheet = SpreadsheetApp.openById('SAME DESTINATION AS FOUR SCRIPTS'); // <--- Please set your destination Spreadsheet ID.
var second_sheet_page = second_sheet.getSheetByName('Sheet86');
var r = 1;
while (second_sheet_page.getRange(r, 1).getValue()) {
r++;
}
second_sheet_page.getRange(r, 1).setValue(first_sheet_page.getRange(1, 1).getValue());
} catch (e) {
return ContentService.createTextOutput(e.message);
} finally {
lock.releaseLock();
return ContentService.createTextOutput("Done");
}
} else {
return ContentService.createTextOutput("Timeout");
}
}
2. Deploy Web Apps.
The detailed information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the importance of this workaround.
Please select "Anyone" for "Who has access".
In your situation, I thought that this setting might be suitable.
Of course, you can use the access token. If you want to use the access token, please set it as Anyone with Google account and use the access token at the client side.
Please click "Deploy" button.
Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
3. Prepare script of 4 clients.
Spreadsheet1
Please copy and paste the following script to the script editor of Spreadsheet 1. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet1() {
const srcSpreadsheetId = 'SPREADSHEET 1'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
Spreadsheet2
Please copy and paste the following script to the script editor of Spreadsheet 2. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet2() {
const srcSpreadsheetId = 'SPREADSHEET 2'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
Spreadsheet3
Please copy and paste the following script to the script editor of Spreadsheet 3. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet3() {
const srcSpreadsheetId = 'SPREADSHEET 3'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
Spreadsheet4
Please copy and paste the following script to the script editor of Spreadsheet 4. And, please reinstall the trigger. Because the scope is authorized.
function Spreadsheet4() {
const srcSpreadsheetId = 'SPREADSHEET 4'; // Please set spreadsheet ID.
const webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.
const res = UrlFetchApp.fetch(webAppsUrl + "?spreadsheetId=" + srcSpreadsheetId);
console.log(res.getContentText());
}
4. Testing.
After the script of Web Apps and the scripts of 4 clients were prepared, please run those functions of clients. By this, the script of Web Apps can be run with LockService. In this case, your 4 clients can be run simultaneously.
Note:
In this case, the order of the functions Spreadsheet1 to Spreadsheet4 cannot be controlled. Please be careful about this.
When you modified the Google Apps Script, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful this.
You can see the detail of this in the report of "Redeploying Web Apps without Changing URL of Web Apps for new IDE".
References:
Benchmark: Concurrent Writing to Google Spreadsheet using Form
Web Apps
Taking advantage of Web Apps with Google Apps Script

Google Calendar events to Google Sheers automatic refresh with onEdit trigger

I am trying to grab time events from my Google Calendar into a Google Spreadsheet.
When a new time-event is created in my Google Calendar this event should be automatically synchronized into my Google Spreadsheet. This should be done automatically by an onEdit event trigger.
At the moment it is only running by refreshing the Google Spreadsheet.
Maybe someone has a better solution for my challenge. Here is my code:
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('myCalendar')
.forSpreadsheet(ss)
.onEdit()
.create();
}
function myCalendar(){
var now=new Date();
// Startzeit
var startpoint=new Date(now.getTime()-60*60*24*365*1000);
// Endzeit
var endpoint=new Date(now.getTime()+60*60*24*1000*1000);
var events=CalendarApp.getCalendarById("your-calendar-ID").getEvents(startpoint, endpoint);
var ss=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("TEST");
ss.clear();
for (var i=0;i<events.length;i++) {
ss.getRange(i+1,1 ).setValue(events[i].getTitle());
ss.getRange(i+1,2).setValue(events[i].getDescription());
ss.getRange(i+1,3).setValue(events[i].getStartTime());
ss.getRange(i+1,4).setValue(events[i].getEndTime());
}
}
Problem
Execute a function updating a spreadsheet when an event in Google Calendar is created.
Solution
Use the EventUpdated installable trigger that is fired each time an event is modified in Calendar (e.g. created, updated, or deleted - see reference). From there, you can go the easy way (update all data in the spreadsheet with a built-in CalendarApp class) or the hard way (update data that was changed with incremental sync - see official guide).
Part 0 - install trigger
/**
* Installs Calendar trigger;
*/
function calendarTrigger() {
var trigger = ScriptApp.newTrigger('callback name here')
.forUserCalendar('calendar owners email here')
.onEventUpdated()
.create();
}
Part 1 - callback (Calendar -> Spreadsheet)
/**
* Updates spreadsheet;
* #param {Object} e event object;
*/
function updateSpreadsheet(e) {
//access spreadsheet;
var ss = SpreadsheetApp.openById('target spreadsheet id');
var sh = ss.getSheetByName('target sheet name');
var datarng = sh.getDataRange(); //assumed that data is only calendar data;
//access calendar;
var calendar = CalendarApp.getCalendarById(e.calendarId);
//set timeframes;
var start = new Date();
var end =new Date();
//get year before and three after;
start.setFullYear(start.getFullYear()-1);
end.setFullYear(end.getFullYear()+3);
//get events;
var events = calendar.getEvents(start, end);
//map events Array to a two-dimensional array of values;
events = events.map(function(event){
return [event.getTitle(),event.getDescription(),event.getStartTime(),event.getEndTime()];
});
//clear values;
datarng.clear();
//setup range;
var rng = sh.getRange(1,1, events.length, events[0].length);
//apply changes;
rng.setValues(events);
}
Notes
As per Tanaike's comment - it is important to account for triggers (both simple and installable) to not firing if event is triggered via script or request (see restrictions reference). To enable such feature you will have to introduce polling or bundle with a WebApp that the script will call after creating an event (see below for a bundling sample).
Your solution is better suited for backwards flow: edit in spreadsheet -> edit in Calendar (if you modify it to perform ops on Calendar instead of updating the spreadsheet, ofc).
Make use of Date built-in object's methods like getFullYear() (see reference for other methods) to make your code more flexible and easier to understand. Btw, I would store "ms in a day" data as a constant (86400000).
Never use getRange(), getValue(), setValue() and similar methods in a loop (and in general call them as little as possible) - they are I/O methods and thus are slow (you can see for yourself by trying to write >200 rows in a loop). Get ranges/values needed at the start, perform modifications and write them in bulk (e.g. with setValues() method).
Reference
EventUpdated event reference;
Calendar incremental synchronization guide;
Date built-in object reference;
setValues() method reference;
Using batch operations in Google Apps Script;
Installable and simple triggers restrictions;
WebApp bundling
Part 0 - prerequisites
If you want to create / update / remove calendar events via script executions, you can bundle the target script with a simple WebApp. You'll need to make sure that:
The WebApp is deployed with access set as anyone, even anonymous (it is strongly recommended to introduce some form of request authentication);
WebApp code has one function named doPost accepting event object (conventionally named e, but it's up to you) as a single argument.
Part 1 - build a WebApp
This build assumes that all modifications are made in the WebApp, but you can, for example, return callback name to run on successfull request and handle updates in the calling script. As only the calendarId property of the event object is used in the callback above, we can pass to it a custom object with only this property set:
/**
* Callback for POST requests (always called "doPost");
* #param {Object} e event object;
* #return {Object} TextOutput;
*/
function doPost(e) {
//access request params;
var body = JSON.parse(e.postData.contents);
//access calendar id;
var calendarId = body.calendar;
if(calendarId) {
updateSpreadsheet({calendarId:calendarId}); //callback;
return ContentService.createTextOutput('Success');
}else {
return ContentService.createTextOutput('Invalid request');
}
}
Part 2 - sample calling script
This build assumes that calling script and the WebApp are the same script project (thus its Url can be accessed via ScriptApp.getService().getUrl(), otherwise paste the one provided to you during WebApp deployment). Being familiar with UrlFetchApp (see reference) is required for the build.
/**
* Creates event;
*/
function createEvent() {
var calendar = CalendarApp.getCalendarById('your calendar id here');
//modify whatever you need to (this build creates a simple event);
calendar.createEvent('TEST AUTO', new Date(), new Date());
//construct request parameters;
var params = {
method: 'post',
contentType: 'application/json',
muteHttpExceptions: true,
payload: JSON.stringify({
calendar: calendar.getId()
})
};
//send request and handle result;
var updated = UrlFetchApp.fetch(ScriptApp.getService().getUrl(),params);
Logger.log(updated); //should log "Success";
}
enter code here
// There are can be many calendar in one calendar of user like his main calendar, created calendar, holiday, etc.
// This function will clear all previous trigger from each calendar of user and create new trigger for remaining calendar of the user.
function createTriggers() {
clearAllTriggers();
let calendars = CalendarApp.getAllCalendars();
calendars.forEach(cal => {
ScriptApp.newTrigger("calendarUpdate").forUserCalendar(cal.id).onEventUpdated().create();
});
}
/* This trigger will provide us the calendar ID from which the event was fired, then you can perform your CalendarApp and sheet operation. If you want to synchronize new update more efficiently then use Calendar Advance Service, which will provide you with synchronization token that you can use to retrieve only updated and added events in calendar.
*/
function calendarUpdate(e) {
logSyncedEvents(e.calendarId);
}
function clearAllTriggers() {
let triggers = ScriptApp.getProjectTriggers();
triggers.forEach(trigger => {
if (trigger.getEventType() == ScriptApp.EventType.ON_EVENT_UPDATED) ScriptApp.deleteTrigger(trigger);
});
}

Google Apps Script create calendar event from sheet forbidden?

In a document-bound Google Appscript in one of our company spreadsheets, I've created a script that turns spreadsheet lines into Google calendar appointments. The function works fine for me, but not for my coworker, even though we both have permissions to edit the calendar and change sharing permissions, and my coworker proved he can create appointments on the calendar from calendar.google.com.
He gets the following error message when he runs the script:
{"message":"Forbidden","name":"GoogleJsonResponseException","fileName":"SCHEDULER","lineNumber":204,"stack":"\tat SCHEDULER:204 (createAppointments)\n"}
Line 204 corresponds to the command:
Calendar.Events.insert(event, CAL, {sendNotifications: true, supportsAttachments:true});
If he has edit rights to the calendar, why is this forbidden? Is there a problem with the Calendar service in Google Apps Script? What is more, I changed the CAL variable to a calendar I personally created and shared out to him with the same permissions. He can edit that calendar just fine.
Here is the psuedocode for the function
function createAppointments() {
var CAL = 'companyname.com_1v033gttnxe2r3eakd8t9sduqg#group.calendar.google.com';
for(/*each row in spreadsheet*/)
{
if(/*needs appointment*/)
{
var object = {/*...STUFF...*/};
var coworker = 'coworker#companyname.com';
var timeArgs = {start: /*UTC Formatted time*/, end: /*UTC Formatted time*/}
if(/*All the data checks out*/{
var summary = 'Name of appointment'
var notes = 'Stuff to put in the body of the calendar appointment';
var location = '123 Happy Trail, Monterrey, TX 12345'
//BUILD GOOGLE CALENDAR OBJECT
var event = {
"summary": summary,
"description": notes,
"start": {
"dateTime": timeArgs.start,
"timeZone": TZ
},
"end": {
"dateTime": timeArgs.end,
"timeZone": TZ
},
"guestsCanInviteOthers": true,
"reminders": {
"useDefault": true
},
"location": location
//,"attendees": []
};
event.attendees = [{coworker#companyname.com, displayName: 'coworker name'}];
//CREATE CALENDAR IN GOOGLE CALENDAR OF CONST CAL
Calendar.Events.insert(event, CAL, {sendNotifications: true, supportsAttachments:true});
} else{/*Tell user to fix data*/}
}
}
Thank you very much!
Update 12/29/2017:
I've Tried adjusting the app according to Jason Allshorn and Crazy Ivan. Thank you for your help, so far! Interestingly, I have run into the same response using both the Advanced Calendar Service and the CalendarApp.
The error is, as shown below:
<!DOCTYPE html><html><head><link rel="shortcut icon" href="//ssl.gstatic.com/docs/script/images/favicon.ico"><title>Error</title><style type="text/css">body {background-color: #fff; margin: 0; padding: 0;}.errorMessage {font-family: Arial,sans-serif; font-size: 12pt; font-weight: bold; line-height: 150%; padding-top: 25px;}</style></head><body style="margin:20px"><div><img alt="Google Apps Script" src="//ssl.gstatic.com/docs/script/images/logo.png"></div><div style="text-align:center;font-family:monospace;margin:50px auto 0;max-width:600px">Object does not allow properties to be added or changed.</div></body></html>
Or, after parsing that through an html editor:
What does that even mean? I have the advanced service enabled, and the script is enabled to run from anyone. Any ideas?
I have confirmed after testing what the error comes back after trying to run the calendarApp/Advanced Calendar event creation command.
Here is my code that caused me to get this far:
function convertURItoObject(url){
url = url.replace(/\+/g,' ')
url = decodeURIComponent(url)
var parts = url.split("&");
var paramsObj = {};
parts.forEach(function(item){
var keyAndValue = item.split("=");
paramsObj[keyAndValue[0]] = keyAndValue[1]
})
return paramsObj; // here's your object
}
function doPost(e) {
var data = e.postData.contents;
data = convertURItoObject(data);
var CAL = data.cal;
var event = JSON.parse(data.event);
var key = data.key;
var start = new Date(event.start.dateTime);
if(ACCEPTEDPROJECTS.indexOf(key) > -1)
{
try{
var calendar = CalendarApp.getCalendarById(CAL);
calendar.createEvent(event.summary, new Date(event.start.dateTime), new Date(event.end.dateTime), {description: event.description, location: event.location, guests: event.guests, sendInvites: true});}
/*try {Calendar.Events.insert(event, CAL, {sendNotifications: true, supportsAttachments:true});} Same error when I use this command*/
catch(fail){return ContentService.createTextOutput(JSON.stringify(fail));}
e.postData.result = 'pass';
return ContentService.createTextOutput(JSON.stringify(e));
}
else {
return ContentService.createTextOutput('Execution not authorized from this source. See CONFIG of target project for details.');
}
}
Your script is using Advanced Google Services, specifically Calendar. Read the section "Enabling advanced services"; everyone will have to follow those steps to use the script.
Alternatively (in my opinion, this is a better solution), rewrite the script so that it uses the standard CalendarApp service. It also allows you to create an event and then you can add various reminders to that event.
A solution from my side would be to abstract the calendar event creation function away from your Spreadsheet bound script to a separate standalone apps-script that runs under your name with your permissions.
Then from your sheet bound script call to the standalone script with a PUT request containing the information needed to update the Calender. This way anyone using your sheet addon can update the calander without any mess with permissions.
The sheet bound script could look something like this:
function updateCalander(){
var data = {
'event': EVENT,
};
var options = {
'method' : 'post',
'contentType': 'application/json',
'payload' : data
};
var secondScriptID = 'STANDALONE_SCRIPT_ID'
var response = UrlFetchApp.fetch("https://script.google.com/macros/s/" + secondScriptID + "/exec", options);
Logger.log(response) // Expected to see sent data sent back
Then your standalone script would look something like this:
function convertURItoObject(url){
url = url.replace(/\+/g,' ')
url = decodeURIComponent(url)
var parts = url.split("&");
var paramsObj = {};
parts.forEach(function(item){
var keyAndValue = item.split("=");
paramsObj[keyAndValue[0]] = keyAndValue[1]
})
return paramsObj; // here's your object
}
function doPost(e) {
var CAL = 'companyname.com_1v033gttnxe2r3eakd8t9sduqg#group.calendar.google.com';
var data = e.postData.contents;
data = convertURItoObject(data)
var event = data.event;
try {
Calendar.Events.insert(event, CAL, {sendNotifications: true, supportsAttachments:true});
}
catch(e){
Logger.log(e)
}
return ContentService.createTextOutput(JSON.stringify(e));
}
Please note, the standalone script needs to be set to anyone can access, and when you make updates to the code be sure to re-publish the code. If you don't re-publish your calls to the standalone script are not made to the latest code.
This is a delayed response, but thanks to all who recommended using the POST method. It turns out the proper way to do this is to use URLFetchApp and pass the Script's project Key to authorize the calendar access (I believe you only need to make sure the person executing the script has rights to edit the actual calendar).
Here is basically how to do it in a functional way:
//GCALENDAR is th e unique ID of the project int it's URL when the script is open for editing
//PROJECTKEY is the unique ID of the project, found in the Project Properties Menu under FILE.
//CREATE CALENDAR IN GOOGLE CALENDAR OF CONST CAL
var data = {
'event': JSON.stringify(event),
'cal': CAL,
'key': PROJECTKEY
};
var options = {
'method' : 'post',
'contentType': 'application/json',
'payload' : data,
'muteHttpExceptions': true
};
var answer = UrlFetchApp.fetch("https://script.google.com/macros/s/" + GCALENDAR + "/exec", options).getContentText();
Logger.log(answer);

Calling a bound script's method using the Execution API

I'm using PropertiesServices as variables, specifically Document Properties , in order to replace some tokens like "{client name}". Since those properties are scoped to the bound script only, I'm looking for a way to modify their values from my PHP application.
Is it possible to call a bound script's function using the Execution API, or maybe from a standalone script? Otherwise, should I instead use the Script Properties instead (although the docs make me think you can't use them if the script isn't 'standalone).
It looks like if the user that the Execution API is running under has permission to the doc that bound script ran by the execution api can read document properties.
Here is my test:
Create a new spreadsheet. Create a new script. Add some data using the menu from onOpen. Run executeAPI inside the script. The log successfully shows the document properties.
function onOpen() {
var testMenu = SpreadsheetApp.getUi().createMenu("test")
testMenu.addItem("Add some data", "addData").addToUi();
testMenu.addItem("Preview data", "getData").addToUi();
}
function getData(){
var keys = PropertiesService.getDocumentProperties().getKeys();
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().clear().appendRow(keys)
}
function returnData(){
return PropertiesService.getDocumentProperties().getKeys();
}
function addData(){
var DT = new Date().toString()
PropertiesService.getDocumentProperties().setProperty(DT,DT);
}
function executeAPI(){
var url = 'https://script.googleapis.com/v1/scripts/'+ScriptApp.getProjectKey()+':run';
var payload = JSON.stringify({"function": "returnData","parameters":[], "devMode": true});
var params={method:"POST",
headers:{Authorization: "Bearer "+ ScriptApp.getOAuthToken()},
payload:payload,
contentType:"application/json",
muteHttpExceptions:true};
var results = UrlFetchApp.fetch(url, params);
Logger.log(results)
}

Google apps script and script db replacement example needed

I've got a google apps script UI I am using in a google doc.
I'm trying to replace the current handler which uses the Script DB. Script DB has since been deprecated. The amount of information I was writing was minimal and I figured I would just write the info to google sheets.
Here is the handler from the .html
function addApprover(){
google.script.run.withSuccessHandler(function() {
getApprovers();
$('#approver').val('');
}).addApprover($("#approver").val());
}
.gs
function addApprover(email){
var db = ScriptDb.getMyDb();
var docId = DocumentApp.getActiveDocument().getId();
var ob = {
docId: docId,
approverEmail: email,
status: null,
emailSent: false
}
db.save(ob);
var history = {
docId: docId,
action: 'Added Approver',
email: email,
date: Utilities.formatDate(new Date(), "GMT", "MM-dd-yyyy' 'HH:mm:ss"),
}
db.save(history);
}
I figure that I still call the .gs function and just need to change the function accordingly.
I'm fairly certain that the text box approver holds the email addresses.
How do I access these items?
I'm fairly certain I'm looking for a "for each" statement to iterate through each email address and send them a message and write their name to a specific area of a sheet but I am unsure how to proceed.
Hopefully this will get you started:
function addApprover(email){
var docId = DocumentApp.getActiveDocument().getId();
var ss = SpreadsheetApp.openById('Your Spreadsheet file ID here');
var sheetToWriteTo = ss.getSheetByName('Your sheet name here');
var rowData = [docId, email, null, false];
sheetToWriteTo.appendRow(rowData);
var history = [docId, 'Added Approver', email, Utilities.formatDate(new Date(), "GMT", "MM-dd-yyyy' 'HH:mm:ss")];
sheetToWriteTo.appendRow(rowData);
}
If you want to write the two sets of data to two different sheets, you'll need to get a reference to a second sheet. The data goes into an array, not an object. Although you will see an array called an object in Google documentation also. If you see brackets [], it's an array.
If you have any problems, debug the code with Logger.log() statements and/or debug and a breakpoint, then post another question if it's a major issue, or if it's something minor, make a comment here.