sift-stack-py 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/__init__.py +1 -0
- google/api/__init__.py +0 -0
- google/api/annotations_pb2.py +27 -0
- google/api/annotations_pb2.pyi +29 -0
- google/api/annotations_pb2_grpc.py +4 -0
- google/api/annotations_pb2_grpc.pyi +30 -0
- google/api/field_behavior_pb2.py +30 -0
- google/api/field_behavior_pb2.pyi +175 -0
- google/api/field_behavior_pb2_grpc.py +4 -0
- google/api/field_behavior_pb2_grpc.pyi +30 -0
- google/api/http_pb2.py +31 -0
- google/api/http_pb2.pyi +433 -0
- google/api/http_pb2_grpc.py +4 -0
- google/api/http_pb2_grpc.pyi +30 -0
- protoc_gen_openapiv2/__init__.py +0 -0
- protoc_gen_openapiv2/options/__init__.py +0 -0
- protoc_gen_openapiv2/options/annotations_pb2.py +27 -0
- protoc_gen_openapiv2/options/annotations_pb2.pyi +48 -0
- protoc_gen_openapiv2/options/annotations_pb2_grpc.py +4 -0
- protoc_gen_openapiv2/options/annotations_pb2_grpc.pyi +17 -0
- protoc_gen_openapiv2/options/openapiv2_pb2.py +132 -0
- protoc_gen_openapiv2/options/openapiv2_pb2.pyi +1533 -0
- protoc_gen_openapiv2/options/openapiv2_pb2_grpc.py +4 -0
- protoc_gen_openapiv2/options/openapiv2_pb2_grpc.pyi +17 -0
- sift/__init__.py +0 -0
- sift/annotation_logs/__init__.py +0 -0
- sift/annotation_logs/v1/__init__.py +0 -0
- sift/annotation_logs/v1/annotation_logs_pb2.py +115 -0
- sift/annotation_logs/v1/annotation_logs_pb2.pyi +370 -0
- sift/annotation_logs/v1/annotation_logs_pb2_grpc.py +135 -0
- sift/annotation_logs/v1/annotation_logs_pb2_grpc.pyi +84 -0
- sift/annotations/__init__.py +0 -0
- sift/annotations/v1/__init__.py +0 -0
- sift/annotations/v1/annotations_pb2.py +180 -0
- sift/annotations/v1/annotations_pb2.pyi +539 -0
- sift/annotations/v1/annotations_pb2_grpc.py +237 -0
- sift/annotations/v1/annotations_pb2_grpc.pyi +144 -0
- sift/assets/__init__.py +0 -0
- sift/assets/v1/__init__.py +0 -0
- sift/assets/v1/assets_pb2.py +90 -0
- sift/assets/v1/assets_pb2.pyi +235 -0
- sift/assets/v1/assets_pb2_grpc.py +168 -0
- sift/assets/v1/assets_pb2_grpc.pyi +101 -0
- sift/calculated_channels/__init__.py +0 -0
- sift/calculated_channels/v1/__init__.py +0 -0
- sift/calculated_channels/v1/calculated_channels_pb2.py +99 -0
- sift/calculated_channels/v1/calculated_channels_pb2.pyi +280 -0
- sift/calculated_channels/v1/calculated_channels_pb2_grpc.py +101 -0
- sift/calculated_channels/v1/calculated_channels_pb2_grpc.pyi +64 -0
- sift/campaigns/__init__.py +0 -0
- sift/campaigns/v1/__init__.py +0 -0
- sift/campaigns/v1/campaigns_pb2.py +144 -0
- sift/campaigns/v1/campaigns_pb2.pyi +383 -0
- sift/campaigns/v1/campaigns_pb2_grpc.py +169 -0
- sift/campaigns/v1/campaigns_pb2_grpc.pyi +104 -0
- sift/channel_schemas/__init__.py +0 -0
- sift/channel_schemas/v1/__init__.py +0 -0
- sift/channel_schemas/v1/channel_schemas_pb2.py +69 -0
- sift/channel_schemas/v1/channel_schemas_pb2.pyi +117 -0
- sift/channel_schemas/v1/channel_schemas_pb2_grpc.py +101 -0
- sift/channel_schemas/v1/channel_schemas_pb2_grpc.pyi +64 -0
- sift/channels/__init__.py +0 -0
- sift/channels/v2/__init__.py +0 -0
- sift/channels/v2/channels_pb2.py +88 -0
- sift/channels/v2/channels_pb2.pyi +183 -0
- sift/channels/v2/channels_pb2_grpc.py +101 -0
- sift/channels/v2/channels_pb2_grpc.pyi +64 -0
- sift/common/__init__.py +0 -0
- sift/common/type/__init__.py +0 -0
- sift/common/type/v1/__init__.py +0 -0
- sift/common/type/v1/channel_bit_field_element_pb2.py +34 -0
- sift/common/type/v1/channel_bit_field_element_pb2.pyi +33 -0
- sift/common/type/v1/channel_bit_field_element_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_bit_field_element_pb2_grpc.pyi +17 -0
- sift/common/type/v1/channel_data_type_pb2.py +29 -0
- sift/common/type/v1/channel_data_type_pb2.pyi +50 -0
- sift/common/type/v1/channel_data_type_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_data_type_pb2_grpc.pyi +17 -0
- sift/common/type/v1/channel_enum_type_pb2.py +32 -0
- sift/common/type/v1/channel_enum_type_pb2.pyi +29 -0
- sift/common/type/v1/channel_enum_type_pb2_grpc.py +4 -0
- sift/common/type/v1/channel_enum_type_pb2_grpc.pyi +17 -0
- sift/common/type/v1/organization_pb2.py +27 -0
- sift/common/type/v1/organization_pb2.pyi +29 -0
- sift/common/type/v1/organization_pb2_grpc.py +4 -0
- sift/common/type/v1/organization_pb2_grpc.pyi +17 -0
- sift/common/type/v1/resource_identifier_pb2.py +46 -0
- sift/common/type/v1/resource_identifier_pb2.pyi +145 -0
- sift/common/type/v1/resource_identifier_pb2_grpc.py +4 -0
- sift/common/type/v1/resource_identifier_pb2_grpc.pyi +17 -0
- sift/common/type/v1/user_pb2.py +33 -0
- sift/common/type/v1/user_pb2.pyi +36 -0
- sift/common/type/v1/user_pb2_grpc.py +4 -0
- sift/common/type/v1/user_pb2_grpc.pyi +17 -0
- sift/data/__init__.py +0 -0
- sift/data/v1/__init__.py +0 -0
- sift/data/v1/data_pb2.py +212 -0
- sift/data/v1/data_pb2.pyi +745 -0
- sift/data/v1/data_pb2_grpc.py +67 -0
- sift/data/v1/data_pb2_grpc.pyi +44 -0
- sift/ingest/__init__.py +0 -0
- sift/ingest/v1/__init__.py +0 -0
- sift/ingest/v1/ingest_pb2.py +35 -0
- sift/ingest/v1/ingest_pb2.pyi +118 -0
- sift/ingest/v1/ingest_pb2_grpc.py +66 -0
- sift/ingest/v1/ingest_pb2_grpc.pyi +41 -0
- sift/ingestion_configs/__init__.py +0 -0
- sift/ingestion_configs/v1/__init__.py +0 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2.py +115 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2.pyi +332 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.py +203 -0
- sift/ingestion_configs/v1/ingestion_configs_pb2_grpc.pyi +124 -0
- sift/notifications/__init__.py +0 -0
- sift/notifications/v1/__init__.py +0 -0
- sift/notifications/v1/notifications_pb2.py +64 -0
- sift/notifications/v1/notifications_pb2.pyi +225 -0
- sift/notifications/v1/notifications_pb2_grpc.py +101 -0
- sift/notifications/v1/notifications_pb2_grpc.pyi +64 -0
- sift/ping/__init__.py +0 -0
- sift/ping/v1/__init__.py +0 -0
- sift/ping/v1/ping_pb2.py +38 -0
- sift/ping/v1/ping_pb2.pyi +36 -0
- sift/ping/v1/ping_pb2_grpc.py +66 -0
- sift/ping/v1/ping_pb2_grpc.pyi +41 -0
- sift/remote_files/__init__.py +0 -0
- sift/remote_files/v1/__init__.py +0 -0
- sift/remote_files/v1/remote_files_pb2.py +174 -0
- sift/remote_files/v1/remote_files_pb2.pyi +472 -0
- sift/remote_files/v1/remote_files_pb2_grpc.py +271 -0
- sift/remote_files/v1/remote_files_pb2_grpc.pyi +164 -0
- sift/report_templates/__init__.py +0 -0
- sift/report_templates/v1/__init__.py +0 -0
- sift/report_templates/v1/report_templates_pb2.py +146 -0
- sift/report_templates/v1/report_templates_pb2.pyi +381 -0
- sift/report_templates/v1/report_templates_pb2_grpc.py +169 -0
- sift/report_templates/v1/report_templates_pb2_grpc.pyi +104 -0
- sift/reports/__init__.py +0 -0
- sift/reports/v1/__init__.py +0 -0
- sift/reports/v1/reports_pb2.py +193 -0
- sift/reports/v1/reports_pb2.pyi +562 -0
- sift/reports/v1/reports_pb2_grpc.py +205 -0
- sift/reports/v1/reports_pb2_grpc.pyi +136 -0
- sift/rule_evaluation/__init__.py +0 -0
- sift/rule_evaluation/v1/__init__.py +0 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2.py +89 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2.pyi +263 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.py +101 -0
- sift/rule_evaluation/v1/rule_evaluation_pb2_grpc.pyi +64 -0
- sift/rules/__init__.py +0 -0
- sift/rules/v1/__init__.py +0 -0
- sift/rules/v1/rules_pb2.py +420 -0
- sift/rules/v1/rules_pb2.pyi +1355 -0
- sift/rules/v1/rules_pb2_grpc.py +577 -0
- sift/rules/v1/rules_pb2_grpc.pyi +351 -0
- sift/runs/__init__.py +0 -0
- sift/runs/v2/__init__.py +0 -0
- sift/runs/v2/runs_pb2.py +150 -0
- sift/runs/v2/runs_pb2.pyi +413 -0
- sift/runs/v2/runs_pb2_grpc.py +271 -0
- sift/runs/v2/runs_pb2_grpc.pyi +164 -0
- sift/saved_searches/__init__.py +0 -0
- sift/saved_searches/v1/__init__.py +0 -0
- sift/saved_searches/v1/saved_searches_pb2.py +144 -0
- sift/saved_searches/v1/saved_searches_pb2.pyi +385 -0
- sift/saved_searches/v1/saved_searches_pb2_grpc.py +237 -0
- sift/saved_searches/v1/saved_searches_pb2_grpc.pyi +144 -0
- sift/tags/__init__.py +0 -0
- sift/tags/v1/__init__.py +0 -0
- sift/tags/v1/tags_pb2.py +49 -0
- sift/tags/v1/tags_pb2.pyi +71 -0
- sift/tags/v1/tags_pb2_grpc.py +4 -0
- sift/tags/v1/tags_pb2_grpc.pyi +17 -0
- sift/users/__init__.py +0 -0
- sift/users/v2/__init__.py +0 -0
- sift/users/v2/users_pb2.py +61 -0
- sift/users/v2/users_pb2.pyi +142 -0
- sift/users/v2/users_pb2_grpc.py +135 -0
- sift/users/v2/users_pb2_grpc.pyi +84 -0
- sift/views/__init__.py +0 -0
- sift/views/v1/__init__.py +0 -0
- sift/views/v1/views_pb2.py +130 -0
- sift/views/v1/views_pb2.pyi +466 -0
- sift/views/v1/views_pb2_grpc.py +305 -0
- sift/views/v1/views_pb2_grpc.pyi +184 -0
- sift_grafana/py.typed +0 -0
- sift_grafana/sift_query_model.py +64 -0
- sift_py/__init__.py +923 -0
- sift_py/_internal/__init__.py +5 -0
- sift_py/_internal/cel.py +18 -0
- sift_py/_internal/channel.py +42 -0
- sift_py/_internal/convert/__init__.py +3 -0
- sift_py/_internal/convert/json.py +24 -0
- sift_py/_internal/convert/protobuf.py +34 -0
- sift_py/_internal/convert/timestamp.py +9 -0
- sift_py/_internal/test_util/__init__.py +0 -0
- sift_py/_internal/test_util/channel.py +136 -0
- sift_py/_internal/test_util/fn.py +14 -0
- sift_py/_internal/test_util/server_interceptor.py +62 -0
- sift_py/_internal/time.py +48 -0
- sift_py/_internal/user.py +39 -0
- sift_py/data/__init__.py +171 -0
- sift_py/data/_channel.py +38 -0
- sift_py/data/_deserialize.py +208 -0
- sift_py/data/_deserialize_test.py +134 -0
- sift_py/data/_service_test.py +276 -0
- sift_py/data/_validate.py +10 -0
- sift_py/data/error.py +5 -0
- sift_py/data/query.py +299 -0
- sift_py/data/service.py +497 -0
- sift_py/data_import/__init__.py +130 -0
- sift_py/data_import/_config.py +167 -0
- sift_py/data_import/_config_test.py +166 -0
- sift_py/data_import/_csv_test.py +395 -0
- sift_py/data_import/_status_test.py +176 -0
- sift_py/data_import/_tdms_test.py +238 -0
- sift_py/data_import/ch10.py +157 -0
- sift_py/data_import/config.py +19 -0
- sift_py/data_import/csv.py +259 -0
- sift_py/data_import/status.py +113 -0
- sift_py/data_import/tdms.py +206 -0
- sift_py/data_import/tempfile.py +30 -0
- sift_py/data_import/time_format.py +39 -0
- sift_py/error.py +11 -0
- sift_py/file_attachment/__init__.py +88 -0
- sift_py/file_attachment/_internal/__init__.py +0 -0
- sift_py/file_attachment/_internal/download.py +13 -0
- sift_py/file_attachment/_internal/upload.py +100 -0
- sift_py/file_attachment/_service_test.py +161 -0
- sift_py/file_attachment/entity.py +30 -0
- sift_py/file_attachment/metadata.py +107 -0
- sift_py/file_attachment/service.py +142 -0
- sift_py/grpc/__init__.py +15 -0
- sift_py/grpc/_async_interceptors/__init__.py +0 -0
- sift_py/grpc/_async_interceptors/base.py +72 -0
- sift_py/grpc/_async_interceptors/metadata.py +36 -0
- sift_py/grpc/_interceptors/__init__.py +0 -0
- sift_py/grpc/_interceptors/base.py +61 -0
- sift_py/grpc/_interceptors/context.py +25 -0
- sift_py/grpc/_interceptors/metadata.py +33 -0
- sift_py/grpc/_retry.py +70 -0
- sift_py/grpc/keepalive.py +34 -0
- sift_py/grpc/transport.py +250 -0
- sift_py/grpc/transport_test.py +170 -0
- sift_py/ingestion/__init__.py +6 -0
- sift_py/ingestion/_internal/__init__.py +6 -0
- sift_py/ingestion/_internal/channel.py +12 -0
- sift_py/ingestion/_internal/error.py +10 -0
- sift_py/ingestion/_internal/ingest.py +350 -0
- sift_py/ingestion/_internal/ingest_test.py +357 -0
- sift_py/ingestion/_internal/ingestion_config.py +130 -0
- sift_py/ingestion/_internal/run.py +46 -0
- sift_py/ingestion/_service_test.py +478 -0
- sift_py/ingestion/buffer.py +189 -0
- sift_py/ingestion/channel.py +422 -0
- sift_py/ingestion/config/__init__.py +3 -0
- sift_py/ingestion/config/telemetry.py +281 -0
- sift_py/ingestion/config/telemetry_test.py +405 -0
- sift_py/ingestion/config/yaml/__init__.py +0 -0
- sift_py/ingestion/config/yaml/error.py +44 -0
- sift_py/ingestion/config/yaml/load.py +126 -0
- sift_py/ingestion/config/yaml/spec.py +58 -0
- sift_py/ingestion/config/yaml/test_load.py +25 -0
- sift_py/ingestion/flow.py +73 -0
- sift_py/ingestion/manager.py +99 -0
- sift_py/ingestion/rule/__init__.py +4 -0
- sift_py/ingestion/rule/config.py +11 -0
- sift_py/ingestion/service.py +237 -0
- sift_py/py.typed +0 -0
- sift_py/report_templates/__init__.py +0 -0
- sift_py/report_templates/_config_test.py +34 -0
- sift_py/report_templates/_service_test.py +94 -0
- sift_py/report_templates/config.py +36 -0
- sift_py/report_templates/service.py +171 -0
- sift_py/rest.py +29 -0
- sift_py/rule/__init__.py +0 -0
- sift_py/rule/_config_test.py +109 -0
- sift_py/rule/_service_test.py +168 -0
- sift_py/rule/config.py +229 -0
- sift_py/rule/service.py +484 -0
- sift_py/yaml/__init__.py +0 -0
- sift_py/yaml/_channel_test.py +169 -0
- sift_py/yaml/_rule_test.py +207 -0
- sift_py/yaml/channel.py +224 -0
- sift_py/yaml/report_templates.py +73 -0
- sift_py/yaml/rule.py +321 -0
- sift_py/yaml/utils.py +15 -0
- sift_stack_py-0.3.2.dist-info/LICENSE +7 -0
- sift_stack_py-0.3.2.dist-info/METADATA +109 -0
- sift_stack_py-0.3.2.dist-info/RECORD +291 -0
- sift_stack_py-0.3.2.dist-info/WHEEL +5 -0
- sift_stack_py-0.3.2.dist-info/top_level.txt +5 -0
sift_py/rule/config.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Any, Dict, List, Optional, TypedDict, Union, cast
|
|
6
|
+
|
|
7
|
+
from sift.annotations.v1.annotations_pb2 import AnnotationType
|
|
8
|
+
from sift.rules.v1.rules_pb2 import ActionKind
|
|
9
|
+
|
|
10
|
+
from sift_py._internal.convert.json import AsJson
|
|
11
|
+
from sift_py.ingestion.channel import ChannelConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RuleConfig(AsJson):
|
|
15
|
+
"""
|
|
16
|
+
Defines a rule to be used during ingestion. If a rule's expression validates to try, then
|
|
17
|
+
a specific action will take place as specified by the `kind` attribute.
|
|
18
|
+
|
|
19
|
+
- `name`: Name of the rule.
|
|
20
|
+
- `description`: Description of the rule.
|
|
21
|
+
- `expression`: A CEL string expression that executes the `action` when evaluated to a truthy value.
|
|
22
|
+
- `action`: The action to execute if the result of an `expression` evaluates to a truthy value.
|
|
23
|
+
- `channel_references`: Reference to channel. If an expression is "$1 < 10", then "$1" is the reference and thus should the key in the dict.
|
|
24
|
+
- `rule_client_key`: User defined unique string that uniquely identifies this rule.
|
|
25
|
+
- `asset_names`: A list of asset names that this rule should be applied to. ONLY VALID if defining rules outside of a telemetry config.
|
|
26
|
+
- `tag_names`: A list of asset names that this rule should be applied to. ONLY VALID if defining rules outside of a telemetry config.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
name: str
|
|
30
|
+
description: str
|
|
31
|
+
expression: str
|
|
32
|
+
action: Optional[RuleAction]
|
|
33
|
+
channel_references: List[ExpressionChannelReference]
|
|
34
|
+
rule_client_key: Optional[str]
|
|
35
|
+
asset_names: List[str]
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
name: str,
|
|
40
|
+
channel_references: List[
|
|
41
|
+
Union[ExpressionChannelReference, ExpressionChannelReferenceChannelConfig]
|
|
42
|
+
],
|
|
43
|
+
description: str = "",
|
|
44
|
+
expression: str = "",
|
|
45
|
+
action: Optional[RuleAction] = None,
|
|
46
|
+
rule_client_key: Optional[str] = None,
|
|
47
|
+
asset_names: Optional[List[str]] = None,
|
|
48
|
+
tag_names: Optional[List[str]] = None,
|
|
49
|
+
sub_expressions: Dict[str, Any] = {},
|
|
50
|
+
):
|
|
51
|
+
self.channel_references = []
|
|
52
|
+
|
|
53
|
+
for channel_reference in channel_references:
|
|
54
|
+
config = channel_reference.get("channel_config")
|
|
55
|
+
|
|
56
|
+
if config is not None:
|
|
57
|
+
config = cast(ChannelConfig, config)
|
|
58
|
+
|
|
59
|
+
self.channel_references.append(
|
|
60
|
+
{
|
|
61
|
+
"channel_reference": channel_reference["channel_reference"],
|
|
62
|
+
"channel_identifier": config.fqn(),
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
channel_ref = cast(ExpressionChannelReference, channel_reference)
|
|
67
|
+
|
|
68
|
+
self.channel_references.append(
|
|
69
|
+
{
|
|
70
|
+
"channel_reference": channel_ref["channel_reference"],
|
|
71
|
+
"channel_identifier": channel_ref["channel_identifier"],
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self.name = name
|
|
76
|
+
self.asset_names = asset_names or []
|
|
77
|
+
self.action = action
|
|
78
|
+
self.rule_client_key = rule_client_key
|
|
79
|
+
self.description = description
|
|
80
|
+
self.expression = self.__class__.interpolate_sub_expressions(expression, sub_expressions)
|
|
81
|
+
|
|
82
|
+
def as_json(self) -> Any:
|
|
83
|
+
"""
|
|
84
|
+
Produces the appropriate JSON structure that's suitable for the Rules API.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
hash_map: Dict[str, Union[List[ExpressionChannelReference], str, List[str], None]] = {
|
|
88
|
+
"name": self.name,
|
|
89
|
+
"description": self.description,
|
|
90
|
+
"expression": self.expression,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
hash_map["expression_channel_references"] = self.channel_references
|
|
94
|
+
|
|
95
|
+
if isinstance(self.action, RuleActionCreateDataReviewAnnotation):
|
|
96
|
+
hash_map["type"] = RuleActionAnnotationKind.REVIEW.value
|
|
97
|
+
hash_map["assignee"] = self.action.assignee
|
|
98
|
+
|
|
99
|
+
if self.action.assignee is not None and len(self.action.assignee) > 0:
|
|
100
|
+
hash_map["assignee"] = self.action.assignee
|
|
101
|
+
|
|
102
|
+
if self.action.tags is not None and len(self.action.tags) > 0:
|
|
103
|
+
hash_map["tags"] = self.action.tags
|
|
104
|
+
|
|
105
|
+
elif isinstance(self.action, RuleActionCreatePhaseAnnotation):
|
|
106
|
+
hash_map["type"] = RuleActionAnnotationKind.PHASE.value
|
|
107
|
+
|
|
108
|
+
if self.action.tags is not None and len(self.action.tags) > 0:
|
|
109
|
+
hash_map["tags"] = self.action.tags
|
|
110
|
+
else:
|
|
111
|
+
kind = self.action.kind() if self.action else self.action
|
|
112
|
+
raise TypeError(f"Unsupported rule action '{kind}'.")
|
|
113
|
+
|
|
114
|
+
return hash_map
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def interpolate_sub_expressions(
|
|
118
|
+
expression: str, sub_expressions: Optional[Dict[str, str]]
|
|
119
|
+
) -> str:
|
|
120
|
+
if sub_expressions:
|
|
121
|
+
for ref, expr in sub_expressions.items():
|
|
122
|
+
if ref not in expression:
|
|
123
|
+
raise ValueError(f"Couldn't find '{ref}' in expression '{expression}'.")
|
|
124
|
+
if isinstance(expr, str):
|
|
125
|
+
expression = expression.replace(ref, f'"{expr}"')
|
|
126
|
+
else:
|
|
127
|
+
expression = expression.replace(ref, str(expr))
|
|
128
|
+
|
|
129
|
+
return expression
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class RuleAction(ABC):
|
|
133
|
+
@abstractmethod
|
|
134
|
+
def kind(self) -> RuleActionKind:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class RuleActionCreateDataReviewAnnotation(RuleAction):
|
|
139
|
+
"""
|
|
140
|
+
Action to create a data-review annotation when a rule evaluates to a truthy value.
|
|
141
|
+
|
|
142
|
+
- `tags`: List of tag names to associate with the newly created data-review annotation.
|
|
143
|
+
- `assignee`: Email of user in organization to assign the newly created data-review annotation.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
tags: Optional[List[str]]
|
|
147
|
+
assignee: Optional[str]
|
|
148
|
+
|
|
149
|
+
def __init__(self, assignee: Optional[str] = None, tags: Optional[List[str]] = None):
|
|
150
|
+
self.assignee = assignee
|
|
151
|
+
self.tags = tags
|
|
152
|
+
|
|
153
|
+
def kind(self) -> RuleActionKind:
|
|
154
|
+
return RuleActionKind.ANNOTATION
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class RuleActionCreatePhaseAnnotation(RuleAction):
|
|
158
|
+
"""
|
|
159
|
+
Action to create a phase annotation when a rule evaluates to a truthy value.
|
|
160
|
+
|
|
161
|
+
- `tags`: List of tag names to associate with the newly created data-review annotation.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
tags: Optional[List[str]]
|
|
165
|
+
|
|
166
|
+
def __init__(self, tags: Optional[List[str]] = None):
|
|
167
|
+
self.tags = tags
|
|
168
|
+
|
|
169
|
+
def kind(self) -> RuleActionKind:
|
|
170
|
+
return RuleActionKind.ANNOTATION
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class RuleActionKind(Enum):
|
|
174
|
+
NOTIFICATION = ActionKind.NOTIFICATION
|
|
175
|
+
ANNOTATION = ActionKind.ANNOTATION
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def from_str(cls, val: str) -> Optional["RuleActionKind"]:
|
|
179
|
+
if val == "ACTION_KIND_NOTIFICATION" or val == RuleActionKindStrRep.NOTIFICATION.value:
|
|
180
|
+
return cls.NOTIFICATION
|
|
181
|
+
elif val == "ACTION_KIND_ANNOTATION" or val == RuleActionKindStrRep.ANNOTATION.value:
|
|
182
|
+
return cls.ANNOTATION
|
|
183
|
+
|
|
184
|
+
return None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class RuleActionAnnotationKind(Enum):
|
|
188
|
+
REVIEW = "review"
|
|
189
|
+
PHASE = "phase"
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def from_annotation_type(cls, annotation_type: AnnotationType) -> "RuleActionAnnotationKind":
|
|
193
|
+
if annotation_type == AnnotationType.ANNOTATION_TYPE_PHASE:
|
|
194
|
+
return cls.PHASE
|
|
195
|
+
return cls.PHASE
|
|
196
|
+
|
|
197
|
+
@classmethod
|
|
198
|
+
def from_str(cls, val: str) -> "RuleActionAnnotationKind":
|
|
199
|
+
if val == cls.REVIEW.value:
|
|
200
|
+
return cls.REVIEW
|
|
201
|
+
elif val == cls.PHASE.value:
|
|
202
|
+
return cls.PHASE
|
|
203
|
+
else:
|
|
204
|
+
raise ValueError(f"Argument '{val}' is not a valid annotation kind.")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class RuleActionKindStrRep(Enum):
|
|
208
|
+
NOTIFICATION = "notification"
|
|
209
|
+
ANNOTATION = "annotation"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class ExpressionChannelReference(TypedDict):
|
|
213
|
+
"""
|
|
214
|
+
`channel_reference`: The channel reference (e.g. '$1') used in the expression.
|
|
215
|
+
`channel_identifier`: The fully qualified channel name. See `sift_py.ingestion.channel.channel_fqn`.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
channel_reference: str
|
|
219
|
+
channel_identifier: str
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class ExpressionChannelReferenceChannelConfig(TypedDict):
|
|
223
|
+
"""
|
|
224
|
+
`channel_reference`: The channel reference (e.g. '$1') used in the expression.
|
|
225
|
+
`channel_config`: Instance of `sift_py.ingestion.channel.ChannelConfig`.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
channel_reference: str
|
|
229
|
+
channel_config: ChannelConfig
|