Preparing the code for the Jira implementation.
This commit is contained in:
parent
81b638e9b3
commit
52a5d9d60d
@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
518
Source/Unrealzilla/Private/ServerBugzillaAPI.cpp
Normal file
518
Source/Unrealzilla/Private/ServerBugzillaAPI.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
518
Source/Unrealzilla/Private/ServerJiraAPI.cpp
Normal file
518
Source/Unrealzilla/Private/ServerJiraAPI.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -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 &);
|
||||
|
||||
@ -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);
|
||||
|
||||
33
Source/Unrealzilla/Public/ServerBugzillaAPI.h
Normal file
33
Source/Unrealzilla/Public/ServerBugzillaAPI.h
Normal 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();
|
||||
};
|
||||
33
Source/Unrealzilla/Public/ServerJiraAPI.h
Normal file
33
Source/Unrealzilla/Public/ServerJiraAPI.h
Normal 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();
|
||||
};
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user