Bugbot/Scripts/server_gitea_api.gd
2024-06-01 03:04:07 -04:00

285 lines
14 KiB
GDScript

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
#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", 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()),
}
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