2222#include " CSP/Common/String.h"
2323#include " CSP/Systems/SystemBase.h"
2424
25+ #include < chrono>
26+ #include < mutex>
27+
28+ CSP_START_IGNORE
29+ #ifdef CSP_TESTS
30+ class CSPEngine_AnalyticsSystemTests_QueueAnalyticsEventQueueSendRateTest_Test ;
31+ class CSPEngine_AnalyticsSystemTests_QueueAnalyticsEventQueueSizeTest_Test ;
32+ class CSPEngine_AnalyticsSystemTests_FlushAnalyticsEventsQueueTest_Test ;
33+ #endif
34+ CSP_END_IGNORE
35+
2536namespace csp
2637{
2738class ClientUserAgent ;
@@ -37,57 +48,147 @@ namespace csp::web
3748class WebClient ;
3849} // namespace csp::web
3950
51+ namespace csp ::services::generated::userservice
52+ {
53+ class AnalyticsRecord ;
54+ }
55+
4056namespace csp ::systems
4157{
4258
4359// / @ingroup Analytics System
44- // / @brief Public facing system that allows AnalyticsRecords to be sent to MCS.
60+ // / @brief Public facing system that allows AnalyticsRecords to be sent to the backend services.
61+ // / @invariant Users must be logged in to send AnalyticsRecords to the backend services.
4562class CSP_API AnalyticsSystem : public SystemBase
4663{
4764 CSP_START_IGNORE
4865 /* * @cond DO_NOT_DOCUMENT */
4966 friend class SystemsManager ;
67+
68+ #ifdef CSP_TESTS
69+ friend class ::CSPEngine_AnalyticsSystemTests_QueueAnalyticsEventQueueSendRateTest_Test;
70+ friend class ::CSPEngine_AnalyticsSystemTests_QueueAnalyticsEventQueueSizeTest_Test;
71+ friend class ::CSPEngine_AnalyticsSystemTests_FlushAnalyticsEventsQueueTest_Test;
72+ #endif
5073 /* * @endcond */
5174 CSP_END_IGNORE
5275
5376public:
5477 /* *
55- * @brief Sends an analytics event to the Analytics service.
56- * @details Please note: The BatchAnalyticsEvent method should be used by default as it will batch events before sending them.
57- * This method will immediately send the analytics event and should therefore only be used when this behaviour is required.
78+ * @brief Constructs an Analytics Record which is added to a queue to be sent to the backend services in a single batch.
79+ * @details The queue will be sent when one of the following conditions are met:
80+ * 1. The time since the last batch was sent reaches the AnalyticsQueueSendRate (default 60 seconds).
81+ * 2. The number of events in the queue reaches the MaxQueueSize threshhold (default 25 events).
82+ * 3. The client application calls FlushAnalyticsEventsQueue(). Clients should call this as part of their log out or shut down procedure to force
83+ * the queue to be sent. For more information about flushing events see the method documentation @ref
84+ * AnalyticsSystem::FlushAnalyticsEventsQueue().
5885 *
5986 * Example: Consider the following user action that is to be captured as an analytics event:
60- * A [web client] user [clicks] on a [menu] item in the [UI].
87+ * - A [web client] user [clicks] on a [menu] item in the [UI].
6188 *
6289 * In this example:
63- * [web client] is captured internally.
64- * [clicks] is the InteractionType.
65- * [menu] is the Category.
66- * [UI] is the ProductContextSection.
90+ * - [web client] is captured internally.
91+ * - [clicks] is the InteractionType.
92+ * - [menu] is the Category.
93+ * - [UI] is the ProductContextSection.
94+ *
95+ * @note The following data is captured internally and included in the analytics record:
96+ * - tenant name, client sku, client version and client build environment.
97+ *
98+ * @pre The user must be logged in to send Analytics Records to the backend services.
99+ * @param ProductContextSection const csp::common::String& : The specific, high-level functional area or context within the product where the
100+ * event occurred. This field acts as a primary identifier for the part of the application or system the user is interacting with.
101+ * @param Category const csp::common::String& : Categorization field which acts as a namespace for the InteractionType. It provides a means of
102+ * grouping similar events, which makes it easier to analyze and filter analytics data.
103+ * @param InteractionType const csp::common::String& : Describes the precise and specific interaction that is being tracked. This field identifies
104+ * what the user did or what happened within the product at a specific moment in time.
105+ * @param SubCategory const csp::common::Optional<csp::common::String>& : Optional sub-category field to provide additional context if required.
106+ * @param Metadata const csp::common::Optional<csp::common::Map<csp::common::String, csp::common::String>>& : Optional analytics event metadata.
107+ * Metadata is the event payload. It may be used to store such information as the space the user is in, their geographical region, as well as
108+ * relevant device specs.
109+ */
110+ void QueueAnalyticsEvent (const csp::common::String& ProductContextSection, const csp::common::String& Category,
111+ const csp::common::String& InteractionType, const csp::common::Optional<csp::common::String>& SubCategory,
112+ const csp::common::Optional<csp::common::Map<csp::common::String, csp::common::String>>& Metadata);
113+
114+ /* *
115+ * @brief Constructs an Analytics Record which is immediately sent to the backend services.
116+ * @note The QueueAnalyticsEvent method should be used by default as it will queue events before sending them. This method will immediately send
117+ * the analytics event and should therefore only be used when this behaviour is required.
67118 *
68- * @note The following data is captured internally and included in the analytics record: tenant name, client sku, client version and client build
69- * environment.
119+ * For more information about how the Analytics Record is constructed, see the documentation for @ref AnalyticsSystem::QueueAnalyticsEvent().
70120 *
71- * @param ProductContextSection const csp::common::String& : Section of the client or runtime-context.
72- * @param Category const csp::common::String& : Categorization field.
73- * @param InteractionType const csp::common::String& : The interaction that occurred.
74- * @param SubCategory const csp::common::Optional<csp::common::String>& : Optional sub-category field.
121+ * @pre The user must be logged in to send Analytics Records to the backend services.
122+ * @param ProductContextSection const csp::common::String& : The specific, high-level functional area or context within the product where the
123+ * event occurred. This field acts as a primary identifier for the part of the application or system the user is interacting with.
124+ * @param Category const csp::common::String& : Categorization field which acts as a namespace for the InteractionType. It provides a means of
125+ * grouping similar events, which makes it easier to analyze and filter analytics data.
126+ * @param InteractionType const csp::common::String& : Describes the precise and specific interaction that is being tracked. This field identifies
127+ * what the user did or what happened within the product at a specific moment in time.
128+ * @param SubCategory const csp::common::Optional<csp::common::String>& : Optional sub-category field to provide additional context if required.
75129 * @param Metadata const csp::common::Optional<csp::common::Map<csp::common::String, csp::common::String>>& : Optional analytics event metadata.
76- * @note Metadata is the event payload. It may be used to store such information as the space the user is in, their geographical region as well as
130+ * Metadata is the event payload. It may be used to store such information as the space the user is in, their geographical region, as well as
77131 * relevant device specs.
132+ * @param Callback NullResultCallback : the callback to execute on completion of the send operation.
78133 */
79134 void SendAnalyticsEvent (const csp::common::String& ProductContextSection, const csp::common::String& Category,
80135 const csp::common::String& InteractionType, const csp::common::Optional<csp::common::String>& SubCategory,
81136 const csp::common::Optional<csp::common::Map<csp::common::String, csp::common::String>>& Metadata, NullResultCallback Callback);
82137
138+ /* *
139+ * @brief Trigger immediate dispatch of the Analytics Records queue to the backend services.
140+ * @note This method should be called as part of client log out or shut down procedure to ensure that any queued analytics records are flushed and
141+ * sent to the backend services before the user is logged out or the application is shut down.
142+ * @param Callback NullResultCallback : the callback to execute on completion of the flush operation.
143+ * @pre The user must be logged in to send an Analytics Record to the backend services.
144+ */
145+ CSP_EVENT void FlushAnalyticsEventsQueue (NullResultCallback Callback);
146+
147+ /* *
148+ * @brief Retrieves the time since the queue was last sent.
149+ * @return std::chrono::milliseconds : time since epoch in milliseconds.
150+ */
151+ CSP_NO_EXPORT std::chrono::milliseconds GetTimeSinceLastQueueSend () const { return TimeSinceLastQueueSend; }
152+
153+ /* *
154+ * @brief Retrieves the rate at which the queued Analytics Records are sent.
155+ * @return std::chrono::milliseconds : send rate in milliseconds.
156+ */
157+ CSP_NO_EXPORT std::chrono::milliseconds GetQueueSendRate () const { return AnalyticsQueueSendRate; }
158+
159+ /* *
160+ * @brief Retrieves the current size of the Analytics Records queue.
161+ * @return size_t : the queue size.
162+ */
163+ CSP_NO_EXPORT size_t GetCurrentQueueSize () const { return AnalyticsRecordQueue.size (); }
164+
165+ /* *
166+ * @brief Retrieves the max permitted size of the Analytics Records queue.
167+ * If the queue size reaches this value, the queue will be sent as a single batch to the backend services.
168+ * @return size_t : the queue size at which a batch will be sent.
169+ */
170+ CSP_NO_EXPORT size_t GetMaxQueueSize () const { return MaxQueueSize; }
171+
83172private:
84173 AnalyticsSystem (); // This constructor is only provided to appease the wrapper generator and should not be used
85174 CSP_NO_EXPORT AnalyticsSystem (csp::web::WebClient* InWebClient, const csp::ClientUserAgent* AgentInfo, common::LogSystem& LogSystem);
86175 ~AnalyticsSystem ();
87176
177+ void SetTimeSinceLastQueueSend (std::chrono::milliseconds NewTimeSinceLastQueueSend);
178+
179+ // This is a utility function to allow us to test the queueing functionality in a reasonable time frame.
180+ void __SetQueueSendRateAndMaxSize (std::chrono::milliseconds NewSendRate, size_t NewQueueSize);
181+
88182 std::unique_ptr<csp::services::ApiBase> AnalyticsApi;
89183
184+ std::unique_ptr<class AnalyticsQueueEventHandler > EventHandler;
185+ std::mutex AnalyticsQueueLock;
186+ std::vector<std::shared_ptr<csp::services::generated::userservice::AnalyticsRecord>> AnalyticsRecordQueue;
187+
90188 const csp::ClientUserAgent* UserAgentInfo;
189+ std::chrono::milliseconds AnalyticsQueueSendRate;
190+ std::chrono::milliseconds TimeSinceLastQueueSend;
191+ size_t MaxQueueSize;
91192};
92193
93194} // namespace csp::systems
0 commit comments