//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; }
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); } }
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)); }
//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)); }
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, ""); }