Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
73 changes: 73 additions & 0 deletions python/lib/sift_py/rule/_service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 5 additions & 5 deletions python/lib/sift_py/rule/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""

Expand All @@ -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__(
Expand All @@ -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 []
Expand All @@ -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:
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions python/lib/sift_py/rule/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
)
)

Expand Down Expand Up @@ -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]:
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions python/lib/sift_py/yaml/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand Down
Loading