From 7b1d8a7eb111796f1fb4c7d827f6f2f4e5d88488 Mon Sep 17 00:00:00 2001 From: Jan Kadlec Date: Sun, 8 Jun 2025 18:27:42 +0200 Subject: [PATCH 1/2] feat: add Snowflake key-pair support to `load_and_put_declarative_data_sources` There was a missing support to obtain credentials from file for Snowflake with key-pair authentication. JIRA: PSDK-218 risk: low --- .../load_and_put_declarative_data_sources.md | 7 ++++ .../declarative_model/data_source.py | 42 +++++++++++++++---- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/docs/content/en/latest/data/data-source/load_and_put_declarative_data_sources.md b/docs/content/en/latest/data/data-source/load_and_put_declarative_data_sources.md index 65d521075..38d2bee0c 100644 --- a/docs/content/en/latest/data/data-source/load_and_put_declarative_data_sources.md +++ b/docs/content/en/latest/data/data-source/load_and_put_declarative_data_sources.md @@ -61,6 +61,13 @@ Example of the credential file: data_sources: demo-test-ds: "demopass" demo-bigquery-ds: "~/home/secrets.json" + demo-snowflake-password-ds: "snowflakepass" + demo-snowflake-key-pair-ds: + private_key: | + -----BEGIN PRIVATE KEY----- + ... (private key content) + -----END PRIVATE KEY----- + private_key_passphrase: "passphrase" # Optional ``` The result is identical. diff --git a/gooddata-sdk/gooddata_sdk/catalog/data_source/declarative_model/data_source.py b/gooddata-sdk/gooddata_sdk/catalog/data_source/declarative_model/data_source.py index b28657e70..2f4aab7bb 100644 --- a/gooddata-sdk/gooddata_sdk/catalog/data_source/declarative_model/data_source.py +++ b/gooddata-sdk/gooddata_sdk/catalog/data_source/declarative_model/data_source.py @@ -19,6 +19,9 @@ BIGQUERY_TYPE = "BIGQUERY" DATABRICKS_TYPE = "DATABRICKS" +SNOWFLAKE_TYPE = "SNOWFLAKE" +PRIVATE_KEY = "private_key" +PRIVATE_KEY_PASSPHRASE = "private_key_passphrase" LAYOUT_DATA_SOURCES_DIR = "data_sources" @@ -30,25 +33,45 @@ def _inject_base(self, credentials: dict[str, Any]) -> DeclarativeDataSources: data_sources = [] client_class = self.client_class() for data_source in self.data_sources: - if data_source.id in credentials: + data_source_credentials = credentials.get(data_source.id) + if data_source_credentials is None: + data_sources.append(data_source.to_api()) + else: if data_source.type == BIGQUERY_TYPE: - token = TokenCredentialsFromFile.token_from_file(credentials[data_source.id]) + token = TokenCredentialsFromFile.token_from_file(data_source_credentials) data_sources.append(data_source.to_api(token=token)) elif data_source.type == DATABRICKS_TYPE: if data_source.client_id and data_source.client_id.strip(): - client_secret = ClientSecretCredentialsFromFile.client_secret_from_file( - credentials[data_source.id] - ) + client_secret = ClientSecretCredentialsFromFile.client_secret_from_file(data_source_credentials) data_sources.append(data_source.to_api(client_secret=client_secret)) else: token = TokenCredentialsFromFile.token_from_file( - file_path=credentials[data_source.id], base64_encode=False + file_path=data_source_credentials, base64_encode=False ) data_sources.append(data_source.to_api(token=token)) + elif data_source.type == SNOWFLAKE_TYPE: + if isinstance(data_source_credentials, str): + data_sources.append(data_source.to_api(password=data_source_credentials)) + elif isinstance(data_source_credentials, dict): + private_key = data_source_credentials.get(PRIVATE_KEY) + private_key_passphrase = data_source_credentials.get(PRIVATE_KEY_PASSPHRASE) + if private_key is None: + raise ValueError( + f"Credentials for data source {data_source.id} should contain {PRIVATE_KEY} but it is missing." + ) + else: + data_sources.append( + data_source.to_api( + private_key=private_key, private_key_passphrase=private_key_passphrase + ) + ) + else: + raise ValueError( + f"Credentials for data source {data_source.id} should be a string or a dictionary, " + f"but got {type(data_source_credentials)}." + ) else: - data_sources.append(data_source.to_api(password=credentials[data_source.id])) - else: - data_sources.append(data_source.to_api()) + data_sources.append(data_source.to_api(password=data_source_credentials)) return client_class(data_sources=data_sources) def _inject_credentials_legacy(self, credentials: dict[str, Any]) -> DeclarativeDataSources: @@ -117,6 +140,7 @@ class CatalogDeclarativeDataSource(Base): decoded_parameters: Optional[list[CatalogParameter]] = None permissions: list[CatalogDeclarativeDataSourcePermission] = attr.field(factory=list) client_id: Optional[str] = None + authentication_type: Optional[str] = None def to_test_request( self, From 059d4272e9acc84afc47190740395b21909d30f8 Mon Sep 17 00:00:00 2001 From: Jan Kadlec Date: Sun, 8 Jun 2025 18:32:34 +0200 Subject: [PATCH 2/2] docs: fix htmltest JIRA: TRIVIAL risk: low --- docs/content/en/latest/administration/user-groups/_index.md | 4 ++-- .../en/latest/administration/users-and-user-groups/_index.md | 4 ++-- docs/content/en/latest/administration/users/_index.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/en/latest/administration/user-groups/_index.md b/docs/content/en/latest/administration/user-groups/_index.md index bff6140cf..99c6c24a4 100644 --- a/docs/content/en/latest/administration/user-groups/_index.md +++ b/docs/content/en/latest/administration/user-groups/_index.md @@ -26,8 +26,8 @@ See [Manage Permissions](https://www.gooddata.com/docs/cloud/manage-deployment/m * [load_and_put_declarative_user_groups](./load_and_put_declarative_user_groups/) ### Permission management -* [get_user_group_permissions](./get_user_group_permissions) -* [manage_user_group_permissions](./manage_user_group_permissions) +* [get_user_group_permissions](./get_user_group_permissions/) +* [manage_user_group_permissions](./manage_user_group_permissions/) ## Example diff --git a/docs/content/en/latest/administration/users-and-user-groups/_index.md b/docs/content/en/latest/administration/users-and-user-groups/_index.md index 397b286bc..a2f6f46b5 100644 --- a/docs/content/en/latest/administration/users-and-user-groups/_index.md +++ b/docs/content/en/latest/administration/users-and-user-groups/_index.md @@ -18,8 +18,8 @@ See [Manage Permissions](https://www.gooddata.com/docs/cloud/manage-deployment/m * [load_and_put_declarative_users_user_groups](./load_and_put_declarative_users_user_groups/) ### Permission management -* [assign_permissions_bulk](./assign_permissions_bulk) -* [revoke_permissions_bulk](./revoke_permissions_bulk) +* [assign_permissions_bulk](./assign_permissions_bulk/) +* [revoke_permissions_bulk](./revoke_permissions_bulk/) ## Example List, create and delete users and user groups: diff --git a/docs/content/en/latest/administration/users/_index.md b/docs/content/en/latest/administration/users/_index.md index 399cc61f1..84c4e79ed 100644 --- a/docs/content/en/latest/administration/users/_index.md +++ b/docs/content/en/latest/administration/users/_index.md @@ -31,8 +31,8 @@ See [Manage Users and UserGroups](https://www.gooddata.com/docs/cloud/manage-dep * [load_and_put_declarative_users](./load_and_put_declarative_users/) ### Permission management -* [get_user_permissions](./get_user_permissions) -* [manage_user_permissions](./manage_user_permissions) +* [get_user_permissions](./get_user_permissions/) +* [manage_user_permissions](./manage_user_permissions/) ## Example