Compare commits
23 Commits
sending-support
...
2.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
| b18e7a4149 | |||
| 41684060c0 | |||
| 3f612bfc9d | |||
| 3d6f16d52b | |||
| 8e8beb1baf | |||
| 43a84bd7f6 | |||
| c908bd7152 | |||
| 2857e87860 | |||
| 8159860223 | |||
| 2431b33726 | |||
| 8b70cbba64 | |||
| db2ba86e6d | |||
| 618d480acf | |||
| 26d5afc2c0 | |||
| dc0495dd24 | |||
| a75fb3a0c7 | |||
| 434c9a7b96 | |||
| aa400d19ee | |||
| 5f9eb61228 | |||
| 63bba7063c | |||
| da962e28f0 | |||
| 41a0d79229 | |||
| 980fe124b2 |
@@ -0,0 +1,94 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
aws-s3: circleci/aws-s3@2.0.0
|
||||
|
||||
commands:
|
||||
zip-source:
|
||||
parameters:
|
||||
target:
|
||||
type: string
|
||||
outputdir:
|
||||
type: string
|
||||
steps:
|
||||
- run:
|
||||
name: "Create Archive"
|
||||
command: |
|
||||
\cp -r ".pluginconfig/<<parameters.target>>/." "./"
|
||||
mkdir -p "<<parameters.outputdir>>/"
|
||||
stashName=`git stash create`;
|
||||
git archive -o "<<parameters.outputdir>>/speckle-unreal-UE<<parameters.target>>.zip" --worktree-attributes --prefix="speckle-unreal/" $stashName
|
||||
|
||||
jobs:
|
||||
generate-source-zip:
|
||||
docker:
|
||||
- image: cimg/base:stable
|
||||
parameters:
|
||||
target:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- zip-source:
|
||||
target: <<parameters.target>>
|
||||
outputdir: "output/unreal/<<parameters.target>>"
|
||||
- store_artifacts:
|
||||
path: "output/unreal/<<parameters.target>>"
|
||||
destination: "unreal-marketplace/<<parameters.target>>"
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
- "output/unreal/<<parameters.target>>"
|
||||
|
||||
marketplace-deploy:
|
||||
docker:
|
||||
- image: cimg/base:stable
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- aws-s3/copy:
|
||||
arguments: "--recursive --endpoint=https://$SPACES_REGION.digitaloceanspaces.com --acl public-read"
|
||||
aws-access-key-id: SPACES_KEY
|
||||
aws-region: SPACES_REGION
|
||||
aws-secret-access-key: SPACES_SECRET
|
||||
from: '"output"'
|
||||
to: s3://speckle-releases/installers/
|
||||
|
||||
workflows:
|
||||
marketplace-publish:
|
||||
jobs:
|
||||
- generate-source-zip:
|
||||
name: "Generate Source Archive UE4.26"
|
||||
target: "4.26"
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?(\/all)?$/
|
||||
- generate-source-zip:
|
||||
name: "Generate Source Archive UE4.27"
|
||||
target: "4.27"
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?(\/all)?$/
|
||||
- generate-source-zip:
|
||||
name: "Generate Source Archive UE5.0"
|
||||
target: "5.0"
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?(\/all)?$/
|
||||
- hold:
|
||||
type: approval
|
||||
requires:
|
||||
- "Generate Source Archive UE4.26"
|
||||
- "Generate Source Archive UE4.27"
|
||||
- "Generate Source Archive UE5.0"
|
||||
- marketplace-deploy:
|
||||
name: "Deploy to marketplace"
|
||||
requires:
|
||||
- hold
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.gitattributes export-ignore
|
||||
.github/* export-ignore
|
||||
.circleci/* export-ignore
|
||||
.pluginconfig/* export-ignore
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "2.6.0",
|
||||
"FriendlyName": "Speckle Unreal",
|
||||
"Description": "Speckle is an open source data platform for Architecture, Engineering, and Construction. It has been open sourced under the MIT license, is customizable, and available in the cloud or via a self-hosted server. It allows users to exchange data between various AEC modelling and content creation platforms.",
|
||||
"Category": "AEC",
|
||||
"CreatedBy": "Speckle",
|
||||
"CreatedByURL": "https://speckle.systems/",
|
||||
"DocsURL": "https://github.com/specklesystems/speckle-unreal",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/98770ce9d4f143de8dd7882a707a6f81",
|
||||
"SupportURL": "https://speckle.community/",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"EngineVersion": "4.26",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "SpeckleUnreal",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
},
|
||||
{
|
||||
"Name": "SpeckleUnrealEditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "ProceduralMeshComponent",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "LidarPointCloud",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "2.6.0",
|
||||
"FriendlyName": "Speckle Unreal",
|
||||
"Description": "Speckle is an open source data platform for Architecture, Engineering, and Construction. It has been open sourced under the MIT license, is customizable, and available in the cloud or via a self-hosted server. It allows users to exchange data between various AEC modelling and content creation platforms.",
|
||||
"Category": "AEC",
|
||||
"CreatedBy": "Speckle",
|
||||
"CreatedByURL": "https://speckle.systems/",
|
||||
"DocsURL": "https://github.com/specklesystems/speckle-unreal",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/98770ce9d4f143de8dd7882a707a6f81",
|
||||
"SupportURL": "https://speckle.community/",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"EngineVersion": "4.27",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "SpeckleUnreal",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
},
|
||||
{
|
||||
"Name": "SpeckleUnrealEditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "ProceduralMeshComponent",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "LidarPointCloud",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "2.6.0",
|
||||
"FriendlyName": "Speckle Unreal",
|
||||
"Description": "Speckle is an open source data platform for Architecture, Engineering, and Construction. It has been open sourced under the MIT license, is customizable, and available in the cloud or via a self-hosted server. It allows users to exchange data between various AEC modelling and content creation platforms.",
|
||||
"Category": "AEC",
|
||||
"CreatedBy": "Speckle",
|
||||
"CreatedByURL": "https://speckle.systems/",
|
||||
"DocsURL": "https://github.com/specklesystems/speckle-unreal",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/98770ce9d4f143de8dd7882a707a6f81",
|
||||
"SupportURL": "https://speckle.community/",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"EngineVersion": "5.0",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "SpeckleUnreal",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
},
|
||||
{
|
||||
"Name": "SpeckleUnrealEditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "ProceduralMeshComponent",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "LidarPointCloud",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,192 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "API/ClientAPI.h"
|
||||
|
||||
#include "HttpModule.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "Interfaces/IHttpResponse.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "Mixpanel.h"
|
||||
|
||||
void FClientAPI::MakeGraphQLRequest(const FString& ServerUrl, const FString& AuthToken,
|
||||
const FString& ResponsePropertyName,
|
||||
const FString& PostPayload, const FString& RequestLogName,
|
||||
const FAPIResponceDelegate OnCompleteAction, const FErrorDelegate OnErrorAction)
|
||||
{
|
||||
|
||||
auto OnError = [=](const FString& Message) mutable
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("%s: failed - %s"), *RequestLogName , *Message);
|
||||
ensureAlwaysMsgf(OnErrorAction.ExecuteIfBound(Message), TEXT("%s: Unhandled error - %s"), *RequestLogName , *Message);
|
||||
};
|
||||
|
||||
auto ResponseHandler = [=](FHttpRequestPtr, FHttpResponsePtr Response, bool bWasSuccessful) mutable
|
||||
{
|
||||
if(CheckRequestFailed(bWasSuccessful, Response, RequestLogName, OnError)) return;
|
||||
|
||||
TSharedPtr<FJsonObject> Obj;
|
||||
if(!GetResponseAsJSON(Response, RequestLogName, Obj, OnError)) return;
|
||||
|
||||
const TSharedPtr<FJsonObject>* DataObjectPtr;
|
||||
if(!Obj->TryGetObjectField("data", DataObjectPtr))
|
||||
{
|
||||
OnError(TEXT("%s responce was invalid, expected to find a \"data\" property in response"));
|
||||
return;
|
||||
}
|
||||
const TSharedPtr<FJsonObject> DataObject = *DataObjectPtr;
|
||||
|
||||
const TSharedPtr<FJsonObject>* RequestedJson;
|
||||
if(!DataObject->TryGetObjectField(ResponsePropertyName, RequestedJson))
|
||||
{
|
||||
//Requested property has a null or non-object value
|
||||
if(DataObject->HasField(ResponsePropertyName))
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("%s: Response data contained the requested property \"%s\", but the value was null was or not an object."),
|
||||
*RequestLogName, *ResponsePropertyName);
|
||||
OnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
//Requested property was missing
|
||||
TArray<FString> Options;
|
||||
Options.Reserve(DataObject->Values.Num());
|
||||
for(const auto& Properties : DataObject->Values)
|
||||
{
|
||||
Options.Add(FString::Printf(TEXT("\"%s\" "), *Properties.Key));
|
||||
}
|
||||
|
||||
FString Message = FString::Printf(TEXT("%s: Could not find the requested property name \"%s\" in responce data.\n Got { %s} instead."),
|
||||
*RequestLogName, *ResponsePropertyName, *FString::Join(Options, TEXT(", ")));
|
||||
OnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
FString OutputString;
|
||||
const TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
|
||||
ensureAlways(FJsonSerializer::Serialize(RequestedJson->ToSharedRef(), Writer));
|
||||
|
||||
// Commented out, deserialisation done by caller
|
||||
//void* DeserializedData;
|
||||
//FSpeckleUser DeserializedData = FSpeckleUser( UserJSONObject );
|
||||
// if(!FJsonObjectConverter::JsonObjectToUStruct(*RequestedJson, OutStructType::StaticStruct(), &DeserializedData, 0, 0))
|
||||
// {
|
||||
// OnError("Failed to deserialize user object");
|
||||
// return;
|
||||
// }
|
||||
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Operation %s completed successfully"), *RequestLogName);
|
||||
ensureAlwaysMsgf(OnCompleteAction.ExecuteIfBound(OutputString), TEXT("%s: Complete handler was not bound properly"), *RequestLogName);
|
||||
};
|
||||
|
||||
const FHttpRequestRef Request = CreatePostRequest(ServerUrl, AuthToken, PostPayload);
|
||||
Request->OnProcessRequestComplete().BindLambda(ResponseHandler);
|
||||
|
||||
if(Request->ProcessRequest())
|
||||
{
|
||||
UE_LOG(LogSpeckle, Log, TEXT("POST Request %s to %s was sent, awaiting response"), *RequestLogName, *Request->GetURL() );
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("POST Request %s to %s failed to send"), *RequestLogName, *Request->GetURL() );
|
||||
}
|
||||
|
||||
FAnalytics::TrackEvent(Request->GetURL(), "NodeRun", TMap<FString, FString> { {"name", __FUNCTION__}});
|
||||
}
|
||||
|
||||
|
||||
bool FClientAPI::GetResponseAsJSON(const FHttpResponsePtr Response, const FString& RequestLogName, TSharedPtr<FJsonObject>& OutObject, const TFunctionRef<void(const FString& Message)> OnErrorAction)
|
||||
{
|
||||
|
||||
const FString JsonResponse = Response->GetContentAsString();
|
||||
|
||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonResponse);
|
||||
|
||||
if(!FJsonSerializer::Deserialize(Reader, OutObject))
|
||||
{
|
||||
const FString Message = FString::Printf(
|
||||
TEXT("Recieved a response from \"%s\" for \"%s\" request, but the response failed to deserialize: %s"),
|
||||
*Response->GetURL(), *RequestLogName, *JsonResponse);
|
||||
OnErrorAction(Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
FString Error;
|
||||
if(CheckForOperationErrors(OutObject, Error))
|
||||
{
|
||||
OnErrorAction(Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FHttpRequestRef FClientAPI::CreatePostRequest(const FString& ServerUrl, const FString AuthToken, const FString& PostPayload, const FString& Encoding)
|
||||
{
|
||||
FHttpRequestRef Request = FHttpModule::Get().CreateRequest();
|
||||
|
||||
Request->SetURL(ServerUrl + "/graphql");
|
||||
Request->SetVerb(TEXT("POST"));
|
||||
Request->SetHeader("Accept-Encoding", Encoding);
|
||||
Request->SetHeader("Content-Type", TEXT("application/json"));
|
||||
Request->SetHeader("Authorization", "Bearer " + AuthToken);
|
||||
Request->SetContentAsString(PostPayload);
|
||||
|
||||
return Request;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool FClientAPI::CheckRequestFailed(bool bWasSuccessful, FHttpResponsePtr Response, const FString& RequestLogName, const TFunctionRef<void(const FString& Message)> OnErrorAction)
|
||||
{
|
||||
//Check the request was sent
|
||||
if(!bWasSuccessful)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request \"%s\" to \"%s\" was unsuccessful: %s"),
|
||||
*RequestLogName, *Response->GetURL(), *Response->GetContentAsString());
|
||||
OnErrorAction(Message);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Check Response code
|
||||
const int32 ResponseCode = Response->GetResponseCode();
|
||||
if (ResponseCode != 200)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request \"%s\" to \"%s\" failed with HTTP response %d - %s"),
|
||||
*RequestLogName, *Response->GetURL(), ResponseCode, *Response->GetContentAsString());
|
||||
OnErrorAction(Message);
|
||||
return true;
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Operation %s recieved a response from %s"), *RequestLogName, *Response->GetURL());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FClientAPI::CheckForOperationErrors(const TSharedPtr<FJsonObject> GraphQLResponse, FString& OutErrorMessage)
|
||||
{
|
||||
check(GraphQLResponse != nullptr);
|
||||
|
||||
bool WasError = false;
|
||||
const TArray<TSharedPtr<FJsonValue>>* Errors;
|
||||
if(GraphQLResponse->TryGetArrayField(TEXT("errors"), Errors))
|
||||
{
|
||||
for(const TSharedPtr<FJsonValue>& e : *Errors)
|
||||
{
|
||||
FString Message;
|
||||
const TSharedPtr<FJsonObject>* ErrorObject;
|
||||
bool HadMessage = e->TryGetObject(ErrorObject)
|
||||
&& (*ErrorObject)->TryGetStringField("message", Message);
|
||||
if(!HadMessage)
|
||||
{
|
||||
Message = "An operation error occured but had no message!\n";
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("%s"), *Message);
|
||||
}
|
||||
|
||||
OutErrorMessage.Append(Message + "\n");
|
||||
WasError = true;
|
||||
}
|
||||
}
|
||||
return WasError;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "API/Operations/ReceiveOperation.h"
|
||||
|
||||
@@ -10,7 +9,10 @@
|
||||
#include "Mixpanel.h"
|
||||
|
||||
|
||||
UReceiveOperation* UReceiveOperation::ReceiveOperation(UObject* WorldContextObject, const FString& ObjectId, TScriptInterface<ITransport> RemoteTransport, TScriptInterface<ITransport> LocalTransport)
|
||||
UReceiveOperation* UReceiveOperation::ReceiveOperation(UObject* WorldContextObject,
|
||||
const FString& ObjectId,
|
||||
TScriptInterface<ITransport> RemoteTransport,
|
||||
TScriptInterface<ITransport> LocalTransport)
|
||||
{
|
||||
UReceiveOperation* Node = NewObject<UReceiveOperation>();
|
||||
Node->ObjectId = ObjectId;
|
||||
@@ -21,12 +23,10 @@ UReceiveOperation* UReceiveOperation::ReceiveOperation(UObject* WorldContextObje
|
||||
return Node;
|
||||
}
|
||||
|
||||
|
||||
void UReceiveOperation::Activate()
|
||||
{
|
||||
FAnalytics::TrackEvent("unknown", "unknown", "NodeRun", TMap<FString, FString> { {"name", StaticClass()->GetName() }});
|
||||
FAnalytics::TrackEvent("unknown", "NodeRun", TMap<FString, FString> { {"name", StaticClass()->GetName()} });
|
||||
|
||||
//Async(EAsyncExecution::Thread, [this]{Receive();});
|
||||
Receive();
|
||||
}
|
||||
|
||||
@@ -46,13 +46,16 @@ void UReceiveOperation::Receive()
|
||||
// 2. Try and get object from remote transport
|
||||
if(RemoteTransport == nullptr)
|
||||
{
|
||||
FString ErrorMessage = TEXT("Could not find specified object using the local transport, and you didn't provide a fallback remote from which to pull it.");
|
||||
FString ErrorMessage = TEXT(
|
||||
"Could not find specified object using the local transport, and you didn't provide a fallback remote from which to pull it.");
|
||||
|
||||
HandleError(ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
FTransportCopyObjectCompleteDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindUObject(this, &UReceiveOperation::HandleReceive);
|
||||
|
||||
FTransportErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindUObject(this, &UReceiveOperation::HandleError);
|
||||
|
||||
@@ -86,5 +89,4 @@ void UReceiveOperation::HandleError(FString& Message)
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
OnError.Broadcast(nullptr, Message);
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "API/Operations/SpeckleStreamAPIOperation.h"
|
||||
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "Mixpanel.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "API/ClientAPI.h"
|
||||
|
||||
|
||||
USpeckleStreamAPIOperation* USpeckleStreamAPIOperation::SpeckleStreamAPIOperation(
|
||||
const FString& ServerUrl,
|
||||
const FString& AuthToken,
|
||||
const FString& GraphQlQuery,
|
||||
const FString& ResponsePropertyName,
|
||||
const FString& RequestLogName)
|
||||
{
|
||||
USpeckleStreamAPIOperation* Node = NewObject<USpeckleStreamAPIOperation>();
|
||||
Node->ServerUrl = ServerUrl.TrimEnd();
|
||||
Node->AuthToken = AuthToken.TrimEnd();
|
||||
Node->Query = GraphQlQuery;
|
||||
Node->ResponsePropertyName = ResponsePropertyName;
|
||||
Node->RequestLogName = RequestLogName;
|
||||
|
||||
return Node;
|
||||
}
|
||||
|
||||
void USpeckleStreamAPIOperation::Activate()
|
||||
{
|
||||
FAnalytics::TrackEvent(ServerUrl, "NodeRun", TMap<FString, FString> { {"name", RequestLogName} });
|
||||
|
||||
Request();
|
||||
}
|
||||
|
||||
void USpeckleStreamAPIOperation::Request()
|
||||
{
|
||||
FString Payload = FString::Printf(TEXT("{\"query\": \"%s\"}"), *Query.ReplaceCharWithEscapedChar());
|
||||
FAPIResponceDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindUObject(this, &USpeckleStreamAPIOperation::HandleReceive);
|
||||
|
||||
FErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindUObject(this, &USpeckleStreamAPIOperation::HandleError);
|
||||
FClientAPI::MakeGraphQLRequest(ServerUrl, AuthToken, ResponsePropertyName, Payload,RequestLogName , CompleteDelegate, ErrorDelegate);
|
||||
}
|
||||
|
||||
void USpeckleStreamAPIOperation::HandleReceive(const FString& ResponseJson)
|
||||
{
|
||||
check(IsInGameThread());
|
||||
|
||||
UE_LOG(LogSpeckle, Log, TEXT("%s to %s Succeeded"), *StaticClass()->GetName(), *ServerUrl);
|
||||
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
OnReceiveSuccessfully.Broadcast(ResponseJson, "");
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
void USpeckleStreamAPIOperation::HandleError(const FString& Message)
|
||||
{
|
||||
check(IsInGameThread());
|
||||
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("%s failed - %s"), *StaticClass()->GetName(), *Message);
|
||||
|
||||
FEditorScriptExecutionGuard ScriptGuard;
|
||||
OnError.Broadcast("", Message);
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
|
||||
|
||||
#include "API/SpeckleAPIFunctions.h"
|
||||
|
||||
#include "JsonObjectConverter.h"
|
||||
|
||||
bool USpeckleAPIFunctions::DeserializeResponse(const FString& JsonString, int32& OutStruct)
|
||||
{
|
||||
// We should never hit this! stubs to avoid NoExport on the class.
|
||||
check(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool USpeckleAPIFunctions::GenericDeserializeResponse(const FString& JsonString, const UScriptStruct* StructType,
|
||||
void* OutStruct)
|
||||
{
|
||||
//see FJsonObjectConverter::JsonObjectStringToUStruct
|
||||
TSharedPtr<FJsonObject> JsonObject;
|
||||
TSharedRef<TJsonReader<> > JsonReader = TJsonReaderFactory<>::Create(JsonString);
|
||||
if (!FJsonSerializer::Deserialize(JsonReader, JsonObject) || !JsonObject.IsValid())
|
||||
{
|
||||
UE_LOG(LogJson, Warning, TEXT("JsonObjectStringToUStruct - Unable to parse json=[%s]"), *JsonString);
|
||||
return false;
|
||||
}
|
||||
if (!FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), StructType, OutStruct, 0, 0))
|
||||
{
|
||||
UE_LOG(LogJson, Warning, TEXT("JsonObjectStringToUStruct - Unable to deserialize. json=[%s]"), *JsonString);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void USpeckleAPIFunctions::CommitReceived(FSpeckleCommit& Commit)
|
||||
{
|
||||
//TODO send read read receipt
|
||||
//FString Query = FString::Printf(TEXT("mutation{ commitReceive(input:%s) }"), );
|
||||
}
|
||||
|
||||
DEFINE_FUNCTION(USpeckleAPIFunctions::execDeserializeResponse)
|
||||
{
|
||||
// Get JsonString
|
||||
P_GET_PROPERTY(FStrProperty, JsonString);
|
||||
|
||||
// Get OutStruct
|
||||
Stack.MostRecentPropertyAddress = nullptr;
|
||||
Stack.StepCompiledIn<FStructProperty>(nullptr);
|
||||
void* OutBlueprintDataPtr = Stack.MostRecentPropertyAddress;
|
||||
FStructProperty* StructProp = CastField<FStructProperty>(Stack.MostRecentProperty);
|
||||
UScriptStruct* StructType = StructProp->Struct;
|
||||
|
||||
P_FINISH;
|
||||
|
||||
P_NATIVE_BEGIN;
|
||||
|
||||
*static_cast<bool*>(RESULT_PARAM) = GenericDeserializeResponse(JsonString, StructType, OutBlueprintDataPtr);
|
||||
P_NATIVE_END;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "API/SpeckleSerializer.h"
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "API/SpeckleSerializer.h"
|
||||
|
||||
#include "Objects/Base.h"
|
||||
#include "LogSpeckle.h"
|
||||
@@ -9,18 +11,18 @@
|
||||
#include "UObject/Package.h"
|
||||
|
||||
|
||||
UBase* USpeckleSerializer::DeserializeBase(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport)
|
||||
UBase* USpeckleSerializer::DeserializeBase(const TSharedPtr<FJsonObject> Obj,
|
||||
const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
if(Obj == nullptr) return nullptr;
|
||||
|
||||
{ // Handle Detached Objects
|
||||
TSharedPtr<FJsonObject> DetachedObject;
|
||||
if(USpeckleObjectUtils::ResolveReference(Obj, ReadTransport, DetachedObject))
|
||||
{
|
||||
// Handle Detached Objects
|
||||
TSharedPtr<FJsonObject> DetachedObject;
|
||||
if(USpeckleObjectUtils::ResolveReference(Obj, ReadTransport, DetachedObject))
|
||||
{
|
||||
return DeserializeBase(DetachedObject, ReadTransport);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FString SpeckleType;
|
||||
if (!Obj->TryGetStringField("speckle_type", SpeckleType)) return nullptr;
|
||||
FString ObjectId = "";
|
||||
@@ -59,7 +61,7 @@ UBase* USpeckleSerializer::DeserializeBase(const TSharedPtr<FJsonObject> Obj, co
|
||||
}
|
||||
|
||||
UBase* USpeckleSerializer::DeserializeBaseById(const FString& ObjectId,
|
||||
const TScriptInterface<ITransport> ReadTransport)
|
||||
const TScriptInterface<ITransport> ReadTransport)
|
||||
{
|
||||
auto Obj = ReadTransport->GetSpeckleObject(ObjectId);
|
||||
return DeserializeBase(Obj, ReadTransport);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "Mixpanel.h"
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Mixpanel.h"
|
||||
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "HttpModule.h"
|
||||
@@ -15,17 +17,27 @@ const FString FAnalytics::MixpanelToken = TEXT("acd87c5a50b56df91a795e999812a3a4
|
||||
const FString FAnalytics::MixpanelServer = TEXT("https://analytics.speckle.systems");
|
||||
const FString FAnalytics::VersionedApplicationName = FString::Printf(TEXT("Unreal Engine %d.%d"), ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION);
|
||||
|
||||
void FAnalytics::TrackEvent(const FString& Email, const FString& Server, const FString& EventName)
|
||||
void FAnalytics::TrackEvent(const FString& Server, const FString& EventName)
|
||||
{
|
||||
#if !SUPPRESS_SPECKLE_ANALYTICS
|
||||
const TMap<FString, FString> CustomProperties;
|
||||
TrackEvent( Server, EventName, CustomProperties);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FAnalytics::TrackEvent(const FString& Server, const FString& EventName, const TMap<FString, FString>& CustomProperties)
|
||||
{
|
||||
#if !SUPPRESS_SPECKLE_ANALYTICS
|
||||
//Since we don't have access to users email address, we will hash the system user name instead (better than not tracking users at all)
|
||||
const FString Email = FString(FGenericPlatformProcess::ComputerName()) + FString(FGenericPlatformProcess::UserName());
|
||||
TrackEvent(Email, Server, EventName, CustomProperties);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FAnalytics::TrackEvent(const FString& Email, const FString& Server, const FString& EventName, const TMap<FString, FString>& CustomProperties)
|
||||
{
|
||||
#if !SUPPRESS_SPECKLE_ANALYTICS
|
||||
|
||||
|
||||
FString HashedEmail = "@" + Hash(Email); //prepending an @ so we distinguish logged and non-logged users
|
||||
FString HashedServer = Hash(Server);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Conversion/Converters/AggregateConverter.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "Conversion/Converters/BlockConverter.h"
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Conversion/Converters/BlockConverter.h"
|
||||
|
||||
#include "Objects/Other/BlockInstance.h"
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Conversion/Converters/MaterialConverter.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Conversion/Converters/PointCloudConverter.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Conversion/Converters/ProceduralMeshConverter.h"
|
||||
|
||||
@@ -19,7 +19,8 @@ UProceduralMeshConverter::UProceduralMeshConverter()
|
||||
ActorMobility = EComponentMobility::Static;
|
||||
}
|
||||
|
||||
UObject* UProceduralMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>& AvailableConverters )
|
||||
UObject* UProceduralMeshConverter::ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World,
|
||||
TScriptInterface<ISpeckleConverter>& AvailableConverters )
|
||||
{
|
||||
const UMesh* m = Cast<UMesh>(SpeckleBase);
|
||||
if(m != nullptr)
|
||||
@@ -47,7 +48,9 @@ UObject* UProceduralMeshConverter::ConvertToNative_Implementation(const UBase* S
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AActor* UProceduralMeshConverter::MeshToNative(const UMesh* SpeckleMesh, UWorld* World, TScriptInterface<ISpeckleConverter>& MaterialConverter)
|
||||
AActor* UProceduralMeshConverter::MeshToNative(const UMesh* SpeckleMesh,
|
||||
UWorld* World,
|
||||
TScriptInterface<ISpeckleConverter>& MaterialConverter)
|
||||
{
|
||||
AActor* MeshActor = CreateEmptyActor(World, USpeckleObjectUtils::CreateTransform(SpeckleMesh->Transform));
|
||||
UProceduralMeshComponent* MeshComponent = NewObject<UProceduralMeshComponent>(MeshActor, FName("SpeckleMeshComponent"));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Conversion/Converters/StaticMeshConverter.h"
|
||||
|
||||
@@ -262,6 +262,13 @@ UStaticMesh* UStaticMeshConverter::MeshesToNativeMesh(UObject* Outer, const UBas
|
||||
for(int j = 0; j < n; j ++)
|
||||
{
|
||||
int32 VertIndex = SpeckleMesh->Faces[i + 1 + j];
|
||||
|
||||
if(VertIndex < 0 || VertIndex >= Vertices.Num())
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Invalid Polygon while creating mesh %s - index %d was outside the bounds of the vertex array, face is ignored"), *SpeckleMesh->Id, VertIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
FVertexID Vert = Vertices[VertIndex];
|
||||
bool AlreadyInSet;
|
||||
Verts.Add(Vert, &AlreadyInSet);
|
||||
@@ -392,7 +399,8 @@ UBase* UStaticMeshConverter::MeshToSpeckle(const UStaticMeshComponent* Object)
|
||||
|
||||
void UStaticMeshConverter::FinishConversion_Implementation()
|
||||
{
|
||||
|
||||
if(StaticMeshesToBuild.Num() <= 0) return;
|
||||
|
||||
FScopeLock Lock(&Lock_StaticMeshesToBuild);
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Conversion/SpeckleConverterComponent.h"
|
||||
|
||||
@@ -15,7 +14,6 @@
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealModule"
|
||||
|
||||
// Sets default values for this component's properties
|
||||
USpeckleConverterComponent::USpeckleConverterComponent()
|
||||
{
|
||||
//TODO consider using an object library for default converters
|
||||
@@ -43,7 +41,11 @@ AActor* USpeckleConverterComponent::RecursivelyConvertToNative(AActor* AOwner, c
|
||||
{
|
||||
float ObjectsToConvert{};
|
||||
Base->TryGetDynamicNumber("totalChildrenCount", ObjectsToConvert);
|
||||
FScopedSlowTask Progress(ObjectsToConvert + 2, LOCTEXT("SpeckleConvertoNative","Converting Speckle Objects to Native"), DisplayProgressBar);
|
||||
|
||||
// Progress bar
|
||||
FScopedSlowTask Progress(ObjectsToConvert + 2,
|
||||
LOCTEXT("SpeckleConvertoNative","Converting Speckle Objects to Native"), DisplayProgressBar);
|
||||
|
||||
#if WITH_EDITOR
|
||||
Progress.MakeDialog(true, false);
|
||||
#endif
|
||||
@@ -85,7 +87,7 @@ AActor* USpeckleConverterComponent::RecursivelyConvertToNative_Internal(AActor*
|
||||
{
|
||||
if(Task->ShouldCancel()) break;
|
||||
|
||||
ConvertChild(Kvp.Value, AOwner, LocalTransport, Task, OutActors);
|
||||
ConvertChild(Kvp.Value, NextOwner, LocalTransport, Task, OutActors);
|
||||
}
|
||||
return AOwner;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "LogSpeckle.h"
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "LogSpeckle.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogSpeckle);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Objects/DisplayValueElement.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Objects/Geometry/Mesh.h"
|
||||
|
||||
@@ -106,11 +105,7 @@ bool UMesh::Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITra
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If not already so, this method will align vertices
|
||||
* such that a vertex and its corresponding texture coordinates have the same index.
|
||||
* See "https://github.com/specklesystems/speckle-sharp/blob/main/Objects/Objects/Geometry/Mesh.cs"
|
||||
*/
|
||||
|
||||
void UMesh::AlignVerticesWithTexCoordsByIndex()
|
||||
{
|
||||
if(TextureCoordinates.Num() == 0) return;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Objects/Geometry/PointCloud.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "Objects/ObjectModelRegistry.h"
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Objects/ObjectModelRegistry.h"
|
||||
|
||||
#include "Objects/Base.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Objects/Other/BlockInstance.h"
|
||||
#include "LogSpeckle.h"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Copyright AEC Systems Ltd
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Objects/Other/View3D.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Objects/Utils/SpeckleObjectUtils.h"
|
||||
|
||||
@@ -158,7 +157,16 @@ bool USpeckleObjectUtils::ParseSpeckleObject(const TSharedPtr<FJsonObject> Objec
|
||||
}
|
||||
|
||||
|
||||
AActor* USpeckleObjectUtils::SpawnActorInWorld(const TSubclassOf<AActor> Class, UWorld* World, const FTransform& Transform)
|
||||
AActor* USpeckleObjectUtils::SpawnActorInWorld(const UObject* WorldContextObject, const TSubclassOf<AActor> Class, UPARAM(ref) const FTransform& Transform)
|
||||
{
|
||||
return World->SpawnActor(Class, &Transform, FActorSpawnParameters());
|
||||
return WorldContextObject->GetWorld()->SpawnActor(Class, &Transform, FActorSpawnParameters());
|
||||
}
|
||||
|
||||
FString USpeckleObjectUtils::DisplayAsString(const FString& msg, const TSharedPtr<FJsonObject> Obj)
|
||||
{
|
||||
FString OutputString;
|
||||
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
|
||||
FJsonSerializer::Serialize(Obj.ToSharedRef(), Writer);
|
||||
UE_LOG(LogSpeckle, Display, TEXT("resulting jsonString from %s -> %s"), *msg, *OutputString);
|
||||
return OutputString;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "ReceiveSelectionComponent.h"
|
||||
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "LogSpeckle.h"
|
||||
#include "API/ClientAPI.h"
|
||||
#include "API/Models/SpeckleUser.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UReceiveSelectionComponent::PostEditChangeProperty(FPropertyChangedEvent& e)
|
||||
{
|
||||
Super::PostEditChangeProperty(e);
|
||||
|
||||
const FName PropertyName = (e.Property != nullptr) ? e.Property->GetFName() : NAME_None;
|
||||
PropertyChangeHandler(PropertyName);
|
||||
}
|
||||
#endif
|
||||
|
||||
void UReceiveSelectionComponent::PropertyChangeHandler(const FName& PropertyName)
|
||||
{
|
||||
if(bManualMode) return;
|
||||
|
||||
if (PropertyName == GET_MEMBER_NAME_CHECKED(UReceiveSelectionComponent, bManualMode))
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
else if (PropertyName == GET_MEMBER_NAME_CHECKED(UReceiveSelectionComponent, AuthToken)
|
||||
|| PropertyName == GET_MEMBER_NAME_CHECKED(UReceiveSelectionComponent, ServerUrl))
|
||||
{
|
||||
IsAccountValid = !AuthToken.IsEmpty() && !ServerUrl.IsEmpty();
|
||||
if(IsAccountValid)
|
||||
{
|
||||
//TODO maybe we should check URL is a valid URL?
|
||||
ServerUrl.TrimEndInline();
|
||||
while(ServerUrl.RemoveFromEnd("/")) { }
|
||||
AuthToken.TrimEndInline();
|
||||
}
|
||||
|
||||
UpdateStreams();
|
||||
}
|
||||
else if (PropertyName == GET_MEMBER_NAME_CHECKED(UReceiveSelectionComponent, SelectedStreamText)
|
||||
&& !SelectedStreamText.IsEmpty())
|
||||
{
|
||||
IsStreamValid = Streams.Contains(SelectedStreamText);
|
||||
if(IsStreamValid)
|
||||
{
|
||||
SelectStream(SelectedStreamText);
|
||||
}
|
||||
else UpdateStreams();
|
||||
}
|
||||
else if (PropertyName == GET_MEMBER_NAME_CHECKED(UReceiveSelectionComponent, SelectedBranchText)
|
||||
&& !SelectedBranchText.IsEmpty())
|
||||
{
|
||||
IsBranchValid = Branches.Contains(SelectedBranchText);
|
||||
if(IsBranchValid)
|
||||
{
|
||||
SelectBranch(SelectedBranchText);
|
||||
}
|
||||
else UpdateBranches();
|
||||
}
|
||||
else if (PropertyName == GET_MEMBER_NAME_CHECKED(UReceiveSelectionComponent, SelectedCommitText)
|
||||
&& !SelectedCommitText.IsEmpty())
|
||||
{
|
||||
IsCommitValid = Commits.Contains(SelectedCommitText);
|
||||
if(IsCommitValid)
|
||||
{
|
||||
SelectCommit(SelectedCommitText);
|
||||
}
|
||||
else UpdateCommits();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UReceiveSelectionComponent::Refresh()
|
||||
{
|
||||
IsAccountValid = !AuthToken.IsEmpty() && !ServerUrl.IsEmpty();
|
||||
|
||||
UpdateStreams();
|
||||
}
|
||||
|
||||
|
||||
bool UReceiveSelectionComponent::TryGetSelectedCommit(FSpeckleCommit& OutCommit) const
|
||||
{
|
||||
if(!bManualMode && IsCommitValid)
|
||||
{
|
||||
OutCommit = Commits.FindRef(SelectedCommitText);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UReceiveSelectionComponent::TryGetSelectedBranch(FSpeckleBranch& OutBranch) const
|
||||
{
|
||||
if(!bManualMode && IsBranchValid)
|
||||
{
|
||||
OutBranch = Branches.FindRef(SelectedBranchText);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UReceiveSelectionComponent::TryGetSelectedStream(FSpeckleStream& OutStream) const
|
||||
{
|
||||
if(!bManualMode && IsStreamValid)
|
||||
{
|
||||
OutStream = Streams.FindRef(SelectedStreamText);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UReceiveSelectionComponent::OpenURLInBrowser() const
|
||||
{
|
||||
const FString URL = GetUrl();
|
||||
|
||||
if(!ensure(FPlatformProcess::CanLaunchURL(*URL))) return;
|
||||
|
||||
FString LaunchErrors;
|
||||
FPlatformProcess::LaunchURL(*URL, nullptr, &LaunchErrors);
|
||||
|
||||
if(LaunchErrors.IsEmpty())
|
||||
{
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Launched URL \"%s\" in browser"), *URL);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Failed to launched URL \"%s\" in browser - %s"), *URL, *LaunchErrors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool UReceiveSelectionComponent::IsSelectionComplete(FString& OutStatusMessage) const
|
||||
{
|
||||
if(bManualMode)
|
||||
{
|
||||
if(ServerUrl.IsEmpty()) OutStatusMessage = TEXT("Server Url is invalid, must specify a valid url");
|
||||
else if(StreamId.IsEmpty()) OutStatusMessage = TEXT("Stream id is invalid, must specify a streamId");
|
||||
else if(ObjectId.IsEmpty()) OutStatusMessage = TEXT("Object id is invalid, must specify a objectId");
|
||||
else
|
||||
{
|
||||
OutStatusMessage = TEXT("Ready to recieve object");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!IsAccountValid) OutStatusMessage = TEXT("Account is invalid, please specify a valid server URL and token. (tokens can be created from your Speckle profiles page)");
|
||||
else if(!IsStreamValid) OutStatusMessage = TEXT("Selected stream is invalid");
|
||||
else if(!IsBranchValid) OutStatusMessage = TEXT("Selected branch is invalid");
|
||||
else if(!IsCommitValid) OutStatusMessage = TEXT("Selected commit is invalid");
|
||||
else
|
||||
{
|
||||
ensure(!ServerUrl.IsEmpty());
|
||||
ensure(!StreamId.IsEmpty());
|
||||
ensure(!ObjectId.IsEmpty());
|
||||
OutStatusMessage = TEXT("Ready to recieve commit");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FString UReceiveSelectionComponent::GetUrl() const
|
||||
{
|
||||
if(bManualMode)
|
||||
{
|
||||
return FString::Printf(TEXT("%s/streams/%s/objects/%s"),
|
||||
*ServerUrl, *StreamId, *ObjectId);
|
||||
}
|
||||
|
||||
if(!IsStreamValid) return ServerUrl;
|
||||
|
||||
if(!IsBranchValid)
|
||||
return FString::Printf(TEXT("%s/streams/%s/"),
|
||||
*ServerUrl, *GetSelectedStream().ID);
|
||||
|
||||
if(!IsCommitValid)
|
||||
return FString::Printf(TEXT("%s/streams/%s/branches/%s"),
|
||||
*ServerUrl, *GetSelectedStream().ID, *GetSelectedBranch().Name);
|
||||
|
||||
return FString::Printf(TEXT("%s/streams/%s/commits/%s"),
|
||||
*ServerUrl, *GetSelectedStream().ID, *GetSelectedCommit().ID);
|
||||
}
|
||||
|
||||
TArray<FString> UReceiveSelectionComponent::GetStreamsOptions() const
|
||||
{
|
||||
TArray<FString> Options;
|
||||
Streams.GetKeys(Options);
|
||||
return Options;
|
||||
}
|
||||
|
||||
bool UReceiveSelectionComponent::SelectStream(const FString& DisplayId)
|
||||
{
|
||||
const bool IsValid = !DisplayId.IsEmpty() && Streams.Contains(DisplayId);
|
||||
if(IsValid && !bManualMode)
|
||||
{
|
||||
SelectedStreamText = DisplayId;
|
||||
IsStreamValid = true;
|
||||
StreamId = GetSelectedStream().ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedStreamText = "";
|
||||
IsStreamValid = false;
|
||||
}
|
||||
UpdateBranches();
|
||||
return IsStreamValid;
|
||||
}
|
||||
|
||||
void UReceiveSelectionComponent::UpdateStreams()
|
||||
{
|
||||
Streams.Reset();
|
||||
SelectStream("");
|
||||
|
||||
if(!IsAccountValid || bManualMode) return;
|
||||
|
||||
const FString LogName(__FUNCTION__);
|
||||
const FString Payload = FString::Printf(TEXT("{\"query\": \"query{user{id streams(limit: %d){items{id name}}}}\"}"), Limit);
|
||||
|
||||
//Response Handling
|
||||
auto OnComplete = [&](const FString& ResponseJson)
|
||||
{
|
||||
if(bManualMode) return;
|
||||
|
||||
FSpeckleUser Response;
|
||||
|
||||
if(!ensure(FJsonObjectConverter::JsonObjectStringToUStruct(*ResponseJson, &Response, 0, 0))) return;
|
||||
|
||||
const TArray<FSpeckleStream>& AvailableStreams = Response.Streams.Items;
|
||||
//Assemble stream map
|
||||
Streams.Reserve(AvailableStreams.Num());
|
||||
for(const auto& s : Response.Streams.Items)
|
||||
{
|
||||
FString DisplayId = FString::Printf(TEXT("%s - %s"), *s.Name, *s.ID);
|
||||
Streams.Add(DisplayId, s);
|
||||
}
|
||||
|
||||
//Set default selection
|
||||
if(AvailableStreams.Num() > 0)
|
||||
{
|
||||
const FSpeckleStream& s = AvailableStreams[0];
|
||||
FString DisplayId = FString::Printf(TEXT("%s - %s"), *s.Name, *s.ID);
|
||||
|
||||
SelectStream(DisplayId);
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("%s was successful"), *LogName);
|
||||
};
|
||||
FAPIResponceDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindLambda(OnComplete);
|
||||
|
||||
//On error
|
||||
FErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindStatic(LogError, LogName);
|
||||
|
||||
FClientAPI::MakeGraphQLRequest(ServerUrl, AuthToken, "user", Payload, LogName, CompleteDelegate, ErrorDelegate);
|
||||
|
||||
}
|
||||
|
||||
FSpeckleStream UReceiveSelectionComponent::GetSelectedStream() const
|
||||
{
|
||||
return Streams[SelectedStreamText];
|
||||
}
|
||||
|
||||
TArray<FString> UReceiveSelectionComponent::GetBranchOptions()
|
||||
{
|
||||
TArray<FString> Options;
|
||||
Branches.GetKeys(Options);
|
||||
return Options;
|
||||
}
|
||||
|
||||
bool UReceiveSelectionComponent::SelectBranch(const FString& DisplayId)
|
||||
{
|
||||
const bool IsValid = !DisplayId.IsEmpty() && Branches.Contains(DisplayId);
|
||||
if(IsValid && !bManualMode)
|
||||
{
|
||||
SelectedBranchText = DisplayId;
|
||||
IsBranchValid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedBranchText = "";
|
||||
IsBranchValid=false;
|
||||
}
|
||||
UpdateCommits();
|
||||
return IsBranchValid;
|
||||
}
|
||||
|
||||
|
||||
void UReceiveSelectionComponent::UpdateBranches()
|
||||
{
|
||||
Branches.Reset();
|
||||
SelectBranch("");
|
||||
|
||||
if(!IsStreamValid || bManualMode) return;
|
||||
|
||||
const FString LogName(__FUNCTION__);
|
||||
const FString Payload = FString::Printf(TEXT("{\"query\": \"query{stream(id: \\\"%s\\\"){branches(limit: %d){items{id name}}}}\"}"), *GetSelectedStream().ID, Limit);
|
||||
|
||||
//Response Handling
|
||||
auto OnComplete = [&](const FString& ResponseJson)
|
||||
{
|
||||
if(bManualMode) return;
|
||||
|
||||
FSpeckleStream Response;
|
||||
|
||||
if(!ensure(FJsonObjectConverter::JsonObjectStringToUStruct(*ResponseJson, &Response, 0, 0))) return;
|
||||
|
||||
const TArray<FSpeckleBranch>& AvailableBranches = Response.Branches.Items;
|
||||
|
||||
//Assemble stream map
|
||||
Branches.Reserve(AvailableBranches.Num());
|
||||
for(const auto& b : AvailableBranches)
|
||||
{
|
||||
FString DisplayId = FString::Printf(TEXT("%s"), *b.Name);
|
||||
Branches.Add(DisplayId, b);
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("%s was successful"), *LogName);
|
||||
|
||||
//Set default selection
|
||||
if(AvailableBranches.Num() > 0)
|
||||
{
|
||||
const FSpeckleBranch& b = AvailableBranches[0];
|
||||
FString DisplayId = FString::Printf(TEXT("%s"), *b.Name);
|
||||
|
||||
SelectBranch(DisplayId);
|
||||
}
|
||||
};
|
||||
FAPIResponceDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindLambda(OnComplete);
|
||||
|
||||
//On error
|
||||
FErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindStatic(LogError, LogName);
|
||||
|
||||
FClientAPI::MakeGraphQLRequest(ServerUrl, AuthToken, "stream", Payload,LogName, CompleteDelegate, ErrorDelegate);
|
||||
}
|
||||
|
||||
|
||||
FSpeckleBranch UReceiveSelectionComponent::GetSelectedBranch() const
|
||||
{
|
||||
return Branches[SelectedBranchText];
|
||||
}
|
||||
|
||||
TArray<FString> UReceiveSelectionComponent::GetCommitOptions()
|
||||
{
|
||||
TArray<FString> Options;
|
||||
Commits.GetKeys(Options);
|
||||
return Options;
|
||||
}
|
||||
|
||||
bool UReceiveSelectionComponent::SelectCommit(const FString& DisplayId)
|
||||
{
|
||||
const bool IsValid = !DisplayId.IsEmpty() && Commits.Contains(DisplayId);
|
||||
if(IsValid && !bManualMode)
|
||||
{
|
||||
SelectedCommitText = DisplayId;
|
||||
IsCommitValid = true;
|
||||
ObjectId = GetSelectedCommit().ReferencedObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedCommitText = "";
|
||||
IsCommitValid=false;
|
||||
ObjectId = "";
|
||||
}
|
||||
return IsCommitValid;
|
||||
}
|
||||
|
||||
void UReceiveSelectionComponent::UpdateCommits()
|
||||
{
|
||||
Commits.Reset();
|
||||
SelectCommit("");
|
||||
|
||||
if(!IsBranchValid || bManualMode) return;
|
||||
|
||||
const FString LogName(__FUNCTION__);
|
||||
const FString Payload = FString::Printf(TEXT("{\"query\": \"query{stream(id: \\\"%s\\\"){branch(name: \\\"%s\\\"){commits(limit: %d){items{id message referencedObject}}}}}\"}"), *GetSelectedStream().ID, *GetSelectedBranch().Name, Limit);
|
||||
|
||||
//Response Handling
|
||||
auto OnComplete = [&](const FString& ResponseJson)
|
||||
{
|
||||
if(bManualMode) return;
|
||||
|
||||
FSpeckleStream Response;
|
||||
|
||||
if(!ensure(FJsonObjectConverter::JsonObjectStringToUStruct(*ResponseJson, &Response, 0, 0))) return;
|
||||
|
||||
const TArray<FSpeckleCommit>& AvailableCommits = Response.Branch.Commits.Items;
|
||||
|
||||
//Assemble stream map
|
||||
for(const auto& c :AvailableCommits)
|
||||
{
|
||||
FString DisplayId = FString::Printf(TEXT("%s - %s"), *c.Message, *c.ID);
|
||||
Commits.Add(DisplayId, c);
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("%s was successful"), *LogName);
|
||||
|
||||
//Set default selection
|
||||
if(AvailableCommits.Num() > 0)
|
||||
{
|
||||
const FSpeckleCommit& c = AvailableCommits[0];
|
||||
FString DisplayId = FString::Printf(TEXT("%s - %s"), *c.Message, *c.ID);
|
||||
SelectCommit(DisplayId);
|
||||
}
|
||||
};
|
||||
FAPIResponceDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindLambda(OnComplete);
|
||||
|
||||
//On error
|
||||
FErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindStatic(LogError, LogName);
|
||||
|
||||
FClientAPI::MakeGraphQLRequest(ServerUrl, AuthToken, "stream", Payload,LogName, CompleteDelegate, ErrorDelegate);
|
||||
}
|
||||
|
||||
FSpeckleCommit UReceiveSelectionComponent::GetSelectedCommit() const
|
||||
{
|
||||
return Commits[SelectedCommitText];
|
||||
}
|
||||
|
||||
void UReceiveSelectionComponent::LogError(const FString& Message, const FString LogName)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("%s: %s"), *LogName, *Message)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "SpeckleUnreal.h"
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
|
||||
void FSpeckleUnrealModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
// This code will execute after your module is loaded into memory.
|
||||
// The exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FSpeckleUnrealModule::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.
|
||||
// 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
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "SpeckleUnrealManager.h"
|
||||
|
||||
#include "ReceiveSelectionComponent.h"
|
||||
#include "API/Operations/ReceiveOperation.h"
|
||||
#include "Transports/MemoryTransport.h"
|
||||
#include "Transports/ServerTransport.h"
|
||||
@@ -9,6 +12,7 @@
|
||||
#include "Misc/ScopedSlowTask.h"
|
||||
#include "Objects/Base.h"
|
||||
#include "Mixpanel.h"
|
||||
#include "API/SpeckleAPIFunctions.h"
|
||||
#include "Engine/Engine.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FSpeckleUnrealModule"
|
||||
@@ -22,10 +26,9 @@ ASpeckleUnrealManager::ASpeckleUnrealManager()
|
||||
RootComponent->SetMobility(EComponentMobility::Static);
|
||||
|
||||
Converter = CreateDefaultSubobject<USpeckleConverterComponent>(FName("Converter"));
|
||||
|
||||
ReceiveSelection = CreateDefaultSubobject<UReceiveSelectionComponent>(FName("ReceiveSelection"));
|
||||
KeepCache = true;
|
||||
DisplayProgressBar = true;
|
||||
ServerUrl = "https://speckle.xyz";
|
||||
}
|
||||
|
||||
void ASpeckleUnrealManager::BeginPlay()
|
||||
@@ -37,40 +40,52 @@ void ASpeckleUnrealManager::BeginPlay()
|
||||
|
||||
void ASpeckleUnrealManager::Receive()
|
||||
{
|
||||
// Check object to receive has been specified properly
|
||||
FString StatusMessage;
|
||||
if(!ReceiveSelection->IsSelectionComplete(StatusMessage))
|
||||
{
|
||||
HandleError(StatusMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete all actors from previous receive operations
|
||||
DeleteObjects();
|
||||
|
||||
const FString ServerUrl = ReceiveSelection->ServerUrl;
|
||||
const FString AuthToken = ReceiveSelection->AuthToken;
|
||||
const FString StreamId = ReceiveSelection->StreamId;
|
||||
const FString ObjectId = ReceiveSelection->ObjectId;
|
||||
|
||||
FAnalytics::TrackEvent( ServerUrl, "NodeRun", TMap<FString, FString> { {"name", StaticClass()->GetName() }, {"worldType", FString::FromInt(GetWorld()->WorldType)}});
|
||||
FString Message = FString::Printf(TEXT("Fetching Objects from Speckle Server: %s"), *ServerUrl);
|
||||
PrintMessage(Message);
|
||||
|
||||
// Trim parameters
|
||||
ServerUrl.TrimEndInline();
|
||||
while(ServerUrl.RemoveFromEnd("/")) { }
|
||||
StreamID.TrimEndInline();
|
||||
ObjectID.TrimEndInline();
|
||||
AuthToken.TrimEndInline();
|
||||
|
||||
FAnalytics::TrackEvent("unknown", "unknown", "NodeRun", TMap<FString, FString> { {"name", StaticClass()->GetName() }, {"worldType", FString::FromInt(GetWorld()->WorldType)}});
|
||||
|
||||
if(!KeepCache && LocalObjectCache.GetObjectRef() != nullptr)
|
||||
// Setup network transports
|
||||
UServerTransport* ServerTransport = UServerTransport::CreateServerTransport(ServerUrl,StreamId,AuthToken);
|
||||
|
||||
if(!KeepCache && LocalObjectCache)
|
||||
{
|
||||
LocalObjectCache.GetObjectRef()->ConditionalBeginDestroy();
|
||||
LocalObjectCache = UMemoryTransport::CreateEmptyMemoryTransport();
|
||||
}
|
||||
if(LocalObjectCache.GetObjectRef() == nullptr)
|
||||
{
|
||||
LocalObjectCache = UMemoryTransport::CreateEmptyMemoryTransport();
|
||||
}
|
||||
|
||||
UServerTransport* ServerTransport = UServerTransport::CreateServerTransport(ServerUrl,StreamID,AuthToken);
|
||||
|
||||
FString Message = FString::Printf(TEXT("Fetching Objects from Speckle Server: %s"), *ServerUrl);
|
||||
PrintMessage(Message);
|
||||
if(!LocalObjectCache) LocalObjectCache = UMemoryTransport::CreateEmptyMemoryTransport();
|
||||
|
||||
// Setup delegate callbacks
|
||||
FTransportErrorDelegate ErrorDelegate;
|
||||
ErrorDelegate.BindUObject(this, &ASpeckleUnrealManager::HandleError);
|
||||
FTransportCopyObjectCompleteDelegate CompleteDelegate;
|
||||
CompleteDelegate.BindUObject(this, &ASpeckleUnrealManager::HandleReceive, DisplayProgressBar);
|
||||
|
||||
|
||||
//Receive
|
||||
ServerTransport->CopyObjectAndChildren(ObjectID, LocalObjectCache, CompleteDelegate, ErrorDelegate);
|
||||
// Send request for objects
|
||||
ServerTransport->CopyObjectAndChildren(ObjectId, LocalObjectCache, CompleteDelegate, ErrorDelegate);
|
||||
|
||||
// Read receipt (if receiving a commit object)
|
||||
FSpeckleCommit Commit;
|
||||
if(ReceiveSelection->TryGetSelectedCommit(Commit))
|
||||
{
|
||||
//TODO read receipt
|
||||
USpeckleAPIFunctions::CommitReceived(Commit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +126,7 @@ void ASpeckleUnrealManager::PrintMessage(FString& Message, bool IsError) const
|
||||
{
|
||||
UE_LOG(LogSpeckle, Log, TEXT("%s"), *Message);
|
||||
}
|
||||
|
||||
|
||||
FColor Color = IsError? FColor::Red : FColor::Green;
|
||||
GEngine->AddOnScreenDebugMessage(0, 5.0f, Color, Message);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
|
||||
#include "LogSpeckle.h"
|
||||
#include "Misc/AutomationTest.h"
|
||||
#include "ReceiveSelectionComponent.h"
|
||||
#include "Tests/AutomationCommon.h"
|
||||
|
||||
#if WITH_DEV_AUTOMATION_TESTS
|
||||
|
||||
|
||||
class UMock : public UReceiveSelectionComponent
|
||||
{
|
||||
friend class FReceiveSelectionComponentTest;
|
||||
friend class FCheckValidSelection;
|
||||
friend class FCheckLimitMatch;
|
||||
};
|
||||
|
||||
DEFINE_LATENT_AUTOMATION_COMMAND_TWO_PARAMETER(FCheckValidSelection, FAutomationTestBase*, Test, UMock*, SelectionComponent);
|
||||
bool FCheckValidSelection::Update()
|
||||
{
|
||||
const UMock* s = SelectionComponent;
|
||||
|
||||
Test->TestTrue(TEXT("Account valid on set"), s->IsAccountValid);
|
||||
Test->TestTrue(TEXT("Stream valid on set"), s->IsStreamValid);
|
||||
Test->TestTrue(TEXT("Branch valid on set"), s->IsBranchValid);
|
||||
Test->TestTrue(TEXT("Commit valid on set"), s->IsCommitValid);
|
||||
return true;
|
||||
}
|
||||
|
||||
DEFINE_LATENT_AUTOMATION_COMMAND_TWO_PARAMETER(FCheckLimitMatch, FAutomationTestBase*, Test, UMock*, SelectionComponent);
|
||||
bool FCheckLimitMatch::Update()
|
||||
{
|
||||
const UMock* s = SelectionComponent;
|
||||
|
||||
Test->TestTrue(TEXT("Fetched streams is less than limit"), s->Streams.Num() <= s->Limit);
|
||||
Test->TestTrue(TEXT("Fetched branches is less than limit"), s->Branches.Num() <= s->Limit);
|
||||
Test->TestTrue(TEXT("Fetched commits is less than limit"), s->Commits.Num() <= s->Limit);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FReceiveSelectionComponentTest, "SpeckleUnreal.ReceiveSelectionComponentTest", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
|
||||
|
||||
bool FReceiveSelectionComponentTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
#ifdef TEST_AUTH_TOKEN
|
||||
UMock* s = NewObject<UMock>();
|
||||
|
||||
|
||||
//Test initialisation
|
||||
TestFalse(TEXT("Account valid on initialise"), s->IsAccountValid);
|
||||
TestFalse(TEXT("Stream valid on initialise"), s->IsStreamValid);
|
||||
TestFalse(TEXT("Branch valid on initialise"), s->IsBranchValid);
|
||||
TestFalse(TEXT("Commit valid on initialise"), s->IsCommitValid);
|
||||
|
||||
|
||||
s->AuthToken = TEST_AUTH_TOKEN;
|
||||
s->ServerUrl = TEXT("https://latest.speckle.dev");
|
||||
s->bManualMode = false;
|
||||
s->Refresh();
|
||||
|
||||
//wait 5 seconds for HTTP requests to finish
|
||||
ADD_LATENT_AUTOMATION_COMMAND(FWaitLatentCommand(5.0f));
|
||||
|
||||
ADD_LATENT_AUTOMATION_COMMAND(FCheckValidSelection(this, s));
|
||||
ADD_LATENT_AUTOMATION_COMMAND(FCheckLimitMatch(this, s));
|
||||
|
||||
|
||||
#else
|
||||
TestTrue(TEXT("TEST_AUTH_TOKEN definition exists"), false);
|
||||
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
#include "Objects/Base.h"
|
||||
#include "Transports/ServerTransport.h"
|
||||
|
||||
|
||||
#if WITH_DEV_AUTOMATION_TESTS
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FServerTransportTest, "SpeckleUnreal.Transports.ServerTransport", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ProductFilter)
|
||||
|
||||
|
||||
bool FServerTransportTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
FString ServerUrl = TEXT("https://example.com");
|
||||
FString StreamId = TEXT("1234");
|
||||
FString Token = TEXT("MyAuthToken");
|
||||
auto Transport = UServerTransport::CreateServerTransport(ServerUrl, StreamId, Token);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,9 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Transports/MemoryTransport.h"
|
||||
|
||||
#include "LogSpeckle.h"
|
||||
|
||||
bool UMemoryTransport::HasObject(const FString& ObjectId) const
|
||||
{
|
||||
return SpeckleObjects.Contains(ObjectId);
|
||||
@@ -17,4 +18,5 @@ TSharedPtr<FJsonObject> UMemoryTransport::GetSpeckleObject(const FString& Object
|
||||
void UMemoryTransport::SaveObject(const FString& ObjectId, const TSharedPtr<FJsonObject> SerializedObject)
|
||||
{
|
||||
SpeckleObjects.Add(ObjectId, SerializedObject);
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("Added %s to in memory transport, now %d objects total "), *ObjectId, SpeckleObjects.Num());
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "Transports/ServerTransport.h"
|
||||
|
||||
#include "LogSpeckle.h"
|
||||
#include "HttpModule.h"
|
||||
#include "Mixpanel.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "HttpModule.h"
|
||||
#include "Interfaces/IHttpRequest.h"
|
||||
#include "Interfaces/IHttpResponse.h"
|
||||
#include "Mixpanel.h"
|
||||
#include "Policies/CondensedJsonPrintPolicy.h"
|
||||
|
||||
|
||||
@@ -29,6 +28,38 @@ bool UServerTransport::HasObject(const FString& ObjectId) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void UServerTransport::HandleRootObjectResponse(const FString& RootObjSerialized,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FString& ObjectId) const
|
||||
{
|
||||
TSharedPtr<FJsonObject> RootObj;
|
||||
if(!LoadJson(RootObjSerialized, RootObj))
|
||||
{
|
||||
FString Message = FString::Printf( TEXT("A Root Object %s was recieved but was invalid and could not be deserialied"), *ObjectId);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
TargetTransport->SaveObject(ObjectId, RootObj);
|
||||
|
||||
// Find children are not already in the target transport
|
||||
const auto Closures = RootObj->GetObjectField("__closure")->Values;
|
||||
|
||||
TArray<FString> ChildrenIds;
|
||||
Closures.GetKeys(ChildrenIds);
|
||||
TArray<FString> NewChildrenIds;
|
||||
for(const FString& Id : ChildrenIds)
|
||||
{
|
||||
if(TargetTransport->HasObject(Id)) continue;
|
||||
|
||||
NewChildrenIds.Add(Id);
|
||||
}
|
||||
|
||||
|
||||
FetchChildren(TargetTransport, ObjectId, NewChildrenIds);
|
||||
}
|
||||
|
||||
|
||||
void UServerTransport::CopyObjectAndChildren(const FString& ObjectId,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
@@ -44,8 +75,7 @@ void UServerTransport::CopyObjectAndChildren(const FString& ObjectId,
|
||||
Request->SetURL(Endpoint);
|
||||
Request->SetHeader("Accept", TEXT("text/plain"));
|
||||
Request->SetHeader("Authorization", "Bearer " + AuthToken);
|
||||
|
||||
|
||||
|
||||
// Response Callback
|
||||
auto ResponseHandler = [=](FHttpRequestPtr, FHttpResponsePtr Response, bool bWasSuccessful) mutable
|
||||
{
|
||||
@@ -65,7 +95,6 @@ void UServerTransport::CopyObjectAndChildren(const FString& ObjectId,
|
||||
}
|
||||
|
||||
HandleRootObjectResponse(Response->GetContentAsString(), TargetTransport, ObjectId);
|
||||
|
||||
};
|
||||
|
||||
Request->OnProcessRequestComplete().BindLambda(ResponseHandler);
|
||||
@@ -80,18 +109,18 @@ void UServerTransport::CopyObjectAndChildren(const FString& ObjectId,
|
||||
return;
|
||||
}
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("GET Request sent for root object at %s, awaiting response"), *Endpoint );
|
||||
FAnalytics::TrackEvent("unknown", ServerUrl, "Receive");
|
||||
FAnalytics::TrackEvent(ServerUrl, "Receive");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void UServerTransport::FetchChildren(TScriptInterface<ITransport> TargetTransport, const FString& ObjectId, const TArray<FString>& ChildrenIds, int32 CStart) const
|
||||
void UServerTransport::FetchChildren(TScriptInterface<ITransport> TargetTransport, const FString& ObjectId,
|
||||
const TArray<FString>& ChildrenIds, int32 CStart) const
|
||||
{
|
||||
// Check if all children have been fetched
|
||||
if(ChildrenIds.Num() <= CStart)
|
||||
{
|
||||
ensureAlwaysMsgf(this->OnComplete.ExecuteIfBound(TargetTransport->GetSpeckleObject(ObjectId)), TEXT("Complete handler was not bound properly"));
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Finished fetching child Speckle objects"));
|
||||
ensureAlwaysMsgf(this->OnComplete.ExecuteIfBound(TargetTransport->GetSpeckleObject(ObjectId)),
|
||||
TEXT("Complete handler was not bound properly"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,8 +134,7 @@ void UServerTransport::FetchChildren(TScriptInterface<ITransport> TargetTranspor
|
||||
for (int32 i = CStart; i < CEnd; i++)
|
||||
{
|
||||
Writer->WriteValue(ChildrenIds[i]);
|
||||
}
|
||||
Writer->WriteArrayEnd();
|
||||
} Writer->WriteArrayEnd();
|
||||
Writer->Close();
|
||||
}
|
||||
|
||||
@@ -134,30 +162,39 @@ void UServerTransport::FetchChildren(TScriptInterface<ITransport> TargetTranspor
|
||||
// Response Callback
|
||||
auto ResponseHandler = [=](FHttpRequestPtr, FHttpResponsePtr Response, bool bWasSuccessful) mutable
|
||||
{
|
||||
// Request Fail
|
||||
if(!bWasSuccessful)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request for children of root object %s/%s failed: %s"), *StreamId, *ObjectId, *Response->GetContentAsString());
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 ResponseCode = Response->GetResponseCode();
|
||||
if (ResponseCode != 200)
|
||||
{
|
||||
FString Message = FString::Printf(TEXT("Request for children of root object %s/%s failed:\nHTTP response %d"), *StreamId, *ObjectId, ResponseCode);
|
||||
FString Message = FString::Printf(TEXT("Request for children of root object %s/%s failed: %s"),
|
||||
*StreamId, *ObjectId, *Response->GetContentAsString());
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Any HTTP Fail
|
||||
const int32 ResponseCode = Response->GetResponseCode();
|
||||
if (ResponseCode != 200)
|
||||
{
|
||||
FString Message = FString::Printf(
|
||||
TEXT("Request for children of root object %s/%s failed:\nHTTP response %d"),
|
||||
*StreamId, *ObjectId, ResponseCode);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Success: Start parsing
|
||||
TArray<FString> Lines;
|
||||
const int32 LineCount = SplitLines(Response->GetContentAsString(), Lines);
|
||||
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("Parsing %d downloaded objects..."), LineCount)
|
||||
|
||||
// Warning: Fewer/More objects then expected
|
||||
if(LineCount != CEnd - CStart)
|
||||
{
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Requested %d objects, but recieved %d"), CEnd - CStart, LineCount);
|
||||
UE_LOG(LogSpeckle, Warning, TEXT("Requested %d objects, but received %d"), CEnd - CStart, LineCount);
|
||||
}
|
||||
|
||||
|
||||
// Load JSON as objects
|
||||
for (const FString& Line : Lines)
|
||||
{
|
||||
FString Id, ObjectJson;
|
||||
@@ -167,14 +204,13 @@ void UServerTransport::FetchChildren(TScriptInterface<ITransport> TargetTranspor
|
||||
TSharedPtr<FJsonObject> JsonObject;
|
||||
if(!LoadJson(ObjectJson, JsonObject)) continue;
|
||||
|
||||
|
||||
TargetTransport->SaveObject(Id, JsonObject);
|
||||
}
|
||||
|
||||
UE_LOG(LogSpeckle, Log, TEXT("Processed %d/%d Child objects"), CEnd, ChildrenIds.Num())
|
||||
|
||||
//Iterate again for any missing children
|
||||
FetchChildren(TargetTransport, ObjectId, ChildrenIds , CEnd);
|
||||
FetchChildren(TargetTransport, ObjectId, ChildrenIds, CEnd);
|
||||
};
|
||||
|
||||
Request->OnProcessRequestComplete().BindLambda(ResponseHandler);
|
||||
@@ -188,36 +224,15 @@ void UServerTransport::FetchChildren(TScriptInterface<ITransport> TargetTranspor
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
UE_LOG(LogSpeckle, Verbose, TEXT("Requesting %d child objects"), CEnd - CStart);
|
||||
}
|
||||
|
||||
void UServerTransport::HandleRootObjectResponse(const FString& RootObjSerialized, TScriptInterface<ITransport> TargetTransport, const FString& ObjectId) const
|
||||
|
||||
void UServerTransport::InvokeOnError(FString& Message) const
|
||||
{
|
||||
TSharedPtr<FJsonObject> RootObj;
|
||||
if(!LoadJson(RootObjSerialized, RootObj))
|
||||
{
|
||||
FString Message = FString::Printf( TEXT("A Root Object %s was recieved but was invalid and could not be deserialied"), *ObjectId);
|
||||
InvokeOnError(Message);
|
||||
return;
|
||||
}
|
||||
|
||||
TargetTransport->SaveObject(ObjectId, RootObj);
|
||||
|
||||
// Find children are not already in the target transport
|
||||
const auto Closures = RootObj->GetObjectField("__closure")->Values;
|
||||
|
||||
TArray<FString> ChildrenIds;
|
||||
Closures.GetKeys(ChildrenIds);
|
||||
TArray<FString> NewChildrenIds;
|
||||
for(const FString& Id : ChildrenIds)
|
||||
{
|
||||
if(TargetTransport->HasObject(Id)) continue;
|
||||
|
||||
NewChildrenIds.Add(Id);
|
||||
}
|
||||
|
||||
FetchChildren(TargetTransport, ObjectId, NewChildrenIds);
|
||||
ensureAlwaysMsgf(this->OnError.ExecuteIfBound(Message), TEXT("ServerTransport: Unhandled error - %s"), *Message);
|
||||
}
|
||||
|
||||
int32 UServerTransport::SplitLines(const FString& Content, TArray<FString>& OutLines)
|
||||
@@ -231,14 +246,9 @@ int32 UServerTransport::SplitLines(const FString& Content, TArray<FString>& OutL
|
||||
return LineCount;
|
||||
}
|
||||
|
||||
bool UServerTransport::LoadJson(const FString& ObjectJson, TSharedPtr<FJsonObject>& OutJsonObject)
|
||||
bool UServerTransport::LoadJson(const FString& StringJson, TSharedPtr<FJsonObject>& OutJsonObject)
|
||||
{
|
||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ObjectJson);
|
||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(StringJson);
|
||||
return FJsonSerializer::Deserialize(Reader, OutJsonObject);
|
||||
}
|
||||
|
||||
void UServerTransport::InvokeOnError(FString& Message) const
|
||||
{
|
||||
ensureAlwaysMsgf(this->OnError.ExecuteIfBound(Message), TEXT("ServerTransport: Unhandled error - %s"), *Message);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
//#pragma once
|
||||
|
||||
|
||||
//#include "CoreMinimal.h"
|
||||
|
||||
//#include "Client.generated.h"
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// */
|
||||
// UCLASS()
|
||||
// class SPECKLEUNREAL_API UClient : public UClass
|
||||
// {
|
||||
// GENERATED_BODY()
|
||||
//
|
||||
// public:
|
||||
//
|
||||
// static int DefaultBranchLimit = 10;
|
||||
//
|
||||
// //UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
// //void GetAccounts();
|
||||
//
|
||||
// //UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
// //Branch StreamGetBranches(FString StreamID, int BranchesLimit = DefaultBranchLimit, int CommitLimit = DefaultBranchLimit);
|
||||
//
|
||||
//
|
||||
// };
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Interfaces/IHttpRequest.h"
|
||||
|
||||
DECLARE_DELEGATE_OneParam(FErrorDelegate, const FString&);
|
||||
DECLARE_DELEGATE_OneParam(FAPIResponceDelegate, const FString&);
|
||||
|
||||
/**
|
||||
* C++ wrapper of Speckle's GraphQL API
|
||||
* Encapsulates sending HTTP requests with GraphQL Queries for specific resources
|
||||
*
|
||||
* Used to send GraphQL requests for commits, branches, streams, user info, collaborators etc.
|
||||
*
|
||||
* See `API/Operations` for usage examples
|
||||
*/
|
||||
class FClientAPI
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Creates and sends a graphql query to the specified url.
|
||||
* Will check HTTP response for errors,
|
||||
* and unpack the response "data" object.
|
||||
* @param ServerUrl URL of the Speckle Server endpoint
|
||||
* @param AuthToken
|
||||
* @param ResponsePropertyName Property name of the requested object. This will be the first name in the query (e.g. "stream", "streams", "user", etc)
|
||||
* @param PostPayload The POST payload containing the GraphQL request
|
||||
* @param RequestLogName Friendly name for this request (for error handling and logging)
|
||||
* @param OnCompleteAction Callback invoked on successful completion of the request, with the request object (as a JSON string)
|
||||
* @param OnErrorAction Callback invoked on any fatal error with, the error message.
|
||||
*/
|
||||
static void MakeGraphQLRequest(const FString& ServerUrl, const FString& AuthToken, const FString& ResponsePropertyName,
|
||||
const FString& PostPayload, const FString& RequestLogName,
|
||||
const FAPIResponceDelegate OnCompleteAction, const FErrorDelegate OnErrorAction);
|
||||
|
||||
|
||||
|
||||
//protected:
|
||||
|
||||
/// Helper function for creating post requests with a GraphQL query
|
||||
static FHttpRequestRef CreatePostRequest(const FString& ServerUrl, FString AuthToken, const FString& PostPayload,
|
||||
const FString& Encoding = TEXT("gzip"));
|
||||
|
||||
static bool GetResponseAsJSON(const FHttpResponsePtr Response, const FString& RequestLogName, TSharedPtr<FJsonObject>& OutObject, const TFunctionRef<void(const FString& Message)> OnErrorAction);
|
||||
|
||||
static bool CheckForOperationErrors(const TSharedPtr<FJsonObject> GraphQLResponse, FString& OutErrorMessage);
|
||||
static bool CheckRequestFailed(bool bWasSuccessful, FHttpResponsePtr Response, const FString& RequestName, const TFunctionRef<void(const FString& Message)> OnErrorAction);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SpeckleCommit.h"
|
||||
#include "SpeckleBranch.generated.h"
|
||||
|
||||
/*
|
||||
* GraphQL model for Branch data
|
||||
* Properties are only when they explicitly requested (through the GraphQL request)
|
||||
* see https://github.com/specklesystems/speckle-sharp/blob/main/Core/Core/Api/GraphQL/Models.cs
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleBranch
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString ID;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Description;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FSpeckleCommits Commits;
|
||||
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleBranches
|
||||
{
|
||||
GENERATED_BODY();
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
int32 TotalCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Cursor;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
TArray<FSpeckleBranch> Items;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SpeckleCollaborator.generated.h"
|
||||
|
||||
/*
|
||||
* GraphQL model for Collaborator data
|
||||
* Properties are only when they explicitly requested (through the GraphQL request)
|
||||
* see https://github.com/specklesystems/speckle-sharp/blob/main/Core/Core/Api/GraphQL/Models.cs
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleCollaborator
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Id;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Role;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Avatar;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SpeckleCommit.generated.h"
|
||||
|
||||
/*
|
||||
* GraphQL model for Commit data
|
||||
* Properties are only when they explicitly requested (through the GraphQL request)
|
||||
* see https://github.com/specklesystems/speckle-sharp/blob/main/Core/Core/Api/GraphQL/Models.cs
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleCommit
|
||||
{
|
||||
GENERATED_BODY();
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString ID;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Message;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString BranchName;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString AuthorName;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString AuthorId;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString AuthorAvatar;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString CreatedAt;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString SourceApplication;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models", DisplayName="Referenced Object Id")
|
||||
FString ReferencedObject;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString TotalChildrenCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
TArray<FString> Parents;
|
||||
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleCommits
|
||||
{
|
||||
GENERATED_BODY();
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
int32 TotalCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Cursor;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
TArray<FSpeckleCommit> Items;
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SpeckleBranch.h"
|
||||
#include "SpeckleCollaborator.h"
|
||||
|
||||
#include "SpeckleStream.generated.h"
|
||||
|
||||
/*
|
||||
* GraphQL model for Stream data
|
||||
* Properties are only when they explicitly requested (through the GraphQL request)
|
||||
* see https://github.com/specklesystems/speckle-sharp/blob/main/Core/Core/Api/GraphQL/Models.cs
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleStream
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models", DisplayName="Stream Id")
|
||||
FString ID;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FString Description;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
bool IsPublic;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FString Role;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FString CreatedAt;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FString UpdatedAt;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FString FavoritedDate;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
TArray<FSpeckleCollaborator> Collaborators;
|
||||
|
||||
// Object properties are only set if explicitly requested by a custom GraphQL request
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FSpeckleBranches Branches;
|
||||
|
||||
// Object properties are only set if explicitly requested by a custom GraphQL request
|
||||
UPROPERTY(BlueprintReadOnly, DisplayName="Branch (Request Only)", Category="Speckle|API Models", AdvancedDisplay)
|
||||
FSpeckleBranch Branch;
|
||||
|
||||
// Object properties are only set if explicitly requested by a custom GraphQL request
|
||||
UPROPERTY(BlueprintReadOnly, DisplayName="Commit (Request Only)", Category="Speckle|API Models", AdvancedDisplay)
|
||||
FSpeckleCommit Commit;
|
||||
|
||||
// Object properties are only set if explicitly requested by a custom GraphQL request
|
||||
UPROPERTY(BlueprintReadOnly, Category="Speckle|API Models")
|
||||
FSpeckleCommits Commits;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleStreams
|
||||
{
|
||||
GENERATED_BODY();
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
int32 TotalCount;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Cursor;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
TArray<FSpeckleStream> Items;
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "SpeckleStream.h"
|
||||
#include "SpeckleUser.generated.h"
|
||||
|
||||
/*
|
||||
* GraphQL model for User data
|
||||
* Properties are only when they explicitly requested (through the GraphQL request)
|
||||
* see https://github.com/specklesystems/speckle-sharp/blob/main/Core/Core/Api/GraphQL/Models.cs
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FSpeckleUser
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Id;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Email;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Bio;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Company;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Avatar;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FString Role;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FSpeckleStreams Streams;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="Speckle|API Models")
|
||||
FSpeckleStreams FavoriteStreams;
|
||||
};
|
||||
@@ -39,7 +39,8 @@ public:
|
||||
FRecieveOperationHandler OnError;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, Category = "Speckle|Operations", meta = (WorldContext = "WorldContextObject"))
|
||||
static UReceiveOperation* ReceiveOperation(UObject* WorldContextObject, const FString& ObjectId, TScriptInterface<ITransport> RemoteTransport, TScriptInterface<ITransport> LocalTransport);
|
||||
static UReceiveOperation* ReceiveOperation(UObject* WorldContextObject, const FString& ObjectId,
|
||||
TScriptInterface<ITransport> RemoteTransport, TScriptInterface<ITransport> LocalTransport);
|
||||
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintAsyncActionBase.h"
|
||||
|
||||
#include "SpeckleStreamAPIOperation.generated.h"
|
||||
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FAPIResponseHandler, FString, JsonResponse, FString, ErrorMessage);
|
||||
|
||||
/**
|
||||
* Allows sending GraphQL queries to the Speckle GraphQL API
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API USpeckleStreamAPIOperation : public UBlueprintAsyncActionBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FAPIResponseHandler OnReceiveSuccessfully;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FAPIResponseHandler OnError;
|
||||
|
||||
/**
|
||||
* Sends an GraphQL queries to a speckle server's "/graphql" endpoint
|
||||
* E.g. for fetching stream/branch/commit/user info.
|
||||
*
|
||||
* @param ServerUrl The URL of the Speckle server
|
||||
* @param AuthToken The auth token of the user (optional for unprivilaged requests)
|
||||
* @param GraphQlQuery The GraphQl query (starting with `query{ }`)
|
||||
* @param ResponsePropertyName The name of the root property being requested. (e.g. for a "query{user{...}" query, then the property name is "user")
|
||||
* @param RequestLogName A friendly name for this request (logging and analytics)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly, Category = "Speckle|Operations")
|
||||
static USpeckleStreamAPIOperation* SpeckleStreamAPIOperation(
|
||||
const FString& ServerUrl,
|
||||
const FString& AuthToken,
|
||||
const FString& GraphQlQuery,
|
||||
const FString& ResponsePropertyName,
|
||||
const FString& RequestLogName);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
protected:
|
||||
void Request();
|
||||
|
||||
FString ServerUrl;
|
||||
FString AuthToken;
|
||||
FString Query;
|
||||
FString RequestLogName;
|
||||
FString ResponsePropertyName;
|
||||
|
||||
void HandleReceive(const FString& ResponseJson);
|
||||
|
||||
void HandleError(const FString& Message);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
#include "SpeckleAPIFunctions.generated.h"
|
||||
|
||||
struct FSpeckleCommit;
|
||||
/**
|
||||
* Blueprint function library for Speckle API
|
||||
*/
|
||||
UCLASS()
|
||||
class SPECKLEUNREAL_API USpeckleAPIFunctions : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, CustomThunk, Category="Speckle|API", meta=(CustomStructureParam="OutStruct", ExpandBoolAsExecs="ReturnValue"))
|
||||
static UPARAM(DisplayName = "WasSucessful") bool DeserializeResponse(const FString& JsonString, int32& OutStruct);
|
||||
DECLARE_FUNCTION(execDeserializeResponse);
|
||||
|
||||
static bool GenericDeserializeResponse(const FString& JsonString, const UScriptStruct* StructType, void* OutStruct);
|
||||
|
||||
static void CommitReceived(FSpeckleCommit& Commit);
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
|
||||
|
||||
#include "SpeckleSerializer.generated.h"
|
||||
|
||||
class UBase;
|
||||
@@ -21,6 +22,5 @@ public:
|
||||
static UBase* DeserializeBaseById(const FString& ObjectId, const TScriptInterface<ITransport> ReadTransport);
|
||||
|
||||
static UBase* DeserializeBase(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport);
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -41,7 +41,9 @@ public:
|
||||
// Sets default values for this actor's properties
|
||||
UProceduralMeshConverter();
|
||||
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World, TScriptInterface<ISpeckleConverter>& AvailableConverters) override;
|
||||
virtual UObject* ConvertToNative_Implementation(const UBase* SpeckleBase, UWorld* World,
|
||||
TScriptInterface<ISpeckleConverter>& AvailableConverters) override;
|
||||
|
||||
virtual UBase* ConvertToSpeckle_Implementation(const UObject* Object) override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
@@ -50,6 +52,7 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category="ToNative")
|
||||
virtual UMesh* MeshToSpeckle(const UProceduralMeshComponent* Object);
|
||||
|
||||
virtual AActor* CreateEmptyActor(UWorld* World, const FTransform& Transform, const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
|
||||
virtual AActor* CreateEmptyActor(UWorld* World, const FTransform& Transform,
|
||||
const FActorSpawnParameters& SpawnParameters = FActorSpawnParameters());
|
||||
|
||||
};
|
||||
|
||||
@@ -33,12 +33,12 @@ public:
|
||||
|
||||
// Converts the given Base and all children into native actors.
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
UPARAM(DisplayName = "RootActor") AActor* RecursivelyConvertToNative(AActor* AOwner, const UBase* Base, const TScriptInterface<ITransport>& LocalTransport, bool DisplayProgressBar, TArray<AActor*>& OutActors);
|
||||
|
||||
|
||||
UPARAM(DisplayName = "RootActor") AActor* RecursivelyConvertToNative(AActor* AOwner, const UBase* Base,
|
||||
const TScriptInterface<ITransport>& LocalTransport, bool DisplayProgressBar, TArray<AActor*>& OutActors);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle|Conversion")
|
||||
virtual void FinishConversion();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual AActor* RecursivelyConvertToNative_Internal(AActor* AOwner, const UBase* Base, const TScriptInterface<ITransport>& LocalTransport, FSlowTask* Task, TArray<AActor*>& OutActors);
|
||||
|
||||
@@ -14,7 +14,8 @@ protected:
|
||||
static const FString VersionedApplicationName;
|
||||
|
||||
public:
|
||||
static void TrackEvent(const FString& Email, const FString& Server, const FString& EventName);
|
||||
static void TrackEvent(const FString& Server, const FString& EventName);
|
||||
static void TrackEvent(const FString& Server, const FString& EventName, const TMap<FString, FString>& CustomProperties);
|
||||
static void TrackEvent(const FString& Email, const FString& Server, const FString& EventName, const TMap<FString, FString>& CustomProperties);
|
||||
static FString Hash(const FString& Input);
|
||||
};
|
||||
};
|
||||
@@ -13,7 +13,7 @@ class ASpeckleUnrealManager;
|
||||
/**
|
||||
* Base type that all Object Models inherit from
|
||||
*/
|
||||
UCLASS(BlueprintType, meta=(ScriptName="Base (Speckle.Objects)"))
|
||||
UCLASS(BlueprintType, meta=(DisplayName="Base (Speckle.Objects)"))
|
||||
class SPECKLEUNREAL_API UBase : public UObject
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -42,5 +42,10 @@ public:
|
||||
virtual bool Parse(const TSharedPtr<FJsonObject> Obj, const TScriptInterface<ITransport> ReadTransport) override;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
/// If not already so, this method will align vertices
|
||||
/// such that a vertex and its corresponding texture coordinates have the same index.
|
||||
/// See "https://github.com/specklesystems/speckle-sharp/blob/main/Objects/Objects/Geometry/Mesh.cs"
|
||||
virtual void AlignVerticesWithTexCoordsByIndex();
|
||||
};
|
||||
|
||||
@@ -44,6 +44,11 @@ public:
|
||||
UFUNCTION(BlueprintPure, Category="Speckle/ObjectUtils")
|
||||
static FTransform CreateTransform(UPARAM(ref) const FMatrix& TransformMatrix);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle/ObjectUtils")
|
||||
static AActor* SpawnActorInWorld(const TSubclassOf<AActor> Class, UWorld* World, UPARAM(ref) const FTransform& Transform);
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle/ObjectUtils", meta=(CallableWithoutWorldContext, WorldContext="WorldContextObject", DeterminesOutputType="Class", DynamicOutputParam="ReturnValue"))
|
||||
static AActor* SpawnActorInWorld(const UObject* WorldContextObject, const TSubclassOf<AActor> Class, UPARAM(ref) const FTransform& Transform);
|
||||
|
||||
// Helper function to print a json obj to console
|
||||
static FString DisplayAsString(const FString& msg, const TSharedPtr<FJsonObject> Obj);
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "API/Models/SpeckleStream.h"
|
||||
|
||||
#include "ReceiveSelectionComponent.generated.h"
|
||||
|
||||
/**
|
||||
* Actor component for selecting a Speckle Stream + Object
|
||||
* Either by Stream Id + Object Id (Object selection mode)
|
||||
* Or by Stream + Branch + Commit (Commit selection mode)
|
||||
*/
|
||||
UCLASS(ClassGroup=(Speckle), meta=(BlueprintSpawnableComponent))
|
||||
class SPECKLEUNREAL_API UReceiveSelectionComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
// URL of the speckle server
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle")
|
||||
FString ServerUrl = "https://speckle.xyz";
|
||||
|
||||
// A Personal Access Token can be created from your Speckle Profile page (Treat tokens like passwords, do not share publicly)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", meta=(PasswordField=true))
|
||||
FString AuthToken;
|
||||
|
||||
// Id of the stream to receive from (checkout the stream URL in the Speckle web viewer)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", meta=(EditCondition="bManualMode", EditConditionHides, DisplayAfter=bManualMode))
|
||||
FString StreamId;
|
||||
|
||||
// Id of the Speckle Object to receive (checkout the properties panel in the Speckle web viewer)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", meta=(EditCondition="bManualMode", EditConditionHides, DisplayAfter=StreamId))
|
||||
FString ObjectId;
|
||||
|
||||
/**
|
||||
* Trys to get the currently selected Commit (Commit selection mode)
|
||||
* @param OutCommit the selected Commit (if return was true)
|
||||
* @returns true if a selection was made
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
bool TryGetSelectedCommit(FSpeckleCommit& OutCommit) const;
|
||||
|
||||
/**
|
||||
* Trys to get the currently selected Branch (Commit selection mode)
|
||||
* @param OutBranch the selected Branch (if return was true)
|
||||
* @returns true if a selection was made
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
bool TryGetSelectedBranch(FSpeckleBranch& OutBranch) const;
|
||||
|
||||
/**
|
||||
* Trys to get the currently selected Stream (Commit selection mode)
|
||||
* @param OutStream the selected Stream (if return was true)
|
||||
* @returns true if a selection was made
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
bool TryGetSelectedStream(FSpeckleStream& OutStream) const;
|
||||
|
||||
/**
|
||||
* @param OutStatusMessage the current status of the object selection as a human readable string
|
||||
* @returns true if a complete object selection is made.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle", CallInEditor)
|
||||
bool IsSelectionComplete(FString& OutStatusMessage) const;
|
||||
|
||||
// Gets the URL of the current selection/partial selection
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle", CallInEditor)
|
||||
FString GetUrl() const;
|
||||
|
||||
/**
|
||||
* Launches the URL of the current selection/partial selection using FPlatformProcess
|
||||
* @returns true if the URL could be launched
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle", CallInEditor)
|
||||
void OpenURLInBrowser() const;
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif
|
||||
|
||||
virtual void PropertyChangeHandler(const FName& PropertyName);
|
||||
|
||||
protected: //Internal logic for branch/stream/commit fetching and selection
|
||||
|
||||
// When true, user specifies stream id + object id, when false, user specifies stream + branch + commit
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="Speckle", meta=(DisplayName="Specify by Object Id", DisplayAfter=AuthToken));
|
||||
bool bManualMode = true;
|
||||
|
||||
// Limit of how many streams/branches/commits to fetch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", AdvancedDisplay, meta=(DisplayName="Options Limit", ClampMin=0, ClampMax=100, UIMin = 1, UIMax=30))
|
||||
int32 Limit = 15;
|
||||
|
||||
// Refreshes the Stream/Branch/Commit options
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle", CallInEditor)
|
||||
void Refresh();
|
||||
|
||||
#pragma region Stream
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="Speckle", meta=(DisplayName="Stream", GetOptions=GetStreamsOptions, EditCondition="IsAccountValid && !bManualMode", NoResetToDefault))
|
||||
FString SelectedStreamText;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient, Category="Speckle")
|
||||
TMap<FString, FSpeckleStream> Streams;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual TArray<FString> GetStreamsOptions() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
bool SelectStream(const FString& DisplayId);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual void UpdateStreams();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual FSpeckleStream GetSelectedStream() const;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient, Category="Speckle")
|
||||
bool IsStreamValid = false;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient, Category="Speckle")
|
||||
bool IsAccountValid = false;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Branch
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="Speckle", meta=(DisplayName="Branch", GetOptions=GetBranchOptions, EditCondition="IsStreamValid && !bManualMode", NoResetToDefault))
|
||||
FString SelectedBranchText;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient, Category="Speckle")
|
||||
TMap<FString, FSpeckleBranch> Branches;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual TArray<FString> GetBranchOptions();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
bool SelectBranch(const FString& DisplayId);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual void UpdateBranches();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual FSpeckleBranch GetSelectedBranch() const;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient, Category="Speckle")
|
||||
bool IsBranchValid = false;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Commit
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="Speckle", meta=(DisplayName="Commit", GetOptions=GetCommitOptions, EditCondition="IsCommitValid && !bManualMode", NoResetToDefault))
|
||||
FString SelectedCommitText;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient, Category="Speckle")
|
||||
TMap<FString, FSpeckleCommit> Commits;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual TArray<FString> GetCommitOptions();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
bool SelectCommit(const FString& DisplayId);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual void UpdateCommits();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Speckle")
|
||||
virtual FSpeckleCommit GetSelectedCommit() const;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Transient, Category="Speckle")
|
||||
bool IsCommitValid = false;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
static void LogError(const FString& Message, const FString LogName);
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
class UServerTransport;
|
||||
class ITransport;
|
||||
class USpeckleConverterComponent;
|
||||
class UReceiveSelectionComponent;
|
||||
class FJsonObject;
|
||||
|
||||
/**
|
||||
@@ -19,20 +20,10 @@ class SPECKLEUNREAL_API ASpeckleUnrealManager : public AActor
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UPROPERTY(VisibleAnywhere, Category="Speckle", BlueprintReadWrite)
|
||||
UReceiveSelectionComponent* ReceiveSelection;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle")
|
||||
FString ServerUrl;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle")
|
||||
FString StreamID;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle")
|
||||
FString ObjectID;
|
||||
|
||||
// A Personal Access Token can be created from your Speckle Profile page (Treat tokens like passwords, do not share publicly) - Required for receiving private streams
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", meta=(PasswordField = true))
|
||||
FString AuthToken;
|
||||
|
||||
// When true, will call `Receive` on BeginPlay
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", AdvancedDisplay)
|
||||
bool ImportAtRuntime;
|
||||
@@ -45,7 +36,7 @@ public:
|
||||
UPROPERTY(VisibleAnywhere, Category="Speckle", BlueprintReadWrite)
|
||||
USpeckleConverterComponent* Converter;
|
||||
|
||||
// Used to stagger transport requests, useful when making requests for a large number of child objects
|
||||
// When true, will display an editor progress bar while receiving objects
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Speckle", AdvancedDisplay)
|
||||
bool DisplayProgressBar;
|
||||
|
||||
@@ -59,6 +50,7 @@ public:
|
||||
// Deletes the Actors created by the previous receive operation
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category="Speckle" , meta=(DisplayAfter="Receive"))
|
||||
virtual void DeleteObjects();
|
||||
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
|
||||
@@ -23,8 +23,13 @@ public:
|
||||
virtual void SaveObject(const FString& ObjectId, const TSharedPtr<FJsonObject> SerializedObject) override;
|
||||
|
||||
virtual bool HasObject(const FString& ObjectId) const override;
|
||||
virtual void CopyObjectAndChildren(const FString& ObjectId, TScriptInterface<ITransport> TargetTransport, const FTransportCopyObjectCompleteDelegate& OnCompleteAction, const FTransportErrorDelegate& OnErrorAction) override { unimplemented(); }
|
||||
|
||||
|
||||
virtual void CopyObjectAndChildren(const FString& ObjectId,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction) override { unimplemented(); }
|
||||
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Speckle|Transports")
|
||||
static UMemoryTransport* CreateEmptyMemoryTransport()
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Transport.h"
|
||||
#include "API/Models/SpeckleStream.h"
|
||||
|
||||
#include "ServerTransport.generated.h"
|
||||
|
||||
@@ -26,29 +27,34 @@ struct FObjectIdRequest
|
||||
UCLASS(BlueprintType)
|
||||
class SPECKLEUNREAL_API UServerTransport : public UObject, public ITransport
|
||||
{
|
||||
GENERATED_BODY()
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
UPROPERTY()
|
||||
FString ServerUrl;
|
||||
|
||||
UPROPERTY()
|
||||
FString StreamId;
|
||||
|
||||
UPROPERTY(meta=(PasswordField))
|
||||
FString AuthToken;
|
||||
|
||||
UPROPERTY()
|
||||
int32 MaxNumberOfObjectsPerRequest = 20000;
|
||||
|
||||
|
||||
FTransportCopyObjectCompleteDelegate OnComplete;
|
||||
FTransportErrorDelegate OnError;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Speckle|Transports")
|
||||
static UServerTransport* CreateServerTransport(UPARAM(ref) FString& _ServerUrl, UPARAM(ref) FString& _StreamId, UPARAM(ref) FString& _AuthToken)
|
||||
static UServerTransport* CreateServerTransport(const FString& _ServerUrl,
|
||||
const FString& _StreamId,
|
||||
const FString& _AuthToken)
|
||||
{
|
||||
UServerTransport* Transport = NewObject<UServerTransport>();
|
||||
Transport->ServerUrl = _ServerUrl;
|
||||
@@ -63,13 +69,15 @@ public:
|
||||
virtual bool HasObject(const FString& ObjectId) const override;
|
||||
|
||||
virtual void CopyObjectAndChildren(const FString& ObjectId,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction) override;
|
||||
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction) override;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void HandleRootObjectResponse(const FString& RootObjSerialized, TScriptInterface<ITransport> TargetTransport, const FString& ObjectId) const;
|
||||
virtual void HandleRootObjectResponse(const FString& RootObjSerialized,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FString& ObjectId) const;
|
||||
|
||||
/**
|
||||
* Iteratively fetches chunks of children
|
||||
@@ -79,15 +87,15 @@ protected:
|
||||
* @param CStart the index in ChildrenIds of the start point of the current chunk
|
||||
*/
|
||||
virtual void FetchChildren(TScriptInterface<ITransport> TargetTransport,
|
||||
const FString& RootObjectId,
|
||||
const FString& RootObjectId,
|
||||
const TArray<FString>& ChildrenIds,
|
||||
int32 CStart = 0) const;
|
||||
|
||||
virtual void InvokeOnError(FString& Message) const;
|
||||
|
||||
static bool LoadJson(const FString& ObjectJson, TSharedPtr<FJsonObject>& OutJsonObject);
|
||||
|
||||
static int32 SplitLines(const FString& Content, TArray<FString>& OutLines);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ class SPECKLEUNREAL_API ITransport
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
virtual void SaveObject(const FString& ObjectId, const TSharedPtr<FJsonObject> SerializedObject) = 0;
|
||||
|
||||
//virtual void SaveObjectFromTransport(FString& ObjectID, TScriptInterface<ITransport> SourceTransport) = 0;
|
||||
@@ -37,8 +38,8 @@ public:
|
||||
virtual bool HasObject(const FString& ObjectId) const = 0;
|
||||
|
||||
virtual void CopyObjectAndChildren(const FString& ObjectId,
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction) = 0;
|
||||
TScriptInterface<ITransport> TargetTransport,
|
||||
const FTransportCopyObjectCompleteDelegate& OnCompleteAction,
|
||||
const FTransportErrorDelegate& OnErrorAction) = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
|
||||
#include "Conversion/ConverterFactory.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#include "SpeckleUnrealEditorModule.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
// Copyright 2022 AEC Systems, Licensed under the Apache License, Version 2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "2.5.2",
|
||||
"VersionName": "2.6.0",
|
||||
"FriendlyName": "Speckle Unreal",
|
||||
"Description": "Speckle is an open source data platform for Architecture, Engineering, and Construction. It has been open sourced under the MIT license, is customizable, and available in the cloud or via a self-hosted server. It allows users to exchange data between various AEC modelling and content creation platforms.",
|
||||
"Category": "AEC",
|
||||
"CreatedBy": "Speckle",
|
||||
"CreatedByURL": "https://speckle.systems/",
|
||||
"DocsURL": "https://github.com/specklesystems/speckle-unreal",
|
||||
"MarketplaceURL": "",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/98770ce9d4f143de8dd7882a707a6f81",
|
||||
"SupportURL": "https://speckle.community/",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
@@ -19,7 +19,7 @@
|
||||
"Name": "SpeckleUnreal",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac", "IOS", "Android" ]
|
||||
"WhitelistPlatforms": [ "Win64", "Linux", "Mac" ]
|
||||
},
|
||||
{
|
||||
"Name": "SpeckleUnrealEditor",
|
||||
|
||||
Reference in New Issue
Block a user