Skip to content

Commit

Permalink
feat: added analytics (#86)
Browse files Browse the repository at this point in the history
  • Loading branch information
YermekG authored May 23, 2024
1 parent 73c457b commit aa7a00d
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 3 deletions.
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

0 comments on commit aa7a00d

Please sign in to comment.