bool EvaluateForwardTriggers(IResourceTriggerFilterPolicyCache policyCache, IEntity entity, IEnumerable <long> changedFields, IEnumerable <long> changedRels, IEnumerable <long> changedRevRels, List <Action> postSaveActions) { using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateForwardTriggers")) { var typeId = entity.TypeIds.FirstOrDefault(); if (typeId != 0) { List <ResourceTriggerFilterDef> policies; if (policyCache.TypePolicyMap.TryGetValue(typeId, out policies)) { if (policies.Any()) { // we match on type var allChanges = changedFields.Union(changedRels).Union(changedRevRels); // // Check if our entity matches any of the fields and rels foreach (var policy in policies) { if (policy == null) { continue; // assert false } var watchedFields = policyCache.PolicyToFieldsMap[policy.Id]; if (!watchedFields.Any() || watchedFields.Intersect(allChanges).Any()) { var handler = policyCache.PolicyTypeHandlerMap[policy.TypeIds.First()]; if (handler != null) { var isNew = entity.IsTemporaryId; bool result = false; SecurityBypassContext.RunAsUser(() => { if (handler.OnBeforeSave(policy, entity, isNew, changedFields, changedRels, changedRevRels)) { result = true; } else { AddWrappedPostSaveAction(postSaveActions, () => handler.OnAfterSave(policy, entity, isNew, changedFields, changedRels, changedRevRels)); } }); if (result) { return(true); // We failed, so bug out } } } } } } } } return(false); }
/// <summary> /// Get the relationships that may have policies that are effected by the deletion of the entity. /// </summary> IEnumerable <Tuple <Relationship, Direction> > GetRelsWithPotentialPolicies(IResourceTriggerFilterPolicyCache policyCache, IEntity entity) { var forwardRelIds = EntityTypeHelper.GetAllRelationships(entity, Direction.Forward).Select(r => r.Id); var reverseRelIds = EntityTypeHelper.GetAllRelationships(entity, Direction.Reverse).Select(r => r.Id); // Prefill cache - note that we are requesting both the toType and fromType for each rel so we only need to do a single trip to the DB var allRelIds = forwardRelIds.Union(reverseRelIds).Select(r => new EntityRef(r)); BulkPreloader.Preload(new EntityRequest(allRelIds, "toType.id, fromType.id")); return(GetRelsWithPotentialPolicies(policyCache, forwardRelIds, Direction.Forward).Union(GetRelsWithPotentialPolicies(policyCache, reverseRelIds, Direction.Reverse))); }
/// <summary> /// Test the policy for the other end of the relationship /// </summary> /// <returns></returns> void TestOtherEndPolicyAndAct(IResourceTriggerFilterPolicyCache policyCache, long relId, Direction direction, IEntity entity, IEntity otherEntity, Action <ResourceTriggerFilterDef> action) { using (Profiler.Measure("ResourceTriggerFilterEventTarget.TestOtherEndPolicyAndAction")) { var typeId = otherEntity.TypeIds.First(); List <ResourceTriggerFilterDef> policies; if (policyCache.TypePolicyMap.TryGetValue(typeId, out policies)) { foreach (var policy in policies) { if (policy.UpdatedRelationshipsToTriggerOn.Any(rel => rel.Id == relId)) { action(policy); } } } } }
IEnumerable <Tuple <Relationship, Direction> > GetRelsWithPotentialPolicies(IResourceTriggerFilterPolicyCache policyCache, IEnumerable <long> relIds, Direction direction) { // Get the rels policies for the other end of the relationships var filteredRels = Entity.Get <Relationship>(relIds).Where(r => { var eType = direction == Direction.Forward ? r?.ToType : r?.FromType; if (eType == null) { return(false); // to handle some malformed relationships } var policies = policyCache.TypePolicyMap[eType.Id]; if (policies == null) { return(false); } return(policies.Any(p => p.UpdatedRelationshipsToTriggerOn.Contains(r, EntityIdEqualityComparer <Relationship> .Instance))); }); return(filteredRels.Select(r => new Tuple <Relationship, Direction>(r, direction))); }
bool EvaluateReverseTriggers(IResourceTriggerFilterPolicyCache policyCache, IEntity entity, IEnumerable <long> changedFields, IEnumerable <long> changedRels, IEnumerable <long> changedRevRels, List <Action> postSaveActions) { using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateReverseTriggers")) { // // Check if any of the related changed resources match any of our policies // TODO: Can we filter this earlier so I don't have to grab the type of every other end? var allChangedRels = changedRels.Select(rId => new { Id = rId, Direction = Direction.Forward }).Union(changedRevRels.Select(rId => new { Id = rId, Direction = Direction.Reverse })); var relChanges = allChangedRels.ToDictionary(rd => rd, rd => entity.GetRelationships(rd.Id, rd.Direction)); // TODO: Can we make sure that all the type information for the entities is grabbed in a single hit? (It might already be happening) foreach (var kvp in relChanges) { var relId = kvp.Key.Id; var direction = kvp.Key.Direction; var otherEntities = kvp.Value; using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateReverseTriggers adds")) { // // Test for any added entities that a policy may apply to // var added = GetAdded(otherEntities); foreach (var otherEntity in added) { if (otherEntity != null) { bool result = false; TestOtherEndPolicyAndAct(policyCache, relId, direction, entity, otherEntity, (policy) => { var handler = policyCache.PolicyTypeHandlerMap[policy.TypeIds.First()]; SecurityBypassContext.RunAsUser(() => { if (handler.OnBeforeReverseAdd(policy, relId, direction.Reverse(), otherEntity, entity, entity.IsTemporaryId)) { result = true; } else { AddWrappedPostSaveAction(postSaveActions, () => handler.OnAfterReverseAdd(policy, relId, direction.Reverse(), otherEntity, entity, entity.IsTemporaryId)); } }); }); if (result) { return(true); // It failed so bug out } } } } using (Profiler.Measure("ResourceTriggerFilterEventTarget.EvaluateReverseTriggers removes")) { // // Test for any removed policies that might apply to // var removed = GetRemoved(otherEntities); foreach (var otherEntity in removed) { if (otherEntity != null) { bool result = false; TestOtherEndPolicyAndAct(policyCache, relId, direction, entity, otherEntity, (policy) => { var handler = policyCache.PolicyTypeHandlerMap[policy.TypeIds.First()]; SecurityBypassContext.RunAsUser(() => { if (handler.OnBeforeReverseRemove(policy, relId, direction.Reverse(), otherEntity, entity)) { result = true; } else { AddWrappedPostSaveAction(postSaveActions, () => handler.OnAfterReverseRemove(policy, relId, direction.Reverse(), otherEntity, entity)); } }); }); if (result) { return(true); // It failed so bug out } } } } } } return(false); }