//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));
        }
        private async Task AddSyncComponents(IGraphMergeContext context, JArray?contentItemIdsJArray = null)
        {
            ContentPickerFieldSettings contentPickerFieldSettings =
                context.ContentPartFieldDefinition !.GetSettings <ContentPickerFieldSettings>();

            //todo: use pickedContentSyncNameProvider in RelationshipTypeContentPicker?
            string relationshipType = await RelationshipTypeContentPicker(contentPickerFieldSettings, context.SyncNameProvider);

            //todo: support multiple pickable content types
            string pickedContentType = contentPickerFieldSettings.DisplayedContentTypes[0];

            ISyncNameProvider pickedContentSyncNameProvider = _serviceProvider.GetSyncNameProvider(pickedContentType);

            IEnumerable <string> destNodeLabels = await pickedContentSyncNameProvider.NodeLabels();

            if (contentItemIdsJArray?.HasValues != true)
            {
                context.ReplaceRelationshipsCommand.RemoveAnyRelationshipsTo(
                    relationshipType,
                    destNodeLabels);
                return;
            }

            ContentItem[] foundDestinationContentItems = await GetContentItemsFromIds(
                contentItemIdsJArray,
                context.ContentManager,
                context.ContentItemVersion);

            if (context.ContentItemVersion.GraphReplicaSetName == GraphReplicaSetNames.Preview &&
                foundDestinationContentItems.Count() != contentItemIdsJArray.Count)
            {
                throw new GraphSyncException(
                          $"Missing picked content items. Looked for {string.Join(",", contentItemIdsJArray.Values<string?>())}. Found {string.Join(",", foundDestinationContentItems.Select(i => i.ContentItemId))}. Current merge node command: {context.MergeNodeCommand}.");
            }

            // if we're syncing to the published graph, some items may be draft only,
            // so it's valid to have less found content items than are picked
            //todo: we could also check when publishing and take into account how many we expect not to find as they are draft only

            IEnumerable <object> foundDestinationNodeIds =
                foundDestinationContentItems.Select(ci => GetNodeId(ci !, pickedContentSyncNameProvider, context.ContentItemVersion));

            long ordinal = 0;

            foreach (var item in foundDestinationNodeIds)
            {
                context.ReplaceRelationshipsCommand.AddRelationshipsTo(
                    relationshipType,
                    ContentPickerRelationshipProperties.Concat(new[] { new KeyValuePair <string, object>("Ordinal", ordinal++) }),
                    destNodeLabels,
                    pickedContentSyncNameProvider.IdPropertyName(),
                    item);
            }
        }
 private async Task PopulateDeleteNodeCommand()
 {
     _deleteNodeCommand.NodeLabels      = new HashSet <string>(await _syncNameProvider.NodeLabels());
     _deleteNodeCommand.IdPropertyName  = _syncNameProvider.IdPropertyName();
     _deleteNodeCommand.IdPropertyValue =
         _syncNameProvider.GetNodeIdPropertyValue(_graphDeleteItemSyncContext !.ContentItem.Content.GraphSyncPart,
                                                  _graphDeleteItemSyncContext.ContentItemVersion);
     _deleteNodeCommand.DeleteNode = !_syncNameProvider.GraphSyncPartSettings.PreexistingNode;
     _deleteNodeCommand.DeleteIncomingRelationshipsProperties =
         _graphDeleteItemSyncContext.DeleteIncomingRelationshipsProperties;
 }
예제 #4
0
        private async Task PopulateMergeNodeCommand(JObject graphSyncPartContent)
        {
            MergeNodeCommand.NodeLabels.UnionWith(await _syncNameProvider.NodeLabels());
            MergeNodeCommand.IdPropertyName = _syncNameProvider.IdPropertyName();

            //todo: we could move population of the time properties to later when syncing, rather than at syncallowed time

            // add created and modified dates to all content items
            if (_graphMergeContext !.ContentItem.CreatedUtc.HasValue)
            {
                MergeNodeCommand.Properties.Add(await _syncNameProvider.PropertyName(CreatedDatePropertyName),
                                                _graphMergeContext.ContentItem.CreatedUtc.Value);
            }

            if (_graphMergeContext.ContentItem.ModifiedUtc.HasValue)
            {
                MergeNodeCommand.Properties.Add(await _syncNameProvider.PropertyName(ModifiedDatePropertyName),
                                                _graphMergeContext.ContentItem.ModifiedUtc.Value);
            }

            await _graphSyncPartGraphSyncer.AddSyncComponents(graphSyncPartContent, _graphMergeContext !);
        }
        private static string?GetNodeId(
            IReadOnlyDictionary <string, object> staxProperties,
            string?contentType,
            ISyncNameProvider syncNameProvider)
        {
            try
            {
                if (!staxProperties.Any() || contentType == null)
                {
                    return(null);
                }

                string?propertyId = syncNameProvider.IdPropertyName(contentType);

                return(staxProperties.FirstOrDefault(x => x.Key == propertyId).Value.ToString());
            }
            catch (Exception)
            {
                //todo: Exception caused by Content Types not being in OC e.g. ESCO__MemberData.
                //To be rectified in a follow up story
                return(null);
            }
        }
예제 #6
0
        public override async Task AddSyncComponents(JObject content, IGraphMergeContext context)
        {
            string?taxonomyContentItemId = (string?)content[TaxonomyContentItemId];

            if (taxonomyContentItemId == null)
            {
                throw new GraphSyncException($"{PartName} is missing {TaxonomyContentItemId}.");
            }

            //todo: check for null
            ContentItem?contentItem = await context.ContentItemVersion.GetContentItem(context.ContentManager, taxonomyContentItemId);

            ISyncNameProvider termSyncNameProvider = _serviceProvider.GetSyncNameProvider(contentItem !.ContentType);

            //todo: override/extension that takes a contentitem
            context.ReplaceRelationshipsCommand.AddRelationshipsTo(
                //todo: go through syncNameProvider
                $"has{contentItem.ContentType}",
                null,
                await termSyncNameProvider.NodeLabels(),
                termSyncNameProvider.IdPropertyName(),
                termSyncNameProvider.GetNodeIdPropertyValue(contentItem.Content.GraphSyncPart, context.ContentItemVersion));
        }
예제 #7
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));
        }
예제 #8
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, "");
        }