diff --git a/Config/DefaultDTFluxAPI.ini b/Config/DefaultDTFluxAPI.ini new file mode 100644 index 0000000..ed9b8f0 --- /dev/null +++ b/Config/DefaultDTFluxAPI.ini @@ -0,0 +1,3 @@ +[CoreRedirects] ++ClassRedirects=(OldName="/Script/DTFluxAPI.DTHttpServerObject",NewName="/Script/DTFluxAPI.DTFluxHttpServerObject") ++StructRedirects=(OldName="/Script/DTFluxAPI.DTHttpServerParams",NewName="/Script/DTFluxAPI.DTFluxHttpServerParams") \ No newline at end of file diff --git a/DTFluxAPI.uplugin b/DTFluxAPI.uplugin new file mode 100644 index 0000000..a278bb5 --- /dev/null +++ b/DTFluxAPI.uplugin @@ -0,0 +1,30 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "DTFluxAPI", + "Description": "DTFlux API plugin", + "Category": "Other", + "CreatedBy": "Ange-Marie MAURIN", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "DTFluxAPI", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "Avalanche", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/Resources/Icon128.png b/Resources/Icon128.png new file mode 100644 index 0000000..1231d4a Binary files /dev/null and b/Resources/Icon128.png differ diff --git a/Source/DTFluxAPI/DTFluxAPI.Build.cs b/Source/DTFluxAPI/DTFluxAPI.Build.cs new file mode 100644 index 0000000..771917a --- /dev/null +++ b/Source/DTFluxAPI/DTFluxAPI.Build.cs @@ -0,0 +1,36 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class DTFluxAPI : ModuleRules +{ + public DTFluxAPI(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core" + } + ); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "HTTPServer", + "HTTP", + "DeveloperToolSettings", + "DeveloperSettings", + "Json", + "JsonUtilities", + "AvalancheCore", + "AvalancheMedia", + } + ); + } +} diff --git a/Source/DTFluxAPI/Private/DTFluxAPI.cpp b/Source/DTFluxAPI/Private/DTFluxAPI.cpp new file mode 100644 index 0000000..dacdf82 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxAPI.cpp @@ -0,0 +1,22 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "DTFluxAPI.h" +#include "DTFluxAPILog.h" + +#define LOCTEXT_NAMESPACE "FDTFluxAPIModule" +DEFINE_LOG_CATEGORY(LogDTFluxAPI); + +void FDTFluxAPIModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FDTFluxAPIModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FDTFluxAPIModule, DTFluxAPI) \ No newline at end of file diff --git a/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp b/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp new file mode 100644 index 0000000..cb85533 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxModel/DTFluxModel.cpp @@ -0,0 +1,4 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DTFluxModel/DTFluxModel.h" diff --git a/Source/DTFluxAPI/Private/DTFluxProjectSettings/DTFluxProjectSettings.cpp b/Source/DTFluxAPI/Private/DTFluxProjectSettings/DTFluxProjectSettings.cpp new file mode 100644 index 0000000..ada8355 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxProjectSettings/DTFluxProjectSettings.cpp @@ -0,0 +1,73 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DTFluxProjectSettings/DTFluxProjectSettings.h" + +FString UDTFluxProjectSettings::GetAPIPath(const TEnumAsByte RouteType, const FString& Filters) const +{ + FString ApiAccessRoute; + UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Filters in GetApiPath settings\nGot %s"), *Filters); + + if (bAccessIsLocal) + { + // http://localhost/_246158/api/8O8JMI2739JS58R8KRJGJEZUSQXF807O + ApiAccessRoute += RaceResultPort != 80 ? FString::Printf(TEXT("%s:%i/_%s/api/"), *ProxyUrl, RaceResultPort, *APIToken) : + FString::Printf(TEXT("%s/_%s/api/"), *ProxyUrl, *APIToken); + } + else + { + // https://api.raceresult.com/246158/8O8JMI2739JS58R8KRJGJEZUSQXF807O + ApiAccessRoute += FString::Printf(TEXT("https://api.raceresult.com/%s/"), *APIToken); + } + switch(RouteType) + { + case (EDTFluxAPIRoute::Results): + return ApiAccessRoute + LiveStageResultsAccessToken + Filters; + break; + case (EDTFluxAPIRoute::FinalClassification): + return ApiAccessRoute + GeneralClassificationAccessToken + Filters; + break; + + default: + return ApiAccessRoute + StartListAccessToken + Filters; + } +} + +FString UDTFluxProjectSettings::GetAPIPathFiltered(const TEnumAsByte RouteType, + const FSearchFilters& Filters) const +{ + const FString Filter = Filters.GetFilter(); + UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Filters in Project settings\nGot %s"), *Filter); + return GetAPIPath(RouteType, Filter ); +} + +FString UDTFluxProjectSettings::GetProxyPath(const TEnumAsByte RouteType, + const int& InContest, const int& InStage ) const +{ + switch(RouteType) + { + case (EDTFluxProxyRoute::ProxyRankingContest): + return FString::Printf(TEXT("%s:%i%s/ranking/contest/%i/"), *ProxyAddress, ProxyPort, *ProxyRootPath, InContest); + break; + case (EDTFluxProxyRoute::ProxyRankingStage): + return FString::Printf(TEXT("%s:%i%s/ranking/contest/%i/stage?id=%i"), + *ProxyAddress, ProxyPort, *ProxyRootPath, InContest, InStage); + break; + case (EDTFluxProxyRoute::ProxyTeams): + return FString::Printf(TEXT("%s:%i%s/teams/"), *ProxyAddress, ProxyPort, *ProxyRootPath); + break; + default : + return FString::Printf(TEXT("%s:%i%s/race/datas/"), *ProxyAddress, ProxyPort, *ProxyRootPath); + break; + } +} + +const UDTFluxProjectSettings* UDTFluxProjectSettings::GetDTFluxAPIProjectSettings() +{ + return GetDefault(); +} + +UDTFluxProjectSettings::UDTFluxProjectSettings() +{ + SectionName = "DTFlux Settings"; +} diff --git a/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp b/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp new file mode 100644 index 0000000..8dcc7aa --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxSubsystem/DTFluxSubsystem.cpp @@ -0,0 +1,331 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "DTFluxSubsystem/DTFluxSubsystem.h" +#include "DTFluxProjectSettings/DTFluxProjectSettings.h" +#include "DTFluxModel/DTFluxModel.h" +#include "HttpServerModule.h" +#include "HttpRouteHandle.h" +#include "DTFluxAPILog.h" +#include "IHttpRouter.h" +#include "HttpModule.h" +#include "JsonObjectConverter.h" +#include "Interfaces/IHttpResponse.h" + +// TODO: Not implemented +bool UDTFluxSubsystem::OnRequest(const FHttpServerRequest& Request) +{ + return true; +} +// TODO: Not implemented +void UDTFluxSubsystem::HandleRequest(const FString& Route,const FHttpServerRequest& Request, FHttpResultCallback OnComplete) +{ + // creating payload string + FString ReqPayload; + if (Request.Body.Num() > 0) + { + const std::string RawBody((char*)Request.Body.GetData(), Request.Body.Num()); + ReqPayload = UTF8_TO_TCHAR(RawBody.c_str()); + } + TSharedPtr JsonPayload; + if(!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(ReqPayload), JsonPayload)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("unable to parse JSON Payload\n%s"), *ReqPayload); + } + //Checking path + + UE_LOG(LogDTFluxAPI,Log, TEXT("Request Received for Route %s"), *Route); + + // EventStart + if(Route == TEXT("/event/start")) + { + FDTFluxStartStagePayload StartStagePayload; + FJsonObjectConverter::JsonObjectToUStruct(JsonPayload.ToSharedRef(), &StartStagePayload, 0, 0); + OnEventStartReceived.Broadcast(StartStagePayload); + } + else if(Route == TEXT("/team/create")) + { + // /team/create route + } + else if(Route == TEXT("/team/update")) + { + // /team/create route + } else if(Route == TEXT("/team/create")) + { + // /team/create route + } + // Default Route + else + { + + } + + //Preparing Response Header + TUniquePtr Response = CreateHttpServerResponse(); + + // Adding Response Body + FDTFluxResponseBody RespBody; + RespBody.Success = TEXT("OK"); + std::string RespBody_c; + RespBody_c = TCHAR_TO_UTF8(*RespBody.Deserialize()); + Response->Body.Append((const uint8*)RespBody_c.c_str(), RespBody_c.length()); + // Return the response + OnComplete(MoveTemp(Response)); + +} + +void UDTFluxSubsystem::OnUpdateStartList(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +{ + if (!bWasSuccessful || !Response.IsValid()) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("RaceResult Request failed")); + return; + } + // Tricks because Payload root is an array + const FString ModifiedData = FString::Printf(TEXT("{\"Participants\":%s}"), *Response->GetContentAsString()); + TSharedPtr Payload; + if(!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(ModifiedData), Payload)) + { + UE_LOG(LogDTFluxAPI, Error, TEXT("unable to parse JSON Payload****")); + }else + { + FDTFluxStartListPayload StartList; + if (FJsonObjectConverter::JsonObjectToUStruct(Payload.ToSharedRef(), &StartList, 0, 0)) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Success Reading")) + + for(const auto& Participant : StartList.Participants) + { + // Creating a new Contest + if(!Contests.Contests.Contains(Participant.ContestName)) + { + FDTFluxContest NewContest; + NewContest.ContestID = Participant.ContestID; + NewContest.ContestName = Participant.ContestName; + Contests.Contests.Add(Participant.ContestName, NewContest); + } + FDTFluxContest* CurrentContest = Contests.Contests.Find(Participant.ContestName); + + FDTFluxTeam Team; + Team.Bib = Participant.Bib; + FDTFluxParticipant ContestParticipant; + ContestParticipant.FirstName = Participant.FirstName; + ContestParticipant.LastName = Participant.LastName; + ContestParticipant.Gender = Participant.Gender; + ContestParticipant.Club = Participant.Club; + ContestParticipant.Category = Participant.Category; + + Team.Participants.Add(ContestParticipant); + // if we have LastName2 it is a two personne Team + if(Participant.LastName2 != "") + { + FDTFluxParticipant ContestParticipant2; + ContestParticipant2.FirstName = Participant.FirstName2; + ContestParticipant2.LastName = Participant.LastName2; + ContestParticipant2.Gender = Participant.Gender2; + ContestParticipant2.Club = Participant.Club2; + ContestParticipant2.Category = Participant.Category; + Team.Participants.Add(ContestParticipant); + Team.TeamName = Participant.TeamName; + UE_LOG(LogDTFluxAPI, Log, TEXT("Participant is a team : TeamName \"%s\" "), *Team.TeamName); + } + else + { + Team.TeamName = Participant.FirstName + TEXT(" ") + Participant.LastName.ToUpper(); + } + // check if Participant already exists + + if(!CurrentContest->TeamAlreadyExist(Team)) + { + // Add it to the TeamList + CurrentContest->AddTeam(Team); + UE_LOG(LogDTFluxAPI, Log, TEXT("Adding Team \"%s\" "), *Team.TeamName); + } + + } + UE_LOG(LogDTFluxAPI, Log, TEXT("Contest has now %i elements"), Contests.Contests.Num()); + + + } + else + { + UE_LOG(LogDTFluxAPI, Error, TEXT("Error Deserializing Payload \n*%s*\n"), *ModifiedData); + + } + } + for(const auto& Element : Contests.Contests) + { + UE_LOG(LogDTFluxAPI, Log, TEXT("Contest %s"), *Element.Key); + FDTFluxContest Contest = Element.Value; + UE_LOG(LogDTFluxAPI, Log, TEXT("Number of participants in this contest %i"), Contest.TeamParticipants.Num()); + + } + +} + +TUniquePtr UDTFluxSubsystem::CreateHttpServerResponse() const +{ + // Create a Response to be returned by the server + TUniquePtr Response = MakeUnique(); + Response->Code = EHttpServerResponseCodes::Ok; + // Response Header + Response->Headers.Add(TEXT("Content-Type"), { TEXT("application/json;charset=utf-8") }); + Response->Headers.Add(TEXT("Access-Control-Allow-Origin"), { TEXT("*") }); + Response->Headers.Add(TEXT("Access-Control-Allow-Methods"), { TEXT("GET,POST,PUT,PATCH,DELETE,OPTIONS") }); + Response->Headers.Add(TEXT("Access-Control-Allow-Headers"), { TEXT("Origin,X-Requested-With,Content-Type,Accept") }); + Response->Headers.Add(TEXT("Access-Control-Max-Age"), { TEXT("600") }); + Response->Headers.Add(TEXT("Access-Control-Allow-Credentials"), { TEXT("true") }); + Response->Headers.Add(TEXT("Server"), { TEXT("UE 5.4.1 EMBBEDED") }); + + return Response; +} + +void UDTFluxSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + mSettings = UDTFluxProjectSettings::GetDTFluxAPIProjectSettings(); + if(mSettings) + { + // Setting up request + HttpRequest = &FHttpModule::Get(); + HttpRouter = FHttpServerModule::Get().GetHttpRouter(mSettings->InPort); + if(!HttpRouter) + { + UE_LOG(LogDTFluxAPI, Type::Error, TEXT("Invalid Http Router for port : %i"), mSettings->InPort) + } + // Setting Up Routes + for(const TArray Routes = mSettings->Endpoints; const FString& Route : Routes) + { + const FHttpRequestHandler OptionRequestHandler = FHttpRequestHandler::CreateLambda([this](const FHttpServerRequest &Request, const FHttpResultCallback& OnComplete) + { + OnComplete(CreateHttpServerResponse()); + return true; + }); + FHttpRouteHandle OptionRouteHandle = HttpRouter->BindRoute(Route, EHttpServerRequestVerbs::VERB_OPTIONS, OptionRequestHandler); + HttpMountedMap.Add(Route + TEXT("HTTPOption"), OptionRouteHandle); + + const FHttpRequestHandler RequestHandler = FHttpRequestHandler::CreateLambda([this, Route](const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete) + { + // Processing Request to Raw OnRequestReceived firing + FDTFluxHttpServerHeaders Headers; + for(const auto &Header: Request.Headers) + { + Headers.Headers.Add(Header.Key, FString::Join(Header.Value, TEXT(","))); + } + FDTFluxHttpServerParams Params; + Params.Params = Request.QueryParams; + + FDTFluxHttpServerBody Payload; + Payload.ReqBody = Request.Body; + + // Raw broadcasting Received event + OnRequestReceived.Broadcast(Headers, Params, Payload); + HandleRequest(Route, Request, OnComplete); + return true; + }); + // Binding Routes + FHttpRouteHandle RouteHandle = HttpRouter->BindRoute(Route, EHttpServerRequestVerbs::VERB_POST, RequestHandler); + HttpMountedMap.Add(Route, RouteHandle); + } + + } + +} + +void UDTFluxSubsystem::Deinitialize() +{ + StopServer(); + UE_LOG(LogDTFluxAPI, Log, TEXT("Route Num %i"), HttpMountedMap.Num()); + for( auto const& Route: HttpMountedMap) + { + HttpRouter->UnbindRoute(Route.Value); + } + HttpMountedMap.Empty(); + HttpRouter.Reset(); + Super::Deinitialize(); +} + +TArray UDTFluxSubsystem::GetMountedRoutes() const +{ + TArray Mounted = TArray(); + for(const auto Route : HttpMountedMap) + { + Mounted.Add(Route.Key); + } + return Mounted; +} + +void UDTFluxSubsystem::StartServer() +{ + if(bIsListening){return ;} + FHttpServerModule::Get().StartAllListeners(); + bIsListening = true; + OnServerListening.Broadcast(); +} + +TArray UDTFluxSubsystem::GetParticipantsByContestId(const int ContestId) +{ + if(Contests.Contests.Num() != 0) + { + FString ContestName = Contests.GetContestName(ContestId); + UE_LOG(LogDTFluxAPI, Log, TEXT("Getting Participants for Contest %s"), *ContestName); + return GetParticipantsByContestName(ContestName); + } + else + { + UE_LOG(LogDTFluxAPI, Error, TEXT("No Contest Yet !!!!")); + TArray EmptyTeam; + return EmptyTeam; + } + +} + +TArray UDTFluxSubsystem::GetParticipantsByContestName(const FString ContestName) +{ + if(Contests.Contests.Num() != 0) + { + return Contests.Contests[ContestName].TeamParticipants; + + } + else + { + UE_LOG(LogDTFluxAPI, Error, TEXT("No Contest Yet !!!!")); + TArray EmptyTeam; + return EmptyTeam; + } +} + +FString UDTFluxSubsystem::GetContestName(const int ContestId) +{ + if(Contests.GetContestName(ContestId) != "") + { + return Contests.GetContestName(ContestId); + } + return TEXT(""); +} + +void UDTFluxSubsystem::UpdateStartList() +{ + const TSharedRef Req = HttpRequest->CreateRequest(); + Req->SetVerb("GET"); + Req->SetURL(mSettings->GetAPIPath(EDTFluxAPIRoute::Starters)); + Req->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + Req->OnProcessRequestComplete().BindUObject(this, &UDTFluxSubsystem::OnUpdateStartList); + + Req->ProcessRequest(); +} + +void UDTFluxSubsystem::UpdateClassification(const int& ContestId, const int& StageId) +{ +} + +TArray UDTFluxSubsystem::GetClassification(const int& ContestId, const int& StageId) +{ + return TArray(); +} + +void UDTFluxSubsystem::StopServer() +{ + FHttpServerModule::Get().StopAllListeners(); + bIsListening = false; + OnServerStopped.Broadcast(); +} diff --git a/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxHttpServerStruct.cpp b/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxHttpServerStruct.cpp new file mode 100644 index 0000000..4bcaf86 --- /dev/null +++ b/Source/DTFluxAPI/Private/DTFluxUtils/DTFluxHttpServerStruct.cpp @@ -0,0 +1,38 @@ +// Copyright 2023 Dexter.Wan. All Rights Reserved. +// EMail: 45141961@qq.com + +#include "DTFluxUtils/DTFluxHttpServerStruct.h" + +void UDTFluxHttpServerBPFn::BreakParams(const FDTFluxHttpServerParams& HttpServerParams, TMap& Params) +{ + Params = HttpServerParams.Params; +} + +void UDTFluxHttpServerBPFn::FindParam(const FDTFluxHttpServerParams& HttpServerParams, const FString& Key, FString& Param) +{ + Param.Empty(); + if ( const FString * pParam = HttpServerParams.Params.Find(Key) ) + { + Param = *pParam; + } +} + +void UDTFluxHttpServerBPFn::BreakHeaders(const FDTFluxHttpServerHeaders& HttpServerHeaders, TMap& Headers) +{ + Headers = HttpServerHeaders.Headers; +} + +void UDTFluxHttpServerBPFn::BreakBody(const FDTFluxHttpServerBody& HttpBody, TArray RawBody) +{ + RawBody = HttpBody.ReqBody; +} + +void UDTFluxHttpServerBPFn::FindHeader(const FDTFluxHttpServerHeaders& HttpServerHeaders, const FString& Key, FString& Header) +{ + Header.Empty(); + if ( const FString * pHeader = HttpServerHeaders.Headers.Find(Key) ) + { + Header = *pHeader; + } +} + diff --git a/Source/DTFluxAPI/Public/DTFluxAPI.h b/Source/DTFluxAPI/Public/DTFluxAPI.h new file mode 100644 index 0000000..a805861 --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxAPI.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FDTFluxAPIModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Source/DTFluxAPI/Public/DTFluxAPILog.h b/Source/DTFluxAPI/Public/DTFluxAPILog.h new file mode 100644 index 0000000..30d3703 --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxAPILog.h @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. +#pragma once + +#include "CoreMinimal.h" + +DECLARE_LOG_CATEGORY_EXTERN(LogDTFluxAPI, Log, All); \ No newline at end of file diff --git a/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h b/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h new file mode 100644 index 0000000..3183ddd --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxModel/DTFluxModel.h @@ -0,0 +1,423 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "DTFluxModel.generated.h" + + +// Forward declarations +struct FDTFluxTeam; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFLuxStartStageData +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Type; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Contest; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Stage; + + // Maybe this one can be made DATETIME + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString RealStartTime; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxStartStagePayload +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Description; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Trigger; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Type; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Datas; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxStartListItemPayload +{ + GENERATED_BODY() + + // "Report": "Start List", + // "ContestID": 0, + // "ContestName": "", + // "Bib": 1039, + // "Firstname": "Mystère", + // "Lastname": "Mystère", + // "Gender": "", + // "Club": "", + // "Firstname2": "", + // "Lastname2": "", + // "Gender2": "", + // "Club2": "", + // "TeamName": "", + // "Category": "", + // "Elite": false + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int ContestID; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString ContestName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Bib; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString FirstName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString LastName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Gender; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Club; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString FirstName2; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString LastName2; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Gender2; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Club2; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString TeamName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Category; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + bool Elite; +}; + + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxStartListPayload +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Participants; + +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxParticipant +{ + GENERATED_BODY() + +public: + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString FirstName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString LastName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Club; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Gender; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Category; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxTeam +{ + GENERATED_BODY() + +public: + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Participants; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString TeamName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Bib; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FDTFluxContest +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int ContestID; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString ContestName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray TeamParticipants; + + bool AddTeam(FDTFluxTeam Team) + { + TeamParticipants.Add(Team); + return true; + } + bool TeamAlreadyExist(FDTFluxTeam Team) + { + for(const auto& MyTeam : TeamParticipants) + { + // Compare Bib + if(MyTeam.Bib == Team.Bib ) + { + return true; + } + } + return false; + } +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct DTFLUXAPI_API FDTFluxContestList +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TMapContests; + + int GetContestId(const FString& ContestName) const + { + const FDTFluxContest* Contest = Contests.Find(ContestName); + if(Contest) + { + return Contest->ContestID; + } + return -1; + } + FString GetContestName(const int& ContestId) const + { + for (const auto& Contest : Contests) + { + if (Contest.Value.ContestID == ContestId) + { + return Contest.Key; + } + } + return FString(); + } +}; + + +UCLASS(Blueprintable, Category="DTFlux|Model") +class DTFLUXAPI_API UDTFluxContestBPFn : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintPure, Category = "DT Http Server|Params") + static int GetContestId(const FDTFluxContestList& ContestList, const FString& ContestName) + { + return ContestList.GetContestId(ContestName); + } + UFUNCTION(BlueprintPure, Category = "DT Http Server|Params") + static FString GetContestName(const FDTFluxContestList& ContestList, const int& ContestId) + { + return ContestList.GetContestName(ContestId); + } +}; + +// TeamListItem Response from proxy Containing Team definition +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FResponseTeamListItem +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Contest; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Bib; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString FirstName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString LastName; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Gender; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + bool Elite; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Club; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Team; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Category; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Status; +}; +// TeamList Response from proxy Containing List of Team +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseTeamList +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Datas; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseStageListItem +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Id; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Name; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString StartTime; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString EndTime; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseSplitsListItem +{ + GENERATED_BODY() + + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Id; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Name; + +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseRaceListItem +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Id; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Name; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Date; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Stages; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Splits; + +}; + + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseContestRankingListItem +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Bib; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Rank; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Time; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Gap; + +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseContestRankingList +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int ContestId; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Datas; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseStageRankingListItem +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Bib; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int Rank; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Time; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Gap; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString TimeSwim; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString TimeTransition; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString TimeRun; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString TimeStart; + +}; + +USTRUCT(BlueprintType, Category="DTFlux|Model") +struct FProxyResponseStageRankingList +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int ContestId; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + int StageId; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + TArray Datas; + +}; \ No newline at end of file diff --git a/Source/DTFluxAPI/Public/DTFluxProjectSettings/DTFluxProjectSettings.h b/Source/DTFluxAPI/Public/DTFluxProjectSettings/DTFluxProjectSettings.h new file mode 100644 index 0000000..0c40be1 --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxProjectSettings/DTFluxProjectSettings.h @@ -0,0 +1,154 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DeveloperSettings.h" +#include "DTFluxAPILog.h" +#include "DTFluxProjectSettings.generated.h" + +UENUM() +enum EDTFluxAPIRoute: uint8 +{ + Root UMETA(DisplayName="API Root"), + Results UMETA(DisplayName="Results Route"), + Starters UMETA(DisplayName="Starters List Route"), + FinalClassification UMETA(DisplayName="Final Classification Route"), + +}; + + +UENUM() +enum EDTFluxProxyRoute : uint8 +{ + ProxyRaceData UMETA(DisplayName="Proxy Race Data"), + ProxyRankingContest UMETA(DisplayName="Proxy Ranking Contest Data"), + ProxyRankingStage UMETA(DisplayName="Proxy Ranking Stage Data"), + ProxyTeams UMETA(DisplayName="Proxy Teams Data"), +}; + +USTRUCT(BlueprintType) +struct FSearchFilters +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Config", EditAnywhere) + int ContestId = 0; + + UPROPERTY(Blueprintable, Category="DTFlux|Config", EditAnywhere) + int StageId = 0; + + UPROPERTY(Blueprintable, Category="DTFlux|Config", EditAnywhere) + int GenderId = 0; + + FString GetFilter() const + { + if(ContestId == 0 && StageId == 0 && GenderId == 0) + { + UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Gender Test -> %i"), GenderId); + return FString(); + } + TArray Filters; + Filters.Add(FString("?")); + if (ContestId > 0) + { + Filters.Add(FString::Printf(TEXT("Contest=%i"), ContestId) ); + } + if (StageId > 0) + { + Filters.Add(FString::Printf(TEXT("SelectorResult=%i"), StageId) ); + } + if (GenderId > 0) + { + switch(GenderId) + { + case (2): + // default is male + UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Gender Female -> %i"), GenderId); + + Filters.Add(FString(TEXT("filter=[Gender]=\"f\"") ) ); + break; + default: + // default is male + UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Gender male -> %i"), GenderId); + Filters.Add(FString(TEXT("filter=[Gender]=\"m\"") ) ); + break; + } + } + for(const auto& Element : Filters) + { + UE_LOG(LogDTFluxAPI, Type::Log, TEXT("Element : %s"), *Element); + + } + FString FinalFilters = FString::Join(Filters, TEXT("&")); + FinalFilters.RemoveAt(1); + return FinalFilters; + }; +}; + +/** + * + */ +UCLASS(Blueprintable, Config=Engine, DefaultConfig, meta=(DisplayName="DTFlux Project Settings")) +class DTFLUXAPI_API UDTFluxProjectSettings : public UDeveloperSettings +{ + GENERATED_BODY() + +public: + + + UPROPERTY(Category="DTFlux|Server Config", Config, EditAnywhere, BlueprintReadOnly) + int InPort = 8080; + + UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly) + int RaceResultPort = 80; + + UPROPERTY(Category="DTFlux|Server Config", Config, EditAnywhere, BlueprintReadOnly) + TArray Endpoints; + + UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly) + FString ProxyUrl = "http://localhost"; + + UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly) + FString APIToken; + + UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly) + bool bAccessIsLocal = true; + + UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly) + FString StartListAccessToken; + + UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly) + FString GeneralClassificationAccessToken; + + UPROPERTY(Category="DTFlux|Config|Race Result API", Config, EditAnywhere, BlueprintReadOnly) + FString LiveStageResultsAccessToken ; + + UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly) + FString ProxyAddress = "http://localhost"; + + UPROPERTY(Category="DTFlux|Config|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly) + FString ProxyRootPath = "/endpoints"; + + UPROPERTY(Category="DTFlux|Chrono Proxy", Config, EditAnywhere, BlueprintReadOnly) + int ProxyPort = 8000; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Config") + FString GetAPIPath(const TEnumAsByte RouteType, const FString& Filters = TEXT("") ) const; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Config") + FString GetAPIPathFiltered(const TEnumAsByte RouteType, const FSearchFilters& Filters ) const; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Config") + FString GetProxyPath(const TEnumAsByte RouteType, const int& InContest = -1, const int& InStage = -1) const; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Config") + static const UDTFluxProjectSettings* GetDTFluxAPIProjectSettings(); + +protected: + + +private: + UDTFluxProjectSettings(); +}; + \ No newline at end of file diff --git a/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h b/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h new file mode 100644 index 0000000..2e3e920 --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxSubsystem/DTFluxSubsystem.h @@ -0,0 +1,153 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Runtime/Engine/Public/Subsystems/EngineSubsystem.h" +#include "HttpServerRequest.h" +#include "HttpResultCallback.h" +#include "HttpRouteHandle.h" +#include + +#include "DTFluxUtils/DTFluxHttpServerStruct.h" +#include "DTFluxAPILog.h" +#include "DTFluxModel/DTFluxModel.h" + +#include "DTFluxSubsystem.generated.h" + + +class UDTFluxProjectSettings; +class IHttpRouter; + +class IHttpRequest; +class IHttpResponse; + +class FHttpModule; +typedef TSharedPtr FHttpRequestPtr; +typedef TSharedPtr FHttpResponsePtr; + +UENUM(BlueprintType, Category="DTFlux|Server") +enum EDTFluxResponseErrorCode +{ + Unknown_Error UMETA(DisplayName="Unknown Error"), + InvalidBody_Error UMETA(DisplayName="Invalid Body"), + InvalidRequest_Error UMETA(DisplayName="Invalid Request"), + Internal_Error UMETA(DisplayName="Internal Server Error") +}; + + +USTRUCT(BlueprintType, Category="DTFlux|Server") +struct FDTFluxResponseBody +{ + GENERATED_BODY() + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Error; + + UPROPERTY(Blueprintable, Category="DTFlux|Model", BlueprintReadOnly) + FString Success; + +FString Deserialize() + { + FString JSONObject; + JSONObject += TEXT("{"); + JSONObject += TEXT("\"error\":\""); + JSONObject += Error.IsEmpty() ? TEXT("") : Error; + JSONObject += TEXT("\",\"success\":\""); + JSONObject += Success.IsEmpty() ? TEXT("") : Success; + JSONObject += TEXT("\"}"); + UE_LOG(LogDTFluxAPI, Log, TEXT("JSONObject : %s"), *JSONObject); + return JSONObject; + } +}; + + + + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FDTFluxOnRequestReceived, FDTFluxHttpServerHeaders, HttpServerHeaders, + FDTFluxHttpServerParams, HttpServerParams, FDTFluxHttpServerBody, HttpRequestBody); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDTFluxOnServerListening); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDTFluxOnServerStopped); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDTFluxOnEventStartReceived, FDTFluxStartStagePayload, Payload); + +/** + * DTFlux API Subsystem + * + * This Subsystem Mount HTTP routes to be listened and an HTTP poller to retrieve basic information. + */ +UCLASS() +class DTFLUXAPI_API UDTFluxSubsystem : public UEngineSubsystem +{ + GENERATED_BODY() + +private: + + bool bIsListening = false; + const UDTFluxProjectSettings* mSettings; + + bool OnRequest(const FHttpServerRequest& Request); + + void HandleRequest(const FString& Route, const FHttpServerRequest& Request, FHttpResultCallback OnComplete); + // // StartList Payload; + // FDTFluxStartListPayload StartList; + // Contest storage + FDTFluxContestList Contests; + + void OnUpdateStartList(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + +protected: + + TMap HttpMountedMap; + TSharedPtr HttpRouter; + // Stop The Server + void StopServer(); + // Create the server response + TUniquePtr CreateHttpServerResponse() const; + FHttpModule* HttpRequest; + + +public: + /** Implement this for initialization of instances of the system */ + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + + /** Implement this for deinitialization of instances of the system */ + virtual void Deinitialize() override; + + UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events") + FDTFluxOnRequestReceived OnRequestReceived; + + UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events") + FDTFluxOnServerListening OnServerListening; + + UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events") + FDTFluxOnServerStopped OnServerStopped; + + UPROPERTY(BlueprintAssignable, Category="DTFlux|Subsystem|Events") + FDTFluxOnEventStartReceived OnEventStartReceived; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem") + TArray GetMountedRoutes() const; + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem") + void StartServer(); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Events") + TArray GetParticipantsByContestId(const int ContestId ); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Events") + TArray GetParticipantsByContestName(const FString ContestName); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Events") + FString GetContestName(const int ContestId); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Race Result Call") + void UpdateStartList(); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Race Result Call") + void UpdateClassification(const int& ContestId, const int& StageId = -1); + + UFUNCTION(BlueprintCallable, Category="DTFlux|Subsystem|Race Result Call") + TArray GetClassification(const int& ContestId, const int& StageId = -1); + +}; + diff --git a/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxHttpServerStruct.h b/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxHttpServerStruct.h new file mode 100644 index 0000000..d21770c --- /dev/null +++ b/Source/DTFluxAPI/Public/DTFluxUtils/DTFluxHttpServerStruct.h @@ -0,0 +1,68 @@ +// Copyright 2023 Dexter.Wan. All Rights Reserved. +// EMail: 45141961@qq.com + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "DTFluxHttpServerStruct.generated.h" + +UENUM(BlueprintType) +enum EDTFluxHttpServerVerbs : uint8 +{ + HTTP_GET, + HTTP_POST, + HTTP_PUT, + HTTP_PATCH, + HTTP_DELETE, +}; + +// USTRUCT(BlueprintType, meta=(DisplayName="DT Http Server Headers", HasNativeBreak = "DTHttpServer.DTHttpServerBPLib.BreakHeaders")) +USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakHeaders")) +struct FDTFluxHttpServerHeaders +{ + GENERATED_BODY() + TMap Headers; +}; + +USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakBody")) +struct FDTFluxHttpServerBody +{ + GENERATED_BODY() + TArray ReqBody; +}; + +// USTRUCT(BlueprintType, meta=(DisplayName="DTFlux Server Params", HasNativeBreak = "DTHttpServer.DTHttpServerBPLib.BreakParams")) +USTRUCT(BlueprintType, Category="DTFlux|Server", meta=(HasNativeBreak = "DTFluxAPI.DTFluxHttpServerBPFn.BreakParams")) +struct FDTFluxHttpServerParams +{ + GENERATED_BODY() + TMap Params; +}; + +UCLASS(NotBlueprintable, NotBlueprintType) +class DTFLUXAPI_API UDTFluxHttpServerBPFn : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + // Break DTFlux Http Server Params + UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Params"), Category = "DT Http Server|Params") + static void BreakParams(const FDTFluxHttpServerParams& HttpServerParams, TMap& Params); + + // Find DTFlux Http Server Params + UFUNCTION(BlueprintPure, meta = (DisplayName="Find Http Server Params"), Category = "DT Http Server|Params") + static void FindParam(const FDTFluxHttpServerParams& HttpServerParams, const FString & Key, FString & Param ); + + // Break DTFlux Http Server Headers + UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Headers"), Category = "DT Http Server|Headers") + static void BreakHeaders(const FDTFluxHttpServerHeaders& HttpServerHeaders, TMap& Headers); + + // Break DTFlux Request Body + UFUNCTION(BlueprintPure, meta = (DisplayName="Break Http Server Headers"), Category = "DT Http Server|Headers") + static void BreakBody(const FDTFluxHttpServerBody& HttpBody, TArray RawBody); + + // Find DTFlux Http Server Headers + UFUNCTION(BlueprintPure, meta = (DisplayName="Find Http Server Headers"), Category = "DT Http Server|Headers") + static void FindHeader(const FDTFluxHttpServerHeaders& HttpServerHeaders, const FString & Key, FString & Header ); +};