validio-sdk 5.3.0__tar.gz → 6.0.0__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.
Files changed (56) hide show
  1. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/PKG-INFO +1 -1
  2. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/pyproject.toml +1 -1
  3. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/_api/api.py +78 -8
  4. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/code/_import.py +2 -1
  5. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/code/plan.py +1 -5
  6. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_diff.py +46 -37
  7. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_resource.py +2 -1
  8. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_server_resources.py +12 -8
  9. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/channels.py +17 -94
  10. validio_sdk-6.0.0/validio_sdk/resource/enums.py +12 -0
  11. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/filters.py +26 -103
  12. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/sources.py +115 -3
  13. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/test__diff.py +3 -2
  14. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/test__plan.py +0 -54
  15. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/test__resource.py +19 -9
  16. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/test_import.py +17 -4
  17. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/thresholds.py +37 -10
  18. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/validators.py +139 -129
  19. validio_sdk-5.3.0/validio_sdk/resource/tests/test__channel.py +0 -137
  20. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/LICENSE +0 -0
  21. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/README_PUBLIC.md +0 -0
  22. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/__init__.py +0 -0
  23. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/_api/__init__.py +0 -0
  24. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/client/__init__.py +0 -0
  25. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/client/client.py +0 -0
  26. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/code/__init__.py +0 -0
  27. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/code/_progress.py +0 -0
  28. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/code/apply.py +0 -0
  29. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/code/scaffold.py +0 -0
  30. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/code/settings.py +0 -0
  31. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/config.py +0 -0
  32. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/dbt.py +0 -0
  33. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/exception.py +0 -0
  34. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/metadata.py +0 -0
  35. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/py.typed +0 -0
  36. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/__init__.py +0 -0
  37. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_diff_util.py +0 -0
  38. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_diffable.py +0 -0
  39. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_errors.py +0 -0
  40. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_resource_graph.py +0 -0
  41. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_serde.py +0 -0
  42. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_update_namespace.py +0 -0
  43. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/_util.py +0 -0
  44. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/credentials.py +0 -0
  45. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/notification_rules.py +0 -0
  46. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/replacement.py +0 -0
  47. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/segmentations.py +0 -0
  48. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tags.py +0 -0
  49. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/__init__.py +0 -0
  50. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/assets/example_manifest.json +0 -0
  51. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/assets/expected_trimmed_manifest.json +0 -0
  52. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/test__dbt.py +0 -0
  53. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/tests/test__sql_validation.py +0 -0
  54. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/resource/windows.py +0 -0
  55. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/scalars.py +0 -0
  56. {validio_sdk-5.3.0 → validio_sdk-6.0.0}/validio_sdk/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: validio-sdk
3
- Version: 5.3.0
3
+ Version: 6.0.0
4
4
  Summary: SDK to interact with the Validio platform
5
5
  Home-page: https://validio.io/
6
6
  License: Apache-2.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.0"
6
+ version = "6.0.0"
7
7
  description = "SDK to interact with the Validio platform"
8
8
  authors = ["Validio <support@validio.io>"]
9
9
  license = "Apache-2.0"
@@ -1,10 +1,11 @@
1
1
  import os
2
2
  from datetime import datetime
3
3
  from enum import Enum
4
- from typing import Any
4
+ from typing import Any, Callable
5
5
 
6
6
  from aiohttp.client_exceptions import ClientConnectorError
7
7
  from gql import gql
8
+ from gql.transport.aiohttp import AIOHTTPTransport
8
9
  from gql.transport.exceptions import TransportQueryError, TransportServerError
9
10
  from graphql import DocumentNode, FieldNode, utilities
10
11
 
@@ -22,6 +23,7 @@ from validio_sdk.exception import (
22
23
  )
23
24
 
24
25
  NUM_CONCURRENT_CONNECTIONS = 5
26
+ DEFAULT_TIMEOUT_SECONDS = 600 # 10 min
25
27
 
26
28
 
27
29
  class APIClient:
@@ -31,9 +33,38 @@ class APIClient:
31
33
  # We use a somewhat higher timeout that the default 10 seconds to
32
34
  # account for longer running operations.
33
35
  self.client.client.execute_timeout = int(
34
- os.environ.get("VALIDIO_TIMEOUT_SECONDS", "60")
36
+ os.environ.get("VALIDIO_TIMEOUT_SECONDS", DEFAULT_TIMEOUT_SECONDS)
35
37
  )
36
38
 
39
+ def set_timeout(self, timeout: int | None) -> Callable:
40
+ original_execute_timeout = self.client.client.execute_timeout
41
+ original_transport_timeout = None
42
+
43
+ if isinstance(self.client.client.transport, AIOHTTPTransport):
44
+ original_transport_timeout = self.client.client.transport.timeout
45
+
46
+ # When creating a session we're connecting to the destination so we have
47
+ # to do this on the client level and can't defer it to the session.
48
+ #
49
+ # This timeout only affects request timeout and not connect timeout when
50
+ # trying to connect to the destination.
51
+ self.client.client.execute_timeout = timeout
52
+
53
+ # This timeout affects connection timeouts and when we should give up
54
+ # when trying to connect to the destination.
55
+ if isinstance(self.client.client.transport, AIOHTTPTransport):
56
+ self.client.client.transport.timeout = timeout
57
+
58
+ # To not have to return and handle multiple values we just return a
59
+ # function that can be used to restore them.
60
+ def reset_values() -> None:
61
+ self.client.client.execute_timeout = original_execute_timeout
62
+
63
+ if isinstance(self.client.client.transport, AIOHTTPTransport):
64
+ self.client.client.transport.timeout = original_transport_timeout
65
+
66
+ return reset_values
67
+
37
68
  async def execute(self, query: str, **kwargs: Any) -> Any:
38
69
  async with self.client as session:
39
70
  return await execute(session, query, **kwargs)
@@ -68,8 +99,12 @@ class APIClient:
68
99
  return await infer_schema(session, class_name, variable_values, no_args)
69
100
 
70
101
  async def test_connection(self) -> None:
102
+ reset_timeout_values = self.set_timeout(30)
103
+
71
104
  async with self.client as session:
72
- return await test_connection(session)
105
+ await test_connection(session)
106
+
107
+ reset_timeout_values()
73
108
 
74
109
  async def test_credential(self, variable_values: dict[str, Any]) -> None:
75
110
  async with self.client as session:
@@ -289,6 +324,13 @@ class APIClient:
289
324
  async with self.client as session:
290
325
  return await get_users(session, user_id)
291
326
 
327
+ async def users_by_emails(
328
+ self,
329
+ emails: list[str],
330
+ ) -> dict[str, str]:
331
+ async with self.client as session:
332
+ return await users_by_emails(session, emails)
333
+
292
334
  async def get_tags(
293
335
  self,
294
336
  ) -> list[dict[str, Any]] | dict[str, Any]:
@@ -367,6 +409,10 @@ async def get_sources(
367
409
  key
368
410
  value
369
411
  }
412
+ priority
413
+ owner {
414
+ email
415
+ }
370
416
  ... on GcpBigQuerySource {
371
417
  config {
372
418
  project
@@ -1194,7 +1240,6 @@ async def get_channels(
1194
1240
  }
1195
1241
  ... on SlackChannel {
1196
1242
  config {
1197
- slackWebhookUrl: webhookUrl
1198
1243
  applicationLinkUrl
1199
1244
  slackChannelId
1200
1245
  interactiveMessageEnabled
@@ -1207,7 +1252,6 @@ async def get_channels(
1207
1252
  }
1208
1253
  ... on MsTeamsChannel {
1209
1254
  config {
1210
- msTeamsWebhookUrl: webhookUrl
1211
1255
  applicationLinkUrl
1212
1256
  msTeamsChannelId
1213
1257
  msTeamsInteractiveMessageEnabled: interactiveMessageEnabled
@@ -1276,8 +1320,6 @@ async def get_channels(
1276
1320
  _replace_aliases(
1277
1321
  ftr["config"],
1278
1322
  [
1279
- ("slackWebhookUrl", "webhookUrl"),
1280
- ("msTeamsWebhookUrl", "webhookUrl"),
1281
1323
  ("msTeamsInteractiveMessageEnabled", "interactiveMessageEnabled"),
1282
1324
  ("emailInteractiveMessageEnabled", "interactiveMessageEnabled"),
1283
1325
  ],
@@ -1739,7 +1781,6 @@ async def get_validators(
1739
1781
  sensitivity
1740
1782
  decisionBoundsType
1741
1783
  adaptionRate
1742
- algorithm
1743
1784
  }}
1744
1785
  ... on DifferenceThreshold {{
1745
1786
  differenceOperator: operator
@@ -1760,6 +1801,9 @@ async def get_validators(
1760
1801
  namespace {{
1761
1802
  id
1762
1803
  }}
1804
+ owner {{
1805
+ email
1806
+ }}
1763
1807
  sourceConfig {{
1764
1808
  filter
1765
1809
  segmentation {{
@@ -1784,6 +1828,7 @@ async def get_validators(
1784
1828
  key
1785
1829
  value
1786
1830
  }}
1831
+ priority
1787
1832
  ... on NumericValidator {{
1788
1833
  config {{
1789
1834
  sourceField
@@ -2422,6 +2467,31 @@ async def get_users(
2422
2467
  return await execute(session, query, variable_values=variable_values)
2423
2468
 
2424
2469
 
2470
+ async def users_by_emails(
2471
+ session: Session,
2472
+ emails: list[str],
2473
+ ) -> dict[str, str]:
2474
+ if not emails:
2475
+ return {}
2476
+
2477
+ query = """
2478
+ query UserByEmails($emails: [String!]!) {
2479
+ usersByEmails(emails: $emails) {
2480
+ id
2481
+ email
2482
+ }
2483
+ }
2484
+ """
2485
+
2486
+ result = await execute(
2487
+ session,
2488
+ query,
2489
+ variable_values={"emails": emails},
2490
+ )
2491
+
2492
+ return {x["email"]: x["id"] for x in result}
2493
+
2494
+
2425
2495
  async def get_tags(
2426
2496
  session: Session,
2427
2497
  ) -> list[dict[str, Any]] | dict[str, Any]:
@@ -69,6 +69,7 @@ class ImportContext:
69
69
  imports = (
70
70
  [
71
71
  "from validio_sdk import *",
72
+ "from validio_sdk.resource.enums import *",
72
73
  "from validio_sdk.resource.thresholds import *",
73
74
  ]
74
75
  + [
@@ -277,7 +278,7 @@ async def _import(
277
278
  ("tags", resource_tags(r), None),
278
279
  ]
279
280
 
280
- if v._filter_resource_mode() and v.filter_name:
281
+ if v.filter_name:
281
282
  filter_ = import_ctx.get_variable(Filter, v.filter_name)
282
283
  inits.append(("filter", filter_, None))
283
284
 
@@ -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 Channel, WebhookChannel
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
@@ -12,7 +12,11 @@ from typing import (
12
12
  from camel_converter import to_snake
13
13
  from gql.transport.exceptions import TransportQueryError
14
14
 
15
- from validio_sdk._api.api import secrets_changed_by_field, split_to_n_chunks
15
+ from validio_sdk._api.api import (
16
+ secrets_changed_by_field,
17
+ split_to_n_chunks,
18
+ users_by_emails,
19
+ )
16
20
  from validio_sdk.client import Session
17
21
  from validio_sdk.exception import ValidioBugError, ValidioError
18
22
  from validio_sdk.resource._diffable import (
@@ -26,7 +30,6 @@ from validio_sdk.resource._errors import (
26
30
  )
27
31
  from validio_sdk.resource._resource import CREATE_ONLY_RESOURCES, DiffContext, Resource
28
32
  from validio_sdk.resource._util import SourceSchemaReinference, _sanitize_error
29
- from validio_sdk.resource.channels import Channel
30
33
  from validio_sdk.resource.replacement import (
31
34
  CascadeReplacementReason,
32
35
  ImmutableFieldReplacementReason,
@@ -36,7 +39,7 @@ from validio_sdk.resource.replacement import (
36
39
  from validio_sdk.resource.segmentations import Segmentation
37
40
  from validio_sdk.resource.sources import Source
38
41
  from validio_sdk.resource.tags import Tag
39
- from validio_sdk.resource.validators import Reference, Validator
42
+ from validio_sdk.resource.validators import Validator
40
43
  from validio_sdk.resource.windows import TumblingWindow
41
44
 
42
45
  if TYPE_CHECKING:
@@ -190,6 +193,8 @@ async def diff_resource_graph(
190
193
  show_secrets=show_secrets,
191
194
  )
192
195
 
196
+ await create_email_lookup_table(manifest_ctx, session)
197
+
193
198
  _compute_replacements(
194
199
  manifest_ctx=manifest_ctx,
195
200
  server_ctx=server_ctx,
@@ -254,8 +259,7 @@ def _visit_resource_to_replace(
254
259
  to_delete: dict[str, Resource] = getattr(graph_diff.to_delete, resource_type)
255
260
 
256
261
  # Remove the resource from the update list.
257
- if resource_name in to_update:
258
- del to_update[resource_name]
262
+ to_update.pop(resource_name, None)
259
263
 
260
264
  # Flag the resource to be created.
261
265
  manifest_resources: dict[str, Resource] = getattr(manifest_ctx, resource_type)
@@ -566,23 +570,6 @@ def diff(
566
570
  if isinstance(manifest_object, Resource) and manifest_object.ignore_changes:
567
571
  return None
568
572
 
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
573
  if isinstance(server_resource, Segmentation) and isinstance(
587
574
  manifest_resource, Segmentation
588
575
  ):
@@ -642,20 +629,6 @@ def diff(
642
629
  server = getattr(server_object, field)
643
630
 
644
631
  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
632
  # Whenever we find there's a change to make, grab all fields on both
660
633
  # resource versions so that we can present them as a diff.
661
634
  # Regardless of at what depth we find a change, the diff we present
@@ -664,7 +637,6 @@ def diff(
664
637
  return ResourceUpdate(
665
638
  manifest_resource,
666
639
  server_resource,
667
- secret_fields_changed=secret_fields_changed,
668
640
  )
669
641
 
670
642
  # Some fields require the resource to be re-created if the type changes
@@ -773,6 +745,43 @@ async def enrich_resource_graph(
773
745
  )
774
746
 
775
747
 
748
+ async def create_email_lookup_table(
749
+ manifest_ctx: DiffContext,
750
+ session: Session,
751
+ ) -> None:
752
+ emails_to_lookup = set()
753
+
754
+ # Only sources and validators can have owners.
755
+ for field in ["sources", "validators"]:
756
+ manifest_resources = getattr(manifest_ctx, field)
757
+
758
+ # Add all owner email addresses from the manifest (create and update) to
759
+ # the lookup set for lookup.
760
+ for resource in manifest_resources.values():
761
+ if not isinstance(resource, (Source, Validator)):
762
+ continue
763
+
764
+ if resource.owner is not None:
765
+ emails_to_lookup.add(resource.owner)
766
+
767
+ if not emails_to_lookup:
768
+ return
769
+
770
+ user_email_ids = await users_by_emails(session, list(emails_to_lookup))
771
+
772
+ if len(emails_to_lookup) != len(user_email_ids):
773
+ missing_emails = [x for x in emails_to_lookup if x not in user_email_ids]
774
+ owner_or_owners = "owner" if len(missing_emails) == 1 else "owners"
775
+ address_or_addresses = "address" if len(missing_emails) == 1 else "addresses"
776
+
777
+ raise ValidioError(
778
+ f"Invalid {owner_or_owners}, unknown email {address_or_addresses}: "
779
+ f"{', '.join(missing_emails)}"
780
+ )
781
+
782
+ manifest_ctx.user_email_ids = user_email_ids
783
+
784
+
776
785
  async def check_for_source_schema_changes(
777
786
  manifest_ctx: DiffContext,
778
787
  server_ctx: DiffContext,
@@ -68,13 +68,14 @@ class DiffContext:
68
68
  pending_validators_raw: dict[str, tuple[type, dict[str, Any]]] = dataclasses.field(
69
69
  default_factory=dict
70
70
  )
71
+ user_email_ids: dict[str, str] = dataclasses.field(default_factory=dict)
71
72
 
72
73
  @staticmethod
73
74
  def fields() -> list[str]:
74
75
  return [
75
76
  f
76
77
  for f in list(inspect.signature(DiffContext).parameters)
77
- if f != "pending_validators_raw"
78
+ if f not in ["pending_validators_raw", "user_email_ids"]
78
79
  ]
79
80
 
80
81
  @staticmethod
@@ -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=unset_or_none,
513
- client_secret=unset_or_none,
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=unset_or_none,
525
- signing_secret=unset_or_none,
526
- app_token=unset_or_none,
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
  )
@@ -606,6 +603,7 @@ async def load_sources(
606
603
  for s in server_sources:
607
604
  name = s["resourceName"]
608
605
  source_config = s.get("config", {})
606
+ owner = s.get("owner") or {}
609
607
 
610
608
  cls = eval(f"validio_sdk.resource.sources.{s['__typename']}")
611
609
  source = cls(
@@ -613,11 +611,13 @@ async def load_sources(
613
611
  **{to_snake(k): v for k, v in source_config.items()},
614
612
  "name": name,
615
613
  "display_name": s["name"],
614
+ "owner": owner.get("email"),
616
615
  "credential": must_find_credential(
617
616
  ctx, s["credential"]["resourceName"]
618
617
  ),
619
618
  "jtd_schema": s["jtdSchema"],
620
619
  "description": s["description"],
620
+ "priority": s["priority"],
621
621
  "tags": [
622
622
  must_find_tag(ctx, Tag._unique_name(t["key"], t["value"]))
623
623
  for t in s["tags"]
@@ -765,7 +765,9 @@ async def load_validators(
765
765
  name = v["resourceName"]
766
766
  display_name = v["name"]
767
767
  description = v["description"]
768
+ priority = v["priority"]
768
769
  config = v["config"]
770
+ owner = v.get("owner") or {}
769
771
 
770
772
  window = must_find_window(ctx, v["sourceConfig"]["window"]["resourceName"])
771
773
  segmentation = must_find_segmentation(
@@ -808,7 +810,9 @@ async def load_validators(
808
810
  "window": window,
809
811
  "segmentation": segmentation,
810
812
  "display_name": display_name,
813
+ "owner": owner.get("email"),
811
814
  "description": description,
815
+ "priority": priority,
812
816
  "tags": [
813
817
  must_find_tag(ctx, Tag._unique_name(t["key"], t["value"]))
814
818
  for t in v["tags"]