validio-sdk 5.3.0__tar.gz → 5.3.1__tar.gz
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.
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/PKG-INFO +1 -1
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/pyproject.toml +1 -1
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/_api/api.py +0 -4
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/code/_import.py +1 -1
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/code/plan.py +1 -5
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_diff.py +1 -36
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_server_resources.py +5 -8
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/channels.py +17 -94
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/filters.py +26 -103
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/test__diff.py +3 -2
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/test__plan.py +0 -54
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/test__resource.py +0 -2
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/validators.py +28 -114
- validio_sdk-5.3.0/validio_sdk/resource/tests/test__channel.py +0 -137
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/LICENSE +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/README_PUBLIC.md +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/__init__.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/_api/__init__.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/client/__init__.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/client/client.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/code/__init__.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/code/_progress.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/code/apply.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/code/scaffold.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/code/settings.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/config.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/dbt.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/exception.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/metadata.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/py.typed +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/__init__.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_diff_util.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_diffable.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_errors.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_resource.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_resource_graph.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_serde.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_update_namespace.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/_util.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/credentials.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/notification_rules.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/replacement.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/segmentations.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/sources.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tags.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/__init__.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/assets/example_manifest.json +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/assets/expected_trimmed_manifest.json +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/test__dbt.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/test__sql_validation.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/tests/test_import.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/thresholds.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/resource/windows.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/scalars.py +0 -0
- {validio_sdk-5.3.0 → validio_sdk-5.3.1}/validio_sdk/util.py +0 -0
|
@@ -3,7 +3,7 @@ name = "validio-sdk"
|
|
|
3
3
|
# This version does not represent the released version or any tag. For each
|
|
4
4
|
# release we automatically bump this before building and publishing so this
|
|
5
5
|
# should be kept at 0.0.1dev1
|
|
6
|
-
version = "5.3.
|
|
6
|
+
version = "5.3.1"
|
|
7
7
|
description = "SDK to interact with the Validio platform"
|
|
8
8
|
authors = ["Validio <support@validio.io>"]
|
|
9
9
|
license = "Apache-2.0"
|
|
@@ -1194,7 +1194,6 @@ async def get_channels(
|
|
|
1194
1194
|
}
|
|
1195
1195
|
... on SlackChannel {
|
|
1196
1196
|
config {
|
|
1197
|
-
slackWebhookUrl: webhookUrl
|
|
1198
1197
|
applicationLinkUrl
|
|
1199
1198
|
slackChannelId
|
|
1200
1199
|
interactiveMessageEnabled
|
|
@@ -1207,7 +1206,6 @@ async def get_channels(
|
|
|
1207
1206
|
}
|
|
1208
1207
|
... on MsTeamsChannel {
|
|
1209
1208
|
config {
|
|
1210
|
-
msTeamsWebhookUrl: webhookUrl
|
|
1211
1209
|
applicationLinkUrl
|
|
1212
1210
|
msTeamsChannelId
|
|
1213
1211
|
msTeamsInteractiveMessageEnabled: interactiveMessageEnabled
|
|
@@ -1276,8 +1274,6 @@ async def get_channels(
|
|
|
1276
1274
|
_replace_aliases(
|
|
1277
1275
|
ftr["config"],
|
|
1278
1276
|
[
|
|
1279
|
-
("slackWebhookUrl", "webhookUrl"),
|
|
1280
|
-
("msTeamsWebhookUrl", "webhookUrl"),
|
|
1281
1277
|
("msTeamsInteractiveMessageEnabled", "interactiveMessageEnabled"),
|
|
1282
1278
|
("emailInteractiveMessageEnabled", "interactiveMessageEnabled"),
|
|
1283
1279
|
],
|
|
@@ -24,7 +24,7 @@ from validio_sdk.resource._resource import (
|
|
|
24
24
|
)
|
|
25
25
|
from validio_sdk.resource._server_resources import load_resources
|
|
26
26
|
from validio_sdk.resource._util import SourceSchemaReinference
|
|
27
|
-
from validio_sdk.resource.channels import
|
|
27
|
+
from validio_sdk.resource.channels import WebhookChannel
|
|
28
28
|
from validio_sdk.resource.tags import Tag
|
|
29
29
|
|
|
30
30
|
|
|
@@ -286,10 +286,6 @@ def _create_resource_diff_object(
|
|
|
286
286
|
if not show_secrets and hasattr(r, "_secret_fields"):
|
|
287
287
|
secret_fields = r._secret_fields()
|
|
288
288
|
|
|
289
|
-
# Workaround for Channel having a special secret field
|
|
290
|
-
if isinstance(r, Channel):
|
|
291
|
-
secret_fields.update(r._deprecated_secret_fields())
|
|
292
|
-
|
|
293
289
|
if secret_fields:
|
|
294
290
|
for field in secret_fields:
|
|
295
291
|
# We want to avoid overriding webhook_url if it is None already
|
|
@@ -26,7 +26,6 @@ from validio_sdk.resource._errors import (
|
|
|
26
26
|
)
|
|
27
27
|
from validio_sdk.resource._resource import CREATE_ONLY_RESOURCES, DiffContext, Resource
|
|
28
28
|
from validio_sdk.resource._util import SourceSchemaReinference, _sanitize_error
|
|
29
|
-
from validio_sdk.resource.channels import Channel
|
|
30
29
|
from validio_sdk.resource.replacement import (
|
|
31
30
|
CascadeReplacementReason,
|
|
32
31
|
ImmutableFieldReplacementReason,
|
|
@@ -36,7 +35,6 @@ from validio_sdk.resource.replacement import (
|
|
|
36
35
|
from validio_sdk.resource.segmentations import Segmentation
|
|
37
36
|
from validio_sdk.resource.sources import Source
|
|
38
37
|
from validio_sdk.resource.tags import Tag
|
|
39
|
-
from validio_sdk.resource.validators import Reference, Validator
|
|
40
38
|
from validio_sdk.resource.windows import TumblingWindow
|
|
41
39
|
|
|
42
40
|
if TYPE_CHECKING:
|
|
@@ -254,8 +252,7 @@ def _visit_resource_to_replace(
|
|
|
254
252
|
to_delete: dict[str, Resource] = getattr(graph_diff.to_delete, resource_type)
|
|
255
253
|
|
|
256
254
|
# Remove the resource from the update list.
|
|
257
|
-
|
|
258
|
-
del to_update[resource_name]
|
|
255
|
+
to_update.pop(resource_name, None)
|
|
259
256
|
|
|
260
257
|
# Flag the resource to be created.
|
|
261
258
|
manifest_resources: dict[str, Resource] = getattr(manifest_ctx, resource_type)
|
|
@@ -566,23 +563,6 @@ def diff(
|
|
|
566
563
|
if isinstance(manifest_object, Resource) and manifest_object.ignore_changes:
|
|
567
564
|
return None
|
|
568
565
|
|
|
569
|
-
# NOTE: Backwards compatibility while we transition to Filter resources.
|
|
570
|
-
# If we're about to diff a validator where the manifest validator still uses
|
|
571
|
-
# the JSONFilterExpression mode, then force the server object to work
|
|
572
|
-
# in the JSONFilterExpression mode so that we can compare the same fields.
|
|
573
|
-
# Same for validator reference.
|
|
574
|
-
if (
|
|
575
|
-
isinstance(manifest_object, (Reference, Validator))
|
|
576
|
-
and
|
|
577
|
-
# NOTE: the server resource is always the Validator. Even if
|
|
578
|
-
# the server object is potentially the nested 'Reference'.
|
|
579
|
-
# This is mostly for type-checking.
|
|
580
|
-
isinstance(server_resource, Validator)
|
|
581
|
-
and isinstance(manifest_resource, Validator)
|
|
582
|
-
) and not manifest_object._filter_resource_mode():
|
|
583
|
-
manifest_resource._force_filter_expression_mode()
|
|
584
|
-
server_resource._force_filter_expression_mode()
|
|
585
|
-
|
|
586
566
|
if isinstance(server_resource, Segmentation) and isinstance(
|
|
587
567
|
manifest_resource, Segmentation
|
|
588
568
|
):
|
|
@@ -642,20 +622,6 @@ def diff(
|
|
|
642
622
|
server = getattr(server_object, field)
|
|
643
623
|
|
|
644
624
|
if manifest != server:
|
|
645
|
-
# This is a temporary fix to support marking deprecated secret fields as
|
|
646
|
-
# changed on a `Channel`. If the field is both in `_mutable_fields`
|
|
647
|
-
# and also in `_deprecated_secret_fields` we will flag the field in
|
|
648
|
-
# `secret_fields_changed` if there has been an update. This ensures
|
|
649
|
-
# that the value is redacted by default when displaying the config.
|
|
650
|
-
secret_fields_changed = None
|
|
651
|
-
if (
|
|
652
|
-
isinstance(manifest_object, Channel)
|
|
653
|
-
and field in manifest_object._deprecated_secret_fields()
|
|
654
|
-
):
|
|
655
|
-
secret_fields_changed = {
|
|
656
|
-
field: True,
|
|
657
|
-
}
|
|
658
|
-
|
|
659
625
|
# Whenever we find there's a change to make, grab all fields on both
|
|
660
626
|
# resource versions so that we can present them as a diff.
|
|
661
627
|
# Regardless of at what depth we find a change, the diff we present
|
|
@@ -664,7 +630,6 @@ def diff(
|
|
|
664
630
|
return ResourceUpdate(
|
|
665
631
|
manifest_resource,
|
|
666
632
|
server_resource,
|
|
667
|
-
secret_fields_changed=secret_fields_changed,
|
|
668
633
|
)
|
|
669
634
|
|
|
670
635
|
# Some fields require the resource to be re-created if the type changes
|
|
@@ -498,7 +498,6 @@ async def load_channels(
|
|
|
498
498
|
|
|
499
499
|
cfg = ch["config"]
|
|
500
500
|
application_link_url = cfg["applicationLinkUrl"]
|
|
501
|
-
unset_or_none = None if cfg.get("webhookUrl") else "UNSET"
|
|
502
501
|
interactive_message_enabled = cfg.get("interactiveMessageEnabled", None)
|
|
503
502
|
display_name = ch["name"]
|
|
504
503
|
# The 'secret' parts of a channel are left unset since they are not
|
|
@@ -509,10 +508,9 @@ async def load_channels(
|
|
|
509
508
|
name=name,
|
|
510
509
|
application_link_url=application_link_url,
|
|
511
510
|
ms_teams_channel_id=cfg["msTeamsChannelId"],
|
|
512
|
-
client_id=
|
|
513
|
-
client_secret=
|
|
511
|
+
client_id="UNSET",
|
|
512
|
+
client_secret="UNSET",
|
|
514
513
|
interactive_message_enabled=interactive_message_enabled,
|
|
515
|
-
webhook_url=cfg["webhookUrl"],
|
|
516
514
|
display_name=display_name,
|
|
517
515
|
__internal__=g,
|
|
518
516
|
)
|
|
@@ -521,11 +519,10 @@ async def load_channels(
|
|
|
521
519
|
name=name,
|
|
522
520
|
application_link_url=application_link_url,
|
|
523
521
|
slack_channel_id=cfg["slackChannelId"],
|
|
524
|
-
token=
|
|
525
|
-
signing_secret=
|
|
526
|
-
app_token=
|
|
522
|
+
token="UNSET",
|
|
523
|
+
signing_secret="UNSET",
|
|
524
|
+
app_token="UNSET",
|
|
527
525
|
interactive_message_enabled=interactive_message_enabled,
|
|
528
|
-
webhook_url=cfg["webhookUrl"],
|
|
529
526
|
display_name=display_name,
|
|
530
527
|
__internal__=g,
|
|
531
528
|
)
|
|
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
|
5
5
|
|
|
6
6
|
from camel_converter import to_camel
|
|
7
7
|
|
|
8
|
-
from validio_sdk.resource._errors import ManifestConfigurationError
|
|
9
8
|
from validio_sdk.resource._resource import (
|
|
10
9
|
ApiSecretChangeNestedResource,
|
|
11
10
|
Resource,
|
|
@@ -69,13 +68,6 @@ class Channel(Resource):
|
|
|
69
68
|
self._resource_graph: ResourceGraph = g
|
|
70
69
|
self._resource_graph._add_root(self)
|
|
71
70
|
|
|
72
|
-
def _deprecated_secret_fields(self) -> set[str]:
|
|
73
|
-
"""Fields that are not supported when calling the secrets changed endpoint.
|
|
74
|
-
|
|
75
|
-
:return: Set of secret fields that are deprecated
|
|
76
|
-
"""
|
|
77
|
-
return set({})
|
|
78
|
-
|
|
79
71
|
def _immutable_fields(self) -> set[str]:
|
|
80
72
|
return set({})
|
|
81
73
|
|
|
@@ -107,7 +99,8 @@ class Channel(Resource):
|
|
|
107
99
|
},
|
|
108
100
|
}
|
|
109
101
|
return _api_create_input_params(
|
|
110
|
-
resource=self,
|
|
102
|
+
resource=self,
|
|
103
|
+
overrides=overrides,
|
|
111
104
|
)
|
|
112
105
|
|
|
113
106
|
def _api_update_input(self, _namespace: str, _ctx: "DiffContext") -> Any:
|
|
@@ -118,16 +111,10 @@ class Channel(Resource):
|
|
|
118
111
|
}
|
|
119
112
|
}
|
|
120
113
|
return _api_update_input_params(
|
|
121
|
-
resource=self,
|
|
114
|
+
resource=self,
|
|
115
|
+
overrides=overrides,
|
|
122
116
|
)
|
|
123
117
|
|
|
124
|
-
def _api_skip_fields(self) -> set[str]:
|
|
125
|
-
return {
|
|
126
|
-
to_camel(f)
|
|
127
|
-
for f in self._deprecated_secret_fields()
|
|
128
|
-
if not getattr(self, f, None)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
118
|
@staticmethod
|
|
132
119
|
def _decode(
|
|
133
120
|
ctx: "DiffContext",
|
|
@@ -166,15 +153,6 @@ class Channel(Resource):
|
|
|
166
153
|
|
|
167
154
|
return channel
|
|
168
155
|
|
|
169
|
-
def _api_secret_change_response(
|
|
170
|
-
self,
|
|
171
|
-
) -> dict[str, None | ApiSecretChangeNestedResource]:
|
|
172
|
-
fields = super()._api_secret_change_response()
|
|
173
|
-
for secret_field in self._deprecated_secret_fields():
|
|
174
|
-
if secret_field in fields:
|
|
175
|
-
del fields[secret_field]
|
|
176
|
-
return fields
|
|
177
|
-
|
|
178
156
|
|
|
179
157
|
class SlackChannel(Channel):
|
|
180
158
|
"""
|
|
@@ -188,12 +166,11 @@ class SlackChannel(Channel):
|
|
|
188
166
|
*,
|
|
189
167
|
name: str,
|
|
190
168
|
application_link_url: str,
|
|
191
|
-
slack_channel_id: str
|
|
192
|
-
token: str
|
|
169
|
+
slack_channel_id: str,
|
|
170
|
+
token: str,
|
|
193
171
|
signing_secret: str | None = None,
|
|
194
172
|
app_token: str | None = None,
|
|
195
|
-
interactive_message_enabled: bool
|
|
196
|
-
webhook_url: str | None = None,
|
|
173
|
+
interactive_message_enabled: bool = True,
|
|
197
174
|
display_name: str | None = None,
|
|
198
175
|
ignore_changes: bool = False,
|
|
199
176
|
__internal__: ResourceGraph | None = None,
|
|
@@ -209,8 +186,6 @@ class SlackChannel(Channel):
|
|
|
209
186
|
:param app_token: App level token used to retrieve user events from Slack.
|
|
210
187
|
:param interactive_message_enabled: If interactive notification messages should
|
|
211
188
|
be used.
|
|
212
|
-
:param webhook_url: Webhook URL provided by Slack to the
|
|
213
|
-
specified Slack channel. (deprecated)
|
|
214
189
|
:param display_name: Human-readable name for the channel. This name is
|
|
215
190
|
visible in the UI and does not need to be unique.
|
|
216
191
|
:param ignore_changes: If set to true, changes to the resource will be ignored.
|
|
@@ -222,7 +197,6 @@ class SlackChannel(Channel):
|
|
|
222
197
|
__internal__=__internal__,
|
|
223
198
|
)
|
|
224
199
|
self.application_link_url = application_link_url
|
|
225
|
-
self.webhook_url = webhook_url
|
|
226
200
|
self.slack_channel_id = slack_channel_id
|
|
227
201
|
self.token = token
|
|
228
202
|
self.signing_secret = signing_secret
|
|
@@ -232,34 +206,12 @@ class SlackChannel(Channel):
|
|
|
232
206
|
if self.signing_secret:
|
|
233
207
|
self.add_field_deprecation("signing_secret", "app_token")
|
|
234
208
|
|
|
235
|
-
if webhook_url and (
|
|
236
|
-
slack_channel_id
|
|
237
|
-
or token
|
|
238
|
-
or signing_secret
|
|
239
|
-
or interactive_message_enabled
|
|
240
|
-
or app_token
|
|
241
|
-
):
|
|
242
|
-
raise ManifestConfigurationError(
|
|
243
|
-
f"invalid configuration for Slack channel {self.name}: either"
|
|
244
|
-
" webhook_url or slack_channel_id, token, app_token and"
|
|
245
|
-
" interactive_message_enabled can be used."
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
if webhook_url:
|
|
249
|
-
self.add_field_deprecation("webhook_url")
|
|
250
|
-
|
|
251
209
|
def _secret_fields(self) -> set[str]:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
else {
|
|
258
|
-
"app_token",
|
|
259
|
-
"signing_secret",
|
|
260
|
-
"token",
|
|
261
|
-
}
|
|
262
|
-
)
|
|
210
|
+
return {
|
|
211
|
+
"app_token",
|
|
212
|
+
"signing_secret",
|
|
213
|
+
"token",
|
|
214
|
+
}
|
|
263
215
|
|
|
264
216
|
def _immutable_fields(self) -> set[str]:
|
|
265
217
|
return set({})
|
|
@@ -271,13 +223,9 @@ class SlackChannel(Channel):
|
|
|
271
223
|
"application_link_url",
|
|
272
224
|
"slack_channel_id",
|
|
273
225
|
"interactive_message_enabled",
|
|
274
|
-
*self._deprecated_secret_fields(),
|
|
275
226
|
},
|
|
276
227
|
}
|
|
277
228
|
|
|
278
|
-
def _deprecated_secret_fields(self) -> set[str]:
|
|
279
|
-
return {"webhook_url"}
|
|
280
|
-
|
|
281
229
|
|
|
282
230
|
class MsTeamsChannel(Channel):
|
|
283
231
|
"""
|
|
@@ -291,11 +239,10 @@ class MsTeamsChannel(Channel):
|
|
|
291
239
|
*,
|
|
292
240
|
name: str,
|
|
293
241
|
application_link_url: str,
|
|
294
|
-
ms_teams_channel_id: str
|
|
295
|
-
client_id: str
|
|
296
|
-
client_secret: str
|
|
297
|
-
interactive_message_enabled: bool
|
|
298
|
-
webhook_url: str | None = None,
|
|
242
|
+
ms_teams_channel_id: str,
|
|
243
|
+
client_id: str,
|
|
244
|
+
client_secret: str,
|
|
245
|
+
interactive_message_enabled: bool = True,
|
|
299
246
|
display_name: str | None = None,
|
|
300
247
|
ignore_changes: bool = False,
|
|
301
248
|
__internal__: ResourceGraph | None = None,
|
|
@@ -310,8 +257,6 @@ class MsTeamsChannel(Channel):
|
|
|
310
257
|
:param client_secret: Client secret for authentication.
|
|
311
258
|
:param interactive_message_enabled: If interactive notification messages should
|
|
312
259
|
be used.
|
|
313
|
-
:param webhook_url: Webhook URL provided by Microsoft Teams to the
|
|
314
|
-
specified channel. (deprecated)
|
|
315
260
|
:param display_name: Human-readable name for the channel. This name is
|
|
316
261
|
visible in the UI and does not need to be unique.
|
|
317
262
|
:param ignore_changes: If set to true, changes to the resource will be ignored.
|
|
@@ -323,31 +268,13 @@ class MsTeamsChannel(Channel):
|
|
|
323
268
|
__internal__=__internal__,
|
|
324
269
|
)
|
|
325
270
|
self.application_link_url = application_link_url
|
|
326
|
-
self.webhook_url = webhook_url
|
|
327
271
|
self.ms_teams_channel_id = ms_teams_channel_id
|
|
328
272
|
self.client_id = client_id
|
|
329
273
|
self.client_secret = client_secret
|
|
330
274
|
self.interactive_message_enabled = interactive_message_enabled
|
|
331
275
|
|
|
332
|
-
if webhook_url and (
|
|
333
|
-
ms_teams_channel_id
|
|
334
|
-
or client_id
|
|
335
|
-
or client_secret
|
|
336
|
-
or interactive_message_enabled
|
|
337
|
-
):
|
|
338
|
-
raise ManifestConfigurationError(
|
|
339
|
-
f"invalid configuration for MS Teams channel {self.name}: either"
|
|
340
|
-
" webhook_url or ms_teams_channel_id, client_id, client_secret,"
|
|
341
|
-
" and interactive_message_enabled can be used."
|
|
342
|
-
)
|
|
343
|
-
|
|
344
|
-
if webhook_url:
|
|
345
|
-
self.add_field_deprecation("webhook_url")
|
|
346
|
-
|
|
347
276
|
def _secret_fields(self) -> set[str]:
|
|
348
|
-
|
|
349
|
-
# avoid calling the secrets changed endpoint as it's not supported
|
|
350
|
-
return set({}) if self.webhook_url else {"client_id", "client_secret"}
|
|
277
|
+
return {"client_id", "client_secret"}
|
|
351
278
|
|
|
352
279
|
def _immutable_fields(self) -> set[str]:
|
|
353
280
|
return set({})
|
|
@@ -359,13 +286,9 @@ class MsTeamsChannel(Channel):
|
|
|
359
286
|
"application_link_url",
|
|
360
287
|
"ms_teams_channel_id",
|
|
361
288
|
"interactive_message_enabled",
|
|
362
|
-
*self._deprecated_secret_fields(),
|
|
363
289
|
},
|
|
364
290
|
}
|
|
365
291
|
|
|
366
|
-
def _deprecated_secret_fields(self) -> set[str]:
|
|
367
|
-
return {"webhook_url"}
|
|
368
|
-
|
|
369
292
|
|
|
370
293
|
class WebhookChannel(Channel):
|
|
371
294
|
"""
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
"""Filters configuration."""
|
|
2
2
|
|
|
3
|
-
from abc import abstractmethod
|
|
4
3
|
from enum import Enum
|
|
5
4
|
from typing import TYPE_CHECKING, Any, Optional
|
|
6
5
|
|
|
7
6
|
from validio_sdk.exception import ValidioBugError
|
|
8
7
|
from validio_sdk.resource._diffable import Diffable
|
|
9
|
-
from validio_sdk.resource.
|
|
10
|
-
from validio_sdk.resource._resource import Resource, ResourceGraph
|
|
8
|
+
from validio_sdk.resource._resource import Resource
|
|
11
9
|
from validio_sdk.resource._serde import (
|
|
12
|
-
CONFIG_FIELD_NAME,
|
|
13
10
|
NODE_TYPE_FIELD_NAME,
|
|
14
11
|
_api_create_input_params,
|
|
15
12
|
_encode_resource,
|
|
@@ -21,10 +18,6 @@ if TYPE_CHECKING:
|
|
|
21
18
|
from validio_sdk.resource.sources import Source
|
|
22
19
|
|
|
23
20
|
|
|
24
|
-
# used to detect when we're in resource vs filter expression mode.
|
|
25
|
-
DUMMY_NAME_PLACEHOLDER = "_validio_dummy_filter"
|
|
26
|
-
|
|
27
|
-
|
|
28
21
|
class Filter(Resource):
|
|
29
22
|
"""
|
|
30
23
|
Base class for a filter configuration.
|
|
@@ -34,8 +27,8 @@ class Filter(Resource):
|
|
|
34
27
|
|
|
35
28
|
def __init__(
|
|
36
29
|
self,
|
|
37
|
-
name: str
|
|
38
|
-
source:
|
|
30
|
+
name: str,
|
|
31
|
+
source: "Source",
|
|
39
32
|
display_name: str | None = None,
|
|
40
33
|
ignore_changes: bool = False,
|
|
41
34
|
):
|
|
@@ -48,28 +41,11 @@ class Filter(Resource):
|
|
|
48
41
|
visible in the UI and does not need to be unique.
|
|
49
42
|
:param ignore_changes: If set to true, changes to the resource will be ignored
|
|
50
43
|
"""
|
|
51
|
-
# For backwards compatibility while we transition from
|
|
52
|
-
# JSONFilterExpression to Filter resources, the Filter class
|
|
53
|
-
# is able to optionally act as a resource.
|
|
54
|
-
if (source is None) and (name is not None and name != DUMMY_NAME_PLACEHOLDER):
|
|
55
|
-
raise ManifestConfigurationError(
|
|
56
|
-
f"invalid {self.__class__.__name__}: "
|
|
57
|
-
"name and source arguments must be provided together."
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
# If in JSONFilterExpression mode, we initialize the base
|
|
61
|
-
# resource with an empty resource graph and a random name.
|
|
62
|
-
# Both values are simply placeholders and are ignored in this
|
|
63
|
-
# mode.
|
|
64
|
-
resource_graph = (
|
|
65
|
-
source._resource_graph if source is not None else ResourceGraph()
|
|
66
|
-
)
|
|
67
|
-
|
|
68
44
|
super().__init__(
|
|
69
|
-
name=name
|
|
45
|
+
name=name,
|
|
70
46
|
display_name=display_name,
|
|
71
47
|
ignore_changes=ignore_changes,
|
|
72
|
-
__internal__=
|
|
48
|
+
__internal__=source._resource_graph,
|
|
73
49
|
)
|
|
74
50
|
|
|
75
51
|
# If in Resource mode, then we add it as usual to the graph
|
|
@@ -78,21 +54,11 @@ class Filter(Resource):
|
|
|
78
54
|
self.source_name: str = source.name
|
|
79
55
|
source.add(self.name, self)
|
|
80
56
|
|
|
81
|
-
def _in_resource_mode(self) -> bool:
|
|
82
|
-
"""Returns true if the filter is in resource mode."""
|
|
83
|
-
return self.name != DUMMY_NAME_PLACEHOLDER
|
|
84
|
-
|
|
85
57
|
def _immutable_fields(self) -> set[str]:
|
|
86
|
-
|
|
87
|
-
return {"source_name"}
|
|
88
|
-
|
|
89
|
-
return set({})
|
|
58
|
+
return {"source_name"}
|
|
90
59
|
|
|
91
60
|
def _mutable_fields(self) -> set[str]:
|
|
92
|
-
|
|
93
|
-
return super()._mutable_fields()
|
|
94
|
-
|
|
95
|
-
return set({})
|
|
61
|
+
return super()._mutable_fields()
|
|
96
62
|
|
|
97
63
|
def _nested_objects(self) -> dict[str, Diffable | list[Diffable] | None]:
|
|
98
64
|
return {}
|
|
@@ -101,24 +67,10 @@ class Filter(Resource):
|
|
|
101
67
|
"""Returns the base class name."""
|
|
102
68
|
return "Filter"
|
|
103
69
|
|
|
104
|
-
@abstractmethod
|
|
105
|
-
def _scalar(self) -> dict[str, object]:
|
|
106
|
-
"""
|
|
107
|
-
Returns the JsonFilterExpression representation of this filter.
|
|
108
|
-
For backwards compatibility while we transition to filter resources.
|
|
109
|
-
"""
|
|
110
|
-
|
|
111
70
|
def _encode(self) -> dict[str, object]:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return _encode_resource(self, skip_fields={"source_name"})
|
|
116
|
-
|
|
117
|
-
data = self._scalar()
|
|
118
|
-
data[NODE_TYPE_FIELD_NAME] = self.__class__.__name__
|
|
119
|
-
data["name"] = self.name
|
|
120
|
-
|
|
121
|
-
return data
|
|
71
|
+
# Drop fields here that are not part of the constructor for when we
|
|
72
|
+
# deserialize back. They will be reinitialized by the constructor.
|
|
73
|
+
return _encode_resource(self, skip_fields={"source_name"})
|
|
122
74
|
|
|
123
75
|
@staticmethod
|
|
124
76
|
def _decode(
|
|
@@ -127,17 +79,11 @@ class Filter(Resource):
|
|
|
127
79
|
) -> "Filter":
|
|
128
80
|
cls = eval(obj[NODE_TYPE_FIELD_NAME])
|
|
129
81
|
|
|
130
|
-
|
|
82
|
+
if source is None:
|
|
83
|
+
raise ValidioBugError("Missing source when decoding filter")
|
|
131
84
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
raise ValidioBugError(
|
|
135
|
-
"Missing source when decoding filter in resource mode"
|
|
136
|
-
)
|
|
137
|
-
args = get_config_node(obj)
|
|
138
|
-
return cls(**{**args, "source": source})
|
|
139
|
-
|
|
140
|
-
return cls(**{k: v for k, v in obj.items() if k != NODE_TYPE_FIELD_NAME})
|
|
85
|
+
args = get_config_node(obj)
|
|
86
|
+
return cls(**{**args, "source": source})
|
|
141
87
|
|
|
142
88
|
def _api_create_input(self, _namespace: str, ctx: "DiffContext") -> dict[str, Any]:
|
|
143
89
|
return _api_create_input_params(
|
|
@@ -145,9 +91,6 @@ class Filter(Resource):
|
|
|
145
91
|
overrides={"sourceId": ctx.sources[self.source_name]._must_id()},
|
|
146
92
|
)
|
|
147
93
|
|
|
148
|
-
def _api_json_filter_expression_create_input(self) -> dict[str, Any]:
|
|
149
|
-
return {"__typename": f"{self.__class__.__name__}Expression", **self._scalar()}
|
|
150
|
-
|
|
151
94
|
|
|
152
95
|
class BooleanFilterOperator(str, Enum):
|
|
153
96
|
"""
|
|
@@ -172,8 +115,8 @@ class BooleanFilter(Filter):
|
|
|
172
115
|
*,
|
|
173
116
|
field: str,
|
|
174
117
|
operator: BooleanFilterOperator,
|
|
175
|
-
name: str
|
|
176
|
-
source:
|
|
118
|
+
name: str,
|
|
119
|
+
source: "Source",
|
|
177
120
|
display_name: str | None = None,
|
|
178
121
|
ignore_changes: bool = False,
|
|
179
122
|
):
|
|
@@ -231,9 +174,9 @@ class NullFilter(Filter):
|
|
|
231
174
|
self,
|
|
232
175
|
*,
|
|
233
176
|
field: str,
|
|
177
|
+
name: str,
|
|
178
|
+
source: "Source",
|
|
234
179
|
operator: NullFilterOperator = NullFilterOperator.IS,
|
|
235
|
-
name: str | None = None,
|
|
236
|
-
source: Optional["Source"] = None,
|
|
237
180
|
display_name: str | None = None,
|
|
238
181
|
ignore_changes: bool = False,
|
|
239
182
|
):
|
|
@@ -265,9 +208,6 @@ class NullFilter(Filter):
|
|
|
265
208
|
def _mutable_fields(self) -> set[str]:
|
|
266
209
|
return {"field", "operator", *super()._mutable_fields()}
|
|
267
210
|
|
|
268
|
-
def _scalar(self) -> dict[str, object]:
|
|
269
|
-
return {"field": self.field, "operator": self.operator.value}
|
|
270
|
-
|
|
271
211
|
|
|
272
212
|
class EnumFilterOperator(str, Enum):
|
|
273
213
|
"""
|
|
@@ -292,9 +232,9 @@ class EnumFilter(Filter):
|
|
|
292
232
|
*,
|
|
293
233
|
field: str,
|
|
294
234
|
values: list[str],
|
|
235
|
+
name: str,
|
|
236
|
+
source: "Source",
|
|
295
237
|
operator: EnumFilterOperator = EnumFilterOperator.ALLOW,
|
|
296
|
-
name: str | None = None,
|
|
297
|
-
source: Optional["Source"] = None,
|
|
298
238
|
display_name: str | None = None,
|
|
299
239
|
ignore_changes: bool = False,
|
|
300
240
|
):
|
|
@@ -375,9 +315,9 @@ class StringFilter(Filter):
|
|
|
375
315
|
*,
|
|
376
316
|
field: str,
|
|
377
317
|
operator: StringFilterOperator,
|
|
318
|
+
name: str,
|
|
319
|
+
source: "Source",
|
|
378
320
|
value: str | None = None,
|
|
379
|
-
name: str | None = None,
|
|
380
|
-
source: Optional["Source"] = None,
|
|
381
321
|
display_name: str | None = None,
|
|
382
322
|
ignore_changes: bool = False,
|
|
383
323
|
):
|
|
@@ -413,13 +353,6 @@ class StringFilter(Filter):
|
|
|
413
353
|
def _mutable_fields(self) -> set[str]:
|
|
414
354
|
return {"field", "operator", "value", *super()._mutable_fields()}
|
|
415
355
|
|
|
416
|
-
def _scalar(self) -> dict[str, object]:
|
|
417
|
-
return {
|
|
418
|
-
"field": self.field,
|
|
419
|
-
"value": self.value,
|
|
420
|
-
"operator": self.operator.value,
|
|
421
|
-
}
|
|
422
|
-
|
|
423
356
|
|
|
424
357
|
class ThresholdFilterOperator(str, Enum):
|
|
425
358
|
"""
|
|
@@ -452,9 +385,9 @@ class ThresholdFilter(Filter):
|
|
|
452
385
|
*,
|
|
453
386
|
field: str,
|
|
454
387
|
value: float,
|
|
388
|
+
name: str,
|
|
389
|
+
source: "Source",
|
|
455
390
|
operator: ThresholdFilterOperator,
|
|
456
|
-
name: str | None = None,
|
|
457
|
-
source: Optional["Source"] = None,
|
|
458
391
|
display_name: str | None = None,
|
|
459
392
|
ignore_changes: bool = False,
|
|
460
393
|
):
|
|
@@ -489,13 +422,6 @@ class ThresholdFilter(Filter):
|
|
|
489
422
|
def _mutable_fields(self) -> set[str]:
|
|
490
423
|
return {"field", "operator", "value", *super()._mutable_fields()}
|
|
491
424
|
|
|
492
|
-
def _scalar(self) -> dict[str, object]:
|
|
493
|
-
return {
|
|
494
|
-
"field": self.field,
|
|
495
|
-
"value": self.value,
|
|
496
|
-
"operator": self.operator.value,
|
|
497
|
-
}
|
|
498
|
-
|
|
499
425
|
|
|
500
426
|
class SqlFilter(Filter):
|
|
501
427
|
"""A SQL filter configuration.
|
|
@@ -507,8 +433,8 @@ class SqlFilter(Filter):
|
|
|
507
433
|
self,
|
|
508
434
|
*,
|
|
509
435
|
query: str,
|
|
510
|
-
name: str
|
|
511
|
-
source:
|
|
436
|
+
name: str,
|
|
437
|
+
source: "Source",
|
|
512
438
|
display_name: str | None = None,
|
|
513
439
|
ignore_changes: bool = False,
|
|
514
440
|
):
|
|
@@ -535,6 +461,3 @@ class SqlFilter(Filter):
|
|
|
535
461
|
|
|
536
462
|
def _mutable_fields(self) -> set[str]:
|
|
537
463
|
return {"query", *super()._mutable_fields()}
|
|
538
|
-
|
|
539
|
-
def _scalar(self) -> dict[str, object]:
|
|
540
|
-
return {"query": self.query}
|