Jira now works too. Too many changes to document still.

This commit is contained in:
Jamie Greunbaum 2023-04-09 23:01:09 -04:00
parent a11bd66fbc
commit 504655d19a
15 changed files with 566 additions and 413 deletions

View File

@ -4,7 +4,7 @@
#include "CoreMinimal.h"
//#include "BugzillaJSONStructs.generated.h"
#include "BugzillaJSONStructs.generated.h"
/**

View File

@ -4,4 +4,4 @@
#include "CoreMinimal.h"
//#include "JiraJSONStructs.generated.h"
#include "JiraJSONStructs.generated.h"

View File

@ -21,13 +21,17 @@ void UBugMarkerSubsystem::Initialize(FSubsystemCollectionBase &Collection)
this->ServerAPI = NewObject<UServerBugzillaAPI>(this);
break;
case EBugReportPlatform::Jira:
// this->ServerAPI = NewObject<UServerJiraAPI>(this);
// break;
this->ServerAPI = NewObject<UServerJiraAPI>(this);
break;
default:
this->ServerAPI = NewObject<UServerAPI>(this);
}
if (!this->ServerAPI)
if (this->ServerAPI)
{
this->ServerAPI->Initialize();
}
else
{
UE_LOG(BugMarkerSubsystemLog, Error, TEXT("Could not create Server API class; this will certainly crash eventually"));
}
@ -79,6 +83,7 @@ void UBugMarkerSubsystem::HideBugMarkers()
}
}
void UBugMarkerSubsystem::PrepareSubmissionFormData()
{
this->ServerAPI->FormDataResponse.BindUObject(this, &UBugMarkerSubsystem::FormPrepResponseCallback);
@ -106,7 +111,7 @@ void UBugMarkerSubsystem::LoadNewBatch()
const FUnrealzillaBugData BugData = this->BugBatch[0];
FString LocationString, UpVectorString;
BugData.MapLocation.Split(":", &LocationString, &UpVectorString);
BugData.MarkerLocation.Split(":", &LocationString, &UpVectorString);
FString LocationX, LocationY, LocationZ;
LocationString.Split(",", &LocationX, &LocationY);

View File

@ -36,7 +36,7 @@ void UBugSubmissionForm::NativeOnInitialized()
{
this->ShowProcessingOverlayLoading();
UBugMarkerSubsystem *BugMarkerSubsystem = UGameplayStatics::GetPlayerController(this, 0)->GetLocalPlayer()->GetSubsystem<UBugMarkerSubsystem>();
UBugMarkerSubsystem *BugMarkerSubsystem = UBugMarkerSubsystem::GetBugMarkerSubsystem(this);
BugMarkerSubsystem->FormPrepResponse.BindUObject(this, &UBugSubmissionForm::PrepareFormData);
BugMarkerSubsystem->PrepareSubmissionFormData();
@ -95,13 +95,13 @@ void UBugSubmissionForm::ShowProcessingOverlayLoading()
this->ProcessingRequestErrorBox->SetVisibility(ESlateVisibility::Collapsed);
}
void UBugSubmissionForm::ShowProcessingOverlayMessage(const FString Message, const bool bExitAfterConfirm)
void UBugSubmissionForm::ShowProcessingOverlayMessage(const FUnrealzillaErrorData &Error)
{
this->ProcessingRequestErrorText->SetText(FText::AsCultureInvariant(Message));
this->ProcessingRequestErrorText->SetText(FText::AsCultureInvariant(Error.ErrorMessage));
this->ProcessingRequestOverlay->SetVisibility(ESlateVisibility::Visible);
this->ProcessingRequestThrobber->SetVisibility(ESlateVisibility::Collapsed);
this->ProcessingRequestErrorBox->SetVisibility(ESlateVisibility::Visible);
if (bExitAfterConfirm)
if (Error.bCancelForm)
{
this->ProcessingRequestErrorButton->OnClicked().AddUObject(this, &UBugSubmissionForm::CancelForm);
}
@ -123,12 +123,13 @@ void UBugSubmissionForm::SubmitForm()
PostData.Component = this->ComponentButton->GetText().ToString();
PostData.Severity = this->SeverityButton->GetText().ToString();
PostData.MapName = this->MapName.ToString();
PostData.MapLocation = this->MarkerLocation.ToString();
PostData.MarkerLocation = this->MarkerLocation.ToString();
PostData.Summary = this->SummaryEntryBox->GetText().ToString();
PostData.Comment = this->CommentEntryBox->GetText().ToString();
UBugMarkerSubsystem *BugMarkerSubsystem = UGameplayStatics::GetPlayerController(this, 0)->GetLocalPlayer()->GetSubsystem<UBugMarkerSubsystem>();
UBugMarkerSubsystem *BugMarkerSubsystem = UBugMarkerSubsystem::GetBugMarkerSubsystem(this);
BugMarkerSubsystem->FormPostResponse.BindUObject(this, &UBugSubmissionForm::UpdateReportMarker);
BugMarkerSubsystem->ErrorResponse.BindUObject(this, &UBugSubmissionForm::ShowProcessingOverlayMessage);
BugMarkerSubsystem->SubmitForm(PostData);
this->OnFormSubmit.ExecuteIfBound();
@ -136,7 +137,16 @@ void UBugSubmissionForm::SubmitForm()
void UBugSubmissionForm::UpdateReportMarker(const FUnrealzillaBugData &BugData)
{
this->BugMarkerActor->SetBugData(BugData);
UBugMarkerSubsystem *BugMarkerSubsystem = UBugMarkerSubsystem::GetBugMarkerSubsystem(this);
if (BugMarkerSubsystem->AreMarkersVisible())
{
this->BugMarkerActor->SetBugData(BugData);
BugMarkerSubsystem->AddBugMarker(this->BugMarkerActor);
}
else
{
this->BugMarkerActor->Destroy();
}
this->CloseForm();
}
@ -159,5 +169,5 @@ void UBugSubmissionForm::CloseForm()
void UBugSubmissionForm::ErrorResponseCallback(const FUnrealzillaErrorData &Error)
{
this->ShowProcessingOverlayMessage(Error.ErrorMessage);
this->ShowProcessingOverlayMessage(Error);
}

View File

@ -30,20 +30,17 @@ void UServerAPI::ServerConnectionError(const EHttpRequestStatus::Type Status)
{
switch (Status) {
case EHttpRequestStatus::Failed_ConnectionError:
this->CreateError("Unable to connect to the server");
//this->ShowProcessingOverlayMessage("Unable to connect to the server", true);
break;
case EHttpRequestStatus::NotStarted:
this->CreateError("Connection could not start");
//this->ShowProcessingOverlayMessage("Connection could not start", true);
this->CreateError("There was an error connecting to the server. Please retry.", true);
break;
case EHttpRequestStatus::Failed:
this->CreateError("Connection failed");
//this->ShowProcessingOverlayMessage("Connection failed", true);
this->CreateError("Connection to the server completed but then failed.", true);
break;
case EHttpRequestStatus::NotStarted:
this->CreateError("Connection was not started.");
break;
case EHttpRequestStatus::Processing:
this->CreateError("Connection is still being processed.");
break;
default:
this->CreateError("Request failed for unknown reasons");
//this->ShowProcessingOverlayMessage("Request failed for unknown reasons", true);
}
}
@ -83,11 +80,12 @@ void UServerAPI::CreateError(const EErrorVerb &Verb, const FBugzillaJSONBugRespo
Error.DocumentationLink = Data.documentation;
this->ErrorResponse.Execute(Error);
}
void UServerAPI::CreateError(const FString &ErrorMessage)
void UServerAPI::CreateError(const FString &ErrorMessage, const bool bCancelForm)
{
FUnrealzillaErrorData Error;
Error.ErrorVerb = EErrorVerb::None;
Error.ErrorMessage = ErrorMessage;
Error.bCancelForm = bCancelForm;
this->ErrorResponse.Execute(Error);
}

View File

@ -10,7 +10,7 @@
#include "Kismet/GameplayStatics.h"
UServerBugzillaAPI::UServerBugzillaAPI()
void UServerBugzillaAPI::Initialize()
{
this->FullURL = UServerAPI::URLBuilder(
GetDefault<UUnrealzillaGlobalSettings>()->BugzillaSubmissionServer,
@ -74,7 +74,7 @@ void UServerBugzillaAPI::ListOfBugsResponse(FHttpRequestPtr Request, FHttpRespon
Bug.Summary = BugzillaData.summary;
Bug.Component = BugzillaData.component;
Bug.MapName = BugzillaData.cf_mapname;
Bug.MapLocation = BugzillaData.cf_location;
Bug.MarkerLocation = BugzillaData.cf_location;
Bug.Platform = BugzillaData.platform;
Bug.OperatingSystem = BugzillaData.op_sys;
Bug.Severity = BugzillaData.severity;
@ -140,7 +140,7 @@ void UServerBugzillaAPI::SendFormData(const FUnrealzillaPostData &PostData)
TMap<FString, FString> QueryData;
QueryData.Add("api_key", GetDefault<UUnrealzillaGlobalSettings>()->BugzillaAPIKey);
const FString DefaultStatus = GetDefault<UUnrealzillaGlobalSettings>()->DefaultStatus;
const FString DefaultStatus = GetDefault<UUnrealzillaGlobalSettings>()->BugzillaDefaultStatus;
FBugzillaJSONPostBug PostDataJSON;
PostDataJSON.product = GetDefault<UUnrealzillaGlobalSettings>()->BugzillaProductName;
@ -150,7 +150,7 @@ void UServerBugzillaAPI::SendFormData(const FUnrealzillaPostData &PostData)
PostDataJSON.component = PostData.Component;
PostDataJSON.severity = PostData.Severity;
PostDataJSON.cf_mapname = PostData.MapName;
PostDataJSON.cf_location = PostData.MapLocation;
PostDataJSON.cf_location = PostData.MarkerLocation;
PostDataJSON.summary = PostData.Summary;
PostDataJSON.description = PostData.Comment;
if (!DefaultStatus.IsEmpty())
@ -278,7 +278,7 @@ void UServerBugzillaAPI::ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request,
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.MarkerLocation = ResponseData.bugs[0].cf_location;
Bug.Platform = ResponseData.bugs[0].platform;
Bug.OperatingSystem = ResponseData.bugs[0].op_sys;
Bug.Severity = ResponseData.bugs[0].severity;

View File

@ -10,437 +10,332 @@
#include "Kismet/GameplayStatics.h"
UServerJiraAPI::UServerJiraAPI()
void UServerJiraAPI::Initialize()
{
this->FullURL = UServerAPI::URLBuilder(
GetDefault<UUnrealzillaGlobalSettings>()->JiraSubmissionServer,
GetDefault<UUnrealzillaGlobalSettings>()->JiraRESTURI
);
FHttpModule &HttpModule = FHttpModule::Get();
// Get board data
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> BoardRequest = HttpModule.CreateRequest();
BoardRequest->SetVerb(TEXT("GET"));
BoardRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
BoardRequest->AppendToHeader(TEXT("Authorization"), TEXT("Basic ") + this->GenerateAuthString());
BoardRequest->SetURL(this->FullURL + "agile/1.0/board");
BoardRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ListOfBoardsResponse);
BoardRequest->ProcessRequest();
// Get custom field data
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> FieldsRequest = HttpModule.CreateRequest();
FieldsRequest->SetVerb(TEXT("GET"));
FieldsRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
FieldsRequest->AppendToHeader(TEXT("Authorization"), TEXT("Basic ") + this->GenerateAuthString());
FieldsRequest->SetURL(this->FullURL + "api/2/field");
FieldsRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ListOfFieldsResponse);
FieldsRequest->ProcessRequest();
}
void UServerJiraAPI::ListOfBoardsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
const FString &JSONResponse = Response->GetContentAsString();
TSharedPtr<FJsonObject> JSON = MakeShareable(new FJsonObject);
const TSharedRef<TJsonReader<>> &Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JSON))
{
const TArray<TSharedPtr<FJsonValue>> &BoardsArray = JSON->GetArrayField("values");
for (const TSharedPtr<FJsonValue> &BoardValue : BoardsArray)
{
const TSharedPtr<FJsonObject> &Board = BoardValue->AsObject();
const TSharedPtr<FJsonObject> &Location = Board->GetObjectField("location");
if (Location->GetStringField("projectName") == GetDefault<UUnrealzillaGlobalSettings>()->JiraProjectName)
{
this->BoardID = Board->GetIntegerField("id");
this->ProjectID = Location->GetIntegerField("projectId");
this->ProjectKey = Location->GetStringField("projectKey");
this->ProjectAvatarURI = Location->GetStringField("avatarURI");
}
}
}
else
{
this->CreateError("Could not retrieve board data.");
}
}
else
{
this->CreateError("Could not retrieve board data.");
}
}
void UServerJiraAPI::ListOfFieldsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
const FString &JSONResponse = "{\"fields\":" + Response->GetContentAsString() + "}";
TSharedPtr<FJsonObject> JSON = MakeShareable(new FJsonObject);
const TSharedRef<TJsonReader<>> &Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JSON))
{
const TArray<TSharedPtr<FJsonValue>> &FieldArray = JSON->GetArrayField("fields");
for (const TSharedPtr<FJsonValue> &FieldValue : FieldArray)
{
const TSharedPtr<FJsonObject> &Field = FieldValue->AsObject();
const FString &Name = Field->GetStringField("name");
const FString &Key = Field->GetStringField("key");
if (Name == GetDefault<UUnrealzillaGlobalSettings>()->JiraMapNameField)
{
this->MapNameCustomField = Key;
}
else if (Name == GetDefault<UUnrealzillaGlobalSettings>()->JiraMarkerLocationField)
{
this->MarkerLocationCustomField = Key;
}
else if (Name == GetDefault<UUnrealzillaGlobalSettings>()->JiraDepartmentField)
{
this->DepartmentCustomField = Key;
}
else if (Name == GetDefault<UUnrealzillaGlobalSettings>()->JiraSeverityField)
{
this->SeverityCustomField = Key;
}
else if (Name == GetDefault<UUnrealzillaGlobalSettings>()->JiraPlatformField)
{
this->PlatformCustomField = Key;
}
else if (Name == GetDefault<UUnrealzillaGlobalSettings>()->JiraVersionField)
{
this->VersionCustomField = Key;
}
if (!this->MapNameCustomField.IsEmpty() &&
!this->MarkerLocationCustomField.IsEmpty() &&
!this->DepartmentCustomField.IsEmpty() &&
!this->SeverityCustomField.IsEmpty() &&
!this->PlatformCustomField.IsEmpty() &&
!this->VersionCustomField.IsEmpty())
{
break;
}
}
}
else
{
this->CreateError("Could not deserialise JSON.");
}
}
}
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->AppendToHeader(TEXT("Authorization"), TEXT("Basic ") + this->GenerateAuthString());
SeverityRequest->SetURL(this->FullURL + "agile/1.0/board/" + FString::FromInt(this->BoardID) + "/issue?expand=names&fields=summary,description,issuetype,status,priority" + (this->MapNameCustomField.IsEmpty()?"":(","+this->MapNameCustomField)) + (this->MarkerLocationCustomField.IsEmpty()?"":(","+this->MarkerLocationCustomField)));
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);
TArray<FUnrealzillaBugData> BugData;
if (!ResponseData.error)
const FString JSONResponse = Response->GetContentAsString();
TSharedPtr<FJsonObject> JSON = MakeShareable(new FJsonObject);
const TSharedRef<TJsonReader<>> &Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JSON))
{
TArray<FUnrealzillaBugData> BugData;
for (const FBugzillaJSONBugData &BugzillaData : ResponseData.bugs)
const TArray<TSharedPtr<FJsonValue>> &IssuesArray = JSON->GetArrayField("issues");
for (const TSharedPtr<FJsonValue> &IssueValue : IssuesArray)
{
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);
const TSharedPtr<FJsonObject> &Issue = IssueValue->AsObject();
const TSharedPtr<FJsonObject> &Fields = Issue->GetObjectField("fields");
const TSharedPtr<FJsonObject> &Status = Fields->GetObjectField("status");
const FString StatusName = Status->GetStringField("name");
if ((GetDefault<UUnrealzillaGlobalSettings>()->bShowUnresolvedBugs && GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses.Contains(StatusName)) ||
(GetDefault<UUnrealzillaGlobalSettings>()->bShowInProgressBugs && GetDefault<UUnrealzillaGlobalSettings>()->InProgressStatuses.Contains(StatusName)) ||
(GetDefault<UUnrealzillaGlobalSettings>()->bShowResolvedBugs && GetDefault<UUnrealzillaGlobalSettings>()->ResolvedStatuses.Contains(StatusName)))
{
const TSharedPtr<FJsonObject> &Priority = Fields->GetObjectField("priority");
FUnrealzillaBugData Bug;
Bug.ID = Issue->GetNumberField("id");
Bug.Status = StatusName;
Bug.Summary = Fields->GetStringField("summary");
Bug.Severity = Priority->GetStringField("name");
//Bug.Component = BugzillaData.component;
//Bug.Platform = BugzillaData.platform;
//Bug.OperatingSystem = BugzillaData.op_sys;
//Bug.Resolution = BugzillaData.resolution;
//Bug.DuplicateOf = BugzillaData.dupe_of;
//Bug.bIsOpen = BugzillaData.is_open;
if (!this->MapNameCustomField.IsEmpty())
{
Bug.MapName = Fields->GetStringField(this->MapNameCustomField);
}
if (!this->MarkerLocationCustomField.IsEmpty())
{
Bug.MarkerLocation = Fields->GetStringField(this->MarkerLocationCustomField);
}
BugData.Add(Bug);
}
}
this->BugDataResponse.Execute(BugData);
}
else
{
this->CreateError(EErrorVerb::GET, ResponseData);
this->CreateError("Could not deserialise JSON.");
}
this->BugDataResponse.Execute(BugData);
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
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);
QueryData.Add("issuetypeNames", GetDefault<UUnrealzillaGlobalSettings>()->JiraBugIssueType);
QueryData.Add("expand", "projects.issuetypes.fields");
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);
// Keep track of how many of these tasks have completed so we can check their completion status later.
this->FieldListsCompletion = 0;
this->FieldListsCompletionMax = 2;
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();
// Send a query to retrieve field options
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> FieldsRequest = HttpModule.CreateRequest();
FieldsRequest->SetVerb(TEXT("GET"));
FieldsRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
FieldsRequest->AppendToHeader(TEXT("Authorization"), TEXT("Basic ") + this->GenerateAuthString());
FieldsRequest->SetURL(this->FullURL + "api/2/issue/createmeta?" + QueryString);
FieldsRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerFieldOptionsResponse);
FieldsRequest->ProcessRequest();
// Send a second query to retrieve version numbers
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> VersionsRequest = HttpModule.CreateRequest();
VersionsRequest->SetVerb(TEXT("GET"));
VersionsRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
VersionsRequest->AppendToHeader(TEXT("Authorization"), TEXT("Basic ") + this->GenerateAuthString());
VersionsRequest->SetURL(this->FullURL + "api/2/project/" + FString::FromInt(this->ProjectID) + "/version");
VersionsRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerVersionsResponse);
VersionsRequest->ProcessRequest();
}
void UServerJiraAPI::ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
void UServerJiraAPI::ServerFieldOptionsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONPostResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
const FString &JSONResponse = Response->GetContentAsString();
if (ResponseData.error)
TSharedPtr<FJsonObject> JSON = MakeShareable(new FJsonObject);
const TSharedRef<TJsonReader<>> &Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JSON))
{
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)
const TArray<TSharedPtr<FJsonValue>> &ProjectsArray = JSON->GetArrayField("projects");
for (const TSharedPtr<FJsonValue> &ProjectValue : ProjectsArray)
{
for (const FString Unresolved : GetDefault<UUnrealzillaGlobalSettings>()->UnresolvedStatuses)
const TSharedPtr<FJsonObject> &Project = ProjectValue->AsObject();
if (Project->GetStringField("name") == GetDefault<UUnrealzillaGlobalSettings>()->JiraProjectName)
{
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)
const TArray<TSharedPtr<FJsonValue>> &IssueTypes = Project->GetArrayField("issuetypes");
for (const TSharedPtr<FJsonValue> &IssueTypeValue : IssueTypes)
{
this->ComponentsList.Add(ComponentData.name);
const TSharedPtr<FJsonObject> &IssueType = IssueTypeValue->AsObject();
const TSharedPtr<FJsonObject> &Fields = IssueType->GetObjectField("fields");
// After all that boilerplate code, finally get all available field options here
{
// Components
const TSharedPtr<FJsonObject> &Department = Fields->GetObjectField(this->DepartmentCustomField);
const TArray<TSharedPtr<FJsonValue>> &AllowedDepartmentsArray = Department->GetArrayField("allowedValues");
for (const TSharedPtr<FJsonValue> &AllowedDepartmentsValue : AllowedDepartmentsArray)
{
const TSharedPtr<FJsonObject> &Value = AllowedDepartmentsValue->AsObject();
this->ComponentsList.Add(Value->GetStringField("value"));
}
// Bug severity
const TSharedPtr<FJsonObject> &Severity = Fields->GetObjectField(this->SeverityCustomField);
const TArray<TSharedPtr<FJsonValue>> &AllowedSeveritiesArray = Severity->GetArrayField("allowedValues");
for (const TSharedPtr<FJsonValue> &AllowedSeveritiesValue : AllowedSeveritiesArray)
{
const TSharedPtr<FJsonObject> &Value = AllowedSeveritiesValue->AsObject();
this->SeverityList.Add(Value->GetStringField("value"));
}
// Platform
const TSharedPtr<FJsonObject> &Platform = Fields->GetObjectField(this->PlatformCustomField);
const TArray<TSharedPtr<FJsonValue>> &PlatformsArray = Platform->GetArrayField("allowedValues");
for (const TSharedPtr<FJsonValue> &PlatformsValue : PlatformsArray)
{
const TSharedPtr<FJsonObject> &Value = PlatformsValue->AsObject();
this->PlatformsList.Add(Value->GetStringField("value"));
// Also get children and add them to the OS list here
const TArray<TSharedPtr<FJsonValue>> &ChildrenArray = Value->GetArrayField("children");
for (const TSharedPtr<FJsonValue> &ChildValue : ChildrenArray)
{
const TSharedPtr<FJsonObject> &Child = ChildValue->AsObject();
this->OSList.AddUnique(Child->GetStringField("value"));
}
}
}
}
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);
break;
}
}
this->CheckIfAllFormResponsesAreIn();
}
else
{
this->CreateError("Could not deserialise JSON.");
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::ServerPlatformInfoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
void UServerJiraAPI::ServerVersionsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
FBugzillaJSONFieldResponse ResponseData;
FString JSONResponse = Response->GetContentAsString();
FJsonObjectConverter::JsonObjectStringToUStruct(JSONResponse, &ResponseData);
const FString &JSONResponse = Response->GetContentAsString();
if (ResponseData.error)
TSharedPtr<FJsonObject> JSON = MakeShareable(new FJsonObject);
const TSharedRef<TJsonReader<>> &Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JSON))
{
//this->ShowProcessingOverlayMessage(ResponseData.message);
this->CreateError(EErrorVerb::GET, ResponseData);
}
else
{
if (!ResponseData.fields.IsEmpty() && ResponseData.fields[0].name == "rep_platform")
const TArray<TSharedPtr<FJsonValue>> &ValuesArray = JSON->GetArrayField("values");
for (const TSharedPtr<FJsonValue> &ValueObject : ValuesArray)
{
for (const FBugzillaJSONFieldValueData &FieldValue : ResponseData.fields[0].values)
{
this->PlatformsList.Add(FieldValue.name);
}
const TSharedPtr<FJsonObject> &Value = ValueObject->AsObject();
this->VersionsList.Add(Value->GetStringField("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();
this->CreateError("Could not deserialise JSON.");
}
}
else
@ -448,12 +343,14 @@ void UServerJiraAPI::ServerOSInfoResponse(FHttpRequestPtr Request, FHttpResponse
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::CheckIfAllFormResponsesAreIn()
{
if (!this->ComponentsList.IsEmpty() && !this->VersionsList.IsEmpty() && !this->SeverityList.IsEmpty() &&
!this->PlatformsList.IsEmpty() && !this->OSList.IsEmpty())
this->FieldListsCompletion++;
if (this->FieldListsCompletion == this->FieldListsCompletionMax)
{
this->FieldListsCompletion = this->FieldListsCompletionMax = 0;
FUnrealzillaFormPrepData Data;
Data.ComponentsList = this->ComponentsList;
@ -468,10 +365,6 @@ void UServerJiraAPI::CheckIfAllFormResponsesAreIn()
{
Data.DetectedVersion = GameVersion;
}
else if (this->VersionsList.Contains("unspecified"))
{
Data.DetectedVersion = "unspecified";
}
else if (this->VersionsList.Contains("Latest"))
{
Data.DetectedVersion = "Latest";
@ -526,3 +419,196 @@ void UServerJiraAPI::CheckIfAllFormResponsesAreIn()
this->OSList.Empty();
}
}
void UServerJiraAPI::SendFormData(const FUnrealzillaPostData &PostData)
{
if (PostData.Summary.IsEmpty())
{
this->CreateError("You must provide a summary.");
return;
}
TSharedPtr<FJsonObject> JSONPostFields = MakeShareable(new FJsonObject());
JSONPostFields->SetStringField("summary", PostData.Summary);
JSONPostFields->SetStringField("description", PostData.Comment);
JSONPostFields->SetStringField(this->MapNameCustomField, PostData.MapName);
JSONPostFields->SetStringField(this->MarkerLocationCustomField, PostData.MarkerLocation);
TSharedPtr<FJsonObject> Project = MakeShareable(new FJsonObject());
Project->SetStringField("key", this->ProjectKey);
JSONPostFields->SetObjectField("project", Project);
TSharedPtr<FJsonObject> IssueType = MakeShareable(new FJsonObject());
IssueType->SetStringField("name", GetDefault<UUnrealzillaGlobalSettings>()->JiraBugIssueType);
JSONPostFields->SetObjectField("issuetype", IssueType);
//const FString DefaultStatus = GetDefault<UUnrealzillaGlobalSettings>()->JiraDefaultStatus;
//if (!DefaultStatus.IsEmpty())
//{
// TSharedPtr<FJsonObject> Status = MakeShareable(new FJsonObject());
// Status->SetStringField("name", DefaultStatus);
// JSONPostFields->SetObjectField("status", Status);
//}
if (!GetDefault<UUnrealzillaGlobalSettings>()->JiraVersionField.IsEmpty() && !PostData.Version.IsEmpty())
{
TSharedPtr<FJsonObject> Version = MakeShareable(new FJsonObject());
Version->SetStringField("name", PostData.Version);
JSONPostFields->SetObjectField(this->VersionCustomField, Version);
}
if (!GetDefault<UUnrealzillaGlobalSettings>()->JiraPlatformField.IsEmpty() && !PostData.Platform.IsEmpty())
{
TSharedPtr<FJsonObject> PlatformCascade = MakeShareable(new FJsonObject());
TSharedPtr<FJsonObject> OSCascade = MakeShareable(new FJsonObject());
PlatformCascade->SetStringField("value", PostData.Platform);
if (!PostData.OS.IsEmpty())
{
OSCascade->SetStringField("value", PostData.OS);
PlatformCascade->SetObjectField("child", OSCascade);
}
JSONPostFields->SetObjectField(this->PlatformCustomField, PlatformCascade);
}
if (!GetDefault<UUnrealzillaGlobalSettings>()->JiraDepartmentField.IsEmpty() && !PostData.Component.IsEmpty())
{
TSharedPtr<FJsonObject> Category = MakeShareable(new FJsonObject());
Category->SetStringField("value", PostData.Component);
JSONPostFields->SetObjectField(this->DepartmentCustomField, Category);
}
if (!GetDefault<UUnrealzillaGlobalSettings>()->JiraSeverityField.IsEmpty() && !PostData.Severity.IsEmpty())
{
TSharedPtr<FJsonObject> Severity = MakeShareable(new FJsonObject());
Severity->SetStringField("value", PostData.Severity);
JSONPostFields->SetObjectField(this->SeverityCustomField, Severity);
}
TSharedPtr<FJsonObject> JSONPostObject = MakeShareable(new FJsonObject());
JSONPostObject->SetObjectField("fields", JSONPostFields);
FString PostJsonString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&PostJsonString);
FJsonSerializer::Serialize(JSONPostObject.ToSharedRef(), Writer);
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> PostRequest = HttpModule.CreateRequest();
PostRequest->SetVerb(TEXT("POST"));
PostRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
PostRequest->AppendToHeader(TEXT("Authorization"), TEXT("Basic ") + this->GenerateAuthString());
PostRequest->SetURL(this->FullURL + "api/2/issue");
PostRequest->SetContentAsString(PostJsonString);
PostRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerPOSTResponse);
PostRequest->ProcessRequest();
}
void UServerJiraAPI::ServerPOSTResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
const FString &JSONResponse = Response->GetContentAsString();
TSharedPtr<FJsonObject> JSON = MakeShareable(new FJsonObject);
const TSharedRef<TJsonReader<>> &Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JSON))
{
// I'm not sure what is meant to show up in the "errorMessages" array, but this is where we handle that.
const TArray<TSharedPtr<FJsonValue>> &ErrorMessageArray = JSON->GetArrayField("errorMessages");
if (!ErrorMessageArray.IsEmpty())
{
// Handle error messages here
}
const TSharedPtr<FJsonObject> &Errors = JSON->GetObjectField("errors");
TArray<FString> ErrorStringBuilder;
TArray<FString> ErrorKeys;
Errors->Values.GenerateKeyArray(ErrorKeys);
for (const FString &ErrorKey : ErrorKeys)
{
FStringFormatOrderedArguments Args;
Args.Add(FStringFormatArg(ErrorKey));
Args.Add(FStringFormatArg(Errors->GetStringField(ErrorKey)));
ErrorStringBuilder.Add(FString::Format(TEXT("{0}: {1}"), Args));
}
const FString &ErrorString = FString::Join(ErrorStringBuilder, TEXT("\n"));
if (!ErrorString.IsEmpty())
{
this->CreateError(ErrorString);
return;
}
//const int32 &NewBugID = FCString::Atoi(*JSON->GetStringField("id"));
//const FString &NewBugKey = JSON->GetStringField("key");
const FString &NewBugURI = JSON->GetStringField("self");
FHttpModule &HttpModule = FHttpModule::Get();
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> NewBugRequest = HttpModule.CreateRequest();
NewBugRequest->SetVerb(TEXT("GET"));
NewBugRequest->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
NewBugRequest->AppendToHeader(TEXT("Authorization"), TEXT("Basic ") + this->GenerateAuthString());
NewBugRequest->SetURL(NewBugURI);
NewBugRequest->OnProcessRequestComplete().BindUObject(this, &UServerJiraAPI::ServerPOSTUpdateMarkerResponse);
NewBugRequest->ProcessRequest();
}
else
{
this->CreateError("Could not deserialise JSON.");
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
void UServerJiraAPI::ServerPOSTUpdateMarkerResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success)
{
if (Success)
{
const FString &JSONResponse = Response->GetContentAsString();
TSharedPtr<FJsonObject> JSON = MakeShareable(new FJsonObject);
const TSharedRef<TJsonReader<>> &Reader = TJsonReaderFactory<>::Create(JSONResponse);
if (FJsonSerializer::Deserialize(Reader, JSON))
{
if (JSON->HasTypedField<EJson::Object>("fields"))
{
const TSharedPtr<FJsonObject> &Fields = JSON->GetObjectField("fields");
const TSharedPtr<FJsonObject> &PlatformFields = Fields->GetObjectField(this->PlatformCustomField);
TArray<FUnrealzillaBugData> BugData;
FUnrealzillaBugData Bug;
Bug.ID = JSON->GetIntegerField("id");
Bug.Summary = Fields->GetStringField("summary");
Bug.Component = Fields->GetObjectField(this->DepartmentCustomField)->GetStringField("value");
Bug.MapName = Fields->GetStringField(this->MapNameCustomField);
Bug.MarkerLocation = Fields->GetStringField(this->MarkerLocationCustomField);
Bug.Platform = PlatformFields->GetStringField("value");
Bug.OperatingSystem = PlatformFields->GetObjectField("child")->GetStringField("value");
Bug.Severity = Fields->GetObjectField(this->SeverityCustomField)->GetStringField("value");
Bug.Status = Fields->GetObjectField("status")->GetStringField("name");
Bug.Resolution = Fields->GetStringField("resolution");
BugData.Add(Bug);
this->BugDataResponse.Execute(BugData);
}
}
else
{
this->CreateError("Could not deserialise JSON.");
}
}
else
{
this->ServerConnectionError(Request->GetStatus());
}
}
const FString UServerJiraAPI::GenerateAuthString() const
{
return FBase64::Encode(
GetDefault<UUnrealzillaGlobalSettings>()->JiraUsername +
":" +
GetDefault<UUnrealzillaGlobalSettings>()->JiraAPIKey
);
}

View File

@ -29,6 +29,8 @@ public:
FString ErrorMessage;
UPROPERTY(BlueprintReadOnly)
FString DocumentationLink;
UPROPERTY(BlueprintReadOnly)
bool bCancelForm = false;
};
USTRUCT(Blueprintable)
@ -45,7 +47,7 @@ public:
UPROPERTY(BlueprintReadOnly)
FString MapName;
UPROPERTY(BlueprintReadOnly)
FString MapLocation;
FString MarkerLocation;
UPROPERTY(BlueprintReadOnly)
FString Platform;
UPROPERTY(BlueprintReadOnly)
@ -105,7 +107,7 @@ public:
UPROPERTY(BlueprintReadOnly)
FString MapName;
UPROPERTY(BlueprintReadOnly)
FString MapLocation;
FString MarkerLocation;
UPROPERTY(BlueprintReadOnly)
FString Summary;
UPROPERTY(BlueprintReadOnly)

View File

@ -29,6 +29,8 @@ public:
UFUNCTION(BlueprintPure)
bool AreMarkersVisible() const { return !this->BugMarkers.IsEmpty(); }
void AddBugMarker(ABugMarkerActor *Marker) { this->BugMarkers.Add(Marker); }
void PrepareSubmissionFormData();
void SubmitForm(const struct FUnrealzillaPostData &PostData);

View File

@ -3,7 +3,6 @@
#pragma once
#include "CoreMinimal.h"
#include "BugzillaJSONStructs.h"
#include "GameFramework/Pawn.h"
#include "BugPlacerPawn.generated.h"

View File

@ -81,7 +81,7 @@ private:
UFUNCTION(BlueprintCallable)
void ShowProcessingOverlayLoading();
UFUNCTION(BlueprintCallable)
void ShowProcessingOverlayMessage(const FString Message, const bool bExitAfterConfirm = false);
void ShowProcessingOverlayMessage(const struct FUnrealzillaErrorData &Error);
UFUNCTION(BlueprintCallable)
void HideProcessingOverlay();

View File

@ -21,6 +21,8 @@ class UNREALZILLA_API UServerAPI : public UObject
GENERATED_BODY()
public:
virtual void Initialize() {}
virtual void ReturnListOfBugs();
virtual void PrepareForm();
virtual void SendFormData(const FUnrealzillaPostData &PostData);
@ -46,7 +48,7 @@ protected:
void CreateError(const EErrorVerb &Verb, const FBugzillaJSONProductResponse &Data);
void CreateError(const EErrorVerb &Verb, const FBugzillaJSONFieldResponse &Data);
void CreateError(const EErrorVerb &Verb, const FBugzillaJSONBugResponse &Data);
void CreateError(const FString &ErrorMessage);
void CreateError(const FString &ErrorMessage, const bool bCancelForm = false);
static const FString URLBuilder(const FString &Server, const FString &Path);
static const FString FormatQueryString(const TMap<FString, FString> &QueryData);

View File

@ -16,7 +16,7 @@ class UNREALZILLA_API UServerBugzillaAPI : public UServerAPI
GENERATED_BODY()
public:
UServerBugzillaAPI();
virtual void Initialize() override;
virtual void ReturnListOfBugs() override;
virtual void PrepareForm() override;

View File

@ -16,20 +16,39 @@ class UNREALZILLA_API UServerJiraAPI : public UServerAPI
GENERATED_BODY()
public:
UServerJiraAPI();
virtual void Initialize() override;
virtual void ReturnListOfBugs() override;
virtual void PrepareForm() override;
virtual void SendFormData(const FUnrealzillaPostData &PostData) override;
private:
void ListOfBoardsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ListOfFieldsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
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 ServerFieldOptionsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void ServerVersionsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool Success);
void CheckIfAllFormResponsesAreIn();
const FString GenerateAuthString() const;
int32 BoardID = -1;
int32 ProjectID = -1;
FString ProjectKey;
FString ProjectAvatarURI;
FString MapNameCustomField;
FString MarkerLocationCustomField;
FString DepartmentCustomField;
FString SeverityCustomField;
FString PlatformCustomField;
FString VersionCustomField;
uint8 FieldListsCompletion = 0;
uint8 FieldListsCompletionMax = 0;
};

View File

@ -36,12 +36,12 @@ 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 = 10000;
// The status to use when filing a new bug. Usually the default is sufficient.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla", meta=(DisplayName="Default Status"))
FString BugzillaDefaultStatus = "UNCONFIRMED";
// The Bugzilla server where bugs will be posted.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Bugzilla", meta=(DisplayName="Server"))
FString BugzillaSubmissionServer;
@ -64,13 +64,43 @@ public:
// 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"))
// The username to use when posting bugs. Should be the user's full email address.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Email"))
FString JiraUsername;
// The API key to use when posting bugs. Using the account password should work here, but it is
// The API key to use when posting bugs. Using the account password might 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;
// Name of the Jira issue type used for bugs. Should simply be "Bug", but if you use something different
// it can be changed here.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Bug Issue Type"))
FString JiraBugIssueType = "Bug";
// Name of the Jira custom field where the map name should be stored. This field is required, and must be
// configured in Jira. The custom field must be a Text Field.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Map Name Field"))
FString JiraMapNameField;
// Name of the Jira custom field where the bug location should be stored. This field is required, and must
// be configured in Jira. The custom field must be a Text Field.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira", meta=(DisplayName="Marker Location Field"))
FString JiraMarkerLocationField;
// Name of the Jira custom field where the department name should be stored. This serves a similar function to
// "Component" in Bugzilla; it's meant to indicate what department in a company should be responsible for fixing
// the bug. The custom field must be a single-choice select list.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira|Optional Fields", meta=(DisplayName="Department Field"))
FString JiraDepartmentField;
// Name of the Jira custom field where the severity should be stored. The custom field must be a single-choice select list.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira|Optional Fields", meta=(DisplayName="Severity Field"))
FString JiraSeverityField;
// Name of the Jira custom field where the hardware and OS used to find the bug should be stored. The custom field
// must be a cascading select list, with the first dropdown being hardware, and the second being the operating system.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira|Optional Fields", meta=(DisplayName="Platform Field"))
FString JiraPlatformField;
// Name of the Jira custom field where the game's version number should be stored. The custom field must be a Version
// select list, and "Releases" must be enabled in your project features and configured with the version numbers you
// wish to track bugs for. Unrealzilla will automatically fill this in with the version number from the project settings,
// and will simply not fill this field in if it can't find a matching version number configured for the project.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Reporting|Jira|Optional Fields", meta=(DisplayName="Version Field"))
FString JiraVersionField;
// Whether to show unresolved bugs when displaying bug report markers.
UPROPERTY(Config, EditDefaultsOnly, BlueprintReadOnly, Category="Markers|Unresolved")