datamasque-python 1.4.0__tar.gz → 1.5.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.4.0 → datamasque_python-1.5.0}/HISTORY.rst +34 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/PKG-INFO +1 -1
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/__init__.py +8 -2
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/connection.py +197 -23
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/pyproject.toml +1 -1
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/helpers.py +2 -35
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_connections.py +290 -247
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/uv.lock +1 -1
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/.editorconfig +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/.github/workflows/ci.yml +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/.github/workflows/release-testpypi.yml +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/.github/workflows/release.yml +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/.gitignore +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/.readthedocs.yaml +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/CONTRIBUTING.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/LICENSE +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/MANIFEST.in +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/Makefile +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/NOTICE +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/README.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/RELEASING.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/base.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/connections.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/discovery.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/discovery_config_libraries.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/discovery_configs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/dmclient.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/exceptions.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/files.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/ifm.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/license.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/__init__.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/data_selection.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/discovery.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/discovery_config.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/discovery_config_library.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/dm_instance.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/files.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/git.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/ifm.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/license.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/pagination.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/rg_config.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/ruleset.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/ruleset_library.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/runs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/safe_data_preview.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/status.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/table_reference.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/models/user.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/py.typed +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/rg_configs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/ruleset_libraries.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/rulesets.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/runs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/settings.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/spcs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/table_references.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/datamasque/client/users.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/Makefile +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/client.models.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/client.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/conf.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/contributing.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/history.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/index.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/installation.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/make.bat +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/modules.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/readme.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/docs/usage.rst +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/__init__.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/conftest.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_base.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_discovery.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_discovery_config_libraries.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_discovery_configs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_files.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_ifm.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_license.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_pagination.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_rg_configs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_ruleset_library.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_rulesets.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_runs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_safe_data_preview.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_settings.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_spcs.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_table_references.py +0 -0
- {datamasque_python-1.4.0 → datamasque_python-1.5.0}/tests/test_users.py +0 -0
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
History
|
|
3
3
|
=======
|
|
4
4
|
|
|
5
|
+
1.5.0 (2026-10-07)
|
|
6
|
+
------------------
|
|
7
|
+
|
|
8
|
+
* **Breaking:** Snowflake masking now stages inside Snowflake, so ``SnowflakeConnectionConfig``
|
|
9
|
+
no longer has the external staging fields ``s3_bucket_name``, ``iam_role_arn``,
|
|
10
|
+
``snowflake_azure_container_name``, ``snowflake_azure_connection_string``,
|
|
11
|
+
``snowflake_azure_connection_string_encrypted`` and ``snowflake_storage_integration_name``.
|
|
12
|
+
``snowflake_stage_location`` and the ``SnowflakeStageLocation`` enum are removed: the server
|
|
13
|
+
detects a Snowflake SPCS deployment from its own environment, so the client no longer states it.
|
|
14
|
+
* Snowflake connections listed from an older server still parse: the removed fields and
|
|
15
|
+
``snowflake_stage_location`` are dropped rather than raising a validation error, and are not sent
|
|
16
|
+
back on ``create_or_update_connection``.
|
|
17
|
+
* Added ``GcsConnectionConfig`` for Google Cloud Storage file connections (``type: gcs_connection``),
|
|
18
|
+
with ``bucket`` and ``service_account_key``:
|
|
19
|
+
the contents of a service account's JSON key file, or the ARN of an AWS Secrets Manager secret that holds it.
|
|
20
|
+
``validate_connection`` and ``list_connections`` accept the new type.
|
|
21
|
+
* Added ``CassandraConnectionConfig`` for Apache Cassandra (``db_type: cassandra``).
|
|
22
|
+
``database`` is the keyspace; ``user`` and ``password`` are optional,
|
|
23
|
+
and ``local_datacenter``, ``tls`` and ``direct_connection`` are supported.
|
|
24
|
+
* Added ``SapAseConnectionConfig`` for SAP ASE (Sybase) (``db_type: sap_ase``),
|
|
25
|
+
with ``tls``, ``tls_server_name`` and ``connect_timeout``.
|
|
26
|
+
It sends no ``schema``: in ASE the schema is the table owner.
|
|
27
|
+
* Added ``SalesforceConnectionConfig`` for Salesforce (``db_type: salesforce``),
|
|
28
|
+
which signs in with an OAuth JWT bearer assertion
|
|
29
|
+
(``instance_url``, ``login_url``, ``client_id``, ``user`` and ``salesforce_private_key_path``,
|
|
30
|
+
with an optional ``salesforce_private_key_passphrase`` and ``api_version``).
|
|
31
|
+
Salesforce is a preview connection type on server 3.26.19:
|
|
32
|
+
creating one fails until an administrator enables preview features.
|
|
33
|
+
* ``DatabaseType`` gains ``cassandra``, ``sap_ase`` and ``salesforce``.
|
|
34
|
+
``DatabaseConnectionConfig`` rejects them in favour of the classes above,
|
|
35
|
+
and ``validate_connection`` and ``list_connections`` dispatch to those classes.
|
|
36
|
+
|
|
37
|
+
Requires server version 3.26.19
|
|
38
|
+
|
|
5
39
|
1.4.0 (2026-09-25)
|
|
6
40
|
------------------
|
|
7
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: datamasque-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.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/
|
|
@@ -29,6 +29,7 @@ from datamasque.client.exceptions import (
|
|
|
29
29
|
from datamasque.client.ifm import DataMasqueIfmClient
|
|
30
30
|
from datamasque.client.models.connection import (
|
|
31
31
|
AzureConnectionConfig,
|
|
32
|
+
CassandraConnectionConfig,
|
|
32
33
|
ConnectionConfig,
|
|
33
34
|
ConnectionId,
|
|
34
35
|
CosmosDbConnectionConfig,
|
|
@@ -38,13 +39,15 @@ from datamasque.client.models.connection import (
|
|
|
38
39
|
DocumentDbConnectionConfig,
|
|
39
40
|
DynamoConnectionConfig,
|
|
40
41
|
FileConnectionConfig,
|
|
42
|
+
GcsConnectionConfig,
|
|
41
43
|
LicenseLock,
|
|
42
44
|
MongoConnectionConfig,
|
|
43
45
|
MountedShareConnectionConfig,
|
|
44
46
|
MssqlLinkedServerConnectionConfig,
|
|
45
47
|
S3ConnectionConfig,
|
|
48
|
+
SalesforceConnectionConfig,
|
|
49
|
+
SapAseConnectionConfig,
|
|
46
50
|
SnowflakeConnectionConfig,
|
|
47
|
-
SnowflakeStageLocation,
|
|
48
51
|
SseConfig,
|
|
49
52
|
SseSelection,
|
|
50
53
|
)
|
|
@@ -179,6 +182,7 @@ __all__ = [
|
|
|
179
182
|
"AzureConnectionConfig",
|
|
180
183
|
"BooleanPreview",
|
|
181
184
|
"BooleanStatistics",
|
|
185
|
+
"CassandraConnectionConfig",
|
|
182
186
|
"ColumnKind",
|
|
183
187
|
"ColumnPreview",
|
|
184
188
|
"CommonStatistics",
|
|
@@ -226,6 +230,7 @@ __all__ = [
|
|
|
226
230
|
"FileRulesetGenerationWithRGConfigRequest",
|
|
227
231
|
"FirstCharsStatistics",
|
|
228
232
|
"ForeignKeyRef",
|
|
233
|
+
"GcsConnectionConfig",
|
|
229
234
|
"GitSnapshot",
|
|
230
235
|
"HashColumnsTableConfig",
|
|
231
236
|
"IfmAuthError",
|
|
@@ -287,6 +292,8 @@ __all__ = [
|
|
|
287
292
|
"S3ConnectionConfig",
|
|
288
293
|
"SafeDataPreview",
|
|
289
294
|
"SafeDataPreviewOptions",
|
|
295
|
+
"SalesforceConnectionConfig",
|
|
296
|
+
"SapAseConnectionConfig",
|
|
290
297
|
"SchemaDiscoveryColumn",
|
|
291
298
|
"SchemaDiscoveryFromConfigRequest",
|
|
292
299
|
"SchemaDiscoveryPage",
|
|
@@ -298,7 +305,6 @@ __all__ = [
|
|
|
298
305
|
"SelectedFileData",
|
|
299
306
|
"SnowflakeConnectionConfig",
|
|
300
307
|
"SnowflakeKeyFile",
|
|
301
|
-
"SnowflakeStageLocation",
|
|
302
308
|
"SseConfig",
|
|
303
309
|
"SseSelection",
|
|
304
310
|
"SslZipFile",
|
|
@@ -51,15 +51,9 @@ class DatabaseType(Enum):
|
|
|
51
51
|
databricks = "databricks"
|
|
52
52
|
informix = "informix"
|
|
53
53
|
saphana = "saphana"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"""Storage backend for a Snowflake connection's external stage."""
|
|
58
|
-
|
|
59
|
-
local = "local" # Not supported for production use
|
|
60
|
-
aws_s3 = "aws_s3"
|
|
61
|
-
azure_blob_storage = "azure_blob_storage"
|
|
62
|
-
spcs = "spcs" # DataMasque running inside Snowflake SPCS; staged on the container's own storage
|
|
54
|
+
cassandra = "cassandra"
|
|
55
|
+
sap_ase = "sap_ase"
|
|
56
|
+
salesforce = "salesforce"
|
|
63
57
|
|
|
64
58
|
|
|
65
59
|
class SseSelection(Enum):
|
|
@@ -285,6 +279,19 @@ class CosmosDbConnectionConfig(MongoConnectionConfig):
|
|
|
285
279
|
return DatabaseType.cosmosdb
|
|
286
280
|
|
|
287
281
|
|
|
282
|
+
# Servers before 3.26.18 still return these on Snowflake connections. With `extra="allow"`
|
|
283
|
+
# they would be sent back on create/update unless dropped here.
|
|
284
|
+
_SNOWFLAKE_EXTERNAL_STAGE_KEYS = (
|
|
285
|
+
"s3_bucket_name",
|
|
286
|
+
"iam_role_arn",
|
|
287
|
+
"snowflake_azure_container_name",
|
|
288
|
+
"snowflake_azure_connection_string",
|
|
289
|
+
"snowflake_azure_connection_string_encrypted",
|
|
290
|
+
"snowflake_storage_integration_name",
|
|
291
|
+
"snowflake_stage_location",
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
|
|
288
295
|
class SnowflakeConnectionConfig(ConnectionConfig):
|
|
289
296
|
"""
|
|
290
297
|
Connection configuration for a Snowflake database.
|
|
@@ -294,14 +301,14 @@ class SnowflakeConnectionConfig(ConnectionConfig):
|
|
|
294
301
|
"""
|
|
295
302
|
|
|
296
303
|
database: str
|
|
297
|
-
# Optional because
|
|
298
|
-
#
|
|
299
|
-
#
|
|
300
|
-
#
|
|
304
|
+
# Optional because connections leave these unset when DataMasque runs inside Snowflake SPCS:
|
|
305
|
+
# the server detects SPCS from its environment and the agent uses the container's OAuth token,
|
|
306
|
+
# the SNOWFLAKE_HOST/SNOWFLAKE_ACCOUNT env and the app-owned QUERY_WAREHOUSE, so user/account/
|
|
307
|
+
# warehouse are None. Mirrors the app's canonical model
|
|
308
|
+
# (agent .../schemas/connection/connection.py), which types these `| None = None`.
|
|
301
309
|
user: Optional[str] = None
|
|
302
310
|
snowflake_account_id: Optional[str] = None
|
|
303
311
|
snowflake_warehouse: Optional[str] = None
|
|
304
|
-
snowflake_storage_integration_name: Optional[str] = None
|
|
305
312
|
host: str = ""
|
|
306
313
|
port: Optional[int] = None
|
|
307
314
|
db_schema: Optional[str] = Field(default=None, alias="schema")
|
|
@@ -310,12 +317,6 @@ class SnowflakeConnectionConfig(ConnectionConfig):
|
|
|
310
317
|
password: Optional[str] = None
|
|
311
318
|
snowflake_private_key: Optional[FileId] = None
|
|
312
319
|
snowflake_private_key_passphrase: Optional[str] = None
|
|
313
|
-
snowflake_stage_location: Optional[SnowflakeStageLocation] = None
|
|
314
|
-
s3_bucket_name: Optional[str] = None
|
|
315
|
-
iam_role_arn: Optional[str] = None
|
|
316
|
-
snowflake_azure_container_name: Optional[str] = None
|
|
317
|
-
snowflake_azure_connection_string: Optional[str] = None
|
|
318
|
-
snowflake_azure_connection_string_encrypted: Optional[str] = None
|
|
319
320
|
|
|
320
321
|
mask_type: Literal["database"] = "database"
|
|
321
322
|
db_type: Literal["snowflake"] = "snowflake"
|
|
@@ -338,9 +339,9 @@ class SnowflakeConnectionConfig(ConnectionConfig):
|
|
|
338
339
|
|
|
339
340
|
@model_validator(mode="before")
|
|
340
341
|
@classmethod
|
|
341
|
-
def
|
|
342
|
+
def _strip_server_only_fields(cls, data: dict) -> dict:
|
|
342
343
|
if isinstance(data, dict):
|
|
343
|
-
for key in ("password_encrypted", "dbpassword"):
|
|
344
|
+
for key in ("password_encrypted", "dbpassword", *_SNOWFLAKE_EXTERNAL_STAGE_KEYS):
|
|
344
345
|
data.pop(key, None)
|
|
345
346
|
return data
|
|
346
347
|
|
|
@@ -366,7 +367,8 @@ class DatabaseConnectionConfig(ConnectionConfig):
|
|
|
366
367
|
Connection configuration for a SQL database.
|
|
367
368
|
|
|
368
369
|
Use `DynamoConnectionConfig` for DynamoDB, `SnowflakeConnectionConfig` for Snowflake,
|
|
369
|
-
|
|
370
|
+
`MongoConnectionConfig` for MongoDB, `CassandraConnectionConfig` for Apache Cassandra,
|
|
371
|
+
`SapAseConnectionConfig` for SAP ASE (Sybase) and `SalesforceConnectionConfig` for Salesforce.
|
|
370
372
|
"""
|
|
371
373
|
|
|
372
374
|
host: str
|
|
@@ -401,6 +403,12 @@ class DatabaseConnectionConfig(ConnectionConfig):
|
|
|
401
403
|
raise ValueError("For Azure Cosmos DB, use the CosmosDbConnectionConfig class instead")
|
|
402
404
|
if self.database_type is DatabaseType.databricks:
|
|
403
405
|
raise ValueError("For Databricks SQL Warehouse, use the DatabricksConnectionConfig class instead")
|
|
406
|
+
if self.database_type is DatabaseType.cassandra:
|
|
407
|
+
raise ValueError("For Apache Cassandra, use the CassandraConnectionConfig class instead")
|
|
408
|
+
if self.database_type is DatabaseType.sap_ase:
|
|
409
|
+
raise ValueError("For SAP ASE (Sybase), use the SapAseConnectionConfig class instead")
|
|
410
|
+
if self.database_type is DatabaseType.salesforce:
|
|
411
|
+
raise ValueError("For Salesforce, use the SalesforceConnectionConfig class instead")
|
|
404
412
|
return self
|
|
405
413
|
|
|
406
414
|
mask_type: Literal["database"] = "database"
|
|
@@ -508,6 +516,29 @@ class AzureConnectionConfig(FileConnectionConfig):
|
|
|
508
516
|
return data
|
|
509
517
|
|
|
510
518
|
|
|
519
|
+
class GcsConnectionConfig(FileConnectionConfig):
|
|
520
|
+
"""
|
|
521
|
+
Connection configuration for a Google Cloud Storage bucket.
|
|
522
|
+
|
|
523
|
+
`service_account_key` holds the contents of a service account's JSON key file,
|
|
524
|
+
or the ARN of an AWS Secrets Manager secret that holds it.
|
|
525
|
+
|
|
526
|
+
Requires server version 3.26.19.
|
|
527
|
+
"""
|
|
528
|
+
|
|
529
|
+
type: Literal["gcs_connection"] = "gcs_connection"
|
|
530
|
+
bucket: str = ""
|
|
531
|
+
service_account_key: Optional[str] = None
|
|
532
|
+
|
|
533
|
+
@model_validator(mode="before")
|
|
534
|
+
@classmethod
|
|
535
|
+
def _strip_encrypted_service_account_key(cls, data: dict) -> dict:
|
|
536
|
+
if isinstance(data, dict):
|
|
537
|
+
# The API returns the encrypted form; drop it so `service_account_key` stays None.
|
|
538
|
+
data.pop("service_account_key_encrypted", None)
|
|
539
|
+
return data
|
|
540
|
+
|
|
541
|
+
|
|
511
542
|
class MountedShareConnectionConfig(FileConnectionConfig):
|
|
512
543
|
"""Connection configuration for a mounted file share."""
|
|
513
544
|
|
|
@@ -540,9 +571,149 @@ class DatabricksConnectionConfig(ConnectionConfig):
|
|
|
540
571
|
return data
|
|
541
572
|
|
|
542
573
|
|
|
574
|
+
class CassandraConnectionConfig(ConnectionConfig):
|
|
575
|
+
"""
|
|
576
|
+
Connection configuration for an Apache Cassandra keyspace.
|
|
577
|
+
|
|
578
|
+
`database` is the keyspace.
|
|
579
|
+
`user` and `password` are optional, but a username needs a password.
|
|
580
|
+
Set `direct_connection` when the node addresses the cluster advertises are unreachable,
|
|
581
|
+
for example behind NAT or in containers.
|
|
582
|
+
|
|
583
|
+
Requires server version 3.26.19.
|
|
584
|
+
"""
|
|
585
|
+
|
|
586
|
+
host: str
|
|
587
|
+
port: int = 9042
|
|
588
|
+
database: str
|
|
589
|
+
user: Optional[str] = None
|
|
590
|
+
password: Optional[str] = None
|
|
591
|
+
local_datacenter: Optional[str] = None
|
|
592
|
+
tls: bool = False
|
|
593
|
+
direct_connection: bool = False
|
|
594
|
+
is_read_only: bool = False
|
|
595
|
+
|
|
596
|
+
mask_type: Literal["database"] = "database"
|
|
597
|
+
db_type: Literal["cassandra"] = "cassandra"
|
|
598
|
+
|
|
599
|
+
@property
|
|
600
|
+
def database_type(self) -> DatabaseType:
|
|
601
|
+
return DatabaseType.cassandra
|
|
602
|
+
|
|
603
|
+
@model_serializer(mode="wrap")
|
|
604
|
+
def _serialize(self, handler: Callable) -> dict:
|
|
605
|
+
d = handler(self)
|
|
606
|
+
# The server expects the password under the `dbpassword` key.
|
|
607
|
+
password = d.pop("password", None)
|
|
608
|
+
if password is not None:
|
|
609
|
+
d["dbpassword"] = password
|
|
610
|
+
return d
|
|
611
|
+
|
|
612
|
+
@model_validator(mode="before")
|
|
613
|
+
@classmethod
|
|
614
|
+
def _strip_encrypted_password(cls, data: dict) -> dict:
|
|
615
|
+
if isinstance(data, dict):
|
|
616
|
+
for key in ("password_encrypted", "dbpassword"):
|
|
617
|
+
data.pop(key, None)
|
|
618
|
+
return data
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
class SapAseConnectionConfig(ConnectionConfig):
|
|
622
|
+
"""
|
|
623
|
+
Connection configuration for an SAP ASE (Sybase) database.
|
|
624
|
+
|
|
625
|
+
ASE has no schema setting: the schema is the table owner,
|
|
626
|
+
and an unqualified name resolves to the connecting user's table first, then to `dbo`'s.
|
|
627
|
+
With `tls` on, the server certificate is always verified;
|
|
628
|
+
`tls_server_name` only changes the name it is verified against.
|
|
629
|
+
|
|
630
|
+
Requires server version 3.26.19.
|
|
631
|
+
"""
|
|
632
|
+
|
|
633
|
+
host: str
|
|
634
|
+
port: int = 5000
|
|
635
|
+
database: str
|
|
636
|
+
user: str
|
|
637
|
+
password: Optional[str] = None
|
|
638
|
+
tls: bool = False
|
|
639
|
+
tls_server_name: Optional[str] = None
|
|
640
|
+
connect_timeout: Optional[int] = None
|
|
641
|
+
is_read_only: bool = False
|
|
642
|
+
|
|
643
|
+
mask_type: Literal["database"] = "database"
|
|
644
|
+
db_type: Literal["sap_ase"] = "sap_ase"
|
|
645
|
+
|
|
646
|
+
@property
|
|
647
|
+
def database_type(self) -> DatabaseType:
|
|
648
|
+
return DatabaseType.sap_ase
|
|
649
|
+
|
|
650
|
+
@model_serializer(mode="wrap")
|
|
651
|
+
def _serialize(self, handler: Callable) -> dict:
|
|
652
|
+
d = handler(self)
|
|
653
|
+
# The server expects the password under the `dbpassword` key.
|
|
654
|
+
password = d.pop("password", None)
|
|
655
|
+
if password is not None:
|
|
656
|
+
d["dbpassword"] = password
|
|
657
|
+
return d
|
|
658
|
+
|
|
659
|
+
@model_validator(mode="before")
|
|
660
|
+
@classmethod
|
|
661
|
+
def _strip_encrypted_password(cls, data: dict) -> dict:
|
|
662
|
+
if isinstance(data, dict):
|
|
663
|
+
for key in ("password_encrypted", "dbpassword"):
|
|
664
|
+
data.pop(key, None)
|
|
665
|
+
return data
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
class SalesforceConnectionConfig(ConnectionConfig):
|
|
669
|
+
"""
|
|
670
|
+
Connection configuration for a Salesforce org.
|
|
671
|
+
|
|
672
|
+
Salesforce is reached over HTTPS with an OAuth JWT bearer assertion,
|
|
673
|
+
so it carries no host, port, database or password.
|
|
674
|
+
`login_url` is the org's My Domain URL and is sent verbatim as the JWT audience,
|
|
675
|
+
which Salesforce compares exactly.
|
|
676
|
+
`salesforce_private_key_path` is relative to the DataMasque files sandbox,
|
|
677
|
+
or to the connection fileset when one is attached.
|
|
678
|
+
`salesforce_private_key_passphrase` is only needed for an encrypted key.
|
|
679
|
+
`api_version` is the server's default unless set.
|
|
680
|
+
|
|
681
|
+
Requires server version 3.26.19.
|
|
682
|
+
Salesforce is a preview connection type there:
|
|
683
|
+
the server refuses to create one until an administrator enables preview features
|
|
684
|
+
under Settings, Preview Features.
|
|
685
|
+
"""
|
|
686
|
+
|
|
687
|
+
instance_url: str
|
|
688
|
+
login_url: str
|
|
689
|
+
client_id: str
|
|
690
|
+
user: str
|
|
691
|
+
salesforce_private_key_path: str
|
|
692
|
+
salesforce_private_key_passphrase: Optional[str] = None
|
|
693
|
+
# Left unset so the server applies its own default, which it stores on the connection.
|
|
694
|
+
# A default here would keep pinning new connections to it after the server moves on.
|
|
695
|
+
api_version: Optional[str] = None
|
|
696
|
+
is_read_only: bool = False
|
|
697
|
+
|
|
698
|
+
mask_type: Literal["database"] = "database"
|
|
699
|
+
db_type: Literal["salesforce"] = "salesforce"
|
|
700
|
+
|
|
701
|
+
@property
|
|
702
|
+
def database_type(self) -> DatabaseType:
|
|
703
|
+
return DatabaseType.salesforce
|
|
704
|
+
|
|
705
|
+
@model_validator(mode="before")
|
|
706
|
+
@classmethod
|
|
707
|
+
def _strip_encrypted_passphrase(cls, data: dict) -> dict:
|
|
708
|
+
if isinstance(data, dict):
|
|
709
|
+
data.pop("salesforce_private_key_passphrase_encrypted", None)
|
|
710
|
+
return data
|
|
711
|
+
|
|
712
|
+
|
|
543
713
|
FILE_TYPE_MAP: dict[str, type[FileConnectionConfig]] = {
|
|
544
714
|
"s3_connection": S3ConnectionConfig,
|
|
545
715
|
"azure_blob_connection": AzureConnectionConfig,
|
|
716
|
+
"gcs_connection": GcsConnectionConfig,
|
|
546
717
|
"mounted_share_connection": MountedShareConnectionConfig,
|
|
547
718
|
}
|
|
548
719
|
|
|
@@ -554,6 +725,9 @@ DB_TYPE_MAP: dict[str, type[ConnectionConfig]] = {
|
|
|
554
725
|
DatabaseType.snowflake.value: SnowflakeConnectionConfig,
|
|
555
726
|
DatabaseType.mssql_linked.value: MssqlLinkedServerConnectionConfig,
|
|
556
727
|
DatabaseType.databricks.value: DatabricksConnectionConfig,
|
|
728
|
+
DatabaseType.cassandra.value: CassandraConnectionConfig,
|
|
729
|
+
DatabaseType.sap_ase.value: SapAseConnectionConfig,
|
|
730
|
+
DatabaseType.salesforce.value: SalesforceConnectionConfig,
|
|
557
731
|
# others use the default `DatabaseConnectionConfig`
|
|
558
732
|
}
|
|
559
733
|
|
|
@@ -8,7 +8,6 @@ from datamasque.client.models.connection import (
|
|
|
8
8
|
DatabaseType,
|
|
9
9
|
S3ConnectionConfig,
|
|
10
10
|
SnowflakeConnectionConfig,
|
|
11
|
-
SnowflakeStageLocation,
|
|
12
11
|
)
|
|
13
12
|
|
|
14
13
|
fake = Faker()
|
|
@@ -117,44 +116,12 @@ def make_ok_response() -> Response:
|
|
|
117
116
|
return response
|
|
118
117
|
|
|
119
118
|
|
|
120
|
-
def
|
|
119
|
+
def snowflake_connection_config():
|
|
121
120
|
return SnowflakeConnectionConfig(
|
|
122
|
-
name="
|
|
121
|
+
name="snowflake",
|
|
123
122
|
database="test_db",
|
|
124
123
|
user="snowflake_user",
|
|
125
124
|
snowflake_account_id="ACCOUNT-123",
|
|
126
125
|
snowflake_warehouse="test_warehouse",
|
|
127
|
-
snowflake_storage_integration_name="test_integration",
|
|
128
126
|
password="test_password",
|
|
129
|
-
snowflake_stage_location=SnowflakeStageLocation.aws_s3,
|
|
130
|
-
s3_bucket_name="test-bucket",
|
|
131
|
-
iam_role_arn="arn:aws:iam::123456789012:role/test-role",
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def snowflake_connection_config_azure():
|
|
136
|
-
return SnowflakeConnectionConfig(
|
|
137
|
-
name="snowflake_azure",
|
|
138
|
-
database="test_db",
|
|
139
|
-
user="snowflake_user",
|
|
140
|
-
snowflake_account_id="ACCOUNT-456",
|
|
141
|
-
snowflake_warehouse="test_warehouse",
|
|
142
|
-
snowflake_storage_integration_name="test_integration",
|
|
143
|
-
password="test_password",
|
|
144
|
-
snowflake_stage_location=SnowflakeStageLocation.azure_blob_storage,
|
|
145
|
-
snowflake_azure_container_name="test-container",
|
|
146
|
-
snowflake_azure_connection_string="DefaultEndpointsProtocol=https;AccountName=test;AccountKey=test",
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def snowflake_connection_config_local():
|
|
151
|
-
return SnowflakeConnectionConfig(
|
|
152
|
-
name="snowflake_local",
|
|
153
|
-
database="test_db",
|
|
154
|
-
user="snowflake_user",
|
|
155
|
-
snowflake_account_id="ACCOUNT-789",
|
|
156
|
-
snowflake_warehouse="test_warehouse",
|
|
157
|
-
snowflake_storage_integration_name="test_integration",
|
|
158
|
-
password="test_password",
|
|
159
|
-
snowflake_stage_location=SnowflakeStageLocation.local,
|
|
160
127
|
)
|