Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 23.2.4
- Mitigated an issue where cached events were not queued when a user property was recorded.

## 23.2.3
- Mitigated an issue where the new device ID was used when ending a session if device ID was changed without merging.

Expand Down
2 changes: 2 additions & 0 deletions include/countly.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class Countly : public cly::CountlyDelegates {

void enableManualSessionControl();

void disableAutoEventsOnUserProperties();

void setHTTPClient(HTTPClientFunction fun);

void setMetrics(const std::string &os, const std::string &os_version, const std::string &device, const std::string &resolution, const std::string &carrier, const std::string &app_version);
Expand Down
2 changes: 1 addition & 1 deletion include/countly/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include <string>

#define COUNTLY_SDK_NAME "cpp-native-unknown"
#define COUNTLY_SDK_VERSION "23.2.3"
#define COUNTLY_SDK_VERSION "23.2.4"
#define COUNTLY_POST_THRESHOLD 2000
#define COUNTLY_KEEPALIVE_INTERVAL 3000
#define COUNTLY_MAX_EVENTS_DEFAULT 200
Expand Down
2 changes: 2 additions & 0 deletions include/countly/countly_configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ struct CountlyConfiguration {

bool manualSessionControl = false;

bool autoEventsOnUserProperties = true;

HTTPClientFunction http_client_function = nullptr;

nlohmann::json metrics;
Expand Down
42 changes: 33 additions & 9 deletions src/countly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,20 @@ void Countly::enableManualSessionControl() {
mutex->unlock();
}

/**
* Disable automatic events on user properties changes.
*/
void Countly::disableAutoEventsOnUserProperties() {
if (is_sdk_initialized) {
log(LogLevel::WARNING, "[Countly][disableAutoEventsOnUserProperties] You can not disable automatic events on user properties after SDK initialization.");
return;
}

mutex->lock();
configuration->autoEventsOnUserProperties = false;
mutex->unlock();
}

void Countly::setMetrics(const std::string &os, const std::string &os_version, const std::string &device, const std::string &resolution, const std::string &carrier, const std::string &app_version) {
if (is_sdk_initialized) {
log(LogLevel::WARNING, "[Countly][setMetrics] You can not set metrics after SDK initialization.");
Expand Down Expand Up @@ -182,6 +196,12 @@ void Countly::setUserDetails(const std::map<std::string, std::string> &value) {
return;
}

if (configuration->autoEventsOnUserProperties == true) {
mutex->unlock();
flushEvents();
mutex->lock();
}

std::map<std::string, std::string> data = {{"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}, {"user_details", session_params["user_details"].dump()}};

requestModule->addRequestToQueue(data);
Expand All @@ -198,6 +218,12 @@ void Countly::setCustomUserDetails(const std::map<std::string, std::string> &val
return;
}

if (configuration->autoEventsOnUserProperties == true) {
mutex->unlock();
flushEvents();
mutex->lock();
}

std::map<std::string, std::string> data = {{"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}, {"user_details", session_params["user_details"].dump()}};
requestModule->addRequestToQueue(data);

Expand Down Expand Up @@ -345,7 +371,7 @@ void Countly::_changeDeviceIdWithoutMerge(const std::string &value) {

// send all event to server and end current session of old user
flushEvents();
if(configuration->manualSessionControl == false){
if (configuration->manualSessionControl == false) {
endSession();
}

Expand All @@ -355,10 +381,9 @@ void Countly::_changeDeviceIdWithoutMerge(const std::string &value) {
mutex->unlock();

// start a new session for new user
if(configuration->manualSessionControl == false){
if (configuration->manualSessionControl == false) {
beginSession();
}

}
#pragma endregion Device Id

Expand Down Expand Up @@ -439,7 +464,7 @@ void Countly::start(const std::string &app_key, const std::string &host, int por

if (!running) {

if(configuration->manualSessionControl == false){
if (configuration->manualSessionControl == false) {
mutex->unlock();
beginSession();
mutex->lock();
Expand Down Expand Up @@ -612,7 +637,7 @@ bool Countly::attemptSessionUpdateEQ() {
return false;
}
#endif
if(configuration->manualSessionControl == false){
if (configuration->manualSessionControl == false) {
return !updateSession();
} else {
packEvents();
Expand Down Expand Up @@ -735,7 +760,7 @@ bool Countly::updateSession() {
mutex->lock();
if (began_session == false) {
mutex->unlock();
if(configuration->manualSessionControl == true){
if (configuration->manualSessionControl == true) {
log(LogLevel::WARNING, "[Countly][updateSession] SDK is in manual session control mode and there is no active session. Please start a session first.");
return false;
}
Expand Down Expand Up @@ -829,7 +854,7 @@ void Countly::packEvents() {
} else {
log(LogLevel::DEBUG, "[Countly][packEvents] EQ empty.");
}
// report events if there are any to request queue
// report events if there are any to request queue
if (!no_events) {
sendEventsToRQ(events);
}
Expand All @@ -852,7 +877,6 @@ void Countly::packEvents() {
mutex->unlock();
}


void Countly::sendEventsToRQ(const nlohmann::json &events) {
log(LogLevel::DEBUG, "[Countly][sendEventsToRQ] Sending events to RQ.");
std::map<std::string, std::string> data = {{"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}, {"events", events.dump()}};
Expand All @@ -861,7 +885,7 @@ void Countly::sendEventsToRQ(const nlohmann::json &events) {

bool Countly::endSession() {
log(LogLevel::INFO, "[Countly][endSession]");
if(began_session == false) {
if (began_session == false) {
log(LogLevel::DEBUG, "[Countly][endSession] There is no active session to end.");
return true;
}
Expand Down
89 changes: 89 additions & 0 deletions tests/event_queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,93 @@ TEST_CASE("Tests that sets 'setEventsToRQThreshold' before and after SDK starts"
nlohmann::json events = nlohmann::json::parse(oldest_call.data["events"]);
CHECK(events.size() == 3);
}
}

TEST_CASE("Tests that saving user details trigger flushing EQ"){
clearSDK();
Countly &countly = Countly::getInstance();

// Automatic saving of events before user props calls
SUBCASE("Saving user properties should flush EQ") {
countly.enableManualSessionControl();
test_utils::initCountlyWithFakeNetworking(true, countly);

test_utils::generateEvents(4, countly);
CHECK(countly.checkEQSize() == 4);

// set user properties, this should flush the EQ
countly.setUserDetails({{"name", "Full name"}});
CHECK(countly.checkEQSize() == 0);

test_utils::generateEvents(4, countly);
CHECK(countly.checkEQSize() == 4);

// set custom user properties, this should flush the EQ
countly.setCustomUserDetails({{"custom_key", "custom_value"}});
CHECK(countly.checkEQSize() == 0);

// RQ should have 4 events and user details
// trigger RQ to send requests to http_call_queue
countly.processRQDebug();
// queue should have 4 requests
CHECK(!http_call_queue.empty());
CHECK(http_call_queue.size() == 4);
HTTPCall eventsReq1 = http_call_queue.front();
http_call_queue.pop_front();
HTTPCall userDetails = http_call_queue.front();
http_call_queue.pop_front();
HTTPCall eventsReq2 = http_call_queue.front();
http_call_queue.pop_front();
HTTPCall customUserDetails = http_call_queue.front();
http_call_queue.pop_front();
CHECK(http_call_queue.size() == 0);

// last call should have 4 events
nlohmann::json events1 = nlohmann::json::parse(eventsReq1.data["events"]);
CHECK(events1.size() == 4);
nlohmann::json userDetailsJson = nlohmann::json::parse(userDetails.data["user_details"]);
CHECK(userDetailsJson["name"] == "Full name");

nlohmann::json events2 = nlohmann::json::parse(eventsReq2.data["events"]);
CHECK(events2.size() == 4);
nlohmann::json customUserDetailsJson = nlohmann::json::parse(customUserDetails.data["user_details"]);
CHECK(customUserDetailsJson["custom"]["custom_key"] == "custom_value");
}

// Automatic saving of events before user props calls
SUBCASE("Saving user properties should not flush EQ when behavior is disabled") {
countly.enableManualSessionControl();
countly.disableAutoEventsOnUserProperties();
test_utils::initCountlyWithFakeNetworking(true, countly);

test_utils::generateEvents(4, countly);
CHECK(countly.checkEQSize() == 4);

// set user properties, this should flush the EQ
countly.setUserDetails({{"name", "Full name"}});
CHECK(countly.checkEQSize() == 4);

test_utils::generateEvents(4, countly);
CHECK(countly.checkEQSize() == 8);

// set custom user properties, this should flush the EQ
countly.setCustomUserDetails({{"custom_key", "custom_value"}});
CHECK(countly.checkEQSize() == 8);
// RQ should have 4 events and user details
// trigger RQ to send requests to http_call_queue
countly.processRQDebug();
// queue should have 2 requests
CHECK(!http_call_queue.empty());
CHECK(http_call_queue.size() == 2);
HTTPCall userDetails = http_call_queue.front();
http_call_queue.pop_front();
HTTPCall customUserDetails = http_call_queue.front();
http_call_queue.pop_front();
CHECK(http_call_queue.size() == 0);

nlohmann::json userDetailsJson = nlohmann::json::parse(userDetails.data["user_details"]);
CHECK(userDetailsJson["name"] == "Full name");
nlohmann::json customUserDetailsJson = nlohmann::json::parse(customUserDetails.data["user_details"]);
CHECK(customUserDetailsJson["custom"]["custom_key"] == "custom_value");
}
}