diff --git a/python/examples/ingestion_with_python_config/telemetry_config.py b/python/examples/ingestion_with_python_config/telemetry_config.py index 62c16b816..fa985609c 100644 --- a/python/examples/ingestion_with_python_config/telemetry_config.py +++ b/python/examples/ingestion_with_python_config/telemetry_config.py @@ -81,7 +81,7 @@ def nostromos_lv_426() -> TelemetryConfig: FlowConfig(name="logs", channels=[log_channel]), ], rules=[ - # Add `is_live=True` if you want these rules to run on live data. + # Add `is_live_evaluation_enabled=True` if you want these rules to run on live data. RuleConfig( name="overheating", description="Checks for vehicle overheating", diff --git a/python/lib/sift_py/rule/_service_test.py b/python/lib/sift_py/rule/_service_test.py index f33ee58ae..e97130037 100644 --- a/python/lib/sift_py/rule/_service_test.py +++ b/python/lib/sift_py/rule/_service_test.py @@ -292,3 +292,76 @@ def test_rule_service_load_rules_from_yaml_with_contextual_channels(rule_service assert len(rule_config.contextual_channels) == 2 assert rule_config.contextual_channels[0] == "humidity" assert rule_config.contextual_channels[1] == "pressure" + + +def test_rule_service_create_rule_with_is_live_evaluation_enabled(rule_service): + """Test creating a rule with is_live_evaluation_enabled""" + rule = RuleConfig( + name="rule", + rule_client_key="rule-client-key", + channel_references=[ + { + "channel_reference": "$1", + "channel_config": ChannelConfig( + name="temperature", + data_type=ChannelDataType.DOUBLE, + ), + } + ], + is_live_evaluation_enabled=True, + expression="$1 > 10", + action=RuleActionCreateDataReviewAnnotation(), + ) + + with mock.patch.object(RuleService, "_create_rule") as mock_create_rule: + rule_service.create_or_update_rule(rule) + mock_create_rule.assert_called_once_with(rule) + created_rule = mock_create_rule.call_args[0][0] + assert created_rule.is_live_evaluation_enabled + + +def test_rule_service_load_rules_from_yaml_with_is_live_evaluation_enabled(rule_service): + """Test loading rules from YAML with is_live_evaluation_enabled""" + rule_yaml = { + "name": "rule", + "rule_client_key": "rule-client-key", + "channel_references": [{"$1": "temperature"}], + "contextual_channels": ["humidity", "pressure"], + "description": "description", + "expression": "$1 > 0", + "type": "review", + "asset_names": ["asset"], + "is_live_evaluation_enabled": True, + } + + with mock.patch.object(RuleService, "create_or_update_rule"): + with mock.patch( + "sift_py.rule.service.load_rule_modules", + return_value=[rule_yaml], + ): + rule_configs = rule_service.load_rules_from_yaml(["path/to/rules.yml"]) + assert len(rule_configs) == 1 + + rule_config = rule_configs[0] + assert rule_config.is_live_evaluation_enabled + + +def test_rule_service_update_rule_with_is_live_evaluation_enabled(rule_service): + """Test updating a rule with is_live_evaluation_enabled""" + rule = RuleConfig( + name="rule", + rule_client_key="rule-client-key", + is_live_evaluation_enabled=True, + channel_references=[], + ) + + with mock.patch.object(RuleService, "_update_rule") as mock_update_rule: + with mock.patch.object( + RuleService, "_get_rule_from_client_key", return_value=Rule(name=rule.name) + ) as mock_get_rule_from_client_key: + rule_service.create_or_update_rule(rule) + mock_get_rule_from_client_key.assert_called_once_with(rule.rule_client_key) + mock_update_rule.assert_called_once() + + updated_rule = mock_update_rule.call_args[0][0] + assert updated_rule.is_live_evaluation_enabled diff --git a/python/lib/sift_py/rule/config.py b/python/lib/sift_py/rule/config.py index 5729c4d0a..d2cad7d60 100644 --- a/python/lib/sift_py/rule/config.py +++ b/python/lib/sift_py/rule/config.py @@ -27,7 +27,7 @@ class RuleConfig(AsJson): - `tag_names`: A list of asset tags that this rule should be applied to. ONLY VALID if defining rules outside of a telemetry config. - `contextual_channels`: A list of channel names that provide context but aren't directly used in the expression. - `is_external`: If this is an external rule. - - `is_live`: If set to True then this rule will be evaluated on live data, otherwise live rule evaluation will be disabled. + - `is_live_evaluation_enabled`: If set to True then this rule will be evaluated on live data, otherwise live rule evaluation will be disabled. This rule can still be used, however, in report generation. """ @@ -41,7 +41,7 @@ class RuleConfig(AsJson): tag_names: List[str] contextual_channels: List[str] is_external: bool - is_live: bool + is_live_evaluation_enabled: bool _rule_id: Optional[str] # Allow passing of rule_id when existing config retrieved from API def __init__( @@ -59,7 +59,7 @@ def __init__( sub_expressions: Dict[str, Any] = {}, contextual_channels: Optional[List[str]] = None, is_external: bool = False, - is_live: bool = False, + is_live_evaluation_enabled: bool = False, ): self.channel_references = _channel_references_from_dicts(channel_references) self.contextual_channels = contextual_channels or [] @@ -72,7 +72,7 @@ def __init__( self.description = description self.expression = self.__class__.interpolate_sub_expressions(expression, sub_expressions) self.is_external = is_external - self.is_live = is_live + self.is_live_evaluation_enabled = is_live_evaluation_enabled self._rule_id = None def as_json(self) -> Any: @@ -90,7 +90,7 @@ def as_json(self) -> Any: "description": self.description, "expression": self.expression, "is_external": self.is_external, - "is_live": self.is_live, + "is_live_evaluation_enabled": self.is_live_evaluation_enabled, } hash_map["expression_channel_references"] = self.channel_references diff --git a/python/lib/sift_py/rule/service.py b/python/lib/sift_py/rule/service.py index 56d4f137e..76dd965b2 100644 --- a/python/lib/sift_py/rule/service.py +++ b/python/lib/sift_py/rule/service.py @@ -252,7 +252,7 @@ def _parse_rules_from_yaml( tag_names=rule_yaml.get("tag_names", []), sub_expressions=subexpr, is_external=rule_yaml.get("is_external", False), - is_live=rule_yaml.get("is_live", False), + is_live_evaluation_enabled=rule_yaml.get("is_live_evaluation_enabled", False), ) ) @@ -549,7 +549,7 @@ def _update_req_from_rule_config( ), contextual_channels=ContextualChannels(channels=contextual_channel_names), is_external=config.is_external, - is_live_evaluation_enabled=config.is_live, + is_live_evaluation_enabled=config.is_live_evaluation_enabled, ) def get_rule(self, rule: str) -> Optional[RuleConfig]: @@ -617,6 +617,7 @@ def get_rule(self, rule: str) -> Optional[RuleConfig]: tag_names=asset_tag_names, action=action, expression=expression, + is_live_evaluation_enabled=rule_pb.is_live_evaluation_enabled, ) # rule_id currently required for an existing rule diff --git a/python/lib/sift_py/yaml/rule.py b/python/lib/sift_py/yaml/rule.py index e2220eb9b..0a3f1e34e 100644 --- a/python/lib/sift_py/yaml/rule.py +++ b/python/lib/sift_py/yaml/rule.py @@ -271,7 +271,7 @@ class RuleYamlSpec(TypedDict): `asset_names`: A list of asset names that this rule should be applied to. ONLY VALID if defining rules outside of a telemetry config. `tag_names`: A list of tag names that this rule should be applied to. ONLY VALID if defining rules outside of a telemetry config. `is_external`: If this is an external rule. - `is_live`: If set to True then this rule will be evaluated on live data, otherwise live rule evaluation will be disabled. + `is_live_evaluation_enabled`: If set to True then this rule will be evaluated on live data, otherwise live rule evaluation will be disabled. This rule can still be used, however, in report generation. Channel references: @@ -330,7 +330,7 @@ class RuleYamlSpec(TypedDict): asset_names: NotRequired[List[str]] tag_names: NotRequired[List[str]] is_external: NotRequired[bool] - is_live: NotRequired[bool] + is_live_evaluation_enabled: NotRequired[bool] class NamedExpressionYamlSpec(TypedDict):