Reputation: 11
I'm trying to use micropython-firebase-firestore library on my tiny Raspberry Pi Pico W microcontroller. And I have an issue with writing data on the Firestore database. I created the Firestore database and configured auth parameters. When I create the code for auth and read, I can successfully read data from Firestore database. But when I want to create new field or change the fields with firestore.get or firestore.patch command, I got an error on my code. I put the error message and my code below. Please help me about the solving this problem.
My Code:
import os
import network
import time
import ufirestore as firestore
from ufirestore.json import FirebaseJson
from firebase_auth import FirebaseAuth
from firebase_auth.firebase_auth import AuthSession
SSID = "WIFI"
password = "PASS"
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
wlan.connect(SSID, password)
print("Connecting to Wifi", end="...")
while not wlan.isconnected():
print(".", end="")
time.sleep(1)
print()
print("Wifi SSID: ", SSID , "IP: ", wlan.ifconfig()[0])
firestore.set_project_id("PROJECT_ID")
auth = FirebaseAuth("API_KEY")
print("Auth complete!")
auth.sign_in("MAIL", "PASS")
print("Sign in complete!")
print(auth.user)
firestore.set_access_token(auth.session.access_token)
print("Access token created!")
#====================================================================================
raw_doc = firestore.get("collection1/document1")
print("raw_doc : ", raw_doc)
doc = FirebaseJson.from_raw(raw_doc)
if doc["fields"].exists("value0"):
print("The field value is: %s" % doc["fields"].get("value0"))
else:
print("No Data!")
#====================================================================================
#============================ISSUE============================================
doc2 = FirebaseJson()
doc2.set("value1/stringValue", "Hello World!")
response = firestore.patch("collection1/document1", doc2, ["value1"], False)
print("Updated!")
#============================ISSUE============================================
auth.sign_out()
Error Messages:
Traceback (most recent call last):
File "<stdin>", line 50, in <module>
File "/lib/ufirestore/ufirestore.py", line 212, in patch
File "/lib/ufirestore/ufirestore.py", line 75, in patch
File "/lib/ufirestore/ufirestore.py", line 51, in send_request
File "urequests.py", line 104, in request
AssertionError:
I tried many ways to solve problem like changing the inside of "doc2.set()" and firestore.patch()
Upvotes: 1
Views: 394
Reputation: 66
This has to do with the cloud firestore API no longer aligning with what was written in ufirestore.py.
The current PATCH API is here, for example:
https://firebase.google.com/docs/firestore/reference/rest/v1beta1/projects.databases.documents/patch
Based on this, you can apply the following to changes to ufirestore.py to achieve this:
def send_request(path, method="GET", params=dict(), data=None, dump=True):
headers = {}
if FIREBASE_GLOBAL_VAR.ACCESS_TOKEN:
headers["Authorization"] = "Bearer " + FIREBASE_GLOBAL_VAR.ACCESS_TOKEN
if method in ["POST", "PATCH", "PUT"]:
# headers["Accept"] = "application/json"
# headers["Content-Type"] = "application/json"
response = urequests.request(method, path, data=None, json=data, headers=headers)
else:
response = urequests.request(method, path, headers=headers)
if dump == True:
if response.status_code < 200 or response.status_code > 299:
print(response.text)
raise FirestoreException(response.reason, response.status_code)
jsonResponse = response.json()
if jsonResponse.get("error"):
error = jsonResponse["error"]
code = error["code"]
message = error["message"]
raise FirestoreException(message, code)
return jsonResponse
class INTERNAL:
def patch(DOCUMENT_PATH, DOC, cb, update_mask=None):
PATH = construct_url(DOCUMENT_PATH)
LOCAL_PARAMS = to_url_params() + "&".join([f"updateMask.fieldPaths={field}" for field in update_mask])
DATA = DOC.process() # name in here has been deprecated I believe
# https://firebase.google.com/docs/firestore/reference/rest/v1beta1/projects.databases.documents/patch
# name is added as a part of the path
# updateMask is a query parameter
LOCAL_OUTPUT = send_request(PATH+LOCAL_PARAMS, "PATCH", data=DATA)
if cb:
try:
return cb(LOCAL_OUTPUT)
except:
raise OSError(
"Callback function could not be executed. Try the function without ufirestore.py callback.")
return LOCAL_OUTPUT
def create(COLLECTION_PATH, DOC, cb, document_id=None):
PATH = construct_url(COLLECTION_PATH)
PARAMS = {"documentId": document_id}
if document_id is not None:
LOCAL_PARAMS = to_url_params(PARAMS)
else:
LOCAL_PARAMS = ""
# DATA = DOC.process(get_resource_name(PATH))
DATA = DOC.process()
LOCAL_OUTPUT = send_request(PATH+LOCAL_PARAMS, "POST", PARAMS, DATA)
if cb:
try:
return cb(LOCAL_OUTPUT)
except:
raise OSError(
"Callback function could not be executed. Try the function without ufirestore.py callback.")
return LOCAL_OUTPUT
This assumes you're using https://github.com/WoolDoughnut310/micropython-firebase-firestore (or https://pypi.org/project/micropython-firebase-firestore/)
Because I ran into the same issues with it and these are the fixes I applied to get it to work (on an ESP32, although I think the only version sensitive thing here is the urequest API which seems to have changed slightly since the library was originally written).
Note that one also has to change the json.py file that is packaged with ufirestore.py to define FirebaseJson(...).process() to take no arguments, as this information is not being passed here anymore in the API call. This looks like the following:
def process(
self,
# name,
):
return {
# "name": name,
"fields": self.data
}
When you do a CREATE call, this also takes the documentId as a url parameter, not as part of the JSON data, but if left blank will still create one for you, this can also be found here: How to set the Doc Id with Firebase REST API
The following is roughly how one uses the API directly per the above Google documentation (using the send_request utility as it attaches the auth headers you get from the firebase_auth library)
res = ufirestore.send_request(
f"https://firestore.googleapis.com/v1/projects/{PROJECT_NAME}/databases/(default)/documents/devices/{DOCUMENT_ID}?updateMask.fieldPaths=watchdog&updateMask.fieldPaths=deviceTemp",
method="PATCH",
data={
"fields": {
"watchdog": {"integerValue": watchdog},
"deviceTemp": {"doubleValue": 24.56},
"displayName": {"stringValue": "hello"},
}
},
)
But as you can see, certain arguments have been moved to query parameters within the URL, others remain json data (in a PATCH method REST request for patch, not POST as currently configured by the library)
You can also play around with the REST API on Google's site (it will handle auth for you) and use urequests directly to build the REST request (it will be similar to the above, but you will need to add the auth as handled by the send_request function)
Upvotes: 0