private async Task AttemptDeleteRepair( ContentTypeDefinition contentTypeDefinition, IContentItemVersion contentItemVersion, ValidateAndRepairResult result, ValidationFailure failure) { var deleteGraphSyncer = _serviceProvider.GetRequiredService <IDeleteGraphSyncer>(); try { await deleteGraphSyncer.DeleteIfAllowed(failure.ContentItem, contentItemVersion, SyncOperation.Delete); } catch (Exception ex) { _logger.LogWarning(ex, "Repair of deleted {ContentItem} failed.", failure.ContentItem); } (bool validated, string?validationFailureReason) = await ValidateDeletedContentItem(failure.ContentItem, contentTypeDefinition, contentItemVersion); if (validated) { _logger.LogInformation("Repair was successful on deleted {ContentType} {ContentItemId} in {CurrentGraph}.", failure.ContentItem.ContentType, failure.ContentItem.ContentItemId, GraphDescription(_currentGraph !)); result.Repaired.Add(new ValidatedContentItem(failure.ContentItem, ValidateType.Delete)); } else { string message = $"Repair was unsuccessful.{Environment.NewLine}{{ValidationFailureReason}}."; _logger.LogWarning(message, validationFailureReason); result.RepairFailures.Add(new RepairFailure(failure.ContentItem, validationFailureReason !, ValidateType.Delete)); } }
private async Task AttemptRepair( IEnumerable <ValidationFailure> syncValidationFailures, ContentTypeDefinition contentTypeDefinition, IContentItemVersion contentItemVersion, ValidateAndRepairResult result) { _logger.LogWarning( "Content items of type {ContentTypeDefinitionName} failed validation ({ValidationFailures}). Attempting to repair them.", contentTypeDefinition.Name, string.Join(", ", syncValidationFailures.Select(f => f.ContentItem.ToString()))); foreach (var failure in syncValidationFailures) { if (failure.Type == ValidateType.Merge) { await AttemptMergeRepair(contentTypeDefinition, contentItemVersion, result, failure); } else { await AttemptDeleteRepair(contentTypeDefinition, contentItemVersion, result, failure); } } }
private async Task AttemptMergeRepair( ContentTypeDefinition contentTypeDefinition, IContentItemVersion contentItemVersion, ValidateAndRepairResult result, ValidationFailure failure) { var mergeGraphSyncer = _serviceProvider.GetRequiredService <IMergeGraphSyncer>(); IGraphReplicaSet graphReplicaSet = _currentGraph !.GetReplicaSetLimitedToThisGraph(); try { await mergeGraphSyncer.SyncToGraphReplicaSetIfAllowed(graphReplicaSet, failure.ContentItem, _contentManager); } catch (Exception ex) { _logger.LogWarning(ex, "Repair of {ContentItem} in {GraphReplicaSet} failed.", failure.ContentItem, graphReplicaSet); } (bool validated, string?validationFailureReason) = await ValidateContentItem(failure.ContentItem, contentTypeDefinition, contentItemVersion); if (validated) { _logger.LogInformation("Repair was successful on {ContentType} {ContentItemId} in {CurrentGraph}.", failure.ContentItem.ContentType, failure.ContentItem.ContentItemId, GraphDescription(_currentGraph !)); result.Repaired.Add(new ValidatedContentItem(failure.ContentItem)); } else { string message = $"Repair was unsuccessful.{Environment.NewLine}{{ValidationFailureReason}}."; _logger.LogWarning(message, validationFailureReason); result.RepairFailures.Add(new RepairFailure(failure.ContentItem, validationFailureReason !)); } }
private async Task <IValidateAndRepairResults> ValidateGraphImpl( ValidationScope validationScope, params string[] graphReplicaSetNames) { IEnumerable <ContentTypeDefinition> syncableContentTypeDefinitions = _contentDefinitionManager .ListTypeDefinitions() .Where(x => x.Parts.Any(p => p.Name == nameof(GraphSyncPart))); DateTime timestamp = DateTime.UtcNow; IEnumerable <string> graphReplicaSetNamesToValidate = graphReplicaSetNames.Any() ? graphReplicaSetNames : _graphClusterLowLevel.GraphReplicaSetNames; DateTime validateFrom = await GetValidateFromTime(validationScope); var results = new ValidateAndRepairResults(validateFrom); //todo: we could optimise to only get content items from the oc database once for each replica set, // rather than for each instance foreach (string graphReplicaSetName in graphReplicaSetNamesToValidate) { IGraphReplicaSetLowLevel graphReplicaSetLowLevel = _graphClusterLowLevel.GetGraphReplicaSetLowLevel(graphReplicaSetName); IContentItemVersion contentItemVersion = _contentItemVersionFactory.Get(graphReplicaSetName); foreach (IGraph graph in graphReplicaSetLowLevel.GraphInstances) { ValidateAndRepairResult result = results.AddNewValidateAndRepairResult( graphReplicaSetName, graph.Instance, graph.Endpoint.Name, graph.GraphName, graph.DefaultGraph); // make current graph available for when parts/fields call back into ValidateContentItem // seems a little messy, and will make concurrent validation a pita, // but stops part/field syncers needing low level graph access _currentGraph = graph; foreach (ContentTypeDefinition contentTypeDefinition in syncableContentTypeDefinitions) { List <ValidationFailure> syncFailures = await ValidateContentItemsOfContentType( contentItemVersion, contentTypeDefinition, validateFrom, result, validationScope); if (syncFailures.Any()) { await AttemptRepair(syncFailures, contentTypeDefinition, contentItemVersion, result); } } } } if (results.AnyRepairFailures) { return(results); } _logger.LogInformation("Woohoo: graph passed validation or was successfully repaired at {Time}.", timestamp.ToString("O")); _session.Save(new AuditSyncLog(timestamp)); await _session.CommitAsync(); return(results); }
private async Task <List <ValidationFailure> > ValidateContentItemsOfContentType( IContentItemVersion contentItemVersion, ContentTypeDefinition contentTypeDefinition, DateTime lastSynced, ValidateAndRepairResult result, ValidationScope scope) { List <ValidationFailure> syncFailures = new List <ValidationFailure>(); (bool?latest, bool?published) = contentItemVersion.ContentItemIndexFilterTerms; //todo: do we want to batch up content items of type? IEnumerable <ContentItem> contentTypeContentItems = await _contentItemsService .Get(contentTypeDefinition.Name, lastSynced, latest : latest, published : published); IEnumerable <ContentItem> deletedContentTypeContentItems = await _contentItemsService .GetDeleted(contentTypeDefinition.Name, lastSynced); if (!contentTypeContentItems.Any() && !deletedContentTypeContentItems.Any()) { _logger.LogDebug("No {ContentType} content items found that require validation.", contentTypeDefinition.Name); return(syncFailures); } foreach (ContentItem contentItem in contentTypeContentItems) { (bool validated, string?validationFailureReason) = await ValidateContentItem(contentItem, contentTypeDefinition, contentItemVersion); if (validated) { _logger.LogInformation("Sync validation passed for {ContentType} {ContentItemId} in {CurrentGraph}.", contentItem.ContentType, contentItem.ContentItemId, GraphDescription(_currentGraph !)); result.Validated.Add(new ValidatedContentItem(contentItem)); } else { string message = $"Sync validation failed in {{CurrentGraph}}.{Environment.NewLine}{{ValidationFailureReason}}."; _logger.LogWarning(message, GraphDescription(_currentGraph !), validationFailureReason); ValidationFailure validationFailure = new ValidationFailure(contentItem, validationFailureReason !); syncFailures.Add(validationFailure); result.ValidationFailures.Add(validationFailure); } } if (scope == ValidationScope.ModifiedSinceLastValidation) { foreach (ContentItem contentItem in deletedContentTypeContentItems) { (bool validated, string?validationFailureReason) = await ValidateDeletedContentItem(contentItem, contentTypeDefinition, contentItemVersion); if (validated) { _logger.LogInformation( "Sync validation passed for deleted {ContentType} {ContentItemId} in {CurrentGraph}.", contentItem.ContentType, contentItem.ContentItemId, GraphDescription(_currentGraph !)); result.Validated.Add(new ValidatedContentItem(contentItem, ValidateType.Delete)); } else { string message = $"Sync validation failed in {{CurrentGraph}}.{Environment.NewLine}{{validationFailureReason}}."; _logger.LogWarning(message, GraphDescription(_currentGraph !), validationFailureReason); ValidationFailure validationFailure = new ValidationFailure(contentItem, validationFailureReason !, ValidateType.Delete); syncFailures.Add(validationFailure); result.ValidationFailures.Add(validationFailure); } } } return(syncFailures); }