Skip to content

Commit

Permalink
RDK-54098: Support event mapping in Analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianM27 authored and Adrian Muzyka committed Jan 9, 2025
1 parent 84607b3 commit 43375d8
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 9 deletions.
1 change: 1 addition & 0 deletions .github/workflows/L2-tests-R4-4-1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ jobs:
-DPLUGIN_ANALYTICS_SIFT_MAX_RANDOMISATION_WINDOW_TIME=15
-DPLUGIN_ANALYTICS_SIFT_STORE_PATH="/tmp/AnalyticsSiftStore"
-DPLUGIN_ANALYTICS_SIFT_URL="127.0.0.1:12345"
-DPLUGIN_ANALYTICS_EVENTS_MAP="/tmp/AnalyticsEventsMap.json"
&&
cmake --build build/rdkservices -j8
&&
Expand Down
1 change: 1 addition & 0 deletions Analytics/Analytics.conf.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ startuporder = "@PLUGIN_ANALYTICS_STARTUPORDER@"
configuration = JSON()

configuration.add("deviceosname", "@PLUGIN_ANALYTICS_DEVICE_OS_NAME@")
configuration.add("eventsmap", "@PLUGIN_ANALYTICS_EVENTS_MAP@")

if boolean("@PLUGIN_ANALYTICS_SIFT_BACKEND_ENABLED@"):
sift = JSON()
Expand Down
1 change: 1 addition & 0 deletions Analytics/Analytics.config
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ endif()

map()
kv(deviceosname ${PLUGIN_ANALYTICS_DEVICE_OS_NAME})
kv(eventsmap, ${PLUGIN_ANALYTICS_EVENTS_MAP})
end()
ans(configuration)

Expand Down
4 changes: 4 additions & 0 deletions Analytics/AnalyticsPlugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
"description":"Device OS name",
"type":"string"
},
"eventsmap":{
"description":"Optional path to json file with array of mapped events name",
"type":"string"
},
"sift":{
"type":"object",
"properties":{
Expand Down
3 changes: 3 additions & 0 deletions Analytics/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ All notable changes to this RDK Service will be documented in this file.
Changes in CHANGELOG should be updated when commits are added to the main or release branches. There should be one CHANGELOG entry per JIRA Ticket. This is not enforced on sprint branches since there could be multiple changes for the same JIRA ticket during development.

For more details, refer to versioning section under Main README.

## [1.0.2] - 2024-12-17
- Support optional event mapping json configuration

## [1.0.2] - 2024-12-04
- Documentation update
Expand Down
1 change: 1 addition & 0 deletions Analytics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ option(PLUGIN_ANALYTICS_SIFT_BACKEND "Enable Sift backend" OFF)
set(PLUGIN_ANALYTICS_STARTUPORDER "" CACHE STRING "To configure startup order of Analytics plugin")
set(PLUGIN_ANALYTICS_AUTOSTART "false" CACHE STRING "Automatically start Analytics plugin")
set(PLUGIN_ANALYTICS_DEVICE_OS_NAME "rdk" CACHE STRING "Device OS name")
set(PLUGIN_ANALYTICS_EVENTS_MAP "" CACHE STRING "Optional path to events mapping file")

set(PLUGIN_ANALYTICS_SIFT_BACKEND_ENABLED ${PLUGIN_ANALYTICS_SIFT_BACKEND} CACHE BOOL "Enable Sift backend configuration")
set(PLUGIN_ANALYTICS_SIFT_2_0_ENABLED "false" CACHE STRING "Enable Sift 2.0 schema")
Expand Down
152 changes: 149 additions & 3 deletions Analytics/Implementation/AnalyticsImplementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ namespace Plugin {

const uint32_t POPULATE_DEVICE_INFO_RETRY_MS = 3000;

class AnalyticsConfig : public Core::JSON::Container {
private:
AnalyticsConfig(const AnalyticsConfig&) = delete;
AnalyticsConfig& operator=(const AnalyticsConfig&) = delete;

public:
AnalyticsConfig()
: Core::JSON::Container()
, EventsMap()
{
Add(_T("eventsmap"), &EventsMap);
}
~AnalyticsConfig()
{
}

public:
Core::JSON::String EventsMap;
};

SERVICE_REGISTRATION(AnalyticsImplementation, 1, 0);

AnalyticsImplementation::AnalyticsImplementation():
Expand Down Expand Up @@ -146,6 +166,21 @@ namespace Plugin {
backend.second->Configure(shell, mSysTime);
}

std::string configLine = mShell->ConfigLine();
Core::OptionalType<Core::JSON::Error> error;
AnalyticsConfig config;

if (config.FromString(configLine, error) == false)
{
SYSLOG(Logging::ParsingError,
(_T("Failed to parse config line, error: '%s', config line: '%s'."),
(error.IsSet() ? error.Value().Message().c_str() : "Unknown"),
configLine.c_str()));
}

LOGINFO("EventsMap: %s", config.EventsMap.Value().c_str());
ParseEventsMapFile(config.EventsMap.Value());

return result;
}

Expand Down Expand Up @@ -273,27 +308,41 @@ namespace Plugin {

void AnalyticsImplementation::SendEventToBackend(const AnalyticsImplementation::Event& event)
{
//TODO: Add mapping of event source/name to backend
IAnalyticsBackend::Event backendEvent;
backendEvent.eventName = event.eventName;
backendEvent.eventName = mEventMapper.MapEventNameIfNeeded(event.eventName, event.eventSource,
event.eventSourceVersion, event.eventVersion);
backendEvent.eventVersion = event.eventVersion;
backendEvent.eventSource = event.eventSource;
backendEvent.eventSourceVersion = event.eventSourceVersion;
backendEvent.epochTimestamp = event.epochTimestamp;
backendEvent.eventPayload = event.eventPayload;
backendEvent.cetList = event.cetList;

//TODO: Add mapping of event source/name to the desired backend
if (mBackends.empty())
{
LOGINFO("No backends available!");
}
else if (mBackends.find(IAnalyticsBackend::SIFT) != mBackends.end())
{
LOGINFO("Sending event to Sift backend: %s", event.eventName.c_str());
LOGINFO("Sending event to Sift backend: %s", backendEvent.eventName.c_str());
mBackends.at(IAnalyticsBackend::SIFT)->SendEvent(backendEvent);
}
}

void AnalyticsImplementation::ParseEventsMapFile(const std::string& eventsMapFile)
{
if (eventsMapFile.empty())
{
LOGINFO("Events map file path is empty, skipping parsing");
return;
}
std::ifstream t(eventsMapFile);
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
mEventMapper.FromString(str);
}

uint64_t AnalyticsImplementation::GetCurrentTimestampInMs()
{
return std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now().time_since_epoch() ).count();
Expand All @@ -319,5 +368,102 @@ namespace Plugin {
return currentTimestamp - uptimeDiff;
}

void AnalyticsImplementation::EventMapper::FromString(const std::string &jsonArrayStr)
{
// expect json array:
// [
// {
// "event_name": "event_name",
// "event_source": "event_source",
// "event_source_version": "event_source_version", - optional
// "event_version": "event_version", - optional
// "mapped_event_name": "mapped_event_name"
// }
// ]
if (jsonArrayStr.empty())
{
LOGERR("Empty events map json array string");
return;
}

JsonArray array;
array.FromString(jsonArrayStr);

if (array.Length() == 0)
{
LOGERR("Empty or corrupted events map json array");
return;
}

for (int i = 0; i < array.Length(); i++)
{
JsonObject entry = array[i].Object();
if (entry.HasLabel("event_name") && entry.HasLabel("event_source") && entry.HasLabel("mapped_event_name"))
{
AnalyticsImplementation::EventMapper::Key key{
entry["event_name"].String(),
entry["event_source"].String(),
entry.HasLabel("event_source_version") ? entry["event_source_version"].String() : "",
entry.HasLabel("event_version") ? entry["event_version"].String() : ""};

std::string mapped_event_name = entry["mapped_event_name"].String();
map[key] = mapped_event_name;
LOGINFO("Index %d: Mapped event: %s -> %s", i, entry["event_name"].String().c_str(), mapped_event_name.c_str());
}
else
{
LOGERR("Invalid entry in events map file at index %d", i);
}
}
}

std::string AnalyticsImplementation::EventMapper::MapEventNameIfNeeded(const std::string &eventName,
const std::string &eventSource,
const std::string &eventSourceVersion,
const std::string &eventVersion) const
{
// Try exact match
AnalyticsImplementation::EventMapper::Key key{eventName, eventSource, eventSourceVersion, eventVersion};
auto it = map.find(key);
if (it != map.end())
{
return it->second;
}

// Try without eventVersion
if (!eventVersion.empty())
{
AnalyticsImplementation::EventMapper::Key partialKey{eventName, eventSource, eventSourceVersion, ""};
it = map.find(partialKey);
if (it != map.end())
{
return it->second;
}
}

// Try without eventSourceVersion
if (!eventSourceVersion.empty())
{
AnalyticsImplementation::EventMapper::Key partialKey{eventName, eventSource, "", eventVersion};
it = map.find(partialKey);
if (it != map.end())
{
return it->second;
}
}

// Try without both eventSourceVersion and eventVersion
if (!eventSourceVersion.empty() || !eventVersion.empty())
{
AnalyticsImplementation::EventMapper::Key partialKey{eventName, eventSource, "", ""};
it = map.find(partialKey);
if (it != map.end())
{
return it->second;
}
}

return eventName; // Not found, nothing to map
}
}
}
52 changes: 52 additions & 0 deletions Analytics/Implementation/AnalyticsImplementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <condition_variable>
#include <thread>
#include <queue>
#include <unordered_map>

namespace WPEFramework {
namespace Plugin {
Expand Down Expand Up @@ -75,6 +76,55 @@ namespace Plugin {
std::string id;
};

class EventMapper
{
private:

struct Key
{
std::string eventName;
std::string eventSource;
std::string eventSourceVersion;
std::string eventVersion;

bool operator==(const Key &other) const
{
return eventName == other.eventName &&
eventSource == other.eventSource &&
eventSourceVersion == other.eventSourceVersion &&
eventVersion == other.eventVersion;
}
};

struct KeyHasher
{
std::size_t operator()(const Key &key) const
{
std::size_t h1 = std::hash<std::string>()(key.eventName);
std::size_t h2 = std::hash<std::string>()(key.eventSource);
std::size_t h3 = std::hash<std::string>()(key.eventSourceVersion);
std::size_t h4 = std::hash<std::string>()(key.eventVersion);

// Combine the hashes
std::size_t combined = h1;
combined ^= h2 + 0x9e3779b9 + (combined << 6) + (combined >> 2);
combined ^= h3 + 0x9e3779b9 + (combined << 6) + (combined >> 2);
combined ^= h4 + 0x9e3779b9 + (combined << 6) + (combined >> 2);
return combined;
}
};

public:

void FromString(const std::string &jsonArrayStr);
std::string MapEventNameIfNeeded(const std::string &eventName,
const std::string &eventSource,
const std::string &eventSourceVersion,
const std::string &eventVersion) const;

private:
std::unordered_map<Key, std::string, KeyHasher> map;
};

// IAnalyticsImplementation interface
Core::hresult SendEvent(const string& eventName,
Expand All @@ -93,6 +143,7 @@ namespace Plugin {
void ActionLoop();
bool IsSysTimeValid();
void SendEventToBackend(const Event& event);
void ParseEventsMapFile(const std::string& eventsMapFile);

static uint64_t GetCurrentTimestampInMs();
static uint64_t GetCurrentUptimeInMs();
Expand All @@ -107,6 +158,7 @@ namespace Plugin {
bool mSysTimeValid;
PluginHost::IShell* mShell;
SystemTimePtr mSysTime;
EventMapper mEventMapper;
};
}
}
12 changes: 6 additions & 6 deletions Analytics/Implementation/Backend/Sift/SiftConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ namespace WPEFramework
{
static std::string sThunderSecurityToken;

class AnalyticsConfig : public Core::JSON::Container {
class AnalyticsSiftConfig : public Core::JSON::Container {
private:
AnalyticsConfig(const AnalyticsConfig&) = delete;
AnalyticsConfig& operator=(const AnalyticsConfig&) = delete;
AnalyticsSiftConfig(const AnalyticsSiftConfig&) = delete;
AnalyticsSiftConfig& operator=(const AnalyticsSiftConfig&) = delete;

public:
class SiftConfig : public Core::JSON::Container {
Expand Down Expand Up @@ -108,15 +108,15 @@ namespace WPEFramework


public:
AnalyticsConfig()
AnalyticsSiftConfig()
: Core::JSON::Container()
, DeviceOsName()
, Sift()
{
Add(_T("deviceosname"), &DeviceOsName);
Add(_T("sift"), &Sift);
}
~AnalyticsConfig()
~AnalyticsSiftConfig()
{
}

Expand Down Expand Up @@ -485,7 +485,7 @@ namespace WPEFramework
ASSERT(mShell != nullptr);
std::string configLine = mShell->ConfigLine();
Core::OptionalType<Core::JSON::Error> error;
AnalyticsConfig config;
AnalyticsSiftConfig config;

if (config.FromString(configLine, error) == false)
{
Expand Down
Loading

0 comments on commit 43375d8

Please sign in to comment.