Connecting Google Apps Script to a Parse Database (ScriptDB Replacement) - google-apps-script

With the recent news that ScriptDB is being deprecated, I'm searching for a suitable replacement. My particular use case is that I'm running Google Apps Script under Google Forms to process and store data relevant to the function of the form.
I've been through Google's migration guide (link), and I'm trying to connect a Google Apps Script running under a Google Form to a Parse Database (link). I've tried both methods listed on the migration guide (URL Fetch Service and ParseDB Library), and I can't get either to work correctly. I was able to write to the Parse Database using ParseDB, but the query function isn't working as expected. I also tried using the parseCom library from the Excel Liberation site (sorry, I'm out of links for this post, apparently), but that didn't work very well either.
I'm most interested in using Google's URL Fetch Service to connect to a Parse database, as that seems to be my most flexible option (i.e. to let me share data between forms - something I really couldn't do with scriptDB), but I feel like I'm in over my head just a bit. I'm open to other options as well. Thanks in advance!

I have the same 'query' problem using the MongoLab database as overstack-asked here. I thought Parse might work as an alternative, but then I saw this post having the same 'query' problem.
If you run your query from the browser directly to Parse it will probably work fine, just as it does for me using the MongoLab database.
Consequently, I strongly suspect the problem is in the URLFetchApp.fetch() function itself, not the Parse (nor MongoLab) database functions.

Here's what I have found to be the best way to query results using UrlFetchApp and Parse.
function query(key,value) {
var properties = getKeys();
var appId = properties.appId;
var restApi = properties.restApi;
var class = 'TestObject';
var url = 'https://api.parse.com/1/classes/' + class;
var query = 'where={"' + key + '":"' + value + '"}'
var encoded = encodeURIComponent(query);
var queryUrl = url + '?' + encoded;
var options = {
"method" : "get",
"headers" : {
"X-Parse-Application-Id": appId,
"X-Parse-REST-API-Key": restApi,
}
}
var data = UrlFetchApp.fetch(queryUrl, options);
return data;
}

I am also looking for this.
As a temp store ScriptDb has been very helpful. Sheets simply does not have the elegance of beautiful coding as in ScriptDb

Related

How to authenticate with Blockfrost.io API?

So I'm trying to import Cardano Blockchain data like address balance, amount staked, rewards etc into a Google Sheet. I found this project named Blockfrost.io which is an API for accessing Cardano blockchain info and import it into apps etc.
I think I can use this with Google Sheets. Problem is I don't know how to authenticate. I've searched all around on the documentation and it's not clear to me. It seems it's possible if your're building an app or using the terminal.
But I just want to authenticate in the easiest way possible like in the browser address bar that way it would be simple to get the JSON with the info I need and import the info to Google Sheets.
This is where it mentions the Authentication:
https://docs.blockfrost.io/#section/Authentication
I already have an API key to access. But how do I authenticate?
So if I want to check the blockchain metrics (mainnet1234567890 is a dummy key, I won't use mine here):
https://cardano-mainnet.blockfrost.io/api/v0/metrics/project_id:mainnet1234567890
The JSON will still output this:
status_code 403
error "Forbidden"
message "Missing project token. Please include project_id in your request."
Is there a correct way to authenticate on the browser address bar?
It's not clear which BlockFrost API you are using Go JavaScript etc...
the API key goes in as a header on the request object. I was manually trying to connect to the service and found for a request is what I had to do in C#...
var aWR = System.Net.WebRequest.Create(url);
aWR.Method = "GET";
aWR.Headers.Add("project_id", "mainnetTheRestOfMyKeyIsHidden");
var webResponse = aWR.GetResponse();
var webStream = webResponse.GetResponseStream();
var reader = new StreamReader(webStream);
var data = reader.ReadToEnd();
Later I realized I wanted to use their API cause they implement the rate limiter, something I would rather use than build... I use the following with the BlockFrost API in c#
const string apiKey = "mainnetPutYourKeyHere";
const string network = "mainnet";
// your key is set during the construction of the provider.
ServiceProvider provider = new ServiceCollection().AddBlockfrost(network, apiKey).BuildServiceProvider();
// from there individual services are created
var AddressService = provider.GetRequiredService<IAddressesService>();
// The call to get the data looked like
AddressTransactionsContentResponseCollection TXR = await AddressService.GetTransactionsAsync(sAddress, sHeightFrom, sHeightTo, 100, iAddressPage, ESortOrder.Desc, new System.Threading.CancellationToken());
// etc. your gonna need to set the bounds above in terms of block height
Try using postman and include the "project_id" header with api key as the value like this - it will clear up the concept for you I think:enter image description here

Published google script link with Google Sheet returning printing empty variable

Since I tend to ramble, first a short version and if you need more information read the long one.
TL;DR
Why is this:
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
Browser.msgBox(jobsCreated);
var params = JSON.stringify({number:jobsCreated});
return ContentService.createTextOutput(params);
}
returning this when I published as website and then open:
{"number":""}
when it should look more like this {"number":2451}
Full Version:
First of all, I learned to program back in uni for my Computer science degree (10 years ago) but since then I haven't done much programming so I am basically a newbie.
Now to the question. I have a very simple script that a created with the script editor from Google Sheets
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
Browser.msgBox(jobsCreated);
var params = JSON.stringify({number:jobsCreated});
return ContentService.createTextOutput(params);
}
First I get the sheet I am working on
Then I select a cell from that sheet
now if I use a msgBox to make sure that I have the right number and run the script, it works and it shows the message.
next, I format the variable as JSON and finally I just create a text output.
Now I deploy as Web app
Execute as ME
Anyone, even anonymous
And when I access the website I can only see this:
{"number":""}
If I change the code and give jobsCreated and static value it works fine
var jobsCreated = 100;
{"number":100}
So my conclusion is that the problem is with accessing the value of the cell when running the script from the published link compare to running it directly from the editor, but I have no idea how to fix this.
A little bit more information, i am trying to use this for a counter called Smiirl, i got most of the information from here
https://medium.com/#m_nebra/bootstrapping-your-company-counter-22f5d4bc7dd4
try this:
function doGet(e) {
return ContentService.createTextOutput(Utilities.formatString('number: %s',SpreadsheetApp.getActiveSheet().getRange(12,2).getValue());
}
Ok as I replied to #Aerials, thank you again for your help btw. Seeing other codes that should work not working with my script, I decided to create a new sheet and script as a test and with the exact same code it works.
But now checking on it a little bit more, something that I didn't think it was a problem since it was getting the number without any problems. The cell it's being populated by a GoogleAnalytics add-on. Now when setting up the add-on again to get the information the script from the website returns an empty value again. SO it seems the issue is with the script getting the information from the sheet (only the published version) when its being populated by the add on
Your issue is in the use of JSON.stringify
In JSON, functions are not allowed as object values.
The JSON.stringify() function will omit or change to null any functions from a JavaScript object.
That said, you can do the following:
function doGet(e){
// Get the value of the range
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
// JSON Stringify it
var params = JSON.stringify({"number" : number=jobsCreated});
// Return JSON string
return ContentService.createTextOutput(params);
}
Returns:
{"number":123} if jobsCreated is the number 123, or
{"number":"Mangos"} if jobsCreated is the string "Mangos".
See it the script deployed here, and the sheet to play with.
Note:
You should avoid using functions in JSON, the functions will lose their scope, and you would have to use eval() to convert them back into functions.

Google App Script request validation on server

I am developing an add-on for Google Docs and I want to make POST request to my web server from add-on. I have already done that, but how should I validate on server-side that the request is coming from my add-on only? Is there csrf like mechanism in Google App Script? If not, any workaround to it?
There is a direct method in Apps Script to get UUID : Utilities.getUuid()
Reference : https://developers.google.com/apps-script/reference/utilities/utilities#getuuid
For memory previous answer below.
There is not mechanism for that but the best way is to add in the post request a specific key. Like API key in Google, example : 94e631ba-9916-4490-a084-cde08dcc0757
For generating a key example here : https://codepen.io/corenominal/pen/rxOmMJ
Adapted code below :
function generateUUID()
{
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)
{
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
}
Then on your server your check this value. If API Key is valid you perform the request if not you return a 403.
If you want you can implement an OAuth flow to connect to your server like Google do for its API but from my point of view it is faster to use an API key. If you combine 2 key like the one above probability to find it is near 0.
Stéphane

Need help parsing through a page & getting all comments found into an Array using a RegEx pattern in Google Apps Script?

My problem lies in 2 parts however I'm hoping solving 1 will fix the other. I've been trying to parse through a page and get all the comments found within a forum thread.
The comments are found using a RegEx pattern and the idea is that whatever lies in the comment will be read into an array until there aren't any more comments left. Each comment div follows this format
<div id="post_message_480683" style="margin-right:2px;"> something </div>
I'm trying to locate up to "post_message_[some number]" since each number seems to be generated randomly and then get whatever is between that particular div. My 1st problem is my RegEx just doesn't seem to be working I've tried a few but none yielded any results (except for when I insert the post message no. in manually), Here's the code so far:
function GetPosts() {
var posts = new Array(60);
var url = "http://forums.blackmesasource.com/showthread.php?p=480683";
var geturl = UrlFetchApp.fetch(url).getContentText().toString();
var post_match = geturl.match(/<div id="post_message_(.+)" style="margin-right:2px;">(\w.+)<\/div>/m);
Logger.log(post_match);
}
Edit: I initially tried getting this info via GAS's Xml.Parse() class but after grabbing the URL I just didn't know what to do since suffixing
.getElement().getElement('div') (I also tried .getElements('div') and other variations with 'body' & 'html')
would cause an error. Here is the last code attempt I tried before trying the RegEx route:
function TestArea() {
var url = "http://forums.blackmesasource.com/showthread.php?p=480683";
var geturl = UrlFetchApp.fetch(url).getContentText().toString();
//after this point things stop making sense
var parseurl = Xml.parse(geturl, true);
Logger.log(geturl);
//None of this makes sense because I don't know HOW!
//The idea: Store each cleaned up Message Div in an Array called posts
//(usually it's no more than 50 per page)
//use a for loop to write each message into a row in GoogleSpreasheet
for (var i = 0; i <= parseurl - 1; i++) {
var display = parseurl[i];
Logger.log(parseurl); }
}
Thanks for reading!
In general like the comment points out - be aware of parsing HTML with RegEx.
In my past personal experience, I've used Yahoo's YQL platform to run the HTML through and using XPath on their service. Seems to work decently well for simple reliable markup. You can then turn that into a JSON or XML REST service that you can grab via UrlFetch and work on that simplified response. No endorsement here, but this might be easier than to bring down the full raw HTML into Google Apps Script. See below for the YQL Console. I also don't know what their quotas are - you should review that.
Of course, the best course is to convince the site owner to provide an RSS feed or an API.

Changing the state of apps script webapp

I'm making standalone web app in Google Apps Script. I have somekind of task flow in the app. First search something from spreadsheet, then do some selections what to do with the collected data and after that fill spreadsheet row with some userinputs. So I need to run between few states.
I'm fairly sure I don't know enough about web tech so this might be very stupid question but here it goes.
I know that the e.parameters comes from the url like this www.google.com&value=helloworld
, e.parameters.value returns helloworld.
So now the problem: How do I set parameter e values in doGet(e) and call this same page or even different page/script in same script project? In otherwords how do I call self&value=helloworld ?
Is there some other way to do this? In GWT I just store everything to database and keep session in cookies. But cookies are not allowed so how to keep the state of the webapp in google apps script?
EDIT: This is how I pass the parameters for doGet(e).
function doGet(e) {
var app = UiApp.createApplication();
var newValue = 0;
if(e.parameter == undefined || e.parameter.value == undefined){
newValue = 1;
}else{
newValue = 1+parseInt(e.parameter.value);
}
var link = 'https://script.google.com/a/macros/domain.com/s/<insertidhere>/dev';
var anchor = app.createAnchor('Next',link+'?&value='+newValue);
anchor.setTarget('_self');
app.add(anchor);
var label = app.createLabel(newValue);
app.add(label);
return app;
}
The link format has changed a bit so instead of concatenate &value=... you need to add ? first like this ?&value1=helloworld&value2=....
Failing to use ? led me to think that there is bug and I need to use old format for the link and using that old format forbit any other than the script owner using it.
Hopefully this helps somebody to solve similar problems.
You've almost answered yourself. Append the URL paramenters to the end of your web app's URL and you can access them in your script.
Let's say you have a URL like
http://script.google.com/.../exec
When you hit the URL
http://script.google.com/.../exec?value=helloworld
,
inside doGet, you can read these URL parameters as
e.parameter.value;
IMO - instead of using ? to separate multiple parameters try keeping all the values in a single anchor with some other character separator of your choice such as # since after the first ? its possibly ignoring the other one and everything afterwards. Would be great to hear back on what you find.