datamasque-python 1.1.7__tar.gz → 1.2.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.
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/HISTORY.rst +22 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/PKG-INFO +1 -1
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/__init__.py +72 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/discovery_config_libraries.py +14 -26
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/dmclient.py +2 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/discovery.py +8 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/discovery_config_library.py +2 -4
- datamasque_python-1.2.0/datamasque/client/models/safe_data_preview.py +282 -0
- datamasque_python-1.2.0/datamasque/client/models/table_reference.py +101 -0
- datamasque_python-1.2.0/datamasque/client/table_references.py +144 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/pyproject.toml +1 -1
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/setup.cfg +1 -1
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_discovery.py +20 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_discovery_config_libraries.py +12 -142
- datamasque_python-1.2.0/tests/test_safe_data_preview.py +178 -0
- datamasque_python-1.2.0/tests/test_table_references.py +502 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/uv.lock +1 -1
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/.editorconfig +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/.github/workflows/ci.yml +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/.github/workflows/release-testpypi.yml +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/.github/workflows/release.yml +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/.gitignore +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/.readthedocs.yaml +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/CONTRIBUTING.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/LICENSE +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/MANIFEST.in +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/Makefile +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/NOTICE +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/README.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/base.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/connections.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/discovery.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/discovery_configs.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/exceptions.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/files.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/ifm.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/license.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/__init__.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/connection.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/data_selection.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/discovery_config.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/dm_instance.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/files.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/git.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/ifm.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/license.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/pagination.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/ruleset.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/ruleset_library.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/runs.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/status.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/models/user.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/py.typed +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/ruleset_libraries.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/rulesets.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/runs.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/settings.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/spcs.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/users.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/Makefile +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/client.models.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/client.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/conf.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/contributing.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/history.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/index.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/installation.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/make.bat +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/modules.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/readme.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/docs/usage.rst +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/__init__.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/conftest.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/helpers.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_base.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_connections.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_discovery_configs.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_files.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_ifm.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_license.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_pagination.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_ruleset_library.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_rulesets.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_runs.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_settings.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_spcs.py +0 -0
- {datamasque_python-1.1.7 → datamasque_python-1.2.0}/tests/test_users.py +0 -0
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
History
|
|
3
3
|
=======
|
|
4
4
|
|
|
5
|
+
1.2.0 (2026-07-27)
|
|
6
|
+
------------------
|
|
7
|
+
|
|
8
|
+
* Made discovery config libraries untyped, matching the DataMasque 3.26.14 server. A single
|
|
9
|
+
library is now identified by ``(namespace, name)`` and may be imported by both database and
|
|
10
|
+
file discovery configs. **Breaking:** removed the ``config_type`` field from
|
|
11
|
+
``DiscoveryConfigLibrary`` and the ``config_type`` argument from
|
|
12
|
+
``get_discovery_config_library_by_name``, ``create_or_update_discovery_config_library``, and
|
|
13
|
+
``delete_discovery_config_library_by_name_if_exists``. Requires a 3.26.14 or later server.
|
|
14
|
+
|
|
15
|
+
1.1.8 (2026-07-27)
|
|
16
|
+
------------------
|
|
17
|
+
|
|
18
|
+
* Added typed Safe Data Preview support:
|
|
19
|
+
|
|
20
|
+
* ``safe_data_preview`` on ``InDataDiscoveryConfig`` (via ``SafeDataPreviewOptions``) to configure or disable it.
|
|
21
|
+
* ``safe_data_preview`` on schema-discovery result columns and file-discovery locators, typed by ``kind``.
|
|
22
|
+
|
|
23
|
+
* Added ``row_count`` to ``TableConstraints`` in schema-discovery ``table_metadata``.
|
|
24
|
+
* Added table-reference management APIs (``list_table_references``, ``create_table_reference``, and friends),
|
|
25
|
+
along with the ``TableReference`` and ``TableReferenceOptions`` models.
|
|
26
|
+
|
|
5
27
|
1.1.7 (2026-07-14)
|
|
6
28
|
------------------
|
|
7
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datamasque-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Official Python client for the DataMasque data-masking API.
|
|
5
5
|
Project-URL: Homepage, https://datamasque.com/
|
|
6
6
|
Project-URL: Documentation, https://datamasque-python.readthedocs.io/
|
|
@@ -116,6 +116,38 @@ from datamasque.client.models.runs import (
|
|
|
116
116
|
RunInfo,
|
|
117
117
|
UnfinishedRun,
|
|
118
118
|
)
|
|
119
|
+
from datamasque.client.models.safe_data_preview import (
|
|
120
|
+
BooleanPreview,
|
|
121
|
+
BooleanStatistics,
|
|
122
|
+
ColumnKind,
|
|
123
|
+
ColumnPreview,
|
|
124
|
+
CommonStatistics,
|
|
125
|
+
FirstCharsStatistics,
|
|
126
|
+
LengthEntry,
|
|
127
|
+
LengthsStatistics,
|
|
128
|
+
MaskedFormEntry,
|
|
129
|
+
NumericBin,
|
|
130
|
+
NumericHistograms,
|
|
131
|
+
NumericPreview,
|
|
132
|
+
NumericStatistics,
|
|
133
|
+
NumericSummaries,
|
|
134
|
+
NumericTemporalDisclosureLevel,
|
|
135
|
+
PatternComposition,
|
|
136
|
+
PatternEntry,
|
|
137
|
+
PatternsStatistics,
|
|
138
|
+
SafeDataPreview,
|
|
139
|
+
SafeDataPreviewOptions,
|
|
140
|
+
StringDisclosureLevel,
|
|
141
|
+
StringPreview,
|
|
142
|
+
StringStatistics,
|
|
143
|
+
TemporalBin,
|
|
144
|
+
TemporalHistograms,
|
|
145
|
+
TemporalPreview,
|
|
146
|
+
TemporalStatistics,
|
|
147
|
+
TemporalSummaries,
|
|
148
|
+
UnsupportedPreview,
|
|
149
|
+
UnsupportedStatistics,
|
|
150
|
+
)
|
|
119
151
|
from datamasque.client.models.status import (
|
|
120
152
|
AsyncRulesetGenerationTaskStatus,
|
|
121
153
|
MaskingRunStatus,
|
|
@@ -123,6 +155,12 @@ from datamasque.client.models.status import (
|
|
|
123
155
|
ValidationErrorType,
|
|
124
156
|
ValidationStatus,
|
|
125
157
|
)
|
|
158
|
+
from datamasque.client.models.table_reference import (
|
|
159
|
+
TableReference,
|
|
160
|
+
TableReferenceFormat,
|
|
161
|
+
TableReferenceId,
|
|
162
|
+
TableReferenceOptions,
|
|
163
|
+
)
|
|
126
164
|
from datamasque.client.models.user import User, UserId, UserRole
|
|
127
165
|
|
|
128
166
|
__version__ = version("datamasque-python")
|
|
@@ -131,6 +169,11 @@ __all__ = [
|
|
|
131
169
|
"AsyncRulesetGenerationInProgressError",
|
|
132
170
|
"AsyncRulesetGenerationTaskStatus",
|
|
133
171
|
"AzureConnectionConfig",
|
|
172
|
+
"BooleanPreview",
|
|
173
|
+
"BooleanStatistics",
|
|
174
|
+
"ColumnKind",
|
|
175
|
+
"ColumnPreview",
|
|
176
|
+
"CommonStatistics",
|
|
134
177
|
"ConnectionConfig",
|
|
135
178
|
"ConnectionId",
|
|
136
179
|
"ConstraintColumns",
|
|
@@ -171,6 +214,7 @@ __all__ = [
|
|
|
171
214
|
"FileId",
|
|
172
215
|
"FileOrContent",
|
|
173
216
|
"FileRulesetGenerationRequest",
|
|
217
|
+
"FirstCharsStatistics",
|
|
174
218
|
"ForeignKeyRef",
|
|
175
219
|
"GitSnapshot",
|
|
176
220
|
"HashColumnsTableConfig",
|
|
@@ -186,16 +230,28 @@ __all__ = [
|
|
|
186
230
|
"InvalidLibraryError",
|
|
187
231
|
"InvalidRulesetError",
|
|
188
232
|
"JsonPath",
|
|
233
|
+
"LengthEntry",
|
|
234
|
+
"LengthsStatistics",
|
|
189
235
|
"LicenseInfo",
|
|
190
236
|
"Locator",
|
|
191
237
|
"MaskType",
|
|
238
|
+
"MaskedFormEntry",
|
|
192
239
|
"MaskingRunOptions",
|
|
193
240
|
"MaskingRunRequest",
|
|
194
241
|
"MaskingRunStatus",
|
|
195
242
|
"MongoConnectionConfig",
|
|
196
243
|
"MountedShareConnectionConfig",
|
|
197
244
|
"MssqlLinkedServerConnectionConfig",
|
|
245
|
+
"NumericBin",
|
|
246
|
+
"NumericHistograms",
|
|
247
|
+
"NumericPreview",
|
|
248
|
+
"NumericStatistics",
|
|
249
|
+
"NumericSummaries",
|
|
250
|
+
"NumericTemporalDisclosureLevel",
|
|
198
251
|
"OracleWalletFile",
|
|
252
|
+
"PatternComposition",
|
|
253
|
+
"PatternEntry",
|
|
254
|
+
"PatternsStatistics",
|
|
199
255
|
"ReferencingForeignKey",
|
|
200
256
|
"Ruleset",
|
|
201
257
|
"RulesetGenerationRequest",
|
|
@@ -213,6 +269,8 @@ __all__ = [
|
|
|
213
269
|
"RunInfo",
|
|
214
270
|
"RunNotCancellableError",
|
|
215
271
|
"S3ConnectionConfig",
|
|
272
|
+
"SafeDataPreview",
|
|
273
|
+
"SafeDataPreviewOptions",
|
|
216
274
|
"SchemaDiscoveryColumn",
|
|
217
275
|
"SchemaDiscoveryFromConfigRequest",
|
|
218
276
|
"SchemaDiscoveryPage",
|
|
@@ -228,9 +286,23 @@ __all__ = [
|
|
|
228
286
|
"SseConfig",
|
|
229
287
|
"SseSelection",
|
|
230
288
|
"SslZipFile",
|
|
289
|
+
"StringDisclosureLevel",
|
|
290
|
+
"StringPreview",
|
|
291
|
+
"StringStatistics",
|
|
231
292
|
"SwitchableLicenseMetadata",
|
|
232
293
|
"TableConstraints",
|
|
294
|
+
"TableReference",
|
|
295
|
+
"TableReferenceFormat",
|
|
296
|
+
"TableReferenceId",
|
|
297
|
+
"TableReferenceOptions",
|
|
298
|
+
"TemporalBin",
|
|
299
|
+
"TemporalHistograms",
|
|
300
|
+
"TemporalPreview",
|
|
301
|
+
"TemporalStatistics",
|
|
302
|
+
"TemporalSummaries",
|
|
233
303
|
"UnfinishedRun",
|
|
304
|
+
"UnsupportedPreview",
|
|
305
|
+
"UnsupportedStatistics",
|
|
234
306
|
"User",
|
|
235
307
|
"UserId",
|
|
236
308
|
"UserRole",
|
{datamasque_python-1.1.7 → datamasque_python-1.2.0}/datamasque/client/discovery_config_libraries.py
RENAMED
|
@@ -3,7 +3,6 @@ from typing import Optional
|
|
|
3
3
|
|
|
4
4
|
from datamasque.client.base import BaseClient
|
|
5
5
|
from datamasque.client.exceptions import DataMasqueApiError
|
|
6
|
-
from datamasque.client.models.discovery_config import DiscoveryConfigType
|
|
7
6
|
from datamasque.client.models.discovery_config_library import DiscoveryConfigLibrary, DiscoveryConfigLibraryId
|
|
8
7
|
|
|
9
8
|
logger = logging.getLogger(__name__)
|
|
@@ -31,14 +30,12 @@ class DiscoveryConfigLibraryClient(BaseClient):
|
|
|
31
30
|
response = self.make_request("GET", f"/api/discovery/config-libraries/{library_id}/")
|
|
32
31
|
return DiscoveryConfigLibrary.model_validate(response.json())
|
|
33
32
|
|
|
34
|
-
def _get_discovery_config_library_id_by_name(
|
|
35
|
-
self, name: str, config_type: DiscoveryConfigType, namespace: str
|
|
36
|
-
) -> Optional[DiscoveryConfigLibraryId]:
|
|
33
|
+
def _get_discovery_config_library_id_by_name(self, name: str, namespace: str) -> Optional[DiscoveryConfigLibraryId]:
|
|
37
34
|
"""
|
|
38
|
-
Returns the ID of the library with the given name
|
|
35
|
+
Returns the ID of the library with the given name and namespace, or `None` if there is none.
|
|
39
36
|
|
|
40
37
|
The listing is filtered by name to keep the response small;
|
|
41
|
-
|
|
38
|
+
the namespace is matched client-side,
|
|
42
39
|
since the API's namespace filter cannot select the default (empty) namespace.
|
|
43
40
|
"""
|
|
44
41
|
|
|
@@ -48,11 +45,7 @@ class DiscoveryConfigLibraryClient(BaseClient):
|
|
|
48
45
|
params={"name_exact": name},
|
|
49
46
|
)
|
|
50
47
|
entries = [DiscoveryConfigLibrary.model_validate(item) for item in response.json()]
|
|
51
|
-
matches = [
|
|
52
|
-
entry
|
|
53
|
-
for entry in entries
|
|
54
|
-
if entry.name == name and entry.namespace == namespace and entry.config_type is config_type
|
|
55
|
-
]
|
|
48
|
+
matches = [entry for entry in entries if entry.name == name and entry.namespace == namespace]
|
|
56
49
|
if not matches:
|
|
57
50
|
return None
|
|
58
51
|
|
|
@@ -64,18 +57,15 @@ class DiscoveryConfigLibraryClient(BaseClient):
|
|
|
64
57
|
|
|
65
58
|
return matches[0].id
|
|
66
59
|
|
|
67
|
-
def get_discovery_config_library_by_name(
|
|
68
|
-
self, name: str, config_type: DiscoveryConfigType, namespace: str = ""
|
|
69
|
-
) -> Optional[DiscoveryConfigLibrary]:
|
|
60
|
+
def get_discovery_config_library_by_name(self, name: str, namespace: str = "") -> Optional[DiscoveryConfigLibrary]:
|
|
70
61
|
"""
|
|
71
|
-
Looks for a discovery config library matching the given name
|
|
62
|
+
Looks for a discovery config library matching the given name and namespace (case-sensitive, exact match).
|
|
72
63
|
|
|
73
|
-
Library names are unique
|
|
74
|
-
so a type is required to identify a single library.
|
|
64
|
+
Library names are unique within a namespace.
|
|
75
65
|
Returns it (with full YAML content) if found, otherwise `None`.
|
|
76
66
|
"""
|
|
77
67
|
|
|
78
|
-
library_id = self._get_discovery_config_library_id_by_name(name,
|
|
68
|
+
library_id = self._get_discovery_config_library_id_by_name(name, namespace)
|
|
79
69
|
if library_id is None:
|
|
80
70
|
return None
|
|
81
71
|
|
|
@@ -106,7 +96,6 @@ class DiscoveryConfigLibraryClient(BaseClient):
|
|
|
106
96
|
|
|
107
97
|
The library must have its `id` set (i.e., it must have been previously created or retrieved from the server)
|
|
108
98
|
and its `yaml` content present.
|
|
109
|
-
A library's `config_type` is fixed at creation and cannot be changed by an update.
|
|
110
99
|
"""
|
|
111
100
|
|
|
112
101
|
if library.id is None:
|
|
@@ -129,12 +118,12 @@ class DiscoveryConfigLibraryClient(BaseClient):
|
|
|
129
118
|
|
|
130
119
|
def create_or_update_discovery_config_library(self, library: DiscoveryConfigLibrary) -> DiscoveryConfigLibrary:
|
|
131
120
|
"""
|
|
132
|
-
Creates the library, or updates the existing one with the same name
|
|
121
|
+
Creates the library, or updates the existing one with the same name and namespace.
|
|
133
122
|
|
|
134
123
|
Sets the library's `id` property.
|
|
135
124
|
"""
|
|
136
125
|
|
|
137
|
-
library_id = self._get_discovery_config_library_id_by_name(library.name, library.
|
|
126
|
+
library_id = self._get_discovery_config_library_id_by_name(library.name, library.namespace)
|
|
138
127
|
if library_id is not None:
|
|
139
128
|
library.id = library_id
|
|
140
129
|
return self.update_discovery_config_library(library)
|
|
@@ -157,16 +146,15 @@ class DiscoveryConfigLibraryClient(BaseClient):
|
|
|
157
146
|
self._delete_if_exists(f"/api/discovery/config-libraries/{library_id}/", params=params)
|
|
158
147
|
|
|
159
148
|
def delete_discovery_config_library_by_name_if_exists(
|
|
160
|
-
self, name: str,
|
|
149
|
+
self, name: str, namespace: str = "", *, force: bool = False
|
|
161
150
|
) -> None:
|
|
162
151
|
"""
|
|
163
|
-
Deletes the discovery config library with the given name
|
|
152
|
+
Deletes the discovery config library with the given name and namespace.
|
|
164
153
|
|
|
165
|
-
Library names are unique
|
|
166
|
-
so a type is required to identify a single library.
|
|
154
|
+
Library names are unique within a namespace.
|
|
167
155
|
No-op if no such library exists.
|
|
168
156
|
"""
|
|
169
157
|
|
|
170
|
-
library_id = self._get_discovery_config_library_id_by_name(name,
|
|
158
|
+
library_id = self._get_discovery_config_library_id_by_name(name, namespace)
|
|
171
159
|
if library_id is not None:
|
|
172
160
|
self.delete_discovery_config_library_by_id_if_exists(library_id, force=force)
|
|
@@ -9,6 +9,7 @@ from datamasque.client.ruleset_libraries import RulesetLibraryClient
|
|
|
9
9
|
from datamasque.client.rulesets import RulesetClient
|
|
10
10
|
from datamasque.client.runs import RunClient
|
|
11
11
|
from datamasque.client.settings import SettingsClient
|
|
12
|
+
from datamasque.client.table_references import TableReferenceClient
|
|
12
13
|
from datamasque.client.users import UserClient
|
|
13
14
|
|
|
14
15
|
__all__ = ["DataMasqueClient", "FileOrContent", "UploadFile"]
|
|
@@ -24,6 +25,7 @@ class DataMasqueClient(
|
|
|
24
25
|
DiscoveryClient,
|
|
25
26
|
DiscoveryConfigClient,
|
|
26
27
|
DiscoveryConfigLibraryClient,
|
|
28
|
+
TableReferenceClient,
|
|
27
29
|
UserClient,
|
|
28
30
|
SettingsClient,
|
|
29
31
|
):
|
|
@@ -10,6 +10,10 @@ from datamasque.client.models.data_selection import HashColumnsTableConfig, Loca
|
|
|
10
10
|
from datamasque.client.models.discovery_config import DiscoveryConfig, DiscoveryConfigId, unwrap_discovery_config_id
|
|
11
11
|
from datamasque.client.models.pagination import Page
|
|
12
12
|
from datamasque.client.models.runs import RunConnectionRef
|
|
13
|
+
from datamasque.client.models.safe_data_preview import (
|
|
14
|
+
SafeDataPreview,
|
|
15
|
+
SafeDataPreviewOptions,
|
|
16
|
+
)
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
class InDataDiscoveryRule(BaseModel):
|
|
@@ -32,6 +36,7 @@ class InDataDiscoveryConfig(BaseModel):
|
|
|
32
36
|
non_sensitive_rules: Optional[list[InDataDiscoveryRule]] = None
|
|
33
37
|
ignore_rules: Optional[list[InDataDiscoveryRule]] = None
|
|
34
38
|
force: Optional[bool] = None
|
|
39
|
+
safe_data_preview: Optional[SafeDataPreviewOptions] = None
|
|
35
40
|
|
|
36
41
|
|
|
37
42
|
class SchemaDiscoveryRequest(BaseModel):
|
|
@@ -294,6 +299,7 @@ class SchemaDiscoveryColumn(BaseModel):
|
|
|
294
299
|
unique_index_names: list[str]
|
|
295
300
|
referencing_foreign_keys: list[ReferencingForeignKey]
|
|
296
301
|
constraint: str # Primary or Unique, or empty string if column does not participate in a PK/UK
|
|
302
|
+
safe_data_preview: Optional[SafeDataPreview] = None
|
|
297
303
|
|
|
298
304
|
|
|
299
305
|
class SchemaDiscoveryResult(BaseModel):
|
|
@@ -324,6 +330,7 @@ class TableConstraints(BaseModel):
|
|
|
324
330
|
primary_keys: Optional[list[ConstraintColumns]] = None
|
|
325
331
|
unique_keys: Optional[list[ConstraintColumns]] = None
|
|
326
332
|
foreign_keys: Optional[list[ConstraintColumns]] = None
|
|
333
|
+
row_count: Optional[int] = None
|
|
327
334
|
|
|
328
335
|
|
|
329
336
|
class SchemaDiscoveryPage(Page[SchemaDiscoveryResult]):
|
|
@@ -356,6 +363,7 @@ class FileDiscoveryLocatorResult(BaseModel):
|
|
|
356
363
|
locator: Locator
|
|
357
364
|
matches: list[FileDiscoveryMatch]
|
|
358
365
|
data_types: list[str]
|
|
366
|
+
safe_data_preview: Optional[SafeDataPreview] = None
|
|
359
367
|
|
|
360
368
|
|
|
361
369
|
class FileDiscoveryFile(BaseModel):
|
|
@@ -3,7 +3,6 @@ from typing import NewType, Optional
|
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel, ConfigDict, Field
|
|
5
5
|
|
|
6
|
-
from datamasque.client.models.discovery_config import DiscoveryConfigType
|
|
7
6
|
from datamasque.client.models.status import ValidationStatus
|
|
8
7
|
|
|
9
8
|
DiscoveryConfigLibraryId = NewType("DiscoveryConfigLibraryId", str)
|
|
@@ -13,14 +12,13 @@ class DiscoveryConfigLibrary(BaseModel):
|
|
|
13
12
|
"""
|
|
14
13
|
Represents a named, namespaced, persisted YAML discovery config library.
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
A library is untyped: the same library may be imported by both database and
|
|
16
|
+
file discovery configs, and its name is unique within a namespace.
|
|
18
17
|
"""
|
|
19
18
|
|
|
20
19
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
|
21
20
|
|
|
22
21
|
name: str
|
|
23
|
-
config_type: DiscoveryConfigType
|
|
24
22
|
namespace: str = ""
|
|
25
23
|
yaml: Optional[str] = Field(default=None, alias="config_yaml")
|
|
26
24
|
# Server-populated read-only fields, excluded from request bodies.
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""Typed request and response shapes for Safe Data Preview, part of In-Data Discovery."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Annotated, Literal, Optional, Union
|
|
5
|
+
|
|
6
|
+
from pydantic import (
|
|
7
|
+
BaseModel,
|
|
8
|
+
ConfigDict,
|
|
9
|
+
Field,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StringDisclosureLevel(Enum):
|
|
14
|
+
"""How much detail a string Safe Data Preview reveals."""
|
|
15
|
+
|
|
16
|
+
lengths = "lengths"
|
|
17
|
+
patterns = "patterns"
|
|
18
|
+
first_chars = "first_chars"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class NumericTemporalDisclosureLevel(Enum):
|
|
22
|
+
"""How much detail a numeric or temporal Safe Data Preview reveals."""
|
|
23
|
+
|
|
24
|
+
summaries = "summaries"
|
|
25
|
+
histograms = "histograms"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SafeDataPreviewOptions(BaseModel):
|
|
29
|
+
"""Configures whether Safe Data Preview runs and how much detail each column reveals."""
|
|
30
|
+
|
|
31
|
+
model_config = ConfigDict(extra="forbid")
|
|
32
|
+
|
|
33
|
+
enabled: Optional[bool] = None
|
|
34
|
+
min_group_size: Optional[int] = None
|
|
35
|
+
string_level: Optional[StringDisclosureLevel] = None
|
|
36
|
+
numeric_level: Optional[NumericTemporalDisclosureLevel] = None
|
|
37
|
+
temporal_level: Optional[NumericTemporalDisclosureLevel] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ColumnKind(str, Enum):
|
|
41
|
+
"""Which shape a column's Safe Data Preview takes."""
|
|
42
|
+
|
|
43
|
+
boolean = "boolean"
|
|
44
|
+
numeric = "numeric"
|
|
45
|
+
string = "string"
|
|
46
|
+
temporal = "temporal"
|
|
47
|
+
unsupported = "unsupported"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CommonStatistics(BaseModel):
|
|
51
|
+
"""Row counts shared by every preview kind."""
|
|
52
|
+
|
|
53
|
+
model_config = ConfigDict(extra="allow")
|
|
54
|
+
|
|
55
|
+
count_row: int
|
|
56
|
+
count_null: int
|
|
57
|
+
count_distinct: Optional[int] = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class LengthEntry(BaseModel):
|
|
61
|
+
"""A value length and how many sampled rows had it."""
|
|
62
|
+
|
|
63
|
+
model_config = ConfigDict(extra="allow")
|
|
64
|
+
|
|
65
|
+
length: int
|
|
66
|
+
count: int
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class LengthsStatistics(BaseModel):
|
|
70
|
+
"""Distribution of value lengths."""
|
|
71
|
+
|
|
72
|
+
model_config = ConfigDict(extra="allow")
|
|
73
|
+
|
|
74
|
+
min: int
|
|
75
|
+
max: int
|
|
76
|
+
mean: float
|
|
77
|
+
median: float
|
|
78
|
+
most_common: list[LengthEntry]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class PatternEntry(BaseModel):
|
|
82
|
+
"""A masked character pattern and how many sampled rows matched it."""
|
|
83
|
+
|
|
84
|
+
model_config = ConfigDict(extra="allow")
|
|
85
|
+
|
|
86
|
+
pattern: str
|
|
87
|
+
count: int
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class PatternComposition(BaseModel):
|
|
91
|
+
"""The proportion of letter, digit, and other characters across sampled values."""
|
|
92
|
+
|
|
93
|
+
model_config = ConfigDict(extra="allow")
|
|
94
|
+
|
|
95
|
+
letter: float
|
|
96
|
+
digit: float
|
|
97
|
+
other: float
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class PatternsStatistics(BaseModel):
|
|
101
|
+
"""The most common character patterns and the overall character composition."""
|
|
102
|
+
|
|
103
|
+
model_config = ConfigDict(extra="allow")
|
|
104
|
+
|
|
105
|
+
top: list[PatternEntry]
|
|
106
|
+
composition: PatternComposition
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class MaskedFormEntry(BaseModel):
|
|
110
|
+
"""A masked leading form of a value and how many sampled rows had it."""
|
|
111
|
+
|
|
112
|
+
model_config = ConfigDict(extra="allow")
|
|
113
|
+
|
|
114
|
+
masked: str
|
|
115
|
+
count: int
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class FirstCharsStatistics(BaseModel):
|
|
119
|
+
"""The most common masked leading forms."""
|
|
120
|
+
|
|
121
|
+
model_config = ConfigDict(extra="allow")
|
|
122
|
+
|
|
123
|
+
top: list[MaskedFormEntry]
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class StringStatistics(BaseModel):
|
|
127
|
+
"""String preview statistics; `patterns`/`first_chars` appear at or above certain disclosure levels."""
|
|
128
|
+
|
|
129
|
+
model_config = ConfigDict(extra="allow")
|
|
130
|
+
|
|
131
|
+
lengths: LengthsStatistics
|
|
132
|
+
patterns: Optional[PatternsStatistics] = None
|
|
133
|
+
first_chars: Optional[FirstCharsStatistics] = None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class NumericSummaries(BaseModel):
|
|
137
|
+
"""Numeric mean and percentile summary."""
|
|
138
|
+
|
|
139
|
+
model_config = ConfigDict(extra="allow")
|
|
140
|
+
|
|
141
|
+
mean: float
|
|
142
|
+
q1: float
|
|
143
|
+
q2: float
|
|
144
|
+
q3: float
|
|
145
|
+
p5: float
|
|
146
|
+
p95: float
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class NumericBin(BaseModel):
|
|
150
|
+
"""One half-open interval `[lower_bound, upper_bound)` of a numeric histogram and its row count."""
|
|
151
|
+
|
|
152
|
+
model_config = ConfigDict(extra="allow")
|
|
153
|
+
|
|
154
|
+
lower_bound: float
|
|
155
|
+
upper_bound: float
|
|
156
|
+
count: int
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class NumericHistograms(BaseModel):
|
|
160
|
+
"""The bins of a numeric histogram."""
|
|
161
|
+
|
|
162
|
+
model_config = ConfigDict(extra="allow")
|
|
163
|
+
|
|
164
|
+
bins: list[NumericBin]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class NumericStatistics(BaseModel):
|
|
168
|
+
"""Numeric preview statistics; `histograms` is present only at the histogram disclosure level."""
|
|
169
|
+
|
|
170
|
+
model_config = ConfigDict(extra="allow")
|
|
171
|
+
|
|
172
|
+
summaries: NumericSummaries
|
|
173
|
+
histograms: Optional[NumericHistograms] = None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class TemporalSummaries(BaseModel):
|
|
177
|
+
"""Temporal mean and percentile summary, each an ISO-formatted string."""
|
|
178
|
+
|
|
179
|
+
model_config = ConfigDict(extra="allow")
|
|
180
|
+
|
|
181
|
+
mean: str
|
|
182
|
+
q1: str
|
|
183
|
+
q2: str
|
|
184
|
+
q3: str
|
|
185
|
+
p5: str
|
|
186
|
+
p95: str
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class TemporalBin(BaseModel):
|
|
190
|
+
"""One interval of a temporal histogram, with ISO-formatted bounds and a human-readable label."""
|
|
191
|
+
|
|
192
|
+
model_config = ConfigDict(extra="allow")
|
|
193
|
+
|
|
194
|
+
lower_bound: str
|
|
195
|
+
upper_bound: str
|
|
196
|
+
label: str
|
|
197
|
+
count: int
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class TemporalHistograms(BaseModel):
|
|
201
|
+
"""The bins of a temporal histogram."""
|
|
202
|
+
|
|
203
|
+
model_config = ConfigDict(extra="allow")
|
|
204
|
+
|
|
205
|
+
bins: list[TemporalBin]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class TemporalStatistics(BaseModel):
|
|
209
|
+
"""Temporal preview statistics; `histograms` is present only at the histogram disclosure level."""
|
|
210
|
+
|
|
211
|
+
model_config = ConfigDict(extra="allow")
|
|
212
|
+
|
|
213
|
+
summaries: TemporalSummaries
|
|
214
|
+
histograms: Optional[TemporalHistograms] = None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class BooleanStatistics(BaseModel):
|
|
218
|
+
"""Counts of true and false values."""
|
|
219
|
+
|
|
220
|
+
model_config = ConfigDict(extra="allow")
|
|
221
|
+
|
|
222
|
+
count_true: int
|
|
223
|
+
count_false: int
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class UnsupportedStatistics(BaseModel):
|
|
227
|
+
"""Why a column could not be previewed."""
|
|
228
|
+
|
|
229
|
+
model_config = ConfigDict(extra="allow")
|
|
230
|
+
|
|
231
|
+
reason: str
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ColumnPreview(BaseModel):
|
|
235
|
+
"""Fields present on every Safe Data Preview."""
|
|
236
|
+
|
|
237
|
+
model_config = ConfigDict(extra="allow")
|
|
238
|
+
|
|
239
|
+
# For grouped file discovery, the file whose sample produced this preview; None for database columns.
|
|
240
|
+
sampled_from: Optional[str] = None
|
|
241
|
+
statistics_common: CommonStatistics
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class StringPreview(ColumnPreview):
|
|
245
|
+
"""A preview of a string column."""
|
|
246
|
+
|
|
247
|
+
kind: Literal[ColumnKind.string] = ColumnKind.string
|
|
248
|
+
statistics_kind: StringStatistics
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class NumericPreview(ColumnPreview):
|
|
252
|
+
"""A preview of a numeric column."""
|
|
253
|
+
|
|
254
|
+
kind: Literal[ColumnKind.numeric] = ColumnKind.numeric
|
|
255
|
+
statistics_kind: NumericStatistics
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class TemporalPreview(ColumnPreview):
|
|
259
|
+
"""A preview of a date/time column."""
|
|
260
|
+
|
|
261
|
+
kind: Literal[ColumnKind.temporal] = ColumnKind.temporal
|
|
262
|
+
statistics_kind: TemporalStatistics
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class BooleanPreview(ColumnPreview):
|
|
266
|
+
"""A preview of a boolean column."""
|
|
267
|
+
|
|
268
|
+
kind: Literal[ColumnKind.boolean] = ColumnKind.boolean
|
|
269
|
+
statistics_kind: BooleanStatistics
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class UnsupportedPreview(ColumnPreview):
|
|
273
|
+
"""A column the preview ran against but declined; `statistics_kind.reason` says why."""
|
|
274
|
+
|
|
275
|
+
kind: Literal[ColumnKind.unsupported] = ColumnKind.unsupported
|
|
276
|
+
statistics_kind: UnsupportedStatistics
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
SafeDataPreview = Annotated[
|
|
280
|
+
Union[StringPreview, NumericPreview, TemporalPreview, BooleanPreview, UnsupportedPreview],
|
|
281
|
+
Field(discriminator="kind"),
|
|
282
|
+
]
|