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 UNRESOLVED_TAG : StringName = &"Status/Unresolved" const IN_PROGRESS_TAG : StringName = &"Status/In Progress" const RESOLVED_TAG : StringName = &"Status/Resolved" #endregion func _return_list_of_bugs(map_name:String, callback:Callable) -> int: return __start_thread_with_callback(__return_list_of_bugs_thread.bind(map_name, callback)) func _prepare_form(callback:Callable) -> int: return __start_thread_with_callback(__prepare_form_thread.bind(callback)) func _send_form_data(data:Dictionary, map_name:String, bug_position:Vector3, bug_normal:Vector3, callback:Callable) -> int: return __start_thread_with_callback(__send_form_data_thread.bind(data, map_name, bug_position, bug_normal, callback)) func __return_list_of_bugs_thread(map_name:String, callback:Callable) -> void: var http_client : HTTPClient = HTTPClient.new() if __connect_to_server(http_client, 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 resolved_labels : Array = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/resolved_statuses", Array()) var in_progress_labels : Array = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/in_progress_statuses", Array()) var unresolved_labels : Array = ProjectSettings.get_setting("bugbot/reporting/gitea/status_labels/unresolved_statuses", Array()) var show_resolved : bool = ProjectSettings.get_setting("bugbot/markers/resolved/show_resolved_bugs", false) var show_in_progress : bool = ProjectSettings.get_setting("bugbot/markers/in_progress/show_in_progress_bugs", true) var show_unresolved : bool = ProjectSettings.get_setting("bugbot/markers/unresolved/show_unresolved_bugs", true) for bug_in:Dictionary in response_data: 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("}"): continue 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: continue 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 else: for labels:Dictionary in bug_in["labels"]: bug_labels.append(labels["name"] as String) for label:String in resolved_labels: if bug_labels.has(label): resolved_tag_found = true for label:String in in_progress_labels: if bug_labels.has(label): in_progress_tag_found = true for label:String in unresolved_labels: 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 = RESOLVED_TAG show_marker = show_resolved elif in_progress_tag_found: bug.resolution = IN_PROGRESS_TAG show_marker = show_in_progress elif unresolved_tag_found or unresolved_labels.is_empty(): bug.resolution = UNRESOLVED_TAG show_marker = show_unresolved if not show_marker: continue 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"] bug.duplicate_of = -1 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, 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 := 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) 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) tag_lists[BugbotTagArray.VERSION].sort_custom(func(a,b):return a["id"] < b["id"]) tag_lists[BugbotTagArray.HARDWARE].sort_custom(func(a,b):return a["id"] < b["id"]) tag_lists[BugbotTagArray.OS].sort_custom(func(a,b):return a["id"] < b["id"]) tag_lists[BugbotTagArray.COMPONENT].sort_custom(func(a,b):return a["id"] < b["id"]) tag_lists[BugbotTagArray.SEVERITY].sort_custom(func(a,b): return a["id"] < b["id"]) 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, 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"]) (data["labels"] as Array).append_array(labels) data["body"] += "\n\n" + """{ "map_name": "%s", "bug_position": [%.4f, %.4f, %.4f], "bug_normal": [%.4f, %.4f, %.4f] }""" % [map_name, bug_position.x, bug_position.y, bug_position.z, bug_normal.x, bug_normal.y, bug_normal.z] # Post issue to Gitea api_url = __build_url_string("issues") var post_data_string : String = JSON.stringify(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 callback.call_deferred(post_response_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