public ActionResult CreateAlert(JobAlert alert)
        {
            var query = HttpUtility.ParseQueryString(Request.Url.Query);

            if (ModelState.IsValid)
            {
                var converter  = new JobSearchQueryConverter(ConfigurationManager.AppSettings["TranslateObsoleteJobTypes"]?.ToUpperInvariant() == "TRUE");
                var encoder    = new JobAlertIdEncoder(converter);
                var alertsRepo = new AzureTableStorageAlertsRepository(converter, ConfigurationManager.ConnectionStrings["Escc.EastSussexGovUK.Umbraco.AzureStorage"].ConnectionString);
                alert.Query   = converter.ToQuery(query);
                alert.AlertId = encoder.GenerateId(alert);
                alertsRepo.SaveAlert(alert);

                var jobAlertsSettings = new JobAlertsSettingsFromUmbraco(Umbraco).GetJobAlertsSettings(alert.JobsSet);
                if (jobAlertsSettings != null && !String.IsNullOrEmpty(jobAlertsSettings.NewAlertEmailSubject))
                {
                    var emailService = ServiceContainer.LoadService <IEmailSender>(new ConfigurationServiceRegistry(), new HttpContextCacheStrategy());
                    var sender       = new JobAlertsByEmailSender(jobAlertsSettings, new HtmlJobAlertFormatter(jobAlertsSettings, encoder), emailService);

                    sender.SendNewAlertConfirmation(alert);
                }

                query.Add("subscribed", "1");
            }
            return(new RedirectToUmbracoPageResult(CurrentPage, query));
        }
        private async Task SendAlertsForJobSet(IJobsDataProvider jobsProvider, JobsSet jobsSet, int?frequency, JobAlertSettings alertSettings, bool forceResend)
        {
            // No point sending alerts without links to the jobs
            if (alertSettings.JobAdvertBaseUrl == null)
            {
                _log.Error("JobAdvertBaseUrl not found - aborting");
                return;
            }

            // We need somewhere to get the alerts from...
            var converter = new JobSearchQueryConverter(ConfigurationManager.AppSettings["TranslateObsoleteJobTypes"]?.ToUpperInvariant() == "TRUE");
            var encoder   = new JobAlertIdEncoder(converter);
            IJobAlertsRepository alertsRepo = new AzureTableStorageAlertsRepository(converter, ConfigurationManager.ConnectionStrings["JobAlerts.AzureStorage"].ConnectionString);

            // We need a way to send the alerts...
            var configuration = new ConfigurationServiceRegistry();
            var emailService  = ServiceContainer.LoadService <IEmailSender>(configuration);
            var sender        = new JobAlertsByEmailSender(alertSettings, new HtmlJobAlertFormatter(alertSettings, encoder), emailService);

            // Get them, sort them and send them
            _log.Info($"Requesting jobs matching {jobsSet} with frequency {frequency} from Azure Storage");
            var alerts = await alertsRepo.GetAlerts(new JobAlertsQuery()
            {
                Frequency = frequency, JobsSet = jobsSet
            });

            var alertsGroupedByEmail = GroupAlertsByEmail(alerts);

            _log.Info($"{alerts.Count()} alerts found for {alertsGroupedByEmail.Count()} email addresses");
            foreach (var alertsForAnEmail in alertsGroupedByEmail)
            {
                foreach (var alert in alertsForAnEmail)
                {
                    var jobsSentForThisEmail = forceResend ? new List <int>() : await alertsRepo.GetJobsSentForEmail(alert.JobsSet, alert.Email);
                    await LookupJobsForAlert(jobsProvider, alert, jobsSentForThisEmail);
                }
            }

            _log.Info("Sending alerts");
            await sender.SendGroupedAlerts(alertsGroupedByEmail, alertsRepo);
        }
        public async Task AllJobsMarkedAsSent()
        {
            var spy    = new JobAlertsSpyRepository();
            var sender = new JobAlertsByEmailSender(new JobAlertSettings(), new FakeJobAlertFormatter(), new FakeEmailSender());
            var alerts = new List <List <JobAlert> >();

            alerts.Add(new List <JobAlert>()
            {
                new JobAlert()
                {
                    AlertId = "123"
                }, new JobAlert()
                {
                    AlertId = "456"
                }
            });
            alerts.Add(new List <JobAlert>()
            {
                new JobAlert()
                {
                    AlertId = "abc"
                }, new JobAlert()
                {
                    AlertId = "def"
                }
            });
            alerts[0][0].MatchingJobs.Add(new Job()
            {
                Id = 1
            });
            alerts[0][0].MatchingJobs.Add(new Job()
            {
                Id = 2
            });
            alerts[0][1].MatchingJobs.Add(new Job()
            {
                Id = 3
            });
            alerts[0][1].MatchingJobs.Add(new Job()
            {
                Id = 4
            });
            alerts[1][0].MatchingJobs.Add(new Job()
            {
                Id = 5
            });
            alerts[1][0].MatchingJobs.Add(new Job()
            {
                Id = 6
            });
            alerts[1][1].MatchingJobs.Add(new Job()
            {
                Id = 7
            });
            alerts[1][1].MatchingJobs.Add(new Job()
            {
                Id = 8
            });

            await sender.SendGroupedAlerts(alerts, spy);

            Assert.AreEqual(8, spy.UniqueJobCount);
        }