Skip to content

[DX-2828] feat: added analytics #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions Source/Immutable/Private/Immutable/ImmutableAnalytics.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "ImmutableAnalytics.h"

#include "JsonDomBuilder.h"

#define EVENT_DATA_SUCCESS "succeeded"


void UImmutableAnalytics::Track(EEventName EventName)
{
check(JSConnector.IsValid());

FString FullData;

FJsonObjectConverter::UStructToJsonObjectString<FEventData>({ "unrealSdk", GetEventName(EventName), "" }, FullData);

JSConnector->CallJS(ImmutablePassportAction::TRACK, FullData, FImtblJSResponseDelegate::CreateUObject(this, &UImmutableAnalytics::OnTrackResponse));
}

void UImmutableAnalytics::Track(EEventName EventName, bool Success)
{
TMap<FString, FString> EventData = {{EVENT_DATA_SUCCESS, Success ? TEXT("true") : TEXT("false") } };
Track(EventName, EventData);
}

void UImmutableAnalytics::Track(EEventName EventName, TMap<FString, FString>& EventData)
{
check(JSConnector.IsValid());

FJsonDomBuilder::FObject Object;

for (auto data : EventData)
{
Object.Set(data.Key, data.Value);
}

FString FullData;

FJsonObjectConverter::UStructToJsonObjectString<FEventData>({ "unrealSdk", GetEventName(EventName), Object.ToString() }, FullData);

JSConnector->CallJS(ImmutablePassportAction::TRACK, FullData, FImtblJSResponseDelegate::CreateUObject(this, &UImmutableAnalytics::OnTrackResponse));
}

void UImmutableAnalytics::Setup(TWeakObjectPtr<UImtblJSConnector> Connector)
{
IMTBL_LOG_FUNCSIG

if (!Connector.IsValid())
{
IMTBL_ERR("Invalid JSConnector passed to UImmutableAnalytics::Setup.")
return;
}

JSConnector = Connector.Get();
}

void UImmutableAnalytics::OnTrackResponse(FImtblJSResponse Response)
{
// Currently, we ignore tracking errors. Edit if it is required
}

FString UImmutableAnalytics::GetEventName(EEventName EventName)
{
switch(EventName)
{
#define CONVERT(EventName, EventNameString) case EEventName::EventName: return EventNameString;
EVENT_NAME_LIST
#undef CONVERT
default:
return "";
}
}
78 changes: 78 additions & 0 deletions Source/Immutable/Private/Immutable/ImmutableAnalytics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#pragma once

#include "Immutable/ImmutablePassport.h"

#include "ImmutableAnalytics.generated.h"

USTRUCT(BlueprintType)
struct FEventData
{
GENERATED_BODY()

UPROPERTY()
FString moduleName;

UPROPERTY()
FString eventName;

UPROPERTY()
FString properties;
};

/**
* Immutable bridge sdk analytics utility
*/
UCLASS()
class IMMUTABLE_API UImmutableAnalytics : public UObject
{
GENERATED_BODY()

public:

/**
* Event names to track
*/
#define EVENT_NAME_LIST \
CONVERT(INIT_PASSPORT, "initialisedPassport") \
CONVERT(START_LOGIN, "startedLogin") \
CONVERT(COMPLETE_LOGIN, "performedLogin") \
CONVERT(START_LOGIN_PKCE, "startedLoginPkce") \
CONVERT(COMPLETE_LOGIN_PKCE, "performedLoginPkce") \
CONVERT(COMPLETE_RELOGIN, "performedRelogin") \
CONVERT(START_CONNECT_IMX, "startedConnectImx") \
CONVERT(COMPLETE_CONNECT_IMX, "performedConnectImx") \
CONVERT(START_CONNECT_IMX_PKCE, "startedConnectImxPkce") \
CONVERT(COMPLETE_CONNECT_IMX_PKCE, "performedConnectImxPkce") \
CONVERT(COMPLETE_RECONNECT, "performedReconnect") \
CONVERT(COMPLETE_LOGOUT, "performedLogout") \
CONVERT(COMPLETE_LOGOUT_PKCE, "performedLogoutPkce")

enum class EEventName: uint8
{
#define CONVERT(name, nameString) name,
EVENT_NAME_LIST
#undef CONVERT
};

public:
void Setup(TWeakObjectPtr<class UImtblJSConnector> Connector);
/**
* Performs the call to game bridge track method
* @param EventName Name that will be tracked
* @param Success Single event data record that track "succeeded" field
* @param EventData Map with customed data, converted to json object
*/
void Track(EEventName EventName);
void Track(EEventName EventName, bool Success);
void Track(EEventName EventName, TMap<FString, FString>& EventData);

private:
// Convert enum to string
FString GetEventName(EEventName EventName);
// Callback method for Track from bridge
void OnTrackResponse(FImtblJSResponse Response);

private:
TWeakObjectPtr<class UImtblJSConnector> JSConnector;

};
22 changes: 19 additions & 3 deletions Source/Immutable/Private/Immutable/ImmutablePassport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "Immutable/ImmutablePassport.h"

#include "ImmutableAnalytics.h"
#include "Immutable/Misc/ImtblLogging.h"
#include "Immutable/ImmutableResponses.h"
#include "Immutable/ImtblJSConnector.h"
Expand Down Expand Up @@ -57,6 +58,7 @@ void UImmutablePassport::Connect(bool IsConnectImx, bool TryToRelogin, const FIm
}
else
{
Analytics->Track(IsConnectImx ? UImmutableAnalytics::EEventName::START_CONNECT_IMX : UImmutableAnalytics::EEventName::START_LOGIN);
CallJS(ImmutablePassportAction::INIT_DEVICE_FLOW, TEXT(""), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnInitDeviceFlowResponse));
}
}
Expand All @@ -70,6 +72,7 @@ void UImmutablePassport::ConnectPKCE(bool IsConnectImx, const FImtblPassportResp
SetStateFlags(IPS_IMX);
}
PKCEResponseDelegate = ResponseDelegate;
Analytics->Track(IsConnectImx ? UImmutableAnalytics::EEventName::START_CONNECT_IMX_PKCE : UImmutableAnalytics::EEventName::START_LOGIN_PKCE);
CallJS(ImmutablePassportAction::GetPKCEAuthUrl, TEXT(""), PKCEResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnGetPKCEAuthUrlResponse));
}
#endif
Expand Down Expand Up @@ -210,11 +213,15 @@ void UImmutablePassport::Setup(const TWeakObjectPtr<UImtblJSConnector> Connector

if (!Connector.IsValid())
{
IMTBL_ERR("Invalid JSConnector passed to UImmutablePassport::Initialize.")
IMTBL_ERR("Invalid JSConnector passed to UImmutablePassport::Setup.")
return;
}

JSConnector = Connector.Get();

// Analytics
Analytics = NewObject<UImmutableAnalytics>(this);
Analytics->Setup(Connector);
}

void UImmutablePassport::ReinstateConnection(FImtblJSResponse Response)
Expand All @@ -224,15 +231,19 @@ void UImmutablePassport::ReinstateConnection(FImtblJSResponse Response)
if (auto ResponseDelegate = GetResponseDelegate(Response))
{
// currently, this response has to be called only for RELOGIN AND RECONNECT bridge routines
const FString CallbackName = (Response.responseFor.Compare(ImmutablePassportAction::RELOGIN, ESearchCase::IgnoreCase) == 0) ? "Relogin" : "Reconnect";
bool IsRelogin = Response.responseFor.Compare(ImmutablePassportAction::RELOGIN, ESearchCase::IgnoreCase) == 0;
const FString CallbackName = IsRelogin ? "Relogin" : "Reconnect";
UImmutableAnalytics::EEventName EventName = IsRelogin ? UImmutableAnalytics::EEventName::COMPLETE_RELOGIN : UImmutableAnalytics::EEventName::COMPLETE_RECONNECT;

if (Response.JsonObject->GetBoolField(TEXT("result")))
{
SetStateFlags(IPS_CONNECTED);
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{true, "", Response});
Analytics->Track(EventName, true);
}
else
{
Analytics->Track(EventName, false);
#if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC
if (IsStateFlagsSet(IPS_PKCE))
{
Expand Down Expand Up @@ -296,7 +307,7 @@ void UImmutablePassport::OnInitializeResponse(FImtblJSResponse Response)
IMTBL_ERR("Passport initialization failed.")
Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error"));
}

Analytics->Track(UImmutableAnalytics::EEventName::INIT_PASSPORT, Response.success);
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{Response.success, Msg, Response});
}
}
Expand Down Expand Up @@ -395,6 +406,7 @@ void UImmutablePassport::OnLogoutResponse(FImtblJSResponse Response)

return;
}
Analytics->Track(UImmutableAnalytics::EEventName::COMPLETE_LOGOUT);
Message = "Logged out";
IMTBL_LOG("%s", *Message)
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{ Response.success, Message });
Expand Down Expand Up @@ -498,6 +510,7 @@ void UImmutablePassport::OnConnectPKCEResponse(FImtblJSResponse Response)
ResetStateFlags(IPS_PKCE);
Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error"));
}
Analytics->Track(IsStateFlagsSet(IPS_IMX) ? UImmutableAnalytics::EEventName::COMPLETE_CONNECT_IMX_PKCE : UImmutableAnalytics::EEventName::COMPLETE_LOGIN_PKCE, Response.success);
PKCEResponseDelegate.ExecuteIfBound(FImmutablePassportResult{Response.success, Msg});
PKCEResponseDelegate = nullptr;

Expand Down Expand Up @@ -675,6 +688,7 @@ void UImmutablePassport::OnConfirmCodeResponse(FImtblJSResponse Response)
{
FString Msg;
FString TypeOfConnection = IsStateFlagsSet(IPS_IMX) ? TEXT("connect") : TEXT("login");
UImmutableAnalytics::EEventName EventName = IsStateFlagsSet(IPS_IMX) ? UImmutableAnalytics::EEventName::COMPLETE_CONNECT_IMX : UImmutableAnalytics::EEventName::COMPLETE_LOGIN;

ResetStateFlags(IPS_CONNECTING);
if (Response.success)
Expand All @@ -687,6 +701,7 @@ void UImmutablePassport::OnConfirmCodeResponse(FImtblJSResponse Response)
IMTBL_LOG("%s code not confirmed.", *TypeOfConnection)
Response.Error.IsSet() ? Msg = Response.Error->ToString() : Msg = Response.JsonObject->GetStringField(TEXT("error"));
}
Analytics->Track(EventName, Response.success);
ResponseDelegate->ExecuteIfBound(FImmutablePassportResult{Response.success, Msg, Response});
}
}
Expand Down Expand Up @@ -850,6 +865,7 @@ void UImmutablePassport::OnDeepLinkActivated(FString DeepLink)
{
FGraphEventRef GameThreadTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]()
{
Analytics->Track(UImmutableAnalytics::EEventName::COMPLETE_LOGOUT_PKCE);
PKCELogoutResponseDelegate.ExecuteIfBound(FImmutablePassportResult{true, "Logged out"});
PKCELogoutResponseDelegate = nullptr;
ResetStateFlags(IPS_CONNECTED | IPS_PKCE | IPS_IMX);
Expand Down
2 changes: 2 additions & 0 deletions Source/Immutable/Public/Immutable/ImmutableNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ namespace ImmutablePassportAction
const FString EnvProduction = TEXT("production");
const FString ImxIsRegisteredOffchain = TEXT("isRegisteredOffchain");
const FString ImxRegisterOffchain = TEXT("registerOffchain");

const FString TRACK = TEXT("track");
} // namespace ImmutablePassportAction
4 changes: 4 additions & 0 deletions Source/Immutable/Public/Immutable/ImmutablePassport.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,8 @@ class IMMUTABLE_API UImmutablePassport : public UObject
};

uint8 StateFlags = IPS_NONE;

UPROPERTY()
class UImmutableAnalytics* Analytics = nullptr;

};
1 change: 1 addition & 0 deletions Source/Immutable/Public/Immutable/ImtblJSConnector.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class IMMUTABLE_API UImtblJSConnector : public UObject
GENERATED_BODY()

friend class UImmutablePassport;
friend class UImmutableAnalytics;

public:
DECLARE_MULTICAST_DELEGATE(FOnBridgeReadyDelegate);
Expand Down
Loading