Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BEAM-2976] Analytics added to MS, cleanup #1624

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Beamable.Api.Analytics;

namespace Beamable.Server.Api.Analytics
{
public interface IMicroserviceAnalyticsService : IBeamAnalyticsService
{
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Beamable.Server.Api;
using Beamable.Server.Api.Analytics;
using Beamable.Server.Api.Announcements;
using Beamable.Server.Api.Calendars;
using Beamable.Server.Api.Chat;
Expand Down Expand Up @@ -66,6 +67,11 @@ namespace Beamable.Server
/// </summary>
public interface IBeamableServices
{
/// <summary>
/// %Microservice entry point for the <a target="_blank" href="https://docs.beamable.com/docs/analytics-feature-overview">Analytics</a> feature
/// </summary>
IMicroserviceAnalyticsService Analytics { get; }

/// <summary>
/// %Microservice entry point for the <a target="_blank" href="https://docs.beamable.com/docs/announcements-feature-overview">Announcements</a> feature
/// </summary>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Beamable.Serialization;

namespace Beamable.Api.Analytics
{
/// <summary>
/// Analytics event request.
/// This is the request object which is used to send data to the Platform
/// </summary>
public class AnalyticsEventRequest : JsonSerializable.ISerializable
{
/// <summary>
/// Gets the payload.
/// The payload is a json string
/// </summary>
/// <value>The payload.</value>
public string Payload
{
get { return _payload; }
}

private string _payload;

/// <summary>
/// Initializes a new instance of the <see cref="AnalyticsEventRequest"/> class.
/// Used mostly for serialization
/// </summary>
public AnalyticsEventRequest() { }

/// <summary>
/// Initializes a new instance of the <see cref="AnalyticsEventRequest"/> class.
/// </summary>
/// <param name="gamerTag">Gamertag.</param>
/// <param name="payload">Payload (json string)</param>
public AnalyticsEventRequest(string payload)
{
_payload = payload;
}

/// <summary>
/// Serialize the specified object back and forth from json.
/// </summary>
/// <param name="s">Serialization stream</param>
public void Serialize(JsonSerializable.IStreamSerializer s)
{
s.Serialize("payload", ref _payload);
}

}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using Beamable.Common.Api;
using Beamable.Common.Spew;
using Beamable.Serialization;
using System;
using System.Collections.Generic;
using System.Text;

namespace Beamable.Api.Analytics
{
public class AnalyticsService : IBeamAnalyticsService
{
private IBeamableRequester _requester;
private IUserContext _context;
StringBuilder _builder = new StringBuilder(string.Empty);

public AnalyticsService(IUserContext context, IBeamableRequester requester)
{
_context = context;
_requester = requester;
}

public void SendAnalyticsEvent(AnalyticsEventRequest eventRequest)
{
AnalyticsEventBatchRequest(new []{eventRequest.Payload});
}

public void SendAnalyticsEventBatch(List<AnalyticsEventRequest> eventBatch)
{
int batchCount = eventBatch.Count;
if (batchCount == 0) return;
var batch = new string[batchCount];

for (int i = 0; i < batchCount; i++)
{
batch[i] = eventBatch[i].Payload;
}
AnalyticsEventBatchRequest(batch);
}

public AnalyticsEventRequest BuildRequest(IAnalyticsEvent analyticsEvent)
{
using (var jsonSaveStream = JsonSerializable.JsonSaveStream.Spawn())
{
// Start as Object and Serialize Directly, so that we can inject shard
jsonSaveStream.Init(JsonSerializable.JsonSaveStream.JsonType.Object);
analyticsEvent.Serialize(jsonSaveStream);
// TODO: Double check that we don't support shards anymore.
// string shard = _platform.Shard;
// if(shard != null)
// jsonSaveStream.Serialize("shard", ref shard);
jsonSaveStream.Conclude();
return new AnalyticsEventRequest(jsonSaveStream.ToString());
}
}

/// <summary>
/// Analytics Event Batch Request coroutine.
/// This constructs the web request and sends it to the Platform
/// </summary>
/// <returns>The event batch request.</returns>
/// <param name="eventBatch">Event batch.</param>
void AnalyticsEventBatchRequest(IList<string> eventBatch)
{
var eventsAmount = eventBatch.Count;
if (eventsAmount == 0)
return;

long gamerTag = _context.UserId;
if (gamerTag == 0)
{
gamerTag = 1;
}
string uri = string.Format("/report/custom_batch/{0}/{1}/{2}", _requester.Cid, _requester.Pid, gamerTag);

string batchJson;
// using (var pooledBuilder = new StringBuilder("["))
{
_builder.Clear();
_builder.Append('[');
int i = 0;
_builder.AppendFormat("{0}", eventBatch[i]);
for (i = 1; i < eventBatch.Count; i++)
{
_builder.AppendFormat(",{0}", eventBatch[i]);
}
_builder.Append(']');

batchJson = _builder.ToString();
}

AnalyticsLogger.LogFormat("AnalyticsService.AnalyticsEventBatchRequest: Sending batch of {0} to uri: {1}", eventBatch.Count, uri);

_requester.Request(Method.POST, uri, body: batchJson, parser: s => s);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Collections.Generic;

namespace Beamable.Api.Analytics
{
/// <summary>
/// Analytics service.
/// This service provides an API to communicate with the Platform
/// </summary>
public interface IBeamAnalyticsService
{
/// <summary>
/// Sends a single analytics event.
/// </summary>
/// <param name="eventRequest">Event request.</param>
void SendAnalyticsEvent(AnalyticsEventRequest eventRequest);

/// <summary>
/// Sends the analytics event batch.
/// This method also groups batches by gamertag, and issues a request for each
/// </summary>
/// <param name="eventBatch">Event batch.</param>
void SendAnalyticsEventBatch(List<AnalyticsEventRequest> eventBatch);

/// <summary>
/// Prepares request based on analytics event.
/// </summary>
/// <param name="analyticsEvent">Event to convert.</param>
/// <returns>Event request ready to send.</returns>
AnalyticsEventRequest BuildRequest(IAnalyticsEvent analyticsEvent);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
using Beamable.Serialization;
using System.Collections.Generic;
namespace Beamable.Api.Analytics
{
/// <summary>
/// Core event class
///
/// This class can be used in two ways:
/// a) Directly create instances in a structured way, such as with factory methods
/// b) Subclassing
/// </summary>
public class CoreEvent : IAnalyticsEvent
{
private const string _opCode = "g.core";
public string OpCode => _opCode;
public string eventName;
public string category;
protected IDictionary<string, object> eventParams;
/// <param name="category">Low cardinality descriptor of the event</param>
/// <param name="eventName">High cardinality descriptor of the event</param>
/// <param name="eventParams">Other parameters: must be flat with no nesting</param>
public CoreEvent(string category, string eventName, IDictionary<string, object> eventParams)
{
this.category = category;
this.eventName = eventName;
this.eventParams = eventParams;
}
/// <summary>
/// Constructs a payload which represents this analytics event
/// </summary>
public void Serialize(JsonSerializable.IStreamSerializer s)
{
var op = _opCode;
s.Serialize("op", ref op);
s.Serialize("e", ref eventName);
s.Serialize("c", ref category);
s.Serialize("p", ref eventParams);
}
}
}
using Beamable.Serialization;
using System.Collections.Generic;

namespace Beamable.Api.Analytics
{
/// <summary>
/// Core event class
///
/// This class can be used in two ways:
/// a) Directly create instances in a structured way, such as with factory methods
/// b) Subclassing
/// </summary>
public class CoreEvent : IAnalyticsEvent
{
private const string _opCode = "g.core";
public string OpCode => _opCode;

public string eventName;
public string category;
protected IDictionary<string, object> eventParams;

/// <param name="category">Low cardinality descriptor of the event</param>
/// <param name="eventName">High cardinality descriptor of the event</param>
/// <param name="eventParams">Other parameters: must be flat with no nesting</param>
public CoreEvent(string category, string eventName, IDictionary<string, object> eventParams)
{
this.category = category;
this.eventName = eventName;
this.eventParams = eventParams;
}

/// <summary>
/// Constructs a payload which represents this analytics event
/// </summary>
public void Serialize(JsonSerializable.IStreamSerializer s)
{
var op = _opCode;
s.Serialize("op", ref op);
s.Serialize("e", ref eventName);
s.Serialize("c", ref category);
s.Serialize("p", ref eventParams);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
using Beamable.Serialization;
namespace Beamable.Api.Analytics
{
/// <summary>
/// Analytics Event interface
/// </summary>
public interface IAnalyticsEvent : JsonSerializable.ISerializable
{
/// <summary>
/// Gets the op code of the analytics event.
/// </summary>
/// <value>The op code.</value>
string OpCode
{
get;
}
}
}
using Beamable.Serialization;

namespace Beamable.Api.Analytics
{

/// <summary>
/// Analytics Event interface
/// </summary>
public interface IAnalyticsEvent : JsonSerializable.ISerializable
{
/// <summary>
/// Gets the op code of the analytics event.
/// </summary>
/// <value>The op code.</value>
string OpCode
{
get;
}
}
}