A JSON object representing a batch of unique event occurrences in your app.
        /// <summary>
        /// Enqueues the events for delivery. The event is stored in an <see cref="Amazon.MobileAnalytics.MobileAnalyticsManager.Internal.IEventStore"/>.
        /// </summary>
        /// <param name="eventObject">Event object. <see cref="Amazon.MobileAnalytics.Model.Event"/></param>
        public void EnqueueEventsForDelivery(Amazon.MobileAnalytics.Model.Event eventObject)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
            {
                string eventString = eventObject.MarshallToJson();
                bool eventStored   = false;

                try
                {
                    eventStored = _eventStore.PutEvent(eventString, _appId);
                }
                catch (Exception e)
                {
                    _logger.Error(e, "Event {0} is unable to be stored.", eventObject.EventType);
                }

                if (eventStored)
                {
                    _logger.DebugFormat("Event {0} is queued for delivery", eventObject.EventType);
                }
                else
                {
                    EventStoreException e = new EventStoreException("Event cannot be stored.");
                    _logger.Error(e, "Event {0} is unable to be queued for delivery.", eventObject.EventType);
                }
            }));
        }
Example #2
0
        private void EnqueueEventsHelper(Amazon.MobileAnalytics.Model.Event eventObject)
        {
            string eventString = null;

            try
            {
                eventString = JsonMapper.ToJson(eventObject);
            }
            catch (Exception e)
            {
                _logger.Error(e, "An exception occurred when converting low level client event to json string.");
                List <Amazon.MobileAnalytics.Model.Event> eventList = new List <Amazon.MobileAnalytics.Model.Event>();
                eventList.Add(eventObject);
                MobileAnalyticsErrorEventArgs eventArgs = new MobileAnalyticsErrorEventArgs(this.GetType().Name, "An exception occurred when converting low level client event to json string.", e, eventList);
                _maManager.OnRaiseErrorEvent(eventArgs);
            }

            if (null != eventString)
            {
                try
                {
                    _eventStore.PutEvent(eventString, _appID);
                }
                catch (Exception e)
                {
                    _logger.Error(e, "Event {0} was not stored.", eventObject.EventType);
                    MobileAnalyticsErrorEventArgs eventArgs = new MobileAnalyticsErrorEventArgs(this.GetType().Name, "An exception occurred when storing event into event store.", e, new List <Amazon.MobileAnalytics.Model.Event>());
                    _maManager.OnRaiseErrorEvent(eventArgs);
                }
                _logger.DebugFormat("Event {0} is queued for delivery", eventObject.EventType);
            }
        }
Example #3
0
 /// <summary>
 /// Enqueues the events for delivery. The event is stored in an instance of <see cref="Amazon.MobileAnalytics.MobileAnalyticsManager.Internal.IEventStore"/>.
 /// </summary>
 /// <param name="eventObject">Event object.<see cref="Amazon.MobileAnalytics.Model.Event"/></param>
 public void EnqueueEventsForDelivery(Amazon.MobileAnalytics.Model.Event eventObject)
 {
     ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
     {
         EnqueueEventsHelper(eventObject);
     }));
 }
        private Amazon.MobileAnalytics.Model.Event BuildSessionEndEvent(string sessionID)
        {
            Amazon.MobileAnalytics.Model.Event sessionEndEvent = new Amazon.MobileAnalytics.Model.Event();

            sessionEndEvent.EventType = "_session.stop";
            //CultureInfo provider = CultureInfo.InvariantCulture;

            Amazon.MobileAnalytics.Model.Session session = new Amazon.MobileAnalytics.Model.Session();
            session.Id              = sessionID;
            session.StartTimestamp  = DateTime.UtcNow;
            session.StopTimestamp   = DateTime.UtcNow;
            session.Duration        = (long)TimeSpan.FromSeconds(20).TotalMilliseconds;
            sessionEndEvent.Session = session;

            Dictionary <string, string> attributes = new Dictionary <string, string>();

            sessionEndEvent.Attributes = attributes;

            Dictionary <string, double> metrics = new Dictionary <string, double>();

            sessionEndEvent.Metrics = metrics;

            sessionEndEvent.Version   = "v2.0";
            sessionEndEvent.Timestamp = DateTime.UtcNow;

            return(sessionEndEvent);
        }
Example #5
0
 /// <summary>
 /// Enqueues the events for delivery. The event is stored in an instance of <see cref="Amazon.MobileAnalytics.MobileAnalyticsManager.Internal.IEventStore"/>.
 /// </summary>
 /// <param name="eventObject">Event object.<see cref="Amazon.MobileAnalytics.Model.Event"/></param>
 public void EnqueueEventsForDelivery(Amazon.MobileAnalytics.Model.Event eventObject)
 {
     Task.Run(() =>
     {
         EnqueueEventsHelper(eventObject);
     });
 }
        private Amazon.MobileAnalytics.Model.Event BuildMonetizationEvent()
        {
            Amazon.MobileAnalytics.Model.Event monetizationEvent = new Amazon.MobileAnalytics.Model.Event();

            monetizationEvent.EventType = "_monetization.purchase";
            Amazon.MobileAnalytics.Model.Session session = new Amazon.MobileAnalytics.Model.Session();
            session.Id                = Guid.NewGuid().ToString();
            session.StartTimestamp    = DateTime.UtcNow;
            monetizationEvent.Session = session;

            Dictionary <string, string> attributes = new Dictionary <string, string>();

            attributes.Add("_currency_", "USD");
            attributes.Add("_product_id", "Kindle");
            monetizationEvent.Attributes = attributes;

            Dictionary <string, double> metrics = new Dictionary <string, double>();

            metrics.Add("_quantity", 2);
            metrics.Add("_item_price", 110.0);
            monetizationEvent.Metrics = metrics;

            monetizationEvent.Version   = "v2.0";
            monetizationEvent.Timestamp = DateTime.Now.ToUniversalTime();

            return(monetizationEvent);
        }
        private Amazon.MobileAnalytics.Model.Event BuildSessionStartEvent(string sessionID)
        {
            Amazon.MobileAnalytics.Model.Event sessionStartEvent = new Amazon.MobileAnalytics.Model.Event();

            sessionStartEvent.EventType = "_session.start";
            //CultureInfo provider = CultureInfo.InvariantCulture;

            Amazon.MobileAnalytics.Model.Session session = new Amazon.MobileAnalytics.Model.Session();
            session.Id                = sessionID;
            session.StartTimestamp    = DateTime.UtcNow;
            sessionStartEvent.Session = session;

            Dictionary <string, string> attributes = new Dictionary <string, string>();

            sessionStartEvent.Attributes = attributes;

            Dictionary <string, double> metrics = new Dictionary <string, double>();

            sessionStartEvent.Metrics = metrics;

            sessionStartEvent.Version   = "v2.0";
            sessionStartEvent.Timestamp = DateTime.UtcNow;

            return(sessionStartEvent);
        }
        private Amazon.MobileAnalytics.Model.Event BuildCustomEvent()
        {
            Amazon.MobileAnalytics.Model.Event customEvent = new Amazon.MobileAnalytics.Model.Event();

            customEvent.EventType = "LevelComplete";

            Amazon.MobileAnalytics.Model.Session session = new Amazon.MobileAnalytics.Model.Session();
            session.Id             = Guid.NewGuid().ToString();
            session.StartTimestamp = DateTime.UtcNow;
            session.StopTimestamp  = DateTime.UtcNow;
            session.Duration       = (long)TimeSpan.FromMinutes(30).TotalMilliseconds;
            customEvent.Session    = session;

            Dictionary <string, string> attributes = new Dictionary <string, string>();

            attributes.Add("LevelName", "Level1");
            attributes.Add("CharacterClass", "Warrior");
            attributes.Add("Successful", "True");
            customEvent.Attributes = attributes;

            Dictionary <string, double> metrics = new Dictionary <string, double>();

            metrics.Add("Score", 12345);
            metrics.Add("TimeInLevel", 64);
            customEvent.Metrics = metrics;

            customEvent.Version   = "v2.0";
            customEvent.Timestamp = DateTime.UtcNow;

            return(customEvent);
        }
Example #9
0
        /// <summary>
        /// Enqueues the events for delivery. The event is stored in an instance of <see cref="Amazon.MobileAnalytics.MobileAnalyticsManager.Internal.IEventStore"/>.
        /// </summary>
        /// <param name="eventObject">Event object.<see cref="Amazon.MobileAnalytics.Model.Event"/></param>
        public void EnqueueEventsForDelivery(Amazon.MobileAnalytics.Model.Event eventObject)
        {
#if BCL35
            ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
            {
#elif PCL || BCL45
            Task.Run(() =>
            {
#endif
                string eventString = JsonMapper.ToJson(eventObject);

                try
                {
                    _eventStore.PutEvent(eventString, _appID);
                }
                catch (Exception e)
                {
                    _logger.Error(e, "Event {0} is unable to be stored.", eventObject.EventType);
                }

                _logger.DebugFormat("Event {0} is queued for delivery", eventObject.EventType);
#if BCL35
            }));
#elif PCL || BCL45
            });
        /// <summary>
        /// Attempts the delivery.
        /// It will fail delivery if any of the policies.isAllowed() returns false.
        /// The policies are attmpted in batches of fixed size. To increase or decrease the size of bytes
        /// transfered per batch you can awsconfig.xml and configure the maxRequestSize property.
        /// </summary>
        /// <param name="policies">list of <see cref="Amazon.MobileAnalytics.MobileAnalyticsManager.IDeliveryPolicy"/></param>
        private void AttemptDelivery(List <IDeliveryPolicy> policies)
        {
            //validate all the policies before attempting the delivery
            foreach (IDeliveryPolicy policy in policies)
            {
                if (!policy.IsAllowed())
                {
                    _logger.InfoFormat("Policy restriction: {0}", policy.GetType().Name);
                    lock (_deliveryLock)
                    {
                        _deliveryInProgress = false;
                    }
                    return;
                }
            }

            List <string> rowIds = new List <string>();

            long            maxRequestSize = AWSConfigsMobileAnalytics.MaxRequestSize;
            List <JsonData> eventList      = _eventStore.GetAllEvents(_appId);

            if (eventList.Count == 0)
            {
                _logger.InfoFormat("No Events to deliver");
                lock (_deliveryLock)
                {
                    _deliveryInProgress = false;
                }
                return;
            }

            long eventsLength = 0L;

            List <Amazon.MobileAnalytics.Model.Event> eventArray = new List <Amazon.MobileAnalytics.Model.Event>();

            foreach (JsonData eventData in eventList)
            {
                eventsLength += ((string)eventData["event"]).Length;
                if (eventsLength < maxRequestSize)
                {
                    string eventString = (string)eventData["event"];

                    _logger.InfoFormat("Event string is {0}", eventString);

                    Amazon.MobileAnalytics.Model.Event _analyticsEvent = Amazon.MobileAnalytics.Model.Event.UnmarshallFromJson(eventString);
                    eventArray.Add(_analyticsEvent);
                    rowIds.Add(eventData["id"].ToString());
                }
                else
                {
                    SubmitEvents(rowIds, eventArray, HandleResponse);
                    rowIds       = new List <string>();
                    eventArray   = new List <Amazon.MobileAnalytics.Model.Event>();
                    eventsLength = 0L;
                }
            }

            SubmitEvents(rowIds, eventArray, HandleResponse);
        }
Example #11
0
        /// <summary>
        /// Enqueues the events for delivery. The event is stored in an instance of <see cref="Amazon.MobileAnalytics.MobileAnalyticsManager.Internal.IEventStore"/>.
        /// </summary>
        /// <param name="eventObject">Event object.<see cref="Amazon.MobileAnalytics.Model.Event"/></param>
        public void EnqueueEventsForDelivery(Amazon.MobileAnalytics.Model.Event eventObject)
        {
#if BCL35
            ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
            {
#elif PCL || BCL45
            Task.Run(() =>
            {
#endif
                string eventString = null;
                try
                {
                    eventString = JsonMapper.ToJson(eventObject);
                }
                catch (Exception e)
                {
                    _logger.Error(e, "An exception occurred when converting low level client event to json string.");
                    List <Amazon.MobileAnalytics.Model.Event> eventList = new List <Amazon.MobileAnalytics.Model.Event>();
                    eventList.Add(eventObject);
                    MobileAnalyticsErrorEventArgs eventArgs = new MobileAnalyticsErrorEventArgs(this.GetType().Name, "An exception occurred when converting low level client event to json string.", e, eventList);
                    _maManager.OnRaiseErrorEvent(eventArgs);
                }

                if (null != eventString)
                {
                    try
                    {
                        _eventStore.PutEvent(eventString, _appID);
                    }
                    catch (Exception e)
                    {
                        _logger.Error(e, "Event {0} was not stored.", eventObject.EventType);
                        MobileAnalyticsErrorEventArgs eventArgs = new MobileAnalyticsErrorEventArgs(this.GetType().Name, "An exception occurred when storing event into event store.", e, new List <Amazon.MobileAnalytics.Model.Event>());
                        _maManager.OnRaiseErrorEvent(eventArgs);
                    }
                    _logger.DebugFormat("Event {0} is queued for delivery", eventObject.EventType);
                }
#if BCL35
            }));
#elif PCL || BCL45
            });
Example #12
0
        /// <summary>
        /// Attempts the delivery.
        /// Delivery will fail if any of the policies IsAllowed() returns false.
        /// The delivery are attmpted in batches of fixed size. To increase or decrease the size,
        /// you can override MaxRequestSize in MobileAnalyticsManagerConfig.<see cref="Amazon.MobileAnalytics.MobileAnalyticsManager.MobileAnalyticsManagerConfig"/>
        /// </summary>
        public void AttemptDelivery()
        {
            lock (_deliveryLock)
            {
                if (_deliveryInProgress)
                {
                    _logger.InfoFormat("Delivery already in progress, failing new delivery");
                    return;
                }
                _deliveryInProgress = true;
            }

            //validate all the policies before attempting the delivery
            foreach (IDeliveryPolicy policy in _deliveryPolicies)
            {
                if (!policy.IsAllowed())
                {
                    _logger.InfoFormat("Policy restriction: {0}", policy.GetType().Name);
                    lock (_deliveryLock)
                    {
                        _deliveryInProgress = false;
                    }
                    return;
                }
            }

            List <JsonData> allEventList = _eventStore.GetEvents(_appID, MAX_ALLOWED_SELECTS);

            if (allEventList.Count == 0)
            {
                _logger.InfoFormat("No Events to deliver.");
                lock (_deliveryLock)
                {
                    _deliveryInProgress = false;
                }
                return;
            }

            List <string> submitEventsIdList = new List <string>();
            List <Amazon.MobileAnalytics.Model.Event> submitEventsList = new List <Amazon.MobileAnalytics.Model.Event>();
            long submitEventsLength = 0L;

            foreach (JsonData eventData in allEventList)
            {
                string eventString = (string)eventData["event"];
                submitEventsLength += eventString.Length;
                if (submitEventsLength < _maConfig.MaxRequestSize)
                {
                    try {
                        Amazon.MobileAnalytics.Model.Event _analyticsEvent = JsonMapper.ToObject <Amazon.MobileAnalytics.Model.Event>(eventString);
                        submitEventsList.Add(_analyticsEvent);
                    } catch (JsonException e) {
                        _logger.Error(e, "Could not load event from event store, discarding.");
                    }

                    submitEventsIdList.Add(eventData["id"].ToString());
                }
                else
                {
                    SubmitEvents(submitEventsIdList, submitEventsList);
                    submitEventsIdList = new List <string>();
                    submitEventsList   = new List <Amazon.MobileAnalytics.Model.Event>();
                    submitEventsLength = 0L;
                }
            }

            if (submitEventsLength > 0)
            {
                SubmitEvents(submitEventsIdList, submitEventsList);
            }
        }
        public void TestMonetizationEventConcurrency()
        {
            // event type
            const string EVENT_TYPE = "_monetization.purchase";

            const double QUANTITY             = 321321;
            const double ITEM_PRICE           = 854584;
            const string PRODUCT_ID           = "PRODUCT_ID123";
            const string ITEM_PRICE_FORMATTED = "$1.99";
            const string STORE          = "STORE";
            const string TRANSACTION_ID = "TRANSACTION_ID123";
            const string CURRENCY       = "2.22";


            // attribute config
            const string ATTR1        = "ATTR1";
            string       ATTR1_VAL_T0 = Guid.NewGuid().ToString();
            string       ATTR1_VAL_T1 = Guid.NewGuid().ToString();
            string       ATTR1_VAL_T2 = Guid.NewGuid().ToString();

            const string ATTR2        = "ATTR2";
            string       ATTR2_VAL_T0 = Guid.NewGuid().ToString();
            string       ATTR2_VAL_T1 = Guid.NewGuid().ToString();
            string       ATTR2_VAL_T2 = Guid.NewGuid().ToString();

            const string ATTR3        = "ATTR3";
            string       ATTR3_VAL_T0 = Guid.NewGuid().ToString();
            string       ATTR3_VAL_T1 = Guid.NewGuid().ToString();
            string       ATTR3_VAL_T2 = Guid.NewGuid().ToString();


            // metric config
            System.Random randObj        = new System.Random();
            const string  METRIC1        = "METRIC1";
            double        METRIC1_VAL_T0 = randObj.Next();
            double        METRIC1_VAL_T1 = randObj.Next();
            double        METRIC1_VAL_T2 = randObj.Next();

            const string METRIC2        = "METRIC2";
            double       METRIC2_VAL_T0 = randObj.Next();
            double       METRIC2_VAL_T1 = randObj.Next();
            double       METRIC2_VAL_T2 = randObj.Next();

            const string METRIC3        = "METRIC3";
            double       METRIC3_VAL_T0 = randObj.Next();
            double       METRIC3_VAL_T1 = randObj.Next();
            double       METRIC3_VAL_T2 = randObj.Next();

            MonetizationEvent monetizationEvent = new MonetizationEvent();

            const int LOOP_COUNT = 1;


            Task task0 = new Task(() =>
            {
                int i = 0;
                for (i = 0; i < LOOP_COUNT; i++)
                {
                    monetizationEvent.AddAttribute(ATTR1, ATTR1_VAL_T0);
                    monetizationEvent.AddGlobalAttribute(ATTR2, ATTR2_VAL_T0);
                    monetizationEvent.AddGlobalAttribute(EVENT_TYPE, ATTR3, ATTR3_VAL_T0);

                    monetizationEvent.AddMetric(METRIC1, METRIC1_VAL_T0);
                    monetizationEvent.AddGlobalMetric(METRIC2, METRIC2_VAL_T0);
                    monetizationEvent.AddGlobalMetric(EVENT_TYPE, METRIC3, METRIC3_VAL_T0);
                }

                monetizationEvent.Quantity  = QUANTITY;
                monetizationEvent.ItemPrice = ITEM_PRICE;
            });



            Task task1 = new Task(() =>
            {
                int i = 0;
                for (i = 0; i < LOOP_COUNT; i++)
                {
                    monetizationEvent.AddAttribute(ATTR1, ATTR1_VAL_T1);
                    monetizationEvent.AddGlobalAttribute(ATTR2, ATTR2_VAL_T1);
                    monetizationEvent.AddGlobalAttribute(EVENT_TYPE, ATTR3, ATTR3_VAL_T1);

                    monetizationEvent.AddMetric(METRIC1, METRIC1_VAL_T1);
                    monetizationEvent.AddGlobalMetric(METRIC2, METRIC2_VAL_T1);
                    monetizationEvent.AddGlobalMetric(EVENT_TYPE, METRIC3, METRIC3_VAL_T1);
                }

                monetizationEvent.ProductId          = PRODUCT_ID;
                monetizationEvent.ItemPriceFormatted = ITEM_PRICE_FORMATTED;
                monetizationEvent.Store         = STORE;
                monetizationEvent.TransactionId = TRANSACTION_ID;
                monetizationEvent.Currency      = CURRENCY;
            });


            Task task2 = new Task(() =>
            {
                int i = 0;
                for (i = 0; i < LOOP_COUNT; i++)
                {
                    monetizationEvent.AddAttribute(ATTR1, ATTR1_VAL_T2);
                    monetizationEvent.AddGlobalAttribute(ATTR2, ATTR2_VAL_T2);
                    monetizationEvent.AddGlobalAttribute(EVENT_TYPE, ATTR3, ATTR3_VAL_T2);

                    monetizationEvent.AddMetric(METRIC1, METRIC1_VAL_T2);
                    monetizationEvent.AddGlobalMetric(METRIC2, METRIC2_VAL_T2);
                    monetizationEvent.AddGlobalMetric(EVENT_TYPE, METRIC3, METRIC3_VAL_T2);
                }
            });


            task0.Start();
            task1.Start();
            task2.Start();

            // wait all task complete
            Task.WaitAll(new[] { task0, task1, task2 });

            // Get model event.
            Amazon.MobileAnalytics.Model.Event modelEvent = monetizationEvent.ConvertToMobileAnalyticsModelEvent(GetMobileAnalyticsManager("TestMonetizationEventConcurrency").Session);

            // Check attribute value.
            if (!modelEvent.Attributes.ContainsKey(ATTR1) || !modelEvent.Attributes.ContainsKey(ATTR2) || !modelEvent.Attributes.ContainsKey(ATTR3))
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[ATTR1] != ATTR1_VAL_T0 && modelEvent.Attributes[ATTR1] != ATTR1_VAL_T1 && modelEvent.Attributes[ATTR1] != ATTR1_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[ATTR2] != ATTR2_VAL_T0 && modelEvent.Attributes[ATTR2] != ATTR2_VAL_T1 && modelEvent.Attributes[ATTR2] != ATTR2_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[ATTR3] != ATTR3_VAL_T0 && modelEvent.Attributes[ATTR3] != ATTR3_VAL_T1 && modelEvent.Attributes[ATTR3] != ATTR3_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            // check metric value
            if (!modelEvent.Metrics.ContainsKey(METRIC1) || !modelEvent.Metrics.ContainsKey(METRIC2) || !modelEvent.Metrics.ContainsKey(METRIC3))
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Metrics[METRIC1] != METRIC1_VAL_T0 && modelEvent.Metrics[METRIC1] != METRIC1_VAL_T1 && modelEvent.Metrics[METRIC1] != METRIC1_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Metrics[METRIC2] != METRIC2_VAL_T0 && modelEvent.Metrics[METRIC2] != METRIC2_VAL_T1 && modelEvent.Metrics[METRIC2] != METRIC2_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Metrics[METRIC3] != METRIC3_VAL_T0 && modelEvent.Metrics[METRIC3] != METRIC3_VAL_T1 && modelEvent.Metrics[METRIC3] != METRIC3_VAL_T2)
            {
                Assert.Fail();
                return;
            }


            // metric name
            const string PURCHASE_EVENT_QUANTITY_METRIC   = "_quantity";
            const string PURCHASE_EVENT_ITEM_PRICE_METRIC = "_item_price";

            // attribute name
            const string PURCHASE_EVENT_PRODUCT_ID_ATTR           = "_product_id";
            const string PURCHASE_EVENT_ITEM_PRICE_FORMATTED_ATTR = "_item_price_formatted";
            const string PURCHASE_EVENT_STORE_ATTR          = "_store";
            const string PURCHASE_EVENT_TRANSACTION_ID_ATTR = "_transaction_id";
            const string PURCHASE_EVENT_CURRENCY_ATTR       = "_currency";

            if (modelEvent.Metrics[PURCHASE_EVENT_QUANTITY_METRIC] != QUANTITY || modelEvent.Metrics[PURCHASE_EVENT_ITEM_PRICE_METRIC] != ITEM_PRICE)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[PURCHASE_EVENT_PRODUCT_ID_ATTR] != PRODUCT_ID ||
                modelEvent.Attributes[PURCHASE_EVENT_ITEM_PRICE_FORMATTED_ATTR] != ITEM_PRICE_FORMATTED ||
                modelEvent.Attributes[PURCHASE_EVENT_STORE_ATTR] != STORE ||
                modelEvent.Attributes[PURCHASE_EVENT_TRANSACTION_ID_ATTR] != TRANSACTION_ID ||
                modelEvent.Attributes[PURCHASE_EVENT_CURRENCY_ATTR] != CURRENCY)
            {
                Assert.Fail();
                return;
            }
        }
        public void TestCustomEventConcurrency()
        {
            // event type
            const string EVENT_TYPE = "MyEventType";

            // attribute config
            string ATTR1        = "ATTR1";
            string ATTR1_VAL_T0 = "dshjadfhjdfa132`23jj`23jh1k2h3h21hg3h21j2gh";
            string ATTR1_VAL_T1 = "gfhfdhgvkfdkgljfdgjfdsj;l34t43jj4erjerb";
            string ATTR1_VAL_T2 = "7t32674tgdfjehkjdksjs;akfdshfjdsafkadsfdsljfa";

            string ATTR2        = "ATTR2";
            string ATTR2_VAL_T0 = "343hjfdshdfjklsafj0913432jh4";
            string ATTR2_VAL_T1 = "378t43y21ggdhsgdahshfdjsfafd";
            string ATTR2_VAL_T2 = "48ry42378tfhsfkds;kfl;dsdfk;ldslks";

            string ATTR3        = "ATTR3";
            string ATTR3_VAL_T0 = "321432g4ghjfjshdggfjhsdgfdskgfjdsjgfsd";
            string ATTR3_VAL_T1 = "76432tgrsgerhjkfgshdfdssfgjdssaf";
            string ATTR3_VAL_T2 = "87549353hjtkgdk;sfgfdgf;kfl;dshfjdsjkhfjs";

            string ATTR_EMPTY1 = "ATTR_EMPTY1";
            string ATTR_EMPTY2 = "ATTR_EMPTY2";
            string ATTR_EMPTY3 = "ATTR_EMPTY3";

            // metric config
            System.Random randObj        = new System.Random();
            const string  METRIC1        = "METRIC1";
            double        METRIC1_VAL_T0 = randObj.Next();
            double        METRIC1_VAL_T1 = randObj.Next();
            double        METRIC1_VAL_T2 = randObj.Next();

            const string METRIC2        = "METRIC2";
            double       METRIC2_VAL_T0 = randObj.Next();
            double       METRIC2_VAL_T1 = randObj.Next();
            double       METRIC2_VAL_T2 = randObj.Next();

            const string METRIC3        = "METRIC3";
            double       METRIC3_VAL_T0 = randObj.Next();
            double       METRIC3_VAL_T1 = randObj.Next();
            double       METRIC3_VAL_T2 = randObj.Next();


            CustomEvent customEvent = new CustomEvent(EVENT_TYPE);
            const int   LOOP_COUNT  = 999;

            Task task0 = new Task(() =>
            {
                for (int i = 0; i < LOOP_COUNT; i++)
                {
                    customEvent.AddAttribute(ATTR1, ATTR1_VAL_T0);
                    customEvent.AddGlobalAttribute(ATTR2, ATTR2_VAL_T0);
                    customEvent.AddGlobalAttribute(EVENT_TYPE, ATTR3, ATTR3_VAL_T0);

                    customEvent.AddMetric(METRIC1, METRIC1_VAL_T0);
                    customEvent.AddGlobalMetric(METRIC2, METRIC2_VAL_T0);
                    customEvent.AddGlobalMetric(EVENT_TYPE, METRIC3, METRIC3_VAL_T0);


                    customEvent.AddAttribute(ATTR_EMPTY1, "");
                    customEvent.AddGlobalAttribute(ATTR_EMPTY2, "");
                    customEvent.AddGlobalAttribute(EVENT_TYPE, ATTR_EMPTY3, "");
                }
            });


            Task task1 = new Task(() =>
            {
                for (int i = 0; i < LOOP_COUNT; i++)
                {
                    customEvent.AddAttribute(ATTR1, ATTR1_VAL_T1);
                    customEvent.AddGlobalAttribute(ATTR2, ATTR2_VAL_T1);
                    customEvent.AddGlobalAttribute(EVENT_TYPE, ATTR3, ATTR3_VAL_T1);

                    customEvent.AddMetric(METRIC1, METRIC1_VAL_T1);
                    customEvent.AddGlobalMetric(METRIC2, METRIC2_VAL_T1);
                    customEvent.AddGlobalMetric(EVENT_TYPE, METRIC3, METRIC3_VAL_T1);
                }
            });


            Task task2 = new Task(() =>
            {
                for (int i = 0; i < LOOP_COUNT; i++)
                {
                    customEvent.AddAttribute(ATTR1, ATTR1_VAL_T1);
                    customEvent.AddGlobalAttribute(ATTR2, ATTR2_VAL_T1);
                    customEvent.AddGlobalAttribute(EVENT_TYPE, ATTR3, ATTR3_VAL_T1);

                    customEvent.AddMetric(METRIC1, METRIC1_VAL_T1);
                    customEvent.AddGlobalMetric(METRIC2, METRIC2_VAL_T1);
                    customEvent.AddGlobalMetric(EVENT_TYPE, METRIC3, METRIC3_VAL_T1);
                }
            });


            task0.Start();
            task1.Start();
            task2.Start();

            // wait all task complete
            Task.WaitAll(new[] { task0, task1, task2 });

            // Get Model event.
            Amazon.MobileAnalytics.Model.Event modelEvent = customEvent.ConvertToMobileAnalyticsModelEvent(GetMobileAnalyticsManager("TestCustomEventConcurrency").Session);

            // check attribute value
            if (!modelEvent.Attributes.ContainsKey(ATTR1) || !modelEvent.Attributes.ContainsKey(ATTR2) || !modelEvent.Attributes.ContainsKey(ATTR3))
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[ATTR1] != ATTR1_VAL_T0 && modelEvent.Attributes[ATTR1] != ATTR1_VAL_T1 && modelEvent.Attributes[ATTR1] != ATTR1_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[ATTR2] != ATTR2_VAL_T0 && modelEvent.Attributes[ATTR2] != ATTR2_VAL_T1 && modelEvent.Attributes[ATTR2] != ATTR2_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[ATTR3] != ATTR3_VAL_T0 && modelEvent.Attributes[ATTR3] != ATTR3_VAL_T1 && modelEvent.Attributes[ATTR3] != ATTR3_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Attributes[ATTR_EMPTY1] != "" || modelEvent.Attributes[ATTR_EMPTY2] != "" || modelEvent.Attributes[ATTR_EMPTY3] != "")
            {
                Assert.Fail();
                return;
            }


            // check metric value
            if (!modelEvent.Metrics.ContainsKey(METRIC1) || !modelEvent.Metrics.ContainsKey(METRIC2) || !modelEvent.Metrics.ContainsKey(METRIC3))
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Metrics[METRIC1] != METRIC1_VAL_T0 && modelEvent.Metrics[METRIC1] != METRIC1_VAL_T1 && modelEvent.Metrics[METRIC1] != METRIC1_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Metrics[METRIC2] != METRIC2_VAL_T0 && modelEvent.Metrics[METRIC2] != METRIC2_VAL_T1 && modelEvent.Metrics[METRIC2] != METRIC2_VAL_T2)
            {
                Assert.Fail();
                return;
            }

            if (modelEvent.Metrics[METRIC3] != METRIC3_VAL_T0 && modelEvent.Metrics[METRIC3] != METRIC3_VAL_T1 && modelEvent.Metrics[METRIC3] != METRIC3_VAL_T2)
            {
                Assert.Fail();
                return;
            }
        }