private void SetScopesProperty(ActionPropertyBag propertyBag, IEnumerable <string> scopes)
 {
     // TODO(mats): how is this supposed to work?  Should we get multiple properties somehow, one per scope?  or do we send up all scopes in a space-delimited string (will make querying hard on the backend)
     foreach (string scope in scopes)
     {
         if (_telemetryAllowedScopes.Contains(scope))
         {
             propertyBag.Add(ActionPropertyNames.ScopeConstStrKey, scope);
         }
         else if (!string.IsNullOrEmpty(scope))
         {
             propertyBag.Add(ActionPropertyNames.ScopeConstStrKey, "ScopeRedacted");
         }
     }
 }
        private ActionArtifacts CreateGenericAction(MatsScenario scenario, string correlationId, ActionType actionType)
        {
            string     actionId = MatsId.Create();
            MatsAction action   = new MatsAction(actionId, scenario, correlationId);

            string corrIdTrim = correlationId.TrimCurlyBraces();

            var propertyBag    = new ActionPropertyBag(_errorStore);
            var startTimePoint = DateTime.UtcNow;

            propertyBag.Add(ActionPropertyNames.UploadIdConstStrKey, MatsId.Create());
            propertyBag.Add(ActionPropertyNames.ActionTypeConstStrKey, MatsConverter.AsString(actionType));
            propertyBag.Add(ScenarioPropertyNames.IdConstStrKey, scenario?.ScenarioId);
            propertyBag.Add(ActionPropertyNames.CorrelationIdConstStrKey, corrIdTrim);
            propertyBag.Add(ActionPropertyNames.StartTimeConstStrKey, DateTimeUtils.GetMillisecondsSinceEpoch(startTimePoint));

            lock (_lockActionIdToPropertyBag)
            {
                _actionIdToPropertyBag[actionId] = propertyBag;
            }

            return(new ActionArtifacts(action, propertyBag));
        }
        private void PopulateDuration(ActionPropertyBag propertyBag)
        {
            var  contents = propertyBag.GetContents();
            long startTime;
            long endTime;

            if (!contents.Int64Properties.TryGetValue(ActionPropertyNames.StartTimeConstStrKey, out startTime))
            {
                _errorStore.ReportError("Could not retrieve start time for duration calculation.", ErrorType.Action, ErrorSeverity.LibraryError);
                return;
            }

            if (!contents.Int64Properties.TryGetValue(ActionPropertyNames.EndTimeConstStrKey, out endTime))
            {
                _errorStore.ReportError("Could not retrieve end time for duration calculation.", ErrorType.Action, ErrorSeverity.LibraryError);
                return;
            }

            long duration = endTime - startTime;

            propertyBag.Add(ActionPropertyNames.DurationConstStrKey + ActionPropertyNames.SumConstStrSuffix, duration);
            propertyBag.Add(ActionPropertyNames.DurationConstStrKey + ActionPropertyNames.MaxConstStrSuffix, duration);
            propertyBag.Add(ActionPropertyNames.DurationConstStrKey + ActionPropertyNames.MinConstStrSuffix, duration);
        }
        private void EndGenericAction(string actionId, string outcome, ErrorSource errorSource, string error, string errorDescription, string accountCid)
        {
            if (string.IsNullOrEmpty(actionId))
            {
                // This is an invalid action; we do not want to upload it.
                _errorStore.ReportError("Tried to end an action with an empty actionId", ErrorType.Action, ErrorSeverity.Warning);
                return;
            }

            ActionPropertyBag propertyBag = GetActionPropertyBagFromId(actionId);

            if (propertyBag == null)
            {
                _errorStore.ReportError("Trying to end an action that doesn't exist or was already uploaded", ErrorType.Action, ErrorSeverity.Warning);
                return;
            }

            if (propertyBag.ReadyForUpload)
            {
                return;
            }

            int startingCount = 1;
            var endTime       = DateTime.UtcNow;

            propertyBag.Add(ActionPropertyNames.OutcomeConstStrKey, outcome);
            propertyBag.Add(ActionPropertyNames.FailureSourceConstStrKey, MatsConverter.AsString(errorSource));
            propertyBag.Add(ActionPropertyNames.FailureConstStrKey, error);
            propertyBag.Add(ActionPropertyNames.FailureDescriptionConstStrKey, errorDescription);
            propertyBag.Add(ActionPropertyNames.EndTimeConstStrKey, DateTimeUtils.GetMillisecondsSinceEpoch(endTime));
            // propertyBag->Add(ActionPropertyNames::getAccountIdConstStrKey(), accountCid);  Commenting this out for GDPR reasons; once pipeline supports this we can upload again.
            propertyBag.Add(ActionPropertyNames.CountConstStrKey, startingCount);
            PopulateDuration(propertyBag);

            //Check if should aggregate here
            var contents = propertyBag.GetContents();

            if (_eventFilter.ShouldAggregateAction(contents))
            {
                EndAggregatedAction(actionId, propertyBag);
            }
            else
            {
                propertyBag.ReadyForUpload = true;
            }
        }