diff --git a/addons/supabase/Auth/auth.gd b/addons/supabase/Auth/auth.gd index b456d49..182fec4 100644 --- a/addons/supabase/Auth/auth.gd +++ b/addons/supabase/Auth/auth.gd @@ -2,14 +2,14 @@ class_name SupabaseAuth extends Node class Providers: - const APPLE := "apple" - const BITBUCKET := "bitbucket" - const DISCORD := "discord" - const FACEBOOK := "facebook" - const GITHUB := "github" - const GITLAB := "gitlab" - const GOOGLE := "google" - const TWITTER := "twitter" + const APPLE := "apple" + const BITBUCKET := "bitbucket" + const DISCORD := "discord" + const FACEBOOK := "facebook" + const GITHUB := "github" + const GITLAB := "gitlab" + const GOOGLE := "google" + const TWITTER := "twitter" signal signed_up(signed_user) signal signed_up_phone(signed_user) @@ -52,273 +52,273 @@ var _expires_in : float = 0 var client : SupabaseUser func _init(conf : Dictionary, head : PoolStringArray) -> void: - _config = conf - _header = head - name = "Authentication" + _config = conf + _header = head + name = "Authentication" func _check_auth() -> AuthTask: - printerr("User already authenticated. Please, logout before authenticating again.") - var auth_task : AuthTask = AuthTask.new(-1, "", [], {}) - auth_task.emit_signal("completed") - return auth_task - + null + var auth_task : AuthTask = AuthTask.new(-1, "", [], {}) + auth_task.emit_signal("completed") + return auth_task + # Allow your users to sign up and create a new account. func sign_up(email : String, password : String) -> AuthTask: - if _auth != "": return _check_auth() - var payload : Dictionary = {"email":email, "password":password} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.SIGNUP, - _config.supabaseUrl + _signup_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + if _auth != "": return _check_auth() + var payload : Dictionary = {"email":email, "password":password} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.SIGNUP, + _config.supabaseUrl + _signup_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # Allow your users to sign up and create a new account using phone/password combination. # NOTE: the OTP sent to the user must be verified. func sign_up_phone(phone : String, password : String) -> AuthTask: - if _auth != "": return _check_auth() - var payload : Dictionary = {"phone":phone, "password":password} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.SIGNUPPHONEPASSWORD, - _config.supabaseUrl + _signup_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + if _auth != "": return _check_auth() + var payload : Dictionary = {"phone":phone, "password":password} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.SIGNUPPHONEPASSWORD, + _config.supabaseUrl + _signup_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # If an account is created, users can login to your app. func sign_in(email : String, password : String = "") -> AuthTask: - if _auth != "": return _check_auth() - var payload : Dictionary = {"email":email, "password":password} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.SIGNIN, - _config.supabaseUrl + _signin_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + if _auth != "": return _check_auth() + var payload : Dictionary = {"email":email, "password":password} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.SIGNIN, + _config.supabaseUrl + _signin_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # If an account is created, users can login to your app using phone/password combination. # NOTE: this requires sign_up_phone() and verify_otp() to work func sign_in_phone(phone : String, password : String = "") -> AuthTask: - if _auth != "": return _check_auth() - var payload : Dictionary = {"phone":phone, "password":password} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.SIGNIN, - _config.supabaseUrl + _signin_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + if _auth != "": return _check_auth() + var payload : Dictionary = {"phone":phone, "password":password} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.SIGNIN, + _config.supabaseUrl + _signin_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # Sign in using OTP - the user won't need to use a password but the token must be validated. # This method always requires to use OTP verification, unlike sign_in_phone() func sign_in_otp(phone : String) -> AuthTask: - if _auth != "": return _check_auth() - var payload : Dictionary = {"phone":phone} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.SIGNINOTP, - _config.supabaseUrl + _signin_otp_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + if _auth != "": return _check_auth() + var payload : Dictionary = {"phone":phone} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.SIGNINOTP, + _config.supabaseUrl + _signin_otp_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # Verify the OTP token sent to a user as an SMS func verify_otp(phone : String, token : String) -> AuthTask: - if _auth != "": return _check_auth() - var payload : Dictionary = {phone = phone, token = token, type = "sms"} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.VERIFYOTP, - _config.supabaseUrl + _verify_otp_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + if _auth != "": return _check_auth() + var payload : Dictionary = {phone = phone, token = token, type = "sms"} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.VERIFYOTP, + _config.supabaseUrl + _verify_otp_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # Sign in as an anonymous user func sign_in_anonymous() -> AuthTask: - if _auth != "": return _check_auth() - var auth_task : AuthTask = AuthTask.new(AuthTask.Task.SIGNINANONYM, "", []) - auth_task.user = SupabaseUser.new({user = {}, access_token = _config.supabaseKey}) - _process_task(auth_task, true) - return auth_task + if _auth != "": return _check_auth() + var auth_task : AuthTask = AuthTask.new(AuthTask.Task.SIGNINANONYM, "", []) + auth_task.user = SupabaseUser.new({user = {}, access_token = _config.supabaseKey}) + _process_task(auth_task, true) + return auth_task # [ CURRENTLY UNSUPPORTED ] # Sign in with a Provider # @provider = Providers.PROVIDER func sign_in_with_provider(provider : String, grab_from_browser : bool = true, port : int = 3000) -> void: - OS.shell_open(_config.supabaseUrl + _provider_endpoint + "?provider="+provider) - # ! to be implemented - pass + OS.shell_open(_config.supabaseUrl + _provider_endpoint + "?provider="+provider) + # ! to be implemented + pass # If a user is logged in, this will log it out func sign_out() -> AuthTask: - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.LOGOUT, - _config.supabaseUrl + _logout_endpoint, - _header + _bearer) - _process_task(auth_task) - return auth_task + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.LOGOUT, + _config.supabaseUrl + _logout_endpoint, + _header + _bearer) + _process_task(auth_task) + return auth_task # If an account is created, users can login to your app with a magic link sent via email. # NOTE: this method currently won't work unless the fragment (#) is *MANUALLY* replaced with a query (?) and the browser is reloaded # [https://github.com/supabase/supabase/issues/1698] func send_magic_link(email : String) -> AuthTask: - var payload : Dictionary = {"email":email} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.MAGICLINK, - _config.supabaseUrl + _magiclink_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + var payload : Dictionary = {"email":email} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.MAGICLINK, + _config.supabaseUrl + _magiclink_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # Get the JSON object for the logged in user. func user(user_access_token : String = _auth) -> AuthTask: - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.USER, - _config.supabaseUrl + _user_endpoint, - _header + _bearer) - _process_task(auth_task) - return auth_task + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.USER, + _config.supabaseUrl + _user_endpoint, + _header + _bearer) + _process_task(auth_task) + return auth_task # Update credentials of the authenticated user, together with optional metadata func update(email : String, password : String = "", data : Dictionary = {}) -> AuthTask: - var payload : Dictionary = {"email":email, "password":password, "data" : data} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.UPDATE, - _config.supabaseUrl + _user_endpoint, - _header + _bearer, - payload) - _process_task(auth_task) - return auth_task + var payload : Dictionary = {"email":email, "password":password, "data" : data} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.UPDATE, + _config.supabaseUrl + _user_endpoint, + _header + _bearer, + payload) + _process_task(auth_task) + return auth_task # Request a reset password mail to the specified email func reset_password_for_email(email : String) -> AuthTask: - var payload : Dictionary = {"email":email} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.RECOVER, - _config.supabaseUrl + _reset_password_endpoint, - _header, - payload) - _process_task(auth_task) - return auth_task + var payload : Dictionary = {"email":email} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.RECOVER, + _config.supabaseUrl + _reset_password_endpoint, + _header, + payload) + _process_task(auth_task) + return auth_task # Invite another user by their email func invite_user_by_email(email : String) -> AuthTask: - var payload : Dictionary = {"email":email} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.INVITE, - _config.supabaseUrl + _invite_endpoint, - _header + _bearer, - payload) - _process_task(auth_task) - return auth_task + var payload : Dictionary = {"email":email} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.INVITE, + _config.supabaseUrl + _invite_endpoint, + _header + _bearer, + payload) + _process_task(auth_task) + return auth_task # Refresh the access_token of the authenticated client using the refresh_token # No need to call this manually except specific needs, since the process will be handled automatically func refresh_token(refresh_token : String = client.refresh_token, expires_in : float = client.expires_in) -> AuthTask: - yield(get_tree().create_timer(expires_in-10), "timeout") - var payload : Dictionary = {refresh_token = refresh_token} - var auth_task : AuthTask = AuthTask.new( - AuthTask.Task.REFRESH, - _config.supabaseUrl + _refresh_token_endpoint, - _header + _bearer, - payload) - _process_task(auth_task) - return auth_task + yield(get_tree().create_timer(expires_in-10), "timeout") + var payload : Dictionary = {refresh_token = refresh_token} + var auth_task : AuthTask = AuthTask.new( + AuthTask.Task.REFRESH, + _config.supabaseUrl + _refresh_token_endpoint, + _header + _bearer, + payload) + _process_task(auth_task) + return auth_task # Retrieve the response from the server func _get_link_response(delta : float) -> void: - yield(get_tree().create_timer(delta), "timeout") - var peer : StreamPeer = tcp_server.take_connection() - if peer != null: - var raw_result : String = peer.get_utf8_string(peer.get_available_bytes()) - return raw_result - else: - _get_link_response(delta) + yield(get_tree().create_timer(delta), "timeout") + var peer : StreamPeer = tcp_server.take_connection() + if peer != null: + var raw_result : String = peer.get_utf8_string(peer.get_available_bytes()) + return raw_result + else: + _get_link_response(delta) # Process a specific task func _process_task(task : AuthTask, _fake : bool = false) -> void: - task.connect("completed", self, "_on_task_completed") - if _fake: - yield(get_tree().create_timer(0.5), "timeout") - task.complete(task.user, task.data, task.error) - else: - var httprequest : HTTPRequest = HTTPRequest.new() - add_child(httprequest) - task.push_request(httprequest) + task.connect("completed", self, "_on_task_completed") + if _fake: + yield(get_tree().create_timer(0.5), "timeout") + task.complete(task.user, task.data, task.error) + else: + var httprequest : HTTPRequest = HTTPRequest.new() + add_child(httprequest) + task.push_request(httprequest) func _on_task_completed(task : AuthTask) -> void: - if task._handler!=null: task._handler.queue_free() - - if task.error != null: - emit_signal("error", task.error) - else: - if task.user != null: - client = task.user - _auth = client.access_token - _bearer[0] = _bearer[0] % _auth - _expires_in = client.expires_in - match task._code: - AuthTask.Task.SIGNUP: - emit_signal("signed_up", client) - AuthTask.Task.SIGNUPPHONEPASSWORD: - emit_signal("signed_up_phone", client) - AuthTask.Task.SIGNIN: - emit_signal("signed_in", client) - AuthTask.Task.SIGNINOTP: - emit_signal("signed_in_otp", client) - AuthTask.Task.UPDATE: - emit_signal("user_updated", client) - AuthTask.Task.REFRESH: - emit_signal("token_refreshed", client) - AuthTask.Task.VERIFYOTP: - emit_signal("otp_verified") - AuthTask.Task.SIGNINANONYM: - emit_signal("signed_in_anonyous") - refresh_token() - else: - if task.data.empty() or task.data == null: - match task._code: - AuthTask.Task.MAGICLINK: - emit_signal("magic_link_sent") - AuthTask.Task.RECOVER: - emit_signal("reset_email_sent") - AuthTask.Task.INVITE: - emit_signal("user_invited") - AuthTask.Task.LOGOUT: - emit_signal("signed_out") - client = null - _auth = "" - _bearer = ["Authorization: Bearer %s"] - _expires_in = 0 - + if task._handler!=null: task._handler.queue_free() + + if task.error != null: + emit_signal("error", task.error) + else: + if task.user != null: + client = task.user + _auth = client.access_token + _bearer[0] = _bearer[0] % _auth + _expires_in = client.expires_in + match task._code: + AuthTask.Task.SIGNUP: + emit_signal("signed_up", client) + AuthTask.Task.SIGNUPPHONEPASSWORD: + emit_signal("signed_up_phone", client) + AuthTask.Task.SIGNIN: + emit_signal("signed_in", client) + AuthTask.Task.SIGNINOTP: + emit_signal("signed_in_otp", client) + AuthTask.Task.UPDATE: + emit_signal("user_updated", client) + AuthTask.Task.REFRESH: + emit_signal("token_refreshed", client) + AuthTask.Task.VERIFYOTP: + emit_signal("otp_verified") + AuthTask.Task.SIGNINANONYM: + emit_signal("signed_in_anonyous") + refresh_token() + else: + if task.data.empty() or task.data == null: + match task._code: + AuthTask.Task.MAGICLINK: + emit_signal("magic_link_sent") + AuthTask.Task.RECOVER: + emit_signal("reset_email_sent") + AuthTask.Task.INVITE: + emit_signal("user_invited") + AuthTask.Task.LOGOUT: + emit_signal("signed_out") + client = null + _auth = "" + _bearer = ["Authorization: Bearer %s"] + _expires_in = 0 + # A timer used to listen through TCP on the redirect uri of the request func _tcp_stream_timer() -> void: - var peer : StreamPeer = tcp_server.take_connection() - # ! to be implemented - pass + var peer : StreamPeer = tcp_server.take_connection() + # ! to be implemented + pass diff --git a/addons/supabase/Auth/auth_error.gd b/addons/supabase/Auth/auth_error.gd index 6f0aea7..2e805a6 100644 --- a/addons/supabase/Auth/auth_error.gd +++ b/addons/supabase/Auth/auth_error.gd @@ -6,14 +6,14 @@ var type : String = "(undefined)" var description : String = "(undefined)" func _init(dictionary : Dictionary = {}) -> void: - _error = dictionary - if not _error.empty(): - type = _error.get("error", "(undefined)") - description = _error.get("error_description", "(undefined)") - if _error.has("code"): - type = str(_error.get("code", -1)) - description = _error.get("msg", "(undefined)") - # different body for same api source ??? + _error = dictionary + if not _error.empty(): + type = _error.get("error", "(undefined)") + description = _error.get("error_description", "(undefined)") + if _error.has("code"): + type = str(_error.get("code", -1)) + description = _error.get("msg", "(undefined)") + # different body for same api source ??? func _to_string(): - return "%s >> %s" % [type, description] + return "%s >> %s" % [type, description] diff --git a/addons/supabase/Auth/auth_task.gd b/addons/supabase/Auth/auth_task.gd index ca4ad0f..7eb09e4 100644 --- a/addons/supabase/Auth/auth_task.gd +++ b/addons/supabase/Auth/auth_task.gd @@ -4,20 +4,20 @@ extends Reference signal completed(task) enum Task { - NONE, - SIGNUP, - SIGNUPPHONEPASSWORD, - SIGNIN, - SIGNINANONYM, - SIGNINOTP, - MAGICLINK, - LOGOUT, - USER, - UPDATE, - RECOVER, - REFRESH, - INVITE, - VERIFYOTP + NONE, + SIGNUP, + SIGNUPPHONEPASSWORD, + SIGNIN, + SIGNINANONYM, + SIGNINOTP, + MAGICLINK, + LOGOUT, + USER, + UPDATE, + RECOVER, + REFRESH, + INVITE, + VERIFYOTP } var _code : int @@ -35,49 +35,49 @@ var data : Dictionary var _handler : HTTPRequest func _init(code : int, endpoint : String, headers : PoolStringArray, payload : Dictionary = {}): - _code = code - _endpoint = endpoint - _headers = headers - _payload = payload - _method = match_code(code) + _code = code + _endpoint = endpoint + _headers = headers + _payload = payload + _method = match_code(code) func match_code(code : int) -> int: - match code: - Task.SIGNIN, Task.SIGNUP, Task.LOGOUT, Task.MAGICLINK, Task.RECOVER, Task.REFRESH, Task.INVITE: - return HTTPClient.METHOD_POST - Task.UPDATE: - return HTTPClient.METHOD_PUT - _, Task.USER: - return HTTPClient.METHOD_GET + match code: + Task.SIGNIN, Task.SIGNUP, Task.LOGOUT, Task.MAGICLINK, Task.RECOVER, Task.REFRESH, Task.INVITE: + return HTTPClient.METHOD_POST + Task.UPDATE: + return HTTPClient.METHOD_PUT + _, Task.USER: + return HTTPClient.METHOD_GET func push_request(httprequest : HTTPRequest) -> void: - _handler = httprequest - _handler.connect("request_completed", self, "_on_task_completed") - _handler.request(_endpoint, _headers, true, _method, to_json(_payload)) - yield(_handler, "request_completed") + _handler = httprequest + _handler.connect("request_completed", self, "_on_task_completed") + _handler.request(_endpoint, _headers, true, _method, to_json(_payload)) + yield(_handler, "request_completed") func _on_task_completed(result : int, response_code : int, headers : PoolStringArray, body : PoolByteArray) -> void: - var result_body : Dictionary = JSON.parse(body.get_string_from_utf8()).result if body.get_string_from_utf8() else {} - match response_code: - 200: - match _code: - Task.SIGNUP, Task.SIGNIN, Task.UPDATE, Task.REFRESH: - complete(SupabaseUser.new(result_body), result_body) - Task.MAGICLINK, Task.RECOVER, Task.INVITE: - complete() - 0, 204: - match _code: - Task.LOGOUT, Task.USER: - complete() - _: - if result_body == null : result_body = {} - complete(null, {}, SupabaseAuthError.new(result_body)) + var result_body : Dictionary = JSON.parse(body.get_string_from_utf8()).result if body.get_string_from_utf8() else {} + match response_code: + 200: + match _code: + Task.SIGNUP, Task.SIGNIN, Task.UPDATE, Task.REFRESH: + complete(SupabaseUser.new(result_body), result_body) + Task.MAGICLINK, Task.RECOVER, Task.INVITE: + complete() + 0, 204: + match _code: + Task.LOGOUT, Task.USER: + complete() + _: + if result_body == null : result_body = {} + complete(null, {}, SupabaseAuthError.new(result_body)) func complete(_user : SupabaseUser = null, _data : Dictionary = {}, _error : SupabaseAuthError = null) -> void: - user = _user - data = _data - error = _error - emit_signal("completed", self) + user = _user + data = _data + error = _error + emit_signal("completed", self) - + diff --git a/addons/supabase/Auth/user.gd b/addons/supabase/Auth/user.gd index e2497fe..a7d5d1d 100644 --- a/addons/supabase/Auth/user.gd +++ b/addons/supabase/Auth/user.gd @@ -17,26 +17,26 @@ var role : String var confirmation_sent_at : String func _init(user_dictionary : Dictionary) -> void: - if user_dictionary.has("user"): - access_token = user_dictionary.get("access_token", "") - token_type = user_dictionary.get("token_type", "") - refresh_token = user_dictionary.get("refresh_token", "") - expires_in = user_dictionary.get("expires_in", 0.0) - dict = user_dictionary.get("user", {}) - last_sign_in_at = dict.get("last_sign_in_at", "") - else: - dict = user_dictionary - confirmation_sent_at = dict.get("confirmation_sent_at", "") - - email = dict.get("email", "") - id = dict.get("id", "") - created_at = dict.get("created_at", "") - updated_at = dict.get("updated_at", "") - user_metadata = ({} if dict.get("user_metadata") == null else dict.get("user_metadata")) - role = dict.get("role", "") + if user_dictionary.has("user"): + access_token = user_dictionary.get("access_token", "") + token_type = user_dictionary.get("token_type", "") + refresh_token = user_dictionary.get("refresh_token", "") + expires_in = user_dictionary.get("expires_in", 0.0) + dict = user_dictionary.get("user", {}) + last_sign_in_at = dict.get("last_sign_in_at", "") + else: + dict = user_dictionary + confirmation_sent_at = dict.get("confirmation_sent_at", "") + + email = dict.get("email", "") + id = dict.get("id", "") + created_at = dict.get("created_at", "") + updated_at = dict.get("updated_at", "") + user_metadata = ({} if dict.get("user_metadata") == null else dict.get("user_metadata")) + role = dict.get("role", "") func _to_string(): - var to_string : String = "%-10s %s\n" % ["USER ID:", id] - to_string += "%-10s %s\n" % ["EMAIL:", email] - to_string += "%-10s %s\n" % ["ROLE:", role] - return to_string + var to_string : String = "%-10s %s\n" % ["USER ID:", id] + to_string += "%-10s %s\n" % ["EMAIL:", email] + to_string += "%-10s %s\n" % ["ROLE:", role] + return to_string diff --git a/addons/supabase/Database/database.gd b/addons/supabase/Database/database.gd index d2e65d2..c5d6148 100644 --- a/addons/supabase/Database/database.gd +++ b/addons/supabase/Database/database.gd @@ -17,57 +17,57 @@ var _bearer : PoolStringArray = ["Authorization: Bearer %s"] var _pooled_tasks : Array = [] func _init(conf : Dictionary, head : PoolStringArray) -> void: - _config = conf - _header += head - name = "Database" + _config = conf + _header += head + name = "Database" # Issue a query on your database func query(supabase_query : SupabaseQuery) -> DatabaseTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + supabase_query.build_query() - var task : DatabaseTask = DatabaseTask.new() - task._setup( - supabase_query, - supabase_query.request, - endpoint, - _header + _bearer + supabase_query.header, - supabase_query.body) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + supabase_query.build_query() + var task : DatabaseTask = DatabaseTask.new() + task._setup( + supabase_query, + supabase_query.request, + endpoint, + _header + _bearer + supabase_query.header, + supabase_query.body) + _process_task(task) + return task # Issue an rpc() call to a function func rpc(function_name : String, arguments : Dictionary = {}, supabase_query : SupabaseQuery = null) -> DatabaseTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "rpc/{function}".format({function = function_name}) + (supabase_query.build_query() if supabase_query!=null else "") - var task : DatabaseTask = DatabaseTask.new() - task._setup( - supabase_query, - -2, - endpoint, - _header + _bearer, - to_json(arguments)) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "rpc/{function}".format({function = function_name}) + (supabase_query.build_query() if supabase_query!=null else "") + var task : DatabaseTask = DatabaseTask.new() + task._setup( + supabase_query, + -2, + endpoint, + _header + _bearer, + to_json(arguments)) + _process_task(task) + return task func _process_task(task : DatabaseTask) -> void: - var httprequest : HTTPRequest = HTTPRequest.new() - add_child(httprequest) - task.connect("completed", self, "_on_task_completed") - task.push_request(httprequest) - _pooled_tasks.append(task) + var httprequest : HTTPRequest = HTTPRequest.new() + add_child(httprequest) + task.connect("completed", self, "_on_task_completed") + task.push_request(httprequest) + _pooled_tasks.append(task) # .............. HTTPRequest completed func _on_task_completed(task : DatabaseTask) -> void: - if task._handler != null: task._handler.queue_free() - if task.data!=null and not task.data.empty(): - match task._code: - SupabaseQuery.REQUESTS.SELECT: emit_signal("selected", task.data) - SupabaseQuery.REQUESTS.INSERT: emit_signal("inserted", task.data) - SupabaseQuery.REQUESTS.UPDATE: emit_signal("updated", task.data) - SupabaseQuery.REQUESTS.DELETE: emit_signal("deleted", task.data) - _: - emit_signal("rpc_completed", task.data) - elif task.error != null: - emit_signal("error", task.error) - _pooled_tasks.erase(task) + if task._handler != null: task._handler.queue_free() + if task.data!=null and not task.data.empty(): + match task._code: + SupabaseQuery.REQUESTS.SELECT: emit_signal("selected", task.data) + SupabaseQuery.REQUESTS.INSERT: emit_signal("inserted", task.data) + SupabaseQuery.REQUESTS.UPDATE: emit_signal("updated", task.data) + SupabaseQuery.REQUESTS.DELETE: emit_signal("deleted", task.data) + _: + emit_signal("rpc_completed", task.data) + elif task.error != null: + emit_signal("error", task.error) + _pooled_tasks.erase(task) diff --git a/addons/supabase/Database/database_error.gd b/addons/supabase/Database/database_error.gd index 5450bdd..e446821 100644 --- a/addons/supabase/Database/database_error.gd +++ b/addons/supabase/Database/database_error.gd @@ -9,13 +9,13 @@ var hint : String = "empty" var details func _init(dictionary : Dictionary = {}) -> void: - _error = dictionary - if not _error.empty(): - code = _error.code if _error.has("code") else "empty" - message = _error.message - hint = _error.hint if _error.has("hint") and _error.hint != null else "empty" - details = _error.get("details", "") + _error = dictionary + if not _error.empty(): + code = _error.code if _error.has("code") else "empty" + message = _error.message + hint = _error.hint if _error.has("hint") and _error.hint != null else "empty" + details = _error.get("details", "") ### always different behavior ??? func _to_string(): - return "%s >> %s: %s (%s)" % [code, message, details, hint] + return "%s >> %s: %s (%s)" % [code, message, details, hint] diff --git a/addons/supabase/Database/database_task.gd b/addons/supabase/Database/database_task.gd index a8b850a..8ad87e1 100644 --- a/addons/supabase/Database/database_task.gd +++ b/addons/supabase/Database/database_task.gd @@ -18,42 +18,42 @@ var error : SupabaseDatabaseError var _handler : HTTPRequest func _init(data = null, error : SupabaseDatabaseError = null) -> void: - self.data = data - self.error = error + self.data = data + self.error = error func _setup(query : SupabaseQuery, code : int, endpoint : String, headers : PoolStringArray, payload : String = ""): - _query = query - _code = code - _endpoint = endpoint - _headers = headers - _payload = payload - _method = match_code(code) + _query = query + _code = code + _endpoint = endpoint + _headers = headers + _payload = payload + _method = match_code(code) func match_code(code : int) -> int: - match code: - SupabaseQuery.REQUESTS.INSERT: return HTTPClient.METHOD_POST - SupabaseQuery.REQUESTS.SELECT: return HTTPClient.METHOD_GET - SupabaseQuery.REQUESTS.UPDATE: return HTTPClient.METHOD_PATCH - SupabaseQuery.REQUESTS.DELETE: return HTTPClient.METHOD_DELETE - _: return HTTPClient.METHOD_POST + match code: + SupabaseQuery.REQUESTS.INSERT: return HTTPClient.METHOD_POST + SupabaseQuery.REQUESTS.SELECT: return HTTPClient.METHOD_GET + SupabaseQuery.REQUESTS.UPDATE: return HTTPClient.METHOD_PATCH + SupabaseQuery.REQUESTS.DELETE: return HTTPClient.METHOD_DELETE + _: return HTTPClient.METHOD_POST func push_request(httprequest : HTTPRequest) -> void: - _handler = httprequest - httprequest.connect("request_completed", self, "_on_task_completed") - httprequest.request(_endpoint, _headers, true, _method, _payload) + _handler = httprequest + httprequest.connect("request_completed", self, "_on_task_completed") + httprequest.request(_endpoint, _headers, true, _method, _payload) func _on_task_completed(result : int, response_code : int, headers : PoolStringArray, body : PoolByteArray) -> void: - var result_body = JSON.parse(body.get_string_from_utf8()).result if body.get_string_from_utf8() else {} - if response_code in [200, 201, 204]: - complete(result_body) - else: - var supabase_error : SupabaseDatabaseError = SupabaseDatabaseError.new(result_body) - complete(null, supabase_error) - if _query!=null: _query.clean() + var result_body = JSON.parse(body.get_string_from_utf8()).result if body.get_string_from_utf8() else {} + if response_code in [200, 201, 204]: + complete(result_body) + else: + var supabase_error : SupabaseDatabaseError = SupabaseDatabaseError.new(result_body) + complete(null, supabase_error) + if _query!=null: _query.clean() func complete(_result, _error : SupabaseDatabaseError = null) -> void: - data = _result - error = _error - if _handler : _handler.queue_free() - emit_signal("completed", self) + data = _result + error = _error + if _handler : _handler.queue_free() + emit_signal("completed", self) diff --git a/addons/supabase/Database/query.gd b/addons/supabase/Database/query.gd index 6c70598..8f7c6c6 100644 --- a/addons/supabase/Database/query.gd +++ b/addons/supabase/Database/query.gd @@ -2,20 +2,20 @@ extends Reference class_name SupabaseQuery var query_struct : Dictionary = { - table = "", - select = PoolStringArray([]), - order = PoolStringArray([]), - Or = PoolStringArray([]), - eq = PoolStringArray([]), - neq = PoolStringArray([]), - like = PoolStringArray([]), - ilike = PoolStringArray([]), - Is = PoolStringArray([]), - in = PoolStringArray([]), - fts = PoolStringArray([]), - plfts = PoolStringArray([]), - phfts = PoolStringArray([]), - wfts = PoolStringArray([]) + table = "", + select = PoolStringArray([]), + order = PoolStringArray([]), + Or = PoolStringArray([]), + eq = PoolStringArray([]), + neq = PoolStringArray([]), + like = PoolStringArray([]), + ilike = PoolStringArray([]), + Is = PoolStringArray([]), + in = PoolStringArray([]), + fts = PoolStringArray([]), + plfts = PoolStringArray([]), + phfts = PoolStringArray([]), + wfts = PoolStringArray([]) } var query : String = "" @@ -26,252 +26,252 @@ var body : String = "" enum REQUESTS { - NONE, - SELECT, - INSERT, - UPDATE, - DELETE + NONE, + SELECT, + INSERT, + UPDATE, + DELETE } enum Directions { - Ascending, - Descending + Ascending, + Descending } enum Nullsorder { - First, - Last + First, + Last } enum Filters { - EQUAL, - NOT_EQUAL, - GREATER_THAN - LESS_THAN, - GREATER_THAN_OR_EQUAL, - LESS_THAN_OR_EQUAL, - LIKE, - ILIKE, - IS, - IN, - FTS, - PLFTS, - PHFLTS, - WFTS, - OR, - ORDER + EQUAL, + NOT_EQUAL, + GREATER_THAN + LESS_THAN, + GREATER_THAN_OR_EQUAL, + LESS_THAN_OR_EQUAL, + LIKE, + ILIKE, + IS, + IN, + FTS, + PLFTS, + PHFLTS, + WFTS, + OR, + ORDER } func _init(_raw_query : String = "", _raw_type : int = -1, _raw_header : PoolStringArray = [], _raw_body : String = ""): - if _raw_query != "": - raw_query = _raw_query - query = _raw_query - request = _raw_type - header = _raw_header as PoolStringArray - body = _raw_body + if _raw_query != "": + raw_query = _raw_query + query = _raw_query + request = _raw_type + header = _raw_header as PoolStringArray + body = _raw_body # Build the query from the scrut func build_query() -> String: - if raw_query == "" and query == raw_query: - for key in query_struct: - if query_struct[key].empty(): continue - if query.length() > 0 : if not query[query.length()-1] in ["/","?"]: query+="&" - match key: - "table": - query += query_struct[key] - "select", "order": - if query_struct[key].empty(): continue - query += (key + "=" + PoolStringArray(query_struct[key]).join(",")) - "eq", "neq", "lt", "gt", "lte", "gte", "like", "ilike", "Is", "in", "fts", "plfts", "phfts", "wfts": - query += PoolStringArray(query_struct[key]).join("&") - "Or": - query += "or=(%s)"%[query_struct[key].join(",")] - return query + if raw_query == "" and query == raw_query: + for key in query_struct: + if query_struct[key].empty(): continue + if query.length() > 0 : if not query[query.length()-1] in ["/","?"]: query+="&" + match key: + "table": + query += query_struct[key] + "select", "order": + if query_struct[key].empty(): continue + query += (key + "=" + PoolStringArray(query_struct[key]).join(",")) + "eq", "neq", "lt", "gt", "lte", "gte", "like", "ilike", "Is", "in", "fts", "plfts", "phfts", "wfts": + query += PoolStringArray(query_struct[key]).join("&") + "Or": + query += "or=(%s)"%[query_struct[key].join(",")] + return query func from(table_name : String) -> SupabaseQuery: - query_struct.table = table_name+"?" - return self + query_struct.table = table_name+"?" + return self # Insert new Row func insert(fields : Array, upsert : bool = false) -> SupabaseQuery: - request = REQUESTS.INSERT - body = to_json(fields) - if upsert : header += PoolStringArray(["Prefer: resolution=merge-duplicates"]) - return self + request = REQUESTS.INSERT + body = to_json(fields) + if upsert : header += PoolStringArray(["Prefer: resolution=merge-duplicates"]) + return self # Select Rows func select(columns : PoolStringArray = PoolStringArray(["*"])) -> SupabaseQuery: - request = REQUESTS.SELECT - query_struct.select += columns - return self + request = REQUESTS.SELECT + query_struct.select += columns + return self # Update Rows func update(fields : Dictionary) -> SupabaseQuery: - request = REQUESTS.UPDATE - body = to_json(fields) - return self + request = REQUESTS.UPDATE + body = to_json(fields) + return self # Delete Rows func delete() -> SupabaseQuery: - request = REQUESTS.DELETE - return self + request = REQUESTS.DELETE + return self ## [MODIFIERS] ----------------------------------------------------------------- func range(from : int, to : int) -> SupabaseQuery: - header = PoolStringArray(["Range: "+str(from)+"-"+str(to)]) - return self + header = PoolStringArray(["Range: "+str(from)+"-"+str(to)]) + return self func order(column : String, direction : int = Directions.Ascending, nullsorder : int = Nullsorder.First) -> SupabaseQuery: - var direction_str : String - match direction: - Directions.Ascending: direction_str = "asc" - Directions.Descending: direction_str = "desc" - var nullsorder_str : String - match nullsorder: - Nullsorder.First: nullsorder_str = "nullsfirst" - Nullsorder.Last: nullsorder_str = "nullslast" - query_struct.order += PoolStringArray([("%s.%s.%s" % [column, direction_str, nullsorder_str])]) - return self + var direction_str : String + match direction: + Directions.Ascending: direction_str = "asc" + Directions.Descending: direction_str = "desc" + var nullsorder_str : String + match nullsorder: + Nullsorder.First: nullsorder_str = "nullsfirst" + Nullsorder.Last: nullsorder_str = "nullslast" + query_struct.order += PoolStringArray([("%s.%s.%s" % [column, direction_str, nullsorder_str])]) + return self ## [FILTERS] -------------------------------------------------------------------- func filter(column : String, filter : int, value : String, _props : Dictionary = {}) -> SupabaseQuery: - var filter_str : String = match_filter(filter) - var array : PoolStringArray = query_struct[filter_str] as PoolStringArray - var struct_filter : String = filter_str - if _props.has("config"): - struct_filter+= "({config})".format(_props) - if _props.has("negate"): - struct_filter = ("not."+struct_filter) if _props.get("negate") else struct_filter - # Apply custom logic or continue with default logic - match filter_str: - "Or": - if _props.has("queries"): - for query in _props.get("queries"): - array.append(query.build_query().replace("=",".") if (not query is String) else query) - _: - array.append("%s=%s.%s" % [column, struct_filter.to_lower(), value]) - query_struct[filter_str] = array - return self + var filter_str : String = match_filter(filter) + var array : PoolStringArray = query_struct[filter_str] as PoolStringArray + var struct_filter : String = filter_str + if _props.has("config"): + struct_filter+= "({config})".format(_props) + if _props.has("negate"): + struct_filter = ("not."+struct_filter) if _props.get("negate") else struct_filter + # Apply custom logic or continue with default logic + match filter_str: + "Or": + if _props.has("queries"): + for query in _props.get("queries"): + array.append(query.build_query().replace("=",".") if (not query is String) else query) + _: + array.append("%s=%s.%s" % [column, struct_filter.to_lower(), value]) + query_struct[filter_str] = array + return self func match_filter(filter : int) -> String: - var filter_str : String - match filter: - Filters.EQUAL: filter_str = "eq" - Filters.FTS: filter_str = "fts" - Filters.ILIKE: filter_str = "ilike" - Filters.IN: filter_str = "in" - Filters.IS: filter_str = "Is" - Filters.GREATER_THAN: filter_str = "gt" - Filters.GREATER_THAN_OR_EQUAL: filter_str = "gte" - Filters.LIKE: filter_str = "like" - Filters.LESS_THAN: filter_str = "lt" - Filters.LESS_THAN_OR_EQUAL: filter_str = "lte" - Filters.NOT_EQUAL: filter_str = "neq" - Filters.OR: filter_str = "Or" - Filters.PLFTS: filter_str = "plfts" - Filters.PHFTS: filter_str = "phfts" - Filters.WFTS: filter_str = "wfts" - return filter_str + var filter_str : String + match filter: + Filters.EQUAL: filter_str = "eq" + Filters.FTS: filter_str = "fts" + Filters.ILIKE: filter_str = "ilike" + Filters.IN: filter_str = "in" + Filters.IS: filter_str = "Is" + Filters.GREATER_THAN: filter_str = "gt" + Filters.GREATER_THAN_OR_EQUAL: filter_str = "gte" + Filters.LIKE: filter_str = "like" + Filters.LESS_THAN: filter_str = "lt" + Filters.LESS_THAN_OR_EQUAL: filter_str = "lte" + Filters.NOT_EQUAL: filter_str = "neq" + Filters.OR: filter_str = "Or" + Filters.PLFTS: filter_str = "plfts" + Filters.PHFTS: filter_str = "phfts" + Filters.WFTS: filter_str = "wfts" + return filter_str # Finds all rows whose value on the stated columns match the specified values. func match(query_dict : Dictionary) -> SupabaseQuery: - for key in query_dict.keys(): - eq(key, query_dict[key]) - return self + for key in query_dict.keys(): + eq(key, query_dict[key]) + return self # Finds all rows whose value on the stated column match the specified value. func eq(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.EQUAL, value) - return self + filter(column, Filters.EQUAL, value) + return self # Finds all rows whose value on the stated column doesn't match the specified value. func neq(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.NOT_EQUAL, value) - return self + filter(column, Filters.NOT_EQUAL, value) + return self # Finds all rows whose value on the stated column is greater than the specified value func gt(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.GREATER_THAN, value) - return self + filter(column, Filters.GREATER_THAN, value) + return self # Finds all rows whose value on the stated column is less than the specified value func lt(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.LESS_THAN, value) - return self + filter(column, Filters.LESS_THAN, value) + return self # Finds all rows whose value on the stated column is greater than or equal to the specified value func gte(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.GREATER_THAN_OR_EQUAL, value) - return self + filter(column, Filters.GREATER_THAN_OR_EQUAL, value) + return self # Finds all rows whose value on the stated column is less than or equal to the specified value func lte(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.LESS_THAN_OR_EQUAL, value) - return self + filter(column, Filters.LESS_THAN_OR_EQUAL, value) + return self # Finds all rows whose value in the stated column matches the supplied pattern (case sensitive). func like(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.LIKE, "*%s*"%value) - return self + filter(column, Filters.LIKE, "*%s*"%value) + return self # Finds all rows whose value in the stated column matches the supplied pattern (case insensitive). func ilike(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.ILIKE, value) - return self + filter(column, Filters.ILIKE, value) + return self # A check for exact equality (null, true, false), finds all rows whose value on the stated column exactly match the specified value. func Is(column : String, value, negate : bool = false) -> SupabaseQuery: - filter(column, Filters.IS, str(value), {negate = negate}) - return self + filter(column, Filters.IS, str(value), {negate = negate}) + return self # Finds all rows whose value on the stated column is found on the specified values. func In(column : String, array : PoolStringArray) -> SupabaseQuery: - filter(column, Filters.IN, "("+array.join(",")+")") - return self + filter(column, Filters.IN, "("+array.join(",")+")") + return self func Or(queries : Array) -> SupabaseQuery: - filter("", Filters.OR, "", {queries = queries}) - return self + filter("", Filters.OR, "", {queries = queries}) + return self # Text Search func text_seach(column : String, query : String, type : String = "", config : String = "") -> SupabaseQuery: - var filter : int - match type: - "plain": filter = Filters.PLFTS - "phrase": filter = Filters.PHFLTS - "websearch": filter = Filters.WFTS - _: filter = Filters.FTS - query = query.replacen(" ", "%20") - filter(column, filter, query, {config = config} if config != "" else {}) - return self + var filter : int + match type: + "plain": filter = Filters.PLFTS + "phrase": filter = Filters.PHFLTS + "websearch": filter = Filters.WFTS + _: filter = Filters.FTS + query = query.replacen(" ", "%20") + filter(column, filter, query, {config = config} if config != "" else {}) + return self func clean() -> void: - query = "" - body = "" - header = [] - request = 0 - - query_struct.table = "" - query_struct.select = PoolStringArray([]) - query_struct.order = PoolStringArray([]) - query_struct.eq = PoolStringArray([]) - query_struct.neq = PoolStringArray([]) - query_struct.gt = PoolStringArray([]) - query_struct.lt = PoolStringArray([]) - query_struct.gte = PoolStringArray([]) - query_struct.lte = PoolStringArray([]) - query_struct.like = PoolStringArray([]) - query_struct.ilike = PoolStringArray([]) - query_struct.IS = PoolStringArray([]) - query_struct.in = PoolStringArray([]) - query_struct.fts = PoolStringArray([]) - query_struct.plfts = PoolStringArray([]) - query_struct.phfts = PoolStringArray([]) - query_struct.wfts = PoolStringArray([]) + query = "" + body = "" + header = [] + request = 0 + + query_struct.table = "" + query_struct.select = PoolStringArray([]) + query_struct.order = PoolStringArray([]) + query_struct.eq = PoolStringArray([]) + query_struct.neq = PoolStringArray([]) + query_struct.gt = PoolStringArray([]) + query_struct.lt = PoolStringArray([]) + query_struct.gte = PoolStringArray([]) + query_struct.lte = PoolStringArray([]) + query_struct.like = PoolStringArray([]) + query_struct.ilike = PoolStringArray([]) + query_struct.IS = PoolStringArray([]) + query_struct.in = PoolStringArray([]) + query_struct.fts = PoolStringArray([]) + query_struct.plfts = PoolStringArray([]) + query_struct.phfts = PoolStringArray([]) + query_struct.wfts = PoolStringArray([]) func _to_string() -> String: - return build_query() + return build_query() diff --git a/addons/supabase/Realtime/realtime.gd b/addons/supabase/Realtime/realtime.gd index 674efbf..7cb8ce1 100644 --- a/addons/supabase/Realtime/realtime.gd +++ b/addons/supabase/Realtime/realtime.gd @@ -4,13 +4,13 @@ extends Node var _config : Dictionary func _init(config : Dictionary) -> void: - _config = config - name = "Realtime" - + _config = config + name = "Realtime" + func _ready(): - pass # Replace with function body. + pass # Replace with function body. func client(url : String = _config.supabaseUrl, apikey : String = _config.supabaseKey, timeout : float = 30) -> RealtimeClient: - var realtime_client : RealtimeClient = RealtimeClient.new(url, apikey, timeout) - add_child(realtime_client) - return realtime_client + var realtime_client : RealtimeClient = RealtimeClient.new(url, apikey, timeout) + add_child(realtime_client) + return realtime_client diff --git a/addons/supabase/Realtime/realtime_channel.gd b/addons/supabase/Realtime/realtime_channel.gd index 4fe47cd..8a3a1ee 100644 --- a/addons/supabase/Realtime/realtime_channel.gd +++ b/addons/supabase/Realtime/realtime_channel.gd @@ -11,50 +11,50 @@ var topic : String var subscribed : bool func _init(topic : String, client) -> void: - self.topic = topic - _client = client + self.topic = topic + _client = client func _publish(message : Dictionary) -> void: - if not subscribed: return - match message.event: - _client.SupabaseEvents.DELETE: - emit_signal("delete", message.payload.old_record, self) - _client.SupabaseEvents.UPDATE: - emit_signal("update", message.payload.old_record, message.payload.record, self) - _client.SupabaseEvents.INSERT: - emit_signal("insert", message.payload.record, self) - emit_signal("all", message.payload.get("old_record", {}), message.payload.get("record", {}), self) + if not subscribed: return + match message.event: + _client.SupabaseEvents.DELETE: + emit_signal("delete", message.payload.old_record, self) + _client.SupabaseEvents.UPDATE: + emit_signal("update", message.payload.old_record, message.payload.record, self) + _client.SupabaseEvents.INSERT: + emit_signal("insert", message.payload.record, self) + emit_signal("all", message.payload.get("old_record", {}), message.payload.get("record", {}), self) func on(event : String, to : Object, function : String) -> RealtimeChannel: - connect(event, to, function) - return self - + connect(event, to, function) + return self + func subscribe() -> RealtimeChannel: - if subscribed: - _client._error("Already subscribed to topic: %s" % topic) - return self - _client.send_message({ - "topic": topic, - "event": _client.PhxEvents.JOIN, - "payload": {}, - "ref": null - }) - subscribed = true - return self + if subscribed: + _client._error("Already subscribed to topic: %s" % topic) + return self + _client.send_message({ + "topic": topic, + "event": _client.PhxEvents.JOIN, + "payload": {}, + "ref": null + }) + subscribed = true + return self - + func unsubscribe() -> RealtimeChannel: - if not subscribed: - _client._error("Already unsubscribed from topic: %s" % topic) - return self - _client.send_message({ - "topic": topic, - "event": _client.PhxEvents.LEAVE, - "payload": {}, - "ref": null - }) - subscribed = false - return self - + if not subscribed: + _client._error("Already unsubscribed from topic: %s" % topic) + return self + _client.send_message({ + "topic": topic, + "event": _client.PhxEvents.LEAVE, + "payload": {}, + "ref": null + }) + subscribed = false + return self + func close() -> void: - _client._remove_channel(self) + _client._remove_channel(self) diff --git a/addons/supabase/Realtime/realtime_client.gd b/addons/supabase/Realtime/realtime_client.gd index 5b0309a..da0bd0c 100644 --- a/addons/supabase/Realtime/realtime_client.gd +++ b/addons/supabase/Realtime/realtime_client.gd @@ -6,17 +6,17 @@ signal disconnected() signal error(message) class PhxEvents: - const JOIN := "phx_join" - const REPLY := "phx_reply" - const LEAVE := "phx_leave" - const ERROR := "phx_error" - const CLOSE := "phx_close" + const JOIN := "phx_join" + const REPLY := "phx_reply" + const LEAVE := "phx_leave" + const ERROR := "phx_error" + const CLOSE := "phx_close" class SupabaseEvents: - const DELETE:= "DELETE" - const UPDATE:= "UPDATE" - const INSERT:= "INSERT" - const ALL := "*" + const DELETE:= "DELETE" + const UPDATE:= "UPDATE" + const INSERT:= "INSERT" + const ALL := "*" var channels : Array = [] @@ -27,146 +27,146 @@ var _ws_client = WebSocketClient.new() var _heartbeat_timer : Timer = Timer.new() func _init(url : String, apikey : String, timeout : float) -> void: - set_process_internal(false) - _db_url = url.replace("http","ws")+"/realtime/v1/websocket" - _apikey = apikey - _heartbeat_timer.set_wait_time(timeout) - _heartbeat_timer.name = "PhxHeartbeat" - name = "RealtimeClient" - + set_process_internal(false) + _db_url = url.replace("http","ws")+"/realtime/v1/websocket" + _apikey = apikey + _heartbeat_timer.set_wait_time(timeout) + _heartbeat_timer.name = "PhxHeartbeat" + name = "RealtimeClient" + func _ready() -> void: - add_child(_heartbeat_timer) + add_child(_heartbeat_timer) func _connect_signals() -> void: - _ws_client.connect("connection_closed", self, "_closed") - _ws_client.connect("connection_error", self, "_error") - _ws_client.connect("connection_established", self, "_connected") - _ws_client.connect("data_received", self, "_on_data") - _heartbeat_timer.connect("timeout", self, "_on_timeout") + _ws_client.connect("connection_closed", self, "_closed") + _ws_client.connect("connection_error", self, "_error") + _ws_client.connect("connection_established", self, "_connected") + _ws_client.connect("data_received", self, "_on_data") + _heartbeat_timer.connect("timeout", self, "_on_timeout") func _disconnect_signals() -> void: - _ws_client.disconnect("connection_closed", self, "_closed") - _ws_client.disconnect("connection_error", self, "_error") - _ws_client.disconnect("connection_established", self, "_connected") - _ws_client.disconnect("data_received", self, "_on_data") - _heartbeat_timer.disconnect("timeout", self, "_on_timeout") + _ws_client.disconnect("connection_closed", self, "_closed") + _ws_client.disconnect("connection_error", self, "_error") + _ws_client.disconnect("connection_established", self, "_connected") + _ws_client.disconnect("data_received", self, "_on_data") + _heartbeat_timer.disconnect("timeout", self, "_on_timeout") func connect_client() -> int: - set_process_internal(true) - _connect_signals() - var err = _ws_client.connect_to_url("{url}?apikey={apikey}".format({url = _db_url, apikey = _apikey})) - if err != OK: - _disconnect_signals() - _heartbeat_timer.stop() - else: - _heartbeat_timer.start() - return err + set_process_internal(true) + _connect_signals() + var err = _ws_client.connect_to_url("{url}?apikey={apikey}".format({url = _db_url, apikey = _apikey})) + if err != OK: + _disconnect_signals() + _heartbeat_timer.stop() + else: + _heartbeat_timer.start() + return err func disconnect_client() -> void: - _ws_client.disconnect_from_host(1000, "Disconnection requested from client.") - set_process_internal(false) + _ws_client.disconnect_from_host(1000, "Disconnection requested from client.") + set_process_internal(false) func remove_client() -> void: - queue_free() + queue_free() func channel(schema : String, table : String = "", col_value : String = "") -> RealtimeChannel: - var topic : String = _build_topic(schema, table, col_value) - var channel : RealtimeChannel = get_channel(topic) - if channel == null: - channel = RealtimeChannel.new(topic, self) - _add_channel(channel) - return channel + var topic : String = _build_topic(schema, table, col_value) + var channel : RealtimeChannel = get_channel(topic) + if channel == null: + channel = RealtimeChannel.new(topic, self) + _add_channel(channel) + return channel func _build_topic(schema : String, table : String = "", col_value : String = "") -> String: - var topic : String = "realtime:"+schema - if table != "": - topic+=":"+table - if col_value!= "": - topic+=":"+col_value - return topic - + var topic : String = "realtime:"+schema + if table != "": + topic+=":"+table + if col_value!= "": + topic+=":"+col_value + return topic + func _add_channel(channel : RealtimeChannel) -> void: - channels.append(channel) + channels.append(channel) func _remove_channel(channel : RealtimeChannel) -> void: - channels.erase(channel) + channels.erase(channel) func _connected(proto = ""): - emit_signal("connected") + emit_signal("connected") func _closed(was_clean : bool = false): - channels = [] - _disconnect_signals() - emit_signal("disconnected") - + channels = [] + _disconnect_signals() + emit_signal("disconnected") + func _error(msg : String = "") -> void: - emit_signal("error", msg) + emit_signal("error", msg) func _on_data() -> void: - var data : Dictionary = get_message(_ws_client.get_peer(1).get_packet()) - match data.event: - PhxEvents.REPLY: - if _check_response(data) == 0: - pass - print_debug("Received reply = "+to_json(data)) - PhxEvents.JOIN: - if _check_response(data) == 0: - pass - print_debug("Joined topic '%s'" % data.topic) - PhxEvents.LEAVE: - if _check_response(data) == 0: - pass - print_debug("Left topic '%s'" % data.topic) - PhxEvents.CLOSE: - pass - print_debug("Channel closed.") - PhxEvents.ERROR: - emit_signal("error", data.payload) - SupabaseEvents.DELETE, SupabaseEvents.INSERT, SupabaseEvents.UPDATE: - print_debug("Received %s event..." % data.event) - var channel : RealtimeChannel = get_channel(data.topic) - if channel != null: - channel._publish(data) + var data : Dictionary = get_message(_ws_client.get_peer(1).get_packet()) + match data.event: + PhxEvents.REPLY: + if _check_response(data) == 0: + pass + get_parent().get_parent()._print_debug("Received reply = "+to_json(data)) + PhxEvents.JOIN: + if _check_response(data) == 0: + pass + get_parent().get_parent()._print_debug("Joined topic '%s'" % data.topic) + PhxEvents.LEAVE: + if _check_response(data) == 0: + pass + get_parent().get_parent()._print_debug("Left topic '%s'" % data.topic) + PhxEvents.CLOSE: + pass + get_parent().get_parent()._print_debug("Channel closed.") + PhxEvents.ERROR: + emit_signal("error", data.payload) + SupabaseEvents.DELETE, SupabaseEvents.INSERT, SupabaseEvents.UPDATE: + get_parent().get_parent()._print_debug("Received %s event..." % data.event) + var channel : RealtimeChannel = get_channel(data.topic) + if channel != null: + channel._publish(data) func get_channel(topic : String) -> RealtimeChannel: - for channel in channels: - if channel.topic == topic: - return channel - return null + for channel in channels: + if channel.topic == topic: + return channel + return null func _check_response(message : Dictionary): - if message.payload.status == "ok": - return 0 + if message.payload.status == "ok": + return 0 func get_message(pb : PoolByteArray) -> Dictionary: - return parse_json(pb.get_string_from_utf8()) - + return parse_json(pb.get_string_from_utf8()) + func send_message(json_message : Dictionary) -> void: - if not _ws_client.get_peer(1).is_connected_to_host(): - yield(self, "connected") - _ws_client.get_peer(1).put_packet(to_json(json_message).to_utf8()) - else: - _ws_client.get_peer(1).put_packet(to_json(json_message).to_utf8()) - - + if not _ws_client.get_peer(1).is_connected_to_host(): + yield(self, "connected") + _ws_client.get_peer(1).put_packet(to_json(json_message).to_utf8()) + else: + _ws_client.get_peer(1).put_packet(to_json(json_message).to_utf8()) + + func _send_heartbeat() -> void: - send_message({ - "topic": "phoenix", - "event": "heartbeat", - "payload": {}, - "ref": null - }) - + send_message({ + "topic": "phoenix", + "event": "heartbeat", + "payload": {}, + "ref": null + }) + func _on_timeout() -> void: - if _ws_client.get_peer(1).is_connected_to_host(): - _send_heartbeat() + if _ws_client.get_peer(1).is_connected_to_host(): + _send_heartbeat() func _notification(what) -> void: - match what: - NOTIFICATION_INTERNAL_PROCESS: - _internal_process(get_process_delta_time()) + match what: + NOTIFICATION_INTERNAL_PROCESS: + _internal_process(get_process_delta_time()) func _internal_process(_delta : float) -> void: - _ws_client.poll() - + _ws_client.poll() + diff --git a/addons/supabase/Storage/storage.gd b/addons/supabase/Storage/storage.gd index fdd0459..3f90868 100644 --- a/addons/supabase/Storage/storage.gd +++ b/addons/supabase/Storage/storage.gd @@ -19,111 +19,111 @@ var _pooled_tasks : Array = [] func _init(config : Dictionary) -> void: - _config = config - name = "Storage" + _config = config + name = "Storage" func list_buckets() -> StorageTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket" - var task : StorageTask = StorageTask.new() - task._setup( - task.METHODS.LIST_BUCKETS, - endpoint, - _header + _bearer) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket" + var task : StorageTask = StorageTask.new() + task._setup( + task.METHODS.LIST_BUCKETS, + endpoint, + _header + _bearer) + _process_task(task) + return task func get_bucket(id : String) -> StorageTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id - var task : StorageTask = StorageTask.new() - task._setup( - task.METHODS.GET_BUCKET, - endpoint, - _header + _bearer) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id + var task : StorageTask = StorageTask.new() + task._setup( + task.METHODS.GET_BUCKET, + endpoint, + _header + _bearer) + _process_task(task) + return task func create_bucket(_name : String, id : String, public : bool = false) -> StorageTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket" - var task : StorageTask = StorageTask.new() - task._setup( - task.METHODS.CREATE_BUCKET, - endpoint, - _header + _bearer, - to_json({"name" : _name, id = id, public = public})) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket" + var task : StorageTask = StorageTask.new() + task._setup( + task.METHODS.CREATE_BUCKET, + endpoint, + _header + _bearer, + to_json({"name" : _name, id = id, public = public})) + _process_task(task) + return task func update_bucket(id : String, public : bool) -> StorageTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id - var task : StorageTask = StorageTask.new() - task._setup( - task.METHODS.UPDATE_BUCKET, - endpoint, - _header + _bearer, - to_json({public = public})) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id + var task : StorageTask = StorageTask.new() + task._setup( + task.METHODS.UPDATE_BUCKET, + endpoint, + _header + _bearer, + to_json({public = public})) + _process_task(task) + return task func empty_bucket(id : String) -> StorageTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id + "/empty" - var task : StorageTask = StorageTask.new() - task._setup( - task.METHODS.EMPTY_BUCKET, - endpoint, - _bearer) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id + "/empty" + var task : StorageTask = StorageTask.new() + task._setup( + task.METHODS.EMPTY_BUCKET, + endpoint, + _bearer) + _process_task(task) + return task func delete_bucket(id : String) -> StorageTask: - _bearer = get_parent().auth._bearer - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id - var task : StorageTask = StorageTask.new() - task._setup( - task.METHODS.DELETE_BUCKET, - endpoint, - _bearer) - _process_task(task) - return task + _bearer = get_parent().auth._bearer + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "bucket/" + id + var task : StorageTask = StorageTask.new() + task._setup( + task.METHODS.DELETE_BUCKET, + endpoint, + _bearer) + _process_task(task) + return task func from(id : String) -> StorageBucket: - for bucket in get_children(): - if bucket.id == id: - return bucket - var storage_bucket : StorageBucket = StorageBucket.new(id, _config, get_parent().auth._bearer) - add_child(storage_bucket) - return storage_bucket + for bucket in get_children(): + if bucket.id == id: + return bucket + var storage_bucket : StorageBucket = StorageBucket.new(id, _config, get_parent().auth._bearer) + add_child(storage_bucket) + return storage_bucket # --- func _process_task(task : StorageTask) -> void: - var httprequest : HTTPRequest = HTTPRequest.new() - add_child(httprequest) - task.connect("completed", self, "_on_task_completed") - task.push_request(httprequest) - _pooled_tasks.append(task) + var httprequest : HTTPRequest = HTTPRequest.new() + add_child(httprequest) + task.connect("completed", self, "_on_task_completed") + task.push_request(httprequest) + _pooled_tasks.append(task) # .............. HTTPRequest completed func _on_task_completed(task : StorageTask) -> void: - if task._handler : task._handler.queue_free() - if task.data!=null and not task.data.empty(): - match task._code: - task.METHODS.LIST_BUCKETS: emit_signal("listed_buckets", task.data) - task.METHODS.GET_BUCKET: emit_signal("got_bucket", task.data) - task.METHODS.CREATE_BUCKET: emit_signal("created_bucket", from(task.data.name)) - task.METHODS.UPDATE_BUCKET: emit_signal("updated_bucket", from(task.data.name)) - task.METHODS.EMPTY_BUCKET: emit_signal("emptied_bucket", from(task.data.name)) - task.METHODS.DELETE_BUCKET: emit_signal("deleted_bucket", task.data) - elif task.error != null: - emit_signal("error", task.error) - _pooled_tasks.erase(task) + if task._handler : task._handler.queue_free() + if task.data!=null and not task.data.empty(): + match task._code: + task.METHODS.LIST_BUCKETS: emit_signal("listed_buckets", task.data) + task.METHODS.GET_BUCKET: emit_signal("got_bucket", task.data) + task.METHODS.CREATE_BUCKET: emit_signal("created_bucket", from(task.data.name)) + task.METHODS.UPDATE_BUCKET: emit_signal("updated_bucket", from(task.data.name)) + task.METHODS.EMPTY_BUCKET: emit_signal("emptied_bucket", from(task.data.name)) + task.METHODS.DELETE_BUCKET: emit_signal("deleted_bucket", task.data) + elif task.error != null: + emit_signal("error", task.error) + _pooled_tasks.erase(task) diff --git a/addons/supabase/Storage/storage_bucket.gd b/addons/supabase/Storage/storage_bucket.gd index 53c0fe5..657bebe 100644 --- a/addons/supabase/Storage/storage_bucket.gd +++ b/addons/supabase/Storage/storage_bucket.gd @@ -2,33 +2,33 @@ class_name StorageBucket extends Node const MIME_TYPES : Dictionary = { - "bmp": "image/bmp", - "css": "text/css", - "csv": "text/csv", - "gd": "text/plain", - "htm": "text/html", - "html": "text/html", - "jpeg": "image/jpeg", - "jpg": "image/jpeg", - "json": "application/json", - "mp3": "audio/mpeg", - "mpeg": "video/mpeg", - "ogg": "audio/ogg", - "ogv": "video/ogg", - "pdf": "application/pdf", - "png": "image/png", - "res": "text/plain", - "shader": "text/plain", - "svg": "image/svg+xml", - "tif": "image/tiff", - "tiff": "image/tiff", - "tres": "text/plain", - "tscn": "text/plain", - "txt": "text/script", - "wav": "audio/wav", - "webm": "video/webm", - "webp": "video/webm", - "xml": "text/xml", + "bmp": "image/bmp", + "css": "text/css", + "csv": "text/csv", + "gd": "text/plain", + "htm": "text/html", + "html": "text/html", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "json": "application/json", + "mp3": "audio/mpeg", + "mpeg": "video/mpeg", + "ogg": "audio/ogg", + "ogv": "video/ogg", + "pdf": "application/pdf", + "png": "image/png", + "res": "text/plain", + "shader": "text/plain", + "svg": "image/svg+xml", + "tif": "image/tiff", + "tiff": "image/tiff", + "tres": "text/plain", + "tscn": "text/plain", + "txt": "text/script", + "wav": "audio/wav", + "webm": "video/webm", + "webp": "video/webm", + "xml": "text/xml", } signal listed_objects(details) @@ -63,241 +63,241 @@ var id : String func _init(id : String , config : Dictionary, bearer : PoolStringArray) -> void: - _config = config - self.id = id - _bearer = bearer - name = "Bucket_"+id - set_process_internal(false) + _config = config + self.id = id + _bearer = bearer + name = "Bucket_"+id + set_process_internal(false) func list(prefix : String = "", limit : int = 100, offset : int = 0, sort_by : Dictionary = {column = "name", order = "asc"} ) -> StorageTask: - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "list/" + id - var task : StorageTask = StorageTask.new() - var header : PoolStringArray = [_header[0] % "application/json"] - task._setup( - task.METHODS.LIST_OBJECTS, - endpoint, - header + _bearer, - to_json({prefix = prefix, limit = limit, offset = offset, sort_by = sort_by})) - _process_task(task) - return task + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "list/" + id + var task : StorageTask = StorageTask.new() + var header : PoolStringArray = [_header[0] % "application/json"] + task._setup( + task.METHODS.LIST_OBJECTS, + endpoint, + header + _bearer, + to_json({prefix = prefix, limit = limit, offset = offset, sort_by = sort_by})) + _process_task(task) + return task func upload(object : String, file_path : String, upsert : bool = false) -> StorageTask: - requesting_raw = true - var task : StorageTask = StorageTask.new() - var endpoint : String = _config.supabaseUrl + _rest_endpoint + id + "/" + object - var file : File = File.new() - var error : int = file.open(file_path, File.READ) - if error != OK: - printerr("could not open %s "%file_path) - task.complete({}) - return task - var header : PoolStringArray = [_header[0] % MIME_TYPES.get(file_path.get_extension(), "application/octet-stream")] - header.append("Content-Length: %s" % file.get_len()) - header.append("x-upsert: %s" % upsert) - task.connect("completed", self, "_on_task_completed") - task._setup( - task.METHODS.UPLOAD_OBJECT, - endpoint, - header + _bearer, - "", - file.get_buffer(file.get_len()) - ) - _current_task = task - set_process_internal(requesting_raw) - file.close() - return task + requesting_raw = true + var task : StorageTask = StorageTask.new() + var endpoint : String = _config.supabaseUrl + _rest_endpoint + id + "/" + object + var file : File = File.new() + var error : int = file.open(file_path, File.READ) + if error != OK: + printerr("could not open %s "%file_path) + task.complete({}) + return task + var header : PoolStringArray = [_header[0] % MIME_TYPES.get(file_path.get_extension(), "application/octet-stream")] + header.append("Content-Length: %s" % file.get_len()) + header.append("x-upsert: %s" % upsert) + task.connect("completed", self, "_on_task_completed") + task._setup( + task.METHODS.UPLOAD_OBJECT, + endpoint, + header + _bearer, + "", + file.get_buffer(file.get_len()) + ) + _current_task = task + set_process_internal(requesting_raw) + file.close() + return task func update(bucket_path : String, file_path : String) -> StorageTask: - requesting_raw = true - var endpoint : String = _config.supabaseUrl + _rest_endpoint + id + "/" + bucket_path - var file : File = File.new() - file.open(file_path, File.READ) - var header : PoolStringArray = [_header[0] % MIME_TYPES[file_path.get_extension()]] - header.append("Content-Length: %s" % file.get_len()) - var task : StorageTask = StorageTask.new() - task.connect("completed", self, "_on_task_completed") - task._setup( - task.METHODS.UPDATE_OBJECT, - endpoint, - header + _bearer, - "", - file.get_buffer(file.get_len()) - ) - _current_task = task - set_process_internal(requesting_raw) - file.close() - return task + requesting_raw = true + var endpoint : String = _config.supabaseUrl + _rest_endpoint + id + "/" + bucket_path + var file : File = File.new() + file.open(file_path, File.READ) + var header : PoolStringArray = [_header[0] % MIME_TYPES[file_path.get_extension()]] + header.append("Content-Length: %s" % file.get_len()) + var task : StorageTask = StorageTask.new() + task.connect("completed", self, "_on_task_completed") + task._setup( + task.METHODS.UPDATE_OBJECT, + endpoint, + header + _bearer, + "", + file.get_buffer(file.get_len()) + ) + _current_task = task + set_process_internal(requesting_raw) + file.close() + return task func move(source_path : String, destination_path : String) -> StorageTask: - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "move" - var task : StorageTask = StorageTask.new() - var header : PoolStringArray = [_header[0] % "application/json"] - task._setup( - task.METHODS.MOVE_OBJECT, - endpoint, - header + _bearer, - to_json({bucketId = id, sourceKey = source_path, destinationKey = destination_path})) - _process_task(task) - return task + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "move" + var task : StorageTask = StorageTask.new() + var header : PoolStringArray = [_header[0] % "application/json"] + task._setup( + task.METHODS.MOVE_OBJECT, + endpoint, + header + _bearer, + to_json({bucketId = id, sourceKey = source_path, destinationKey = destination_path})) + _process_task(task) + return task func create_signed_url(object : String, expires_in : int = 60000) -> StorageTask: - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "sign/" + id + "/" + object - var task : StorageTask = StorageTask.new() - var header : PoolStringArray = [_header[0] % "application/json"] - task._setup( - task.METHODS.CREATE_SIGNED_URL, - endpoint, - header + _bearer, - to_json({expiresIn = expires_in}) - ) - _process_task(task) - return task + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "sign/" + id + "/" + object + var task : StorageTask = StorageTask.new() + var header : PoolStringArray = [_header[0] % "application/json"] + task._setup( + task.METHODS.CREATE_SIGNED_URL, + endpoint, + header + _bearer, + to_json({expiresIn = expires_in}) + ) + _process_task(task) + return task func download(object : String, to_path : String = "", private : bool = false) -> StorageTask: - if not private: - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "public/" + id + "/" + object - var task : StorageTask = StorageTask.new() - var header : PoolStringArray = [_header[0] % "application/json"] - task._setup( - task.METHODS.DOWNLOAD, - endpoint, - header + _bearer - ) - _process_task(task, {download_file = to_path}) - return task - else: - var endpoint : String = _config.supabaseUrl + _rest_endpoint + "authenticated/" + id + "/" + object - var task : StorageTask = StorageTask.new() - var header : PoolStringArray = [_header[0] % "application/json"] - task._setup( - task.METHODS.DOWNLOAD, - endpoint, - header + _bearer - ) - _process_task(task, {download_file = to_path}) - return task + if not private: + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "public/" + id + "/" + object + var task : StorageTask = StorageTask.new() + var header : PoolStringArray = [_header[0] % "application/json"] + task._setup( + task.METHODS.DOWNLOAD, + endpoint, + header + _bearer + ) + _process_task(task, {download_file = to_path}) + return task + else: + var endpoint : String = _config.supabaseUrl + _rest_endpoint + "authenticated/" + id + "/" + object + var task : StorageTask = StorageTask.new() + var header : PoolStringArray = [_header[0] % "application/json"] + task._setup( + task.METHODS.DOWNLOAD, + endpoint, + header + _bearer + ) + _process_task(task, {download_file = to_path}) + return task func get_public_url(object : String) -> String: - return _config.supabaseUrl + _rest_endpoint + "public/" + id + "/" + object + return _config.supabaseUrl + _rest_endpoint + "public/" + id + "/" + object func remove(objects : PoolStringArray) -> StorageTask: - var endpoint : String = _config.supabaseUrl + _rest_endpoint + id + ("/" + objects[0] if objects.size() == 1 else "") - var task : StorageTask = StorageTask.new() - var header : PoolStringArray = [_header[0] % "application/json"] - task._setup( - task.METHODS.REMOVE, - endpoint, - header + _bearer, - to_json({prefixes = objects}) if objects.size() > 1 else "" ) - _process_task(task) - return task + var endpoint : String = _config.supabaseUrl + _rest_endpoint + id + ("/" + objects[0] if objects.size() == 1 else "") + var task : StorageTask = StorageTask.new() + var header : PoolStringArray = [_header[0] % "application/json"] + task._setup( + task.METHODS.REMOVE, + endpoint, + header + _bearer, + to_json({prefixes = objects}) if objects.size() > 1 else "" ) + _process_task(task) + return task func _notification(what : int) -> void: - if what == NOTIFICATION_INTERNAL_PROCESS: - _internal_process(get_process_delta_time()) + if what == NOTIFICATION_INTERNAL_PROCESS: + _internal_process(get_process_delta_time()) func _internal_process(_delta : float) -> void: - if not requesting_raw: - set_process_internal(false) - return - - var task : StorageTask = _current_task - - match _http_client.get_status(): - HTTPClient.STATUS_DISCONNECTED: - _http_client.connect_to_host(_config.supabaseUrl, 443, true) - - HTTPClient.STATUS_RESOLVING, HTTPClient.STATUS_REQUESTING, HTTPClient.STATUS_CONNECTING: - _http_client.poll() - - HTTPClient.STATUS_CONNECTED: - var err : int = _http_client.request_raw(task._method, task._endpoint.replace(_config.supabaseUrl, ""), task._headers, task._bytepayload) - if err : - task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CONNECTION_ERROR}) - _on_task_completed(task) - - HTTPClient.STATUS_BODY: - if _http_client.has_response() or _reading_body: - _reading_body = true - - # If there is a response... - if _response_headers.empty(): - _response_headers = _http_client.get_response_headers() # Get response headers. - _response_code = _http_client.get_response_code() - - for header in _response_headers: - if "Content-Length" in header: - _content_length = header.trim_prefix("Content-Length: ").to_int() - - _http_client.poll() - var chunk : PoolByteArray = _http_client.read_response_body_chunk() # Get a chunk. - if chunk.size() == 0: - # Got nothing, wait for buffers to fill a bit. - pass - else: - _response_data += chunk # Append to read buffer. - if _content_length != 0: - pass - if _http_client.get_status() != HTTPClient.STATUS_BODY: - task._on_task_completed(0, _response_code, _response_headers, []) - else: - task._on_task_completed(0, _response_code, _response_headers, []) - - HTTPClient.STATUS_CANT_CONNECT: - task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CANT_CONNECT}) - HTTPClient.STATUS_CANT_RESOLVE: - task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CANT_RESOLVE}) - HTTPClient.STATUS_CONNECTION_ERROR: - task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CONNECTION_ERROR}) - HTTPClient.STATUS_SSL_HANDSHAKE_ERROR: - task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_SSL_HANDSHAKE_ERROR}) + if not requesting_raw: + set_process_internal(false) + return + + var task : StorageTask = _current_task + + match _http_client.get_status(): + HTTPClient.STATUS_DISCONNECTED: + _http_client.connect_to_host(_config.supabaseUrl, 443, true) + + HTTPClient.STATUS_RESOLVING, HTTPClient.STATUS_REQUESTING, HTTPClient.STATUS_CONNECTING: + _http_client.poll() + + HTTPClient.STATUS_CONNECTED: + var err : int = _http_client.request_raw(task._method, task._endpoint.replace(_config.supabaseUrl, ""), task._headers, task._bytepayload) + if err : + task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CONNECTION_ERROR}) + _on_task_completed(task) + + HTTPClient.STATUS_BODY: + if _http_client.has_response() or _reading_body: + _reading_body = true + + # If there is a response... + if _response_headers.empty(): + _response_headers = _http_client.get_response_headers() # Get response headers. + _response_code = _http_client.get_response_code() + + for header in _response_headers: + if "Content-Length" in header: + _content_length = header.trim_prefix("Content-Length: ").to_int() + + _http_client.poll() + var chunk : PoolByteArray = _http_client.read_response_body_chunk() # Get a chunk. + if chunk.size() == 0: + # Got nothing, wait for buffers to fill a bit. + pass + else: + _response_data += chunk # Append to read buffer. + if _content_length != 0: + pass + if _http_client.get_status() != HTTPClient.STATUS_BODY: + task._on_task_completed(0, _response_code, _response_headers, []) + else: + task._on_task_completed(0, _response_code, _response_headers, []) + + HTTPClient.STATUS_CANT_CONNECT: + task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CANT_CONNECT}) + HTTPClient.STATUS_CANT_RESOLVE: + task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CANT_RESOLVE}) + HTTPClient.STATUS_CONNECTION_ERROR: + task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_CONNECTION_ERROR}) + HTTPClient.STATUS_SSL_HANDSHAKE_ERROR: + task.error = SupabaseStorageError.new({statusCode = HTTPRequest.RESULT_SSL_HANDSHAKE_ERROR}) # --- func _process_task(task : StorageTask, _params : Dictionary = {}) -> void: - var httprequest : HTTPRequest = HTTPRequest.new() - add_child(httprequest) - if not _params.empty(): - httprequest.download_file = _params.get("download_file", "") - task.connect("completed", self, "_on_task_completed") - task.push_request(httprequest) - _pooled_tasks.append(task) + var httprequest : HTTPRequest = HTTPRequest.new() + add_child(httprequest) + if not _params.empty(): + httprequest.download_file = _params.get("download_file", "") + task.connect("completed", self, "_on_task_completed") + task.push_request(httprequest) + _pooled_tasks.append(task) # .............. HTTPRequest completed func _on_task_completed(task : StorageTask) -> void: - if task._handler : task._handler.queue_free() - if requesting_raw: - _clear_raw_request() - if task.data!=null and not task.data.empty(): - match task._code: - task.METHODS.LIST_OBJECTS: emit_signal("listed_objects", task.data) - task.METHODS.UPLOAD_OBJECT: emit_signal("uploaded_object", task.data) - task.METHODS.UPDATE_OBJECT: emit_signal("updated_object", task.data) - task.METHODS.MOVE_OBJECT: emit_signal("moved_object", task.data) - task.METHODS.REMOVE: emit_signal("removed_objects", task.data) - task.METHODS.CREATE_SIGNED_URL: emit_signal("created_signed_url", task.data) - task.METHODS.DOWNLOAD: emit_signal("downloaded_object", task.data) - elif task.error != null: - emit_signal("error", task.error) - _pooled_tasks.erase(task) + if task._handler : task._handler.queue_free() + if requesting_raw: + _clear_raw_request() + if task.data!=null and not task.data.empty(): + match task._code: + task.METHODS.LIST_OBJECTS: emit_signal("listed_objects", task.data) + task.METHODS.UPLOAD_OBJECT: emit_signal("uploaded_object", task.data) + task.METHODS.UPDATE_OBJECT: emit_signal("updated_object", task.data) + task.METHODS.MOVE_OBJECT: emit_signal("moved_object", task.data) + task.METHODS.REMOVE: emit_signal("removed_objects", task.data) + task.METHODS.CREATE_SIGNED_URL: emit_signal("created_signed_url", task.data) + task.METHODS.DOWNLOAD: emit_signal("downloaded_object", task.data) + elif task.error != null: + emit_signal("error", task.error) + _pooled_tasks.erase(task) func _clear_raw_request() -> void: - requesting_raw = false - _current_task = null - _reading_body = false - _response_headers = [] - _response_data = [] - _content_length = -1 - _response_code = -1 - set_process_internal(requesting_raw) - _http_client.close() + requesting_raw = false + _current_task = null + _reading_body = false + _response_headers = [] + _response_data = [] + _content_length = -1 + _response_code = -1 + set_process_internal(requesting_raw) + _http_client.close() diff --git a/addons/supabase/Storage/storage_error.gd b/addons/supabase/Storage/storage_error.gd index 0f80684..6a36513 100644 --- a/addons/supabase/Storage/storage_error.gd +++ b/addons/supabase/Storage/storage_error.gd @@ -7,11 +7,11 @@ var error : String = "empty" var message : String = "empty" func _init(dictionary : Dictionary = {}) -> void: - _error = dictionary - if not _error.empty(): - status_code = _error.get("statusCode", "empty") as String - error = _error.get("error", "empty") - message = _error.get("message", "empty") + _error = dictionary + if not _error.empty(): + status_code = _error.get("statusCode", "empty") as String + error = _error.get("error", "empty") + message = _error.get("message", "empty") func _to_string(): - return "%s >> %s: %s" % [status_code, error, message] + return "%s >> %s: %s" % [status_code, error, message] diff --git a/addons/supabase/Storage/storage_task.gd b/addons/supabase/Storage/storage_task.gd index 6726190..034fe81 100644 --- a/addons/supabase/Storage/storage_task.gd +++ b/addons/supabase/Storage/storage_task.gd @@ -4,21 +4,21 @@ extends Reference signal completed(task) enum METHODS { - LIST_BUCKETS, - GET_BUCKET, - CREATE_BUCKET, - UPDATE_BUCKET, - EMPTY_BUCKET, - DELETE_BUCKET, - - LIST_OBJECTS, - UPLOAD_OBJECT, - UPDATE_OBJECT, - MOVE_OBJECT, - CREATE_SIGNED_URL, - DOWNLOAD, - GET_PUBLIC_URL, - REMOVE + LIST_BUCKETS, + GET_BUCKET, + CREATE_BUCKET, + UPDATE_BUCKET, + EMPTY_BUCKET, + DELETE_BUCKET, + + LIST_OBJECTS, + UPLOAD_OBJECT, + UPDATE_OBJECT, + MOVE_OBJECT, + CREATE_SIGNED_URL, + DOWNLOAD, + GET_PUBLIC_URL, + REMOVE } var _code : int @@ -36,48 +36,48 @@ var error : SupabaseStorageError var _handler : HTTPRequest = null func _init(data = null, error : SupabaseStorageError = null) -> void: - self.data = data - self.error = error + self.data = data + self.error = error func _setup(code : int, endpoint : String, headers : PoolStringArray, payload : String = "", bytepayload : PoolByteArray = []): - _code = code - _endpoint = endpoint - _headers = headers - _payload = payload - _bytepayload = bytepayload - _method = match_code(code) + _code = code + _endpoint = endpoint + _headers = headers + _payload = payload + _bytepayload = bytepayload + _method = match_code(code) func match_code(code : int) -> int: - match code: - METHODS.LIST_BUCKETS, METHODS.GET_BUCKET, METHODS.DOWNLOAD: return HTTPClient.METHOD_GET - METHODS.CREATE_BUCKET, METHODS.UPDATE_BUCKET, METHODS.EMPTY_BUCKET, \ - METHODS.LIST_OBJECTS, METHODS.UPLOAD_OBJECT, METHODS.MOVE_OBJECT, \ - METHODS.CREATE_SIGNED_URL: return HTTPClient.METHOD_POST - METHODS.UPDATE_OBJECT: return HTTPClient.METHOD_PUT - METHODS.DELETE_BUCKET, METHODS.REMOVE: return HTTPClient.METHOD_DELETE - _: return HTTPClient.METHOD_GET + match code: + METHODS.LIST_BUCKETS, METHODS.GET_BUCKET, METHODS.DOWNLOAD: return HTTPClient.METHOD_GET + METHODS.CREATE_BUCKET, METHODS.UPDATE_BUCKET, METHODS.EMPTY_BUCKET, \ + METHODS.LIST_OBJECTS, METHODS.UPLOAD_OBJECT, METHODS.MOVE_OBJECT, \ + METHODS.CREATE_SIGNED_URL: return HTTPClient.METHOD_POST + METHODS.UPDATE_OBJECT: return HTTPClient.METHOD_PUT + METHODS.DELETE_BUCKET, METHODS.REMOVE: return HTTPClient.METHOD_DELETE + _: return HTTPClient.METHOD_GET func push_request(httprequest : HTTPRequest) -> void: - _handler = httprequest - httprequest.connect("request_completed", self, "_on_task_completed") - httprequest.request(_endpoint, _headers, true, _method, _payload) + _handler = httprequest + httprequest.connect("request_completed", self, "_on_task_completed") + httprequest.request(_endpoint, _headers, true, _method, _payload) func _on_task_completed(result : int, response_code : int, headers : PoolStringArray, body : PoolByteArray) -> void: - var result_body = JSON.parse(body.get_string_from_utf8()).result if body.get_string_from_utf8() else {} - if response_code in [200, 201, 204]: - if _code == METHODS.DOWNLOAD: - complete(body) - else: - complete(result_body) - else: - var supabase_error : SupabaseStorageError = SupabaseStorageError.new(result_body) - complete(null, supabase_error) + var result_body = JSON.parse(body.get_string_from_utf8()).result if body.get_string_from_utf8() else {} + if response_code in [200, 201, 204]: + if _code == METHODS.DOWNLOAD: + complete(body) + else: + complete(result_body) + else: + var supabase_error : SupabaseStorageError = SupabaseStorageError.new(result_body) + complete(null, supabase_error) func complete(_result, _error : SupabaseStorageError = null) -> void: - data = _result - error = _error - if _handler: _handler.queue_free() - emit_signal("completed", self) + data = _result + error = _error + if _handler: _handler.queue_free() + emit_signal("completed", self) diff --git a/addons/supabase/Supabase/supabase.gd b/addons/supabase/Supabase/supabase.gd index 98677ec..43bfe80 100644 --- a/addons/supabase/Supabase/supabase.gd +++ b/addons/supabase/Supabase/supabase.gd @@ -7,44 +7,52 @@ var database : SupabaseDatabase var realtime : SupabaseRealtime var storage : SupabaseStorage +var debug: bool = false + var config : Dictionary = { - "supabaseUrl": "", - "supabaseKey": "" + "supabaseUrl": "", + "supabaseKey": "" } var header : PoolStringArray = [ - "Content-Type: application/json", - "Accept: application/json" + "Content-Type: application/json", + "Accept: application/json" ] func _ready() -> void: - load_config() - load_nodes() + load_config() + load_nodes() # Load all config settings from ProjectSettings func load_config() -> void: - if config.supabaseKey != "" and config.supabaseUrl != "": - pass - else: - var env = ConfigFile.new() - var err = env.load("res://addons/supabase/.env") - if err == OK: - for key in config.keys(): - var value : String = env.get_value(ENVIRONMENT_VARIABLES, key, "") - if value == "": - printerr("%s has not a valid value." % key) - else: - config[key] = value - else: - printerr("Unable to read .env file at path 'res://.env'") - header.append("apikey: %s"%[config.supabaseKey]) + if config.supabaseKey != "" and config.supabaseUrl != "": + pass + else: + var env = ConfigFile.new() + var err = env.load("res://addons/supabase/.env") + if err == OK: + for key in config.keys(): + var value : String = env.get_value(ENVIRONMENT_VARIABLES, key, "") + if value == "": + printerr("%s has not a valid value." % key) + else: + config[key] = value + else: + printerr("Unable to read .env file at path 'res://.env'") + header.append("apikey: %s"%[config.supabaseKey]) func load_nodes() -> void: - auth = SupabaseAuth.new(config, header) - database = SupabaseDatabase.new(config, header) - realtime = SupabaseRealtime.new(config) - storage = SupabaseStorage.new(config) - add_child(auth) - add_child(database) - add_child(realtime) - add_child(storage) + auth = SupabaseAuth.new(config, header) + database = SupabaseDatabase.new(config, header) + realtime = SupabaseRealtime.new(config) + storage = SupabaseStorage.new(config) + add_child(auth) + add_child(database) + add_child(realtime) + add_child(storage) + +func debug(debugging: bool) -> void: + debug = debugging + +func _print_debug(msg: String) -> void: + if debug: print_debug(msg) diff --git a/addons/supabase/User/user.gd b/addons/supabase/User/user.gd deleted file mode 100644 index 2a0e50a..0000000 --- a/addons/supabase/User/user.gd +++ /dev/null @@ -1,42 +0,0 @@ -extends Reference -class_name SupabaseUser - -var email : String -var id : String -var access_token : String -var token_type : String -var refresh_token : String -var expires_in : float -var created_at : String -var updated_at : String -var last_sign_in_at : String -var user : Dictionary -var user_metadata : Dictionary -var app_metadata : Dictionary -var role : String -var confirmation_sent_at : String - -func _init(user_dictionary : Dictionary) -> void: - if user_dictionary.has("user"): - access_token = user_dictionary.access_token - token_type = user_dictionary.token_type - refresh_token = user_dictionary.refresh_token - expires_in = user_dictionary.expires_in - user = user_dictionary.user - last_sign_in_at = user.last_sign_in_at - else: - user = user_dictionary - confirmation_sent_at = user.confirmation_sent_at - - email = user.email - id = user.id - created_at = user.created_at - updated_at = user.updated_at - user_metadata = user.user_metadata if user.user_metadata != null else {} - role = user.role - -func _to_string(): - var to_string : String = "%-10s %s\n" % ["USER ID:", id] - to_string += "%-10s %s\n" % ["EMAIL:", email] - to_string += "%-10s %s\n" % ["ROLE:", role] - return to_string diff --git a/addons/supabase/plugin.cfg b/addons/supabase/plugin.cfg index 9544c07..60d2d2e 100644 --- a/addons/supabase/plugin.cfg +++ b/addons/supabase/plugin.cfg @@ -3,5 +3,5 @@ name="Supabase" description="A lightweight addon which integrates Supabase APIs for Godot Engine out of the box." author="Nicolò (fenix-hub) Santilio" -version="3.0.5" +version="3.2.0" script="plugin.gd"