Skip to content

Commit 3f6691b

Browse files
adrianM27goruklu
authored andcommitted
RDK-54098: Support event mapping in Analytics
1 parent af91914 commit 3f6691b

File tree

10 files changed

+340
-9
lines changed

10 files changed

+340
-9
lines changed

.github/workflows/L2-tests-R4-4-1.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ jobs:
284284
-DPLUGIN_ANALYTICS_SIFT_MAX_RANDOMISATION_WINDOW_TIME=15
285285
-DPLUGIN_ANALYTICS_SIFT_STORE_PATH="/tmp/AnalyticsSiftStore"
286286
-DPLUGIN_ANALYTICS_SIFT_URL="127.0.0.1:12345"
287+
-DPLUGIN_ANALYTICS_EVENTS_MAP="/tmp/AnalyticsEventsMap.json"
287288
&&
288289
cmake --build build/rdkservices -j8
289290
&&

Analytics/Analytics.conf.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ startuporder = "@PLUGIN_ANALYTICS_STARTUPORDER@"
66
configuration = JSON()
77

88
configuration.add("deviceosname", "@PLUGIN_ANALYTICS_DEVICE_OS_NAME@")
9+
configuration.add("eventsmap", "@PLUGIN_ANALYTICS_EVENTS_MAP@")
910

1011
if boolean("@PLUGIN_ANALYTICS_SIFT_BACKEND_ENABLED@"):
1112
sift = JSON()

Analytics/Analytics.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ endif()
88

99
map()
1010
kv(deviceosname ${PLUGIN_ANALYTICS_DEVICE_OS_NAME})
11+
kv(eventsmap, ${PLUGIN_ANALYTICS_EVENTS_MAP})
1112
end()
1213
ans(configuration)
1314

Analytics/AnalyticsPlugin.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
"description":"Device OS name",
1818
"type":"string"
1919
},
20+
"eventsmap":{
21+
"description":"Optional path to json file with array of mapped events name",
22+
"type":"string"
23+
},
2024
"sift":{
2125
"type":"object",
2226
"properties":{

Analytics/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ All notable changes to this RDK Service will be documented in this file.
1313
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.
1414

1515
For more details, refer to versioning section under Main README.
16+
17+
## [1.0.2] - 2024-12-17
18+
- Support optional event mapping json configuration
1619

1720
## [1.0.2] - 2024-12-04
1821
- Documentation update

Analytics/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ option(PLUGIN_ANALYTICS_SIFT_BACKEND "Enable Sift backend" OFF)
3333
set(PLUGIN_ANALYTICS_STARTUPORDER "" CACHE STRING "To configure startup order of Analytics plugin")
3434
set(PLUGIN_ANALYTICS_AUTOSTART "false" CACHE STRING "Automatically start Analytics plugin")
3535
set(PLUGIN_ANALYTICS_DEVICE_OS_NAME "rdk" CACHE STRING "Device OS name")
36+
set(PLUGIN_ANALYTICS_EVENTS_MAP "" CACHE STRING "Optional path to events mapping file")
3637

3738
set(PLUGIN_ANALYTICS_SIFT_BACKEND_ENABLED ${PLUGIN_ANALYTICS_SIFT_BACKEND} CACHE BOOL "Enable Sift backend configuration")
3839
set(PLUGIN_ANALYTICS_SIFT_2_0_ENABLED "false" CACHE STRING "Enable Sift 2.0 schema")

Analytics/Implementation/AnalyticsImplementation.cpp

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@ namespace Plugin {
3030

3131
const uint32_t POPULATE_DEVICE_INFO_RETRY_MS = 3000;
3232

33+
class AnalyticsConfig : public Core::JSON::Container {
34+
private:
35+
AnalyticsConfig(const AnalyticsConfig&) = delete;
36+
AnalyticsConfig& operator=(const AnalyticsConfig&) = delete;
37+
38+
public:
39+
AnalyticsConfig()
40+
: Core::JSON::Container()
41+
, EventsMap()
42+
{
43+
Add(_T("eventsmap"), &EventsMap);
44+
}
45+
~AnalyticsConfig()
46+
{
47+
}
48+
49+
public:
50+
Core::JSON::String EventsMap;
51+
};
52+
3353
SERVICE_REGISTRATION(AnalyticsImplementation, 1, 0);
3454

3555
AnalyticsImplementation::AnalyticsImplementation():
@@ -146,6 +166,21 @@ namespace Plugin {
146166
backend.second->Configure(shell, mSysTime);
147167
}
148168

169+
std::string configLine = mShell->ConfigLine();
170+
Core::OptionalType<Core::JSON::Error> error;
171+
AnalyticsConfig config;
172+
173+
if (config.FromString(configLine, error) == false)
174+
{
175+
SYSLOG(Logging::ParsingError,
176+
(_T("Failed to parse config line, error: '%s', config line: '%s'."),
177+
(error.IsSet() ? error.Value().Message().c_str() : "Unknown"),
178+
configLine.c_str()));
179+
}
180+
181+
LOGINFO("EventsMap: %s", config.EventsMap.Value().c_str());
182+
ParseEventsMapFile(config.EventsMap.Value());
183+
149184
return result;
150185
}
151186

@@ -273,27 +308,41 @@ namespace Plugin {
273308

274309
void AnalyticsImplementation::SendEventToBackend(const AnalyticsImplementation::Event& event)
275310
{
276-
//TODO: Add mapping of event source/name to backend
277311
IAnalyticsBackend::Event backendEvent;
278-
backendEvent.eventName = event.eventName;
312+
backendEvent.eventName = mEventMapper.MapEventNameIfNeeded(event.eventName, event.eventSource,
313+
event.eventSourceVersion, event.eventVersion);
279314
backendEvent.eventVersion = event.eventVersion;
280315
backendEvent.eventSource = event.eventSource;
281316
backendEvent.eventSourceVersion = event.eventSourceVersion;
282317
backendEvent.epochTimestamp = event.epochTimestamp;
283318
backendEvent.eventPayload = event.eventPayload;
284319
backendEvent.cetList = event.cetList;
285320

321+
//TODO: Add mapping of event source/name to the desired backend
286322
if (mBackends.empty())
287323
{
288324
LOGINFO("No backends available!");
289325
}
290326
else if (mBackends.find(IAnalyticsBackend::SIFT) != mBackends.end())
291327
{
292-
LOGINFO("Sending event to Sift backend: %s", event.eventName.c_str());
328+
LOGINFO("Sending event to Sift backend: %s", backendEvent.eventName.c_str());
293329
mBackends.at(IAnalyticsBackend::SIFT)->SendEvent(backendEvent);
294330
}
295331
}
296332

333+
void AnalyticsImplementation::ParseEventsMapFile(const std::string& eventsMapFile)
334+
{
335+
if (eventsMapFile.empty())
336+
{
337+
LOGINFO("Events map file path is empty, skipping parsing");
338+
return;
339+
}
340+
std::ifstream t(eventsMapFile);
341+
std::string str((std::istreambuf_iterator<char>(t)),
342+
std::istreambuf_iterator<char>());
343+
mEventMapper.FromString(str);
344+
}
345+
297346
uint64_t AnalyticsImplementation::GetCurrentTimestampInMs()
298347
{
299348
return std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now().time_since_epoch() ).count();
@@ -319,5 +368,102 @@ namespace Plugin {
319368
return currentTimestamp - uptimeDiff;
320369
}
321370

371+
void AnalyticsImplementation::EventMapper::FromString(const std::string &jsonArrayStr)
372+
{
373+
// expect json array:
374+
// [
375+
// {
376+
// "event_name": "event_name",
377+
// "event_source": "event_source",
378+
// "event_source_version": "event_source_version", - optional
379+
// "event_version": "event_version", - optional
380+
// "mapped_event_name": "mapped_event_name"
381+
// }
382+
// ]
383+
if (jsonArrayStr.empty())
384+
{
385+
LOGERR("Empty events map json array string");
386+
return;
387+
}
388+
389+
JsonArray array;
390+
array.FromString(jsonArrayStr);
391+
392+
if (array.Length() == 0)
393+
{
394+
LOGERR("Empty or corrupted events map json array");
395+
return;
396+
}
397+
398+
for (int i = 0; i < array.Length(); i++)
399+
{
400+
JsonObject entry = array[i].Object();
401+
if (entry.HasLabel("event_name") && entry.HasLabel("event_source") && entry.HasLabel("mapped_event_name"))
402+
{
403+
AnalyticsImplementation::EventMapper::Key key{
404+
entry["event_name"].String(),
405+
entry["event_source"].String(),
406+
entry.HasLabel("event_source_version") ? entry["event_source_version"].String() : "",
407+
entry.HasLabel("event_version") ? entry["event_version"].String() : ""};
408+
409+
std::string mapped_event_name = entry["mapped_event_name"].String();
410+
map[key] = mapped_event_name;
411+
LOGINFO("Index %d: Mapped event: %s -> %s", i, entry["event_name"].String().c_str(), mapped_event_name.c_str());
412+
}
413+
else
414+
{
415+
LOGERR("Invalid entry in events map file at index %d", i);
416+
}
417+
}
418+
}
419+
420+
std::string AnalyticsImplementation::EventMapper::MapEventNameIfNeeded(const std::string &eventName,
421+
const std::string &eventSource,
422+
const std::string &eventSourceVersion,
423+
const std::string &eventVersion) const
424+
{
425+
// Try exact match
426+
AnalyticsImplementation::EventMapper::Key key{eventName, eventSource, eventSourceVersion, eventVersion};
427+
auto it = map.find(key);
428+
if (it != map.end())
429+
{
430+
return it->second;
431+
}
432+
433+
// Try without eventVersion
434+
if (!eventVersion.empty())
435+
{
436+
AnalyticsImplementation::EventMapper::Key partialKey{eventName, eventSource, eventSourceVersion, ""};
437+
it = map.find(partialKey);
438+
if (it != map.end())
439+
{
440+
return it->second;
441+
}
442+
}
443+
444+
// Try without eventSourceVersion
445+
if (!eventSourceVersion.empty())
446+
{
447+
AnalyticsImplementation::EventMapper::Key partialKey{eventName, eventSource, "", eventVersion};
448+
it = map.find(partialKey);
449+
if (it != map.end())
450+
{
451+
return it->second;
452+
}
453+
}
454+
455+
// Try without both eventSourceVersion and eventVersion
456+
if (!eventSourceVersion.empty() || !eventVersion.empty())
457+
{
458+
AnalyticsImplementation::EventMapper::Key partialKey{eventName, eventSource, "", ""};
459+
it = map.find(partialKey);
460+
if (it != map.end())
461+
{
462+
return it->second;
463+
}
464+
}
465+
466+
return eventName; // Not found, nothing to map
467+
}
322468
}
323469
}

Analytics/Implementation/AnalyticsImplementation.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <condition_variable>
2929
#include <thread>
3030
#include <queue>
31+
#include <unordered_map>
3132

3233
namespace WPEFramework {
3334
namespace Plugin {
@@ -75,6 +76,55 @@ namespace Plugin {
7576
std::string id;
7677
};
7778

79+
class EventMapper
80+
{
81+
private:
82+
83+
struct Key
84+
{
85+
std::string eventName;
86+
std::string eventSource;
87+
std::string eventSourceVersion;
88+
std::string eventVersion;
89+
90+
bool operator==(const Key &other) const
91+
{
92+
return eventName == other.eventName &&
93+
eventSource == other.eventSource &&
94+
eventSourceVersion == other.eventSourceVersion &&
95+
eventVersion == other.eventVersion;
96+
}
97+
};
98+
99+
struct KeyHasher
100+
{
101+
std::size_t operator()(const Key &key) const
102+
{
103+
std::size_t h1 = std::hash<std::string>()(key.eventName);
104+
std::size_t h2 = std::hash<std::string>()(key.eventSource);
105+
std::size_t h3 = std::hash<std::string>()(key.eventSourceVersion);
106+
std::size_t h4 = std::hash<std::string>()(key.eventVersion);
107+
108+
// Combine the hashes
109+
std::size_t combined = h1;
110+
combined ^= h2 + 0x9e3779b9 + (combined << 6) + (combined >> 2);
111+
combined ^= h3 + 0x9e3779b9 + (combined << 6) + (combined >> 2);
112+
combined ^= h4 + 0x9e3779b9 + (combined << 6) + (combined >> 2);
113+
return combined;
114+
}
115+
};
116+
117+
public:
118+
119+
void FromString(const std::string &jsonArrayStr);
120+
std::string MapEventNameIfNeeded(const std::string &eventName,
121+
const std::string &eventSource,
122+
const std::string &eventSourceVersion,
123+
const std::string &eventVersion) const;
124+
125+
private:
126+
std::unordered_map<Key, std::string, KeyHasher> map;
127+
};
78128

79129
// IAnalyticsImplementation interface
80130
Core::hresult SendEvent(const string& eventName,
@@ -93,6 +143,7 @@ namespace Plugin {
93143
void ActionLoop();
94144
bool IsSysTimeValid();
95145
void SendEventToBackend(const Event& event);
146+
void ParseEventsMapFile(const std::string& eventsMapFile);
96147

97148
static uint64_t GetCurrentTimestampInMs();
98149
static uint64_t GetCurrentUptimeInMs();
@@ -107,6 +158,7 @@ namespace Plugin {
107158
bool mSysTimeValid;
108159
PluginHost::IShell* mShell;
109160
SystemTimePtr mSysTime;
161+
EventMapper mEventMapper;
110162
};
111163
}
112164
}

Analytics/Implementation/Backend/Sift/SiftConfig.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ namespace WPEFramework
3737
{
3838
static std::string sThunderSecurityToken;
3939

40-
class AnalyticsConfig : public Core::JSON::Container {
40+
class AnalyticsSiftConfig : public Core::JSON::Container {
4141
private:
42-
AnalyticsConfig(const AnalyticsConfig&) = delete;
43-
AnalyticsConfig& operator=(const AnalyticsConfig&) = delete;
42+
AnalyticsSiftConfig(const AnalyticsSiftConfig&) = delete;
43+
AnalyticsSiftConfig& operator=(const AnalyticsSiftConfig&) = delete;
4444

4545
public:
4646
class SiftConfig : public Core::JSON::Container {
@@ -108,15 +108,15 @@ namespace WPEFramework
108108

109109

110110
public:
111-
AnalyticsConfig()
111+
AnalyticsSiftConfig()
112112
: Core::JSON::Container()
113113
, DeviceOsName()
114114
, Sift()
115115
{
116116
Add(_T("deviceosname"), &DeviceOsName);
117117
Add(_T("sift"), &Sift);
118118
}
119-
~AnalyticsConfig()
119+
~AnalyticsSiftConfig()
120120
{
121121
}
122122

@@ -485,7 +485,7 @@ namespace WPEFramework
485485
ASSERT(mShell != nullptr);
486486
std::string configLine = mShell->ConfigLine();
487487
Core::OptionalType<Core::JSON::Error> error;
488-
AnalyticsConfig config;
488+
AnalyticsSiftConfig config;
489489

490490
if (config.FromString(configLine, error) == false)
491491
{

0 commit comments

Comments
 (0)