class_name BugbotServerGiteaAPI extends "res://addons/Bugbot/Scripts/server_api.gd" #region Consts const DEFAULT_SERVER : StringName = &"https://gitea.example.com" const DEFAULT_BASE_URL : StringName = &"api/v1/" const DEFAULT_OWNER_NAME : StringName = &"ProjectOwner" const DEFAULT_REPO_NAME : StringName = &"ProjectName" const DEFAULT_API_KEY : StringName = &"0123456789abcdef0123456789abcdef01234567" const DEFAULT_BUG_LABEL : StringName = &"Kind/Bug" const DEFAULT_STATUS_LABEL : StringName = &"Status/Need More Info" const DEFAULT_VERSION_LABEL_PREFIX : StringName = &"Version/" const DEFAULT_HARDWARE_LABEL_PREFIX : StringName = &"Hardware/" const DEFAULT_OS_LABEL_PREFIX : StringName = &"OS/" const DEFAULT_COMPONENT_LABEL_PREFIX : StringName = &"Component/" const DEFAULT_PRIORITY_LABEL_PREFIX : StringName = &"Priority/" const DEFAULT_SHOW_ASSIGNED_AS_IN_PROGRESS : bool = true const DEFAULT_UNRESOLVED_STATUSES : Array = [] const DEFAULT_IN_PROGRESS_STATUSES : Array = [] const DEFAULT_RESOLVED_STATUSES : Array = [] #endregion func __return_list_of_bugs_thread(map_name:String, callback:Callable) -> void: var http_client : HTTPClient = HTTPClient.new() if __connect_to_server(http_client, ProjectSettings.get_setting("bugbot/reporting/gitea/server", DEFAULT_SERVER)) != HTTPClient.STATUS_CONNECTED: printerr("Could not connect to server.") return var api_url : String = __build_url_string("issues") api_url += "?state=all&labels=" + ProjectSettings.get_setting("bugbot/reporting/gitea/bug_label", DEFAULT_BUG_LABEL).uri_encode() var header_data : Array = __create_header_data() var error : int = http_client.request(HTTPClient.METHOD_GET, api_url, header_data) assert(error == Error.OK) while http_client.get_status() == HTTPClient.STATUS_REQUESTING: http_client.poll() assert(http_client.get_status() == HTTPClient.STATUS_BODY or http_client.get_status() == HTTPClient.STATUS_CONNECTED) var response_string : String = __get_http_client_chunk_response(http_client) var response_data := JSON.parse_string(response_string) if __validate_server_response(response_data) != Error.OK: return var bug_array : Array var label_dict : Dictionary = { "show_unresolved": ProjectSettings.get_setting("bugbot/markers/unresolved/show_unresolved_bugs", BugbotServerAPI.DEFAULT_SHOW_UNRESOLVED_BUGS), "show_in_progress": ProjectSettings.get_setting("bugbot/markers/in_progress/show_in_progress_bugs", BugbotServerAPI.DEFAULT_SHOW_IN_PROGRESS_BUGS), "show_resolved": ProjectSettings.get_setting("bugbot/markers/resolved/show_resolved_bugs", BugbotServerAPI.DEFAULT_SHOW_RESOLVED_BUGS), "unresolved_labels": ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/unresolved_statuses", DEFAULT_UNRESOLVED_STATUSES), "in_progress_labels": ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/in_progress_statuses", DEFAULT_IN_PROGRESS_STATUSES), "resolved_labels": ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/resolved_statuses", DEFAULT_RESOLVED_STATUSES), } for bug_in:Dictionary in response_data: var assigned_as_in_progress : bool = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/show_assigned_as_in_progress", DEFAULT_SHOW_ASSIGNED_AS_IN_PROGRESS) var bug : BugbotBugData = __create_bug_data_from_server_response(bug_in, map_name, label_dict, true, assigned_as_in_progress) if bug: bug_array.append(bug) callback.call_deferred(bug_array) __bugbot_server_thread.call_deferred("wait_to_finish") func __prepare_form_thread(callback:Callable) -> void: var http_client : HTTPClient = HTTPClient.new() if __connect_to_server(http_client, ProjectSettings.get_setting("bugbot/reporting/gitea/server", DEFAULT_SERVER)) != HTTPClient.STATUS_CONNECTED: printerr("Could not connect to server.") return # Pull a list of issue labels so we can get the ones we care about. var api_url : String = __build_url_string("labels") var header_data : Array = __create_header_data() var error : int = http_client.request(HTTPClient.METHOD_GET, api_url, header_data) assert(error == Error.OK) while http_client.get_status() == HTTPClient.STATUS_REQUESTING: http_client.poll() assert(http_client.get_status() == HTTPClient.STATUS_BODY or http_client.get_status() == HTTPClient.STATUS_CONNECTED) var response_string : String = __get_http_client_chunk_response(http_client) var response_data : Variant = JSON.parse_string(response_string) if __validate_server_response(response_data) != Error.OK: return var tag_lists : Array tag_lists.resize(BugbotTagArray.MAX) tag_lists[BugbotTagArray.VERSION] = Array() tag_lists[BugbotTagArray.HARDWARE] = Array() tag_lists[BugbotTagArray.OS] = Array() tag_lists[BugbotTagArray.COMPONENT] = Array() tag_lists[BugbotTagArray.SEVERITY] = Array() var version_prefix : String = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/version_label_prefix", DEFAULT_VERSION_LABEL_PREFIX) var hardware_prefix : String = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/hardware_label_prefix", DEFAULT_HARDWARE_LABEL_PREFIX) var os_prefix : String = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/os_label_prefix", DEFAULT_OS_LABEL_PREFIX) var component_prefix : String = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/component_label_prefix", DEFAULT_COMPONENT_LABEL_PREFIX) var priority_prefix : String = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/priority_label_prefix", DEFAULT_PRIORITY_LABEL_PREFIX) response_data.sort_custom(func(a,b):return a["id"] < b["id"]) for label_in:Dictionary in response_data: if version_prefix and label_in["name"].begins_with(version_prefix): label_in["name"] = (label_in["name"] as String).replace(version_prefix, "") tag_lists[BugbotTagArray.VERSION].append(label_in) if hardware_prefix and label_in["name"].begins_with(hardware_prefix): label_in["name"] = (label_in["name"] as String).replace(hardware_prefix, "") tag_lists[BugbotTagArray.HARDWARE].append(label_in) if os_prefix and label_in["name"].begins_with(os_prefix): label_in["name"] = (label_in["name"] as String).replace(os_prefix, "") tag_lists[BugbotTagArray.OS].append(label_in) if component_prefix and label_in["name"].begins_with(component_prefix): label_in["name"] = (label_in["name"] as String).replace(component_prefix, "") tag_lists[BugbotTagArray.COMPONENT].append(label_in) if priority_prefix and label_in["name"].begins_with(priority_prefix): label_in["name"] = (label_in["name"] as String).replace(priority_prefix, "") tag_lists[BugbotTagArray.SEVERITY].append(label_in) callback.call_deferred(tag_lists) __bugbot_server_thread.call_deferred("wait_to_finish") func __send_form_data_thread(data:Dictionary, map_name:String, bug_position:Vector3, bug_normal:Vector3, callback:Callable) -> void: var http_client : HTTPClient = HTTPClient.new() if __connect_to_server(http_client, ProjectSettings.get_setting("bugbot/reporting/gitea/server", DEFAULT_SERVER)) != HTTPClient.STATUS_CONNECTED: printerr("Could not connect to server.") return # Get a list of available labels to apply to the issue var api_url : String = __build_url_string("labels") var header_data : Array = __create_header_data() var error : int = http_client.request(HTTPClient.METHOD_GET, api_url, header_data) assert(error == Error.OK) while http_client.get_status() == HTTPClient.STATUS_REQUESTING: http_client.poll() assert(http_client.get_status() == HTTPClient.STATUS_BODY or http_client.get_status() == HTTPClient.STATUS_CONNECTED) var response_data : Variant = JSON.parse_string(__get_http_client_chunk_response(http_client)) if __validate_server_response(response_data) != Error.OK: return # Collect the label IDs for each label we want to apply to this issue var labels : Array var bug_label : String = ProjectSettings.get_setting("bugbot/reporting/gitea/bug_label", DEFAULT_BUG_LABEL) var status_label : String = ProjectSettings.get_setting("bugbot/reporting/gitea/default_status_label", DEFAULT_STATUS_LABEL) for label_in:Dictionary in response_data: if label_in["name"] == bug_label or label_in["name"] == status_label: labels.append(label_in["id"]) if data["labels"].has("version"): labels.append(data["labels"]["version"]["id"]) if data["labels"].has("hardware"): labels.append(data["labels"]["hardware"]["id"]) if data["labels"].has("os"): labels.append(data["labels"]["os"]["id"]) if data["labels"].has("component"): labels.append(data["labels"]["component"]["id"]) if data["labels"].has("severity"): labels.append(data["labels"]["severity"]["id"]) var marker_data : Dictionary = { "map_name": map_name, "bug_position": [bug_position.x, bug_position.y, bug_position.z], "bug_normal": [bug_normal.x, bug_normal.y, bug_normal.z] } var post_data : Dictionary = { "title": data["title"], "body": data["body"] + "\n\n" + JSON.stringify(marker_data, "", false), "labels": labels } # Post issue to Gitea api_url = __build_url_string("issues") var post_data_string : String = JSON.stringify(post_data) header_data = __create_header_data(post_data_string.length()) error = http_client.request(HTTPClient.METHOD_POST, api_url, header_data, post_data_string) assert(error == Error.OK) while http_client.get_status() == HTTPClient.STATUS_REQUESTING: http_client.poll() assert(http_client.get_status() == HTTPClient.STATUS_BODY or http_client.get_status() == HTTPClient.STATUS_CONNECTED) var post_response_data : Variant = JSON.parse_string(__get_http_client_chunk_response(http_client)) if __validate_server_response(post_response_data) != Error.OK: return var label_dict : Dictionary = { "show_unresolved": ProjectSettings.get_setting("bugbot/markers/unresolved/show_unresolved_bugs", true), "show_in_progress": ProjectSettings.get_setting("bugbot/markers/in_progress/show_in_progress_bugs", true), "show_resolved": ProjectSettings.get_setting("bugbot/markers/resolved/show_resolved_bugs", false), "unresolved_labels": ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/unresolved_statuses", Array()), "in_progress_labels": ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/in_progress_statuses", Array()), "resolved_labels": ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/resolved_statuses", Array()), } var assigned_as_in_progress : bool = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/show_assigned_as_in_progress", DEFAULT_SHOW_ASSIGNED_AS_IN_PROGRESS) var bug_data : BugbotBugData = __create_bug_data_from_server_response(post_response_data, map_name, label_dict, false, assigned_as_in_progress) callback.call_deferred(bug_data) __bugbot_server_thread.call_deferred("wait_to_finish") func _current_server_api() -> String: return "Gitea" func __build_url_string(_api_suffix:String) -> String: return "/" + \ ProjectSettings.get_setting("bugbot/reporting/gitea/base_URL", DEFAULT_BASE_URL) + \ "/repos/" + \ ProjectSettings.get_setting("bugbot/reporting/gitea/owner_name", DEFAULT_OWNER_NAME) + \ "/" + \ ProjectSettings.get_setting("bugbot/reporting/gitea/repo_name", DEFAULT_REPO_NAME) + \ "/" + _api_suffix func __create_header_data(content_length:int = -1) -> Array: var header : Array = [ "User-Agent: Pirulo/1.0 (Godot)", "Accept: application/json", ] header.append("Authorization: token " + ProjectSettings.get_setting("bugbot/reporting/gitea/API_key", DEFAULT_API_KEY)) if content_length >= 0: header.append("Content-Type: application/json") header.append("Content-Length: " + String.num_uint64(content_length)) return header func __validate_server_response(_response:Variant) -> int: # If the response has a message field, make the assumption that this is # because the response was an error code. if _response.has("message"): var error_data : BugbotErrorData = BugbotErrorData.new() error_data.code = 1 error_data.message = _response["message"] error_data.url = _response["url"] printerr(error_data.message) __bugbot_server_thread.call_deferred("wait_to_finish") return Error.FAILED return Error.OK func __create_bug_data_from_server_response(bug_in:Dictionary, map_name:String, label_dict:Dictionary, validate_labels:bool, assigned_as_in_progress:bool) -> BugbotBugData: var bugbot_marker_string : String = bug_in["body"].split("\n")[-1] if not bugbot_marker_string.begins_with("{") and not bugbot_marker_string.ends_with("}"): return null var bug : BugbotBugData = BugbotBugData.new() # Check if the map name is valid for the scene we're in. var bugbot_marker_data : Dictionary = JSON.parse_string(bugbot_marker_string) bug.map_name = bugbot_marker_data["map_name"] if bug.map_name != map_name: return null var marker_location : Array = bugbot_marker_data["bug_position"] var marker_normal : Array = bugbot_marker_data["bug_normal"] bug.marker_position = Vector3(marker_location[0], marker_location[1], marker_location[2]) bug.marker_normal = Vector3(marker_normal[0], marker_normal[1], marker_normal[2]) var bug_labels : Array = [] var resolved_tag_found : bool = false var in_progress_tag_found : bool = false var unresolved_tag_found : bool = false # Find which resolution statuses apply to this bug. bug.is_open = (bug_in["state"] == "open") if not bug.is_open: resolved_tag_found = true elif assigned_as_in_progress and bug_in["assignees"] is Array: in_progress_tag_found = true else: for labels:Dictionary in bug_in["labels"]: bug_labels.append(labels["name"] as String) for label:String in label_dict["resolved_labels"] as Array: if bug_labels.has(label): resolved_tag_found = true for label:String in label_dict["in_progress_labels"] as Array: if bug_labels.has(label): in_progress_tag_found = true for label:String in label_dict["unresolved_labels"] as Array: if bug_labels.has(label): unresolved_tag_found = true # Figure out which resolution tag to prioritise, and whether we should show the marker. var show_marker : bool if resolved_tag_found: bug.resolution = BugbotServerAPI.RESOLVED_TAG show_marker = label_dict["show_resolved"] as bool elif in_progress_tag_found: bug.resolution = BugbotServerAPI.IN_PROGRESS_TAG show_marker = label_dict["show_in_progress"] as bool elif unresolved_tag_found or (label_dict["unresolved_labels"] as Array).is_empty(): bug.resolution = BugbotServerAPI.UNRESOLVED_TAG show_marker = label_dict["show_unresolved"] as bool if validate_labels and not show_marker: return null bug.id = bug_in["id"] bug.title = bug_in["title"] bug.body = bug_in["body"] bug.component = &"Unused" bug.platform = &"Unused" bug.operating_system = &"Unused" bug.severity = "Severity will go here." bug.status = bug_in["state"] return bug