diff --git a/README.md b/README.md index ea34663..44f1782 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,42 @@ data = { db.update(data) ``` +#### Conditional Requests + +It's possible to do conditional sets and removes by using the `conditional_set()` and `conitional_remove()` methods respectively. You can read more about conditional requests in Firebase [here](https://firebase.google.com/docs/reference/rest/database/#section-conditional-requests). + +To use these methods, you first get the ETag of a particular path by using the `get_etag()` method. You can then use that tag in your conditional request. + +```python +etag = db.child("users").child("Morty").get_etag() +data = {"name": "Mortimer 'Morty' Smith"} +db.child("users").child("Morty").conditional_set(data, etag["ETag"]) +``` + +If the passed ETag does not match the ETag of the path in the database, the data will not be written, and both conditional request methods will return a single key-value pair with the new ETag to use of the following form: + +```json +{ "ETag": "8KnE63B6HiKp67Wf3HQrXanujSM=", "value": "" } +``` + +Here's an example of checking whether or not a conditional removal was successful: + +```python +etag = db.child("users").child("Morty").get_etag() +response = db.child("users").child("Morty").conditional_remove(etag["ETag"]) +if type(response) is dict and "ETag" in response: + etag = response["ETag"] # our ETag was out-of-date +else: + print("We removed the data successfully!") +``` +Here's an example of looping to increase age by 1: + +```python +etag = db.child("users").child("Morty").child("age").get_etag() +while type(etag) is dict and "ETag" in etag: + new_age = etag["value"] + 1 + etag = db.child("users").child("Morty").child("age").conditional_set(new_age, etag["ETag"]) +``` ### Retrieve Data diff --git a/pyrebase/pyrebase.py b/pyrebase/pyrebase.py index f7170c1..b7169c2 100644 --- a/pyrebase/pyrebase.py +++ b/pyrebase/pyrebase.py @@ -387,41 +387,50 @@ def sort(self, origin, by_key, reverse=False): return PyreResponse(convert_to_pyre(data), origin.key()) def get_etag(self, token=None, json_kwargs={}): - request_ref = self.build_request_url(token) - headers = self.build_headers(token) - # extra header to get ETag - headers['X-Firebase-ETag'] = 'true' - request_object = self.requests.get(request_ref, headers=headers) - raise_detailed_error(request_object) - return request_object.headers['ETag'] + request_ref = self.build_request_url(token) + headers = self.build_headers(token) + # extra header to get ETag + headers['X-Firebase-ETag'] = 'true' + request_object = self.requests.get(request_ref, headers=headers) + raise_detailed_error(request_object) + return { + 'ETag': request_object.headers['ETag'], + 'value': request_object.json() + } def conditional_set(self, data, etag, token=None, json_kwargs={}): - request_ref = self.check_token(self.database_url, self.path, token) - self.path = "" - headers = self.build_headers(token) - headers['if-match'] = etag - request_object = self.requests.put(request_ref, headers=headers, data=json.dumps(data, **json_kwargs).encode("utf-8")) + request_ref = self.check_token(self.database_url, self.path, token) + self.path = "" + headers = self.build_headers(token) + headers['if-match'] = etag + request_object = self.requests.put(request_ref, headers=headers, data=json.dumps(data, **json_kwargs).encode("utf-8")) - # ETag didn't match, so we should return the correct one for the user to try again - if request_object.status_code == 412: - return {'ETag': request_object.headers['ETag']} + # ETag didn't match, so we should return the correct one for the user to try again + if request_object.status_code == 412: + return { + 'ETag': request_object.headers['ETag'], + 'value': request_object.json() + } - raise_detailed_error(request_object) - return request_object.json() + raise_detailed_error(request_object) + return request_object.json() def conditional_remove(self, etag, token=None): - request_ref = self.check_token(self.database_url, self.path, token) - self.path = "" - headers = self.build_headers(token) - headers['if-match'] = etag - request_object = self.requests.delete(request_ref, headers=headers) - - # ETag didn't match, so we should return the correct one for the user to try again - if request_object.status_code == 412: - return {'ETag': request_object.headers['ETag']} - - raise_detailed_error(request_object) - return request_object.json() + request_ref = self.check_token(self.database_url, self.path, token) + self.path = "" + headers = self.build_headers(token) + headers['if-match'] = etag + request_object = self.requests.delete(request_ref, headers=headers) + + # ETag didn't match, so we should return the correct one for the user to try again + if request_object.status_code == 412: + return { + 'ETag': request_object.headers['ETag'], + 'value': request_object.json() + } + + raise_detailed_error(request_object) + return request_object.json() class Storage: