Preparing the code for the Jira implementation.

This commit is contained in:
Jamie Greunbaum 2023-04-01 00:04:24 -04:00
parent 81b638e9b3
commit 52a5d9d60d
10 changed files with 1202 additions and 537 deletions

View File

@ -6,7 +6,8 @@
#include "BugMarkerActor.h"
#include "HttpModule.h"
#include "JsonObjectConverter.h"
#include "ServerAPI.h"
#include "ServerBugzillaAPI.h"
#include "ServerJiraAPI.h"
#include "UnrealzillaGlobalSettings.h"
DEFINE_LOG_CATEGORY(BugMarkerSubsystemLog);
@ -14,9 +15,35 @@ DEFINE_LOG_CATEGORY(BugMarkerSubsystemLog);
void UBugMarkerSubsystem::Initialize(FSubsystemCollectionBase &Collection)
{
if (!(this->ServerAPI = NewObject<UServerAPI>(this)))
switch (GetDefault<UUnrealzillaGlobalSettings>()->BugReportPlatform)
{
UE_LOG(BugMarkerSubsystemLog, Error, TEXT("Could not create Server API class."));
case EBugReportPlatform::Bugzilla:
this->ServerAPI = NewObject<UServerBugzillaAPI>(this);
break;
case EBugReportPlatform::Jira:
// this->ServerAPI = NewObject<UServerJiraAPI>(this);
// break;
default:
this->ServerAPI = NewObject<UServerAPI>(this);
}
if (!this->ServerAPI)
{
UE_LOG(BugMarkerSubsystemLog, Error, TEXT("Could not create Server API class; this will certainly crash eventually"));
}
}
FString UBugMarkerSubsystem::GetProjectName()
{
switch (GetDefault<UUnrealzillaGlobalSettings>()->BugReportPlatform)
{
case EBugReportPlatform::Bugzilla:
return GetDefault<UUnrealzillaGlobalSettings>()->BugzillaProductName;
case EBugReportPlatform::Jira:
//return GetDefault<UUnrealzillaGlobalSettings>()->JiraProjectName;
default:
return "No project";
}
}

View File

@ -37,8 +37,6 @@ void UBugSubmissionForm::NativeOnInitialized()
{
this->ShowProcessingOverlayLoading();
this->ProductNameValue->SetText(FText::AsCultureInvariant(GetDefault<UUnrealzillaGlobalSettings>()->ProductName));
UBugMarkerSubsystem *BugMarkerSubsystem = UGameplayStatics::GetPlayerController(this, 0)->GetLocalPlayer()->GetSubsystem<UBugMarkerSubsystem>();
BugMarkerSubsystem->FormPrepResponse.BindUObject(this, &UBugSubmissionForm::PrepareFormData);
BugMarkerSubsystem->PrepareSubmissionFormData();

View File

@ -24,510 +24,19 @@ const FString GetGameVersion()
void UServerAPI::ReturnListOfBugs()
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->SubmissionServer + "/rest.cgi";
TArray<FString> StatusQueries;
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowUnresolvedBugs)
{
for (const FString Unresolved : GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses)
{
StatusQueries.Add("status=" + Unresolved);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowInProgressBugs)
{
for (const FString InProgress : GetDefault<UUnrealzillaGlobalSettings>()->InProgressStatuses)
{
StatusQueries.Add("status=" + InProgress);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowResolvedBugs)
{
for (const FString Resolved : GetDefault<UUnrealzillaGlobalSettings>()->ResolvedStatuses)
{
StatusQueries.Add("status=" + Resolved);
}
}
StatusQueries.Add("cf_mapname=" + this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len()));
StatusQueries.Add("api_key=" + GetDefault<UUnrealzillaGlobalSettings>()->APIKey);
const FString QueryString = FString::Join(StatusQueries, TEXT("&"));
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/bug" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerAPI::ListOfBugsResponse);
SeverityRequest->ProcessRequest();
}
void UServerAPI::ListOfBugsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONBugResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (!ResponseData.error)
{
TArray<FUnrealzillaBugData> BugData;
for (const FBugzillaJSONBugData &BugzillaData : ResponseData.bugs)
{
FUnrealzillaBugData Bug;
Bug.ID = BugzillaData.id;
Bug.Summary = BugzillaData.summary;
Bug.Component = BugzillaData.component;
Bug.MapName = BugzillaData.cf_mapname;
Bug.MapLocation = BugzillaData.cf_location;
Bug.Platform = BugzillaData.platform;
Bug.OperatingSystem = BugzillaData.op_sys;
Bug.Severity = BugzillaData.severity;
Bug.Status = BugzillaData.status;
Bug.Resolution = BugzillaData.resolution;
Bug.DuplicateOf = BugzillaData.dupe_of;
Bug.bIsOpen = BugzillaData.is_open;
BugData.Add(Bug);
}
this->BugDataResponse.Execute(BugData);
}
else
{
this->CreateError(EErrorVerb::GET, ResponseData);
}
}
this->BugDataResponse.Execute(TArray<FUnrealzillaBugData>());
}
void UServerAPI::PrepareForm()
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->SubmissionServer + "/rest.cgi";
// Assemble query data into key:value pairs
TMap<FString, FString> QueryData;
QueryData.Add("api_key", GetDefault<UUnrealzillaGlobalSettings>()->APIKey);
const FString QueryString = UServerAPI::FormatQueryString(QueryData);
// Query the server for information about the current product
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> ProductRequest = HttpModule.CreateRequest();
ProductRequest->SetVerb(TEXT("GET"));
ProductRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
ProductRequest->SetURL(FullURL + "/product/" + GetDefault<UUnrealzillaGlobalSettings>()->ProductName + "?" + QueryString);
ProductRequest->OnProcessRequestComplete().BindUObject(this, &UServerAPI::ServerProductInfoResponse);
ProductRequest->ProcessRequest();
// Send a second query to retrieve severity options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/field/bug/bug_severity" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerAPI::ServerSeverityInfoResponse);
SeverityRequest->ProcessRequest();
// Send a third query to retrieve platform options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> PlatformsRequest = HttpModule.CreateRequest();
PlatformsRequest->SetVerb(TEXT("GET"));
PlatformsRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
PlatformsRequest->SetURL(FullURL + "/field/bug/rep_platform" + "?" + QueryString);
PlatformsRequest->OnProcessRequestComplete().BindUObject(this, &UServerAPI::ServerPlatformInfoResponse);
PlatformsRequest->ProcessRequest();
// Send a final query to retrieve OS options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> OSRequest = HttpModule.CreateRequest();
OSRequest->SetVerb(TEXT("GET"));
OSRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
OSRequest->SetURL(FullURL + "/field/bug/op_sys" + "?" + QueryString);
OSRequest->OnProcessRequestComplete().BindUObject(this, &UServerAPI::ServerOSInfoResponse);
OSRequest->ProcessRequest();
this->FormDataResponse.Execute(FUnrealzillaFormPrepData());
}
void UServerAPI::SendFormData(const FUnrealzillaPostData &PostData)
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->SubmissionServer + "/rest.cgi";
const FString RequestURL = "/bug";
// Assemble query data into key:value pairs
TMap<FString, FString> QueryData;
QueryData.Add("api_key", GetDefault<UUnrealzillaGlobalSettings>()->APIKey);
const FString DefaultStatus = GetDefault<UUnrealzillaGlobalSettings>()->DefaultStatus;
FBugzillaJSONPostBug PostDataJSON;
PostDataJSON.product = GetDefault<UUnrealzillaGlobalSettings>()->ProductName;
PostDataJSON.version = PostData.Version;
PostDataJSON.platform = PostData.Platform;
PostDataJSON.op_sys = PostData.OS;
PostDataJSON.component = PostData.Component;
PostDataJSON.severity = PostData.Severity;
PostDataJSON.cf_mapname = PostData.MapName;
PostDataJSON.cf_location = PostData.MapLocation;
PostDataJSON.summary = PostData.Summary;
PostDataJSON.description = PostData.Comment;
if (!DefaultStatus.IsEmpty())
{
PostDataJSON.status = DefaultStatus;
this->BugDataResponse.Execute(TArray<FUnrealzillaBugData>());
}
if (PostDataJSON.version.IsEmpty())
{
this->CreateError("You must select a version number.");
return;
}
if (PostDataJSON.platform.IsEmpty() || PostDataJSON.op_sys.IsEmpty())
{
PostDataJSON.platform = "All";
PostDataJSON.op_sys = "All";
}
if (PostDataJSON.component.IsEmpty())
{
this->CreateError("You must select a component.");
return;
}
if (PostDataJSON.severity.IsEmpty())
{
this->CreateError("You must select a severity level.");
return;
}
if (PostDataJSON.summary.IsEmpty())
{
this->CreateError("You must provide a summary.");
return;
}
FString PostJsonString;
FJsonObjectConverter::UStructToJsonObjectString(PostDataJSON, PostJsonString);
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = HttpModule.CreateRequest();
Request->SetVerb(TEXT("POST"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->SetURL(FullURL + RequestURL + "?" + UServerAPI::FormatQueryString(QueryData));
Request->SetContentAsString(PostJsonString);
Request->OnProcessRequestComplete().BindUObject(this, &UServerAPI::ServerPOSTResponse);
Request->ProcessRequest();
}
void UServerAPI::ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONPostResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
this->CreateError(EErrorVerb::POST, ResponseData);
}
else
{
// Use the response's bug ID to get the info from the newly filed bug report and update its marker
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->SubmissionServer + "/rest.cgi";
TArray<FString> StatusQueries;
StatusQueries.Add("id=" + FString::FromInt(ResponseData.id));
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowUnresolvedBugs)
{
for (const FString Unresolved : GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses)
{
StatusQueries.Add("status=" + Unresolved);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowInProgressBugs)
{
for (const FString InProgress : GetDefault<UUnrealzillaGlobalSettings>()->InProgressStatuses)
{
StatusQueries.Add("status=" + InProgress);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowResolvedBugs)
{
for (const FString Resolved : GetDefault<UUnrealzillaGlobalSettings>()->ResolvedStatuses)
{
StatusQueries.Add("status=" + Resolved);
}
}
StatusQueries.Add("cf_mapname=" + this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len()));
StatusQueries.Add("api_key=" + GetDefault<UUnrealzillaGlobalSettings>()->APIKey);
const FString QueryString = FString::Join(StatusQueries, TEXT("&"));
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/bug" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerAPI::ServerPOSTUpdateMarkerResponse);
SeverityRequest->ProcessRequest();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerAPI::ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONBugResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.bugs.IsEmpty())
{
TArray<FUnrealzillaBugData> BugData;
FUnrealzillaBugData Bug;
Bug.ID = ResponseData.bugs[0].id;
Bug.Summary = ResponseData.bugs[0].summary;
Bug.Component = ResponseData.bugs[0].component;
Bug.MapName = ResponseData.bugs[0].cf_mapname;
Bug.MapLocation = ResponseData.bugs[0].cf_location;
Bug.Platform = ResponseData.bugs[0].platform;
Bug.OperatingSystem = ResponseData.bugs[0].op_sys;
Bug.Severity = ResponseData.bugs[0].severity;
Bug.Status = ResponseData.bugs[0].status;
Bug.Resolution = ResponseData.bugs[0].resolution;
Bug.DuplicateOf = ResponseData.bugs[0].dupe_of;
Bug.bIsOpen = ResponseData.bugs[0].is_open;
BugData.Add(Bug);
this->BugDataResponse.Execute(BugData);
}
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerAPI::ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONProductResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.products.IsEmpty())
{
const FBugzillaJSONProductData &ProductData = ResponseData.products[0];
if (ProductData.name == GetDefault<UUnrealzillaGlobalSettings>()->ProductName)
{
for (const FBugzillaJSONComponentData &ComponentData : ProductData.components)
{
this->ComponentsList.Add(ComponentData.name);
}
for (const FBugzillaJSONVersionData &VersionData : ProductData.versions)
{
this->VersionsList.Add(VersionData.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
else
{
FStringFormatOrderedArguments Args;
Args.Add(FStringFormatArg(GetDefault<UUnrealzillaGlobalSettings>()->ProductName));
//this->ShowProcessingOverlayMessage(FString::Format(TEXT("Could not find data for a product called {0}"), Args), true);
this->CreateError(FString::Format(TEXT("Could not find data for a product called {0}"), Args));
}
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerAPI::ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "bug_severity")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->SeverityList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerAPI::ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "rep_platform")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->PlatformsList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerAPI::ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "op_sys")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->OSList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerAPI::CheckIfAllFormResponsesAreIn()
{
if (!this->ComponentsList.IsEmpty() && !this->VersionsList.IsEmpty() && !this->SeverityList.IsEmpty() &&
!this->PlatformsList.IsEmpty() && !this->OSList.IsEmpty())
{
FUnrealzillaFormPrepData Data;
Data.ComponentsList = this->ComponentsList;
Data.SeverityList = this->SeverityList;
Data.VersionsList = this->VersionsList;
Data.PlatformsList = this->PlatformsList;
Data.OSList = this->OSList;
// Find a default version number to use if possible
if (this->VersionsList.Contains(GetGameVersion()))
{
Data.DetectedVersion = GetGameVersion();
}
else if (this->VersionsList.Contains("unspecified"))
{
Data.DetectedVersion = "unspecified";
}
else if (this->VersionsList.Contains("Latest"))
{
Data.DetectedVersion = "Latest";
}
else if (!this->VersionsList.IsEmpty())
{
Data.DetectedVersion = this->VersionsList[0];
}
// Set these as defaults in case nothing below changes this setting
Data.DetectedHardware = "All";
Data.DetectedOS = "All";
if (this->PlatformsList.Contains("PC"))
{
// Try our best to auto-detect PC hardware
if (UGameplayStatics::GetPlatformName() == "Windows" && this->OSList.Contains("Windows"))
{
Data.DetectedHardware = "PC";
Data.DetectedOS = "Windows";
}
else if (UGameplayStatics::GetPlatformName() == "Linux" && this->OSList.Contains("Linux"))
{
Data.DetectedHardware = "PC";
Data.DetectedOS = "Linux";
}
else if (UGameplayStatics::GetPlatformName() == "Mac" && this->OSList.Contains("Mac OS"))
{
Data.DetectedHardware = "All";
Data.DetectedOS = "Mac OS";
}
}
if (UGameplayStatics::GetPlatformName() == "Mac")
{
// Try our best to auto-detect Macintosh hardware
if (this->PlatformsList.Contains("Macintosh"))
{
if (this->OSList.Contains("Mac OS"))
{
Data.DetectedHardware = "Macintosh";
Data.DetectedOS = "Mac OS";
}
}
}
this->FormDataResponse.Execute(Data);
this->ComponentsList.Empty();
this->VersionsList.Empty();
this->SeverityList.Empty();
this->PlatformsList.Empty();
this->OSList.Empty();
}
}
void UServerAPI::ServerConnectionError(const EHttpRequestStatus::Type Status)
{

View File

@ -0,0 +1,518 @@
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
#include "ServerBugzillaAPI.h"
#include "HttpModule.h"
#include "JsonObjectConverter.h"
#include "UnrealzillaGlobalSettings.h"
#include "Kismet/GameplayStatics.h"
void UServerBugzillaAPI::ReturnListOfBugs()
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->BugzillaSubmissionServer + "rest.cgi";
TArray<FString> StatusQueries;
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowUnresolvedBugs)
{
for (const FString Unresolved : GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses)
{
StatusQueries.Add("status=" + Unresolved);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowInProgressBugs)
{
for (const FString InProgress : GetDefault<UUnrealzillaGlobalSettings>()->InProgressStatuses)
{
StatusQueries.Add("status=" + InProgress);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowResolvedBugs)
{
for (const FString Resolved : GetDefault<UUnrealzillaGlobalSettings>()->ResolvedStatuses)
{
StatusQueries.Add("status=" + Resolved);
}
}
StatusQueries.Add("cf_mapname=" + this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len()));
StatusQueries.Add("api_key=" + GetDefault<UUnrealzillaGlobalSettings>()->BugzillaAPIKey);
const FString QueryString = FString::Join(StatusQueries, TEXT("&"));
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/bug" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerBugzillaAPI::ListOfBugsResponse);
SeverityRequest->ProcessRequest();
}
void UServerBugzillaAPI::ListOfBugsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONBugResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (!ResponseData.error)
{
TArray<FUnrealzillaBugData> BugData;
for (const FBugzillaJSONBugData &BugzillaData : ResponseData.bugs)
{
FUnrealzillaBugData Bug;
Bug.ID = BugzillaData.id;
Bug.Summary = BugzillaData.summary;
Bug.Component = BugzillaData.component;
Bug.MapName = BugzillaData.cf_mapname;
Bug.MapLocation = BugzillaData.cf_location;
Bug.Platform = BugzillaData.platform;
Bug.OperatingSystem = BugzillaData.op_sys;
Bug.Severity = BugzillaData.severity;
Bug.Status = BugzillaData.status;
Bug.Resolution = BugzillaData.resolution;
Bug.DuplicateOf = BugzillaData.dupe_of;
Bug.bIsOpen = BugzillaData.is_open;
BugData.Add(Bug);
}
this->BugDataResponse.Execute(BugData);
}
else
{
this->CreateError(EErrorVerb::GET, ResponseData);
}
}
}
void UServerBugzillaAPI::PrepareForm()
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->BugzillaSubmissionServer + "/rest.cgi";
// Assemble query data into key:value pairs
TMap<FString, FString> QueryData;
QueryData.Add("api_key", GetDefault<UUnrealzillaGlobalSettings>()->BugzillaAPIKey);
const FString QueryString = UServerAPI::FormatQueryString(QueryData);
// Query the server for information about the current product
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> ProductRequest = HttpModule.CreateRequest();
ProductRequest->SetVerb(TEXT("GET"));
ProductRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
ProductRequest->SetURL(FullURL + "/product/" + GetDefault<UUnrealzillaGlobalSettings>()->BugzillaProductName + "?" + QueryString);
ProductRequest->OnProcessRequestComplete().BindUObject(this, &UServerBugzillaAPI::ServerProductInfoResponse);
ProductRequest->ProcessRequest();
// Send a second query to retrieve severity options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/field/bug/bug_severity" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerBugzillaAPI::ServerSeverityInfoResponse);
SeverityRequest->ProcessRequest();
// Send a third query to retrieve platform options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> PlatformsRequest = HttpModule.CreateRequest();
PlatformsRequest->SetVerb(TEXT("GET"));
PlatformsRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
PlatformsRequest->SetURL(FullURL + "/field/bug/rep_platform" + "?" + QueryString);
PlatformsRequest->OnProcessRequestComplete().BindUObject(this, &UServerBugzillaAPI::ServerPlatformInfoResponse);
PlatformsRequest->ProcessRequest();
// Send a final query to retrieve OS options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> OSRequest = HttpModule.CreateRequest();
OSRequest->SetVerb(TEXT("GET"));
OSRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
OSRequest->SetURL(FullURL + "/field/bug/op_sys" + "?" + QueryString);
OSRequest->OnProcessRequestComplete().BindUObject(this, &UServerBugzillaAPI::ServerOSInfoResponse);
OSRequest->ProcessRequest();
}
void UServerBugzillaAPI::SendFormData(const FUnrealzillaPostData &PostData)
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->BugzillaSubmissionServer + "/rest.cgi";
const FString RequestURL = "/bug";
// Assemble query data into key:value pairs
TMap<FString, FString> QueryData;
QueryData.Add("api_key", GetDefault<UUnrealzillaGlobalSettings>()->BugzillaAPIKey);
const FString DefaultStatus = GetDefault<UUnrealzillaGlobalSettings>()->DefaultStatus;
FBugzillaJSONPostBug PostDataJSON;
PostDataJSON.product = GetDefault<UUnrealzillaGlobalSettings>()->BugzillaProductName;
PostDataJSON.version = PostData.Version;
PostDataJSON.platform = PostData.Platform;
PostDataJSON.op_sys = PostData.OS;
PostDataJSON.component = PostData.Component;
PostDataJSON.severity = PostData.Severity;
PostDataJSON.cf_mapname = PostData.MapName;
PostDataJSON.cf_location = PostData.MapLocation;
PostDataJSON.summary = PostData.Summary;
PostDataJSON.description = PostData.Comment;
if (!DefaultStatus.IsEmpty())
{
PostDataJSON.status = DefaultStatus;
}
if (PostDataJSON.version.IsEmpty())
{
this->CreateError("You must select a version number.");
return;
}
if (PostDataJSON.platform.IsEmpty() || PostDataJSON.op_sys.IsEmpty())
{
PostDataJSON.platform = "All";
PostDataJSON.op_sys = "All";
}
if (PostDataJSON.component.IsEmpty())
{
this->CreateError("You must select a component.");
return;
}
if (PostDataJSON.severity.IsEmpty())
{
this->CreateError("You must select a severity level.");
return;
}
if (PostDataJSON.summary.IsEmpty())
{
this->CreateError("You must provide a summary.");
return;
}
FString PostJsonString;
FJsonObjectConverter::UStructToJsonObjectString(PostDataJSON, PostJsonString);
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = HttpModule.CreateRequest();
Request->SetVerb(TEXT("POST"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->SetURL(FullURL + RequestURL + "?" + UServerAPI::FormatQueryString(QueryData));
Request->SetContentAsString(PostJsonString);
Request->OnProcessRequestComplete().BindUObject(this, &UServerBugzillaAPI::ServerPOSTResponse);
Request->ProcessRequest();
}
void UServerBugzillaAPI::ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONPostResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
this->CreateError(EErrorVerb::POST, ResponseData);
}
else
{
// Use the response's bug ID to get the info from the newly filed bug report and update its marker
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->BugzillaSubmissionServer + "/rest.cgi";
TArray<FString> StatusQueries;
StatusQueries.Add("id=" + FString::FromInt(ResponseData.id));
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowUnresolvedBugs)
{
for (const FString Unresolved : GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses)
{
StatusQueries.Add("status=" + Unresolved);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowInProgressBugs)
{
for (const FString InProgress : GetDefault<UUnrealzillaGlobalSettings>()->InProgressStatuses)
{
StatusQueries.Add("status=" + InProgress);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowResolvedBugs)
{
for (const FString Resolved : GetDefault<UUnrealzillaGlobalSettings>()->ResolvedStatuses)
{
StatusQueries.Add("status=" + Resolved);
}
}
StatusQueries.Add("cf_mapname=" + this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len()));
StatusQueries.Add("api_key=" + GetDefault<UUnrealzillaGlobalSettings>()->BugzillaAPIKey);
const FString QueryString = FString::Join(StatusQueries, TEXT("&"));
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/bug" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerBugzillaAPI::ServerPOSTUpdateMarkerResponse);
SeverityRequest->ProcessRequest();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerBugzillaAPI::ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONBugResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.bugs.IsEmpty())
{
TArray<FUnrealzillaBugData> BugData;
FUnrealzillaBugData Bug;
Bug.ID = ResponseData.bugs[0].id;
Bug.Summary = ResponseData.bugs[0].summary;
Bug.Component = ResponseData.bugs[0].component;
Bug.MapName = ResponseData.bugs[0].cf_mapname;
Bug.MapLocation = ResponseData.bugs[0].cf_location;
Bug.Platform = ResponseData.bugs[0].platform;
Bug.OperatingSystem = ResponseData.bugs[0].op_sys;
Bug.Severity = ResponseData.bugs[0].severity;
Bug.Status = ResponseData.bugs[0].status;
Bug.Resolution = ResponseData.bugs[0].resolution;
Bug.DuplicateOf = ResponseData.bugs[0].dupe_of;
Bug.bIsOpen = ResponseData.bugs[0].is_open;
BugData.Add(Bug);
this->BugDataResponse.Execute(BugData);
}
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerBugzillaAPI::ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONProductResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.products.IsEmpty())
{
const FBugzillaJSONProductData &ProductData = ResponseData.products[0];
if (ProductData.name == GetDefault<UUnrealzillaGlobalSettings>()->BugzillaProductName)
{
for (const FBugzillaJSONComponentData &ComponentData : ProductData.components)
{
this->ComponentsList.Add(ComponentData.name);
}
for (const FBugzillaJSONVersionData &VersionData : ProductData.versions)
{
this->VersionsList.Add(VersionData.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
else
{
FStringFormatOrderedArguments Args;
Args.Add(FStringFormatArg(GetDefault<UUnrealzillaGlobalSettings>()->BugzillaProductName));
//this->ShowProcessingOverlayMessage(FString::Format(TEXT("Could not find data for a product called {0}"), Args), true);
this->CreateError(FString::Format(TEXT("Could not find data for a product called {0}"), Args));
}
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerBugzillaAPI::ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "bug_severity")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->SeverityList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerBugzillaAPI::ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "rep_platform")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->PlatformsList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerBugzillaAPI::ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "op_sys")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->OSList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerBugzillaAPI::CheckIfAllFormResponsesAreIn()
{
if (!this->ComponentsList.IsEmpty() && !this->VersionsList.IsEmpty() && !this->SeverityList.IsEmpty() &&
!this->PlatformsList.IsEmpty() && !this->OSList.IsEmpty())
{
FUnrealzillaFormPrepData Data;
Data.ComponentsList = this->ComponentsList;
Data.SeverityList = this->SeverityList;
Data.VersionsList = this->VersionsList;
Data.PlatformsList = this->PlatformsList;
Data.OSList = this->OSList;
// Find a default version number to use if possible
if (this->VersionsList.Contains(GetGameVersion()))
{
Data.DetectedVersion = GetGameVersion();
}
else if (this->VersionsList.Contains("unspecified"))
{
Data.DetectedVersion = "unspecified";
}
else if (this->VersionsList.Contains("Latest"))
{
Data.DetectedVersion = "Latest";
}
else if (!this->VersionsList.IsEmpty())
{
Data.DetectedVersion = this->VersionsList[0];
}
// Set these as defaults in case nothing below changes this setting
Data.DetectedHardware = "All";
Data.DetectedOS = "All";
if (this->PlatformsList.Contains("PC"))
{
// Try our best to auto-detect PC hardware
if (UGameplayStatics::GetPlatformName() == "Windows" && this->OSList.Contains("Windows"))
{
Data.DetectedHardware = "PC";
Data.DetectedOS = "Windows";
}
else if (UGameplayStatics::GetPlatformName() == "Linux" && this->OSList.Contains("Linux"))
{
Data.DetectedHardware = "PC";
Data.DetectedOS = "Linux";
}
else if (UGameplayStatics::GetPlatformName() == "Mac" && this->OSList.Contains("Mac OS"))
{
Data.DetectedHardware = "All";
Data.DetectedOS = "Mac OS";
}
}
if (UGameplayStatics::GetPlatformName() == "Mac")
{
// Try our best to auto-detect Macintosh hardware
if (this->PlatformsList.Contains("Macintosh"))
{
if (this->OSList.Contains("Mac OS"))
{
Data.DetectedHardware = "Macintosh";
Data.DetectedOS = "Mac OS";
}
}
}
this->FormDataResponse.Execute(Data);
this->ComponentsList.Empty();
this->VersionsList.Empty();
this->SeverityList.Empty();
this->PlatformsList.Empty();
this->OSList.Empty();
}
}

View File

@ -0,0 +1,518 @@
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
#include "ServerJiraAPI.h"
#include "HttpModule.h"
#include "JsonObjectConverter.h"
#include "UnrealzillaGlobalSettings.h"
#include "Kismet/GameplayStatics.h"
void UServerJiraAPI::ReturnListOfBugs()
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->JiraSubmissionServer + "/rest";
TArray<FString> StatusQueries;
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowUnresolvedBugs)
{
for (const FString Unresolved : GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses)
{
StatusQueries.Add("status=" + Unresolved);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowInProgressBugs)
{
for (const FString InProgress : GetDefault<UUnrealzillaGlobalSettings>()->InProgressStatuses)
{
StatusQueries.Add("status=" + InProgress);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowResolvedBugs)
{
for (const FString Resolved : GetDefault<UUnrealzillaGlobalSettings>()->ResolvedStatuses)
{
StatusQueries.Add("status=" + Resolved);
}
}
StatusQueries.Add("cf_mapname=" + this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len()));
StatusQueries.Add("api_key=" + GetDefault<UUnrealzillaGlobalSettings>()->JiraAPIKey);
const FString QueryString = FString::Join(StatusQueries, TEXT("&"));
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/bug" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ListOfBugsResponse);
SeverityRequest->ProcessRequest();
}
void UServerJiraAPI::ListOfBugsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONBugResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (!ResponseData.error)
{
TArray<FUnrealzillaBugData> BugData;
for (const FBugzillaJSONBugData &BugzillaData : ResponseData.bugs)
{
FUnrealzillaBugData Bug;
Bug.ID = BugzillaData.id;
Bug.Summary = BugzillaData.summary;
Bug.Component = BugzillaData.component;
Bug.MapName = BugzillaData.cf_mapname;
Bug.MapLocation = BugzillaData.cf_location;
Bug.Platform = BugzillaData.platform;
Bug.OperatingSystem = BugzillaData.op_sys;
Bug.Severity = BugzillaData.severity;
Bug.Status = BugzillaData.status;
Bug.Resolution = BugzillaData.resolution;
Bug.DuplicateOf = BugzillaData.dupe_of;
Bug.bIsOpen = BugzillaData.is_open;
BugData.Add(Bug);
}
this->BugDataResponse.Execute(BugData);
}
else
{
this->CreateError(EErrorVerb::GET, ResponseData);
}
}
}
void UServerJiraAPI::PrepareForm()
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->JiraSubmissionServer + "/rest";
// Assemble query data into key:value pairs
TMap<FString, FString> QueryData;
QueryData.Add("api_key", GetDefault<UUnrealzillaGlobalSettings>()->JiraAPIKey);
const FString QueryString = UServerAPI::FormatQueryString(QueryData);
// Query the server for information about the current product
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> ProductRequest = HttpModule.CreateRequest();
ProductRequest->SetVerb(TEXT("GET"));
ProductRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
ProductRequest->SetURL(FullURL + "/product/" + GetDefault<UUnrealzillaGlobalSettings>()->JiraProjectName + "?" + QueryString);
ProductRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerProductInfoResponse);
ProductRequest->ProcessRequest();
// Send a second query to retrieve severity options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/field/bug/bug_severity" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerSeverityInfoResponse);
SeverityRequest->ProcessRequest();
// Send a third query to retrieve platform options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> PlatformsRequest = HttpModule.CreateRequest();
PlatformsRequest->SetVerb(TEXT("GET"));
PlatformsRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
PlatformsRequest->SetURL(FullURL + "/field/bug/rep_platform" + "?" + QueryString);
PlatformsRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerPlatformInfoResponse);
PlatformsRequest->ProcessRequest();
// Send a final query to retrieve OS options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> OSRequest = HttpModule.CreateRequest();
OSRequest->SetVerb(TEXT("GET"));
OSRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
OSRequest->SetURL(FullURL + "/field/bug/op_sys" + "?" + QueryString);
OSRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerOSInfoResponse);
OSRequest->ProcessRequest();
}
void UServerJiraAPI::SendFormData(const FUnrealzillaPostData &PostData)
{
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->JiraSubmissionServer + "/rest";
const FString RequestURL = "/bug";
// Assemble query data into key:value pairs
TMap<FString, FString> QueryData;
QueryData.Add("api_key", GetDefault<UUnrealzillaGlobalSettings>()->JiraAPIKey);
const FString DefaultStatus = GetDefault<UUnrealzillaGlobalSettings>()->DefaultStatus;
FBugzillaJSONPostBug PostDataJSON;
PostDataJSON.product = GetDefault<UUnrealzillaGlobalSettings>()->JiraProjectName;
PostDataJSON.version = PostData.Version;
PostDataJSON.platform = PostData.Platform;
PostDataJSON.op_sys = PostData.OS;
PostDataJSON.component = PostData.Component;
PostDataJSON.severity = PostData.Severity;
PostDataJSON.cf_mapname = PostData.MapName;
PostDataJSON.cf_location = PostData.MapLocation;
PostDataJSON.summary = PostData.Summary;
PostDataJSON.description = PostData.Comment;
if (!DefaultStatus.IsEmpty())
{
PostDataJSON.status = DefaultStatus;
}
if (PostDataJSON.version.IsEmpty())
{
this->CreateError("You must select a version number.");
return;
}
if (PostDataJSON.platform.IsEmpty() || PostDataJSON.op_sys.IsEmpty())
{
PostDataJSON.platform = "All";
PostDataJSON.op_sys = "All";
}
if (PostDataJSON.component.IsEmpty())
{
this->CreateError("You must select a component.");
return;
}
if (PostDataJSON.severity.IsEmpty())
{
this->CreateError("You must select a severity level.");
return;
}
if (PostDataJSON.summary.IsEmpty())
{
this->CreateError("You must provide a summary.");
return;
}
FString PostJsonString;
FJsonObjectConverter::UStructToJsonObjectString(PostDataJSON, PostJsonString);
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = HttpModule.CreateRequest();
Request->SetVerb(TEXT("POST"));
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->SetURL(FullURL + RequestURL + "?" + UServerAPI::FormatQueryString(QueryData));
Request->SetContentAsString(PostJsonString);
Request->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerPOSTResponse);
Request->ProcessRequest();
}
void UServerJiraAPI::ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONPostResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
this->CreateError(EErrorVerb::POST, ResponseData);
}
else
{
// Use the response's bug ID to get the info from the newly filed bug report and update its marker
const FString FullURL = GetDefault<UUnrealzillaGlobalSettings>()->JiraSubmissionServer + "/rest";
TArray<FString> StatusQueries;
StatusQueries.Add("id=" + FString::FromInt(ResponseData.id));
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowUnresolvedBugs)
{
for (const FString Unresolved : GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses)
{
StatusQueries.Add("status=" + Unresolved);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowInProgressBugs)
{
for (const FString InProgress : GetDefault<UUnrealzillaGlobalSettings>()->InProgressStatuses)
{
StatusQueries.Add("status=" + InProgress);
}
}
if (GetDefault<UUnrealzillaGlobalSettings>()->bShowResolvedBugs)
{
for (const FString Resolved : GetDefault<UUnrealzillaGlobalSettings>()->ResolvedStatuses)
{
StatusQueries.Add("status=" + Resolved);
}
}
StatusQueries.Add("cf_mapname=" + this->GetWorld()->GetMapName().RightChop(this->GetWorld()->StreamingLevelsPrefix.Len()));
StatusQueries.Add("api_key=" + GetDefault<UUnrealzillaGlobalSettings>()->JiraAPIKey);
const FString QueryString = FString::Join(StatusQueries, TEXT("&"));
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> SeverityRequest = HttpModule.CreateRequest();
SeverityRequest->SetVerb(TEXT("GET"));
SeverityRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
SeverityRequest->SetURL(FullURL + "/bug" + "?" + QueryString);
SeverityRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerPOSTUpdateMarkerResponse);
SeverityRequest->ProcessRequest();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONBugResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.bugs.IsEmpty())
{
TArray<FUnrealzillaBugData> BugData;
FUnrealzillaBugData Bug;
Bug.ID = ResponseData.bugs[0].id;
Bug.Summary = ResponseData.bugs[0].summary;
Bug.Component = ResponseData.bugs[0].component;
Bug.MapName = ResponseData.bugs[0].cf_mapname;
Bug.MapLocation = ResponseData.bugs[0].cf_location;
Bug.Platform = ResponseData.bugs[0].platform;
Bug.OperatingSystem = ResponseData.bugs[0].op_sys;
Bug.Severity = ResponseData.bugs[0].severity;
Bug.Status = ResponseData.bugs[0].status;
Bug.Resolution = ResponseData.bugs[0].resolution;
Bug.DuplicateOf = ResponseData.bugs[0].dupe_of;
Bug.bIsOpen = ResponseData.bugs[0].is_open;
BugData.Add(Bug);
this->BugDataResponse.Execute(BugData);
}
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONProductResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.products.IsEmpty())
{
const FBugzillaJSONProductData &ProductData = ResponseData.products[0];
if (ProductData.name == GetDefault<UUnrealzillaGlobalSettings>()->JiraProjectName)
{
for (const FBugzillaJSONComponentData &ComponentData : ProductData.components)
{
this->ComponentsList.Add(ComponentData.name);
}
for (const FBugzillaJSONVersionData &VersionData : ProductData.versions)
{
this->VersionsList.Add(VersionData.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
else
{
FStringFormatOrderedArguments Args;
Args.Add(FStringFormatArg(GetDefault<UUnrealzillaGlobalSettings>()->JiraProjectName));
//this->ShowProcessingOverlayMessage(FString::Format(TEXT("Could not find data for a product called {0}"), Args), true);
this->CreateError(FString::Format(TEXT("Could not find data for a product called {0}"), Args));
}
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "bug_severity")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->SeverityList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "rep_platform")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->PlatformsList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
if (ResponseData.error)
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "op_sys")
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->OSList.Add(FieldValue.name);
}
}
this->CheckIfAllFormResponsesAreIn();
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::CheckIfAllFormResponsesAreIn()
{
if (!this->ComponentsList.IsEmpty() && !this->VersionsList.IsEmpty() && !this->SeverityList.IsEmpty() &&
!this->PlatformsList.IsEmpty() && !this->OSList.IsEmpty())
{
FUnrealzillaFormPrepData Data;
Data.ComponentsList = this->ComponentsList;
Data.SeverityList = this->SeverityList;
Data.VersionsList = this->VersionsList;
Data.PlatformsList = this->PlatformsList;
Data.OSList = this->OSList;
// Find a default version number to use if possible
if (this->VersionsList.Contains(GetGameVersion()))
{
Data.DetectedVersion = GetGameVersion();
}
else if (this->VersionsList.Contains("unspecified"))
{
Data.DetectedVersion = "unspecified";
}
else if (this->VersionsList.Contains("Latest"))
{
Data.DetectedVersion = "Latest";
}
else if (!this->VersionsList.IsEmpty())
{
Data.DetectedVersion = this->VersionsList[0];
}
// Set these as defaults in case nothing below changes this setting
Data.DetectedHardware = "All";
Data.DetectedOS = "All";
if (this->PlatformsList.Contains("PC"))
{
// Try our best to auto-detect PC hardware
if (UGameplayStatics::GetPlatformName() == "Windows" && this->OSList.Contains("Windows"))
{
Data.DetectedHardware = "PC";
Data.DetectedOS = "Windows";
}
else if (UGameplayStatics::GetPlatformName() == "Linux" && this->OSList.Contains("Linux"))
{
Data.DetectedHardware = "PC";
Data.DetectedOS = "Linux";
}
else if (UGameplayStatics::GetPlatformName() == "Mac" && this->OSList.Contains("Mac OS"))
{
Data.DetectedHardware = "All";
Data.DetectedOS = "Mac OS";
}
}
if (UGameplayStatics::GetPlatformName() == "Mac")
{
// Try our best to auto-detect Macintosh hardware
if (this->PlatformsList.Contains("Macintosh"))
{
if (this->OSList.Contains("Mac OS"))
{
Data.DetectedHardware = "Macintosh";
Data.DetectedOS = "Mac OS";
}
}
}
this->FormDataResponse.Execute(Data);
this->ComponentsList.Empty();
this->VersionsList.Empty();
this->SeverityList.Empty();
this->PlatformsList.Empty();
this->OSList.Empty();
}
}

View File

@ -8,6 +8,7 @@
#include "API/BugzillaJSONStructs.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Kismet/GameplayStatics.h"
#include "BugMarkerSubsystem.generated.h"
@ -32,6 +33,11 @@ public:
void SubmitForm(const struct FUnrealzillaPostData &PostData);
UFUNCTION(BlueprintPure, meta=(WorldContext="WorldContextObject"))
static UBugMarkerSubsystem *GetBugMarkerSubsystem(const UObject *WorldContextObject) { return UGameplayStatics::GetPlayerController(WorldContextObject, 0)->GetLocalPlayer()->GetSubsystem<UBugMarkerSubsystem>(); }
static FString GetProjectName();
TObjectPtr<class UServerAPI> &GetServerAPI() { return this->ServerAPI; }
DECLARE_DELEGATE_OneParam(FBugMarkerSubsystemFormPrep, const struct FUnrealzillaFormPrepData &);

View File

@ -21,9 +21,9 @@ class UNREALZILLA_API UServerAPI : public UObject
GENERATED_BODY()
public:
void ReturnListOfBugs();
void PrepareForm();
void SendFormData(const FUnrealzillaPostData &PostData);
virtual void ReturnListOfBugs();
virtual void PrepareForm();
virtual void SendFormData(const FUnrealzillaPostData &PostData);
DECLARE_DELEGATE_OneParam(FBugListResponseDelegate, const TArray<FUnrealzillaBugData>&);
FBugListResponseDelegate BugDataResponse;
@ -40,16 +40,7 @@ public:
TArray<FString> GetPlatformsList() const { return this->PlatformsList; }
TArray<FString> GetOSList() const { return this->OSList; }
private:
void ListOfBugsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void CheckIfAllFormResponsesAreIn();
protected:
void ServerConnectionError(const EHttpRequestStatus::Type Status);
void CreateError(const EErrorVerb &Verb, const FBugzillaJSONPostResponse &Data);

View File

@ -0,0 +1,33 @@
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
#pragma once
#include "ServerAPI.h"
#include "ServerBugzillaAPI.generated.h"
/**
* Class for communicating with REST API on Bugzilla
*/
UCLASS()
class UNREALZILLA_API UServerBugzillaAPI : public UServerAPI
{
GENERATED_BODY()
public:
virtual void ReturnListOfBugs() override;
virtual void PrepareForm() override;
virtual void SendFormData(const FUnrealzillaPostData &PostData) override;
private:
void ListOfBugsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void CheckIfAllFormResponsesAreIn();
};

View File

@ -0,0 +1,33 @@
// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.
#pragma once
#include "ServerAPI.h"
#include "ServerJiraAPI.generated.h"
/**
* Class for communicating with REST API on Jira
*/
UCLASS()
class UNREALZILLA_API UServerJiraAPI : public UServerAPI
{
GENERATED_BODY()
public:
virtual void ReturnListOfBugs() override;
virtual void PrepareForm() override;
virtual void SendFormData(const FUnrealzillaPostData &PostData) override;
private:
void ListOfBugsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerProductInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerSeverityInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void CheckIfAllFormResponsesAreIn();
};

View File

@ -8,6 +8,14 @@
#include "UnrealzillaGlobalSettings.generated.h"
UENUM()
enum class EBugReportPlatform : uint8
{
Bugzilla UMETA(DisplayName="Bugzilla"),
Jira UMETA(DisplayName="Jira")
};
/**
* Global settings for Unrealzilla classes
*/
@ -17,6 +25,10 @@ class UNREALZILLA_API UUnrealzillaGlobalSettings : public UDeveloperSettingsBack
GENERATED_BODY()
public:
// Platform to use for reporting bugs
UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly)
EBugReportPlatform BugReportPlatform = EBugReportPlatform::Bugzilla;
// The distance to send the trace out when placing bug markers in the world.
UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, Category="Bug Placement", meta=(DisplayName="Precise Placement Distance"))
float BugPlacementTraceDistance = 1500.0f;
@ -24,60 +36,80 @@ public:
UPROPERTY(Config, BlueprintReadOnly, EditDefaultsOnly, Category="Bug Placement", meta=(DisplayName="Arbitrary Placement Distance"))
float ArbitraryBugPlacementDistance = 250.0f;
// The status to use when filing a new bug. A status such as "UNCONFIRMED" is suggested.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting")
FString DefaultStatus;
// The viewport depth of the bug report interface widget.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting")
int32 BugReportWidgetDepth = 0;
// The Bugzilla server where bugs will be posted.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla")
FString SubmissionServer;
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla", meta=(DisplayName="Server"))
FString BugzillaSubmissionServer;
// URI path to your Jira instance's REST API.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla", meta=(DisplayName="Bugzilla REST URI"))
FString BugzillaRESTURI = "rest/";
// The name of the product for which bugs will be posted.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla")
FString ProductName;
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla", meta=(DisplayName="Product Name"))
FString BugzillaProductName;
// The API key to use when posting bugs. All bugs will be posted under the account of the owner of this API key.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla", meta=(DisplayName="API Key"))
FString APIKey;
// The viewport depth of the bug report interface widget.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla")
int32 BugReportWidgetDepth = 0;
// The status to use when filing a new bug. A status such as "UNCONFIRMED" is suggested.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla")
FString DefaultStatus;
FString BugzillaAPIKey;
// The Jira server where bugs will be posted.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Server"))
FString JiraSubmissionServer;
// URI path to your Jira instance's REST API.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Jira REST URI"))
FString JiraRESTURI = "rest/";
// The name of the project for which bugs will be posted.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Project Name"))
FString JiraProjectName;
// The username to use when posting bugs.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Username"))
FString JiraUsername;
// The API key to use when posting bugs. Using the account password should work here, but it is
// highly recommended that you generate a proper API key in the Atlassian account of the above user.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="API Key"))
FString JiraAPIKey;
// Whether to show unresolved bugs when displaying bug report markers.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Unresolved")
bool bShowUnresolvedBugs = true;
// Colour tint to use for unresolved bugs.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Unresolved")
FLinearColor UnresolvedTint;
// A list of bug statuses that represent unresolved bugs in your Bugzilla install.
// A list of bug statuses that represent unresolved bugs on your server.
// Generally this would be something like "UNCONFIRMED" and "CONFIRMED".
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Unresolved")
TArray<FString> UnresolvedStatuses;
// Whether to show in-progress bugs when displaying bug report markers.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|In Progress")
bool bShowInProgressBugs = true;
// Colour tint to use for in-progress bugs.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|In Progress")
FLinearColor InProgressTint;
// A list of bug statuses that represent in progress bugs in your Bugzilla install.
// A list of bug statuses that represent in progress bugs on your server.
// Generally this would include "IN_PROGRESS".
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|In Progress")
TArray<FString> InProgressStatuses;
// Whether to show resolved bugs when displaying bug report markers.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Resolved")
bool bShowResolvedBugs = false;
// Colour tint to use for resolved bugs.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Resolved")
FLinearColor ResolvedTint;
// A list of bug statuses that represent resolved bugs in your Bugzilla install.
// A list of bug statuses that represent resolved bugs on your server.
// Generally this would include "RESOLVED" and "VERIFIED".
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Resolved")
TArray<FString> ResolvedStatuses;
// How many bug markers to show in one batch. Each batch loads on a new frame.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Display")
int32 LoadMarkersBatch = 10;
// How many bug markers to hide in one batch. Each batch loads on a new frame.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Marking")
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Display")
int32 UnloadMarkersBatch = 25;
public: