I am trying to execute a simple Apps script to retrieve data from my google sheet. However, I get the error:
Bad value (line 2, file "Code", project "Untitled project")
when I paste the link:https://script.google.com/macros/s/AKfycbw8_1qGhuEMvfOEbLAx69x4fXyPl4sKQc4dvka81i5t42MkZpU/exec (the link provided for my web app after publishing) into my browser and hit enter.
However,when I click "test web app for your latest code" after publishing, it displays the JSON correctly without any issues. My app script code is as follows:
function doGet() {
var ss = SpreadsheetApp.openById('1gQKvGO0fhcrgqeke8HkUqm5Mb9dXWbaflIDmIo-pWcA');
var sheet = ss.getSheets()[0];
// Get the range of cells that store employee data.
var employeeDataRange = ss.getRangeByName("Range1");
// For every row of employee data, generate an employee object.
var employeeObjects = getRowsData(sheet, employeeDataRange);
return ContentService.createTextOutput(JSON.stringify(employeeObjects)).setMimeType(ContentService.MimeType.JSON);
}
Other posts mention that the spreadsheet id must be correct but I am pretty sure my id is correct.
From your question, it seems that you have changed the code but you have not republished it with latest version, so when you "test web app for your latest code", it works fine but when you use your publishing link, it crashes.
go to : publish > deploy as webapp > select project version to new and update the project.
Related
I'm using a web app to send data to Google Sheets via Apps Script. I'm using 'appendrow' to post several variables. Everything works fine but one variable is a number, call it 'Amount'. I found out that I can type a Google Sheets type formula in my web app such as '=10-5', '=10*2', '=10/2' and when it gets posted to my Google Sheets it provides the expected result. Good! However, if I type '=10+5', I get an error in my Google Sheets. When I look at the formula bar, it shows '10 5'. Why does it not show the formula as '=10+5'? The previous examples that I gave all show the expected formula in the formula bar.
function doGet(e) {
// I cannot take credit for this script. I copied and modified it from another user.
var ss = SpreadsheetApp.openById("FILE ID GOES HERE")
var sh = ss.getSheetByName("Register"); //sheet name is 'Register'
var date = e.parameter.date;
e.parameter.date = '1/2/2023'
var amount = e.parameter.amount;
var paytype = e.parameter.paytype;
var payid = e.parameter.payid;
var maincat = e.parameter.maincat;
var subcat = e.parameter.subcat;
var explanation = e.parameter.explanation;
sh.appendRow([date, amount, paytype, payid, maincat, subcat,
explanation]);
}
So when data get passed to Google Sheets via the sh.appendRow, the amount always works correctly when it contains, for example:
'10.52', '=10-5', '=10*2', '=10/2', but not '=10+5'. Instead, I get '10 5' and shows an error in the spreadsheet cell.
You may have guessed that I am a novice when it comes to script.
Thank you for any ideas.
What I wish to achieve:
Whenever a cell is changed in any google sheet on my shared drive (by
any user on the domain) I want to call an API endpoint and include
information about which cell was edited.
My approach:
I believe Google App Scripts Add-on is what I need. Installed for all users on the domain.
I see there are "bound" scripts and standalone scripts. For standalone scripts I am not able to create any other triggers than timer and calender based triggers. Bound scripts seem to be permanently bound to a single sheet and won't impact other sheets in any way.
What am I missing?
I find a few end-to-end tutorials on blogs for making bound scripts, but nothing for generic cross-domain stuff.
You can achieve all this through a standalone script. Create a standalone script and follow these steps:
Step 1: Get spreadsheet ids
First you would have to get the id of the different Spreadsheets in your shared drive. You can do it in Google Apps Script itself if you use the Advanced Drive Service (see Reference below). To activate this service, go to Resources > Advanced Google services... in your script editor and enable Drive API.
Then, write a function that will return an array of the spreadsheet ids in the shared drive. You will have to call Drive.Files.list for that. It could be something along the following lines (please write your shared driveId in the corresponding line):
function getFileIds() {
var params = {
corpora: "drive",
driveId: "your-shared-drive-id", // Please change this accordingly
includeItemsFromAllDrives: true,
q: "mimeType = 'application/vnd.google-apps.spreadsheet'",
supportsAllDrives: true
}
var files = Drive.Files.list(params)["items"];
var ids = files.map(function(file) {
return file["id"];
})
return ids;
}
Step 2: Create triggers for each spreadsheet
Install an onEdit trigger programmatically for each of the spreadsheets (an edit trigger fires a function every time the corresponding spreadsheet is edited, so I assume this is the trigger you want). For this, the ids retrieved in step 1 will be used. It could be something similar to this:
function createTriggers(ids) {
ids.forEach(function(id) {
var ss = SpreadsheetApp.openById(id);
createTrigger(ss);
})
}
function createTrigger(ss) {
ScriptApp.newTrigger('sendDataOnEdit')
.forSpreadsheet(ss)
.onEdit()
.create();
}
The function createTriggers gets an array of ids as a parameter and, for each id, creates an onEdit trigger: everytime any of these spreadsheets is edited, the function sendDataOnEdit will run, and that's where you will want to call your API endpoint with information about the edited cell.
Step 3: Call API endpoint
The function sendDataOnEdit has to get data from the edited cell and send it somewhere.
function sendDataOnEdit(e) {
// Please fill this up accordingly
var range = e.range;
var value = range.getValue();
UrlFetchApp.fetch(url, params) // Please fill this up accordingly
}
First, it can get information about the cell that was edited via the event object, passed to the function as the parameter e (you can get its column, its row, its value, the sheet and the spreadsheet where it is located, etc.). For example, to retrieve the value of the cell you can do e.range.getValue(). Check the link I provide in reference to get more details on this.
Second, when you have correctly retrieved the data you want to send, you can use UrlFetchApp.fetch(url, params) to make a request to your URL. In the link I provide below, you can see the parameters you can specify here (e.g., HTTP method, payload, etc.).
Please bear in mind that you might need to grant some authorization to access the API endpoint, if this is not public. Check the OAuth reference I attach below.
(You have to edit this function accordingly to retrieve and send exactly what you want. What I wrote is an example).
Summing this up:
In order to create the triggers you should run createTriggers once (if you run it more times, it will start creating duplicates). Run for example, this function, that first gets the file ids via Drive API and then creates the corresponding triggers:
function main() {
var ids = getFileIds();
createTriggers(ids);
}
Also, it would be useful to have a function that will delete all the triggers. Run this in case you want to start from fresh and make sure you don't have duplicates:
function deleteTriggers() {
var triggers = ScriptApp.getProjectTriggers();
triggers.forEach(function(trigger) {
ScriptApp.deleteTrigger(trigger);
})
}
Reference:
Advanced Drive Service
Drive.Files.list
onEdit trigger
Install trigger programmatically
onEdit event object
UrlFetchApp.fetch(url, params)
Connecting to external APIs
OAuth2 for Apps Script
ScriptApp.deleteTrigger(trigger)
I hope this is of any help.
For reference: Here is my Google Form.
Here is a Google PE Blank Sheet which has been tied to the output of my Google Form (answers will output on tab Form Responses 2).
I've built up a Google Form capable of collecting a good bit of data. It is meant to be filled out by a manager requesting SAP permissions for a new hire.
They proceed through the Form and enter their primary department (SAP Stream) which leads them to the next page/section that allows the manager to check off what the employee's required permissions will be.
Sometimes, an employee will interface with multiple departments, so the option for a supporting/secondary SAP Stream exists on the second page. This can be continued as needed until all of an employee's requisite roles/permissions are collected in a Google Sheet.
In the Sheet, you will see that:
Columns A-F include basic information
Columns H-T are for the Job Responsibilities
Columns U-AG are a range for the information of one's Supporting SAP Stream
Columns AH-AT are the Supporting SAP Stream's Job Responsibilities
Columns AU-BG collect the Secondary Supporting SAP Stream
I had built Google Script language to collect each of these bits of information in variables which would then get declared as global variables so they could be passed along to an HTML page in the App Script.
Finally, an email consisting of this HTML page would be sent out to the manager in charge of designating SAP permissions.
In previous iterations (which were much simpler), I used an IF statement to check whether a column at the end of the row was empty.
// Loop over the rows
data.forEach((row,i) => {
// Identify whether notification has been sent
if (row[59] === '') {
If it was, the Script would check the Sheet, collecting all of the aforementioned information, and send an email. Then, it would populate that last cell with "Notification Sent" so that the next time the Form was submitted and the Script checked, it would skip sending out a redundant email (and we would have a record that the process went through successfully).
The latest version included variables for
function jobResponsibilities(a){
var z = a.range.getRow();
var valuesA = a.range.getSheet().getRange(z,8,1,13).getValues();
var jobResponsibilities = valuesA.join(' ');
console.log(jobResponsibilities);
}
function supportingSAP(b){
var y = b.range.getRow();
var valuesB = b.range.getSheet().getRange(y,21,1,13).getValues();
var supportingSAP = valuesB.join(' ');
}
function secondaryJob(c){
var x = c.range.getRow();
var valuesC = c.range.getSheet().getRange(x,34,1,13).getValues();
var secondaryJob = valuesC.join(' ');
}
function secondarySAP(d){
var w = d.range.getRow();
var valuesD = d.range.getSheet().getRange(w,47,1,13).getValues();
var secondarySAP = valuesD.join(' ');
}
Collecting the four varying types of information. I used a getRange in order to scope out the exact parts of the Sheet which would have this information and join them together so that the variable could be pulled into the email in a simple table format.
For some reason, upon Form submission, the Notification IF doesn't seem to be running and the email never gets sent.
Is it a problem with the way I've ordered things? Did I improperly build the functions off what you showed me? Is it something as simple as my brackets at the bottom?
I have the following code in Google Apps Script which retrieves CSV data from a webpage via HTTP using basic authentication and places it into a spreadsheet:
CSVImport.gs
function parseCSVtoSheet(sheetName, url)
{
// Credentials
var username = "myusername";
var password = "mypassword";
var header = "Basic " + Utilities.base64Encode(username + ":" + password);
// Setting the authorization header for basic HTTP authentication
var options = {
"headers": {
"Authorization": header
}
};
// Getting the ID of the sheet with the name passed as parameter
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName(sheetName);
var sheetId = sheet.getSheetId();
// Getting the CSV data and placing it into the spreadsheet
var csvContent = UrlFetchApp.fetch(url, options).getContentText();
var resource = {requests: [{pasteData: {data: csvContent, coordinate: {sheetId: sheetId}, delimiter: ","}}]};
Sheets.Spreadsheets.batchUpdate(resource, spreadsheet.getId());
}
This has been working up until recently where randomly I get the following error on the UrlFetchApp.fetch line:
Exception: Unexpected error: http://www.myurl.com/data/myfile.csv (line 21, file "CSVImport")
I have tried:
Putting the credentials directly in the URL instead of in an Authorization header (I received a different error saying "Login information disallowed").
Encoding the credentials to base64 right when I pass it into the headers object (didn't work, same error).
Removing authentication altogether (predictably I received a 401 response from the HTTP page).
I'm not sure what else to try and why this randomly broke down all of a sudden. Any advice?
This is related to a new bug, see here
Many users are affected, I recommend you to "star" the issue to increase visibility and hopefully accelerate the process.
I had the same situation. At that time, I could noticed that when the built-in function of Google Spreadsheet is used for the URL, the values can be retrieved. In that case, as the current workaround, I used the following flow.
Put a formula of =IMPORTDATA(URL).
Retrieve the values from the sheet.
When above flow is reflected to your URL of http://www.myurl.com/data/myfile.csv, it becomes as follows.
About basic authorization for URL:
When I saw your script, I confirmed that you are using the basic authorization. In this case, the user name and password can be used for the URL like http://username:password#www.myurl.com/data/myfile.csv.
From your script, when the values of username and password are myusername and mypassword, respectively, you can use the URL as http://myusername:mypassword#www.myurl.com/data/myfile.csv.
Here, there is an important point. If the specific characters are included in username and password, please do the url encode for them.
Sample script:
function myFunction() {
const url = "http://myusername:mypassword#www.myurl.com/data/myfile.csv"; // This is your URL.
// Retrieve the values from URL.
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName(sheetName);
sheet.clear();
var range = sheet.getRange("A1");
range.setFormula(`=IMPORTDATA("${url}")`);
// Retrieve the values from sheet to an array.
SpreadsheetApp.flush();
var values = sheet.getDataRange().getValues();
range.clear();
console.log(values)
}
When above script is run, the values from the URL are put to the sheet, and the values are retrieved as 2 dimensional array for values. If you want to leave only values without the formula, I think that you can copy and paste the values.
In this answer, I used IMPORTDATA. But for each situation, other functions might be suitable. In that case, please check them.
Note:
This is the current workaround. So when this issue was removed, I think that you can use your original script.
References:
IMPORTDATA
setFormula()
Disable Chrome V8 Runtime Engine until Google fix this.
To disable:
From Menu click on Run > Disable new Apps Script runtime powered by Chrome V8
As per #346 from the official issue tracker https://issuetracker.google.com/issues/175141974
hey guys I have found a possible solution that is working for me try
inputting empty {} like this
UrlFetchApp.fetch(apiLink, {});
it worked for me
I tried this, and it too is working for me. Works even when using the "new Apps Script runtime powered by Chrome V8".
I am working on a simple script that creates an account in the control panel of a domain - for example Gmail - and I was looking for a function in the Google apps script that creates an account automatically on inserting data to a spreadsheet
I searched the internet and I did find this though : https://developers.google.com/apps-script/class_usermanager
and the method I am using is : var user = UserManager.createUser("john.smith", "John", "Smith", "password
My question is, how can I insert the parameters from the spreadsheet that I have.
Sorry if this sounds a bit stupid I'm just new to Google apps script.
To read from the spreadsheet, you would use the SpreadsheetApp.
An example of reading a set of rows. (Let's say all rows).
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
.getValues() returns a 2D array. So you would access it by data[rowNum][colNum]. Let's say you want to add every row as a new user, you could do
for (var i in data) {
UserManager.createUser(data[i][0], data[i][1], data[i][2], data[i][3]);
}
How would you run said script? You could put all of it inside some function (function addAllUsers()) and then run it from the run menu in the Script Editor.