Firebase Demo:
No Security
with Web Component
INTRO
The aim here will be to show in some detail the various elements that are available through the REST API, then to bring this all together in a working demo.
The Main REST elements for Firebase Realtime Database
Just using the web component, there are four ways to handle data on a Firebase realtime database: POST, PUT, GET, and DELETE.
Throughout, I will be using a firebase URL of https://myProject.firebasio.com and a Project Bucket of FBDEMONOSEC, and the rules will be set as follows:
{
"rules": {
"FBDEMONOSEC": {
".read" : true,
".write": true
}
}
}
All "returns" will have passed through a jsonDecoder.
POST
By using POST, the web component will upload the provided data to Firebase, which will create a chronologically based node under which it places the data. Firebase will return the name of this node to the app. We do not see this using the experimental component, but it can have its uses.
The URL one would use is as follows: (/<tag> is optional, or could be many)
Url:
<firebaseUrl>/<projectBucket>/<tag>.json
PostText:
string/json
Example:
https://myProject.firebaseio.com/FBDEMONOSEC.json
Returns: [["name":"-MKe1Nm6qIBtih6e65XN"]]
If I was to keep clicking Button1, it would keep adding new entries to the projectBucket, with a different chronological code. You can send strings or lists.
PUT
By using PUT, the web component will upload the provided data to Firebase, and create a node, named after the "Tag", under the projectBucket. Doing a PUT to the same tag will overwrite the existing data completely. The developer needs to call in the data from that tag, amend it on app, then save it back out again. It can handle strings or lists.
The URL one would use is as follows: (/<tag> is optional, or could be many)
Url:
<firebaseUrl>/<projectBucket>/<tag>.json
PutText:
string/json
Example:
https://myProject.firebaseio.com/FBDEMONOSEC/myData1.json
Returns: [["myFirstData":"myFirstContent"]]
GET
By using GET, as you would expect, the web component sends a GET request to the specified Tag and returns the content. Calling a Tag on an upper level, or just the ProjectBucket, will return all the data held in the tags below it.
The URL one would use is as follows: (/<tag> is optional, or could be many)
Url:
<firebaseUrl>/<projectBucket>/<tag>.json
Get:
Example:
https://myProject.firebaseio.com/FBDEMONOSEC/myData1.json
Returns: [["myFirstData":"myFirstContent"]]
DELETE
By using DELETE, as you would expect, the web component sends a DELETE request to the specified Tag and deletes the content and the Tag. Without any rules, one could set this to the projectBucket or simply just the firebase URL and delete ALL the data therein. (Told you about rules.....)
The URL one would use is as follows: (/<tag> is optional, or could be many)
Url:
<firebaseUrl>/<projectBucket>/<tag>.json
Delete:
Example:
https://myProject.firebaseio.com/FBDEMONOSEC/myData1.json
Returns: null
GET (TAGS)
By using GET, with an additional parameter, the web component can return a summary of all the tags below a level, e.g. ProjectBucket. Here we use the "?shallow=true". These can then be stored in a list, by extracting the first item in each sub list, and used to select tags for action.
The URL one would use is as follows: (/<tag> is optional, or could be many)
Url:
<firebaseUrl>/<projectBucket>/<tag>.json?shallow=true
Get:
Example:
https://myProject.firebaseio.com/FBDEMONOSEC.json?shallow=true
Returns: [["-MKaSVOL6vZlsMM5CZ-m", true], ["-MKafCLF1Zp_8wL9krM0", true], ["-MKah9uqv-xyeiqTkN0X", true], ["-MKe1Nm6qIBtih6e65XN", true], ["Images", true], ["identify", true], ["myTag", true], ["suit", true], ["trucks", true]]
DATA CHANGED
Unfortunately, there is no REST method for handling data changed that can be handled through the Web component. A developer "could" store a local copy of everything on Firebase and keep comparing, but this would be bandwidth wasteful.
PATCH
PATCH is another method available in the Firebase REST API, the PATCH element is now available in the Web component. Developers can use PATCH to update and add content to a node/tag level. Trying to do the same thing with PUT just wipes out any existing information.
Example:
https://myProject.firebaseio.com/FBDEMONOSEC/Tom.json
Returns: [["0", false]]
The Firebase Storage URL requirements
As I understand it, we do not actually use any of the firebase SDK's to upload and download files from Firebase Storage, which forms a part of Google Cloud Storage. There is no REST API for Firebase Storage, and for Google Cloud Storage is somewhat incomplete for our purposes. Fortunately, there is enough information to go on out there in order to get the job done!
My examples will only use png image files, but it should be possible to use any file type. I have set up a folder called FBDEMONOSEC, and I am using "myProject.appspot.com" as the application ID.
UPLOAD A FILE
We will use the POST method in the web component, and we will also need to provide a web header identifying the content type (mimetype). The developer will need to know the full path to the file, and also to provide a filename for the file to be uploaded (best to give it an extension too)
Headers:
content-type : <file mime type>
Url:
https://firebasestorage.googleapis.com/v0/b/<storageAppID>/o/<projectBucket>%2F<filename>?alt=media
PostFile:
<full/path/to/filename>
Example:
content-type : image/png
https://firebasestorage.googleapis.com/v0/b/myProject.appspot.com/o/FBDEMONOSEC%2Fmyimage1.png?alt=media
PostFile:
/storage/emulated/0/Android/data/edu.mit.appinventor.aicompanion3/files/myimage1.png
Note: (something changed ?) the above blocks are showing a full path to the file for PostFile. It may be that you need a full path, e.g to include "file://" before the rest of the path shown if you get an error 1104 showing up.
Returns:
[["bucket", "myProject.appspot.com"], ["contentDisposition", "inline; filename*=utf-8''myimage1.png"], ["contentEncoding", "identity"], ["contentType", "image/png"], ["crc32c", "AlvaFg=="], ["downloadTokens", "69c52b42-32d1-40a0-9b98-574872eeed2c"], ["etag", "CO+Dx4SB1ewCEAE="], ["generation", "1603809785790959"], ["md5Hash", "ZGOY4M+44ZmWCTAHLvtCtQ=="], ["metageneration", "1"], ["name", "FBDEMONOSEC/myimage1.png"], ["size", "38106"], ["storageClass", "STANDARD"], ["timeCreated", "2020-10-27T14:43:05.790Z"], ["updated", "2020-10-27T14:43:05.790Z"]]
You will see to the left what is returned. We really only need two of these items, the downloadTokens and the name. These need to be captured on return and saved off into a list (or preferably up to a Firebase tag) so that they can be recalled later to display or download the image. You will note that the path returned, includes any folders.
DOWNLOAD/VIEW A FILE
We simply can use the exact same url again. When rules are "true", I have found that we do not actually need to use the "downloadTokens". However, for completeness, and for what is coming next (!) we will include the token in our url. The token is always offered as a part of the url when you click on it in the console. (Select file, look right and down, and hover mouse over the downloadToken for instructions)
Url:
https://firebasestorage.googleapis.com/v0/b/<storageAppID>/o/<projectBucket>%2F<filename>?alt=media&token=<downloadTokens>
Example:
https://firebasestorage.googleapis.com/v0/b/myProject.appspot.com/o/FBDEMONOSEC%2Fmyimage1.png?alt=media
&token=69c52b42-32d1-40a0-9b98-574872eeed2c
and here is the image from that url....
and below how we might load the image to an image component (without saving it as a file)
and the blocks for downloading a file to your device
DELETE
(note - worked this one out after writing the rest of this and making the video!)
Just use the HTTP Delete block with the file url and your file is gone!
Returns: there is nothing returned on Delete
DEMO
I have put together a demo app, which brings all of this together, and hopefully shows how it all can work. I used the FileTools extension from Sunny Gupta, to access files from the device, and to provide the mime type for the file upload. Other than that, a few buttons, a label, an image, and a single web component. The demo starts up by fetching a list of all the images that have been stored through the app to Firebase (having stored the names and tokens for each file in the database). The POST and PUT functions have been automated to make life easier. I added a "blank" to the top of the tags list, in case I want to return all the data inside FBDEMONOSEC.
That just about wraps up this part. We now know how to send and get data to and from Firebase using the web component. We also how to upload and download files with Firebase Storage, and how to delete them. But this is all done with read and write rules set to true. We now need to move on to setting up authenticated users and locking down the rules...