Exemple #1
0
        public async Task OnMessageDispatchedAsync(OutputResult outputResult)
        {
            // If dispatch failed, update the history table with target -1
            if (!outputResult.Delivered)
            {
                var historyTable = await GetHistoryTableAsync(outputResult.EngagementAccount);

                var historyEntity = new MessageHistoryTableEntity(outputResult.EngagementAccount, outputResult.MessageId.ToString());
                if (historyEntity == null)
                {
                    EmailProviderEventSource.Current.Warning(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnMessageDispatchedAsync), OperationStates.Dropped, $"Does not find record for messageId={outputResult.MessageId}");
                    return;
                }

                historyEntity.Targets = -1;
                await MessageHistoryTableEntity.InsertOrMergeAsync(historyTable, historyEntity);
            }

            // If succeed, update the in-progress table
            else
            {
                // Insert in-progress table
                var inProgressEntity = new ReportInProgressTableEntity(outputResult.EngagementAccount, outputResult.MessageId, outputResult.DeliveryResponse.CustomMessageId);
                await ReportInProgressTableEntity.InsertOrMergeAsync(reportInProgressTable, inProgressEntity);
            }
        }
        public static async Task <bool> TryUpdateAsync(CloudTable table, ReportInProgressTableEntity entity)
        {
            try
            {
                // Azure Table by default uses optimistic concurrency checks as the default behavior for Replace
                var operation = TableOperation.Replace(entity);
                var result    = await table.ExecuteAsync(operation);

                entity = (ReportInProgressTableEntity)result.Result;

                return(true);
            }
            catch (StorageException ex)
            {
                if (ex.RequestInformation.HttpStatusCode == 412)
                {
                    EmailProviderEventSource.Current.Info(EmailProviderEventSource.EmptyTrackingId, "ReportInProgressTableEntity", "TryUpdateAsync", OperationStates.Skipping, $"Update entity failed due to conflict, skipped. account={entity.EngagementAccount} messageId={entity.MessageId}");
                    return(false);
                }
                else
                {
                    throw;
                }
            }
        }
 public MessageHistoryTableEntity(ReportInProgressTableEntity entity)
     : this(entity.EngagementAccount, entity.MessageId)
 {
     this.CustomMessageId = entity.CustomMessageId;
     this.Targets         = entity.Targets;
     this.Delivered       = entity.Delivered;
     this.Opened          = entity.Opened;
     this.Clicked         = entity.Clicked;
 }
Exemple #4
0
        public async Task <MessageRecord> GetMessageReportAsync(string engagementAccount, string messageId)
        {
            // Get from history table for metadata
            var historyTable = await GetHistoryTableAsync(engagementAccount);

            var historyEntity = await MessageHistoryTableEntity.GetAsync(historyTable, engagementAccount, messageId);

            if (historyEntity == null)
            {
                return(null);
            }

            var record = new MessageRecord
            {
                MessageId       = historyEntity.MessageId,
                SendTime        = historyEntity.SendTime,
                Targets         = historyEntity.Targets,
                Delivered       = historyEntity.Delivered,
                Opened          = historyEntity.Opened,
                Clicked         = historyEntity.Clicked,
                CustomMessageId = historyEntity.CustomMessageId
            };

            // Try to get from in-progress table
            var inProgressEntity = await ReportInProgressTableEntity.GetAsync(reportInProgressTable, engagementAccount, messageId);

            if (inProgressEntity != null)
            {
                record.Targets         = inProgressEntity.Targets;
                record.Delivered       = inProgressEntity.Delivered;
                record.Opened          = inProgressEntity.Opened;
                record.Clicked         = inProgressEntity.Clicked;
                record.CustomMessageId = inProgressEntity.CustomMessageId;
            }

            return(record);
        }
 public static async Task DeleteAsync(CloudTable table, ReportInProgressTableEntity entity)
 {
     var operation = TableOperation.Delete(entity);
     await table.ExecuteAsync(operation);
 }
        public static async Task <ReportInProgressTableEntity> InsertOrMergeAsync(CloudTable table, ReportInProgressTableEntity entity)
        {
            var operation = TableOperation.InsertOrMerge(entity);
            var result    = await table.ExecuteAsync(operation);

            return((ReportInProgressTableEntity)result?.Result);
        }
Exemple #7
0
        public async Task <List <MessageIdentifer> > ListInProgressMessagesAsync(string engagementAccount)
        {
            var messages = await ReportInProgressTableEntity.ListByAccountAsync(reportInProgressTable, engagementAccount);

            return(messages?.Select(m => new MessageIdentifer(m.MessageId, m.CustomMessageId)).ToList());
        }
Exemple #8
0
        public async Task <bool> OnReportUpdatedAsync(string engagementAccount, Report report)
        {
            try
            {
                // Get record in in-progress table
                var inProgressEntity = await ReportInProgressTableEntity.GetAsync(reportInProgressTable, engagementAccount, report.MessageIdentifer.MessageId);

                if (inProgressEntity == null)
                {
                    // Record should be already in history table
                    var historyTable = await GetHistoryTableAsync(engagementAccount);

                    var historyEntity = await MessageHistoryTableEntity.GetAsync(historyTable, engagementAccount, report.MessageIdentifer.MessageId);

                    if (historyEntity == null)
                    {
                        EmailProviderEventSource.Current.Warning(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Dropped, $"Does not find record for messageId={report.MessageIdentifer.MessageId}");
                        return(false);
                    }

                    // TODO: if after 24h we get new number of target shall we charge?
                    historyEntity.Targets        = report.TotalTarget;
                    historyEntity.Delivered      = report.TotalDelivered;
                    historyEntity.Opened         = report.TotalOpened;
                    historyEntity.Clicked        = report.TotalClicked;
                    historyEntity.LastUpdateTime = DateTime.UtcNow;

                    await MessageHistoryTableEntity.InsertOrMergeAsync(historyTable, historyEntity);

                    return(true);
                }

                var lastTarget = inProgressEntity.Targets;

                // Update the record in in-progress table
                inProgressEntity.Targets        = report.TotalTarget;
                inProgressEntity.Delivered      = report.TotalDelivered;
                inProgressEntity.Opened         = report.TotalOpened;
                inProgressEntity.Clicked        = report.TotalClicked;
                inProgressEntity.LastUpdateTime = DateTime.UtcNow;

                var updated = await ReportInProgressTableEntity.TryUpdateAsync(reportInProgressTable, inProgressEntity);

                // New target sent, do the charge (only if update succeed)
                if (updated && lastTarget < report.TotalTarget)
                {
                    try
                    {
                        var delta = report.TotalTarget - lastTarget;

                        // Get account detail
                        var account = await this.store.GetAccountAsync(engagementAccount);

                        metricManager.LogDeliverSuccess(delta, engagementAccount, account?.SubscriptionId ?? string.Empty);

                        var usage = new ResourceUsageRecord();
                        usage.EngagementAccount = engagementAccount;
                        usage.UsageType         = ResourceUsageType.EmailMessage;
                        usage.Quantity          = delta;

                        await this.billingAgent.StoreBillingUsageAsync(new List <ResourceUsageRecord> {
                            usage
                        }, CancellationToken.None);

                        EmailProviderEventSource.Current.Info(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Succeeded, $"Usage pushed to billing service account={engagementAccount} quantity={usage.Quantity}");
                    }
                    catch (Exception ex)
                    {
                        // We should monitor for billing failure
                        EmailProviderEventSource.Current.CriticalException(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Failed, $"Failed at pushing to billing service account={engagementAccount}", ex);
                    }
                }

                // If the record in in-progress for 24h, treat it as completed and move it to history table
                if (inProgressEntity.Timestamp.AddHours(Constants.ReportInProgressIntervalByHours) < DateTime.UtcNow)
                {
                    // Record should be already in history table
                    var historyTable = await GetHistoryTableAsync(engagementAccount);

                    var historyEntity = new MessageHistoryTableEntity(inProgressEntity);

                    await MessageHistoryTableEntity.InsertOrMergeAsync(historyTable, historyEntity);

                    await ReportInProgressTableEntity.DeleteAsync(reportInProgressTable, inProgressEntity);
                }

                return(true);
            }
            catch (Exception ex)
            {
                EmailProviderEventSource.Current.ErrorException(EmailProviderEventSource.EmptyTrackingId, this, nameof(this.OnReportUpdatedAsync), OperationStates.Failed, string.Empty, ex);
                return(false);
            }
        }