Skip to content

Analytics SetDefaultParameters #1262

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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
58 changes: 58 additions & 0 deletions analytics/src/FirebaseAnalytics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
*/

using System.Threading.Tasks;
using Firebase.Platform;

namespace Firebase.Analytics {

public static partial class FirebaseAnalytics {

private static readonly Firebase.Platform.ModuleLogger logger = new Firebase.Platform.ModuleLogger("FirebaseAnalytics");

/// Get the instance ID from the analytics service.
///
/// @returns A Task with the Analytics instance ID.
Expand Down Expand Up @@ -248,6 +251,61 @@ public static void SetConsent(System.Collections.Generic.IDictionary< ConsentTyp
FirebaseAnalyticsInternal.SetConsentWithInts(consentSettingsMap);
}

/// @brief Sets default event parameters.
///
/// These parameters are logged with all events, in addition to the parameters passed to the
/// LogEvent() call. They are useful for logging common parameters with all events.
/// Default event parameters are overridden by event-specific parameters if the names are the
/// same. Default parameters are removed if the dictionary is null or empty.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change it so that default parameters are only cleared if the dictionary is null. Individual parameters will be removed (by the underlying implementation) if the value of a given key is null.

///
/// @param[in] parameters The dictionary of parameters to set.
/// If null, clears all default parameters.
public static void SetDefaultEventParameters(
System.Collections.Generic.IDictionary<string, object> parameters) {
if (parameters == null) {
logger.LogMessage(LogLevel.Debug, "Input parameters dictionary is null. Clearing all default event parameters.");
FirebaseAnalyticsInternal.ClearDefaultEventParameters();
} else {
StringList parameterNames = new StringList();
VariantList parameterValues = new VariantList();

if (parameters.Count == 0) {
logger.LogMessage(LogLevel.Debug, "Input parameters dictionary is empty. Clearing all default event parameters via empty map.");
// Proceed with empty lists, C++ SDK will clear defaults when given an empty map.
} else {
logger.LogMessage(LogLevel.Debug, string.Format("Processing {0} default event parameter(s).", parameters.Count));
foreach (var kvp in parameters) {
try {
Firebase.Variant variantValue = Firebase.Variant.FromObject(kvp.Value);
// Assuming Variant.FromObject(null) correctly yields a Variant.Null()
// and doesn't throw for it, or that Variant.Null() is a valid state.
// If Variant.FromObject might return an "invalid" Variant object for certain inputs
// instead of throwing (e.g. for unsupported types that don't cause exceptions),
// an additional check like `if (!variantValue.IsValid())` might be needed here.
// For now, relying on try-catch for conversion failures.

parameterNames.Add(kvp.Key);
parameterValues.Add(variantValue);
} catch (System.Exception e) {
logger.LogMessage(LogLevel.Warning, string.Format(
"Failed to convert default parameter '{0}' (value type: {1}). Skipping. Error: {2}",
kvp.Key, kvp.Value?.GetType().FullName ?? "null", e.Message));
}
}
}

// If parameters.Count was > 0 but all failed conversion, parameterNames will be empty.
// Sending empty lists to C++ SetDefaultEventParameters (with map) results in clearing.
if (parameters.Count > 0 && parameterNames.Count == 0) {
logger.LogMessage(LogLevel.Warning, "All supplied default parameters failed conversion. This will result in clearing all default parameters.");
} else if (parameterNames.Count > 0) {
logger.LogMessage(LogLevel.Debug, string.Format("Setting {0} default event parameter(s) via helper.", parameterNames.Count));
}
// Always call SetDefaultEventParametersHelper. If lists are empty, it clears params.
FirebaseAnalyticsInternal.SetDefaultEventParametersHelper(parameterNames, parameterValues);
}
}

/// @brief Sets the duration of inactivity that terminates the current session.
///
/// @note The default value is 30 minutes.
Expand Down
24 changes: 24 additions & 0 deletions analytics/src/swig/analytics.i
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,24 @@ void SetConsentWithInts(const std::map<int, int>& settings) {
SetConsent(converted);
}

// Helper function to set default event parameters from vectors of strings and variants.
void SetDefaultEventParametersHelper(
const std::vector<std::string>& parameter_names,
const std::vector<firebase::Variant>& parameter_values) {
if (parameter_names.size() != parameter_values.size()) {
firebase::LogError(
"SetDefaultEventParametersHelper given different list sizes (%d, %d)",
parameter_names.size(), parameter_values.size());
return;
}

std::map<std::string, firebase::Variant> default_parameters;
for (size_t i = 0; i < parameter_names.size(); ++i) {
default_parameters[parameter_names[i]] = parameter_values[i];
}
SetDefaultEventParameters(default_parameters);
}

} // namespace analytics
} // namespace firebase

Expand All @@ -100,6 +118,8 @@ void SetConsentWithInts(const std::map<int, int>& settings) {
%ignore firebase::analytics::LogEvent(const char*, const Parameter*, size_t);
// Ignore SetConsent, in order to convert the types with our own function.
%ignore firebase::analytics::SetConsent;
// Ignore SetDefaultEventParameters, as we handle it with a custom helper.
%ignore firebase::analytics::SetDefaultEventParameters;
// Ignore the Parameter class, as we don't want to expose that to C# at all.
%ignore firebase::analytics::Parameter;

Expand All @@ -121,5 +141,9 @@ namespace analytics {
void LogEvent(const char* name, std::vector<std::string> parameter_names,
std::vector<firebase::Variant> parameter_values);
void SetConsentWithInts(const std::map<int, int>& settings);
void SetDefaultEventParametersHelper(
const std::vector<std::string>& parameter_names,
const std::vector<firebase::Variant>& parameter_values);
void ClearDefaultEventParameters();
} // namespace analytics
} // namespace firebase
29 changes: 29 additions & 0 deletions analytics/testapp/Assets/Firebase/Sample/Analytics/UIHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,32 @@ public void AnalyticsSetConsent() {
});
}

public void TestSetDefaultEventParameters() {
DebugLog("Starting TestSetDefaultEventParameters...");

DebugLog("Setting single default string parameter: {'default_param_string', 'default_value_1'}");
FirebaseAnalytics.SetDefaultEventParameters(new Dictionary<string, object>
{
{ "default_param_string", "default_value_1" }
});

DebugLog("Setting multiple default parameters: {'default_param_int', 123, 'default_param_double', 45.67, 'default_param_bool', true}");
FirebaseAnalytics.SetDefaultEventParameters(new Dictionary<string, object>
{
{ "default_param_int", 123 },
{ "default_param_double", 45.67 },
{ "default_param_bool", true }
});

DebugLog("Clearing default parameters with null.");
FirebaseAnalytics.SetDefaultEventParameters(null);

DebugLog("Clearing default parameters with empty dictionary.");
FirebaseAnalytics.SetDefaultEventParameters(new Dictionary<string, object>());

DebugLog("TestSetDefaultEventParameters completed.");
}

// Get the current app instance ID.
public Task<string> DisplayAnalyticsInstanceId() {
return FirebaseAnalytics.GetAnalyticsInstanceIdAsync().ContinueWithOnMainThread(task => {
Expand Down Expand Up @@ -257,6 +283,9 @@ void GUIDisplayControls() {
if (GUILayout.Button("Test SetConsent")) {
AnalyticsSetConsent();
}
if (GUILayout.Button("Test SetDefaultEventParameters")) {
TestSetDefaultEventParameters();
}
GUILayout.EndVertical();
GUILayout.EndScrollView();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public override void Start() {
TestAnalyticsSetConsentDoesNotThrow,
TestInstanceIdChangeAfterReset,
TestResetAnalyticsData,
TestSetDefaultEventParametersAutomated, // Renamed test here
// Temporarily disabled until this test is deflaked. b/143603151
//TestCheckAndFixDependenciesInvalidOperation,
TestCheckAndFixDependenciesDoubleCall,
Expand Down Expand Up @@ -108,6 +109,40 @@ Task TestAnalyticsSetConsentDoesNotThrow() {
return true;
});
}

Task TestSetDefaultEventParametersAutomated() {
DebugLog("Automated Test: Starting TestSetDefaultEventParametersAutomated...");

DebugLog("Automated Test: Setting single default string parameter: {'auto_default_param_string', 'auto_value_1'}");
FirebaseAnalytics.SetDefaultEventParameters(new Dictionary<string, object>
{
{ "auto_default_param_string", "auto_value_1" }
});

DebugLog("Automated Test: Setting multiple default parameters: {'auto_default_param_int', 789, 'auto_default_param_double', 12.34, 'auto_default_param_bool', false}");
FirebaseAnalytics.SetDefaultEventParameters(new Dictionary<string, object>
{
{ "auto_default_param_int", 789 },
{ "auto_default_param_double", 12.34 },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also test with a value of null to make sure that that's passed through correctly.

{ "auto_default_param_bool", false }
});

DebugLog("Automated Test: Setting a default parameter with a null value: {'param_with_null_value', null}");
FirebaseAnalytics.SetDefaultEventParameters(new Dictionary<string, object>
{
{ "param_with_null_value", null }
});

DebugLog("Automated Test: Clearing default parameters with null.");
FirebaseAnalytics.SetDefaultEventParameters(null);

DebugLog("Automated Test: Clearing default parameters with empty dictionary.");
FirebaseAnalytics.SetDefaultEventParameters(new Dictionary<string, object>());

DebugLog("Automated Test: TestSetDefaultEventParametersAutomated completed.");
return Task.FromResult(true); // Indicate successful completion
}

Task TestCheckAndFixDependenciesInvalidOperation() {
// Only run the test on Android, as CheckAndFixDependenciesAsync is short
// lived on other platforms, and thus could finish before the extra call.
Expand Down