Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions analytics/integration_test/src/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -341,4 +341,70 @@ TEST_F(FirebaseAnalyticsTest, TestSetConsent) {
did_test_setconsent_ = true;
}

TEST_F(FirebaseAnalyticsTest, TestDefaultEventParametersUsage) {
LogInfo(
"Testing SetDefaultEventParameters with initial values, then updating "
"with Null.");

std::map<std::string, firebase::Variant> initial_defaults;
initial_defaults["initial_key"] = "initial_value";
initial_defaults["key_to_be_nulled"] = "text_before_null";
initial_defaults["numeric_default"] = 12345;

LogInfo("Setting initial default event parameters.");
firebase::analytics::SetDefaultEventParameters(initial_defaults);
// Log an event that would pick up these defaults.
firebase::analytics::LogEvent("event_with_initial_defaults");
LogInfo("Logged event_with_initial_defaults.");
ProcessEvents(
500); // Short pause for event logging, if it matters for backend.

std::map<std::string, firebase::Variant> updated_defaults;
updated_defaults["key_to_be_nulled"] = firebase::Variant::Null();
updated_defaults["another_key"] = "another_value";
// "initial_key" should persist if not overwritten.
// "numeric_default" should persist.

LogInfo(
"Updating default event parameters, setting key_to_be_nulled to Null.");
firebase::analytics::SetDefaultEventParameters(updated_defaults);
// Log an event that would pick up the updated defaults.
firebase::analytics::LogEvent("event_after_nulling_and_adding");
LogInfo("Logged event_after_nulling_and_adding.");
ProcessEvents(500);

// For this C++ SDK integration test, we primarily ensure API calls complete.
// Actual parameter presence on logged events would be verified by
// backend/native tests.
LogInfo(
"TestDefaultEventParametersUsage completed. Calls were made "
"successfully.");
}

TEST_F(FirebaseAnalyticsTest, TestClearDefaultEventParametersFunctionality) {
LogInfo("Testing ClearDefaultEventParameters.");

std::map<std::string, firebase::Variant> defaults_to_clear;
defaults_to_clear["default_one"] = "will_be_cleared";
defaults_to_clear["default_two"] = 9876;

LogInfo("Setting default parameters before clearing.");
firebase::analytics::SetDefaultEventParameters(defaults_to_clear);
// Log an event that would pick up these defaults.
firebase::analytics::LogEvent("event_before_global_clear");
LogInfo("Logged event_before_global_clear.");
ProcessEvents(500);

LogInfo("Calling ClearDefaultEventParameters.");
firebase::analytics::ClearDefaultEventParameters();
// Log an event that should not have the previous defaults.
firebase::analytics::LogEvent("event_after_global_clear");
LogInfo("Logged event_after_global_clear.");
ProcessEvents(500);

LogInfo(
"TestClearDefaultEventParametersFunctionality completed. Call was made "
"successfully.");
}

} // namespace firebase_testapp_automated
104 changes: 103 additions & 1 deletion analytics/src/analytics_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ static const ::firebase::App* g_app = nullptr;
"()Lcom/google/android/gms/tasks/Task;"), \
X(GetSessionId, "getSessionId", \
"()Lcom/google/android/gms/tasks/Task;"), \
X(SetDefaultEventParameters, "setDefaultEventParameters", \
"(Landroid/os/Bundle;)V", util::kMethodTypeInstance), \
X(GetInstance, "getInstance", "(Landroid/content/Context;)" \
"Lcom/google/firebase/analytics/FirebaseAnalytics;", \
firebase::util::kMethodTypeStatic)
Expand Down Expand Up @@ -512,7 +514,8 @@ void LogEvent(const char* name, const Parameter* parameters,
LogError(
"LogEvent(%s): %s is not a valid parameter value type. "
"No event was logged.",
parameter.name, Variant::TypeName(parameter.value.type()));
parameter.name,
firebase::Variant::TypeName(parameter.value.type()));
}
}
});
Expand Down Expand Up @@ -609,6 +612,105 @@ void ResetAnalyticsData() {
util::CheckAndClearJniExceptions(env);
}

void SetDefaultEventParameters(
const std::map<std::string, firebase::Variant>& default_parameters) {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
JNIEnv* env = g_app->GetJNIEnv();
if (!env) return;

jobject bundle =
env->NewObject(util::bundle::GetClass(),
util::bundle::GetMethodId(util::bundle::kConstructor));
if (util::CheckAndClearJniExceptions(env) || !bundle) {
LogError("Failed to create Bundle for SetDefaultEventParameters.");
if (bundle) env->DeleteLocalRef(bundle);
return;
}

for (const auto& pair : default_parameters) {
const Variant& value = pair.second;
const char* key_cstr = pair.first.c_str();

if (value.is_null()) {
jstring key_jstring = env->NewStringUTF(key_cstr);
if (util::CheckAndClearJniExceptions(env) || !key_jstring) {
LogError(
"SetDefaultEventParameters: Failed to create jstring for null "
"value key: %s",
key_cstr);
if (key_jstring) env->DeleteLocalRef(key_jstring);
continue;
}
env->CallVoidMethod(bundle,
util::bundle::GetMethodId(util::bundle::kPutString),
key_jstring, nullptr);
if (util::CheckAndClearJniExceptions(env)) {
LogError(
"SetDefaultEventParameters: Failed to put null string for key: %s",
key_cstr);
}
env->DeleteLocalRef(key_jstring);
} else if (value.is_string() || value.is_int64() || value.is_double() ||
value.is_bool()) {
// AddVariantToBundle handles these types and their JNI conversions.
// It also logs if an individual AddToBundle within it fails or if a type
// is unsupported by it.
if (!AddVariantToBundle(env, bundle, key_cstr, value)) {
// This specific log gives context that the failure happened during
// SetDefaultEventParameters for a type that was expected to be
// supported by AddVariantToBundle.
LogError(
"SetDefaultEventParameters: Failed to add parameter for key '%s' "
"with supported type '%s'. This might indicate a JNI issue during "
"conversion.",
key_cstr, Variant::TypeName(value.type()));
}
} else if (value.is_vector() || value.is_map()) {
LogError(
"SetDefaultEventParameters: Value for key '%s' has type '%s' which "
"is not supported for default event parameters. Only string, int64, "
"double, bool, and null are supported. Skipping.",
key_cstr, Variant::TypeName(value.type()));
} else {
// This case handles other fundamental Variant types that are not scalars
// and not vector/map.
LogError(
"SetDefaultEventParameters: Value for key '%s' has an unexpected and "
"unsupported type '%s'. Skipping.",
key_cstr, Variant::TypeName(value.type()));
}
}

env->CallVoidMethod(
g_analytics_class_instance,
analytics::GetMethodId(analytics::kSetDefaultEventParameters), bundle);
if (util::CheckAndClearJniExceptions(env)) {
LogError("Failed to call setDefaultEventParameters on Java instance.");
}

env->DeleteLocalRef(bundle);
}

void ClearDefaultEventParameters() {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
JNIEnv* env = g_app->GetJNIEnv();
if (!env) return;

// Calling with nullptr bundle should clear the parameters.
env->CallVoidMethod(
g_analytics_class_instance,
analytics::GetMethodId(analytics::kSetDefaultEventParameters), nullptr);
if (util::CheckAndClearJniExceptions(env)) {
// This might happen if the method isn't available on older SDKs,
// or if some other JNI error occurs.
LogError(
"Failed to call setDefaultEventParameters(null) on Java instance. "
"This may indicate the method is not available on this Android SDK "
"version "
"or another JNI error occurred.");
}
}

Future<std::string> GetAnalyticsInstanceId() {
FIREBASE_ASSERT_RETURN(GetAnalyticsInstanceIdLastResult(),
internal::IsInitialized());
Expand Down
40 changes: 39 additions & 1 deletion analytics/src/analytics_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ void LogEvent(const char* name, const Parameter* parameters, size_t number_of_pa
// A Variant type that couldn't be handled was passed in.
LogError("LogEvent(%s): %s is not a valid parameter value type. "
"No event was logged.",
parameter.name, Variant::TypeName(parameter.value.type()));
parameter.name, firebase::Variant::TypeName(parameter.value.type()));
}
}
[FIRAnalytics logEventWithName:@(name) parameters:parameters_dict];
Expand Down Expand Up @@ -373,6 +373,44 @@ void SetSessionTimeoutDuration(int64_t milliseconds) {
setSessionTimeoutInterval:static_cast<NSTimeInterval>(milliseconds) / kMillisecondsPerSecond];
}

void SetDefaultEventParameters(const std::map<std::string, firebase::Variant>& default_parameters) {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
NSMutableDictionary* ns_default_parameters =
[[NSMutableDictionary alloc] initWithCapacity:default_parameters.size()];
for (const auto& pair : default_parameters) {
NSString* key = SafeString(pair.first.c_str());
const Variant& value = pair.second;

if (value.is_null()) {
[ns_default_parameters setObject:[NSNull null] forKey:key];
} else if (value.is_int64()) {
[ns_default_parameters setObject:[NSNumber numberWithLongLong:value.int64_value()]
forKey:key];
} else if (value.is_double()) {
[ns_default_parameters setObject:[NSNumber numberWithDouble:value.double_value()] forKey:key];
} else if (value.is_string()) {
[ns_default_parameters setObject:SafeString(value.string_value()) forKey:key];
} else if (value.is_bool()) {
[ns_default_parameters setObject:[NSNumber numberWithBool:value.bool_value()] forKey:key];
} else if (value.is_vector() || value.is_map()) {
LogError("SetDefaultEventParameters: Value for key '%s' has type '%s' which is not supported "
"for default event parameters. Only string, int64, double, bool, and null are "
"supported. Skipping.",
pair.first.c_str(), Variant::TypeName(value.type()));
} else {
LogError("SetDefaultEventParameters: Value for key '%s' has an unexpected type '%s' which is "
"not supported. Skipping.",
pair.first.c_str(), Variant::TypeName(value.type()));
}
}
[FIRAnalytics setDefaultEventParameters:ns_default_parameters];
}

void ClearDefaultEventParameters() {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
[FIRAnalytics setDefaultEventParameters:nil];
}

void ResetAnalyticsData() {
MutexLock lock(g_mutex);
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
Expand Down
11 changes: 11 additions & 0 deletions analytics/src/analytics_stub.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ void SetSessionTimeoutDuration(int64_t /*milliseconds*/) {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
}

void SetDefaultEventParameters(
const std::map<std::string, firebase::Variant>& /*default_parameters*/) {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
// This is a stub implementation. No operation needed.
}

void ClearDefaultEventParameters() {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
// This is a stub implementation. No operation needed.
}

void ResetAnalyticsData() {
FIREBASE_ASSERT_RETURN_VOID(internal::IsInitialized());
g_fake_instance_id++;
Expand Down
21 changes: 19 additions & 2 deletions analytics/src/include/firebase/analytics.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ struct Parameter {
/// @param parameter_name Name of the parameter (see Parameter::name).
/// @param parameter_value Value for the parameter. Variants can
/// hold numbers and strings.
Parameter(const char* parameter_name, Variant parameter_value)
Parameter(const char* parameter_name, firebase::Variant parameter_value)
: name(parameter_name) {
value = parameter_value;
}
Expand Down Expand Up @@ -245,7 +245,7 @@ struct Parameter {
///
/// See firebase::Variant for usage information.
/// @note String values can be up to 100 characters long.
Variant value;
firebase::Variant value;
#endif // SWIG
};

Expand Down Expand Up @@ -558,6 +558,23 @@ void SetSessionTimeoutDuration(int64_t milliseconds);
/// instance id.
void ResetAnalyticsData();

/// @brief Sets the default event parameters.
///
/// These parameters will be automatically logged with all calls to `LogEvent`.
/// Default parameters are overridden by parameters supplied to the `LogEvent`
/// method.
///
/// When a value in the `default_parameters` map is
/// `firebase::Variant::Null()`, it signifies that the default parameter for
/// that specific key should be cleared.
///
/// @param[in] default_parameters A map of parameter names to Variant values.
void SetDefaultEventParameters(
const std::map<std::string, firebase::Variant>& default_parameters);

/// @brief Clears all default event parameters.
void ClearDefaultEventParameters();

/// Get the instance ID from the analytics service.
///
/// @note This is *not* the same ID as the ID returned by
Expand Down
2 changes: 2 additions & 0 deletions analytics/src_ios/fake/FIRAnalytics.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@

+ (void)resetAnalyticsData;

+ (void)setDefaultEventParameters:(nullable NSDictionary<NSString *, id> *)parameters;

@end
13 changes: 13 additions & 0 deletions analytics/src_ios/fake/FIRAnalytics.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
@implementation FIRAnalytics

+ (NSString *)stringForValue:(id)value {
if (value == [NSNull null]) {
return @"<NSNull>";
}
return [NSString stringWithFormat:@"%@", value];
}

Expand Down Expand Up @@ -94,4 +97,14 @@ + (void)resetAnalyticsData {
FakeReporter->AddReport("+[FIRAnalytics resetAnalyticsData]", {});
}

+ (void)setDefaultEventParameters:(nullable NSDictionary<NSString *, id> *)parameters {
if (parameters == nil) {
FakeReporter->AddReport("+[FIRAnalytics setDefaultEventParameters:]", {"nil"});
} else {
NSString *parameterString = [self stringForParameters:parameters];
FakeReporter->AddReport("+[FIRAnalytics setDefaultEventParameters:]",
{ [parameterString UTF8String] });
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,31 @@ public void setSessionTimeoutDuration(long milliseconds) {
FakeReporter.addReport(
"FirebaseAnalytics.setSessionTimeoutDuration", Long.toString(milliseconds));
}

public void setDefaultEventParameters(Bundle bundle) {
if (bundle == null) {
FakeReporter.addReport("FirebaseAnalytics.setDefaultEventParameters", "null");
} else {
StringBuilder paramsString = new StringBuilder();
// Sort keys for predictable ordering.
for (String key : new TreeSet<>(bundle.keySet())) {
paramsString.append(key);
paramsString.append("=");
Object value = bundle.get(key); // Get as Object first
if (value == null) {
// This case handles when bundle.putString(key, null) was called.
paramsString.append("<null_string_value>");
} else {
paramsString.append(value.toString());
}
paramsString.append(",");
}
// Remove trailing comma if paramsString is not empty
if (paramsString.length() > 0) {
paramsString.setLength(paramsString.length() - 1);
}
FakeReporter.addReport(
"FirebaseAnalytics.setDefaultEventParameters", paramsString.toString());
}
}
}
Loading
Loading