Google Spreadsheet URL with Query Does not support callback? - google-apps-script

I am trying to develop an application where I am going to fetch data from Google Spreadsheet on some conditions. I use following URL to fetch data. The data comes in JSON and it work perfectly if I am entering the URL in address bar.
https://docs.google.com/spreadsheet/tq?tqx=out:json&tq=select%20*%20%20where%20B%20%3D%20%27&KEY=MY_SPREADSHEET_KEY
When I am going to fetch it from JavaScript using callback (JSONP) it does it receive data.
var resource = document.createElement('script');
resource.type = 'text/javascript';
resource.async = true;
var spreadsheetkey = "MY_SPREADSHEET_KEY";
var url="http://www.example.com";
resource.src = "https://docs.google.com/spreadsheet/tq?tqx=out:json&tq=select%20*%20%20where%20B%20%3D%20%27"+url+"%27&key="+spreadsheetkey+"&format=json&callback=getReply";
var script = document.getElementsByTagName('script')[0];
script.parentNode.insertBefore(resource, script);
function getReply(data){
alert(data);
}
I am not getting any alert which I supposed to. Can anybody tell me what might be the problem.

For unauthenticated calls to the spreadsheet feed you need to make your spreadsheet public.

Related

Is GAS/google sheet an alternative option to PHP/mySQL?

I keep finding forum results that refer to the google visualiser for displaying my query results. But it just seems to dump the data in a pre-made table. I haven't found a way to write my own custom table dynamically.
In the past, when I have hacked together PHP to make use of mySQL DB, I would simply connect to my DB, run a query into an array, then cycle through the array and write the table in HTML. I could do IF statements in the middle for formatting or extra tweaks to the displayed data, etc. But I am struggling to find documentation on how to do something similar in a google script. What is the equivalent work flow? Can someone point me to a tutorial that will start me down this path?
I just want a simple HTML page with text box and submit button that runs a query on my Google sheet (back in the .gs file) and displays the results in a table back on the HTML page.
Maybe my understanding that GAS/google sheets is an alternative to PHP/mySQL is where I'm going wrong? Am I trying to make a smoothie with a toaster instead of a blender?
Any help appreciated
Welcome David. Ruben is right, it's cool to post up some code you have tried. But I spent many months getting my head around Apps-script and love to share what I know. There are several ways to get the data out of a Google sheet. There is a very well document Spreadsheet Service For GAS. There is also a client API..
There is the approach you mention. I suggest converting the incoming data to JSON so you can do with it as you like.
Unauthenticated frontend queries are also possible. Which needs the spreadsheet to be published and set to anyone with the link can view, and uses the Google visualisation API
var sql = 'SELECT A,B,C,D,E,F,G where A = true order by A DESC LIMIT 10 offset '
var queryString = encodeURIComponent(sql);
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/'+ spreadsheetId +'/gviz/tq?tq=' + queryString);
query.send(handleSampleDataQueryResponse);
function handleSampleDataQueryResponseTotal(responsetotal) {
var myData = responsetotal.getDataTable();
var myObject = JSON.parse(myData.toJSON());
console.log(myObject)
}
Other Approaches
In your GAS back end this can get all of your data in columns A to C as an array of arrays ([[row1],[row2],[row3]]).
function getData(query){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange('A1:C');
var data = range.getValues();
// return data after some query
}
In your GAS front end this can call to your backend.
var data = google.script.run.withSuccessHandler(success).getData(query);
var success = (e) => {
console.log(e)
}
In your GAS backend this can add data to your sheet. The client side API will also add data, but is more complex and needs authentication.
function getData(data){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange('A1:C');
var data = range.setValues([[row1],[row2],[row3]]);
return 'success!'
}
In your GAS frontend this can send data to your backend.
var updateData = [[row1],[row2],[row3]]
var data = google.script.run.withSuccessHandler(success).getData(updateData);
var success = (e) => {
console.log(e)
}
Finally
Or you can get everything from the sheet as JSON and do the query in the client. This works okay if you manipulate the data in the sheet as you will need it. This also needs the spreadsheet to be published and set to anyone with the link can view.
var firstSheet = function(){
var spreadsheetID = "SOME_ID";
var url = "https://spreadsheets.google.com/feeds/list/" + spreadsheetID +"/1/public/values?alt=json";
return new Promise((resolve,reject)=>{
$.getJSON(url, (data)=>{
let result = data.feed.entry
resolve(result)
});
})
}
firstSheet().then(function(data){
console.log(data)
})

How to pull all user sharing activity using the Activity API inside App Script

I am new to Google API and got training wheels on for the App Script.
I need to know if there is a way to utilize the Activity API or any other inside App Script to pull sharing activity for all domain users.
Thanks in advance.
I got the following working for me but it is not showing me the "shared_externally" in particular, I need to use https://developers.google.com/admin-sdk/reports/v1/reference/activities/list api for all users on drive app, externally and internally shared filter.
function listShares() {
var userKey = 'all';
var applicationName = 'drive';
var optionalArgs = {
visibility: 'shared_externally'
};
var response = AdminReports.Activities.list(userKey, applicationName,
optionalArgs)
Logger.log(response);
}
My output is the same with or without the optionalArgs for shared_externally, can someone help with the proper syntax for the visibility arguments?
Ok I got it to work, yay.
function listShares() {
var userKey = 'all';
var applicationName = 'drive';
var optionalArgs = {
filters: 'visibility==shared_externally'
};
var response = AdminReports.Activities.list(userKey, applicationName,
optionalArgs)
Logger.log(response);}

Missing publish to Chrome Web Store option for web application

I'm trying to make an inbox-listener element to automatically pull message content with a specific subject line into a Google Spreadsheet for reporting. I'm hoping this GitHub Project will do the trick, and I've made the Google Apps Script project that should handle it. Part of the process is to verify the project ownership by publishing it as draft on the Chrome web store. According to Google's documentation, that should be under the publish function, but I don't see it there, or anywhere in the script editor. Could anyone tell me what I'm doing wrong, or if this feature is disabled?
Here is what the menu looks like in my IDE.
Adapted from a OP's comment to the accepted answer
The script is definitely not contained in a Google Doc or Spreadsheet, but it is in a workspace tied to a Google Site. Interestingly, I've tried following the link in the KENdi's answer to edit Apps Scripts under my work account, and I'm told I don't have >permission to generate app scripts on that level. I'll send an email to my Admin to see if I can elevate privileges so I can publish through this channel.
GAS Code
main.gs
<meta name="google-site-verification" content="eA0WbBgImGB_wcsnSADjvwnCBaNyrSifyyxuNhHSXf8" />
var PROJECTID = 'api-project-...';
var WEBHOOK_URL = 'My custom project URL'
function doPost(e){
var postBody = JSON.parse(e.postData.getDataAsString());
var messageData = Utilities.newBlob(Utilities.base64Decode(postBody.message.data)).getDataAsString();
var ss = SpreadsheetApp.openById('...').getSheetByName("Log");
ss.appendRow([new Date(), messageData, JSON.stringify(postBody,undefined,2)])
return 200;
}
function setupPubSub(){
var newTopic = CreateTopic("mailTrigger");
newTopic.setIamPolicy(addGmailPolicy());
Logger.log(newTopic.getName());
var newSub = CreateSubscription("mailTrigger",newTopic.getName(),WEBHOOK_URL);
}
function disEnrollEmail(email){
var email = email || "me";
var res = UrlFetchApp.fetch("https://www.googleapis.com/gmail/v1/users/"+email+"/stop",{method:"POST",headers:{authorization:"Bearer "+ScriptApp.getOAuthToken()}});
Logger.log(res.getContentText());
}
function enrollEmail(email){
var email = email || "me";
PubSubApp.setTokenService(getTokenService())
var topicName = PubSubApp.PublishingApp(PROJECTID).getTopicName("mailTrigger")
Logger.log(watchEmail(topicName,{labelIds:["INBOX"], email:email}));
}
helper.gs
function addGmailPolicy(Policy){
return PubSubApp.policyBuilder()
[(Policy)?"editPolicy":"newPolicy"](Policy)
.addPublisher("SERVICEACCOUNT", 'gmail-api-push#system.gserviceaccount.com')
.getPolicy();
}
function addDomainSubs(Domain,Policy){
return PubSubApp.policyBuilder()
[(Policy)?"editPolicy":"newPolicy"](Policy)
.addPublisher("DOMAIN", Domain)
.getPolicy();
}
function getSubscriptionPolicy(){
return PubSubApp.policyBuilder()
.newPolicy()
.addSubscriber("DOMAIN","ccsknights.org")
}
function watchEmail(fullTopicName,watchOptions){
var options = {email:"me",token:ScriptApp.getOAuthToken(),labelIds:[]};
for(var option in watchOptions){
if(option in options){
options[option] = watchOptions[option];
}
}
Logger.log(options);
var url = "https://www.googleapis.com/gmail/v1/users/"+options.email+"/watch"
var payload = {
topicName: fullTopicName,
labelIds: options.labelIds
}
var params = {
method:"POST",
contentType: "application/json",
payload: JSON.stringify(payload),
headers:{Authorization: "Bearer "+ options.token
},
muteHttpExceptions:true
}
var results = UrlFetchApp.fetch(url, params);
if(results.getResponseCode() != 200){
throw new Error(results.getContentText())
}else{
return JSON.parse(results.getContentText());
}
}
function CreateTopic(topicName) {
var topic;
PubSubApp.setTokenService(getTokenService());
var pubservice = PubSubApp.PublishingApp(PROJECTID);
try{topic = pubservice.newTopic(topicName)}
catch(e){topic = pubservice.getTopic(topicName);}
return topic;
}
function CreateSubscription(subscriptionName,topicName,webhookUrl){
var sub;
PubSubApp.setTokenService(getTokenService());
var subService = PubSubApp.SubscriptionApp(PROJECTID);
try{sub = subService.newSubscription(subscriptionName,topicName,webhookUrl)}
catch(e){sub = subService.getSubscription(subscriptionName,topicName,webhookUrl)}
return sub;
}
function getTokenService(){
var jsonKey = JSON.parse(PropertiesService.getScriptProperties().getProperty("jsonKey"));
var privateKey = jsonKey.private_key;
var serviceAccountEmail = jsonKey.client_email;
var sa = GSApp.init(privateKey, ['https://www.googleapis.com/auth/pubsub'], serviceAccountEmail);
sa.addUser(serviceAccountEmail)
.requestToken();
return sa.tokenService(serviceAccountEmail);
}
function requestGmailScope_(){GmailApp.getAliases()}
The "Register in Chrome Web Store" option is only available in the standalone scripts.
Meaning, if this script is bound to a Google Sheets, Docs, Forms or scripts appear among your files in Google Drive, then this "Register in Chrome Web Store" is not available.
You can verify it by checking the publish in this "https://www.google.com/script/start/". This one will have an option of "Register in Chrome Web Store" in the publish.
While by checking the script editor of a spreadsheet, created in your Drive, the "Register in Chrome Web Store" will not be found in the Publish section.
Hope this information helps you.
It turns out I couldn't build Apps Scripts under my work account because my browser was defaulting to my personal Gmail account. Resetting the browser and connecting as my work account first fixed the problem. For future reference, you have to make your scripts under Script.Google.com in order for them to be shareable via the web store. You can bypass an authentication problems like this by resetting your browser history or running Chrome in incognito mode if you have to.
Great catch, KENDi!

Allow script to run as admin to update spreadsheet

I have a spreadsheet that is shared with some other users. Many of the cell are range protected. However, through a menu I allow a user to run a script (which access an external library therefore invisible and not in the control of the user) that will modify some of the protected ranges. However, the script throws that there is no permission to perform this operation.
Is there any option to have this library run with library 'admin' rights so it doesn't throws due to protection?
Thx!
According to the documentation : "the library does not have its own instance of the feature/resource and instead is using the one created by the script that invoked it. "
So library is not the way to go.
You can achieve that behavior using a standalone script that would run as a service (a doGet() function in a deployed webapp) that you would deploy as "running as you" and that you would call with parameters to tell it what to do on the target spreadsheet range.
Edit : in its most basic implementation you can use a simple script like this one as a server app :
function doGet(e) {
if(e.parameter.mode==null){return ContentService.createTextOutput("error, wrong request").setMimeType(ContentService.MimeType.TEXT)};
var coord = e.parameter.coord;
var mode = e.parameter.mode;
var value = e.parameter.value;
var ss = SpreadsheetApp.openById('11myX1YX_________________FS6BesaBEnQ');
var sh = ss.getSheetByName(e.parameter.sN);
if(mode=='r'){
var sheetValue = JSON.stringify(sh.getRange(coord).getValue());
var valToReturn = ContentService.createTextOutput(sheetValue).setMimeType(ContentService.MimeType.JSON);
return valToReturn;
}
if(mode=='w'){
sh.getRange(coord).setValue(value);
return ContentService.createTextOutput(value).setMimeType(ContentService.MimeType.JSON);
}
return ContentService.createTextOutput('error').setMimeType(ContentService.MimeType.TEXT);
}
The above script should be deployed with the following parameters :
Then you can use it with a simple urlFetch like below :
var url = "https://script.google.com/macros/s/AKfycbxs9M0ib-VRmmcVJ0UUJXmHITOrWcoG8bYrK4EK7Tvl0krzsYc/exec"
function testServerLink(){
var coord = 'A3';//coordinates in A1 notation
var sheetName = 'Sheet1';
var data = 'test value';
var mode = 'w';// w for "write" and r for "read"
var write = sheetService(mode,coord,sheetName,data);
Logger.log(write);//shows the result in logger
var read = sheetService('r','A1',sheetName,data);
Logger.log(read);//shows the value that was in A1 cell
}
function sheetService(mode,coord,sheetName,data){
Logger.log(url+"?mode="+mode+"&coord="+coord+"&sN="+sheetName+"&value="+data);// shows the actual url with parameters, can be tested in a browser
var result = UrlFetchApp.fetch(url+"?mode="+mode+"&coord="+coord+"&sN="+sheetName+"&value="+data);
return result
}

Google Apps Script Persistence

Background: I have a google site and I have been pulling information from a google spreadsheet containing the marks of my students, however I'd like to make it more dynamic so that they can request a report of all of their current marks whenever they'd like. In the script that I've written, students will enter a password, click a button and then their marks will be generated.
Issue: From what I've read, when they click the button, the handler for the button causes the script to be re-run. The current spreadsheet cannot be stored and when I try to access the spreadsheet, it tells me that it is null. How can I get access to the spreadsheet again? I've tried using ScriptProperties, but I got the same result. By the way, it works if I do not try to run it as a webapp.
Here's the doGet() function and part of the getPassword() function that is called once the button on the UI is pressed.
function doGet() {
var app = UiApp.createApplication();
app.add(app.loadComponent("MyGui"));
var panel = app.getElementById("VerticalPanel1");
var text = app.createPasswordTextBox().setName("text");
var handler = app.createServerHandler("getResults").addCallbackElement(text);
panel.add(text);
panel.add(app.createButton("Get Record", handler));
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
function getResults(eventInfo) {
var app = UiApp.createApplication();
var password = eventInfo.parameter.text;
var panel = app.getElementById("VerticalPanel1");
var textArea = app.createRichTextArea();
panel.add(textArea);
var pointsSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var passwordCheckRange = pointsSheet.getRange("B70:C94").getValues();
...
The problem probably is that when the script is run as a webapp theres no "activeSpreadSheet"
so
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
fails. An alternate aproach will be to pass the SpreadSheet id in hidden field to the call back.
In your doGet function:
var hidden = app.createHidden('ssId', 'YOUR_SS_ID_HERE');
panel.add(hidden);
var handler = app.createServerHandler("getResults").addCallbackElement(text);
handler.addCallbackElement(hidden)`
In your callback function
var ssID = eventInfo.parameter.ssId;
var pointsSheet = SpreadsheetApp.openById(ssID).getSheetByName('SHEET_NAME');