dasl-client 1.0.7__tar.gz → 1.0.11__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.
Potentially problematic release.
This version of dasl-client might be problematic. Click here for more details.
- {dasl_client-1.0.7 → dasl_client-1.0.11}/PKG-INFO +3 -2
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/auth/auth.py +6 -5
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/client.py +45 -9
- dasl_client-1.0.11/dasl_client/preset_development/__init__.py +4 -0
- dasl_client-1.0.11/dasl_client/preset_development/errors.py +159 -0
- dasl_client-1.0.11/dasl_client/preset_development/preview_engine.py +344 -0
- dasl_client-1.0.11/dasl_client/preset_development/preview_parameters.py +386 -0
- dasl_client-1.0.11/dasl_client/preset_development/stage.py +559 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/__init__.py +1 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/admin_config.py +10 -7
- dasl_client-1.0.11/dasl_client/types/content.py +235 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/datasource.py +177 -138
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/dbui.py +46 -34
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/rule.py +91 -65
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/types.py +67 -54
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/workspace_config.py +90 -74
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client.egg-info/PKG-INFO +3 -2
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client.egg-info/SOURCES.txt +6 -0
- dasl_client-1.0.11/dasl_client.egg-info/requires.txt +4 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/pyproject.toml +4 -3
- dasl_client-1.0.7/dasl_client.egg-info/requires.txt +0 -3
- {dasl_client-1.0.7 → dasl_client-1.0.11}/LICENSE +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/README.md +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/__init__.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/auth/__init__.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/conn/__init__.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/conn/client_identifier.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/conn/conn.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/errors/__init__.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/errors/errors.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/helpers.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client/types/helpers.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client.egg-info/dependency_links.txt +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/dasl_client.egg-info/top_level.txt +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/setup.cfg +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/setup.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/test/test_api_changes.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/test/test_api_surface.py +0 -0
- {dasl_client-1.0.7 → dasl_client-1.0.11}/test/test_marshaling.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dasl_client
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.11
|
|
4
4
|
Summary: The DASL client library used for interacting with the DASL workspace
|
|
5
5
|
Home-page: https://github.com/antimatter/asl
|
|
6
6
|
Author: Antimatter Team
|
|
@@ -8,9 +8,10 @@ Author-email: Antimatter Team <support@antimatter.io>
|
|
|
8
8
|
Requires-Python: >=3.8
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Dist: dasl_api==0.1.
|
|
11
|
+
Requires-Dist: dasl_api==0.1.14
|
|
12
12
|
Requires-Dist: databricks-sdk>=0.41.0
|
|
13
13
|
Requires-Dist: pydantic>=2
|
|
14
|
+
Requires-Dist: typing_extensions==4.10.0
|
|
14
15
|
|
|
15
16
|
# DASL Client Library
|
|
16
17
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import base64
|
|
2
3
|
import time
|
|
3
4
|
from datetime import datetime
|
|
4
5
|
|
|
@@ -176,15 +177,15 @@ class DatabricksSecretAuth(Authorization):
|
|
|
176
177
|
principalName=self._principal,
|
|
177
178
|
)
|
|
178
179
|
handler = api.WorkspaceV1Api(api_client=self._client)
|
|
179
|
-
resp = handler.
|
|
180
|
-
workspace=self._workspace,
|
|
180
|
+
resp = handler.workspace_v1_request_secret(
|
|
181
|
+
workspace=self._workspace, workspace_v1_request_secret_request=req
|
|
181
182
|
)
|
|
182
183
|
secret_name = resp.secret_name
|
|
183
184
|
secret_value = ""
|
|
184
185
|
for tries in range(3):
|
|
185
186
|
try:
|
|
186
|
-
secret_value =
|
|
187
|
-
secret_name, "token"
|
|
187
|
+
secret_value = (
|
|
188
|
+
WorkspaceClient().secrets.get_secret(secret_name, "token").value
|
|
188
189
|
)
|
|
189
190
|
break
|
|
190
191
|
except ResourceDoesNotExist:
|
|
@@ -194,7 +195,7 @@ class DatabricksSecretAuth(Authorization):
|
|
|
194
195
|
raise RuntimeError(f"failed to complete secret auth")
|
|
195
196
|
|
|
196
197
|
req = WorkspaceV1AuthenticateRequest(
|
|
197
|
-
databricks_secret=secret_value,
|
|
198
|
+
databricks_secret=base64.b64decode(secret_value).decode("utf-8"),
|
|
198
199
|
)
|
|
199
200
|
handler = api.WorkspaceV1Api(api_client=self._client)
|
|
200
201
|
|
|
@@ -3,8 +3,8 @@ from typing import Any, Callable, Iterator, List, Optional, TypeVar
|
|
|
3
3
|
|
|
4
4
|
from dasl_api import (
|
|
5
5
|
CoreV1Api,
|
|
6
|
-
CoreV1DataSourceAutoloaderSpec,
|
|
7
6
|
DbuiV1Api,
|
|
7
|
+
ContentV1Api,
|
|
8
8
|
WorkspaceV1Api,
|
|
9
9
|
WorkspaceV1CreateWorkspaceRequest,
|
|
10
10
|
api,
|
|
@@ -13,6 +13,7 @@ from pydantic import Field
|
|
|
13
13
|
|
|
14
14
|
from dasl_client.auth.auth import (
|
|
15
15
|
Authorization,
|
|
16
|
+
DatabricksSecretAuth,
|
|
16
17
|
DatabricksTokenAuth,
|
|
17
18
|
ServiceAccountKeyAuth,
|
|
18
19
|
)
|
|
@@ -29,6 +30,8 @@ from .types import (
|
|
|
29
30
|
WorkspaceConfig,
|
|
30
31
|
TransformRequest,
|
|
31
32
|
TransformResponse,
|
|
33
|
+
DataSourcePresetsList,
|
|
34
|
+
DataSourcePreset,
|
|
32
35
|
)
|
|
33
36
|
|
|
34
37
|
|
|
@@ -51,7 +54,7 @@ class Client:
|
|
|
51
54
|
|
|
52
55
|
:param auth: Authorization instance for authorizing requests to
|
|
53
56
|
the dasl control plane.
|
|
54
|
-
:returns Client
|
|
57
|
+
:returns: Client
|
|
55
58
|
"""
|
|
56
59
|
self.auth = auth
|
|
57
60
|
|
|
@@ -83,7 +86,7 @@ class Client:
|
|
|
83
86
|
:param dasl_host: The URL of the DASL server. This value should
|
|
84
87
|
not generally be specified unless you are testing against
|
|
85
88
|
an alternative environment.
|
|
86
|
-
:returns Client
|
|
89
|
+
:returns: Client for the newly created workspace.
|
|
87
90
|
"""
|
|
88
91
|
with error_handler():
|
|
89
92
|
if workspace_url is None:
|
|
@@ -131,7 +134,7 @@ class Client:
|
|
|
131
134
|
:param dasl_host: The URL of the DASL server. This value should
|
|
132
135
|
not generally be specified unless you are testing against
|
|
133
136
|
an alternative environment.
|
|
134
|
-
:returns Client
|
|
137
|
+
:returns: Client for the existing workspace.
|
|
135
138
|
"""
|
|
136
139
|
with error_handler():
|
|
137
140
|
if workspace_url is None:
|
|
@@ -195,7 +198,7 @@ class Client:
|
|
|
195
198
|
:param dasl_host: The URL of the DASL server. This value should
|
|
196
199
|
not generally be specified unless you are testing against
|
|
197
200
|
an alternative environment.
|
|
198
|
-
:returns Client
|
|
201
|
+
:returns: Client for the newly created or existing workspace.
|
|
199
202
|
"""
|
|
200
203
|
try:
|
|
201
204
|
return Client.new_workspace(
|
|
@@ -229,6 +232,9 @@ class Client:
|
|
|
229
232
|
def _dbui_client(self) -> DbuiV1Api:
|
|
230
233
|
return DbuiV1Api(self.auth.client())
|
|
231
234
|
|
|
235
|
+
def _content_client(self) -> ContentV1Api:
|
|
236
|
+
return ContentV1Api(self.auth.client())
|
|
237
|
+
|
|
232
238
|
def _workspace(self) -> str:
|
|
233
239
|
return self.auth.workspace()
|
|
234
240
|
|
|
@@ -274,7 +280,7 @@ class Client:
|
|
|
274
280
|
you will need to repopulate the service_principal_secret correctly
|
|
275
281
|
before passing the result back to put_admin_config.
|
|
276
282
|
|
|
277
|
-
:returns AdminConfig
|
|
283
|
+
:returns: AdminConfig containing the current settings.
|
|
278
284
|
"""
|
|
279
285
|
with error_handler():
|
|
280
286
|
return AdminConfig.from_api_obj(
|
|
@@ -306,6 +312,8 @@ class Client:
|
|
|
306
312
|
Retrieve the WorkspaceConfig from the DASL server. The returned
|
|
307
313
|
value can be updated directly and passed to put_config in order
|
|
308
314
|
to make changes.
|
|
315
|
+
|
|
316
|
+
:returns: WorkspaceConfig containing the current configuration.
|
|
309
317
|
"""
|
|
310
318
|
with error_handler():
|
|
311
319
|
return WorkspaceConfig.from_api_obj(
|
|
@@ -348,7 +356,7 @@ class Client:
|
|
|
348
356
|
in order to make changes.
|
|
349
357
|
|
|
350
358
|
:param name: The unique name of the DataSource within this workspace
|
|
351
|
-
:returns DataSource
|
|
359
|
+
:returns: DataSource
|
|
352
360
|
"""
|
|
353
361
|
with error_handler():
|
|
354
362
|
return DataSource.from_api_obj(
|
|
@@ -459,7 +467,7 @@ class Client:
|
|
|
459
467
|
in order to make changes.
|
|
460
468
|
|
|
461
469
|
:param name: The unique name of the Rule within this workspace
|
|
462
|
-
:returns Rule
|
|
470
|
+
:returns: Rule
|
|
463
471
|
"""
|
|
464
472
|
with error_handler():
|
|
465
473
|
return Rule.from_api_obj(
|
|
@@ -601,7 +609,7 @@ class Client:
|
|
|
601
609
|
:param value: The observable value
|
|
602
610
|
:param cursor: A cursor to be used when paginating results
|
|
603
611
|
:param limit: A limit of the number of results to return
|
|
604
|
-
:returns EventsList
|
|
612
|
+
:returns: EventsList
|
|
605
613
|
"""
|
|
606
614
|
with error_handler():
|
|
607
615
|
return Dbui.ObservableEvents.EventsList.from_api_obj(
|
|
@@ -614,3 +622,31 @@ class Client:
|
|
|
614
622
|
limit=limit,
|
|
615
623
|
)
|
|
616
624
|
)
|
|
625
|
+
|
|
626
|
+
def list_presets(self) -> DataSourcePresetsList:
|
|
627
|
+
"""
|
|
628
|
+
List the Presets in this workspace. This will include any user defined
|
|
629
|
+
presets if a custom presets path has been configured in the workspace.
|
|
630
|
+
|
|
631
|
+
:returns: DataSourcePresetsList
|
|
632
|
+
"""
|
|
633
|
+
with error_handler():
|
|
634
|
+
return DataSourcePresetsList.from_api_obj(
|
|
635
|
+
self._content_client().content_v1_get_preset_data_sources(
|
|
636
|
+
self._workspace(),
|
|
637
|
+
)
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
def get_preset(self, name: str) -> DataSourcePreset:
|
|
641
|
+
"""
|
|
642
|
+
Get the preset with the argument name from the DASL server. If the preset name
|
|
643
|
+
begins with 'internal_' it will instead be collected from the user catalog,
|
|
644
|
+
provided a preset path is set in the workspace config.
|
|
645
|
+
|
|
646
|
+
:param name: The unique name of the DataSource preset within this workspace.
|
|
647
|
+
:returns: DataSourcePreset
|
|
648
|
+
"""
|
|
649
|
+
with error_handler():
|
|
650
|
+
return DataSourcePreset.from_api_obj(
|
|
651
|
+
self._content_client().content_v1_get_preset_datasource(self._workspace(), name)
|
|
652
|
+
)
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from pyspark.sql import DataFrame, SparkSession
|
|
2
|
+
from pyspark.sql.types import *
|
|
3
|
+
from pyspark.sql.dataframe import DataFrame
|
|
4
|
+
from typing import Dict, Any, List, Mapping, Tuple
|
|
5
|
+
from IPython import get_ipython
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PresetError(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class InvalidGoldTableSchemaError(PresetError):
|
|
13
|
+
def __init__(self, schema: str, additional_message: str = ""):
|
|
14
|
+
self.schema = schema
|
|
15
|
+
message = (
|
|
16
|
+
f"Malformed gold schema provided {schema}. {additional_message}".strip()
|
|
17
|
+
)
|
|
18
|
+
super().__init__(message)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class NoSilverStageProvdedError(PresetError):
|
|
22
|
+
def __init__(self, additional_msg: str = ""):
|
|
23
|
+
message = f"No silver stage provided{additional_msg}."
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class NoSilverTransformStageProvdedError(PresetError):
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
message: str = "No silver transform stage provided, but gold stage is present.",
|
|
31
|
+
):
|
|
32
|
+
super().__init__(message)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PreTransformNotFound(PresetError):
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
message: str = "Requested silver pretransform name not found in preset's silver pretransforms.",
|
|
39
|
+
):
|
|
40
|
+
super().__init__(message)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class NoSilverPreTransformStageProvdedError(PresetError):
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
message: str = "No silver transform stage provided, but prestransform name provided.",
|
|
47
|
+
):
|
|
48
|
+
super().__init__(message)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class MissingTableFieldError(PresetError):
|
|
52
|
+
def __init__(self, layer: str, table_name: str, field_name: str):
|
|
53
|
+
self.layer = layer
|
|
54
|
+
self.table_name = table_name
|
|
55
|
+
self.field_name = field_name
|
|
56
|
+
message = f"{layer} stage {table_name} is missing {field_name} field."
|
|
57
|
+
super().__init__(message)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class DuplicateFieldNameError(PresetError):
|
|
61
|
+
def __init__(self, stage: str, stage_name: str, field_name: str):
|
|
62
|
+
self.stage = stage
|
|
63
|
+
self.stage_name = stage_name
|
|
64
|
+
self.field_name = field_name
|
|
65
|
+
message = f"Duplicate field specification name found in {stage} stage {stage_name} named {field_name}."
|
|
66
|
+
super().__init__(message)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class MalformedFieldError(PresetError):
|
|
70
|
+
def __init__(self, stage: str, stage_name: str, field_name: str):
|
|
71
|
+
self.stage = stage
|
|
72
|
+
self.stage_name = stage_name
|
|
73
|
+
self.field_name = field_name
|
|
74
|
+
message = f"Please provide 1 operation only in {stage} stage {stage_name}'s field specification named {field_name}."
|
|
75
|
+
super().__init__(message)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class MissingFieldNameError(PresetError):
|
|
79
|
+
def __init__(self, stage: str, stage_name: str):
|
|
80
|
+
self.stage = stage
|
|
81
|
+
self.stage_name = stage_name
|
|
82
|
+
message = (
|
|
83
|
+
f"Field specification in {stage} stage {stage_name} missing name field."
|
|
84
|
+
)
|
|
85
|
+
super().__init__(message)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class MissingSilverKeysError(PresetError):
|
|
89
|
+
def __init__(self, missing_keys: str):
|
|
90
|
+
self.missing_keys = missing_keys
|
|
91
|
+
message = f"Gold table/s have no corresponding input from silver table/s: {missing_keys}"
|
|
92
|
+
super().__init__(message)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class MissingAutoloaderConfigError(PresetError):
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
message: str = "Autoloader mode selected, but no autoloader configuration found in preset.autoloader.",
|
|
99
|
+
):
|
|
100
|
+
super().__init__(message)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class AutoloaderMissingFieldError(PresetError):
|
|
104
|
+
def __init__(self, field_name: str):
|
|
105
|
+
self.field_name = field_name
|
|
106
|
+
message = f"Autoloader mode selected, but missing field {field_name} in preset."
|
|
107
|
+
super().__init__(message)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class UnknownGoldTableError(PresetError):
|
|
111
|
+
def __init__(self, table_name: str, schema: str):
|
|
112
|
+
self.table_name = table_name
|
|
113
|
+
self.schema = schema
|
|
114
|
+
message = (
|
|
115
|
+
f"The referenced Gold table name {table_name} does not exist in {schema}."
|
|
116
|
+
)
|
|
117
|
+
super().__init__(message)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class GoldTableCompatibilityError(PresetError):
|
|
121
|
+
def __init__(self, message: str):
|
|
122
|
+
super().__init__(message)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class ReferencedColumnMissingError(PresetError):
|
|
126
|
+
def __init__(self, operation: str, column_name: str):
|
|
127
|
+
self.operation = operation
|
|
128
|
+
self.column_name = column_name
|
|
129
|
+
message = f"The referenced column {column_name} was not found in the dataframe during {operation} operation."
|
|
130
|
+
super().__init__(message)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class MissingJoinFieldError(PresetError):
|
|
134
|
+
def __init__(self, field_name: str):
|
|
135
|
+
self.field_name = field_name
|
|
136
|
+
message = f"Join operation is missing required field {field_name}."
|
|
137
|
+
super().__init__(message)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class MissingUtilityConfigurationFieldError(PresetError):
|
|
141
|
+
def __init__(self, operation: str, field_name: str):
|
|
142
|
+
self.operation = operation
|
|
143
|
+
self.field_name = field_name
|
|
144
|
+
message = f"The required configuration field {field_name} was not suppled in the {operation} operation."
|
|
145
|
+
super().__init__(message)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class AssertionFailedError(PresetError):
|
|
149
|
+
def __init__(self, expr: str, assertion_message: str, df: DataFrame):
|
|
150
|
+
# Get the Databricks built-in functions out the namespace.
|
|
151
|
+
ipython = get_ipython()
|
|
152
|
+
display = ipython.user_ns["display"]
|
|
153
|
+
|
|
154
|
+
self.expr = expr
|
|
155
|
+
self.assertion_message = assertion_message
|
|
156
|
+
self.df = df
|
|
157
|
+
message = f"The above rows failed the assertion expression {expr} with reason: {assertion_message}\n"
|
|
158
|
+
display(df)
|
|
159
|
+
super().__init__(message)
|