I get this exception since I started integrating Google Sheets and Firebase and Firestore. Despite all the tutorials and articles, non of the solutions worked with me.
my issue is when using getDocuments method for retrieving all the docs, the program throws an exception saying Error: Missing or insufficient permissions.
I already project created in Google Cloud, and I have created all the services needed and added all the APIs as instructed from Google Cloud website. Also, I have added oauthscopes needed. unfortunately, it did not work also.
My code:
function getRatingData() {
// Initialize firestore
const firestore = initFirestore();
// Initialize spread sheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeSpreadSheet = ss.getSheetByName("Sheet1");
// Get all docs
const docs = firestore.getDocuments("rating_debug");
for(var i = 0; i < docs.length; i++){
var array = [];
const lastRow = activeSpreadSheet.getLastRow();
// Get data field of each doc
var date = docs[i].fields["date"];
array.push(date);
var customerID = docs[i].fields["customerID"];
array.push(customerID.stringValue);
var mandoobID = docs[i].fields["mandoobID"];
array.push(mandoobID.stringValue);
var offerUniqueID = docs[i].fields["offerUniqueID"];
array.push(offerUniqueID.stringValue);
var message = docs[i].fields["message"];
array.push(message.stringValue);
var rating = docs[i].fields["rating"];
array.push(rating);
activeSpreadSheet.appendRow(array)[lastRow + 1];
}
}
function initFirestore(){
var config = {
"project_id" : "PROJECT-ID",
"private_key" : "PRIVATE-KEY",
"client_email" : "CLIENT-EMAIL",
};
return FirestoreApp.getFirestore(config.client_email, config.private_key, config.project_id);
}
My oauthScopes
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/cloud-platform.read-only",
"https://www.googleapis.com/auth/datastore",
"https://www.googleapis.com/auth/firebase",
"https://www.googleapis.com/auth/firebase.readonly",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/script.deployments",
"https://www.googleapis.com/auth/script.projects",
"https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/script.scriptapp",
"https://www.googleapis.com/auth/script.locale",
"https://www.googleapis.com/auth/documents.currentonly",
"https://www.googleapis.com/auth/spreadsheets.currentonly",
"https://www.googleapis.com/auth/presentations.currentonly",
"https://www.googleapis.com/auth/drive.addons.metadata.readonly",
"https://www.googleapis.com/auth/gmail.addons.current.action.compose",
"https://www.googleapis.com/auth/firebase.database"
This is my output for more clarification
photo of the output
I have set up a link between Woocommerce orders (a ecommerce plugin for WordPress that we use for our NGO), and a Google Sheet table using this script in Google Sheet's script editor:
//this is a function that fires when the webapp receives a GET request
function doGet(e) {
return HtmlService.createHtmlOutput("request received");
}
//this is a function that fires when the webapp receives a POST request
function doPost(e) {
var myData = JSON.parse([e.postData.contents]);
var order_number = myData.number;
var order_created = myData.date_created;
var order_status = myData.status;
var order_total = myData.total;
var billing_first_name = myData.billing.first_name;
var billing_last_name = myData.billing.last_name;
var billing_email = myData.billing.email;
var billing_phone = myData.billing.phone;
var shipping_first_name = myData.shipping.first_name;
var shipping_last_name = myData.shipping.last_name;
var shipping_address_1 = myData.shipping.address_1;
var shipping_address_2 = myData.shipping.address_2;
var shipping_postcode = myData.shipping.postcode;
var shipping_city = myData.shipping.city;
var shipping_country = myData.shipping.country;
var payment_method = myData.payment_method_title;
var currency = myData.currency;
var timestamp = new Date();
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
}
Everything works as intended, every new order is populated in the Google Sheet table a few seconds later.
However, when I apply a filter on any column in Google Sheet, let's say for payment method, selecting "PayPal", no new order will populate the Google Sheet's table.
They are registered in the woocommerce plugin, payment is ok, all is fine, except that Google Sheet does not receive the order.
Even after removing the filter, it doesn't appear.
All next orders will appear if all filters are deactivated in Google Sheet.
So, there is an issue with Google Sheet filters, but I don't know what is causing it. Is it my script? Is it Google API's fault? Woocommerce webhook?
Please note that I am not a developer, I found this script online and tweaked it myself by try and guess for my own needs.
Modification points:
When the sheet of Google Spreadsheet uses the basic filter, when the values are put using appendRow(), the values are not appended. This might be the current specification.
I thought that this might be the reason of your issue.
In your script, the values are put using appendRow(), and appendRow() is used in a loop. In this case, the process cost of the script will become a bit high. When setValues() is used, this issue can be also removed.
In this case, I would like to propose to append the values using setValues(). When setValues() is used, the values can be put to the filtered sheet. But, when the values are put to the filtered sheet, the filtered sheet is not changed while the values are put.
So it is required to refresh the basic filter after the values are put.
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify your script as follows.
From:
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
To:
var sheet = SpreadsheetApp.getActiveSheet();
var values = [];
for (var i = 0; i < myData.line_items.length; i++) {
var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
values.push([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]);
}
// Put values using "setValues".
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
// Refresh basic filter.
var filter = sheet.getFilter();
if (filter) {
var range = filter.getRange();
for (var i = range.getColumn(), maxCol = range.getLastColumn(); i <= maxCol; i++) {
var filterCriteria = filter.getColumnFilterCriteria(i)
if (filterCriteria) {
filter.setColumnFilterCriteria(i, filterCriteria);
}
}
}
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
setValues(values)
getLastRow()
getFilter()
Class Filter
Data sending from google sheet:
Script using to send data to firebase from google sheet.
function writeData() {
var ss = SpreadsheetApp.openById("####");
var sheet = ss.getSheets()[0];
var data = sheet.getDataRange().getValues();
var dataToImport = {};
for(var i = 1; i < data.length; i++) {
var department = data[i][0];
var year = data[i][1];
var course = data[i][2];
dataToImport[department] = {};
dataToImport[department][year] = {}
dataToImport[department][year][course] = {}
dataToImport[department][year][course][i] = {
course: data[i][3],
dateAdded: data[i][4],
fileSize: data[i][5],
fileType:data[i][6],
downloadLink: data[i][7],
};
}
var firebaseUrl = "https:url";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
base.setData("",dataToImport);
}
Data that is sent:
Q: My questions that since i had parent electrical and its two childs that were 1 and 2 and each child had a course dsp and aes respectively but only 1 child is send that is the second one.
Why first child is not sent to firebase ?
This code replacing previous entry with new entry. Replace this -
dataToImport[department] = {};
dataToImport[department][year] = {}
dataToImport[department][year][course] = {}
with this -
dataToImport[department] = dataToImport[department] || {};
dataToImport[department][year] = dataToImport[department][year] || {}
dataToImport[department][year][course] = dataToImport[department][year][course] || {}
As a side note to #ra89fi's answer (because I didn't pick up on the overwrite bug).
The line base.setData("",dataToImport); is setting all data at the root of your database ("") with the given data (dataToImport). Any other data in your database will be deleted.
Instead, you should use an update operation.
I unfortunately can't establish which version of the Firebase API you are using. It's not quite the REST API and not quite JavaScript. So here are some relevant documentation links.
REST Update Guide
REST Update Reference
JavaScript (Web) Update Guide
JavaScript (Web) Update Reference
As I would like to create documents by merging the entries in a list into a Google Docs template. I have therefore integrated the DocumentMerge method from my previous question into a printButton in a list widget.
Clicking on the printButton should produce a document that merges the contents of the current row into the document template. But when I click on the printButton the method fails due to a circular reference. How can I fix that? The print method goes like this ...
function printReview(widget) {
var review = app.models.Review.getRecord(widget.datasource.item._key);
var templateId = 'templateId';
var filename = 'Review for ...' + new Date();
var copyFile = DriveApp.getFileById(templateId).makeCopy(filename);
var copyDoc = DocumentApp.openById(copyFile.getId());
var copyBody = copyDoc.getBody();
var fields = app.metadata.models.Review.fields;
for (var i in fields) {
var text = '$$' + fields[i].name + '$$';
var data = review[fields[i].name];
copyBody.replaceText(text, data);
}
copyDoc.saveAndClose();
}
As Morfinismo noticed you are getting the error because you are trying to pass complex object from client to server and serializer fails to handle it. In order to fix that you need to adjust your code:
// onClick button's event handler (client script)
function onPrintClick(button) {
var reviewKey = button.datasource.item._key;
google.script.run
.withSuccessHandler(function() { /* TODO */ })
.withFailureHandler(function() { /* TODO */ })
.printReview(reviewKey);
}
// server script
function printReview(reviewKey) {
var review = app.models.Review.getRecord(reviewKey);
...
}
I can't seem to get the Firebase QuickStart to work for me. I'm pretty sure that I followed all of the instructions correctly. But I'm not getting access to write to the database. And according to the instructions the database is currently open to the public with the suggest rules change displayed below. Has anybody ever gone through this tutorial before? I'd like a hint as to what to do next.
Here's the code.
function writeDataToFirebase() {
var ss = SpreadsheetApp.openById("1rV2_S2q5rcakOuHs2E1iLeKR2floRIozSytAt2iRXo8");
//var ss = SpreadsheetApp.getActive();//I tried running it with data in this spreadsheet copied from the recommended source
var sheet = ss.getSheets()[0];
//var sheet = ss.getActiveSheet();
var data = sheet.getDataRange().getValues();
var dataToImport = {};
for(var i = 1; i < data.length; i++) {
var firstName = data[i][0];
var lastName = data[i][1];
dataToImport[firstName + '-' + lastName] = {
firstName:firstName,
lastName:lastName,
emailAddress:data[i][2],
country:data[i][4],
department:data[i][5],
weight:data[i][6],
birthDate:data[i][7]
};
}
var firebaseUrl = "https://script-examples.firebaseio.com/";
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
base.setData("", dataToImport);
}
The Data:
This is my library dialog:
This is the error I keep on getting:
This name of my script in the FireBase Site:
This is the access rules change:
So I guess I missed an important point. The url of firebase database has your projectId in it. So the url would be something like https://' + projectId + '.firebaseio.com'. Sorry, to bother everyone with such a simple question.