Bugbot/Scripts/server_bugzilla_api.gd

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