diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a969e95..99353dee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,19 @@ ## Unreleased +### Breaking Changes + +- User feedback API reworked: + - Feedback no longer needs to be associated with a specific event - the only required parameter is the user message + - `SentryUserFeedback` class replaced with `SentryFeedback` + - `CaptureUserFeedback` function in `SentrySubsystem` replaced with `CaptureFeedback` + - `CreateSentryUserFeedback` function in `SentryLibrary` replaced with `CreateSentryFeedback` +- On Windows and Linux, `ToString` function of `SentryId` class now returns the ID without dashes + ### Features - Add functionality to give/revoke user consent for crash uploads ([#1053](https://github.com/getsentry/sentry-unreal/pull/1053)) +- Add new API for capturing user feedback ([#1051](https://github.com/getsentry/sentry-unreal/pull/1051)) ### Dependencies diff --git a/plugin-dev/Source/Sentry/Private/Android/AndroidSentryFeedback.cpp b/plugin-dev/Source/Sentry/Private/Android/AndroidSentryFeedback.cpp new file mode 100644 index 000000000..f28c78ecd --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Android/AndroidSentryFeedback.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#include "AndroidSentryFeedback.h" + +#include "AndroidSentryId.h" + +#include "Infrastructure/AndroidSentryJavaClasses.h" + +FAndroidSentryFeedback::FAndroidSentryFeedback(const FString& message) + : FSentryJavaObjectWrapper(SentryJavaClasses::Feedback, "(Ljava/lang/String;)V", + *GetJString(message)) +{ + SetupClassMethods(); +} + +void FAndroidSentryFeedback::SetupClassMethods() +{ + GetMessageMethod = GetMethod("getMessage", "()Ljava/lang/String;"); + SetNameMethod = GetMethod("setName", "(Ljava/lang/String;)V"); + GetNameMethod = GetMethod("getName", "()Ljava/lang/String;"); + SetContactEmailMethod = GetMethod("setContactEmail", "(Ljava/lang/String;)V"); + GetContactEmailMethod = GetMethod("getContactEmail", "()Ljava/lang/String;"); + SetAssociatedEventMethod = GetMethod("setAssociatedEventId", "(Lio/sentry/protocol/SentryId;)V"); + GetAssociatedEventMethod = GetMethod("getAssociatedEventId", "()Lio/sentry/protocol/SentryId;"); +} + +FString FAndroidSentryFeedback::GetMessage() const +{ + return CallMethod(GetMessageMethod); +} + +void FAndroidSentryFeedback::SetName(const FString& name) +{ + CallMethod(SetNameMethod, *GetJString(name)); +} + +FString FAndroidSentryFeedback::GetName() const +{ + return CallMethod(GetNameMethod); +} + +void FAndroidSentryFeedback::SetContactEmail(const FString& email) +{ + CallMethod(SetContactEmailMethod, *GetJString(email)); +} + +FString FAndroidSentryFeedback::GetContactEmail() const +{ + return CallMethod(GetContactEmailMethod); +} + +void FAndroidSentryFeedback::SetAssociatedEvent(const FString& eventId) +{ + if (eventId.IsEmpty()) + return; + + TSharedPtr idAndroid = MakeShareable(new FAndroidSentryId(eventId)); + CallMethod(SetAssociatedEventMethod, idAndroid->GetJObject()); +} + +FString FAndroidSentryFeedback::GetAssociatedEvent() const +{ + auto idAndroid = CallObjectMethod(GetAssociatedEventMethod); + if (!idAndroid) + { + return FString(); + } + + TSharedPtr eventId = MakeShareable(new FAndroidSentryId(*idAndroid)); + return eventId->ToString(); +} diff --git a/plugin-dev/Source/Sentry/Private/Android/AndroidSentryFeedback.h b/plugin-dev/Source/Sentry/Private/Android/AndroidSentryFeedback.h new file mode 100644 index 000000000..7ac7a8381 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Android/AndroidSentryFeedback.h @@ -0,0 +1,36 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#pragma once + +#include "Interface/SentryFeedbackInterface.h" + +#include "Infrastructure/AndroidSentryJavaObjectWrapper.h" + +class ISentryId; + +class FAndroidSentryFeedback : public ISentryFeedback, public FSentryJavaObjectWrapper +{ +public: + FAndroidSentryFeedback(const FString& message); + + void SetupClassMethods(); + + virtual FString GetMessage() const override; + virtual void SetName(const FString& name) override; + virtual FString GetName() const override; + virtual void SetContactEmail(const FString& email) override; + virtual FString GetContactEmail() const override; + virtual void SetAssociatedEvent(const FString& eventId) override; + virtual FString GetAssociatedEvent() const override; + +private: + FSentryJavaMethod GetMessageMethod; + FSentryJavaMethod SetNameMethod; + FSentryJavaMethod GetNameMethod; + FSentryJavaMethod SetContactEmailMethod; + FSentryJavaMethod GetContactEmailMethod; + FSentryJavaMethod SetAssociatedEventMethod; + FSentryJavaMethod GetAssociatedEventMethod; +}; + +typedef FAndroidSentryFeedback FPlatformSentryFeedback; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp b/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp index 99068f427..e4f251484 100644 --- a/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.cpp @@ -5,12 +5,12 @@ #include "AndroidSentryAttachment.h" #include "AndroidSentryBreadcrumb.h" #include "AndroidSentryEvent.h" +#include "AndroidSentryFeedback.h" #include "AndroidSentryId.h" #include "AndroidSentryTransaction.h" #include "AndroidSentryTransactionContext.h" #include "AndroidSentryTransactionOptions.h" #include "AndroidSentryUser.h" -#include "AndroidSentryUserFeedback.h" #include "SentryBeforeSendHandler.h" #include "SentryDefines.h" @@ -200,12 +200,12 @@ TSharedPtr FAndroidSentrySubsystem::CaptureEnsure(const FString& type return MakeShareable(new FAndroidSentryId(*id)); } -void FAndroidSentrySubsystem::CaptureUserFeedback(TSharedPtr userFeedback) +void FAndroidSentrySubsystem::CaptureFeedback(TSharedPtr feedback) { - TSharedPtr userFeedbackAndroid = StaticCastSharedPtr(userFeedback); + TSharedPtr feedbackAndroid = StaticCastSharedPtr(feedback); - FSentryJavaObjectWrapper::CallStaticMethod(SentryJavaClasses::Sentry, "captureUserFeedback", "(Lio/sentry/UserFeedback;)V", - userFeedbackAndroid->GetJObject()); + FSentryJavaObjectWrapper::CallStaticObjectMethod(SentryJavaClasses::Sentry, "captureFeedback", "(Lio/sentry/protocol/Feedback;)Lio/sentry/protocol/SentryId;", + feedbackAndroid->GetJObject()); } void FAndroidSentrySubsystem::SetUser(TSharedPtr user) diff --git a/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.h b/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.h index b2ea9a976..b985906c5 100644 --- a/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Private/Android/AndroidSentrySubsystem.h @@ -22,7 +22,7 @@ class FAndroidSentrySubsystem : public ISentrySubsystem virtual TSharedPtr CaptureEvent(TSharedPtr event) override; virtual TSharedPtr CaptureEventWithScope(TSharedPtr event, const FSentryScopeDelegate& onConfigureScope) override; virtual TSharedPtr CaptureEnsure(const FString& type, const FString& message) override; - virtual void CaptureUserFeedback(TSharedPtr userFeedback) override; + virtual void CaptureFeedback(TSharedPtr feedback) override; virtual void SetUser(TSharedPtr user) override; virtual void RemoveUser() override; virtual void SetContext(const FString& key, const TMap& values) override; diff --git a/plugin-dev/Source/Sentry/Private/Android/AndroidSentryUserFeedback.cpp b/plugin-dev/Source/Sentry/Private/Android/AndroidSentryUserFeedback.cpp deleted file mode 100644 index ad59b4585..000000000 --- a/plugin-dev/Source/Sentry/Private/Android/AndroidSentryUserFeedback.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#include "AndroidSentryUserFeedback.h" - -#include "AndroidSentryId.h" - -#include "Infrastructure/AndroidSentryJavaClasses.h" - -FAndroidSentryUserFeedback::FAndroidSentryUserFeedback(TSharedPtr eventId) - : FSentryJavaObjectWrapper(SentryJavaClasses::UserFeedback, "(Lio/sentry/protocol/SentryId;)V", - StaticCastSharedPtr(eventId)->GetJObject()) -{ - SetupClassMethods(); -} - -void FAndroidSentryUserFeedback::SetupClassMethods() -{ - SetNameMethod = GetMethod("setName", "(Ljava/lang/String;)V"); - GetNameMethod = GetMethod("getName", "()Ljava/lang/String;"); - SetEmailMethod = GetMethod("setEmail", "(Ljava/lang/String;)V"); - GetEmailMethod = GetMethod("getEmail", "()Ljava/lang/String;"); - SetCommentMethod = GetMethod("setComments", "(Ljava/lang/String;)V"); - GetCommentMethod = GetMethod("getComments", "()Ljava/lang/String;"); -} - -void FAndroidSentryUserFeedback::SetName(const FString& name) -{ - CallMethod(SetNameMethod, *GetJString(name)); -} - -FString FAndroidSentryUserFeedback::GetName() const -{ - return CallMethod(GetNameMethod); -} - -void FAndroidSentryUserFeedback::SetEmail(const FString& email) -{ - CallMethod(SetEmailMethod, *GetJString(email)); -} - -FString FAndroidSentryUserFeedback::GetEmail() const -{ - return CallMethod(GetEmailMethod); -} - -void FAndroidSentryUserFeedback::SetComment(const FString& comment) -{ - CallMethod(SetCommentMethod, *GetJString(comment)); -} - -FString FAndroidSentryUserFeedback::GetComment() const -{ - return CallMethod(GetCommentMethod); -} diff --git a/plugin-dev/Source/Sentry/Private/Android/AndroidSentryUserFeedback.h b/plugin-dev/Source/Sentry/Private/Android/AndroidSentryUserFeedback.h deleted file mode 100644 index 3ea36a3c2..000000000 --- a/plugin-dev/Source/Sentry/Private/Android/AndroidSentryUserFeedback.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#pragma once - -#include "Interface/SentryUserFeedbackInterface.h" - -#include "Infrastructure/AndroidSentryJavaObjectWrapper.h" - -class ISentryId; - -class FAndroidSentryUserFeedback : public ISentryUserFeedback, public FSentryJavaObjectWrapper -{ -public: - FAndroidSentryUserFeedback(TSharedPtr eventId); - - void SetupClassMethods(); - - virtual void SetName(const FString& name) override; - virtual FString GetName() const override; - virtual void SetEmail(const FString& email) override; - virtual FString GetEmail() const override; - virtual void SetComment(const FString& comment) override; - virtual FString GetComment() const override; - -private: - FSentryJavaMethod SetNameMethod; - FSentryJavaMethod GetNameMethod; - FSentryJavaMethod SetEmailMethod; - FSentryJavaMethod GetEmailMethod; - FSentryJavaMethod SetCommentMethod; - FSentryJavaMethod GetCommentMethod; -}; - -typedef FAndroidSentryUserFeedback FPlatformSentryUserFeedback; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.cpp b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.cpp index 5a719a025..853d0a4a2 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.cpp +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.cpp @@ -14,7 +14,7 @@ const FSentryJavaClass SentryJavaClasses::SentryId = FSentryJavaClass { "io/s const FSentryJavaClass SentryJavaClasses::Scope = FSentryJavaClass { "io/sentry/IScope", ESentryJavaClassType::External }; const FSentryJavaClass SentryJavaClasses::ScopeImpl = FSentryJavaClass { "io/sentry/Scope", ESentryJavaClassType::External }; const FSentryJavaClass SentryJavaClasses::User = FSentryJavaClass { "io/sentry/protocol/User", ESentryJavaClassType::External }; -const FSentryJavaClass SentryJavaClasses::UserFeedback = FSentryJavaClass { "io/sentry/UserFeedback", ESentryJavaClassType::External }; +const FSentryJavaClass SentryJavaClasses::Feedback = FSentryJavaClass { "io/sentry/protocol/Feedback", ESentryJavaClassType::External }; const FSentryJavaClass SentryJavaClasses::Message = FSentryJavaClass { "io/sentry/protocol/Message", ESentryJavaClassType::External }; const FSentryJavaClass SentryJavaClasses::SentryLevel = FSentryJavaClass { "io/sentry/SentryLevel", ESentryJavaClassType::External }; const FSentryJavaClass SentryJavaClasses::SentryHint = FSentryJavaClass { "io/sentry/Hint", ESentryJavaClassType::External }; diff --git a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.h b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.h index 5d62532d8..3daa177a8 100644 --- a/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.h +++ b/plugin-dev/Source/Sentry/Private/Android/Infrastructure/AndroidSentryJavaClasses.h @@ -16,7 +16,7 @@ struct SentryJavaClasses const static FSentryJavaClass Scope; const static FSentryJavaClass ScopeImpl; const static FSentryJavaClass User; - const static FSentryJavaClass UserFeedback; + const static FSentryJavaClass Feedback; const static FSentryJavaClass Message; const static FSentryJavaClass SentryLevel; const static FSentryJavaClass SentryHint; diff --git a/plugin-dev/Source/Sentry/Private/Apple/AppleSentryFeedback.cpp b/plugin-dev/Source/Sentry/Private/Apple/AppleSentryFeedback.cpp new file mode 100644 index 000000000..d98244d41 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Apple/AppleSentryFeedback.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#include "AppleSentryFeedback.h" + +#include "AppleSentryId.h" + +#include "Convenience/AppleSentryInclude.h" +#include "Convenience/AppleSentryMacro.h" + +FAppleSentryFeedback::FAppleSentryFeedback(const FString& message) + : Message(message) +{ +} + +FAppleSentryFeedback::~FAppleSentryFeedback() +{ + // Put custom destructor logic here if needed +} + +FString FAppleSentryFeedback::GetMessage() const +{ + return Message; +} + +void FAppleSentryFeedback::SetName(const FString& name) +{ + Name = name; +} + +FString FAppleSentryFeedback::GetName() const +{ + return Name; +} + +void FAppleSentryFeedback::SetContactEmail(const FString& email) +{ + Email = email; +} + +FString FAppleSentryFeedback::GetContactEmail() const +{ + return Email; +} + +void FAppleSentryFeedback::SetAssociatedEvent(const FString& eventId) +{ + EventId = eventId; +} + +FString FAppleSentryFeedback::GetAssociatedEvent() const +{ + return EventId; +} + +SentryFeedback* FAppleSentryFeedback::CreateSentryFeedback(TSharedPtr feedback) +{ + SentryId* id = nil; + if (!feedback->EventId.IsEmpty()) + { + TSharedPtr idIOS = MakeShareable(new FAppleSentryId(feedback->EventId)); + id = idIOS->GetNativeObject(); + } + + return [[SENTRY_APPLE_CLASS(SentryFeedback) alloc] initWithMessage:feedback->Message.GetNSString() + name:feedback->Name.GetNSString() + email:feedback->Email.GetNSString() + source:SentryFeedbackSourceCustom + associatedEventId:id + attachments:nil]; +} diff --git a/plugin-dev/Source/Sentry/Private/Apple/AppleSentryFeedback.h b/plugin-dev/Source/Sentry/Private/Apple/AppleSentryFeedback.h new file mode 100644 index 000000000..a35369482 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Apple/AppleSentryFeedback.h @@ -0,0 +1,32 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#pragma once + +#include "Interface/SentryFeedbackInterface.h" + +@class SentryFeedback; + +class FAppleSentryFeedback : public ISentryFeedback +{ +public: + FAppleSentryFeedback(const FString& message); + virtual ~FAppleSentryFeedback() override; + + virtual FString GetMessage() const override; + virtual void SetName(const FString& name) override; + virtual FString GetName() const override; + virtual void SetContactEmail(const FString& email) override; + virtual FString GetContactEmail() const override; + virtual void SetAssociatedEvent(const FString& eventId) override; + virtual FString GetAssociatedEvent() const override; + + static SentryFeedback* CreateSentryFeedback(TSharedPtr feedback); + +private: + FString Message; + FString Name; + FString Email; + FString EventId; +}; + +typedef FAppleSentryFeedback FPlatformSentryFeedback; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.cpp b/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.cpp index c767f3677..ba27a2188 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.cpp +++ b/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.cpp @@ -5,13 +5,13 @@ #include "AppleSentryAttachment.h" #include "AppleSentryBreadcrumb.h" #include "AppleSentryEvent.h" +#include "AppleSentryFeedback.h" #include "AppleSentryId.h" #include "AppleSentrySamplingContext.h" #include "AppleSentryScope.h" #include "AppleSentryTransaction.h" #include "AppleSentryTransactionContext.h" #include "AppleSentryUser.h" -#include "AppleSentryUserFeedback.h" #include "Convenience/AppleSentryMacro.h" #include "SentryBeforeBreadcrumbHandler.h" @@ -290,11 +290,11 @@ TSharedPtr FAppleSentrySubsystem::CaptureEnsure(const FString& type, return id; } -void FAppleSentrySubsystem::CaptureUserFeedback(TSharedPtr userFeedback) +void FAppleSentrySubsystem::CaptureFeedback(TSharedPtr feedback) { - TSharedPtr userFeedbackApple = StaticCastSharedPtr(userFeedback); + TSharedPtr feedbackApple = StaticCastSharedPtr(feedback); - [SENTRY_APPLE_CLASS(SentrySDK) captureFeedback:FAppleSentryUserFeedback::CreateSentryFeedback(userFeedbackApple)]; + [SENTRY_APPLE_CLASS(SentrySDK) captureFeedback:FAppleSentryFeedback::CreateSentryFeedback(feedbackApple)]; } void FAppleSentrySubsystem::SetUser(TSharedPtr user) diff --git a/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.h b/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.h index 39d1c4299..d26233c44 100644 --- a/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Private/Apple/AppleSentrySubsystem.h @@ -22,7 +22,7 @@ class FAppleSentrySubsystem : public ISentrySubsystem virtual TSharedPtr CaptureEvent(TSharedPtr event) override; virtual TSharedPtr CaptureEventWithScope(TSharedPtr event, const FSentryScopeDelegate& onConfigureScope) override; virtual TSharedPtr CaptureEnsure(const FString& type, const FString& message) override; - virtual void CaptureUserFeedback(TSharedPtr userFeedback) override; + virtual void CaptureFeedback(TSharedPtr feedback) override; virtual void SetUser(TSharedPtr user) override; virtual void RemoveUser() override; virtual void SetContext(const FString& key, const TMap& values) override; diff --git a/plugin-dev/Source/Sentry/Private/Apple/AppleSentryUserFeedback.cpp b/plugin-dev/Source/Sentry/Private/Apple/AppleSentryUserFeedback.cpp deleted file mode 100644 index 21882fca3..000000000 --- a/plugin-dev/Source/Sentry/Private/Apple/AppleSentryUserFeedback.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#include "AppleSentryUserFeedback.h" - -#include "AppleSentryId.h" - -#include "Convenience/AppleSentryInclude.h" -#include "Convenience/AppleSentryMacro.h" - -FAppleSentryUserFeedback::FAppleSentryUserFeedback(TSharedPtr eventId) - : EventId(eventId) -{ - TSharedPtr idIOS = StaticCastSharedPtr(eventId); - SentryId* id = idIOS->GetNativeObject(); - - UserFeedbackApple = [[SENTRY_APPLE_CLASS(SentryFeedback) alloc] initWithMessage:@"" - name:nil - email:nil - source:SentryFeedbackSourceCustom - associatedEventId:id - attachments:nil]; -} - -FAppleSentryUserFeedback::~FAppleSentryUserFeedback() -{ - // Put custom destructor logic here if needed -} - -void FAppleSentryUserFeedback::SetNativeObject(SentryFeedback* feedback) -{ - UserFeedbackApple = feedback; -} - -SentryFeedback* FAppleSentryUserFeedback::GetNativeObject() -{ - return UserFeedbackApple; -} - -void FAppleSentryUserFeedback::SetName(const FString& name) -{ - Name = name; -} - -FString FAppleSentryUserFeedback::GetName() const -{ - return Name; -} - -void FAppleSentryUserFeedback::SetEmail(const FString& email) -{ - Email = email; -} - -FString FAppleSentryUserFeedback::GetEmail() const -{ - return Email; -} - -void FAppleSentryUserFeedback::SetComment(const FString& comment) -{ - Comment = comment; -} - -FString FAppleSentryUserFeedback::GetComment() const -{ - return Comment; -} - -SentryFeedback* FAppleSentryUserFeedback::CreateSentryFeedback(TSharedPtr feedback) -{ - TSharedPtr idIOS = StaticCastSharedPtr(feedback->EventId); - SentryId* id = idIOS->GetNativeObject(); - - return [[SENTRY_APPLE_CLASS(SentryFeedback) alloc] initWithMessage:feedback->Comment.GetNSString() - name:feedback->Name.GetNSString() - email:feedback->Email.GetNSString() - source:SentryFeedbackSourceCustom - associatedEventId:id - attachments:nil]; -} diff --git a/plugin-dev/Source/Sentry/Private/Apple/AppleSentryUserFeedback.h b/plugin-dev/Source/Sentry/Private/Apple/AppleSentryUserFeedback.h deleted file mode 100644 index 931feb6a4..000000000 --- a/plugin-dev/Source/Sentry/Private/Apple/AppleSentryUserFeedback.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#pragma once - -#include "Interface/SentryUserFeedbackInterface.h" - -class ISentryId; - -@class SentryFeedback; - -class FAppleSentryUserFeedback : public ISentryUserFeedback -{ -public: - FAppleSentryUserFeedback(TSharedPtr eventId); - virtual ~FAppleSentryUserFeedback() override; - - void SetNativeObject(SentryFeedback* feedback); - SentryFeedback* GetNativeObject(); - - virtual void SetName(const FString& name) override; - virtual FString GetName() const override; - virtual void SetEmail(const FString& email) override; - virtual FString GetEmail() const override; - virtual void SetComment(const FString& comment) override; - virtual FString GetComment() const override; - - static SentryFeedback* CreateSentryFeedback(TSharedPtr feedback); - -private: - TSharedPtr EventId; - FString Name; - FString Email; - FString Comment; - - SentryFeedback* UserFeedbackApple; -}; - -typedef FAppleSentryUserFeedback FPlatformSentryUserFeedback; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.cpp b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.cpp new file mode 100644 index 000000000..38379dea4 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#include "GenericPlatformSentryFeedback.h" + +#include "Infrastructure/GenericPlatformSentryConverters.h" + +#if USE_SENTRY_NATIVE + +FGenericPlatformSentryFeedback::FGenericPlatformSentryFeedback() +{ + Feedback = sentry_value_new_object(); +} + +FGenericPlatformSentryFeedback::FGenericPlatformSentryFeedback(const FString& message) +{ + Feedback = sentry_value_new_object(); + sentry_value_set_by_key(Feedback, "message", sentry_value_new_string(TCHAR_TO_UTF8(*message))); +} + +FGenericPlatformSentryFeedback::~FGenericPlatformSentryFeedback() +{ + // Put custom destructor logic here if needed +} + +sentry_value_t FGenericPlatformSentryFeedback::GetNativeObject() +{ + return Feedback; +} + +FString FGenericPlatformSentryFeedback::GetMessage() const +{ + sentry_value_t message = sentry_value_get_by_key(Feedback, "message"); + return FString(sentry_value_as_string(message)); +} + +void FGenericPlatformSentryFeedback::SetName(const FString& name) +{ + sentry_value_set_by_key(Feedback, "name", sentry_value_new_string(TCHAR_TO_UTF8(*name))); +} + +FString FGenericPlatformSentryFeedback::GetName() const +{ + sentry_value_t username = sentry_value_get_by_key(Feedback, "name"); + return FString(sentry_value_as_string(username)); +} + +void FGenericPlatformSentryFeedback::SetContactEmail(const FString& email) +{ + sentry_value_set_by_key(Feedback, "contact_email", sentry_value_new_string(TCHAR_TO_UTF8(*email))); +} + +FString FGenericPlatformSentryFeedback::GetContactEmail() const +{ + sentry_value_t email = sentry_value_get_by_key(Feedback, "contact_email"); + return FString(sentry_value_as_string(email)); +} + +void FGenericPlatformSentryFeedback::SetAssociatedEvent(const FString& eventId) +{ + if (eventId.IsEmpty()) + return; + + sentry_value_set_by_key(Feedback, "associated_event_id", sentry_value_new_string(TCHAR_TO_UTF8(*eventId))); +} + +FString FGenericPlatformSentryFeedback::GetAssociatedEvent() const +{ + sentry_value_t comment = sentry_value_get_by_key(Feedback, "associated_event_id"); + return FString(sentry_value_as_string(comment)); +} + +#endif diff --git a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.h b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.h new file mode 100644 index 000000000..083ba822c --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.h @@ -0,0 +1,34 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#pragma once + +#include "Convenience/GenericPlatformSentryInclude.h" + +#include "Interface/SentryFeedbackInterface.h" + +#if USE_SENTRY_NATIVE + +class FGenericPlatformSentryFeedback : public ISentryFeedback +{ +public: + FGenericPlatformSentryFeedback(); + FGenericPlatformSentryFeedback(const FString& message); + virtual ~FGenericPlatformSentryFeedback() override; + + sentry_value_t GetNativeObject(); + + virtual FString GetMessage() const override; + virtual void SetName(const FString& name) override; + virtual FString GetName() const override; + virtual void SetContactEmail(const FString& email) override; + virtual FString GetContactEmail() const override; + virtual void SetAssociatedEvent(const FString& eventId) override; + virtual FString GetAssociatedEvent() const override; + +private: + sentry_value_t Feedback; +}; + +typedef FGenericPlatformSentryFeedback FPlatformSentryFeedback; + +#endif diff --git a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryId.cpp b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryId.cpp index 110b75061..2b91c84e6 100644 --- a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryId.cpp +++ b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryId.cpp @@ -33,7 +33,11 @@ FString FGenericPlatformSentryId::ToString() const { char IdString[37]; sentry_uuid_as_string(&Id, IdString); - return FString(IdString); + + FString SanitizedIdString(IdString); + SanitizedIdString.ReplaceInline(TEXT("-"), TEXT("")); + + return SanitizedIdString; } #endif diff --git a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.cpp b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.cpp index 4a4ae586c..f2fb3c3d5 100644 --- a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.cpp +++ b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.cpp @@ -4,12 +4,12 @@ #include "GenericPlatformSentryAttachment.h" #include "GenericPlatformSentryBreadcrumb.h" #include "GenericPlatformSentryEvent.h" +#include "GenericPlatformSentryFeedback.h" #include "GenericPlatformSentryId.h" #include "GenericPlatformSentryScope.h" #include "GenericPlatformSentryTransaction.h" #include "GenericPlatformSentryTransactionContext.h" #include "GenericPlatformSentryUser.h" -#include "GenericPlatformSentryUserFeedback.h" #include "SentryBeforeBreadcrumbHandler.h" #include "SentryBeforeSendHandler.h" @@ -537,10 +537,10 @@ TSharedPtr FGenericPlatformSentrySubsystem::CaptureEnsure(const FStri return MakeShareable(new FGenericPlatformSentryId(id)); } -void FGenericPlatformSentrySubsystem::CaptureUserFeedback(TSharedPtr InUserFeedback) +void FGenericPlatformSentrySubsystem::CaptureFeedback(TSharedPtr feedback) { - TSharedPtr userFeedback = StaticCastSharedPtr(InUserFeedback); - sentry_capture_feedback(userFeedback->GetNativeObject()); + TSharedPtr Feedback = StaticCastSharedPtr(feedback); + sentry_capture_feedback(Feedback->GetNativeObject()); } void FGenericPlatformSentrySubsystem::SetUser(TSharedPtr InUser) diff --git a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.h b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.h index 58b7a087f..c395a01e5 100644 --- a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentrySubsystem.h @@ -34,7 +34,7 @@ class FGenericPlatformSentrySubsystem : public ISentrySubsystem virtual TSharedPtr CaptureEvent(TSharedPtr event) override; virtual TSharedPtr CaptureEventWithScope(TSharedPtr event, const FSentryScopeDelegate& onScopeConfigure) override; virtual TSharedPtr CaptureEnsure(const FString& type, const FString& message) override; - virtual void CaptureUserFeedback(TSharedPtr userFeedback) override; + virtual void CaptureFeedback(TSharedPtr feedback) override; virtual void SetUser(TSharedPtr user) override; virtual void RemoveUser() override; virtual void SetContext(const FString& key, const TMap& values) override; diff --git a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.cpp b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.cpp deleted file mode 100644 index a5cd44a9d..000000000 --- a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#include "GenericPlatformSentryUserFeedback.h" - -#include "GenericPlatformSentryId.h" - -#include "Infrastructure/GenericPlatformSentryConverters.h" - -#if USE_SENTRY_NATIVE - -FGenericPlatformSentryUserFeedback::FGenericPlatformSentryUserFeedback() -{ - UserFeedback = sentry_value_new_object(); -} - -FGenericPlatformSentryUserFeedback::FGenericPlatformSentryUserFeedback(TSharedPtr eventId) -{ - UserFeedback = sentry_value_new_object(); - sentry_value_set_by_key(UserFeedback, "associated_event_id", sentry_value_new_string(TCHAR_TO_ANSI(*eventId->ToString()))); -} - -FGenericPlatformSentryUserFeedback::~FGenericPlatformSentryUserFeedback() -{ - // Put custom destructor logic here if needed -} - -sentry_value_t FGenericPlatformSentryUserFeedback::GetNativeObject() -{ - return UserFeedback; -} - -void FGenericPlatformSentryUserFeedback::SetName(const FString& name) -{ - sentry_value_set_by_key(UserFeedback, "name", sentry_value_new_string(TCHAR_TO_UTF8(*name))); -} - -FString FGenericPlatformSentryUserFeedback::GetName() const -{ - sentry_value_t username = sentry_value_get_by_key(UserFeedback, "name"); - return FString(sentry_value_as_string(username)); -} - -void FGenericPlatformSentryUserFeedback::SetEmail(const FString& email) -{ - sentry_value_set_by_key(UserFeedback, "contact_email", sentry_value_new_string(TCHAR_TO_ANSI(*email))); -} - -FString FGenericPlatformSentryUserFeedback::GetEmail() const -{ - sentry_value_t email = sentry_value_get_by_key(UserFeedback, "contact_email"); - return FString(sentry_value_as_string(email)); -} - -void FGenericPlatformSentryUserFeedback::SetComment(const FString& comment) -{ - sentry_value_set_by_key(UserFeedback, "message", sentry_value_new_string(TCHAR_TO_UTF8(*comment))); -} - -FString FGenericPlatformSentryUserFeedback::GetComment() const -{ - sentry_value_t comment = sentry_value_get_by_key(UserFeedback, "message"); - return FString(sentry_value_as_string(comment)); -} - -#endif diff --git a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.h b/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.h deleted file mode 100644 index 952d3102c..000000000 --- a/plugin-dev/Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#pragma once - -#include "Convenience/GenericPlatformSentryInclude.h" - -#include "Interface/SentryUserFeedbackInterface.h" - -#if USE_SENTRY_NATIVE - -class ISentryId; - -class FGenericPlatformSentryUserFeedback : public ISentryUserFeedback -{ -public: - FGenericPlatformSentryUserFeedback(); - FGenericPlatformSentryUserFeedback(TSharedPtr eventId); - virtual ~FGenericPlatformSentryUserFeedback() override; - - sentry_value_t GetNativeObject(); - - virtual void SetName(const FString& name) override; - virtual FString GetName() const override; - virtual void SetEmail(const FString& email) override; - virtual FString GetEmail() const override; - virtual void SetComment(const FString& comment) override; - virtual FString GetComment() const override; - -private: - sentry_value_t UserFeedback; -}; - -typedef FGenericPlatformSentryUserFeedback FPlatformSentryUserFeedback; - -#endif diff --git a/plugin-dev/Source/Sentry/Private/HAL/PlatformSentryFeedback.h b/plugin-dev/Source/Sentry/Private/HAL/PlatformSentryFeedback.h new file mode 100644 index 000000000..61afb732e --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/HAL/PlatformSentryFeedback.h @@ -0,0 +1,13 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#pragma once + +#if PLATFORM_ANDROID +#include "Android/AndroidSentryFeedback.h" +#elif PLATFORM_APPLE +#include "Apple/AppleSentryFeedback.h" +#elif USE_SENTRY_NATIVE +#include "GenericPlatform/GenericPlatformSentryFeedback.h" +#else +#include "Null/NullSentryFeedback.h" +#endif diff --git a/plugin-dev/Source/Sentry/Private/HAL/PlatformSentryUserFeedback.h b/plugin-dev/Source/Sentry/Private/HAL/PlatformSentryUserFeedback.h deleted file mode 100644 index baa89ad18..000000000 --- a/plugin-dev/Source/Sentry/Private/HAL/PlatformSentryUserFeedback.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#pragma once - -#if PLATFORM_ANDROID -#include "Android/AndroidSentryUserFeedback.h" -#elif PLATFORM_APPLE -#include "Apple/AppleSentryUserFeedback.h" -#elif USE_SENTRY_NATIVE -#include "GenericPlatform/GenericPlatformSentryUserFeedback.h" -#else -#include "Null/NullSentryUserFeedback.h" -#endif - -#include "PlatformSentryId.h" - -static TSharedPtr CreateSharedSentryUserFeedback(const FString& EventId) -{ - TSharedPtr Id = MakeShareable(new FPlatformSentryId(EventId)); - - return MakeShareable(new FPlatformSentryUserFeedback(Id)); -} diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentryFeedbackInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentryFeedbackInterface.h new file mode 100644 index 000000000..2a3434482 --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Interface/SentryFeedbackInterface.h @@ -0,0 +1,19 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +class ISentryFeedback +{ +public: + virtual ~ISentryFeedback() = default; + + virtual FString GetMessage() const = 0; + virtual void SetName(const FString& name) = 0; + virtual FString GetName() const = 0; + virtual void SetContactEmail(const FString& email) = 0; + virtual FString GetContactEmail() const = 0; + virtual void SetAssociatedEvent(const FString& eventId) = 0; + virtual FString GetAssociatedEvent() const = 0; +}; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h index f271fc732..be72668bd 100644 --- a/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h +++ b/plugin-dev/Source/Sentry/Private/Interface/SentrySubsystemInterface.h @@ -10,7 +10,7 @@ class ISentryAttachment; class ISentryBreadcrumb; class ISentryEvent; -class ISentryUserFeedback; +class ISentryFeedback; class ISentryUser; class ISentryTransaction; class ISentryTransactionContext; @@ -45,7 +45,7 @@ class ISentrySubsystem virtual TSharedPtr CaptureEvent(TSharedPtr event) = 0; virtual TSharedPtr CaptureEventWithScope(TSharedPtr event, const FSentryScopeDelegate& onConfigureScope) = 0; virtual TSharedPtr CaptureEnsure(const FString& type, const FString& message) = 0; - virtual void CaptureUserFeedback(TSharedPtr userFeedback) = 0; + virtual void CaptureFeedback(TSharedPtr feedback) = 0; virtual void SetUser(TSharedPtr user) = 0; virtual void RemoveUser() = 0; virtual void SetContext(const FString& key, const TMap& values) = 0; diff --git a/plugin-dev/Source/Sentry/Private/Interface/SentryUserFeedbackInterface.h b/plugin-dev/Source/Sentry/Private/Interface/SentryUserFeedbackInterface.h deleted file mode 100644 index a9675164a..000000000 --- a/plugin-dev/Source/Sentry/Private/Interface/SentryUserFeedbackInterface.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -class ISentryUserFeedback -{ -public: - virtual ~ISentryUserFeedback() = default; - - virtual void SetName(const FString& name) = 0; - virtual FString GetName() const = 0; - virtual void SetEmail(const FString& email) = 0; - virtual FString GetEmail() const = 0; - virtual void SetComment(const FString& comment) = 0; - virtual FString GetComment() const = 0; -}; \ No newline at end of file diff --git a/plugin-dev/Source/Sentry/Private/Null/NullSentryFeedback.h b/plugin-dev/Source/Sentry/Private/Null/NullSentryFeedback.h new file mode 100644 index 000000000..4d27234aa --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Null/NullSentryFeedback.h @@ -0,0 +1,25 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#pragma once + +#include "Interface/SentryFeedbackInterface.h" + +class ISentryId; + +class FNullSentryFeedback final : public ISentryFeedback +{ +public: + FNullSentryFeedback(const FString& message) {} + + virtual ~FNullSentryFeedback() override = default; + + virtual FString GetMessage() const override { return TEXT(""); } + virtual void SetName(const FString& name) override {} + virtual FString GetName() const override { return TEXT(""); } + virtual void SetContactEmail(const FString& email) override {} + virtual FString GetContactEmail() const override { return TEXT(""); } + virtual void SetAssociatedEvent(const FString& eventId) override {} + virtual FString GetAssociatedEvent() const override { return TEXT(""); } +}; + +typedef FNullSentryFeedback FPlatformSentryFeedback; diff --git a/plugin-dev/Source/Sentry/Private/Null/NullSentrySubsystem.h b/plugin-dev/Source/Sentry/Private/Null/NullSentrySubsystem.h index 3aa691dca..23c28fc47 100644 --- a/plugin-dev/Source/Sentry/Private/Null/NullSentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Private/Null/NullSentrySubsystem.h @@ -24,7 +24,7 @@ class FNullSentrySubsystem : public ISentrySubsystem virtual TSharedPtr CaptureEvent(TSharedPtr event) override { return nullptr; } virtual TSharedPtr CaptureEventWithScope(TSharedPtr event, const FSentryScopeDelegate& onScopeConfigure) override { return nullptr; } virtual TSharedPtr CaptureEnsure(const FString& type, const FString& message) override { return nullptr; } - virtual void CaptureUserFeedback(TSharedPtr userFeedback) override {} + virtual void CaptureFeedback(TSharedPtr feedback) override {} virtual void SetUser(TSharedPtr user) override {} virtual void RemoveUser() override {} virtual void SetContext(const FString& key, const TMap& values) override {} diff --git a/plugin-dev/Source/Sentry/Private/Null/NullSentryUserFeedback.h b/plugin-dev/Source/Sentry/Private/Null/NullSentryUserFeedback.h deleted file mode 100644 index 0ab40e3a2..000000000 --- a/plugin-dev/Source/Sentry/Private/Null/NullSentryUserFeedback.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#pragma once - -#include "Interface/SentryUserFeedbackInterface.h" - -class ISentryId; - -class FNullSentryUserFeedback final : public ISentryUserFeedback -{ -public: - FNullSentryUserFeedback(TSharedPtr eventId) {} - - virtual ~FNullSentryUserFeedback() override = default; - - virtual void SetName(const FString& name) override {} - virtual FString GetName() const override { return TEXT(""); } - virtual void SetEmail(const FString& email) override {} - virtual FString GetEmail() const override { return TEXT(""); } - virtual void SetComment(const FString& comment) override {} - virtual FString GetComment() const override { return TEXT(""); } -}; - -typedef FNullSentryUserFeedback FPlatformSentryUserFeedback; diff --git a/plugin-dev/Source/Sentry/Private/SentryFeedback.cpp b/plugin-dev/Source/Sentry/Private/SentryFeedback.cpp new file mode 100644 index 000000000..0b5b6c72f --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/SentryFeedback.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#include "SentryFeedback.h" + +#include "HAL/PlatformSentryFeedback.h" + +void USentryFeedback::Initialize(const FString& Message) +{ + if (ensure(!Message.IsEmpty())) + { + NativeImpl = MakeShareable(new FPlatformSentryFeedback(Message)); + } +} + +FString USentryFeedback::GetMessage() const +{ + if (!NativeImpl) + return FString(); + + return NativeImpl->GetMessage(); +} + +void USentryFeedback::SetName(const FString& Name) +{ + if (!NativeImpl) + return; + + NativeImpl->SetName(Name); +} + +FString USentryFeedback::GetName() const +{ + if (!NativeImpl) + return FString(); + + return NativeImpl->GetName(); +} + +void USentryFeedback::SetContactEmail(const FString& Email) +{ + if (!NativeImpl) + return; + + NativeImpl->SetContactEmail(Email); +} + +FString USentryFeedback::GetContactEmail() const +{ + if (!NativeImpl) + return FString(); + + return NativeImpl->GetContactEmail(); +} + +void USentryFeedback::SetAssociatedEvent(const FString& EventId) +{ + if (!NativeImpl) + return; + + NativeImpl->SetAssociatedEvent(EventId); +} + +FString USentryFeedback::GetAssociatedEvent() const +{ + if (!NativeImpl) + return FString(); + + return NativeImpl->GetAssociatedEvent(); +} diff --git a/plugin-dev/Source/Sentry/Private/SentryLibrary.cpp b/plugin-dev/Source/Sentry/Private/SentryLibrary.cpp index a28c4a253..ea0508229 100644 --- a/plugin-dev/Source/Sentry/Private/SentryLibrary.cpp +++ b/plugin-dev/Source/Sentry/Private/SentryLibrary.cpp @@ -4,16 +4,16 @@ #include "SentryAttachment.h" #include "SentryBreadcrumb.h" #include "SentryEvent.h" +#include "SentryFeedback.h" #include "SentryTransactionContext.h" #include "SentryUser.h" -#include "SentryUserFeedback.h" #include "HAL/PlatformSentryAttachment.h" #include "HAL/PlatformSentryBreadcrumb.h" #include "HAL/PlatformSentryEvent.h" +#include "HAL/PlatformSentryFeedback.h" #include "HAL/PlatformSentryTransactionContext.h" #include "HAL/PlatformSentryUser.h" -#include "HAL/PlatformSentryUserFeedback.h" USentryEvent* USentryLibrary::CreateSentryEvent(const FString& Message, ESentryLevel Level) { @@ -46,18 +46,18 @@ USentryUser* USentryLibrary::CreateSentryUser(const FString& Email, const FStrin return User; } -USentryUserFeedback* USentryLibrary::CreateSentryUserFeedback(const FString& EventId, const FString& Name, const FString& Email, const FString& Comments) +USentryFeedback* USentryLibrary::CreateSentryFeedback(const FString& Message, const FString& Name, const FString& Email, const FString& EventId) { - USentryUserFeedback* UserFeedback = USentryUserFeedback::Create(CreateSharedSentryUserFeedback(EventId)); + USentryFeedback* Feedback = USentryFeedback::Create(MakeShareable(new FPlatformSentryFeedback(Message))); if (!Name.IsEmpty()) - UserFeedback->SetName(Name); + Feedback->SetName(Name); if (!Email.IsEmpty()) - UserFeedback->SetEmail(Email); - if (!Comments.IsEmpty()) - UserFeedback->SetComment(Comments); + Feedback->SetContactEmail(Email); + if (!EventId.IsEmpty()) + Feedback->SetAssociatedEvent(EventId); - return UserFeedback; + return Feedback; } USentryBreadcrumb* USentryLibrary::CreateSentryBreadcrumb(const FString& Message, const FString& Type, const FString& Category, diff --git a/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp b/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp index d34b59d24..b7fa7b364 100644 --- a/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp +++ b/plugin-dev/Source/Sentry/Private/SentrySubsystem.cpp @@ -8,6 +8,7 @@ #include "SentryDefines.h" #include "SentryErrorOutputDevice.h" #include "SentryEvent.h" +#include "SentryFeedback.h" #include "SentryModule.h" #include "SentryOutputDevice.h" #include "SentrySettings.h" @@ -15,7 +16,6 @@ #include "SentryTransaction.h" #include "SentryTransactionContext.h" #include "SentryUser.h" -#include "SentryUserFeedback.h" #include "CoreGlobals.h" #include "Engine/World.h" @@ -29,8 +29,9 @@ #include "Interface/SentrySubsystemInterface.h" +#include "HAL/PlatformSentryFeedback.h" +#include "HAL/PlatformSentryId.h" #include "HAL/PlatformSentrySubsystem.h" -#include "HAL/PlatformSentryUserFeedback.h" void USentrySubsystem::Initialize(FSubsystemCollectionBase& Collection) { @@ -341,32 +342,35 @@ FString USentrySubsystem::CaptureEventWithScope(USentryEvent* Event, const FConf return SentryId->ToString(); } -void USentrySubsystem::CaptureUserFeedback(USentryUserFeedback* UserFeedback) +void USentrySubsystem::CaptureFeedback(USentryFeedback* Feedback) { check(SubsystemNativeImpl); - check(UserFeedback); + check(Feedback); if (!SubsystemNativeImpl || !SubsystemNativeImpl->IsEnabled()) { return; } - SubsystemNativeImpl->CaptureUserFeedback(UserFeedback->GetNativeObject()); + SubsystemNativeImpl->CaptureFeedback(Feedback->GetNativeObject()); } -void USentrySubsystem::CaptureUserFeedbackWithParams(const FString& EventId, const FString& Email, const FString& Comments, const FString& Name) +void USentrySubsystem::CaptureFeedbackWithParams(const FString& Message, const FString& Name, const FString& Email, const FString& EventId) { check(SubsystemNativeImpl); - check(!EventId.IsEmpty()); + check(!Message.IsEmpty()); - USentryUserFeedback* UserFeedback = USentryUserFeedback::Create(CreateSharedSentryUserFeedback(EventId)); - check(UserFeedback); + USentryFeedback* Feedback = USentryFeedback::Create(MakeShareable(new FPlatformSentryFeedback(Message))); + check(Feedback); - UserFeedback->SetEmail(Email); - UserFeedback->SetComment(Comments); - UserFeedback->SetName(Name); + if (!Name.IsEmpty()) + Feedback->SetName(Name); + if (!Email.IsEmpty()) + Feedback->SetContactEmail(Email); + if (!EventId.IsEmpty()) + Feedback->SetAssociatedEvent(EventId); - CaptureUserFeedback(UserFeedback); + CaptureFeedback(Feedback); } void USentrySubsystem::SetUser(USentryUser* User) diff --git a/plugin-dev/Source/Sentry/Private/SentryUserFeedback.cpp b/plugin-dev/Source/Sentry/Private/SentryUserFeedback.cpp deleted file mode 100644 index 2b9dbc9a2..000000000 --- a/plugin-dev/Source/Sentry/Private/SentryUserFeedback.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#include "SentryUserFeedback.h" - -#include "HAL/PlatformSentryUserFeedback.h" - -void USentryUserFeedback::Initialize(const FString& EventId) -{ - if (ensure(!EventId.IsEmpty())) - { - NativeImpl = CreateSharedSentryUserFeedback(EventId); - } -} - -void USentryUserFeedback::SetName(const FString& Name) -{ - if (!NativeImpl) - return; - - NativeImpl->SetName(Name); -} - -FString USentryUserFeedback::GetName() const -{ - if (!NativeImpl) - return FString(); - - return NativeImpl->GetName(); -} - -void USentryUserFeedback::SetEmail(const FString& Email) -{ - if (!NativeImpl) - return; - - NativeImpl->SetEmail(Email); -} - -FString USentryUserFeedback::GetEmail() const -{ - if (!NativeImpl) - return FString(); - - return NativeImpl->GetEmail(); -} - -void USentryUserFeedback::SetComment(const FString& Comments) -{ - if (!NativeImpl) - return; - - NativeImpl->SetComment(Comments); -} - -FString USentryUserFeedback::GetComment() const -{ - if (!NativeImpl) - return FString(); - - return NativeImpl->GetComment(); -} diff --git a/plugin-dev/Source/Sentry/Private/Tests/SentryFeedback.spec.cpp b/plugin-dev/Source/Sentry/Private/Tests/SentryFeedback.spec.cpp new file mode 100644 index 000000000..d968e5e6a --- /dev/null +++ b/plugin-dev/Source/Sentry/Private/Tests/SentryFeedback.spec.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#include "SentryFeedback.h" +#include "SentryTests.h" + +#include "Misc/AutomationTest.h" + +#include "HAL/PlatformSentryFeedback.h" + +#if WITH_AUTOMATION_TESTS + +BEGIN_DEFINE_SPEC(SentryFeedbackSpec, "Sentry.SentryFeedback", EAutomationTestFlags::ProductFilter | SentryApplicationContextMask) + USentryFeedback* SentryFeedback; +END_DEFINE_SPEC(SentryFeedbackSpec) + +void SentryFeedbackSpec::Define() +{ + BeforeEach([this]() + { + SentryFeedback = USentryFeedback::Create(MakeShareable(new FPlatformSentryFeedback(TEXT("Test feedback")))); + }); + + Describe("Feedback message", [this]() + { + It("should not be empty", [this]() + { + TestFalse("Message", SentryFeedback->GetMessage().IsEmpty()); + }); + }); + + Describe("Feedback name", [this]() + { + It("should be empty if not set", [this]() + { + TestTrue("Name", SentryFeedback->GetName().IsEmpty()); + }); + + It("should be empty if initialized with empty string", [this]() + { + SentryFeedback->SetName(TEXT("")); + TestTrue("Name", SentryFeedback->GetName().IsEmpty()); + }); + + It("should retain its value", [this]() + { + const FString TestName = TEXT("John Doe"); + SentryFeedback->SetName(TestName); + TestEqual("Name", SentryFeedback->GetName(), TestName); + }); + }); + + Describe("Feedback contact email", [this]() + { + It("should be empty if not set", [this]() + { + TestTrue("Contact email", SentryFeedback->GetContactEmail().IsEmpty()); + }); + + It("should be empty if initialized with empty string", [this]() + { + SentryFeedback->SetContactEmail(TEXT("")); + TestTrue("Contact email", SentryFeedback->GetContactEmail().IsEmpty()); + }); + + It("should retain its value", [this]() + { + const FString TestEmail = TEXT("feedback-mail@example.org"); + SentryFeedback->SetContactEmail(TestEmail); + TestEqual("Contact email", SentryFeedback->GetContactEmail(), TestEmail); + }); + }); + + Describe("Feedback associated event", [this]() + { + It("should be empty if not set", [this]() + { + TestTrue("Event Id", SentryFeedback->GetAssociatedEvent().IsEmpty()); + }); + + It("should be empty if initialized with empty string", [this]() + { + SentryFeedback->SetAssociatedEvent(TEXT("")); + TestTrue("Event Id", SentryFeedback->GetAssociatedEvent().IsEmpty()); + }); + + It("should retain its value", [this]() + { + const FString TestEventId = TEXT("c3829f10764848442d813c4124cf44a0"); + SentryFeedback->SetAssociatedEvent(TestEventId); + TestEqual("Event Id", SentryFeedback->GetAssociatedEvent(), TestEventId); + }); + }); +} + +#endif diff --git a/plugin-dev/Source/Sentry/Public/SentryUserFeedback.h b/plugin-dev/Source/Sentry/Public/SentryFeedback.h similarity index 50% rename from plugin-dev/Source/Sentry/Public/SentryUserFeedback.h rename to plugin-dev/Source/Sentry/Public/SentryFeedback.h index 04d7ac2f1..d0d87486b 100644 --- a/plugin-dev/Source/Sentry/Public/SentryUserFeedback.h +++ b/plugin-dev/Source/Sentry/Public/SentryFeedback.h @@ -4,27 +4,30 @@ #include "SentryImplWrapper.h" -#include "SentryUserFeedback.generated.h" +#include "SentryFeedback.generated.h" -class USentryId; -class ISentryUserFeedback; +class ISentryFeedback; /** - * Additional information about what happened to an event. + * Additional information about what happened to be sent to Sentry. */ UCLASS(BlueprintType, NotBlueprintable, HideDropdown) -class SENTRY_API USentryUserFeedback : public UObject, public TSentryImplWrapper +class SENTRY_API USentryFeedback : public UObject, public TSentryImplWrapper { GENERATED_BODY() public: /** - * Initializes the user feedback with the event identifier to which it is associated. + * Initializes the user feedback with the provided message. * - * @param EventId The associated event identifier. + * @param Message The user feedback message to record. */ UFUNCTION(BlueprintCallable, Category = "Sentry") - void Initialize(const FString& EventId); + void Initialize(const FString& Message); + + /** Gets the feedback message. */ + UFUNCTION(BlueprintPure, Category = "Sentry") + FString GetMessage() const; /** Sets the name of the user. */ UFUNCTION(BlueprintCallable, Category = "Sentry") @@ -36,17 +39,17 @@ class SENTRY_API USentryUserFeedback : public UObject, public TSentryImplWrapper /** Sets the email of the user. */ UFUNCTION(BlueprintCallable, Category = "Sentry") - void SetEmail(const FString& Email); + void SetContactEmail(const FString& Email); /** Gets the email of the user. */ UFUNCTION(BlueprintPure, Category = "Sentry") - FString GetEmail() const; + FString GetContactEmail() const; - /** Sets comments of the user about what happened. */ + /** Sets associated event identifier. */ UFUNCTION(BlueprintCallable, Category = "Sentry") - void SetComment(const FString& Comments); + void SetAssociatedEvent(const FString& EventId); - /** Gets comments of the user about what happened. */ + /** Gets associated event identifier. */ UFUNCTION(BlueprintPure, Category = "Sentry") - FString GetComment() const; + FString GetAssociatedEvent() const; }; diff --git a/plugin-dev/Source/Sentry/Public/SentryLibrary.h b/plugin-dev/Source/Sentry/Public/SentryLibrary.h index 1a73a710f..53b09d986 100644 --- a/plugin-dev/Source/Sentry/Public/SentryLibrary.h +++ b/plugin-dev/Source/Sentry/Public/SentryLibrary.h @@ -13,7 +13,7 @@ class USentryTransactionContext; class USentryEvent; class USentryBreadcrumb; class USentryUser; -class USentryUserFeedback; +class USentryFeedback; class USentryAttachment; /** @@ -48,15 +48,15 @@ class SENTRY_API USentryLibrary : public UBlueprintFunctionLibrary const TMap& Data); /** - * Creates user feedback for the event. + * Creates user feedback. * - * @param EventId Id of the event to which user feedback is associated. - * @param Name Name of the user. - * @param Email Email of the user. - * @param Comments Comments of the user about what happened. + * @param Message User feedback message (required). + * @param Name User name. + * @param Email User email. + * @param EventId Associated event identifier. */ UFUNCTION(BlueprintCallable, Category = "Sentry") - static USentryUserFeedback* CreateSentryUserFeedback(const FString& EventId, const FString& Name, const FString& Email, const FString& Comments); + static USentryFeedback* CreateSentryFeedback(const FString& Message, const FString& Name, const FString& Email, const FString& EventId); /** * Creates breadcrumb. diff --git a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h index ec14f30aa..4057fee62 100644 --- a/plugin-dev/Source/Sentry/Public/SentrySubsystem.h +++ b/plugin-dev/Source/Sentry/Public/SentrySubsystem.h @@ -14,7 +14,7 @@ class USentrySettings; class USentryBreadcrumb; class USentryEvent; -class USentryUserFeedback; +class USentryFeedback; class USentryUser; class USentryBeforeSendHandler; class USentryBeforeBreadcrumbHandler; @@ -167,21 +167,21 @@ class SENTRY_API USentrySubsystem : public UEngineSubsystem /** * Captures a user feedback. * - * @param UserFeedback The user feedback to send to Sentry. + * @param Feedback The feedback to send to Sentry. */ UFUNCTION(BlueprintCallable, Category = "Sentry") - void CaptureUserFeedback(USentryUserFeedback* UserFeedback); + void CaptureFeedback(USentryFeedback* Feedback); /** * Captures a user feedback. * - * @param EventId The event Id. - * @param Email The user email. - * @param Comments The user comments. - * @param Name The optional username. + * @param Message User feedback message (required). + * @param Name User name. + * @param Email User email. + * @param EventId Associated event identifier. */ UFUNCTION(BlueprintCallable, Category = "Sentry") - void CaptureUserFeedbackWithParams(const FString& EventId, const FString& Email, const FString& Comments, const FString& Name); + void CaptureFeedbackWithParams(const FString& Message, const FString& Name, const FString& Email, const FString& EventId); /** * Sets a user for the current scope. diff --git a/sample/Content/UI/W_SentryDemo.uasset b/sample/Content/UI/W_SentryDemo.uasset index 44642b68c..9ebff3763 100644 Binary files a/sample/Content/UI/W_SentryDemo.uasset and b/sample/Content/UI/W_SentryDemo.uasset differ diff --git a/scripts/packaging/package.snapshot b/scripts/packaging/package.snapshot index 0975bfced..b50804f0c 100644 --- a/scripts/packaging/package.snapshot +++ b/scripts/packaging/package.snapshot @@ -13,6 +13,8 @@ Source/Sentry/Private/Android/AndroidSentryBreadcrumb.cpp Source/Sentry/Private/Android/AndroidSentryBreadcrumb.h Source/Sentry/Private/Android/AndroidSentryEvent.cpp Source/Sentry/Private/Android/AndroidSentryEvent.h +Source/Sentry/Private/Android/AndroidSentryFeedback.cpp +Source/Sentry/Private/Android/AndroidSentryFeedback.h Source/Sentry/Private/Android/AndroidSentryHint.cpp Source/Sentry/Private/Android/AndroidSentryHint.h Source/Sentry/Private/Android/AndroidSentryId.cpp @@ -35,8 +37,6 @@ Source/Sentry/Private/Android/AndroidSentryTransactionOptions.cpp Source/Sentry/Private/Android/AndroidSentryTransactionOptions.h Source/Sentry/Private/Android/AndroidSentryUser.cpp Source/Sentry/Private/Android/AndroidSentryUser.h -Source/Sentry/Private/Android/AndroidSentryUserFeedback.cpp -Source/Sentry/Private/Android/AndroidSentryUserFeedback.h Source/Sentry/Private/Android/Callbacks/AndroidSentryScopeCallback.cpp Source/Sentry/Private/Android/Callbacks/AndroidSentryScopeCallback.h Source/Sentry/Private/Android/Infrastructure/AndroidSentryConverters.cpp @@ -54,6 +54,8 @@ Source/Sentry/Private/Apple/AppleSentryBreadcrumb.cpp Source/Sentry/Private/Apple/AppleSentryBreadcrumb.h Source/Sentry/Private/Apple/AppleSentryEvent.cpp Source/Sentry/Private/Apple/AppleSentryEvent.h +Source/Sentry/Private/Apple/AppleSentryFeedback.cpp +Source/Sentry/Private/Apple/AppleSentryFeedback.h Source/Sentry/Private/Apple/AppleSentryId.cpp Source/Sentry/Private/Apple/AppleSentryId.h Source/Sentry/Private/Apple/AppleSentrySamplingContext.cpp @@ -70,8 +72,6 @@ Source/Sentry/Private/Apple/AppleSentryTransactionContext.cpp Source/Sentry/Private/Apple/AppleSentryTransactionContext.h Source/Sentry/Private/Apple/AppleSentryUser.cpp Source/Sentry/Private/Apple/AppleSentryUser.h -Source/Sentry/Private/Apple/AppleSentryUserFeedback.cpp -Source/Sentry/Private/Apple/AppleSentryUserFeedback.h Source/Sentry/Private/Apple/Convenience/AppleSentryInclude.h Source/Sentry/Private/Apple/Convenience/AppleSentryMacro.h Source/Sentry/Private/Apple/Infrastructure/AppleSentryConverters.cpp @@ -87,6 +87,8 @@ Source/Sentry/Private/GenericPlatform/GenericPlatformSentryBreadcrumb.cpp Source/Sentry/Private/GenericPlatform/GenericPlatformSentryBreadcrumb.h Source/Sentry/Private/GenericPlatform/GenericPlatformSentryEvent.cpp Source/Sentry/Private/GenericPlatform/GenericPlatformSentryEvent.h +Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.cpp +Source/Sentry/Private/GenericPlatform/GenericPlatformSentryFeedback.h Source/Sentry/Private/GenericPlatform/GenericPlatformSentryId.cpp Source/Sentry/Private/GenericPlatform/GenericPlatformSentryId.h Source/Sentry/Private/GenericPlatform/GenericPlatformSentryScope.cpp @@ -101,14 +103,13 @@ Source/Sentry/Private/GenericPlatform/GenericPlatformSentryTransactionContext.cp Source/Sentry/Private/GenericPlatform/GenericPlatformSentryTransactionContext.h Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUser.cpp Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUser.h -Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.cpp -Source/Sentry/Private/GenericPlatform/GenericPlatformSentryUserFeedback.h Source/Sentry/Private/GenericPlatform/Infrastructure/GenericPlatformSentryConverters.cpp Source/Sentry/Private/GenericPlatform/Infrastructure/GenericPlatformSentryConverters.h Source/Sentry/Private/HAL/PlatformSentryAttachment.h Source/Sentry/Private/HAL/PlatformSentryBreadcrumb.h Source/Sentry/Private/HAL/PlatformSentryDefines.h Source/Sentry/Private/HAL/PlatformSentryEvent.h +Source/Sentry/Private/HAL/PlatformSentryFeedback.h Source/Sentry/Private/HAL/PlatformSentryHint.h Source/Sentry/Private/HAL/PlatformSentryId.h Source/Sentry/Private/HAL/PlatformSentrySamplingContext.h @@ -118,10 +119,10 @@ Source/Sentry/Private/HAL/PlatformSentrySubsystem.h Source/Sentry/Private/HAL/PlatformSentryTransaction.h Source/Sentry/Private/HAL/PlatformSentryTransactionContext.h Source/Sentry/Private/HAL/PlatformSentryUser.h -Source/Sentry/Private/HAL/PlatformSentryUserFeedback.h Source/Sentry/Private/Interface/SentryAttachmentInterface.h Source/Sentry/Private/Interface/SentryBreadcrumbInterface.h Source/Sentry/Private/Interface/SentryEventInterface.h +Source/Sentry/Private/Interface/SentryFeedbackInterface.h Source/Sentry/Private/Interface/SentryHintInterface.h Source/Sentry/Private/Interface/SentryIdInterface.h Source/Sentry/Private/Interface/SentrySamplingContextInterface.h @@ -130,7 +131,6 @@ Source/Sentry/Private/Interface/SentrySpanInterface.h Source/Sentry/Private/Interface/SentrySubsystemInterface.h Source/Sentry/Private/Interface/SentryTransactionContextInterface.h Source/Sentry/Private/Interface/SentryTransactionInterface.h -Source/Sentry/Private/Interface/SentryUserFeedbackInterface.h Source/Sentry/Private/Interface/SentryUserInterface.h Source/Sentry/Private/IOS/IOSSentrySubsystem.cpp Source/Sentry/Private/IOS/IOSSentrySubsystem.h @@ -145,6 +145,7 @@ Source/Sentry/Private/Microsoft/MicrosoftSentrySubsystem.h Source/Sentry/Private/Null/NullSentryAttachment.h Source/Sentry/Private/Null/NullSentryBreadcrumb.h Source/Sentry/Private/Null/NullSentryEvent.h +Source/Sentry/Private/Null/NullSentryFeedback.h Source/Sentry/Private/Null/NullSentryHint.h Source/Sentry/Private/Null/NullSentryId.h Source/Sentry/Private/Null/NullSentrySamplingContext.h @@ -154,7 +155,6 @@ Source/Sentry/Private/Null/NullSentrySubsystem.h Source/Sentry/Private/Null/NullSentryTransaction.h Source/Sentry/Private/Null/NullSentryTransactionContext.h Source/Sentry/Private/Null/NullSentryUser.h -Source/Sentry/Private/Null/NullSentryUserFeedback.h Source/Sentry/Private/SentryAttachment.cpp Source/Sentry/Private/SentryBeforeBreadcrumbHandler.cpp Source/Sentry/Private/SentryBeforeSendHandler.cpp @@ -162,6 +162,7 @@ Source/Sentry/Private/SentryBreadcrumb.cpp Source/Sentry/Private/SentryDefines.h Source/Sentry/Private/SentryErrorOutputDevice.cpp Source/Sentry/Private/SentryEvent.cpp +Source/Sentry/Private/SentryFeedback.cpp Source/Sentry/Private/SentryHint.cpp Source/Sentry/Private/SentryLibrary.cpp Source/Sentry/Private/SentryModule.cpp @@ -175,10 +176,10 @@ Source/Sentry/Private/SentryTraceSampler.cpp Source/Sentry/Private/SentryTransaction.cpp Source/Sentry/Private/SentryTransactionContext.cpp Source/Sentry/Private/SentryUser.cpp -Source/Sentry/Private/SentryUserFeedback.cpp Source/Sentry/Private/SentryVariant.cpp Source/Sentry/Private/Tests/SentryBreadcrumb.spec.cpp Source/Sentry/Private/Tests/SentryEvent.spec.cpp +Source/Sentry/Private/Tests/SentryFeedback.spec.cpp Source/Sentry/Private/Tests/SentryScope.spec.cpp Source/Sentry/Private/Tests/SentryScopeBeforeSendHandler.h Source/Sentry/Private/Tests/SentrySubsystem.spec.cpp @@ -202,6 +203,7 @@ Source/Sentry/Public/SentryBreadcrumb.h Source/Sentry/Public/SentryDataTypes.h Source/Sentry/Public/SentryErrorOutputDevice.h Source/Sentry/Public/SentryEvent.h +Source/Sentry/Public/SentryFeedback.h Source/Sentry/Public/SentryHint.h Source/Sentry/Public/SentryImplWrapper.h Source/Sentry/Public/SentryLibrary.h @@ -216,7 +218,6 @@ Source/Sentry/Public/SentryTraceSampler.h Source/Sentry/Public/SentryTransaction.h Source/Sentry/Public/SentryTransactionContext.h Source/Sentry/Public/SentryUser.h -Source/Sentry/Public/SentryUserFeedback.h Source/Sentry/Public/SentryVariant.h Source/Sentry/Sentry_Android_UPL.xml Source/Sentry/Sentry_IOS_UPL.xml