public SeedResponse Seed() { var allContent = new List <IContent>(); // Add all root nodes var rootContent = _contentService.GetRootContent().ToList(); allContent.AddRange(rootContent); // Add all descendants foreach (var content in rootContent) { var descendants = _contentService.GetPagedDescendants(content.Id, 0, int.MaxValue, out var total).ToList(); allContent.AddRange(descendants); } var jobs = new List <EnterspeedJob>(); if (!allContent.Any()) { return(new SeedResponse { JobsAdded = 0, Nodes = 0, }); } using (var context = _umbracoContextFactory.EnsureUmbracoContext()) { foreach (var content in allContent) { var contentJobs = GetJobsForContent(content, context); if (contentJobs == null || !contentJobs.Any()) { continue; } jobs.AddRange(contentJobs); } } _enterspeedJobRepository.Save(jobs); return(new SeedResponse { Nodes = allContent.Count, JobsAdded = jobs.Count }); }
private void EnqueueJobs(List <EnterspeedJob> jobs) { if (!jobs.Any()) { return; } _enterspeedJobRepository.Save(jobs); using (_umbracoContextFactory.EnsureUmbracoContext()) { if (_scopeProvider.Context != null) { var key = $"UpdateEnterspeed_{DateTime.Now.Ticks}"; // Add a callback to the current Scope which will execute when it's completed _scopeProvider.Context.Enlist(key, scopeCompleted => HandleJobs(scopeCompleted, jobs)); } } }
public void HandleJobs(List <EnterspeedJob> jobs) { if (!jobs.Any()) { return; } _logger.Debug <EnterspeedJobHandler>("Handling {jobsCount} jobs", jobs.Count); // Update jobs from pending to processing foreach (var job in jobs) { job.State = EnterspeedJobState.Processing; job.UpdatedAt = DateTime.UtcNow; } _enterspeedJobRepository.Save(jobs); // Process nodes var failedJobs = new List <EnterspeedJob>(); var failedJobsToDelete = new List <EnterspeedJob>(); // Get a dictionary of contentid and cultures to handle var jobsToHandle = jobs .Select(x => x.ContentId) .Distinct() .ToDictionary(x => x, x => jobs.Where(j => j.ContentId == x).Select(j => j.Culture).Distinct().ToList()); // Fetch all failed jobs for these content ids. We need to do this to delete the failed jobs if they no longer fails var failedJobsToHandle = _enterspeedJobRepository.GetFailedJobs(jobs.Select(x => x.ContentId).Distinct().ToList()); using (var context = _umbracoContextFactory.EnsureUmbracoContext()) { foreach (var jobInfo in jobsToHandle) { foreach (var culture in jobInfo.Value) { // List of all jobs with this contentid and culture var jobsToRun = jobs .Where(x => x.ContentId == jobInfo.Key && x.Culture == culture) .OrderBy(x => x.CreatedAt) .ToList(); // Get the failed jobs and add it to the batch of jobs that needs to be handled, so we can delete them afterwards failedJobsToDelete.AddRange(failedJobsToHandle.Where(x => x.ContentId == jobInfo.Key && x.Culture == culture)); // We only need to execute the latest jobs instruction. var newestJob = jobsToRun.LastOrDefault(); var shouldDelete = newestJob?.JobType == EnterspeedJobType.Delete; var shouldPublish = newestJob?.JobType == EnterspeedJobType.Publish; if (shouldPublish) { var content = context.UmbracoContext.Content.GetById(newestJob.ContentId); if (content == null) { // Create a new failed job var exception = $"Content with id {newestJob.ContentId} not in cache"; failedJobs.Add(GetFailedJob(newestJob, exception)); _logger.Warn <EnterspeedJobHandler>(exception); continue; } // Create Umbraco Enterspeed Entity UmbracoContentEntity umbracoData; try { var redirects = _redirectsService.GetRedirects(content.Key, culture); umbracoData = new UmbracoContentEntity(content, _enterspeedPropertyService, _entityIdentityService, redirects, culture); } catch (Exception e) { // Create a new failed job var exception = $"Failed creating entity ({newestJob.ContentId}/{newestJob.Culture}). Message: {e.Message}. StackTrace: {e.StackTrace}"; failedJobs.Add(GetFailedJob(newestJob, exception)); _logger.Warn <EnterspeedJobHandler>(exception); continue; } var ingestResponse = _enterspeedIngestService.Save(umbracoData); if (!ingestResponse.Success) { // Create a new failed job var message = ingestResponse.Exception != null ? ingestResponse.Exception.Message : ingestResponse.Message; var exception = $"Failed ingesting entity ({newestJob.ContentId}/{newestJob.Culture}). Message: {message}"; failedJobs.Add(GetFailedJob(newestJob, exception)); _logger.Warn <EnterspeedJobHandler>(exception); } } else if (shouldDelete) { var id = _entityIdentityService.GetId(newestJob.ContentId, newestJob.Culture); var deleteResponse = _enterspeedIngestService.Delete(id); if (!deleteResponse.Success && deleteResponse.Status != HttpStatusCode.NotFound) { // Create a new failed job var exception = $"Failed deleting entity ({newestJob.ContentId}/{newestJob.Culture}). Message: {deleteResponse.Message}"; failedJobs.Add(GetFailedJob(newestJob, exception)); _logger.Warn <EnterspeedJobHandler>(exception); continue; } } } } } // Save all jobs that failed _enterspeedJobRepository.Save(failedJobs); // Delete all jobs - Note, that it's safe to delete all jobs because failed jobs will be created as a new job _enterspeedJobRepository.Delete(jobs.Select(x => x.Id).Concat(failedJobsToDelete.Select(x => x.Id)).ToList()); // Throw exception with a combined exception message for all jobs that failed if any if (failedJobs.Any()) { var failedJobExceptions = string.Join(Environment.NewLine, failedJobs.Select(x => x.Exception)); throw new Exception(failedJobExceptions); } }