diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdd1a32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Project specific files +.env +export/ +scn/ +project.godot +icon.png +*.import + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg +default_env.tres + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ \ No newline at end of file diff --git a/addons/supabase/.env b/addons/supabase/.env new file mode 100644 index 0000000..060d21e --- /dev/null +++ b/addons/supabase/.env @@ -0,0 +1,4 @@ +[supabase/config] + +supabaseUrl="" +supabaseKey="" diff --git a/addons/supabase/Database/database_error.gd b/addons/supabase/Database/database_error.gd index b6cab6a..5450bdd 100644 --- a/addons/supabase/Database/database_error.gd +++ b/addons/supabase/Database/database_error.gd @@ -6,7 +6,7 @@ var code : String = "empty" #var id : int = -1 var message : String = "empty" var hint : String = "empty" -var details : String = "empty" +var details func _init(dictionary : Dictionary = {}) -> void: _error = dictionary @@ -14,7 +14,7 @@ func _init(dictionary : Dictionary = {}) -> void: 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.details if _error.has("details") and _error.details != null else "empty" + details = _error.get("details", "") ### always different behavior ??? func _to_string(): diff --git a/addons/supabase/Database/query.gd b/addons/supabase/Database/query.gd index f22f0f7..8f46e67 100644 --- a/addons/supabase/Database/query.gd +++ b/addons/supabase/Database/query.gd @@ -5,11 +5,12 @@ var query_struct : Dictionary = { table = "", select = PoolStringArray([]), order = PoolStringArray([]), + Or = PoolStringArray([]), eq = PoolStringArray([]), neq = PoolStringArray([]), like = PoolStringArray([]), ilike = PoolStringArray([]), - IS = PoolStringArray([]), + Is = PoolStringArray([]), in = PoolStringArray([]), fts = PoolStringArray([]), plfts = PoolStringArray([]), @@ -56,7 +57,8 @@ enum Filters { FTS, PLFTS, PHFLTS, - WFTS + WFTS, + OR } func _init(_raw_query : String = "", _raw_type : int = -1, _raw_header : PoolStringArray = [], _raw_body : String = ""): @@ -72,14 +74,17 @@ 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 += (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 @@ -90,7 +95,7 @@ func from(table_name : String) -> SupabaseQuery: # Insert new Row func insert(fields : Array, upsert : bool = false) -> SupabaseQuery: request = REQUESTS.INSERT - body = JSON.print(fields) + body = to_json(fields) if upsert : header += PoolStringArray(["Prefer: resolution=merge-duplicates"]) return self @@ -103,7 +108,7 @@ func select(columns : PoolStringArray = PoolStringArray(["*"])) -> SupabaseQuery # Update Rows func update(fields : Dictionary) -> SupabaseQuery: request = REQUESTS.UPDATE - body = JSON.print(fields) + body = to_json(fields) return self # Delete Rows @@ -132,30 +137,43 @@ func order(column : String, direction : int = Directions.Ascending, nullsorder : ## [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 + +func match_filter(filter : int) -> String: var filter_str : String match filter: Filters.EQUAL: filter_str = "eq" - Filters.NOT_EQUAL: filter_str = "neq" + 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.LESS_THAN: filter_str = "lt" Filters.GREATER_THAN_OR_EQUAL: filter_str = "gte" - Filters.LESS_THAN_OR_EQUAL: filter_str = "lte" Filters.LIKE: filter_str = "like" - Filters.ILIKE: filter_str = "ilike" - Filters.IS: filter_str = "is" - Filters.IN: filter_str = "in" - Filters.FTS: filter_str = "fts" + 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" - var array : PoolStringArray = query_struct[filter_str] as PoolStringArray - var struct_filter : String = filter_str - if _props.has("config"): - struct_filter+= "({config})".format(_props) - array.append("%s=%s.%s" % [column, struct_filter, value]) - query_struct[filter_str] = array - return self - + return filter_str # Finds all rows whose value on the stated columns match the specified values. func match(query_dict : Dictionary) -> SupabaseQuery: @@ -195,7 +213,7 @@ func lte(column : String, value : String) -> SupabaseQuery: # 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, value) + filter(column, Filters.LIKE, "*%s*"%value) return self # Finds all rows whose value in the stated column matches the supplied pattern (case insensitive). @@ -204,8 +222,8 @@ func ilike(column : String, value : String) -> SupabaseQuery: 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) -> SupabaseQuery: - filter(column, Filters.IS, str(value)) +func Is(column : String, value, negate : bool = false) -> SupabaseQuery: + 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. @@ -213,8 +231,8 @@ func In(column : String, array : PoolStringArray) -> SupabaseQuery: filter(column, Filters.IN, "("+array.join(",")+")") return self -func Or(column : String, value : String) -> SupabaseQuery: - filter(column, Filters.OR, value) +func Or(queries : Array) -> SupabaseQuery: + filter("", Filters.OR, "", {queries = queries}) return self # Text Search diff --git a/addons/supabase/Realtime/realtime_client.gd b/addons/supabase/Realtime/realtime_client.gd index 02a995e..5b0309a 100644 --- a/addons/supabase/Realtime/realtime_client.gd +++ b/addons/supabase/Realtime/realtime_client.gd @@ -109,22 +109,22 @@ func _on_data() -> void: PhxEvents.REPLY: if _check_response(data) == 0: pass -# print("Received reply = "+to_json(data)) + print_debug("Received reply = "+to_json(data)) PhxEvents.JOIN: if _check_response(data) == 0: pass -# print("Joined topic '%s'" % data.topic) + print_debug("Joined topic '%s'" % data.topic) PhxEvents.LEAVE: if _check_response(data) == 0: pass -# print("Left topic '%s'" % data.topic) + print_debug("Left topic '%s'" % data.topic) PhxEvents.CLOSE: pass -# print("Channel closed.") + print_debug("Channel closed.") PhxEvents.ERROR: emit_signal("error", data.payload) SupabaseEvents.DELETE, SupabaseEvents.INSERT, SupabaseEvents.UPDATE: -# print("Received %s event..." % data.event) + print_debug("Received %s event..." % data.event) var channel : RealtimeChannel = get_channel(data.topic) if channel != null: channel._publish(data) diff --git a/addons/supabase/Storage/storage_bucket.gd b/addons/supabase/Storage/storage_bucket.gd index 2b7e613..27f88ca 100644 --- a/addons/supabase/Storage/storage_bucket.gd +++ b/addons/supabase/Storage/storage_bucket.gd @@ -92,16 +92,18 @@ func upload(object : String, file_path : String, upsert : bool = false) -> Stora 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, - to_json({upsert = upsert}), + "", file.get_buffer(file.get_len()) ) _current_task = task diff --git a/addons/supabase/Supabase/supabase.gd b/addons/supabase/Supabase/supabase.gd index 573c5c4..98677ec 100644 --- a/addons/supabase/Supabase/supabase.gd +++ b/addons/supabase/Supabase/supabase.gd @@ -1,6 +1,6 @@ extends Node -const ENVIRONMENT_VARIABLES : String = "supabase/config/" +const ENVIRONMENT_VARIABLES : String = "supabase/config" var auth : SupabaseAuth var database : SupabaseDatabase @@ -24,16 +24,19 @@ func _ready() -> void: # Load all config settings from ProjectSettings func load_config() -> void: if config.supabaseKey != "" and config.supabaseUrl != "": - return - for key in config.keys(): - if ProjectSettings.has_setting(ENVIRONMENT_VARIABLES+key): - var value : String = ProjectSettings.get_setting(ENVIRONMENT_VARIABLES+key) - if value == "": - printerr("%s has not a valid value." % key) - else: - config[key] = value + 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("%s key is not defined." % key) + printerr("Unable to read .env file at path 'res://.env'") header.append("apikey: %s"%[config.supabaseKey]) func load_nodes() -> void: diff --git a/addons/supabase/plugin.cfg b/addons/supabase/plugin.cfg index 05bb792..c64f292 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="1.3" +version="3.0.2" script="plugin.gd"