public void Include(Bundle bundle, IEnumerable <string> includes) { if (includes == null) { return; } IEnumerable <Uri> keys = bundle.GetReferences(includes).Distinct(); IEnumerable <BundleEntry> entries = store.Get(keys, null); bundle.AddRange(entries); }
/// <summary> /// TODO: Remove the MDM Ignore Relationships /// </summary> public override IdentifiedData UnIgnore(Guid masterKey, IEnumerable <Guid> ignoredKeys) { try { this.m_pepService.Demand(MdmPermissionPolicyIdentifiers.WriteMdmMaster); Bundle transaction = new Bundle(); transaction.AddRange(ignoredKeys.SelectMany(o => this.m_dataManager.MdmTxUnIgnoreCandidateMatch(masterKey, o, transaction.Item))); // Commit the transaction return(this.m_batchPersistence.Insert(transaction, TransactionMode.Commit, AuthenticationContext.Current.Principal)); } catch (Exception ex) { throw new MdmException("Error performing ignore", ex); } }
/// <summary> /// Perform the operation /// </summary> public override object Invoke(Type scopingType, object scopingKey, ParameterCollection parameters) { var dataManager = MdmDataManagerFactory.GetDataManager(scopingType); if (dataManager == null) { throw new NotSupportedException($"MDM is not configured for {scopingType}"); } if (scopingKey == null) { parameters.TryGet <bool>("clear", out bool clear); this.m_jobManager.StartJob(typeof(MdmMatchJob <>).MakeGenericType(scopingType), new object[] { clear }); return(null); } else if (scopingKey is Guid scopingObjectKey) { // Load the current master from @scopingKey if (!dataManager.IsMaster(scopingObjectKey)) { throw new KeyNotFoundException($"{scopingObjectKey} is not an MDM Master"); } // Now - we want to prepare a transaction Bundle retVal = new Bundle(); if (parameters.TryGet <bool>("clear", out bool clear) && clear) { foreach (var itm in dataManager.GetCandidateLocals(scopingObjectKey).Where(o => o.ClassificationKey == MdmConstants.AutomagicClassification)) { if (itm is EntityRelationship er) { er.BatchOperation = Core.Model.DataTypes.BatchOperationType.Delete; retVal.Add(er); } else if (itm is ActRelationship ar) { ar.BatchOperation = Core.Model.DataTypes.BatchOperationType.Delete; retVal.Add(ar); } } } retVal.AddRange(dataManager.MdmTxDetectCandidates(dataManager.MdmGet(scopingObjectKey).Synthesize(AuthenticationContext.Current.Principal) as Entity, retVal.Item)); // Now we want to save? try { retVal = this.m_batchService.Insert(retVal, TransactionMode.Commit, AuthenticationContext.Current.Principal); } catch (Exception e) { this.m_tracer.TraceError("Error persisting re-match: {0}", e.Message); throw new MdmException("Error persisting re-match operation", e); } return(retVal); } else { throw new InvalidOperationException("Cannot determine the operation"); } }
/// <summary> /// Merge the specified records together /// </summary> public override RecordMergeResult Merge(Guid survivorKey, IEnumerable <Guid> linkedDuplicates) { try { // Merging if (this.FireMerging(survivorKey, linkedDuplicates)) { this.m_tracer.TraceWarning("Pre-Event Handler for merge indicated cancel on {0}", survivorKey); return(new RecordMergeResult(RecordMergeStatus.Cancelled, null, null)); } // We want to get the target RecordMergeStatus recordMergeStatus = RecordMergeStatus.Success; var survivor = this.m_dataManager.GetRaw(survivorKey) as Entity; bool isSurvivorMaster = this.m_dataManager.IsMaster(survivorKey); if (isSurvivorMaster) { try { // Trying to write to master - do they have permission? this.m_pepService.Demand(MdmPermissionPolicyIdentifiers.WriteMdmMaster); } catch (PolicyViolationException e) when(e.PolicyId == MdmPermissionPolicyIdentifiers.WriteMdmMaster) { survivor = this.m_dataManager.GetLocalFor(survivorKey, AuthenticationContext.Current.Principal); if (survivor == null) { throw new DetectedIssueException(Core.BusinessRules.DetectedIssuePriorityType.Error, MdmConstants.INVALID_MERGE_ISSUE, $"Principal has no authority to merge into {survivorKey}", DetectedIssueKeys.SecurityIssue, e); } recordMergeStatus = RecordMergeStatus.Alternate; isSurvivorMaster = false; } } Bundle transactionBundle = new Bundle(); // For each linked duplicate var replaced = linkedDuplicates.Select(itm => { var victim = this.m_dataManager.GetRaw(itm) as Entity; var isVictimMaster = this.m_dataManager.IsMaster(itm); if (isVictimMaster) { try { // Trying to write to master - do they have permission? this.m_pepService.Demand(MdmPermissionPolicyIdentifiers.MergeMdmMaster); } catch (PolicyViolationException e) when(e.PolicyId == MdmPermissionPolicyIdentifiers.MergeMdmMaster) { victim = this.m_dataManager.GetLocalFor(itm, AuthenticationContext.Current.Principal); if (victim == null) { throw new DetectedIssueException(Core.BusinessRules.DetectedIssuePriorityType.Error, MdmConstants.INVALID_MERGE_ISSUE, $"Principal has no authority to merge {itm}", DetectedIssueKeys.SecurityIssue, e); } isVictimMaster = false; recordMergeStatus = RecordMergeStatus.Alternate; } } // Sanity check if (victim.Key == survivor.Key) { throw new DetectedIssueException(DetectedIssuePriorityType.Error, MdmConstants.INVALID_MERGE_ISSUE, "Records cannot be merged into themselves", DetectedIssueKeys.FormalConstraintIssue, null); } // MASTER>MASTER if (isSurvivorMaster && isVictimMaster) // MASTER>MASTER { this.m_tracer.TraceInfo("MASTER({0})>MASTER({0}) MERGE", victim.Key, survivor.Key); transactionBundle.AddRange(this.m_dataManager.MdmTxMergeMasters(survivorKey, itm, transactionBundle.Item)); } else if (isSurvivorMaster && !isVictimMaster) // LOCAL>MASTER = LINK { // Ensure that the local manipulation is allowed if (!this.m_dataManager.IsOwner((TEntity)victim, AuthenticationContext.Current.Principal)) { this.m_pepService.Demand(MdmPermissionPolicyIdentifiers.UnrestrictedMdm); // MUST BE ABLE TO MANIPULATE OTHER LOCALS } this.m_tracer.TraceInfo("LOCAL({0})>MASTER({0}) MERGE", victim.Key, survivor.Key); transactionBundle.AddRange(this.m_dataManager.MdmTxMasterLink(survivorKey, victim.Key.Value, transactionBundle.Item, true)); } else if (!isSurvivorMaster && !isVictimMaster) // LOCAL>LOCAL = MERGE { // First, target replaces victim transactionBundle.Add(new EntityRelationship(EntityRelationshipTypeKeys.Replaces, survivor.Key, victim.Key, null) { RelationshipRoleKey = EntityRelationshipTypeKeys.Duplicate }); this.m_tracer.TraceInfo("LOCAL({0})>LOCAL({0}) MERGE", victim.Key, survivor.Key); // Obsolete the victim - the victim is obsolete since it was accurate and is no longer the accurate victim.StatusConceptKey = StatusKeys.Inactive; transactionBundle.Add(victim); // Obsolete the old identifiers over transactionBundle.AddRange( victim.LoadCollection(o => o.Identifiers).Where(i => !survivor.LoadCollection(o => o.Identifiers).Any(e => e.SemanticEquals(i))).Select(o => { o.BatchOperation = BatchOperationType.Delete; return(o); }) ); // Copy identifiers over transactionBundle.AddRange( victim.LoadCollection(o => o.Identifiers).Where(i => !survivor.LoadCollection(o => o.Identifiers).Any(e => e.SemanticEquals(i))).Select(o => new EntityIdentifier(o.Authority, o.Value) { SourceEntityKey = survivor.Key, IssueDate = o.IssueDate, ExpiryDate = o.ExpiryDate }) ); // Remove links from victim foreach (var rel in this.m_dataManager.GetAllMdmAssociations(victim.Key.Value).OfType <EntityRelationship>()) { rel.BatchOperation = BatchOperationType.Delete; transactionBundle.Add(rel); } // Recheck the master to ensure that it isn't dangling out here var otherLocals = this.m_relationshipPersistence.Count(o => o.RelationshipTypeKey == MdmConstants.MasterRecordRelationship && o.TargetEntityKey == itm && o.SourceEntityKey != victim.Key, AuthenticationContext.SystemPrincipal); if (otherLocals == 0) { transactionBundle.Add(new Entity() { BatchOperation = BatchOperationType.Delete, Key = itm }); } } else { throw new MdmException($"Cannot determine viable merge/link strategy between {survivor.Key} and {victim.Key}", null); } return(victim.Key.Value); }).ToArray(); this.m_batchPersistence.Insert(transactionBundle, TransactionMode.Commit, AuthenticationContext.Current.Principal); this.FireMerged(survivor.Key.Value, replaced); return(new RecordMergeResult(recordMergeStatus, new Guid[] { survivor.Key.Value }, replaced)); } catch (Exception ex) { this.m_tracer.TraceError("Error performing MDM merging operation on {0}: {1}", survivorKey, ex); throw new MdmException($"Error performing MDM merge on {survivorKey}", ex); } }