//todo: if issue with data, don't just hang visualiser
        private async Task <IEnumerable <IQuery <object?> > > BuildVisualisationCommands(
            string contentItemId,
            IContentItemVersion contentItemVersion)
        {
            ContentItem?contentItem = await contentItemVersion.GetContentItem(_contentManager, contentItemId);

            if (contentItem == null)
            {
                return(Enumerable.Empty <IQuery <INodeAndOutRelationshipsAndTheirInRelationships> >());
            }
            //todo: best to not use dynamic
            dynamic?graphSyncPartContent = contentItem.Content[nameof(GraphSyncPart)];

            _syncNameProvider.ContentType = contentItem.ContentType;

            string?sourceNodeId = _syncNameProvider.GetNodeIdPropertyValue(graphSyncPartContent, contentItemVersion);
            IEnumerable <string> sourceNodeLabels = await _syncNameProvider.NodeLabels();

            string sourceNodeIdPropertyName = _syncNameProvider.IdPropertyName();

            var rootContext = await _describeContentItemHelper.BuildRelationships(
                contentItem, sourceNodeIdPropertyName, sourceNodeId, sourceNodeLabels, _syncNameProvider,
                _contentManager, contentItemVersion, null, _serviceProvider);

            //todo: return relationships - can we do it without creating cypher outside of a query?

            if (rootContext == null)
            {
                return(Enumerable.Empty <IQuery <object?> >());
            }

            //todo: should create relationships in here
            return(await _describeContentItemHelper.GetRelationshipCommands(rootContext));
        }
        public async Task <IAllowSync> DeleteAllowed(ContentItem contentItem,
                                                     IContentItemVersion contentItemVersion,
                                                     SyncOperation syncOperation,
                                                     IEnumerable <KeyValuePair <string, object> >?deleteIncomingRelationshipsProperties = null,
                                                     IGraphDeleteContext?parentContext = null)
        {
            _syncNameProvider.ContentType = contentItem.ContentType;

            if (contentItem.Content.GraphSyncPart == null || _syncNameProvider.GraphSyncPartSettings.PreexistingNode)
            {
                return(AllowSync.NotRequired);
            }

            //todo: helper for this
            var allDeleteIncomingRelationshipsProperties = new HashSet <KeyValuePair <string, object> >();

            if (deleteIncomingRelationshipsProperties != null)
            {
                allDeleteIncomingRelationshipsProperties.UnionWith(deleteIncomingRelationshipsProperties);
            }
            //todo: unions unnecessarily when called embeddedly : new deleteembeddedallowed method?
            if (syncOperation == SyncOperation.Unpublish)
            {
                allDeleteIncomingRelationshipsProperties.UnionWith(
                    ContentPickerFieldGraphSyncer.ContentPickerRelationshipProperties);
            }

            _graphDeleteItemSyncContext = new GraphDeleteContext(
                contentItem, _deleteNodeCommand, this, syncOperation, _syncNameProvider,
                _contentManager, contentItemVersion, allDeleteIncomingRelationshipsProperties, parentContext,
                _serviceProvider);

            return(await DeleteAllowed());
        }
Exemplo n.º 3
0
        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));
            }
        }
Exemplo n.º 4
0
        #pragma warning restore S4144

        private async Task PublishContentEvent(
            IOrchestrationContext context,
            ContentEventType eventType)
        {
            if (!_eventGridConfiguration.CurrentValue.PublishEvents)
            {
                _logger.LogInformation("Event grid publishing is disabled. No events will be published.");
                return;
            }

            try
            {
                IContentItemVersion contentItemVersion = eventType switch
                {
                    ContentEventType.Published => _publishedContentItemVersion,
                    ContentEventType.Draft => _previewContentItemVersion,
                    _ => _neutralEventContentItemVersion
                };

                string userId = _syncNameProvider.GetEventIdPropertyValue(
                    context.ContentItem.Content.GraphSyncPart,
                    contentItemVersion);

                ContentEvent contentEvent = new ContentEvent(context.ContentItem, userId, eventType);
                await _eventGridContentClient.Publish(contentEvent);
            }
            catch (Exception publishException)
            {
                _logger.LogError(publishException, "The event grid event could not be published.");
                await context.Notifier.Add("Warning: the event grid event could not be published. Composite apps might not show your changes.",
                                           "Exception", publishException, type : NotifyType.Warning);
            }
        }
Exemplo n.º 5
0
 public DescribeRelationshipsContext(string sourceNodeIdPropertyName,
                                     string sourceNodeId,
                                     IEnumerable <string> sourceNodeLabels,
                                     ContentItem contentItem,
                                     int maxDepthFromHere,
                                     ISyncNameProvider graphSyncHelper,
                                     IContentManager contentManager,
                                     IContentItemVersion contentItemVersion,
                                     IDescribeRelationshipsContext?parentContext,
                                     IServiceProvider serviceProvider) : base(
         contentItem,
         graphSyncHelper,
         contentManager,
         contentItemVersion,
         parentContext,
         serviceProvider.GetRequiredService <ILogger <GraphDeleteContext> >())
 {
     AvailableRelationships   = new List <ContentItemRelationship>();
     ServiceProvider          = serviceProvider;
     SourceNodeId             = sourceNodeId;
     SourceNodeLabels         = sourceNodeLabels;
     MaxDepthFromHere         = maxDepthFromHere;
     SourceNodeIdPropertyName = sourceNodeIdPropertyName;
     CurrentDepth             = (parentContext?.CurrentDepth + 1) ?? 0;
 }
        private async Task <(bool validated, string failureReason)> ValidateDeletedContentItem(
            ContentItem contentItem,
            ContentTypeDefinition contentTypeDefinition,
            IContentItemVersion contentItemVersion)
        {
            _logger.LogDebug("Validating deleted {ContentType} {ContentItemId} '{ContentDisplayText}'.",
                             contentItem.ContentType, contentItem.ContentItemId, contentItem.DisplayText);

            _syncNameProvider.ContentType = contentItem.ContentType;

            object nodeId = _syncNameProvider.GetNodeIdPropertyValue(contentItem.Content.GraphSyncPart, contentItemVersion);

            // this is basically querying to see if the node's there or not - a simpler query might be better
            ISubgraph?nodeWithRelationships = (await _currentGraph !.Run(
                                                   new SubgraphQuery(
                                                       await _syncNameProvider.NodeLabels(),
                                                       _syncNameProvider.IdPropertyName(),
                                                       nodeId,
                                                       SubgraphQuery.RelationshipFilterNone, 0)))
                                              .FirstOrDefault();

            return(nodeWithRelationships?.SourceNode != null
                ? (false, $"{contentTypeDefinition.DisplayName} {contentItem.ContentItemId} is still present in the graph.")
                : (true, ""));
        }
Exemplo n.º 7
0
        protected async Task <(IAllowSync, IDeleteGraphSyncer?)> GetDeleteGraphSyncerIfDeleteAllowed(
            ContentItem contentItem,
            IContentItemVersion contentItemVersion,
            SyncOperation syncOperation)
        {
            try
            {
                IDeleteGraphSyncer deleteGraphSyncer = _serviceProvider.GetRequiredService <IDeleteGraphSyncer>();

                IAllowSync allowSync = await deleteGraphSyncer.DeleteAllowed(
                    contentItem,
                    contentItemVersion,
                    syncOperation);

                return(allowSync, deleteGraphSyncer);
            }
            catch (Exception exception)
            {
                string contentType = GetContentTypeDisplayName(contentItem);

                //todo: will get logged twice, but want to keep the param version
                _logger.LogError(exception, "Unable to check if the '{ContentItem}' {ContentType} can be {DeleteOperation} from the {GraphReplicaSetName} graph.",
                                 contentItem.DisplayText, contentType, syncOperation.ToString("PrP", null).ToLower(), contentItemVersion.GraphReplicaSetName);

                await _notifier.Add(GetSyncOperationCancelledUserMessage(syncOperation, contentItem.DisplayText, contentType),
                                    $"Unable to check if the '{contentItem.DisplayText}' {contentType} can be {syncOperation.ToString("PrP", null).ToLower()} from the {contentItemVersion.GraphReplicaSetName} graph.",
                                    exception : exception);

                throw;
            }
        }
        private object GetNodeId(
            ContentItem pickedContentItem,
            ISyncNameProvider syncNameProvider,
            IContentItemVersion contentItemVersion)
        {
            //todo: add GetNodeId support to TaxonomyFieldGraphSyncer

            return(syncNameProvider.GetNodeIdPropertyValue(
                       pickedContentItem.Content[nameof(GraphSyncPart)], contentItemVersion));
        }
Exemplo n.º 9
0
        private object?GetNodeId(
            string termContentItemId,
            IDictionary <string, ContentItem> taxonomyTerms,
            ISyncNameProvider termSyncNameProvider,
            IContentItemVersion contentItemVersion)
        {
            ContentItem termContentItem = taxonomyTerms[termContentItemId];

            return(termSyncNameProvider.GetNodeIdPropertyValue(
                       (JObject)termContentItem.Content[nameof(GraphSyncPart)] !, contentItemVersion));
        }
        //todo: contentmanager
        //todo: taxonomies use reltype*maxdepth ?
        //todo: for child contexts, do we need anything more than parentcontext, contentitem & relationships?

        public async Task <IDescribeRelationshipsContext?> BuildRelationships(
            ContentItem contentItem,
            string sourceNodeIdPropertyName,
            string sourceNodeId,
            IEnumerable <string> sourceNodeLabels,
            ISyncNameProvider syncNameProvider,
            IContentManager contentManager,
            IContentItemVersion contentItemVersion,
            IDescribeRelationshipsContext?parentContext,
            IServiceProvider serviceProvider)
        {
            var graphSyncPartSettings = syncNameProvider.GetGraphSyncPartSettings(contentItem.ContentType);

            int maxDepthFromHere;

            if (parentContext == null)
            {
                maxDepthFromHere = Math.Min(graphSyncPartSettings.VisualiserNodeDepth ?? int.MaxValue,
                                            //todo: store in root in case changes mid flow?
                                            _graphSyncSettings.CurrentValue.MaxVisualiserNodeDepth);
            }
            else
            {
                if (_encounteredContentTypes.Any(x => x == contentItem.ContentType))
                {
                    return(null);
                }

                maxDepthFromHere = Math.Min(parentContext.MaxDepthFromHere - 1,
                                            graphSyncPartSettings.VisualiserNodeDepth ?? int.MaxValue);
            }

            if (maxDepthFromHere <= 0)
            {
                return(null);
            }

            var context = new DescribeRelationshipsContext(
                sourceNodeIdPropertyName, sourceNodeId, sourceNodeLabels, contentItem, maxDepthFromHere, syncNameProvider,
                contentManager, contentItemVersion, parentContext, serviceProvider);

            foreach (IContentItemGraphSyncer itemSyncer in _contentItemGraphSyncers)
            {
                //todo: allow syncers to chain or not? probably not
                if (itemSyncer.CanSync(context.ContentItem))
                {
                    await itemSyncer.AddRelationship(context);
                }
            }

            _encounteredContentTypes.Add(contentItem.ContentType);

            return(context);
        }
Exemplo n.º 11
0
        private async Task <ContentItem?> GetTaxonomyContentItem(
            JObject contentItemField,
            IContentItemVersion contentItemVersion,
            IContentManager contentManager)
        {
            string taxonomyContentItemId = contentItemField[TaxonomyContentItemId]?.ToObject <string>() !;

            //todo: null?

            //todo: need to really think this through/test it
            return(await contentItemVersion.GetContentItem(contentManager, taxonomyContentItemId));
        }
        protected GraphSyncContext(
            ContentItem contentItem,
            ISyncNameProvider syncNameProvider,
            IContentManager contentManager,
            IContentItemVersion contentItemVersion,
            IGraphSyncContext?parentContext,
            ILogger logger)
            : base(contentItem, syncNameProvider, contentManager, contentItemVersion, logger)
        {
            ParentContext = parentContext;
            parentContext?.AddChildContext(this);

            _childContexts = new List <IGraphSyncContext>();
        }
Exemplo n.º 13
0
        protected GraphOperationContext(
            ContentItem contentItem,
            ISyncNameProvider syncNameProvider,
            IContentManager contentManager,
            IContentItemVersion contentItemVersion,
            ILogger logger)
        {
            _logger            = logger;
            ContentItem        = contentItem;
            SyncNameProvider   = syncNameProvider;
            ContentManager     = contentManager;
            ContentItemVersion = contentItemVersion;

            // will be set before any syncers receive a context
            ContentTypePartDefinition = default !;
Exemplo n.º 14
0
 public CloneContext(
     ContentItem contentItem,
     ICloneGraphSync cloneGraphSync,
     ISyncNameProvider syncNameProvider,
     IContentManager contentManager,
     IContentItemVersion contentItemVersion,
     IServiceProvider serviceProvider,
     ICloneContext?parentContext = null)
     : base(
         contentItem,
         syncNameProvider,
         contentManager,
         contentItemVersion,
         parentContext,
         serviceProvider.GetRequiredService <ILogger <CloneContext> >())
 {
     CloneGraphSync = cloneGraphSync;
 }
        private async Task <bool> DeleteFromGraphReplicaSetIfAllowed(
            ContentItem contentItem,
            IContentItemVersion contentItemVersion,
            SyncOperation syncOperation)
        {
            (IAllowSync allowSync, IDeleteGraphSyncer? publishedDeleteGraphSyncer)
                = await GetDeleteGraphSyncerIfDeleteAllowed(
                      contentItem,
                      contentItemVersion,
                      syncOperation);

            switch (allowSync.Result)
            {
            case AllowSyncResult.Blocked:
                await _notifier.AddBlocked(
                    syncOperation,
                    contentItem,
                    new[] { (contentItemVersion.GraphReplicaSetName, allowSync) });
        public ValidateAndRepairContext(
            ContentItem contentItem,
            IContentManager contentManager,
            IContentItemVersion contentItemVersion,
            ISubgraph nodeWithRelationships,
            ISyncNameProvider syncNameProvider,
            IGraphValidationHelper graphValidationHelper,
            IValidateAndRepairGraph validateAndRepairGraph,
            ILogger logger)
            : base(contentItem, syncNameProvider, contentManager, contentItemVersion, logger)
        {
            ContentItemVersion     = contentItemVersion;
            NodeWithRelationships  = nodeWithRelationships;
            GraphValidationHelper  = graphValidationHelper;
            ValidateAndRepairGraph = validateAndRepairGraph;

            ExpectedRelationshipCounts = new Dictionary <string, int>();
        }
        public async Task <IAllowSync> DeleteIfAllowed(
            ContentItem contentItem,
            IContentItemVersion contentItemVersion,
            SyncOperation syncOperation,
            IEnumerable <KeyValuePair <string, object> >?deleteIncomingRelationshipsProperties = null)
        {
            IAllowSync allowSync = await DeleteAllowed(
                contentItem,
                contentItemVersion,
                syncOperation,
                deleteIncomingRelationshipsProperties);

            if (allowSync.Result == AllowSyncResult.Allowed)
            {
                await Delete();
            }

            return(allowSync);
        }
        public ValidateAndRepairItemSyncContext(
            ContentItem contentItem,
            IContentManager contentManager,
            IContentItemVersion contentItemVersion,
            ISubgraph nodeWithRelationships,
            ISyncNameProvider syncNameProvider,
            IGraphValidationHelper graphValidationHelper,
            IValidateAndRepairGraph validateAndRepairGraph,
            ContentTypeDefinition contentTypeDefinition,
            object nodeId,
            IServiceProvider serviceProvider)

            : base(contentItem, contentManager, contentItemVersion, nodeWithRelationships,
                   syncNameProvider, graphValidationHelper, validateAndRepairGraph,
                   serviceProvider.GetRequiredService <ILogger <ValidateAndRepairItemSyncContext> >())
        {
            ContentTypeDefinition = contentTypeDefinition;
            NodeId = nodeId;
        }
        public async Task <Subgraph> GetVisualisationSubgraph(
            string contentItemId,
            string graphName,
            IContentItemVersion contentItemVersion)
        {
            var relationshipCommands = await BuildVisualisationCommands(contentItemId, contentItemVersion !);

            // get all results atomically
            var result = await _neoGraphCluster.Run(graphName, relationshipCommands.ToArray());

            var inAndOutResults =
                result.OfType <INodeAndOutRelationshipsAndTheirInRelationships?>();

            //todo: should really always return the source node (until then, the subgraph will pull it if the main results don't)
            Subgraph subgraph;

            if (inAndOutResults.Any())
            {
                // get all outgoing relationships from the query and add in any source nodes

                subgraph = new Subgraph(
                    inAndOutResults
                    .SelectMany(x => x !.OutgoingRelationships.Select(x => x.outgoingRelationship.DestinationNode))
                    .Union(inAndOutResults.GroupBy(x => x !.SourceNode).Select(z => z.FirstOrDefault() !.SourceNode)),
                    inAndOutResults !
                    .SelectMany(y => y !.OutgoingRelationships.Select(z => z.outgoingRelationship.Relationship))
                    .ToHashSet(),
                    inAndOutResults.FirstOrDefault()?.SourceNode);
            }
            else
            {
                subgraph = new Subgraph();
            }

            ISubgraph?inResults = result.OfType <ISubgraph>().FirstOrDefault();

            if (inResults != null)
            {
                subgraph.Add(inResults);
            }

            return(subgraph);
        }
Exemplo n.º 20
0
        //todo: nodeId should be object
        public async Task <string?> GetContentItemId(string nodeId, string graphReplicaSetName)
        {
            IContentItemVersion contentItemVersion = _contentItemVersionFactory.Get(graphReplicaSetName);

            // what we should be doing, but we'd have to pass along the node labels to get the content type
            //ISyncNameProvider syncNameProvider = _serviceProvider.GetSyncNameProvider(pickedContentType);

            //string graphSyncNodeId = syncNameProvider.IdPropertyValueFromNodeValue(nodeId, contentItemVersion);

            string graphSyncNodeId = _syncNameProvider.ConvertIdPropertyValue(nodeId,
                                                                              _superpositionContentItemVersion,
                                                                              contentItemVersion, _escoContentItemVersion);

            var contentItems = await _session
                               .Query <ContentItem, GraphSyncPartIndex>(x => x.NodeId == graphSyncNodeId)
                               .ListAsync();

            return(contentItems?.FirstOrDefault()?.ContentItemId);
        }
        private async Task <ContentItem[]> GetContentItemsFromIds(
            JArray contentItemIds,
            IContentManager contentManager,
            IContentItemVersion contentItemVersion)
        {
            // GetAsync should be returning ContentItem? as it can be null

            ContentItem?[] contentItems = await Task.WhenAll(contentItemIds
                                                             .Select(idJToken => idJToken.ToObject <string?>())
                                                             .Select(async id => await contentItemVersion.GetContentItem(contentManager, id !)));

#pragma warning disable S1905
            return(contentItems
                   .Where(ci => ci != null)
                   .Cast <ContentItem>()
                   .ToArray());

#pragma warning restore S1905
        }
 public GraphDeleteContext(ContentItem contentItem,
                           IDeleteNodeCommand deleteNodeCommand,
                           IDeleteGraphSyncer deleteGraphSyncer,
                           SyncOperation syncOperation,
                           ISyncNameProvider syncNameProvider,
                           IContentManager contentManager,
                           IContentItemVersion contentItemVersion,
                           IEnumerable <KeyValuePair <string, object> >?deleteIncomingRelationshipsProperties,
                           IGraphDeleteContext?parentGraphDeleteContext,
                           IServiceProvider serviceProvider)
     : base(
         contentItem,
         syncNameProvider,
         contentManager,
         contentItemVersion,
         parentGraphDeleteContext,
         serviceProvider.GetRequiredService <ILogger <GraphDeleteContext> >())
 {
     DeleteGraphSyncer = deleteGraphSyncer;
     DeleteNodeCommand = deleteNodeCommand;
     SyncOperation     = syncOperation;
     DeleteIncomingRelationshipsProperties = deleteIncomingRelationshipsProperties;
 }
Exemplo n.º 23
0
        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);
                }
            }
        }
Exemplo n.º 24
0
        private async Task <(bool validated, string failureReason)> ValidateDeletedContentItem(
            ContentItem contentItem,
            ContentTypeDefinition contentTypeDefinition,
            IContentItemVersion contentItemVersion)
        {
            _logger.LogDebug("Validating deleted {ContentType} {ContentItemId} '{ContentDisplayText}'.",
                             contentItem.ContentType, contentItem.ContentItemId, contentItem.DisplayText);

            _syncNameProvider.ContentType = contentItem.ContentType;

            object nodeId = _syncNameProvider.GetNodeIdPropertyValue(contentItem.Content.GraphSyncPart, contentItemVersion);

            //todo: one query to fetch outgoing and incoming
            List <INodeWithOutgoingRelationships?> results = await _currentGraph !.Run(
                new NodeWithOutgoingRelationshipsQuery(
                    await _syncNameProvider.NodeLabels(),
                    _syncNameProvider.IdPropertyName(),
                    nodeId));

            return(results.Any()
                ? (false, $"{contentTypeDefinition.DisplayName} {contentItem.ContentItemId} is still present in the graph.")
                : (true, ""));
        }
Exemplo n.º 25
0
        //todo: if issue with data, don't just hang visualiser
        private async Task <IEnumerable <IQuery <object?> > > BuildVisualisationCommands(
            string contentItemId,
            IContentItemVersion contentItemVersion)
        {
            ContentItem?contentItem = await contentItemVersion.GetContentItem(_contentManager, contentItemId);

            if (contentItem == null)
            {
                return(Enumerable.Empty <IQuery <INodeAndOutRelationshipsAndTheirInRelationships> >());
            }
            //todo: best to not use dynamic
            dynamic?graphSyncPartContent = contentItem.Content[nameof(GraphSyncPart)];

            _syncNameProvider.ContentType = contentItem.ContentType;

            string?sourceNodeId = _syncNameProvider.GetNodeIdPropertyValue(graphSyncPartContent, contentItemVersion);
            IEnumerable <string> sourceNodeLabels = await _syncNameProvider.NodeLabels();

            string sourceNodeIdPropertyName = _syncNameProvider.IdPropertyName();

            var rootContext = await _describeContentItemHelper.BuildRelationships(
                contentItem, sourceNodeIdPropertyName, sourceNodeId, sourceNodeLabels, _syncNameProvider,
                _contentManager, contentItemVersion, null, _serviceProvider);

            //todo: return relationships - can we do it without creating cypher outside of a query?
            //todo: current depth is always 0, so deep nodes like PersonalityQuestionSet returns masses of data
            //todo: depth cut-off is done after build relationships, so does more work than is necessary
            //var relationships = new List<ContentItemRelationship>();

            if (rootContext == null)
            {
                return(Enumerable.Empty <IQuery <object?> >());
            }

            //todo: should create relationships in here
            return(await _describeContentItemHelper.GetRelationshipCommands(rootContext));
        }
Exemplo n.º 26
0
        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 !));
            }
        }
Exemplo n.º 27
0
        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);
        }
Exemplo n.º 28
0
        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);
        }
Exemplo n.º 29
0
        public async Task <(bool validated, string failureReason)> ValidateContentItem(
            ContentItem contentItem,
            ContentTypeDefinition contentTypeDefinition,
            IContentItemVersion contentItemVersion)
        {
            _logger.LogDebug("Validating {ContentType} {ContentItemId} '{ContentDisplayText}'.",
                             contentItem.ContentType, contentItem.ContentItemId, contentItem.DisplayText);

            _syncNameProvider.ContentType = contentItem.ContentType;

            object nodeId = _syncNameProvider.GetNodeIdPropertyValue(contentItem.Content.GraphSyncPart, contentItemVersion);

            //todo: one query to fetch outgoing and incoming
            List <INodeWithOutgoingRelationships?> results = await _currentGraph !.Run(
                new NodeWithOutgoingRelationshipsQuery(
                    await _syncNameProvider.NodeLabels(),
                    _syncNameProvider.IdPropertyName(),
                    nodeId));

            //todo: does this belong in the query?
            INodeWithOutgoingRelationships?nodeWithOutgoingRelationships = results.FirstOrDefault();

            if (nodeWithOutgoingRelationships == null)
            {
                return(false, FailureContext("Node not found querying outgoing relationships.", contentItem));
            }

            List <INodeWithIncomingRelationships?> incomingResults = await _currentGraph !.Run(
                new NodeWithIncomingRelationshipsQuery(
                    await _syncNameProvider.NodeLabels(),
                    _syncNameProvider.IdPropertyName(),
                    nodeId));

            INodeWithIncomingRelationships?nodeWithIncomingRelationships = incomingResults.FirstOrDefault();

            if (nodeWithIncomingRelationships == null)
            {
                return(false, FailureContext("Node not found querying incoming relationships.", contentItem));
            }

            ValidateAndRepairItemSyncContext context = new ValidateAndRepairItemSyncContext(
                contentItem, _contentManager, contentItemVersion, nodeWithOutgoingRelationships,
                nodeWithIncomingRelationships, _syncNameProvider, _graphValidationHelper, this,
                contentTypeDefinition, nodeId, _serviceProvider);

            foreach (IContentItemGraphSyncer itemSyncer in _itemSyncers)
            {
                //todo: allow syncers to chain or not?
                if (itemSyncer.CanSync(contentItem))
                {
                    (bool validated, string failureReason) =
                        await itemSyncer.ValidateSyncComponent(context);

                    if (!validated)
                    {
                        return(validated, failureReason);
                    }
                }
            }

            // check there aren't any more relationships of each type than there should be
            // we need to do this after all parts have added their own expected relationship counts
            // to handle different parts or multiple named instances of a part creating relationships of the same type
            foreach ((string relationshipType, int relationshipsInDbCount) in context.ExpectedRelationshipCounts)
            {
                int relationshipsInGraphCount =
                    nodeWithOutgoingRelationships.OutgoingRelationships.Count(r =>
                                                                              r.Relationship.Type == relationshipType);

                if (relationshipsInDbCount != relationshipsInGraphCount)
                {
                    return(false, FailureContext(
                               $"Expecting {relationshipsInDbCount} relationships of type {relationshipType} in graph, but found {relationshipsInGraphCount}.",
                               contentItem));
                }
            }

            return(true, "");
        }