Пример #1
0
        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);
        }
Пример #2
0
        /// <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);
            }
        }
Пример #3
0
        /// <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");
            }
        }
Пример #4
0
        /// <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);
            }
        }