diff --git a/Assets/Plugins/CountlySDK/Helpers/Constants.cs b/Assets/Plugins/CountlySDK/Helpers/Constants.cs
index 0aaa5603..936d6131 100644
--- a/Assets/Plugins/CountlySDK/Helpers/Constants.cs
+++ b/Assets/Plugins/CountlySDK/Helpers/Constants.cs
@@ -5,7 +5,7 @@ namespace Plugins.CountlySDK.Helpers
{
internal class Constants
{
- public const string SdkVersion = "20.11.4";
+ public const string SdkVersion = "20.11.5";
#if UNITY_EDITOR
public const string SdkName = "csharp-unity-editor";
diff --git a/Assets/Plugins/CountlySDK/Models/CountlyConfiguration.cs b/Assets/Plugins/CountlySDK/Models/CountlyConfiguration.cs
index f5a3be13..f67cc128 100644
--- a/Assets/Plugins/CountlySDK/Models/CountlyConfiguration.cs
+++ b/Assets/Plugins/CountlySDK/Models/CountlyConfiguration.cs
@@ -75,6 +75,31 @@ public class CountlyConfiguration
///
public int SessionDuration = 60;
+ ///
+ /// Maximum size of all string keys
+ ///
+ public int MaxKeyLength = 128;
+
+ ///
+ /// Maximum size of all values in our key-value pairs
+ ///
+ public int MaxValueSize = 256;
+
+ ///
+ /// Max amount of custom (dev provided) segmentation in one event
+ ///
+ public int MaxSegmentationValues = 30;
+
+ ///
+ ///Limits how many stack trace lines would be recorded per thread
+ ///
+ public int MaxStackTraceLinesPerThread = 30;
+
+ ///
+ ///Limits how many characters are allowed per stack trace line
+ ///
+ public int MaxStackTraceLineLength = 200;
+
///
/// Set threshold value for the number of events that can be stored locally.
///
@@ -224,4 +249,4 @@ public void AddNotificationListener(INotificationListener listener)
NotificationEventListeners.Add(listener);
}
}
-}
\ No newline at end of file
+}
diff --git a/Assets/Plugins/CountlySDK/Models/CountlyUserDetailsModel.cs b/Assets/Plugins/CountlySDK/Models/CountlyUserDetailsModel.cs
index e35ebef8..4a64f9c7 100644
--- a/Assets/Plugins/CountlySDK/Models/CountlyUserDetailsModel.cs
+++ b/Assets/Plugins/CountlySDK/Models/CountlyUserDetailsModel.cs
@@ -28,7 +28,7 @@ public class CountlyUserDetailsModel
[JsonProperty("custom")]
//dots (.) and dollar signs ($) in key names will be stripped out.
- internal Dictionary Custom { get; set; }
+ internal IDictionary Custom { get; set; }
///
/// Initializes a new instance of User Model with the specified params
diff --git a/Assets/Plugins/CountlySDK/Services/AbstractBaseService.cs b/Assets/Plugins/CountlySDK/Services/AbstractBaseService.cs
index f377114f..4dfe0dae 100644
--- a/Assets/Plugins/CountlySDK/Services/AbstractBaseService.cs
+++ b/Assets/Plugins/CountlySDK/Services/AbstractBaseService.cs
@@ -1,5 +1,6 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using Plugins.CountlySDK.Enums;
using Plugins.CountlySDK.Models;
@@ -23,6 +24,102 @@ protected AbstractBaseService(CountlyConfiguration configuration, CountlyLogHelp
_consentService = consentService;
}
+ protected IDictionary RemoveSegmentInvalidDataTypes(IDictionary segments)
+ {
+
+ if (segments == null || segments.Count == 0) {
+ return segments;
+ }
+
+ string moduleName = GetType().Name;
+ int i = 0;
+ List toRemove = new List();
+ foreach (KeyValuePair item in segments) {
+ if (++i > _configuration.MaxSegmentationValues) {
+ toRemove.Add(item.Key);
+ continue;
+ }
+ Type type = item.Value?.GetType();
+ bool isValidDataType = item.Value != null
+ && (type == typeof(int)
+ || type == typeof(bool)
+ || type == typeof(float)
+ || type == typeof(double)
+ || type == typeof(string));
+
+
+ if (!isValidDataType) {
+ toRemove.Add(item.Key);
+ Log.Warning("[" + moduleName + "] RemoveSegmentInvalidDataTypes: In segmentation Data type '" + type + "' of item '" + item.Key + "' isn't valid.");
+ }
+ }
+
+ foreach (string k in toRemove) {
+ segments.Remove(k);
+ }
+
+ return segments;
+ }
+
+ protected string TrimKey(string k)
+ {
+ if (k.Length > _configuration.MaxKeyLength) {
+ Log.Warning("[" + GetType().Name + "] TrimKey : Max allowed key length is " + _configuration.MaxKeyLength + ". "+ k + " will be truncated.");
+ k = k.Substring(0, _configuration.MaxKeyLength);
+ }
+
+ return k;
+ }
+
+ protected string[] TrimValues(string[] values)
+ {
+ for (int i = 0; i < values.Length; ++i) {
+ if (values[i].Length > _configuration.MaxValueSize) {
+ Log.Warning("[" + GetType().Name + "] TrimKey : Max allowed value length is " + _configuration.MaxKeyLength + ". " + values[i] + " will be truncated.");
+ values[i] = values[i].Substring(0, _configuration.MaxValueSize);
+ }
+ }
+
+
+ return values;
+ }
+
+ protected string TrimValue(string fieldName, string v)
+ {
+ if (v.Length > _configuration.MaxValueSize) {
+ Log.Warning("[" + GetType().Name + "] TrimValue : Max allowed '" + fieldName + "' length is " + _configuration.MaxValueSize + ". " + v + " will be truncated.");
+ v = v.Substring(0, _configuration.MaxValueSize);
+ }
+
+ return v;
+ }
+
+ protected IDictionary FixSegmentKeysAndValues(IDictionary segments)
+ {
+ if (segments == null || segments.Count == 0) {
+ return segments;
+ }
+
+ IDictionary segmentation = new Dictionary();
+ foreach (KeyValuePair item in segments) {
+ string k = item.Key;
+ object v = item.Value;
+
+ if (k == null || v == null) {
+ continue;
+ }
+
+ k = TrimKey(k);
+
+ if (v.GetType() == typeof(string)) {
+ v = TrimValue(k, (string)v);
+ }
+
+ segmentation.Add(k, v);
+ }
+
+ return segmentation;
+ }
internal virtual void OnInitializationCompleted() { }
internal virtual void DeviceIdChanged(string deviceId, bool merged) { }
internal virtual void ConsentChanged(List updatedConsents, bool newConsentValue) { }
diff --git a/Assets/Plugins/CountlySDK/Services/CrashReportsCountlyService.cs b/Assets/Plugins/CountlySDK/Services/CrashReportsCountlyService.cs
index 3a66efc2..bd01c88d 100644
--- a/Assets/Plugins/CountlySDK/Services/CrashReportsCountlyService.cs
+++ b/Assets/Plugins/CountlySDK/Services/CrashReportsCountlyService.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Plugins.CountlySDK.Enums;
@@ -22,6 +23,38 @@ internal CrashReportsCountlyService(CountlyConfiguration configuration, CountlyL
_requestCountlyHelper = requestCountlyHelper;
}
+ #region Helper Methods
+ private string ManipulateStackTrace(string stackTrace)
+ {
+ string result = null;
+ if (!string.IsNullOrEmpty(stackTrace)) {
+ string[] lines = stackTrace.Split('\n');
+
+ int limit = lines.Length;
+
+ if (limit > _configuration.MaxStackTraceLinesPerThread) {
+ limit = _configuration.MaxStackTraceLinesPerThread;
+ }
+
+ for (int i = 0; i < limit; ++i) {
+ string line = lines[i];
+
+ if (line.Length > _configuration.MaxStackTraceLineLength) {
+ line = line.Substring(0, _configuration.MaxStackTraceLineLength);
+ }
+
+ if (i + 1 != limit) {
+ line += '\n';
+ }
+
+ result += line;
+ }
+ }
+
+ return result;
+ }
+ #endregion
+
///
/// Called when there is an exception
@@ -42,11 +75,12 @@ public async void LogCallback(string message, string stackTrace, LogType type)
Log.Warning("[CrashReportsCountlyService] LogCallback : The parameter 'message' can't be null or empty");
return;
}
- CountlyExceptionDetailModel model = ExceptionDetailModel(message, stackTrace, false, null);
+
+ CountlyExceptionDetailModel model = ExceptionDetailModel(message, ManipulateStackTrace(stackTrace), false, null);
if (_configuration.EnableAutomaticCrashReporting
&& (type == LogType.Error || type == LogType.Exception)) {
- _=SendCrashReportInternal(model);
+ _ = SendCrashReportInternal(model);
}
}
}
@@ -75,8 +109,11 @@ public async Task SendCrashReportAsync(string message, string stackTrace, LogTyp
return;
}
- CountlyExceptionDetailModel model = ExceptionDetailModel(message, stackTrace, nonfatal, segments);
- _=SendCrashReportInternal(model);
+ IDictionary segmentation = RemoveSegmentInvalidDataTypes(segments);
+ segmentation = FixSegmentKeysAndValues(segments);
+
+ CountlyExceptionDetailModel model = ExceptionDetailModel(message, ManipulateStackTrace(stackTrace), nonfatal, segmentation);
+ _ = SendCrashReportInternal(model);
}
}
@@ -100,8 +137,6 @@ internal async Task SendCrashReportInternal(CountlyExceptionDetailModel model)
///
/// Adds string value to a list which is later sent over as logs whenever a cash is reported by system.
- /// The length of a breadcrumb is limited to 1000 characters. Only first 1000 characters will be accepted in case the length is more
- /// than 1000 characters.
///
/// a bread crumb for the crash report
public void AddBreadcrumbs(string value)
@@ -116,7 +151,7 @@ public void AddBreadcrumbs(string value)
return;
}
- string validBreadcrumb = value.Length > 1000 ? value.Substring(0, 1000) : value;
+ string validBreadcrumb = value.Length > _configuration.MaxValueSize ? value.Substring(0, _configuration.MaxValueSize) : value;
if (_crashBreadcrumbs.Count == _configuration.TotalBreadcrumbsAllowed) {
_crashBreadcrumbs.Dequeue();
diff --git a/Assets/Plugins/CountlySDK/Services/EventCountlyService.cs b/Assets/Plugins/CountlySDK/Services/EventCountlyService.cs
index af65c3ce..131d5007 100644
--- a/Assets/Plugins/CountlySDK/Services/EventCountlyService.cs
+++ b/Assets/Plugins/CountlySDK/Services/EventCountlyService.cs
@@ -71,7 +71,6 @@ internal void AddEventsToRequestQueue()
///
internal async Task RecordEventAsync(CountlyEventModel @event)
{
-
Log.Debug("[EventCountlyService] RecordEventAsync : " + @event.ToString());
if (_configuration.EnableTestMode) {
@@ -163,29 +162,15 @@ public async Task RecordEventAsync(string key, IDictionary segme
return;
}
- if (segmentation != null) {
- List toRemove = new List();
-
- foreach (KeyValuePair item in segmentation) {
- bool isValidDataType = item.Value != null
- && (item.Value.GetType() == typeof(int)
- || item.Value.GetType() == typeof(bool)
- || item.Value.GetType() == typeof(float)
- || item.Value.GetType() == typeof(double)
- || item.Value.GetType() == typeof(string));
-
- if (!isValidDataType) {
- toRemove.Add(item.Key);
- Log.Warning("[EventCountlyService] ReportCustomEventAsync : In segmentation Data type '" + (item.Value?.GetType()) + "' of item '" + item.Key + "' isn't valid.");
- }
- }
-
- foreach (string k in toRemove) {
- segmentation.Remove(k);
- }
+ if (key.Length > _configuration.MaxKeyLength) {
+ Log.Warning("[EventCountlyService] RecordEventAsync : Max allowed key length is " + _configuration.MaxKeyLength);
+ key = key.Substring(0, _configuration.MaxKeyLength);
}
- CountlyEventModel @event = new CountlyEventModel(key, segmentation, count, sum, duration);
+ IDictionary segments = RemoveSegmentInvalidDataTypes(segmentation);
+ segments = FixSegmentKeysAndValues(segments);
+
+ CountlyEventModel @event = new CountlyEventModel(key, segments, count, sum, duration);
_ = RecordEventAsync(@event);
}
@@ -219,29 +204,16 @@ public async Task ReportCustomEventAsync(string key,
return;
}
+ if (key.Length > _configuration.MaxKeyLength) {
+ Log.Warning("[EventCountlyService] ReportCustomEventAsync : Max allowed key length is " + _configuration.MaxKeyLength);
+ key = key.Substring(0, _configuration.MaxKeyLength);
+ }
- if (segmentation != null) {
- List toRemove = new List();
-
- foreach (KeyValuePair item in segmentation) {
- bool isValidDataType = item.Value.GetType() == typeof(int)
- || item.Value.GetType() == typeof(bool)
- || item.Value.GetType() == typeof(float)
- || item.Value.GetType() == typeof(double)
- || item.Value.GetType() == typeof(string);
-
- if (!isValidDataType) {
- toRemove.Add(item.Key);
- Log.Warning("[EventCountlyService] ReportCustomEventAsync : In segmentation Data type '" + (item.Value?.GetType()) + "' of item '" + item.Key + "'isn't valid.");
- }
- }
- foreach (string k in toRemove) {
- segmentation.Remove(k);
- }
- }
+ IDictionary segments = RemoveSegmentInvalidDataTypes(segmentation);
+ segments = FixSegmentKeysAndValues(segments);
- CountlyEventModel @event = new CountlyEventModel(key, segmentation, count, sum, duration);
+ CountlyEventModel @event = new CountlyEventModel(key, segments, count, sum, duration);
_ = RecordEventAsync(@event);
}
diff --git a/Assets/Plugins/CountlySDK/Services/UserDetailsCountlyService.cs b/Assets/Plugins/CountlySDK/Services/UserDetailsCountlyService.cs
index 315198ac..71b9d489 100644
--- a/Assets/Plugins/CountlySDK/Services/UserDetailsCountlyService.cs
+++ b/Assets/Plugins/CountlySDK/Services/UserDetailsCountlyService.cs
@@ -25,40 +25,27 @@ internal UserDetailsCountlyService(CountlyConfiguration configuration, CountlyLo
}
///
- /// Modifies all user data. Custom data should be json string.
- /// Deletes an already defined custom property from the Countly server, if it is supplied with a NULL value
+ /// Add user custom detail to request queue.
///
- /// User's detail object
///
- internal async Task UserDetailsAsync(CountlyUserDetailsModel userDetailsModel)
+ private void AddCustomDetailToRequestQueue(IDictionary segments)
{
- Log.Debug("[UserDetailsCountlyService] UserDetailsAsync : userDetails = " + (userDetailsModel != null));
+ IDictionary customDetail = FixSegmentKeysAndValues(segments);
- if (userDetailsModel == null) {
- Log.Warning("[UserDetailsCountlyService] UserDetailsAsync : The parameter 'userDetailsModel' can't be null.");
- return;
- }
-
- await SetUserDetailsAsync(userDetailsModel);
- }
-
- ///
- /// Modifies custom user data only. Custom data should be json string.
- /// Deletes an already defined custom property from the Countly server, if it is supplied with a NULL value
- ///
- /// User's custom detail object
- ///
- internal async Task UserCustomDetailsAsync(CountlyUserDetailsModel userDetailsModel)
- {
- Log.Debug("[UserDetailsCountlyService] UserCustomDetailsAsync " + (userDetailsModel != null));
-
- if (userDetailsModel == null) {
- Log.Warning("[UserDetailsCountlyService] UserCustomDetailsAsync : The parameter 'userDetailsModel' can't be null.");
- return;
- }
-
- await SetCustomUserDetailsAsync(userDetailsModel);
+ Dictionary requestParams =
+ new Dictionary
+ {
+ { "user_details",
+ JsonConvert.SerializeObject(
+ new Dictionary
+ {
+ { "custom", customDetail }
+ })
+ }
+ };
+ _requestCountlyHelper.AddToRequestQueue(requestParams);
+ _ = _requestCountlyHelper.ProcessQueue();
}
///
@@ -84,6 +71,21 @@ public async Task SetUserDetailsAsync(CountlyUserDetailsModel userDetailsModel)
throw new Exception("Accepted picture formats are .png, .gif and .jpeg");
}
+
+ userDetailsModel.Name = TrimValue("Name", userDetailsModel.Name);
+ userDetailsModel.Phone = TrimValue("Phone", userDetailsModel.Phone);
+ userDetailsModel.Email = TrimValue("Email", userDetailsModel.Email);
+ userDetailsModel.Gender = TrimValue("Gender", userDetailsModel.Gender);
+ userDetailsModel.Username = TrimValue("Username", userDetailsModel.Username);
+ userDetailsModel.BirthYear = TrimValue("BirthYear", userDetailsModel.BirthYear);
+ userDetailsModel.Organization = TrimValue("Organization", userDetailsModel.Organization);
+
+ if (userDetailsModel.PictureUrl.Length > 4096) {
+ Log.Warning("[" + GetType().Name + "] TrimValue : Max allowed length of 'PictureUrl' is " + _configuration.MaxValueSize);
+ userDetailsModel.PictureUrl = userDetailsModel.PictureUrl.Substring(0, 4096);
+ }
+
+ userDetailsModel.Custom = FixSegmentKeysAndValues(userDetailsModel.Custom);
Dictionary requestParams =
new Dictionary
{
@@ -92,7 +94,7 @@ public async Task SetUserDetailsAsync(CountlyUserDetailsModel userDetailsModel)
};
_requestCountlyHelper.AddToRequestQueue(requestParams);
- _= _requestCountlyHelper.ProcessQueue();
+ _ = _requestCountlyHelper.ProcessQueue();
}
}
@@ -122,19 +124,7 @@ public async Task SetCustomUserDetailsAsync(CountlyUserDetailsModel userDetailsM
return;
}
- Dictionary requestParams =
- new Dictionary
- {
- { "user_details",
- JsonConvert.SerializeObject(
- new Dictionary
- {
- { "custom", userDetailsModel.Custom }
- })
- }
- };
- _requestCountlyHelper.AddToRequestQueue(requestParams);
- _= _requestCountlyHelper.ProcessQueue();
+ AddCustomDetailToRequestQueue(userDetailsModel.Custom);
}
}
@@ -155,7 +145,7 @@ public async Task SaveAsync()
CountlyUserDetailsModel model = new CountlyUserDetailsModel(CustomDataProperties);
CustomDataProperties = new Dictionary { };
- _= SetCustomUserDetailsAsync(model);
+ AddCustomDetailToRequestQueue(CustomDataProperties);
}
}
@@ -167,10 +157,23 @@ public async Task SaveAsync()
/// string with value for the property
public void Set(string key, string value)
{
+
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] Set : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
+ if (string.IsNullOrEmpty(value)) {
+ Log.Warning("[UserDetailsCountlyService] Set : value '" + value + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] Set : key = " + key + ", value = " + value);
- AddToCustomData(key, value);
+ AddToCustomData(key, TrimValue(key, value));
}
}
@@ -181,10 +184,22 @@ public void Set(string key, string value)
/// string value to set
public void SetOnce(string key, string value)
{
+ if (string.IsNullOrEmpty(value)) {
+ Log.Warning("[UserDetailsCountlyService] SetOnce : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
+ if (string.IsNullOrEmpty(value)) {
+ Log.Warning("[UserDetailsCountlyService] SetOnce : value '" + value + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] SetOnce : key = " + key + ", value = " + value);
- AddToCustomData(key, new Dictionary { { "$setOnce", value } });
+ AddToCustomData(key, new Dictionary { { "$setOnce", TrimValue(key, value) } });
}
}
@@ -194,6 +209,12 @@ public void SetOnce(string key, string value)
/// string with property name to increment
public void Increment(string key)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] Increment : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] Increment : key = " + key);
@@ -208,6 +229,11 @@ public void Increment(string key)
/// double value by which to increment
public void IncrementBy(string key, double value)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] IncrementBy : key '" + key + "'isn't valid.");
+
+ return;
+ }
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] IncrementBy : key = " + key + ", value = " + value);
@@ -222,6 +248,12 @@ public void IncrementBy(string key, double value)
/// double value by which to multiply
public void Multiply(string key, double value)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] Multiply : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] Multiply : key = " + key + ", value = " + value);
@@ -236,6 +268,12 @@ public void Multiply(string key, double value)
/// double value to check for max
public void Max(string key, double value)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] Max : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] Max : key = " + key + ", value = " + value);
@@ -250,6 +288,12 @@ public void Max(string key, double value)
/// double value to check for min
public void Min(string key, double value)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] Min : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] Min : key = " + key + ", value = " + value);
@@ -265,10 +309,22 @@ public void Min(string key, double value)
/// array with values to add
public void Push(string key, string[] value)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] Push : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
+ if (value == null) {
+ Log.Warning("[UserDetailsCountlyService] Push : value '" + value + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] Push : key = " + key + ", value = " + value);
- AddToCustomData(key, new Dictionary { { "$push", value } });
+ AddToCustomData(key, new Dictionary { { "$push", TrimValues(value) } });
}
}
@@ -280,10 +336,21 @@ public void Push(string key, string[] value)
/// array with values to add
public void PushUnique(string key, string[] value)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] PushUnique : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
+ if (value == null) {
+ Log.Warning("[UserDetailsCountlyService] PushUnique : value '" + value + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] PushUnique : key = " + key + ", value = " + value);
-
- AddToCustomData(key, new Dictionary { { "$addToSet", value } });
+ AddToCustomData(key, new Dictionary { { "$addToSet", TrimValues(value) } });
}
}
@@ -294,9 +361,22 @@ public void PushUnique(string key, string[] value)
/// array with values to remove from array
public void Pull(string key, string[] value)
{
+ if (string.IsNullOrEmpty(key)) {
+ Log.Warning("[UserDetailsCountlyService] Pull : key '" + key + "'isn't valid.");
+
+ return;
+ }
+
+ if (value == null) {
+ Log.Warning("[UserDetailsCountlyService] Pull : value '" + value + "'isn't valid.");
+
+ return;
+ }
+
lock (LockObj) {
Log.Info("[UserDetailsCountlyService] Pull : key = " + key + ", value = " + value);
+ value = TrimValues(value);
AddToCustomData(key, new Dictionary { { "$pull", value } });
}
}
@@ -315,6 +395,8 @@ private void AddToCustomData(string key, object value)
return;
}
+ key = TrimKey(key);
+
if (CustomDataProperties.ContainsKey(key)) {
string item = CustomDataProperties.Select(x => x.Key).FirstOrDefault(x => x.Equals(key, StringComparison.OrdinalIgnoreCase));
if (item != null) {
diff --git a/Assets/Plugins/CountlySDK/Services/ViewCountlyService.cs b/Assets/Plugins/CountlySDK/Services/ViewCountlyService.cs
index 654308ba..5b8c3a35 100644
--- a/Assets/Plugins/CountlySDK/Services/ViewCountlyService.cs
+++ b/Assets/Plugins/CountlySDK/Services/ViewCountlyService.cs
@@ -53,6 +53,11 @@ public async Task RecordOpenViewAsync(string name)
return;
}
+ if (name.Length > _configuration.MaxKeyLength) {
+ Log.Verbose("[ViewCountlyService] RecordOpenViewAsync : Max allowed key length is " + _configuration.MaxKeyLength);
+ name = name.Substring(0, _configuration.MaxKeyLength);
+ }
+
ViewSegment currentViewSegment =
new ViewSegment {
Name = name,
@@ -105,6 +110,11 @@ public async Task RecordCloseViewAsync(string name)
return;
}
+ if (name.Length > _configuration.MaxKeyLength) {
+ Log.Verbose("[ViewCountlyService] RecordCloseViewAsync : Max allowed key length is " + _configuration.MaxKeyLength);
+ name = name.Substring(0, _configuration.MaxKeyLength);
+ }
+
ViewSegment currentViewSegment =
new ViewSegment {
Name = name,
diff --git a/Assets/Tests/PlayModeTests/ConfigurationTests.cs b/Assets/Tests/PlayModeTests/ConfigurationTests.cs
index f98e7e57..d89ef49e 100644
--- a/Assets/Tests/PlayModeTests/ConfigurationTests.cs
+++ b/Assets/Tests/PlayModeTests/ConfigurationTests.cs
@@ -34,6 +34,12 @@ public void TestSDKInitParams()
EventQueueThreshold = 150,
TotalBreadcrumbsAllowed = 200,
+ MaxValueSize = 4,
+ MaxKeyLength = 5,
+ MaxSegmentationValues = 6,
+ MaxStackTraceLineLength = 7,
+ MaxStackTraceLinesPerThread = 8,
+
NotificationMode = TestMode.AndroidTestToken
};
@@ -72,6 +78,12 @@ public void TestSDKInitParams()
Assert.AreEqual(Countly.Instance.Configuration.TotalBreadcrumbsAllowed, 200);
Assert.AreEqual(Countly.Instance.Configuration.NotificationMode, TestMode.AndroidTestToken);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxValueSize, 4);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxKeyLength, 5);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxSegmentationValues, 6);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxStackTraceLineLength, 7);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxStackTraceLinesPerThread, 8);
+
Assert.AreEqual(Countly.Instance.Configuration.Salt, "091355076ead");
Assert.AreEqual(Countly.Instance.Configuration.DeviceId, "device-xyz");
@@ -102,6 +114,13 @@ public void TestDefaultConfigValues()
Assert.AreEqual(Countly.Instance.Configuration.TotalBreadcrumbsAllowed, 100);
Assert.AreEqual(Countly.Instance.Configuration.NotificationMode, TestMode.None);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxValueSize, 256);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxKeyLength, 128);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxSegmentationValues, 30);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxStackTraceLineLength, 200);
+ Assert.AreEqual(Countly.Instance.Configuration.MaxStackTraceLinesPerThread, 30);
+
+
Assert.AreEqual(Countly.Instance.Configuration.Salt, null);
Assert.AreEqual(Countly.Instance.Configuration.DeviceId, null);
Assert.AreEqual(Countly.Instance.Configuration.EnablePost, false);
diff --git a/Assets/Tests/PlayModeTests/CrashTests.cs b/Assets/Tests/PlayModeTests/CrashTests.cs
index 3060ed82..1b8261aa 100644
--- a/Assets/Tests/PlayModeTests/CrashTests.cs
+++ b/Assets/Tests/PlayModeTests/CrashTests.cs
@@ -67,6 +67,63 @@ public void TestCrashBreadCrumbs()
}
+ ///
+ /// It validates the crash limits.
+ ///
+ [Test]
+ public async void TestCrashLimits()
+ {
+ CountlyConfiguration configuration = new CountlyConfiguration {
+ ServerUrl = _serverUrl,
+ AppKey = _appKey,
+ MaxValueSize = 5,
+ MaxKeyLength = 5,
+ MaxSegmentationValues = 2,
+ MaxStackTraceLineLength = 5,
+ MaxStackTraceLinesPerThread = 2
+ };
+
+ Countly.Instance.Init(configuration);
+ Countly.Instance.ClearStorage();
+
+ Assert.IsNotNull(Countly.Instance.CrashReports);
+ Assert.AreEqual(0, Countly.Instance.CrashReports._requestCountlyHelper._requestRepo.Count);
+
+ await Countly.Instance.CrashReports.SendCrashReportAsync("", "StackTrace", LogType.Exception, null);
+ Assert.AreEqual(0, Countly.Instance.CrashReports._requestCountlyHelper._requestRepo.Count);
+
+ await Countly.Instance.CrashReports.SendCrashReportAsync(null, "StackTrace", LogType.Exception, null);
+ Assert.AreEqual(0, Countly.Instance.CrashReports._requestCountlyHelper._requestRepo.Count);
+
+ await Countly.Instance.CrashReports.SendCrashReportAsync(" ", "StackTrace", LogType.Exception, null);
+ Assert.AreEqual(0, Countly.Instance.CrashReports._requestCountlyHelper._requestRepo.Count);
+
+
+ Dictionary seg = new Dictionary{
+ { "Time", "1234455"},
+ { "Retry Attempts", "10"},
+ { "Temp", "100"}
+ };
+
+ await Countly.Instance.CrashReports.SendCrashReportAsync("message", "StackTrace_1\nStackTrace_2\nStackTrace_3", LogType.Exception, seg);
+ Assert.AreEqual(1, Countly.Instance.CrashReports._requestCountlyHelper._requestRepo.Count);
+
+ CountlyRequestModel requestModel = Countly.Instance.CrashReports._requestCountlyHelper._requestRepo.Dequeue();
+ string myUri = requestModel.RequestUrl;
+ string crash = HttpUtility.ParseQueryString(myUri).Get("crash");
+ JObject json = JObject.Parse(crash);
+ Assert.AreEqual("message", json.GetValue("_name").ToString());
+ Assert.AreEqual("True", json.GetValue("_nonfatal").ToString());
+ Assert.AreEqual("Stack\nStack", json.GetValue("_error").ToString());
+
+ JObject custom = json["_custom"].ToObject();
+
+ Assert.AreEqual(2, custom.Count);
+ Assert.AreEqual("12344", custom.GetValue("Time").ToString());
+ Assert.AreEqual("10", custom.GetValue("Retry").ToString());
+
+ }
+
///
/// It validates the functionality of 'SendCrashReportAsync'.
///
@@ -154,7 +211,7 @@ public async void TestMethod_SendCrashReportInternalGetMethod()
}
};
- string url =
+ string url =
Countly.Instance.CrashReports._requestCountlyHelper.BuildGetRequest(requestParams);
int index = url.IndexOf("crash");
Assert.AreEqual(url.Substring(index), requestModel.RequestUrl.Substring(index));
@@ -233,7 +290,7 @@ public async void TestMethod_SendCrashReportInternalPostMethod()
}
///
- /// It validates the limit on bread crumbs length (limit = 1000).
+ /// It validates the maximum size of a bread crumb.
///
[Test]
public void TestCrashBreadCrumbsLength()
@@ -255,10 +312,10 @@ public void TestCrashBreadCrumbsLength()
Assert.AreEqual(1, Countly.Instance.CrashReports._crashBreadcrumbs.Count);
string qBreadCrumbs = Countly.Instance.CrashReports._crashBreadcrumbs.Dequeue();
- Assert.AreEqual(1000, qBreadCrumbs.Length);
+ Assert.AreEqual(256, qBreadCrumbs.Length);
- string validBreadcrumb = breadCrumbs.Length > 1000 ? breadCrumbs.Substring(0, 1000) : breadCrumbs;
- Assert.AreEqual(validBreadcrumb, qBreadCrumbs);
+ string validBreadcrumb = breadCrumbs.Length > 256 ? breadCrumbs.Substring(0, 256) : breadCrumbs;
+ Assert.AreEqual(validBreadcrumb, qBreadCrumbs);
}
///
diff --git a/Assets/Tests/PlayModeTests/EventTests.cs b/Assets/Tests/PlayModeTests/EventTests.cs
index abca70d3..5fc89b2a 100644
--- a/Assets/Tests/PlayModeTests/EventTests.cs
+++ b/Assets/Tests/PlayModeTests/EventTests.cs
@@ -162,6 +162,48 @@ public async void TestEventMethod_ReportCustomEventAsync()
Assert.AreEqual("value2", model.Segmentation["key2"]);
}
+ ///
+ /// It validates the the event limits.
+ ///
+ [Test]
+ public async void TestEventLimits()
+ {
+ CountlyConfiguration configuration = new CountlyConfiguration {
+ ServerUrl = _serverUrl,
+ AppKey = _appKey,
+ MaxKeyLength = 4,
+ MaxValueSize = 6,
+ MaxSegmentationValues = 2
+ };
+
+ Countly.Instance.Init(configuration);
+
+ Assert.IsNotNull(Countly.Instance.Events);
+
+ Assert.AreEqual(0, Countly.Instance.Events._eventRepo.Count);
+
+
+ Dictionary segments = new Dictionary{
+ { "key1", "value1"},
+ { "key2_00", "value2_00"},
+ { "key3_00", "value3"}
+ };
+
+ SegmentModel segmentModel = new SegmentModel(segments);
+
+ await Countly.Instance.Events.RecordEventAsync("test_event", segmentation: segmentModel, sum: 23, duration: 5);
+
+ CountlyEventModel model = Countly.Instance.Events._eventRepo.Dequeue();
+
+ Assert.AreEqual("test", model.Key);
+ Assert.AreEqual(23, model.Sum);
+ Assert.AreEqual(1, model.Count);
+ Assert.AreEqual(5, model.Duration);
+ Assert.AreEqual(2, model.Segmentation.Count);
+ Assert.AreEqual("value1", model.Segmentation["key1"]);
+ Assert.AreEqual("value2", model.Segmentation["key2"]);
+ }
+
///
/// It validates the data type of segment items.
///
diff --git a/Assets/Tests/PlayModeTests/UserCustomDetailsTests.cs b/Assets/Tests/PlayModeTests/UserCustomDetailsTests.cs
index 5b8579ce..80570191 100644
--- a/Assets/Tests/PlayModeTests/UserCustomDetailsTests.cs
+++ b/Assets/Tests/PlayModeTests/UserCustomDetailsTests.cs
@@ -70,6 +70,65 @@ public async void TestUserDetailMethod_SetUserDetailsAsync()
Assert.AreEqual("5.9", userDetailJson["custom"]["Height"].ToString());
}
+ ///
+ /// It validate user profile fields limits.
+ ///
+ [Test]
+ public async void TestUserProfileFieldsLimits()
+ {
+ CountlyConfiguration configuration = new CountlyConfiguration {
+ ServerUrl = _serverUrl,
+ AppKey = _appKey,
+ MaxValueSize = 3,
+ EnablePost = true
+ };
+
+ Countly.Instance.Init(configuration);
+ Countly.Instance.ClearStorage();
+
+ Assert.IsNotNull(Countly.Instance.UserDetails);
+ Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
+ CountlyUserDetailsModel userDetails = null;
+
+ await Countly.Instance.UserDetails.SetUserDetailsAsync(userDetails);
+ Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
+ userDetails = new CountlyUserDetailsModel("Full Name", "username", "useremail@email.com", "Organization",
+ "222-222-222",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.png",
+ "M", "1986",
+ new Dictionary{
+ { "Hair", "Black" },
+ { "Height", "5.9" },
+ });
+ await Countly.Instance.UserDetails.SetUserDetailsAsync(userDetails);
+ Assert.AreEqual(1, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
+ CountlyRequestModel requestModel = Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Dequeue();
+
+ string userDetailData = requestModel.RequestData;
+ JObject json = JObject.Parse(userDetailData);
+ JObject userDetailJson = JObject.Parse(json["user_details"].ToString());
+
+ Assert.AreEqual("Ful", userDetailJson["name"].ToString());
+ Assert.AreEqual("use", userDetailJson["username"].ToString());
+ Assert.AreEqual("use", userDetailJson["email"].ToString());
+ Assert.AreEqual("Org", userDetailJson["organization"].ToString());
+ Assert.AreEqual("222", userDetailJson["phone"].ToString());
+ Assert.AreEqual(4096, userDetailJson["picture"].ToString().Length);
+ Assert.AreEqual("M", userDetailJson["gender"].ToString());
+ Assert.AreEqual("198", userDetailJson["byear"].ToString());
+
+ Assert.AreEqual("Bla", userDetailJson["custom"]["Hair"].ToString());
+ Assert.AreEqual("5.9", userDetailJson["custom"]["Height"].ToString());
+ }
+
///
/// It check the working of method 'SetUserDetailsAsync'.
///
@@ -114,6 +173,131 @@ public async void TestUserDetailMethod_SetCustomUserDetailsAsync()
}
+
+ ///
+ /// It validate user detail segments limits.
+ ///
+ [Test]
+ public async void TestUserDetailSegmentLimits()
+ {
+ CountlyConfiguration configuration = new CountlyConfiguration {
+ ServerUrl = _serverUrl,
+ AppKey = _appKey,
+ MaxKeyLength = 5,
+ MaxValueSize = 6,
+ EnablePost = true
+ };
+
+ Countly.Instance.Init(configuration);
+ Countly.Instance.ClearStorage();
+
+ Assert.IsNotNull(Countly.Instance.UserDetails);
+ Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
+ CountlyUserDetailsModel userDetails = null;
+
+ await Countly.Instance.UserDetails.SetCustomUserDetailsAsync(userDetails);
+ Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
+ userDetails = new CountlyUserDetailsModel(
+ new Dictionary{
+ { "Hair", "Black_000" },
+ { "Height", "5.9" },
+ });
+ await Countly.Instance.UserDetails.SetCustomUserDetailsAsync(userDetails);
+ Assert.AreEqual(1, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
+ CountlyRequestModel requestModel = Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Dequeue();
+
+
+ string userDetailData = requestModel.RequestData;
+ JObject json = JObject.Parse(userDetailData);
+ string userDetail = json["user_details"].ToString();
+ JObject custom = JObject.Parse(userDetail);
+
+ Assert.AreEqual("Black_", custom["custom"]["Hair"].ToString());
+ Assert.AreEqual("5.9", custom["custom"]["Heigh"].ToString());
+
+ }
+
+ ///
+ /// It validate custom user detail segments key and value limits.
+ ///
+ [Test]
+ public void TestCustomUserDetailSegmentLimits()
+ {
+ CountlyConfiguration configuration = new CountlyConfiguration {
+ ServerUrl = _serverUrl,
+ AppKey = _appKey,
+ MaxKeyLength = 5,
+ MaxValueSize = 4,
+ EnablePost = true
+ };
+
+ Countly.Instance.Init(configuration);
+ Countly.Instance.ClearStorage();
+
+ Assert.IsNotNull(Countly.Instance.UserDetails);
+ Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
+
+ Countly.Instance.UserDetails.IncrementBy("IncrementBy", 5);
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Incre"));
+ Dictionary dic = Countly.Instance.UserDetails.CustomDataProperties["Incre"] as Dictionary;
+ Assert.AreEqual(5, dic["$inc"]);
+
+ Countly.Instance.UserDetails.Increment("Increment");
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Incre"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["Incre"] as Dictionary;
+ Assert.AreEqual(1, dic["$inc"]);
+
+ Countly.Instance.UserDetails.SetOnce("SetOnce", "100KM");
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("SetOn"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["SetOn"] as Dictionary;
+ Assert.AreEqual("100K", dic["$setOnce"]);
+
+ Countly.Instance.UserDetails.Set("Set", "6000.0");
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Set"));
+ string height = (string)Countly.Instance.UserDetails.CustomDataProperties["Set"];
+ Assert.AreEqual("6000", height);
+
+ Countly.Instance.UserDetails.Pull("Pull", new string[] { "50KM", "100KM" });
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Pull"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["Pull"] as Dictionary;
+ Assert.AreEqual("50KM", ((string[])dic["$pull"])[0]);
+ Assert.AreEqual("100K", ((string[])dic["$pull"])[1]);
+
+ Countly.Instance.UserDetails.PushUnique("PushUnique", new string[] { "2900.0" });
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("PushU"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["PushU"] as Dictionary;
+ Assert.AreEqual(new string[] { "2900" }, dic["$addToSet"]);
+ Countly.Instance.UserDetails.CustomDataProperties.Clear();
+
+ Countly.Instance.UserDetails.Push("Push", new string[] { "6000.0" });
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Push"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["Push"] as Dictionary;
+ Assert.AreEqual("6000", ((string[])dic["$push"])[0]);
+ Countly.Instance.UserDetails.CustomDataProperties.Clear();
+
+ Countly.Instance.UserDetails.Min("Min", 10.0);
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Min"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["Min"] as Dictionary;
+ Assert.AreEqual(10.0, dic["$min"]);
+ Countly.Instance.UserDetails.CustomDataProperties.Clear();
+
+ Countly.Instance.UserDetails.Max("Max", 10000.0);
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Max"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["Max"] as Dictionary;
+ Assert.AreEqual(10000.0, dic["$max"]);
+
+ Countly.Instance.UserDetails.Multiply("Multiply", 10.0);
+ Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Multi"));
+ dic = Countly.Instance.UserDetails.CustomDataProperties["Multi"] as Dictionary;
+ Assert.AreEqual(10.0, dic["$mul"]);
+ Countly.Instance.UserDetails.CustomDataProperties.Clear();
+
+ }
+
///
/// It check the working of method 'UserCustomDetailsAsync'.
///
@@ -132,11 +316,11 @@ public async void TestUserDetailMethod_UserCustomDetailsAsync()
Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
CountlyUserDetailsModel InvalidUserDetails = null;
- await Countly.Instance.UserDetails.UserCustomDetailsAsync(InvalidUserDetails);
+ await Countly.Instance.UserDetails.SetCustomUserDetailsAsync(InvalidUserDetails);
Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
InvalidUserDetails = new CountlyUserDetailsModel(null);
- await Countly.Instance.UserDetails.UserCustomDetailsAsync(InvalidUserDetails);
+ await Countly.Instance.UserDetails.SetCustomUserDetailsAsync(InvalidUserDetails);
Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
Dictionary customDetail = new Dictionary{
@@ -144,7 +328,7 @@ public async void TestUserDetailMethod_UserCustomDetailsAsync()
{ "Mole", "Lower Left Cheek" }
};
CountlyUserDetailsModel userDetails = new CountlyUserDetailsModel(customDetail);
- await Countly.Instance.UserDetails.UserCustomDetailsAsync(userDetails);
+ await Countly.Instance.UserDetails.SetCustomUserDetailsAsync(userDetails);
Assert.AreEqual(1, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
}
///
@@ -155,22 +339,25 @@ public void TestUserCustomProperty_SetOnceAndSet()
{
CountlyConfiguration configuration = new CountlyConfiguration {
ServerUrl = _serverUrl,
- AppKey = _appKey,
+ AppKey = _appKey
};
Countly.Instance.Init(configuration);
+ Countly.Instance.ClearStorage();
Assert.IsNotNull(Countly.Instance.UserDetails);
+ Assert.AreEqual(0, Countly.Instance.UserDetails._requestCountlyHelper._requestRepo.Count);
+
- Countly.Instance.UserDetails.SetOnce("Distance", "10KM");
+ Countly.Instance.UserDetails.SetOnce("Distance", "100KM");
Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Distance"));
Dictionary dic = Countly.Instance.UserDetails.CustomDataProperties["Distance"] as Dictionary;
- Assert.AreEqual("10KM", dic["$setOnce"]);
+ Assert.AreEqual("100KM", dic["$setOnce"]);
- Countly.Instance.UserDetails.Set("Height", "6");
+ Countly.Instance.UserDetails.Set("Height", "5.9125");
Assert.IsTrue(Countly.Instance.UserDetails.CustomDataProperties.ContainsKey("Height"));
string height = (string)Countly.Instance.UserDetails.CustomDataProperties["Height"];
- Assert.AreEqual("6", height);
+ Assert.AreEqual("5.9125", height);
}
///
diff --git a/Assets/Tests/PlayModeTests/ViewsTests.cs b/Assets/Tests/PlayModeTests/ViewsTests.cs
index de32c854..e5fef603 100644
--- a/Assets/Tests/PlayModeTests/ViewsTests.cs
+++ b/Assets/Tests/PlayModeTests/ViewsTests.cs
@@ -110,6 +110,47 @@ public async void TestViewsConsent()
}
+ ///
+ /// It validates the limit of the view's name size.
+ ///
+ [Test]
+ public async void TestViewNameLimit()
+ {
+ CountlyConfiguration configuration = new CountlyConfiguration {
+ ServerUrl = _serverUrl,
+ AppKey = _appKey,
+ MaxKeyLength = 5
+ };
+
+ Countly.Instance.Init(configuration);
+
+ Countly.Instance.ClearStorage();
+ Assert.IsNotNull(Countly.Instance.Views);
+ Assert.AreEqual(0, Countly.Instance.Views._eventService._eventRepo.Count);
+
+ await Countly.Instance.Views.RecordCloseViewAsync("open_view");
+ await Countly.Instance.Views.RecordCloseViewAsync("close_view");
+ Assert.AreEqual(2, Countly.Instance.Views._eventService._eventRepo.Count);
+
+ CountlyEventModel model = Countly.Instance.Views._eventService._eventRepo.Dequeue();
+
+ Assert.AreEqual(CountlyEventModel.ViewEvent, model.Key);
+ Assert.IsNull(model.Sum);
+ Assert.AreEqual(1, model.Count);
+ Assert.IsNull(model.Duration);
+ Assert.IsNotNull(model.Segmentation);
+ Assert.AreEqual("open_", model.Segmentation["name"]);
+
+ model = Countly.Instance.Views._eventService._eventRepo.Dequeue();
+
+ Assert.AreEqual(CountlyEventModel.ViewEvent, model.Key);
+ Assert.IsNull(model.Sum);
+ Assert.AreEqual(1, model.Count);
+ Assert.IsNull(model.Duration);
+ Assert.IsNotNull(model.Segmentation);
+ Assert.AreEqual("close", model.Segmentation["name"]);
+ }
+
///
/// It validates functionality of method 'RecordCloseViewAsync'.
///
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cd9faaa..71f56828 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 20.11.5
+* Added new configuration fields to manipulate internal SDK value and key limits.
+* Warning! This release will introduce configurable maximum size limits for values and keys throughout the SDK. If they would exceed the limits, they would be truncated.
+
## 20.11.4
* Added "lock" checks for all public SDK methods.
* Fixed race issue bug that occured when events are combined into a request.