public async Task DistributeScheduledAsync(UserEventMessage userEvent, bool isLastAttempt) { await userNotificationsStore.TrackAttemptAsync(userEvent); try { var user = await userStore.GetCachedAsync(userEvent.AppId, userEvent.UserId); if (user == null) { throw new DomainException(Texts.Notification_NoApp); } var app = await appStore.GetCachedAsync(userEvent.AppId, default); if (app == null) { throw new DomainException(Texts.Notification_NoUser); } var(notification, targets) = await CreateUserNotificationAsync(userEvent, user, app); try { await userNotificationsStore.InsertAsync(notification); } catch (UniqueConstraintException) { throw new DomainException(Texts.Notification_AlreadyProcessed); } foreach (var channel in targets) { if (notification.Settings.TryGetValue(channel.Name, out var preference)) { await channel.SendAsync(notification, preference, user, app, false); } } } catch (Exception ex) { if (isLastAttempt) { await userNotificationsStore.TrackFailedAsync(userEvent); } if (ex is DomainException domainException) { await logStore.LogAsync(userEvent.AppId, domainException.Message); } else { throw; } } }
public async Task <IActionResult> Confirm(string id, [FromQuery] string?channel = null, [FromQuery] string?deviceIdentifier = null) { var token = TrackingToken.Parse(id, channel, deviceIdentifier); await userNotificationService.TrackConfirmedAsync(token); var notification = await userNotificationStore.FindAsync(token.Id, HttpContext.RequestAborted); if (notification == null) { return(View()); } var app = await appStore.GetCachedAsync(notification.AppId, HttpContext.RequestAborted); if (app?.ConfirmUrl != null && Uri.IsWellFormedUriString(app.ConfirmUrl, UriKind.Absolute)) { var url = app.ConfirmUrl !; if (url.Contains('?', StringComparison.OrdinalIgnoreCase)) { url += $"&id={id:notEmpty}"; } else { url += $"?id={id:notEmpty}"; } return(Redirect(url)); } return(View()); }
public Task SendAsync(List <UserNotification> notifications, bool isLastAttempt, CancellationToken ct) { return(log.ProfileAsync("SendEmail", async() => { await UpdateAsync(notifications, ProcessStatus.Attempt); var first = notifications[0]; var app = await appStore.GetCachedAsync(first.AppId, ct); if (app == null) { log.LogWarning(w => w .WriteProperty("action", "SendEmail") .WriteProperty("status", "Failed") .WriteProperty("reason", "App not found")); return; } try { var user = await userStore.GetCachedAsync(first.AppId, first.UserId, ct); if (user == null) { throw new DomainException(Texts.Email_UserNoEmail); } if (string.IsNullOrWhiteSpace(user.EmailAddress)) { throw new DomainException(Texts.Email_UserNoEmail); } await SendAsync(notifications, app, user, ct); await UpdateAsync(notifications, ProcessStatus.Handled); } catch (Exception ex) { if (isLastAttempt) { await UpdateAsync(notifications, ProcessStatus.Failed); } if (ex is DomainException domainException) { await logStore.LogAsync(app.Id, domainException.Message); } else { throw; } } }));
private async Task SendJobAsync(MessagingJob job, CancellationToken ct) { using (Telemetry.Activities.StartActivity("Send")) { var app = await appStore.GetCachedAsync(job.Notification.AppId, ct); if (app == null) { log.LogWarning("Cannot send message: App not found."); await UpdateAsync(job.Notification, ProcessStatus.Handled); return; } try { await UpdateAsync(job.Notification, ProcessStatus.Attempt); var senders = integrationManager.Resolve <IMessagingSender>(app, job.Notification).Select(x => x.Target).ToList(); if (senders.Count == 0) { await SkipAsync(job, Texts.Messaging_ConfigReset); return; } var(skip, template) = await GetTemplateAsync( job.Notification.AppId, job.Notification.UserLanguage, job.NotificationTemplate, ct); if (skip != null) { await SkipAsync(job, skip); return; } var text = messagingFormatter.Format(template, job.Notification); await SendCoreAsync(job, text, senders, ct); } catch (DomainException ex) { await logStore.LogAsync(job.Notification.AppId, Name, ex.Message); throw; } } }
public Task SendAsync(MobilePushJob job, bool isLastAttempt, CancellationToken ct) { return(log.ProfileAsync("SendMobilePush", async() => { var notification = job.Notification; var app = await appStore.GetCachedAsync(notification.AppId, ct); if (app == null) { log.LogWarning(w => w .WriteProperty("action", "SendMobilePush") .WriteProperty("status", "Failed") .WriteProperty("reason", "App not found")); return; } try { await UpdateAsync(notification, ProcessStatus.Attempt); if (!IsFirebaseConfigured(app)) { throw new DomainException(Texts.MobilePush_ConfigReset); } var messaging = pool.GetMessaging(app); await SendAnyAsync(job, messaging, ct); await UpdateAsync(notification, ProcessStatus.Handled); } catch (Exception ex) { if (isLastAttempt) { await UpdateAsync(notification, ProcessStatus.Failed); } if (ex is DomainException domainException) { await logStore.LogAsync(app.Id, domainException.Message); } else { throw; } } }));
private async Task SendJobAsync(MobilePushJob job, CancellationToken ct) { using (Telemetry.Activities.StartActivity("SendMobilePush")) { var notification = job.Notification; var app = await appStore.GetCachedAsync(notification.AppId, ct); if (app == null) { log.LogWarning("Cannot send email: App not found."); await UpdateAsync(job, ProcessStatus.Handled); return; } try { await UpdateAsync(job, ProcessStatus.Attempt); var senders = integrationManager.Resolve <IMobilePushSender>(app, notification).Select(x => x.Target).ToList(); if (senders.Count == 0) { await SkipAsync(job, Texts.Sms_ConfigReset); return; } await SendCoreAsync(job, app, senders, ct); await UpdateAsync(job, ProcessStatus.Handled); } catch (DomainException ex) { await logStore.LogAsync(app.Id, Name, ex.Message); throw; } } }
public async Task SendJobsAsync(List <EmailJob> jobs, CancellationToken ct) { using (Telemetry.Activities.StartActivity("Send")) { var first = jobs[0]; var commonEmail = first.EmailAddress; var commonApp = first.Notification.AppId; var commonUser = first.Notification.UserId; await UpdateAsync(first.Notification, commonEmail, ProcessStatus.Attempt); var app = await appStore.GetCachedAsync(first.Notification.AppId, ct); if (app == null) { log.LogWarning("Cannot send email: App not found."); await UpdateAsync(jobs, commonEmail, ProcessStatus.Handled); return; } try { var user = await userStore.GetCachedAsync(commonApp, commonUser, ct); if (user == null) { await SkipAsync(jobs, commonEmail, Texts.Email_UserDeleted); return; } if (string.IsNullOrWhiteSpace(user.EmailAddress)) { await SkipAsync(jobs, commonEmail, Texts.Email_UserNoEmail); return; } var senders = integrationManager.Resolve <IEmailSender>(app, first.Notification).Select(x => x.Target).ToList(); if (senders.Count == 0) { await SkipAsync(jobs, commonEmail, Texts.Email_ConfigReset); return; } EmailMessage?message; using (Telemetry.Activities.StartActivity("Format")) { var(skip, template) = await GetTemplateAsync( first.Notification.AppId, first.Notification.UserLanguage, first.EmailTemplate, ct); if (skip != null) { await SkipAsync(jobs, commonEmail, skip !); return; } if (template == null) { return; } var(result, errors) = await emailFormatter.FormatAsync(jobs, template, app, user, false, ct); if (errors.Count > 0 || result == null) { throw new EmailFormattingException(errors); } message = result; } await SendCoreAsync(message, app.Id, senders, ct); await UpdateAsync(jobs, commonEmail, ProcessStatus.Handled); } catch (DomainException ex) { await logStore.LogAsync(app.Id, Name, ex.Message); throw; } } }
public async Task DistributeScheduledAsync(UserEventMessage userEvent, bool isLastAttempt) { var links = userEvent.Links(); var parentContext = Activity.Current?.Context ?? default; using (var activity = Telemetry.Activities.StartActivity("DistributeUserEventScheduled", ActivityKind.Internal, parentContext, links: links)) { await userNotificationsStore.TrackAttemptAsync(userEvent); try { var user = await userStore.GetCachedAsync(userEvent.AppId, userEvent.UserId); if (user == null) { throw new DomainException(Texts.Notification_NoApp); } var app = await appStore.GetCachedAsync(userEvent.AppId); if (app == null) { throw new DomainException(Texts.Notification_NoUser); } var options = new SendOptions { App = app, User = user }; var notification = await CreateUserNotificationAsync(userEvent, options); notification.NotificationActivity = activity?.Context ?? default; try { await userNotificationsStore.InsertAsync(notification); } catch (UniqueConstraintException) { throw new DomainException(Texts.Notification_AlreadyProcessed); } foreach (var channel in channels) { if (notification.Channels.TryGetValue(channel.Name, out var notificationChannel)) { foreach (var configuration in notificationChannel.Status.Keys) { await channel.SendAsync(notification, notificationChannel.Setting, configuration, options, default); } } } log.LogInformation("Processed user event for app {appId} with ID {id} to topic {topic}.", userEvent.AppId, userEvent.EventId, userEvent.Topic); } catch (Exception ex) { if (isLastAttempt) { await userNotificationsStore.TrackFailedAsync(userEvent); } if (ex is DomainException domainException) { await logStore.LogAsync(userEvent.AppId, domainException.Message); } else { log.LogError(ex, "Failed to process user event for app {appId} with ID {id} to topic {topic}.", userEvent.AppId, userEvent.EventId, userEvent.Topic); throw; } } } }