Skip to content

Commit d468a54

Browse files
committed
add rules evaluation engine
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent c384036 commit d468a54

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

policy/engine.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# DejaCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: AGPL-3.0-only
5+
# See https://github.com/aboutcode-org/dejacode for support or download.
6+
# See https://aboutcode.org for more information about AboutCode FOSS projects.
7+
#
8+
9+
from django.utils import timezone
10+
11+
from notification.models import fire_webhooks
12+
from policy.models import PolicyRule
13+
from policy.rules import RULE_REGISTRY
14+
from product_portfolio.models import ProductPolicyViolation
15+
16+
17+
def evaluate_rule(policy_rule, product):
18+
"""
19+
Evaluate a single PolicyRule against a product, create or update the
20+
ProductPolicyViolation record, and fire the notification event on new violations
21+
or resolutions.
22+
Returns the ProductPolicyViolation instance, or None if no violation exists.
23+
"""
24+
rule_handler = RULE_REGISTRY.get(policy_rule.rule_type)
25+
if not rule_handler:
26+
return None
27+
28+
violation_count = rule_handler.count_violations(policy_rule, product)
29+
30+
lookup = {"policy_rule": policy_rule, "product": product, "resolved": False}
31+
32+
if violation_count > 0:
33+
violation, created = ProductPolicyViolation.objects.get_or_create(
34+
**lookup,
35+
defaults={"dataspace": policy_rule.dataspace, "violation_count": violation_count},
36+
)
37+
if not created:
38+
violation.violation_count = violation_count
39+
violation.save()
40+
if created and policy_rule.event_name:
41+
fire_violation_event(policy_rule, product, violation_count)
42+
return violation
43+
else:
44+
resolved_count = ProductPolicyViolation.objects.filter(**lookup).update(
45+
resolved=True,
46+
resolved_date=timezone.now(),
47+
)
48+
if resolved_count and policy_rule.event_name:
49+
fire_resolution_event(policy_rule, product)
50+
return None
51+
52+
53+
def evaluate_rules(dataspace, product):
54+
"""
55+
Evaluate all active PolicyRules for the given product.
56+
Returns the list of active ProductPolicyViolation instances.
57+
"""
58+
violations = []
59+
for policy_rule in PolicyRule.objects.scope(dataspace).active():
60+
violation = evaluate_rule(policy_rule, product)
61+
if violation:
62+
violations.append(violation)
63+
64+
return violations
65+
66+
67+
def fire_violation_event(policy_rule, product, violation_count):
68+
fire_webhooks(
69+
policy_rule.event_name,
70+
instance=None,
71+
dataspace=policy_rule.dataspace,
72+
payload_override={
73+
"rule": policy_rule.name,
74+
"rule_type": policy_rule.rule_type,
75+
"violation_count": violation_count,
76+
"product": str(product),
77+
},
78+
)
79+
80+
81+
def fire_resolution_event(policy_rule, product):
82+
fire_webhooks(
83+
policy_rule.event_name,
84+
instance=None,
85+
dataspace=policy_rule.dataspace,
86+
payload_override={
87+
"rule": policy_rule.name,
88+
"status": "resolved",
89+
"product": str(product),
90+
},
91+
)

0 commit comments

Comments
 (0)