311 lines
15 KiB
GDScript
311 lines
15 KiB
GDScript
class_name BugbotServerBugzillaAPI
|
|
extends "res://addons/Bugbot/Scripts/server_api.gd"
|
|
|
|
##region Consts
|
|
const DEFAULT_SERVER : StringName = &"https://bugzilla.example.com"
|
|
const DEFAULT_REST_URI : StringName = &"rest/"
|
|
const DEFAULT_PRODUCT_NAME : StringName = &"ProductName"
|
|
const DEFAULT_API_KEY : StringName = &"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn"
|
|
const DEFAULT_POST_STATUS : StringName = &"UNCONFIRMED"
|
|
|
|
const DEFAULT_MAP_NAME_FIELD : StringName = &"cf_mapname"
|
|
const DEFAULT_MARKER_LOCATION_FIELD : StringName = &"cf_location"
|
|
|
|
const DEFAULT_UNRESOLVED_STATUSES : Array = [&"UNCONFIRMED", &"CONFIRMED"]
|
|
const DEFAULT_IN_PROGRESS_STATUSES : Array = [&"IN_PROGRESS"]
|
|
const DEFAULT_RESOLVED_STATUSES : Array = [&"RESOLVED", &"VERIFIED"]
|
|
#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/bugzilla/server", DEFAULT_SERVER)) != HTTPClient.STATUS_CONNECTED:
|
|
printerr("Could not connect to server.")
|
|
return
|
|
|
|
var show_unresolved : bool = ProjectSettings.get_setting("bugbot/markers/unresolved/show_unresolved_bugs", DEFAULT_SHOW_UNRESOLVED_BUGS)
|
|
var show_in_progress : bool = ProjectSettings.get_setting("bugbot/markers/in_progress/show_in_progress_bugs", DEFAULT_SHOW_IN_PROGRESS_BUGS)
|
|
var show_resolved : bool = ProjectSettings.get_setting("bugbot/markers/resolved/show_resolved_bugs", DEFAULT_SHOW_RESOLVED_BUGS)
|
|
var unresolved_labels : Array = ProjectSettings.get_setting("bugbot/reporting/bugzilla/status_labels/unresolved_statuses", DEFAULT_UNRESOLVED_STATUSES)
|
|
var in_progress_labels : Array = ProjectSettings.get_setting("bugbot/reporting/bugzilla/status_labels/in_progress_statuses", DEFAULT_IN_PROGRESS_STATUSES)
|
|
var resolved_labels : Array = ProjectSettings.get_setting("bugbot/reporting/bugzilla/status_labels/resolved_statuses", DEFAULT_RESOLVED_STATUSES)
|
|
|
|
var map_name_field : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/map_name_field", DEFAULT_MAP_NAME_FIELD)
|
|
var marker_location_field : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/marker_location_field", DEFAULT_MARKER_LOCATION_FIELD)
|
|
var api_key : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/API_key", DEFAULT_API_KEY)
|
|
var api_url : String = __build_url_string("bug?")
|
|
api_url += map_name_field + "=" + map_name
|
|
if show_unresolved:
|
|
for unresolved_status in unresolved_labels:
|
|
api_url += "&status=" + unresolved_status
|
|
if show_in_progress:
|
|
for in_progress_status in in_progress_labels:
|
|
api_url += "&status=" + in_progress_status
|
|
if show_resolved:
|
|
for resolved_status in resolved_labels:
|
|
api_url += "&status=" + resolved_status
|
|
api_url += "&api_key=" + api_key
|
|
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": show_unresolved,
|
|
"show_in_progress": show_in_progress,
|
|
"show_resolved": show_resolved,
|
|
|
|
"unresolved_labels": unresolved_labels,
|
|
"in_progress_labels": in_progress_labels,
|
|
"resolved_labels": resolved_labels,
|
|
|
|
"map_name_field": map_name_field,
|
|
"marker_location_field": marker_location_field,
|
|
}
|
|
for bug_in:Dictionary in response_data["bugs"]:
|
|
var bug : BugbotBugData = __create_bug_data_from_server_response(bug_in, map_name, label_dict, true)
|
|
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/bugzilla/server", DEFAULT_SERVER)) != HTTPClient.STATUS_CONNECTED:
|
|
printerr("Could not connect to server.")
|
|
return
|
|
|
|
var header_data : Array = __create_header_data()
|
|
var product_name : String = _get_project_name()
|
|
var api_key : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/API_key", DEFAULT_API_KEY)
|
|
|
|
var api_url : String = "product/%s?" % [product_name]
|
|
api_url += "api_key=" + api_key
|
|
var error : int = http_client.request(HTTPClient.METHOD_GET, __build_url_string(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 product_response_string : String = __get_http_client_chunk_response(http_client)
|
|
var product_response : Dictionary = JSON.parse_string(product_response_string)
|
|
if __validate_server_response(product_response) != Error.OK:
|
|
return
|
|
|
|
api_url = "field/bug?"
|
|
api_url += "api_key=" + api_key
|
|
error = http_client.request(HTTPClient.METHOD_GET, __build_url_string(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 fields_response_string : String = __get_http_client_chunk_response(http_client)
|
|
var fields_response : Dictionary = JSON.parse_string(fields_response_string)
|
|
if __validate_server_response(fields_response) != Error.OK:
|
|
return
|
|
|
|
var tag_lists : Array
|
|
tag_lists.resize(BugbotTagArray.MAX)
|
|
tag_lists[BugbotTagArray.VERSION] = []
|
|
tag_lists[BugbotTagArray.HARDWARE] = []
|
|
tag_lists[BugbotTagArray.OS] = []
|
|
tag_lists[BugbotTagArray.COMPONENT] = []
|
|
tag_lists[BugbotTagArray.SEVERITY] = []
|
|
|
|
var product_info : Dictionary = product_response["products"][0]
|
|
if product_info["name"] != product_name:
|
|
printerr("Incorrect product found.")
|
|
__bugbot_server_thread.call_deferred("wait_to_finish")
|
|
return
|
|
|
|
for component:Dictionary in product_info["components"]:
|
|
tag_lists[BugbotTagArray.COMPONENT].append({ "name": component["name"], "id": component["sort_key"] })
|
|
for version:Dictionary in product_info["versions"]:
|
|
tag_lists[BugbotTagArray.VERSION].append({ "name": version["name"], "id": version["sort_key"] })
|
|
|
|
for field:Dictionary in fields_response["fields"]:
|
|
if field.has("values"):
|
|
var field_name : String = field["name"]
|
|
var field_values : Array = field["values"]
|
|
if field_name == "bug_severity":
|
|
for severity:Dictionary in field_values:
|
|
tag_lists[BugbotTagArray.SEVERITY].append({ "name": severity["name"], "id": severity["sort_key"] })
|
|
elif field_name == "rep_platform":
|
|
for platform:Dictionary in field_values:
|
|
tag_lists[BugbotTagArray.HARDWARE].append({ "name": platform["name"], "id": platform["sort_key"] })
|
|
elif field_name == "op_sys":
|
|
for os:Dictionary in field_values:
|
|
tag_lists[BugbotTagArray.OS].append({ "name": os["name"], "id": os["sort_key"] })
|
|
|
|
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/bugzilla/server", DEFAULT_SERVER)) != HTTPClient.STATUS_CONNECTED:
|
|
printerr("Could not connect to server.")
|
|
return
|
|
|
|
var product_name : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/product_name", DEFAULT_PRODUCT_NAME)
|
|
var api_key : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/API_key", DEFAULT_API_KEY)
|
|
var default_post_status : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/default_status", DEFAULT_POST_STATUS)
|
|
var map_name_field : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/map_name_field", DEFAULT_MAP_NAME_FIELD)
|
|
var marker_location_field : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/marker_location_field", DEFAULT_MARKER_LOCATION_FIELD)
|
|
var marker_location : String = "%.4f,%.4f,%.4f:%.4f,%.4f,%.4f" % [bug_position.x, bug_position.y, bug_position.z, bug_normal.x, bug_normal.y, bug_normal.z]
|
|
|
|
var api_url : String = "bug?"
|
|
api_url += "api_key=" + api_key
|
|
|
|
var post_data : Dictionary = {
|
|
"product": product_name,
|
|
map_name_field: map_name,
|
|
marker_location_field: marker_location,
|
|
"summary": data["title"],
|
|
"description": data["body"],
|
|
"status": default_post_status,
|
|
}
|
|
if not data["labels"]["version"].is_empty(): post_data["version"] = data["labels"]["version"]["name"]
|
|
if not data["labels"]["hardware"].is_empty(): post_data["platform"] = data["labels"]["hardware"]["name"]
|
|
if not data["labels"]["os"].is_empty(): post_data["op_sys"] = data["labels"]["os"]["name"]
|
|
if not data["labels"]["component"].is_empty(): post_data["component"] = data["labels"]["component"]["name"]
|
|
if not data["labels"]["severity"].is_empty(): post_data["severity"] = data["labels"]["severity"]["name"]
|
|
|
|
var post_data_string : String = JSON.stringify(post_data)
|
|
|
|
var header_data : Array = __create_header_data(post_data_string.length())
|
|
var error : int = http_client.request(HTTPClient.METHOD_POST, __build_url_string(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_string : String = __get_http_client_chunk_response(http_client)
|
|
var post_response : Dictionary = JSON.parse_string(post_response_string)
|
|
if __validate_server_response(post_response) != Error.OK:
|
|
return
|
|
|
|
post_data["id"] = post_response["id"]
|
|
post_data["is_open"] = true
|
|
|
|
var label_dict : Dictionary = {
|
|
"show_unresolved": ProjectSettings.get_setting("bugbot/markers/unresolved/show_unresolved_bugs", DEFAULT_SHOW_UNRESOLVED_BUGS),
|
|
"show_in_progress": ProjectSettings.get_setting("bugbot/markers/in_progress/show_in_progress_bugs", DEFAULT_SHOW_IN_PROGRESS_BUGS),
|
|
"show_resolved": ProjectSettings.get_setting("bugbot/markers/resolved/show_resolved_bugs", DEFAULT_SHOW_RESOLVED_BUGS),
|
|
|
|
"unresolved_labels": ProjectSettings.get_setting("bugbot/reporting/bugzilla/status_labels/unresolved_statuses", DEFAULT_UNRESOLVED_STATUSES),
|
|
"in_progress_labels": ProjectSettings.get_setting("bugbot/reporting/bugzilla/status_labels/in_progress_statuses", DEFAULT_IN_PROGRESS_STATUSES),
|
|
"resolved_labels": ProjectSettings.get_setting("bugbot/reporting/bugzilla/status_labels/resolved_statuses", DEFAULT_RESOLVED_STATUSES),
|
|
|
|
"map_name_field": map_name_field,
|
|
"marker_location_field": marker_location_field,
|
|
}
|
|
var bug_data : BugbotBugData = __create_bug_data_from_server_response(post_data, map_name, label_dict, false)
|
|
|
|
callback.call_deferred(bug_data)
|
|
__bugbot_server_thread.call_deferred("wait_to_finish")
|
|
|
|
|
|
func _current_server_api() -> String:
|
|
return "Bugzilla"
|
|
|
|
func _get_project_name() -> String:
|
|
return ProjectSettings.get_setting("bugbot/reporting/bugzilla/product_name", DEFAULT_PRODUCT_NAME)
|
|
|
|
func _get_bug_url(bug_data:BugbotBugData) -> String:
|
|
var bugzilla_server : String = ProjectSettings.get_setting("bugbot/reporting/bugzilla/server", DEFAULT_SERVER)
|
|
return "%s/show_bug.cgi?id=%d" % [bugzilla_server, bug_data.id]
|
|
|
|
func __build_url_string(_api_suffix:String) -> String:
|
|
return "/" + \
|
|
ProjectSettings.get_setting("bugbot/reporting/bugzilla/REST_URI", DEFAULT_REST_URI) + \
|
|
"/" + \
|
|
_api_suffix
|
|
|
|
func __create_header_data(content_length:int = -1) -> Array:
|
|
var header : Array = [
|
|
"User-Agent: Pirulo/1.0 (Godot)",
|
|
"Accept: application/json",
|
|
]
|
|
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("error") and _response["error"] == true) or _response.has("message"):
|
|
var error_data : BugbotErrorData = BugbotErrorData.new()
|
|
error_data.message = _response["message"]
|
|
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) -> BugbotBugData:
|
|
var bug : BugbotBugData = BugbotBugData.new()
|
|
|
|
# Check if the map name is valid for the scene we're in.
|
|
bug.map_name = bug_in[label_dict["map_name_field"]]
|
|
if bug.map_name != map_name:
|
|
return null
|
|
|
|
var marker_location : String = bug_in[label_dict["marker_location_field"]]
|
|
if not marker_location.is_empty():
|
|
var marker_location_parts : PackedStringArray = marker_location.split(":")
|
|
if marker_location_parts.size() == 2:
|
|
var marker_position : PackedStringArray = marker_location_parts[0].split(",")
|
|
var marker_normal : PackedStringArray = marker_location_parts[1].split(",")
|
|
if marker_position.size() == 3:
|
|
bug.marker_position = Vector3(float(marker_position[0]), float(marker_position[1]), float(marker_position[2]))
|
|
if marker_normal.size() == 3:
|
|
bug.marker_normal = Vector3(float(marker_normal[0]), float(marker_normal[1]), float(marker_normal[2]))
|
|
|
|
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["is_open"]
|
|
bug.status = bug_in["status"]
|
|
if not bug.is_open:
|
|
resolved_tag_found = true
|
|
else:
|
|
for label:String in label_dict["resolved_labels"] as Array:
|
|
if bug.status == label: resolved_tag_found = true
|
|
for label:String in label_dict["in_progress_labels"] as Array:
|
|
if bug.status == label: in_progress_tag_found = true
|
|
for label:String in label_dict["unresolved_labels"] as Array:
|
|
if bug.status == 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.key = String.num_int64(bug.id)
|
|
bug.title = bug_in["summary"]
|
|
bug.component = bug_in["component"]
|
|
bug.platform = bug_in["platform"]
|
|
bug.operating_system = bug_in["op_sys"]
|
|
bug.severity = bug_in["severity"]
|
|
|
|
return bug
|