added firebase and rudimentary leaderboard support
This commit is contained in:
188
addons/godot-firebase/firestore/firestore_task.gd
Normal file
188
addons/godot-firebase/firestore/firestore_task.gd
Normal file
@@ -0,0 +1,188 @@
|
||||
## @meta-authors Nicolò 'fenix' Santilio, Kyle 'backat50ft' Szklenski
|
||||
## @meta-version 1.4
|
||||
##
|
||||
## A [code]FirestoreTask[/code] is an independent node inheriting [code]HTTPRequest[/code] that processes a [code]Firestore[/code] request.
|
||||
## Once the Task is completed (both if successfully or not) it will emit the relative signal (or a general purpose signal [code]task_finished()[/code]) and will destroy automatically.
|
||||
##
|
||||
## Being a [code]Node[/code] it can be stored in a variable to yield checked it, and receive its result as a callback.
|
||||
## All signals emitted by a [code]FirestoreTask[/code] represent a direct level of signal communication, which can be high ([code]get_document(document), result_query(result)[/code]) or low ([code]task_finished(result)[/code]).
|
||||
## An indirect level of communication with Tasks is also provided, redirecting signals to the [class FirebaseFirestore] module.
|
||||
##
|
||||
## ex.
|
||||
## [code]var task : FirestoreTask = Firebase.Firestore.query(query)[/code]
|
||||
## [code]var result : Array = await task.task_finished[/code]
|
||||
## [code]var result : Array = await task.result_query[/code]
|
||||
## [code]var result : Array = await Firebase.Firestore.task_finished[/code]
|
||||
## [code]var result : Array = await Firebase.Firestore.result_query[/code]
|
||||
##
|
||||
## @tutorial https://github.com/GodotNuts/GodotFirebase/wiki/Firestore#FirestoreTask
|
||||
|
||||
@tool
|
||||
class_name FirestoreTask
|
||||
extends RefCounted
|
||||
|
||||
## Emitted when a request is completed. The request can be successful or not successful: if not, an [code]error[/code] Dictionary will be passed as a result.
|
||||
## @arg-types Variant
|
||||
signal task_finished()
|
||||
|
||||
enum Task {
|
||||
TASK_GET, ## A GET Request Task, processing a get() request
|
||||
TASK_POST, ## A POST Request Task, processing add() request
|
||||
TASK_PATCH, ## A PATCH Request Task, processing a update() request
|
||||
TASK_DELETE, ## A DELETE Request Task, processing a delete() request
|
||||
TASK_QUERY, ## A POST Request Task, processing a query() request
|
||||
TASK_AGG_QUERY, ## A POST Request Task, processing an aggregation_query() request
|
||||
TASK_LIST, ## A POST Request Task, processing a list() request
|
||||
TASK_COMMIT ## A POST Request Task that hits the write api
|
||||
}
|
||||
|
||||
## Mapping of Task enum values to descriptions for use in printing user-friendly error codes.
|
||||
const TASK_MAP = {
|
||||
Task.TASK_GET: "GET DOCUMENT",
|
||||
Task.TASK_POST: "ADD DOCUMENT",
|
||||
Task.TASK_PATCH: "UPDATE DOCUMENT",
|
||||
Task.TASK_DELETE: "DELETE DOCUMENT",
|
||||
Task.TASK_QUERY: "QUERY COLLECTION",
|
||||
Task.TASK_LIST: "LIST DOCUMENTS",
|
||||
Task.TASK_COMMIT: "COMMIT DOCUMENT",
|
||||
Task.TASK_AGG_QUERY: "AGG QUERY COLLECTION"
|
||||
}
|
||||
|
||||
## The code indicating the request Firestore is processing.
|
||||
## See @[enum FirebaseFirestore.Requests] to get a full list of codes identifiers.
|
||||
## @setter set_action
|
||||
var action : int = -1 : set = set_action
|
||||
|
||||
## A variable, temporary holding the result of the request.
|
||||
var data
|
||||
var error: Dictionary
|
||||
var document: FirestoreDocument
|
||||
|
||||
var _response_headers: PackedStringArray = PackedStringArray()
|
||||
var _response_code: int = 0
|
||||
|
||||
var _method: int = -1
|
||||
var _url: String = ""
|
||||
var _fields: String = ""
|
||||
var _headers: PackedStringArray = []
|
||||
|
||||
func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
var bod = body.get_string_from_utf8()
|
||||
if bod != "":
|
||||
bod = Utilities.get_json_data(bod)
|
||||
|
||||
var failed: bool = bod is Dictionary and bod.has("error") and response_code != HTTPClient.RESPONSE_OK
|
||||
# Probably going to regret this...
|
||||
if response_code == HTTPClient.RESPONSE_OK:
|
||||
match action:
|
||||
Task.TASK_POST, Task.TASK_GET, Task.TASK_PATCH:
|
||||
document = FirestoreDocument.new(bod)
|
||||
data = document
|
||||
Task.TASK_DELETE:
|
||||
data = true
|
||||
Task.TASK_QUERY:
|
||||
data = []
|
||||
for doc in bod:
|
||||
if doc.has('document'):
|
||||
data.append(FirestoreDocument.new(doc.document))
|
||||
Task.TASK_AGG_QUERY:
|
||||
var agg_results = []
|
||||
for agg_result in bod:
|
||||
var idx = 0
|
||||
var query_results = {}
|
||||
for field_value in agg_result.result.aggregateFields.keys():
|
||||
var agg = data.aggregations[idx]
|
||||
var field = agg_result.result.aggregateFields[field_value]
|
||||
query_results[agg.keys()[0]] = Utilities.from_firebase_type(field)
|
||||
idx += 1
|
||||
agg_results.push_back(query_results)
|
||||
data = agg_results
|
||||
Task.TASK_LIST:
|
||||
data = []
|
||||
if bod.has('documents'):
|
||||
for doc in bod.documents:
|
||||
data.append(FirestoreDocument.new(doc))
|
||||
if bod.has("nextPageToken"):
|
||||
data.append(bod.nextPageToken)
|
||||
Task.TASK_COMMIT:
|
||||
data = bod # Commit's response is not a full document, so don't treat it as such
|
||||
else:
|
||||
var description = ""
|
||||
if TASK_MAP.has(action):
|
||||
description = "(" + TASK_MAP[action] + ")"
|
||||
|
||||
Firebase._printerr("Action in error was: " + str(action) + " " + description)
|
||||
build_error(bod, action, description)
|
||||
|
||||
task_finished.emit()
|
||||
|
||||
func build_error(_error, action, description) -> void:
|
||||
if _error:
|
||||
if _error is Array and _error.size() > 0 and _error[0].has("error"):
|
||||
_error = _error[0].error
|
||||
elif _error is Dictionary and _error.keys().size() > 0 and _error.has("error"):
|
||||
_error = _error.error
|
||||
|
||||
error = _error
|
||||
else:
|
||||
#error.code, error.status, error.message
|
||||
error = { "error": {
|
||||
"code": 0,
|
||||
"status": "Unknown Error",
|
||||
"message": "Error: %s - %s" % [action, description]
|
||||
}
|
||||
}
|
||||
|
||||
data = null
|
||||
|
||||
func set_action(value : int) -> void:
|
||||
action = value
|
||||
match action:
|
||||
Task.TASK_GET, Task.TASK_LIST:
|
||||
_method = HTTPClient.METHOD_GET
|
||||
Task.TASK_POST, Task.TASK_QUERY, Task.TASK_AGG_QUERY:
|
||||
_method = HTTPClient.METHOD_POST
|
||||
Task.TASK_PATCH:
|
||||
_method = HTTPClient.METHOD_PATCH
|
||||
Task.TASK_DELETE:
|
||||
_method = HTTPClient.METHOD_DELETE
|
||||
Task.TASK_COMMIT:
|
||||
_method = HTTPClient.METHOD_POST
|
||||
_:
|
||||
assert(false)
|
||||
|
||||
|
||||
func _merge_dict(dic_a : Dictionary, dic_b : Dictionary, nullify := false) -> Dictionary:
|
||||
var ret := dic_a.duplicate(true)
|
||||
for key in dic_b:
|
||||
var val = dic_b[key]
|
||||
|
||||
if val == null and nullify:
|
||||
ret.erase(key)
|
||||
elif val is Array:
|
||||
ret[key] = _merge_array(ret.get(key) if ret.get(key) else [], val)
|
||||
elif val is Dictionary:
|
||||
ret[key] = _merge_dict(ret.get(key) if ret.get(key) else {}, val)
|
||||
else:
|
||||
ret[key] = val
|
||||
return ret
|
||||
|
||||
|
||||
func _merge_array(arr_a : Array, arr_b : Array, nullify := false) -> Array:
|
||||
var ret := arr_a.duplicate(true)
|
||||
ret.resize(len(arr_b))
|
||||
|
||||
var deletions := 0
|
||||
for i in len(arr_b):
|
||||
var index : int = i - deletions
|
||||
var val = arr_b[index]
|
||||
if val == null and nullify:
|
||||
ret.remove_at(index)
|
||||
deletions += i
|
||||
elif val is Array:
|
||||
ret[index] = _merge_array(ret[index] if ret[index] else [], val)
|
||||
elif val is Dictionary:
|
||||
ret[index] = _merge_dict(ret[index] if ret[index] else {}, val)
|
||||
else:
|
||||
ret[index] = val
|
||||
return ret
|
||||
Reference in New Issue
Block a user