diff --git a/src/managedcleanroom/HISTORY.rst b/src/managedcleanroom/HISTORY.rst index 4447c820abe..49a22108b47 100644 --- a/src/managedcleanroom/HISTORY.rst +++ b/src/managedcleanroom/HISTORY.rst @@ -46,4 +46,23 @@ Release History * Updated: Added --active-only filter to collaboration list and show commands * Updated: Added --pending-only filter to invitation list command * Updated: Added --scope, --from-seqno, --to-seqno filters to audit event list command -* Updated: Response structures modernized (many list endpoints now return structured objects with value arrays) \ No newline at end of file +* Updated: Response structures modernized (many list endpoints now return structured objects with value arrays) + +1.0.0b5 ++++++++ +* Updated to latest Frontend API spec (2026-03-01-preview with SKR policy) +* Regenerated analytics_frontend_api SDK with updated method signatures and SKR policy support +* BREAKING CHANGE: Removed `az managedcleanroom frontend analytics cleanroompolicy` command +* Added: `az managedcleanroom frontend analytics skr-policy` - Get SKR (Secure Key Release) policy for a specific dataset + - New required parameter: --dataset-id to specify the dataset for which to retrieve the SKR policy +* SDK Changes (internal): + - Added: collaboration.analytics_skr_policy_get(collaboration_id, dataset_id) method + - Removed: collaboration.analytics_cleanroompolicy_get(collaboration_id) method + - Fixed: analytics_queries_document_id_runhistory_get → analytics_queries_document_id_runs_get + - Fixed: analytics_queries_jobid_get → analytics_runs_job_id_get +* Bug Fixes: + - Fixed token normalization in _frontend_auth.py to handle tuple, AccessToken, and string token formats + - Added SSL verification environment variable support (AZURE_CLI_DISABLE_CONNECTION_VERIFICATION, REQUESTS_CA_BUNDLE) + - Fixed schema_file parameter handling in dataset publish to support Azure CLI auto-loading (dict, string, and @file formats) + - Fixed runhistory API endpoint method name + - Fixed runresult API endpoint method name diff --git a/src/managedcleanroom/azext_managedcleanroom/__init__.py b/src/managedcleanroom/azext_managedcleanroom/__init__.py index 70e501dbb1c..81c24125b6a 100644 --- a/src/managedcleanroom/azext_managedcleanroom/__init__.py +++ b/src/managedcleanroom/azext_managedcleanroom/__init__.py @@ -9,6 +9,11 @@ from azure.cli.core import AzCommandsLoader from azext_managedcleanroom._help import helps # pylint: disable=unused-import +try: + from azext_managedcleanroom import _breaking_change # noqa: F401 pylint: disable=unused-import +except ImportError: + pass + class ManagedcleanroomCommandsLoader(AzCommandsLoader): diff --git a/src/managedcleanroom/azext_managedcleanroom/_breaking_change.py b/src/managedcleanroom/azext_managedcleanroom/_breaking_change.py new file mode 100644 index 00000000000..1d6d8dd744a --- /dev/null +++ b/src/managedcleanroom/azext_managedcleanroom/_breaking_change.py @@ -0,0 +1,13 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from azure.cli.core.breaking_change import register_command_deprecate + +# Register the deprecated command that was removed in v1.0.0b5 +register_command_deprecate( + 'managedcleanroom frontend analytics cleanroompolicy', + redirect='managedcleanroom frontend analytics skr-policy', + expiration='1.0.0b5' +) diff --git a/src/managedcleanroom/azext_managedcleanroom/_frontend_auth.py b/src/managedcleanroom/azext_managedcleanroom/_frontend_auth.py index 138dce170f1..4826c1cbbba 100644 --- a/src/managedcleanroom/azext_managedcleanroom/_frontend_auth.py +++ b/src/managedcleanroom/azext_managedcleanroom/_frontend_auth.py @@ -32,12 +32,14 @@ def get_frontend_token(cmd): # Priority 0: explicit token via environment variable (for local/test envs # only) - env_token = os.environ.get('MANAGEDCLEANROOM_ACCESS_TOKEN') + env_token = os.environ.get("MANAGEDCLEANROOM_ACCESS_TOKEN") if env_token: logger.warning( - "Using token from MANAGEDCLEANROOM_ACCESS_TOKEN env var FOR TESTING PURPOSES ONLY") + "Using token from MANAGEDCLEANROOM_ACCESS_TOKEN env var FOR TESTING PURPOSES ONLY" + ) from collections import namedtuple - AccessToken = namedtuple('AccessToken', ['token', 'expires_on']) + + AccessToken = namedtuple("AccessToken", ["token", "expires_on"]) token_obj = AccessToken(token=env_token, expires_on=0) return (token_obj, subscription, None) @@ -49,20 +51,46 @@ def get_frontend_token(cmd): msal_token = get_msal_token(cmd) if msal_token: logger.debug("Using MSAL device code flow token") - return (msal_token[0], subscription, msal_token[2]) + from collections import namedtuple + + AccessToken = namedtuple("AccessToken", ["token", "expires_on"]) + token_obj = AccessToken(token=msal_token[0], expires_on=0) + return (token_obj, subscription, msal_token[2]) logger.debug("Using Azure CLI (az login) token") - return profile.get_raw_token( - subscription=subscription, - resource=auth_scope + raw_token = profile.get_raw_token( + subscription=subscription, resource=auth_scope ) + # Normalize to AccessToken object + from collections import namedtuple + + AccessToken = namedtuple("AccessToken", ["token", "expires_on"]) + + # Handle different return types from get_raw_token + if isinstance(raw_token, tuple) and len(raw_token) >= 3: + # Tuple format: ('Bearer', 'token_string', {...}) + # raw_token[2] should be a dict with metadata + metadata = raw_token[2] if isinstance(raw_token[2], dict) else {} + token_obj = AccessToken( + token=raw_token[1], expires_on=metadata.get("expiresOn", 0) + ) + elif hasattr(raw_token, "token"): + # Already AccessToken object + token_obj = raw_token + else: + # Raw string token + token_obj = AccessToken(token=str(raw_token), expires_on=0) + + return (token_obj, subscription, None) + except Exception as ex: raise CLIError( - f'Failed to get access token: {str(ex)}\n\n' - 'Please authenticate using one of:\n' - ' 1. az managedcleanroom frontend login (MSAL device code flow)\n' - ' 2. az login (Azure CLI authentication)\n') + f"Failed to get access token: {str(ex)}\n\n" + "Please authenticate using one of:\n" + " 1. az managedcleanroom frontend login (MSAL device code flow)\n" + " 2. az login (Azure CLI authentication)\n" + ) from ex def get_frontend_config(cmd): @@ -73,7 +101,7 @@ def get_frontend_config(cmd): :rtype: str or None """ config = cmd.cli_ctx.config - return config.get('managedcleanroom-frontend', 'endpoint', fallback=None) + return config.get("managedcleanroom-frontend", "endpoint", fallback=None) def set_frontend_config(cmd, endpoint): @@ -83,10 +111,7 @@ def set_frontend_config(cmd, endpoint): :param endpoint: API endpoint URL to store :type endpoint: str """ - cmd.cli_ctx.config.set_value( - 'managedcleanroom-frontend', - 'endpoint', - endpoint) + cmd.cli_ctx.config.set_value("managedcleanroom-frontend", "endpoint", endpoint) def get_frontend_client(cmd, endpoint=None, api_version=None): @@ -104,33 +129,40 @@ def get_frontend_client(cmd, endpoint=None, api_version=None): :raises: CLIError if token fetch fails or endpoint not configured """ from .analytics_frontend_api import AnalyticsFrontendAPI - from azure.core.pipeline.policies import BearerTokenCredentialPolicy, SansIOHTTPPolicy + from azure.core.pipeline.policies import ( + BearerTokenCredentialPolicy, + SansIOHTTPPolicy, + ) # Use provided api_version or default if api_version is None: - api_version = '2026-03-01-preview' + api_version = "2026-03-01-preview" api_endpoint = endpoint or get_frontend_config(cmd) if not api_endpoint: raise CLIError( - 'Analytics Frontend API endpoint not configured.\n' - 'Configure using: az config set managedcleanroom-frontend.endpoint=\n' - 'Or use the --endpoint flag with your command.') + "Analytics Frontend API endpoint not configured.\n" + "Configure using: az config set managedcleanroom-frontend.endpoint=\n" + "Or use the --endpoint flag with your command." + ) access_token_obj, _, _ = get_frontend_token(cmd) logger.debug( - "Creating Analytics Frontend API client for endpoint: %s", - api_endpoint) + "Creating Analytics Frontend API client for endpoint: %s", api_endpoint + ) # Check if this is a local development endpoint - is_local = api_endpoint.startswith( - 'http://localhost') or api_endpoint.startswith('http://127.0.0.1') + is_local = api_endpoint.startswith("http://localhost") or api_endpoint.startswith( + "http://127.0.0.1" + ) # Create simple credential wrapper for the access token - credential = type('TokenCredential', (), { - 'get_token': lambda self, *args, **kwargs: access_token_obj - })() + credential = type( + "TokenCredential", + (), + {"get_token": lambda self, *args, **kwargs: access_token_obj}, + )() if is_local: # For local development, create a custom auth policy that bypasses @@ -146,7 +178,7 @@ def on_request(self, request): # Extract token string from AccessToken object # The token might be a tuple ('Bearer', 'token_string') or just # the token string - if hasattr(self._token, 'token'): + if hasattr(self._token, "token"): token_value = self._token.token else: token_value = self._token @@ -157,10 +189,11 @@ def on_request(self, request): else: token_string = str(token_value) - auth_header = f'Bearer {token_string}' + auth_header = f"Bearer {token_string}" logger.debug( - "Setting Authorization header: Bearer %s...", token_string[:50]) - request.http_request.headers['Authorization'] = auth_header + "Setting Authorization header: Bearer %s...", token_string[:50] + ) + request.http_request.headers["Authorization"] = auth_header auth_policy = LocalBearerTokenPolicy(access_token_obj) else: @@ -168,18 +201,31 @@ def on_request(self, request): # enforcement # Use configured auth_scope with .default suffix for Azure SDK from ._msal_auth import get_auth_scope + scope = get_auth_scope(cmd) - if not scope.endswith('/.default'): - scope = f'{scope}/.default' + if not scope.endswith("/.default"): + scope = f"{scope}/.default" - auth_policy = BearerTokenCredentialPolicy( - credential, - scope + auth_policy = BearerTokenCredentialPolicy(credential, scope) + + # Handle SSL verification settings + import os + + client_kwargs = {} + if os.environ.get("AZURE_CLI_DISABLE_CONNECTION_VERIFICATION"): + logger.warning( + "SSL verification disabled via AZURE_CLI_DISABLE_CONNECTION_VERIFICATION" ) + client_kwargs["connection_verify"] = False + elif os.environ.get("REQUESTS_CA_BUNDLE"): + ca_bundle = os.environ["REQUESTS_CA_BUNDLE"] + logger.debug("Using custom CA bundle: %s", ca_bundle) + client_kwargs["connection_verify"] = ca_bundle # Return configured client return AnalyticsFrontendAPI( endpoint=api_endpoint, api_version=api_version, - authentication_policy=auth_policy + authentication_policy=auth_policy, + **client_kwargs, ) diff --git a/src/managedcleanroom/azext_managedcleanroom/_frontend_commands.py b/src/managedcleanroom/azext_managedcleanroom/_frontend_commands.py index 540c0617bbd..361a60ebdd7 100644 --- a/src/managedcleanroom/azext_managedcleanroom/_frontend_commands.py +++ b/src/managedcleanroom/azext_managedcleanroom/_frontend_commands.py @@ -32,8 +32,8 @@ def load_frontend_command_table(loader, _): with loader.command_group('managedcleanroom frontend analytics', custom_command_type=frontend_custom) as g: g.custom_show_command('show', 'frontend_collaboration_analytics_show') g.custom_command( - 'cleanroompolicy', - 'frontend_collaboration_analytics_cleanroompolicy') + 'skr-policy', + 'frontend_collaboration_analytics_skr_policy') # OIDC commands with loader.command_group('managedcleanroom frontend oidc', custom_command_type=frontend_custom) as g: diff --git a/src/managedcleanroom/azext_managedcleanroom/_frontend_custom.py b/src/managedcleanroom/azext_managedcleanroom/_frontend_custom.py index 5ca97afba3c..e646cd969cf 100644 --- a/src/managedcleanroom/azext_managedcleanroom/_frontend_custom.py +++ b/src/managedcleanroom/azext_managedcleanroom/_frontend_custom.py @@ -7,7 +7,11 @@ """Custom command implementations for Analytics Frontend API""" from knack.log import get_logger -from ._frontend_auth import get_frontend_client, set_frontend_config, get_frontend_config +from ._frontend_auth import ( + get_frontend_client, + set_frontend_config, + get_frontend_config, +) logger = get_logger(__name__) @@ -16,6 +20,7 @@ # Base Collaboration Commands # ============================================================================ + def frontend_collaboration_list(cmd, active_only=False, api_version=None): """List all collaborations @@ -29,10 +34,8 @@ def frontend_collaboration_list(cmd, active_only=False, api_version=None): def frontend_collaboration_show( - cmd, - collaboration_id, - active_only=False, - api_version=None): + cmd, collaboration_id, active_only=False, api_version=None +): """Show collaboration details :param cmd: CLI command context @@ -42,12 +45,10 @@ def frontend_collaboration_show( :return: Collaboration details """ client = get_frontend_client(cmd, api_version=api_version) - return client.collaboration.id_get( - collaboration_id, active_only=active_only) + return client.collaboration.id_get(collaboration_id, active_only=active_only) -def frontend_collaboration_report_show( - cmd, collaboration_id, api_version=None): +def frontend_collaboration_report_show(cmd, collaboration_id, api_version=None): """Get collaboration report (comprehensive attestation report) Replaces the deprecated attestation cgs and cleanroom commands. @@ -66,8 +67,8 @@ def frontend_collaboration_report_show( # Analytics Commands # ============================================================================ -def frontend_collaboration_analytics_show( - cmd, collaboration_id, api_version=None): + +def frontend_collaboration_analytics_show(cmd, collaboration_id, api_version=None): """Show analytics information for a collaboration :param cmd: CLI command context @@ -79,26 +80,29 @@ def frontend_collaboration_analytics_show( return client.collaboration.analytics_get(collaboration_id) -def frontend_collaboration_analytics_cleanroompolicy( - cmd, collaboration_id, api_version=None): - """Get cleanroom policy for analytics workload +def frontend_collaboration_analytics_skr_policy( + cmd, collaboration_id, dataset_id, api_version=None +): + """Get SKR policy for a dataset in analytics workload :param cmd: CLI command context :param collaboration_id: Collaboration identifier + :param dataset_id: Dataset identifier :param api_version: API version to use for this request - :return: Cleanroom policy + :return: SKR policy for the dataset """ client = get_frontend_client(cmd, api_version=api_version) - return client.collaboration.analytics_cleanroompolicy_get( - collaboration_id) + return client.collaboration.analytics_skr_policy_get(collaboration_id, dataset_id) # ============================================================================ # OIDC Commands # ============================================================================ + def frontend_collaboration_oidc_issuerinfo_show( - cmd, collaboration_id, api_version=None): + cmd, collaboration_id, api_version=None +): """Show OIDC issuer information :param cmd: CLI command context @@ -111,7 +115,8 @@ def frontend_collaboration_oidc_issuerinfo_show( def frontend_collaboration_oidc_set_issuer_url( - cmd, collaboration_id, url, api_version=None): + cmd, collaboration_id, url, api_version=None +): """Set collaboration OIDC issuer URL :param cmd: CLI command context @@ -122,12 +127,10 @@ def frontend_collaboration_oidc_set_issuer_url( """ body = {"url": url} client = get_frontend_client(cmd, api_version=api_version) - return client.collaboration.oidc_set_issuer_url_post( - collaboration_id, body=body) + return client.collaboration.oidc_set_issuer_url_post(collaboration_id, body=body) -def frontend_collaboration_oidc_keys_show( - cmd, collaboration_id, api_version=None): +def frontend_collaboration_oidc_keys_show(cmd, collaboration_id, api_version=None): """Get collaboration OIDC signing keys (JWKS format) :param cmd: CLI command context @@ -143,11 +146,10 @@ def frontend_collaboration_oidc_keys_show( # Invitation Commands # ============================================================================ + def frontend_collaboration_invitation_list( - cmd, - collaboration_id, - pending_only=False, - api_version=None): + cmd, collaboration_id, pending_only=False, api_version=None +): """List invitations for a collaboration :param cmd: CLI command context @@ -158,11 +160,13 @@ def frontend_collaboration_invitation_list( """ client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.invitations_get( - collaboration_id, pending_only=pending_only) + collaboration_id, pending_only=pending_only + ) def frontend_collaboration_invitation_show( - cmd, collaboration_id, invitation_id, api_version=None): + cmd, collaboration_id, invitation_id, api_version=None +): """Show invitation details :param cmd: CLI command context @@ -172,12 +176,12 @@ def frontend_collaboration_invitation_show( :return: Invitation details """ client = get_frontend_client(cmd, api_version=api_version) - return client.collaboration.invitation_id_get( - collaboration_id, invitation_id) + return client.collaboration.invitation_id_get(collaboration_id, invitation_id) def frontend_collaboration_invitation_accept( - cmd, collaboration_id, invitation_id, api_version=None): + cmd, collaboration_id, invitation_id, api_version=None +): """Accept an invitation :param cmd: CLI command context @@ -188,15 +192,16 @@ def frontend_collaboration_invitation_accept( """ client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.invitation_id_accept_post( - collaboration_id, invitation_id) + collaboration_id, invitation_id + ) # ============================================================================ # Dataset Commands # ============================================================================ -def frontend_collaboration_dataset_list( - cmd, collaboration_id, api_version=None): + +def frontend_collaboration_dataset_list(cmd, collaboration_id, api_version=None): """List datasets for a collaboration :param cmd: CLI command context @@ -209,10 +214,8 @@ def frontend_collaboration_dataset_list( def frontend_collaboration_dataset_show( - cmd, - collaboration_id, - document_id, - api_version=None): + cmd, collaboration_id, document_id, api_version=None +): """Show dataset details :param cmd: CLI command context @@ -223,31 +226,35 @@ def frontend_collaboration_dataset_show( """ client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_datasets_document_id_get( - collaboration_id, document_id) + collaboration_id, document_id + ) # pylint: disable=too-many-locals def frontend_collaboration_dataset_publish( - cmd, collaboration_id, document_id, - body=None, - storage_account_url=None, - container_name=None, - storage_account_type=None, - encryption_mode=None, - schema_file=None, - schema_format=None, - access_mode=None, - allowed_fields=None, - identity_name=None, - identity_client_id=None, - identity_tenant_id=None, - identity_issuer_url=None, - dek_keyvault_url=None, - dek_secret_id=None, - kek_keyvault_url=None, - kek_secret_id=None, - kek_maa_url=None, - api_version=None): + cmd, + collaboration_id, + document_id, + body=None, + storage_account_url=None, + container_name=None, + storage_account_type=None, + encryption_mode=None, + schema_file=None, + schema_format=None, + access_mode=None, + allowed_fields=None, + identity_name=None, + identity_client_id=None, + identity_tenant_id=None, + identity_issuer_url=None, + dek_keyvault_url=None, + dek_secret_id=None, + kek_keyvault_url=None, + kek_secret_id=None, + kek_maa_url=None, + api_version=None, +): """Publish a dataset :param cmd: CLI command context @@ -278,16 +285,26 @@ def frontend_collaboration_dataset_publish( from azure.cli.core.util import CLIError # Check for mutual exclusion: body vs parameters - has_params = any([ - storage_account_url, container_name, storage_account_type, encryption_mode, - schema_file, access_mode, identity_name, identity_client_id, - identity_tenant_id, identity_issuer_url - ]) + has_params = any( + [ + storage_account_url, + container_name, + storage_account_type, + encryption_mode, + schema_file, + access_mode, + identity_name, + identity_client_id, + identity_tenant_id, + identity_issuer_url, + ] + ) if body and has_params: raise CLIError( - 'Cannot use --body together with individual parameters. ' - 'Use either --body or the parameter flags.') + "Cannot use --body together with individual parameters. " + "Use either --body or the parameter flags." + ) # Legacy mode: use body directly if body: @@ -295,118 +312,132 @@ def frontend_collaboration_dataset_publish( body = json.loads(body) client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_datasets_document_id_publish_post( - collaboration_id, document_id, body) + collaboration_id, document_id, body + ) # Parameter mode: construct body from parameters if not has_params: raise CLIError( - 'Either --body or individual parameters (--storage-account-url, ' - '--container-name, etc.) must be provided.') + "Either --body or individual parameters (--storage-account-url, " + "--container-name, etc.) must be provided." + ) # Validate required parameters required_params = { - 'storage_account_url': storage_account_url, - 'container_name': container_name, - 'storage_account_type': storage_account_type, - 'encryption_mode': encryption_mode, - 'schema_file': schema_file, - 'access_mode': access_mode, - 'identity_name': identity_name, - 'identity_client_id': identity_client_id, - 'identity_tenant_id': identity_tenant_id, - 'identity_issuer_url': identity_issuer_url + "storage_account_url": storage_account_url, + "container_name": container_name, + "storage_account_type": storage_account_type, + "encryption_mode": encryption_mode, + "schema_file": schema_file, + "access_mode": access_mode, + "identity_name": identity_name, + "identity_client_id": identity_client_id, + "identity_tenant_id": identity_tenant_id, + "identity_issuer_url": identity_issuer_url, } missing = [k for k, v in required_params.items() if v is None] if missing: missing_params = ", ".join(f"--{k.replace('_', '-')}" for k in missing) - raise CLIError(f'Missing required parameters: {missing_params}') + raise CLIError(f"Missing required parameters: {missing_params}") # Validate CPK parameters if encryption_mode is CPK - if encryption_mode and encryption_mode.upper() == 'CPK': + if encryption_mode and encryption_mode.upper() == "CPK": cpk_params = { - 'dek_keyvault_url': dek_keyvault_url, - 'dek_secret_id': dek_secret_id, - 'kek_keyvault_url': kek_keyvault_url, - 'kek_secret_id': kek_secret_id, - 'kek_maa_url': kek_maa_url + "dek_keyvault_url": dek_keyvault_url, + "dek_secret_id": dek_secret_id, + "kek_keyvault_url": kek_keyvault_url, + "kek_secret_id": kek_secret_id, + "kek_maa_url": kek_maa_url, } missing_cpk = [k for k, v in cpk_params.items() if v is None] if missing_cpk: missing_cpk_params = ", ".join( - f"--{k.replace('_', '-')}" for k in missing_cpk) - raise CLIError( - f'CPK encryption mode requires: {missing_cpk_params}') + f"--{k.replace('_', '-')}" for k in missing_cpk + ) + raise CLIError(f"CPK encryption mode requires: {missing_cpk_params}") - # Load schema from file + # Load schema from file (handle Azure CLI auto-loading) schema_content = None - if schema_file.startswith('@'): + + if isinstance(schema_file, dict): + # CLI already parsed the @file.json and loaded it as dict + schema_content = schema_file + elif isinstance(schema_file, str) and schema_file.startswith("@"): + # Manual file reference (not auto-loaded) schema_path = schema_file[1:] try: - with open(schema_path, 'r') as f: + with open(schema_path, "r", encoding="utf-8") as f: schema_content = json.load(f) - except FileNotFoundError: - raise CLIError(f'Schema file not found: {schema_path}') + except FileNotFoundError as exc: + raise CLIError(f"Schema file not found: {schema_path}") from exc + except json.JSONDecodeError as e: + raise CLIError(f"Invalid JSON in schema file: {str(e)}") from e + elif isinstance(schema_file, str): + # CLI auto-loaded file content as string + try: + schema_content = json.loads(schema_file) except json.JSONDecodeError as e: - raise CLIError(f'Invalid JSON in schema file: {str(e)}') + raise CLIError(f"Invalid JSON in schema content: {str(e)}") from e else: - raise CLIError('--schema-file must be a file path prefixed with @ (e.g., @schema.json)') + raise CLIError( + "--schema-file must be a file path prefixed with @ (e.g., @schema.json) or valid JSON" + ) # Override format if provided if schema_format: - schema_content['format'] = schema_format + schema_content["format"] = schema_format # Build datasetAccessPolicy - dataset_access_policy = { - 'accessMode': access_mode - } + dataset_access_policy = {"accessMode": access_mode} if allowed_fields: - dataset_access_policy['allowedFields'] = [f.strip() for f in allowed_fields.split(',')] + dataset_access_policy["allowedFields"] = [ + f.strip() for f in allowed_fields.split(",") + ] # Build store configuration store = { - 'storageAccountUrl': storage_account_url, - 'containerName': container_name, - 'storageAccountType': storage_account_type, - 'encryptionMode': encryption_mode + "storageAccountUrl": storage_account_url, + "containerName": container_name, + "storageAccountType": storage_account_type, + "encryptionMode": encryption_mode, } # Build identity identity = { - 'name': identity_name, - 'clientId': identity_client_id, - 'tenantId': identity_tenant_id, - 'issuerUrl': identity_issuer_url + "name": identity_name, + "clientId": identity_client_id, + "tenantId": identity_tenant_id, + "issuerUrl": identity_issuer_url, } # Construct final body body = { - 'name': document_id, - 'datasetSchema': schema_content, - 'datasetAccessPolicy': dataset_access_policy, - 'store': store, - 'identity': identity + "name": document_id, + "datasetSchema": schema_content, + "datasetAccessPolicy": dataset_access_policy, + "store": store, + "identity": identity, } # Add DEK/KEK for CPK mode - if encryption_mode and encryption_mode.upper() == 'CPK': - body['dek'] = { - 'keyVaultUrl': dek_keyvault_url, - 'secretId': dek_secret_id - } - body['kek'] = { - 'keyVaultUrl': kek_keyvault_url, - 'secretId': kek_secret_id, - 'maaUrl': kek_maa_url + if encryption_mode and encryption_mode.upper() == "CPK": + body["dek"] = {"keyVaultUrl": dek_keyvault_url, "secretId": dek_secret_id} + body["kek"] = { + "keyVaultUrl": kek_keyvault_url, + "secretId": kek_secret_id, + "maaUrl": kek_maa_url, } client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_datasets_document_id_publish_post( - collaboration_id, document_id, body) + collaboration_id, document_id, body + ) def frontend_collaboration_dataset_queries_list( - cmd, collaboration_id, document_id, api_version=None): + cmd, collaboration_id, document_id, api_version=None +): """List queries that use a specific dataset :param cmd: CLI command context @@ -417,18 +448,18 @@ def frontend_collaboration_dataset_queries_list( """ client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_datasets_document_id_queries_get( - collaboration_id, document_id) + collaboration_id, document_id + ) # ============================================================================ # Consent Commands # ============================================================================ + def frontend_collaboration_consent_check( - cmd, - collaboration_id, - document_id, - api_version=None): + cmd, collaboration_id, document_id, api_version=None +): """Check consent document status :param cmd: CLI command context @@ -438,12 +469,12 @@ def frontend_collaboration_consent_check( :return: Consent status """ client = get_frontend_client(cmd, api_version=api_version) - return client.collaboration.consent_document_id_get( - collaboration_id, document_id) + return client.collaboration.consent_document_id_get(collaboration_id, document_id) def frontend_collaboration_consent_set( - cmd, collaboration_id, document_id, consent_action, api_version=None): + cmd, collaboration_id, document_id, consent_action, api_version=None +): """Set consent document action NOTE: API changed - consent action is now 'enable' or 'disable' (not accept/reject) @@ -466,6 +497,7 @@ def frontend_collaboration_consent_set( # Query Commands # ============================================================================ + def frontend_collaboration_query_list(cmd, collaboration_id, api_version=None): """List queries for a collaboration @@ -479,10 +511,8 @@ def frontend_collaboration_query_list(cmd, collaboration_id, api_version=None): def frontend_collaboration_query_show( - cmd, - collaboration_id, - document_id, - api_version=None): + cmd, collaboration_id, document_id, api_version=None +): """Show query details :param cmd: CLI command context @@ -493,18 +523,22 @@ def frontend_collaboration_query_show( """ client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_queries_document_id_get( - collaboration_id, document_id) + collaboration_id, document_id + ) # pylint: disable=too-many-locals,too-many-branches def frontend_collaboration_query_publish( - cmd, collaboration_id, document_id, - body=None, - query_segment=None, - execution_sequence=None, - input_datasets=None, - output_dataset=None, - api_version=None): + cmd, + collaboration_id, + document_id, + body=None, + query_segment=None, + execution_sequence=None, + input_datasets=None, + output_dataset=None, + api_version=None, +): """Publish a query :param cmd: CLI command context @@ -522,13 +556,15 @@ def frontend_collaboration_query_publish( from azure.cli.core.util import CLIError # Check for mutual exclusion: body vs parameters - has_params = any([ - query_segment, execution_sequence, input_datasets, output_dataset]) + has_params = any( + [query_segment, execution_sequence, input_datasets, output_dataset] + ) if body and has_params: raise CLIError( - 'Cannot use --body together with individual parameters. ' - 'Use either --body or the parameter flags.') + "Cannot use --body together with individual parameters. " + "Use either --body or the parameter flags." + ) # Legacy mode: use body directly if body: @@ -536,142 +572,178 @@ def frontend_collaboration_query_publish( body = json.loads(body) client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_queries_document_id_publish_post( - collaboration_id, document_id, body) + collaboration_id, document_id, body + ) # Parameter mode: construct body from parameters if not has_params: raise CLIError( - 'Either --body or individual parameters (--query-segment, ' - '--execution-sequence, etc.) must be provided.') + "Either --body or individual parameters (--query-segment, " + "--execution-sequence, etc.) must be provided." + ) # Validate required parameters if not query_segment: - raise CLIError( - '--query-segment is required (can be specified multiple times)') + raise CLIError("--query-segment is required (can be specified multiple times)") if not input_datasets: - raise CLIError('--input-datasets is required') + raise CLIError("--input-datasets is required") if not output_dataset: - raise CLIError('--output-dataset is required') - - # Parse query segments - detect mode (FILE vs INLINE) - file_segments = [s for s in query_segment if s.startswith('@')] - inline_segments = [s for s in query_segment if not s.startswith('@')] - - # Cannot mix file and inline segments - if file_segments and inline_segments: - raise CLIError( - 'Cannot mix @file.json and inline SQL segments. ' - 'Either use all @file.json segments or all inline SQL strings.') + raise CLIError("--output-dataset is required") + # Parse query segments - detect mode (FILE/DICT vs INLINE) + # Azure CLI auto-loads @file.json content but for action='append' params, + # the file content arrives as a raw string (not parsed dict). We need to + # detect JSON strings vs inline SQL. query_data = [] - - if file_segments: - # FILE mode: segments are JSON files with full object structure - if execution_sequence: - raise CLIError( - '--execution-sequence must not be provided when using ' - '@file.json segments. Include executionSequence inside ' - 'each JSON file.') - - for seg in file_segments: + parsed_segments = [] + + for seg in query_segment: + if isinstance(seg, dict): + # Already parsed dict (unlikely with append but handle it) + parsed_segments.append(seg) + elif isinstance(seg, str) and seg.startswith("@"): + # Manual @file reference (not auto-loaded) file_path = seg[1:] try: - with open(file_path, 'r') as f: + with open(file_path, "r", encoding="utf-8") as f: segment_obj = json.load(f) - except FileNotFoundError: - raise CLIError(f'Query segment file not found: {file_path}') + except FileNotFoundError as exc: + raise CLIError(f"Query segment file not found: {file_path}") from exc except json.JSONDecodeError as e: raise CLIError( - f'Invalid JSON in segment file {file_path}: {str(e)}') + f"Invalid JSON in segment file {file_path}: {str(e)}" + ) from e + parsed_segments.append(segment_obj) + elif isinstance(seg, str): + # Could be auto-loaded JSON string or inline SQL. + # Try JSON parse first. + seg_stripped = seg.strip() + if seg_stripped.startswith("{"): + try: + segment_obj = json.loads(seg_stripped) + if isinstance(segment_obj, dict) and "data" in segment_obj: + parsed_segments.append(segment_obj) + continue + except json.JSONDecodeError: + pass + # Treat as inline SQL + parsed_segments.append(seg) + else: + raise CLIError(f"Unexpected segment type: {type(seg)}") + + # Now classify: structured (dict) vs inline (str) + structured = [s for s in parsed_segments if isinstance(s, dict)] + inline_segments = [s for s in parsed_segments if isinstance(s, str)] + + # Cannot mix structured and inline segments + if structured and inline_segments: + raise CLIError( + "Cannot mix @file.json / JSON-dict and inline SQL segments. " + "Either use all structured segments or all inline SQL strings." + ) - # Validate required fields - if 'data' not in segment_obj: - raise CLIError( - f'Segment file {file_path} must contain "data" field') - if 'executionSequence' not in segment_obj: - raise CLIError( - f'Segment file {file_path} must contain ' - f'"executionSequence" field') + if structured: + # STRUCTURED mode: segments are dicts with full object structure + if execution_sequence: + raise CLIError( + "--execution-sequence must not be provided when using " + "@file.json segments. Include executionSequence inside " + "each JSON file." + ) - # Build segment with defaults for optional fields - query_data.append({ - 'data': segment_obj['data'], - 'executionSequence': segment_obj['executionSequence'], - 'preConditions': segment_obj.get('preConditions', ''), - 'postFilters': segment_obj.get('postFilters', '') - }) + for segment_obj in structured: + # Validate required fields + if "data" not in segment_obj: + raise CLIError('Segment must contain "data" field') + if "executionSequence" not in segment_obj: + raise CLIError('Segment must contain "executionSequence" field') + + query_data.append( + { + "data": segment_obj["data"], + "executionSequence": segment_obj["executionSequence"], + "preConditions": segment_obj.get("preConditions", ""), + "postFilters": segment_obj.get("postFilters", ""), + } + ) else: # INLINE mode: segments are raw SQL strings if not execution_sequence: raise CLIError( - '--execution-sequence is required when using inline SQL ' - 'segments.') + "--execution-sequence is required when using inline SQL segments." + ) # Parse execution sequence try: - exec_seq = [int(x.strip()) - for x in execution_sequence.split(',')] - except ValueError: + exec_seq = [int(x.strip()) for x in execution_sequence.split(",")] + except ValueError as exc: raise CLIError( - '--execution-sequence must be comma-separated integers ' - '(e.g., "1,1,2")') + '--execution-sequence must be comma-separated integers (e.g., "1,1,2")' + ) from exc # Validate segment count matches execution sequence count if len(inline_segments) != len(exec_seq): raise CLIError( - f'Number of query segments ({len(inline_segments)}) must ' - f'match execution sequence count ({len(exec_seq)})') + f"Number of query segments ({len(inline_segments)}) must " + f"match execution sequence count ({len(exec_seq)})" + ) # Build queryData array from inline SQL for sql, seq in zip(inline_segments, exec_seq): - query_data.append({ - 'data': sql, - 'executionSequence': seq, - 'preConditions': '', - 'postFilters': '' - }) + query_data.append( + { + "data": sql, + "executionSequence": seq, + "preConditions": "", + "postFilters": "", + } + ) # Parse input datasets (comma-separated datasetId:viewName pairs) input_ds_list = [] - for ds in input_datasets.split(','): + for ds in input_datasets.split(","): ds = ds.strip() - if ':' not in ds: + if ":" not in ds: raise CLIError( - f'Invalid input dataset format: {ds}. ' - f'Expected format: datasetId:viewName') - dataset_id, view_name = ds.split(':', 1) - input_ds_list.append(f'{dataset_id.strip()}:{view_name.strip()}') - input_ds_str = ','.join(input_ds_list) + f"Invalid input dataset format: {ds}. " + f"Expected format: datasetId:viewName" + ) + dataset_id, view_name = ds.split(":", 1) + input_ds_list.append(f"{dataset_id.strip()}:{view_name.strip()}") + input_ds_str = ",".join(input_ds_list) # Parse output dataset - if ':' not in output_dataset: + if ":" not in output_dataset: raise CLIError( - f'Invalid output dataset format: {output_dataset}. ' - f'Expected format: datasetId:viewName') + f"Invalid output dataset format: {output_dataset}. " + f"Expected format: datasetId:viewName" + ) # Construct body body = { - 'inputDatasets': input_ds_str, - 'outputDataset': output_dataset, - 'queryData': query_data + "inputDatasets": input_ds_str, + "outputDataset": output_dataset, + "queryData": query_data, } client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_queries_document_id_publish_post( - collaboration_id, document_id, body) + collaboration_id, document_id, body + ) def frontend_collaboration_query_run( - cmd, - collaboration_id, - document_id, - body=None, - dry_run=False, - start_date=None, - end_date=None, - use_optimizer=False, - api_version=None): + cmd, + collaboration_id, + document_id, + body=None, + dry_run=False, + start_date=None, + end_date=None, + use_optimizer=False, + api_version=None, +): """Run a query :param cmd: CLI command context @@ -695,8 +767,9 @@ def frontend_collaboration_query_run( if body and has_params: raise CLIError( - 'Cannot use --body together with individual parameters. ' - 'Use either --body or the parameter flags.') + "Cannot use --body together with individual parameters. " + "Use either --body or the parameter flags." + ) # Handle body parameter - convert string to dict if needed if body and isinstance(body, str): @@ -709,30 +782,27 @@ def frontend_collaboration_query_run( # Add parameter values to body if in parameter mode if has_params: if dry_run: - body['dryRun'] = True + body["dryRun"] = True if start_date: - body['startDate'] = start_date + body["startDate"] = start_date if end_date: - body['endDate'] = end_date + body["endDate"] = end_date if use_optimizer: - body['useOptimizer'] = True + body["useOptimizer"] = True # Auto-generate runId if not provided - if 'runId' not in body: - body['runId'] = str(uuid.uuid4()) + if "runId" not in body: + body["runId"] = str(uuid.uuid4()) client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_queries_document_id_run_post( - collaboration_id, document_id, body=body) + collaboration_id, document_id, body=body + ) def frontend_collaboration_query_vote( - cmd, - collaboration_id, - document_id, - vote_action, - proposal_id=None, - api_version=None): + cmd, collaboration_id, document_id, vote_action, proposal_id=None, api_version=None +): """Vote on a query (unified accept/reject endpoint) :param cmd: CLI command context @@ -743,24 +813,25 @@ def frontend_collaboration_query_vote( :param api_version: API version to use for this request :return: Vote result (None on success - 204 No Content) """ - body = { - "voteAction": vote_action - } + body = {"voteAction": vote_action} if proposal_id: body["proposalId"] = proposal_id client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_queries_document_id_vote_post( - collaboration_id, document_id, body=body) + collaboration_id, document_id, body=body + ) # ============================================================================ # Query Run History Commands # ============================================================================ + def frontend_collaboration_query_runhistory_list( - cmd, collaboration_id, document_id, api_version=None): + cmd, collaboration_id, document_id, api_version=None +): """List query run history :param cmd: CLI command context @@ -770,13 +841,14 @@ def frontend_collaboration_query_runhistory_list( :return: List of query runs """ client = get_frontend_client(cmd, api_version=api_version) - return client.collaboration.analytics_queries_document_id_runhistory_get( + return client.collaboration.analytics_queries_document_id_runs_get( collaboration_id, document_id ) def frontend_collaboration_query_runresult_show( - cmd, collaboration_id, job_id, api_version=None): + cmd, collaboration_id, job_id, api_version=None +): """Show query job result details :param cmd: CLI command context @@ -786,21 +858,17 @@ def frontend_collaboration_query_runresult_show( :return: Query job result details """ client = get_frontend_client(cmd, api_version=api_version) - return client.collaboration.analytics_queries_jobid_get( - collaboration_id, job_id) + return client.collaboration.analytics_runs_job_id_get(collaboration_id, job_id) # ============================================================================ # Audit Commands # ============================================================================ + def frontend_collaboration_audit_list( - cmd, - collaboration_id, - scope=None, - from_seqno=None, - to_seqno=None, - api_version=None): + cmd, collaboration_id, scope=None, from_seqno=None, to_seqno=None, api_version=None +): """List audit events for a collaboration :param cmd: CLI command context @@ -813,15 +881,13 @@ def frontend_collaboration_audit_list( """ client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_auditevents_get( - collaboration_id, scope=scope, from_seqno=from_seqno, to_seqno=to_seqno) + collaboration_id, scope=scope, from_seqno=from_seqno, to_seqno=to_seqno + ) def frontend_collaboration_analytics_secret_set( - cmd, - collaboration_id, - secret_name, - secret_value, - api_version=None): + cmd, collaboration_id, secret_name, secret_value, api_version=None +): """Set secret for analytics workload :param cmd: CLI command context @@ -834,13 +900,15 @@ def frontend_collaboration_analytics_secret_set( body = {"secretValue": secret_value} client = get_frontend_client(cmd, api_version=api_version) return client.collaboration.analytics_secrets_secret_name_put( - collaboration_id, secret_name, body=body) + collaboration_id, secret_name, body=body + ) # ============================================================================ # Authentication Commands # ============================================================================ + def frontend_login(cmd, use_microsoft_identity=False): # pylint: disable=unused-argument """Login using Microsoft device code flow @@ -867,17 +935,13 @@ def frontend_login(cmd, use_microsoft_identity=False): # pylint: disable=unused oid = claims.get("oid", "Unknown") return { - 'status': 'success', - 'message': 'Login successful. Token cached for future use.', - 'authentication_method': 'MSAL device code flow', - 'user': { - 'name': name, - 'email': email, - 'oid': oid - } + "status": "success", + "message": "Login successful. Token cached for future use.", + "authentication_method": "MSAL device code flow", + "user": {"name": name, "email": email, "oid": oid}, } except Exception as ex: - raise CLIError(f'Login failed: {str(ex)}') + raise CLIError(f"Login failed: {str(ex)}") from ex def frontend_logout(cmd): # pylint: disable=unused-argument @@ -895,18 +959,19 @@ def frontend_logout(cmd): # pylint: disable=unused-argument try: clear_msal_cache() return { - 'status': 'success', - 'message': 'Logged out successfully. MSAL token cache cleared.', - 'note': 'Azure CLI authentication (az login) is not affected.' + "status": "success", + "message": "Logged out successfully. MSAL token cache cleared.", + "note": "Azure CLI authentication (az login) is not affected.", } except Exception as ex: - raise CLIError(f'Logout failed: {str(ex)}') + raise CLIError(f"Logout failed: {str(ex)}") from ex # ============================================================================ # Configuration Commands # ============================================================================ + def frontend_configure(cmd, endpoint=None, auth_scope=None): """Configure Analytics Frontend API settings @@ -921,19 +986,18 @@ def frontend_configure(cmd, endpoint=None, auth_scope=None): if endpoint: set_frontend_config(cmd, endpoint) - return {'status': 'success', 'endpoint': endpoint} + return {"status": "success", "endpoint": endpoint} if auth_scope: cmd.cli_ctx.config.set_value( - 'managedcleanroom-frontend', - 'auth_scope', - auth_scope + "managedcleanroom-frontend", "auth_scope", auth_scope ) - return {'status': 'success', 'auth_scope': auth_scope} + return {"status": "success", "auth_scope": auth_scope} config_endpoint = get_frontend_config(cmd) # Get effective auth_scope (env var takes priority over config) from ._msal_auth import get_auth_scope + config_auth_scope = get_auth_scope(cmd) auth_method = None @@ -942,56 +1006,61 @@ def frontend_configure(cmd, endpoint=None, auth_scope=None): msal_token = get_msal_token(cmd) if msal_token: - auth_method = 'MSAL device code flow' + auth_method = "MSAL device code flow" logged_in = True try: import base64 import json - token_parts = msal_token[0].split('.') + + token_parts = msal_token[0].split(".") if len(token_parts) >= 2: payload = token_parts[1] - payload += '=' * (4 - len(payload) % 4) + payload += "=" * (4 - len(payload) % 4) decoded = base64.b64decode(payload) claims = json.loads(decoded) - user_info = claims.get('preferred_username') or claims.get( - 'upn') or claims.get('email') or 'MSAL User' + user_info = ( + claims.get("preferred_username") + or claims.get("upn") + or claims.get("email") + or "MSAL User" + ) else: - user_info = 'MSAL User' + user_info = "MSAL User" except Exception: - user_info = 'MSAL User' + user_info = "MSAL User" msal_config = get_msal_config(cmd) return { - 'endpoint': config_endpoint, - 'auth_scope': config_auth_scope, - 'authentication_method': auth_method, - 'logged_in': logged_in, - 'user': user_info, - 'msal_config': { - 'client_id': msal_config['client_id'], - 'tenant_id': msal_config['tenant_id'], - 'scopes': msal_config['scopes'], - 'cache_dir': str(get_msal_cache_file().parent) - } + "endpoint": config_endpoint, + "auth_scope": config_auth_scope, + "authentication_method": auth_method, + "logged_in": logged_in, + "user": user_info, + "msal_config": { + "client_id": msal_config["client_id"], + "tenant_id": msal_config["tenant_id"], + "scopes": msal_config["scopes"], + "cache_dir": str(get_msal_cache_file().parent), + }, } profile = Profile(cli_ctx=cmd.cli_ctx) try: account = profile.get_subscription() - auth_method = 'Azure CLI (az login)' + auth_method = "Azure CLI (az login)" logged_in = True - user_info = account['user']['name'] + user_info = account["user"]["name"] except Exception: - auth_method = 'None' + auth_method = "None" logged_in = False user_info = None return { - 'endpoint': config_endpoint, - 'auth_scope': config_auth_scope, - 'authentication_method': auth_method, - 'logged_in': logged_in, - 'user': user_info + "endpoint": config_endpoint, + "auth_scope": config_auth_scope, + "authentication_method": auth_method, + "logged_in": logged_in, + "user": user_info, } diff --git a/src/managedcleanroom/azext_managedcleanroom/_help.py b/src/managedcleanroom/azext_managedcleanroom/_help.py index 6186b2ea1c3..8aa779e8c8c 100644 --- a/src/managedcleanroom/azext_managedcleanroom/_help.py +++ b/src/managedcleanroom/azext_managedcleanroom/_help.py @@ -176,12 +176,12 @@ text: az managedcleanroom frontend analytics show -c """ -helps['managedcleanroom frontend analytics cleanroompolicy'] = """ +helps['managedcleanroom frontend analytics skr-policy'] = """ type: command - short-summary: Get cleanroom policy + short-summary: Get SKR policy for a dataset examples: - - name: Get cleanroom policy - text: az managedcleanroom frontend analytics cleanroompolicy -c + - name: Get SKR policy for a specific dataset + text: az managedcleanroom frontend analytics skr-policy -c -d """ diff --git a/src/managedcleanroom/azext_managedcleanroom/_msal_auth.py b/src/managedcleanroom/azext_managedcleanroom/_msal_auth.py index ffbc5fc9728..98691074f39 100644 --- a/src/managedcleanroom/azext_managedcleanroom/_msal_auth.py +++ b/src/managedcleanroom/azext_managedcleanroom/_msal_auth.py @@ -33,16 +33,12 @@ def get_config_value(cmd, config_key, env_var_name, default_value): # Priority 1: Environment variable env_value = os.environ.get(env_var_name) if env_value: - logger.debug( - "Using %s from environment variable: %s", config_key, env_var_name) + logger.debug("Using %s from environment variable: %s", config_key, env_var_name) return env_value # Priority 2: Azure CLI config config = cmd.cli_ctx.config - config_value = config.get( - 'managedcleanroom-frontend', - config_key, - fallback=None) + config_value = config.get("managedcleanroom-frontend", config_key, fallback=None) if config_value: logger.debug("Using %s from Azure CLI config", config_key) return config_value @@ -59,31 +55,25 @@ def get_msal_config(cmd): :return: Dict with client_id, tenant_id, authority, scopes """ client_id = get_config_value( - cmd, - 'client_id', - 'MANAGEDCLEANROOM_CLIENT_ID', - DEFAULT_MS_CLIENT_ID) + cmd, "client_id", "MANAGEDCLEANROOM_CLIENT_ID", DEFAULT_MS_CLIENT_ID + ) tenant_id = get_config_value( - cmd, - 'tenant_id', - 'MANAGEDCLEANROOM_TENANT_ID', - DEFAULT_MS_TENANT_ID) + cmd, "tenant_id", "MANAGEDCLEANROOM_TENANT_ID", DEFAULT_MS_TENANT_ID + ) scopes_str = get_config_value( - cmd, - 'scopes', - 'MANAGEDCLEANROOM_SCOPES', - DEFAULT_MS_SCOPES) + cmd, "scopes", "MANAGEDCLEANROOM_SCOPES", DEFAULT_MS_SCOPES + ) # Parse scopes (comma-separated string to list) - scopes = [s.strip() for s in scopes_str.split(',')] + scopes = [s.strip() for s in scopes_str.split(",")] authority = f"https://login.microsoftonline.com/{tenant_id}" return { - 'client_id': client_id, - 'tenant_id': tenant_id, - 'authority': authority, - 'scopes': scopes + "client_id": client_id, + "tenant_id": tenant_id, + "authority": authority, + "scopes": scopes, } @@ -97,9 +87,9 @@ def get_auth_scope(cmd): """ return get_config_value( cmd, - 'auth_scope', - 'MANAGEDCLEANROOM_AUTH_SCOPE', - 'https://management.azure.com/' + "auth_scope", + "MANAGEDCLEANROOM_AUTH_SCOPE", + "https://management.azure.com/", ) @@ -168,22 +158,23 @@ def perform_device_code_flow(cmd): token_cache = load_cache(cache_file) app = msal.PublicClientApplication( - config['client_id'], - authority=config['authority'], - token_cache=token_cache + config["client_id"], authority=config["authority"], token_cache=token_cache ) # Check for existing account in cache account = None for acc in app.get_accounts(): - if acc["environment"] == "login.microsoftonline.com" and acc["realm"] == config['tenant_id']: + if ( + acc["environment"] == "login.microsoftonline.com" + and acc["realm"] == config["tenant_id"] + ): account = acc break if account: # Try silent token acquisition first logger.debug("Attempting silent token acquisition") - result = app.acquire_token_silent(config['scopes'], account=account) + result = app.acquire_token_silent(config["scopes"], account=account) if result and "access_token" in result: logger.debug("Successfully acquired token silently") save_cache(token_cache, cache_file) @@ -191,14 +182,14 @@ def perform_device_code_flow(cmd): # No cached token found, perform device code flow logger.info("Initiating device code flow authentication") - flow = app.initiate_device_flow(scopes=config['scopes']) + flow = app.initiate_device_flow(scopes=config["scopes"]) if "user_code" not in flow: raise Exception( "Failed to create device flow: {}".format( - flow.get( - 'error_description', - 'Unknown error'))) + flow.get("error_description", "Unknown error") + ) + ) # Display instructions to user (matches cleanroom pattern) print("Please go to", flow["verification_uri"]) @@ -214,8 +205,7 @@ def perform_device_code_flow(cmd): oid = result["id_token_claims"].get("oid", "") if email == "" or oid == "": - raise Exception( - "Login failed: missing email or oid in token claims") + raise Exception("Login failed: missing email or oid in token claims") logger.info("Authentication successful for user: %s", email) print("User:", name) @@ -229,6 +219,52 @@ def perform_device_code_flow(cmd): raise Exception("Login failed: {}".format(err)) +def _read_id_token_from_cache(token_cache, client_id, cache_file): + """Read the ID token (JWT) from the MSAL token cache. + + acquire_token_silent() does not return the id_token on silent refresh, + but it IS persisted in the cache under the 'IdToken' collection. + This helper extracts it, matching on client_id. + + :param token_cache: MSAL SerializableTokenCache (already loaded) + :param client_id: The MSAL application client ID to match + :param cache_file: Path to the cache JSON file (fallback) + :return: JWT id_token string, or None + """ + import json as _json + + # Approach 1: Use the in-memory cache's internal _cache dict. + # SerializableTokenCache stores the deserialized JSON in _cache. + try: + id_tokens = getattr(token_cache, "_cache", {}).get("IdToken", {}) + for _key, entry in id_tokens.items(): + if entry.get("client_id") == client_id: + secret = entry.get("secret", "") + if secret.startswith("eyJ") and secret.count(".") == 2: + logger.debug("Read ID token from in-memory MSAL cache") + return secret + except Exception as ex: + logger.debug("Failed to read ID token from in-memory cache: %s", ex) + + # Approach 2: Fall back to reading the JSON file directly. + try: + if os.path.exists(cache_file): + with open(cache_file, "r") as f: + cache_data = _json.load(f) + id_tokens = cache_data.get("IdToken", {}) + for _key, entry in id_tokens.items(): + if entry.get("client_id") == client_id: + secret = entry.get("secret", "") + if secret.startswith("eyJ") and secret.count(".") == 2: + logger.debug("Read ID token from cache file") + return secret + except Exception as ex: + logger.debug("Failed to read ID token from cache file: %s", ex) + + logger.debug("No matching ID token found in MSAL cache") + return None + + def get_msal_token(cmd): """Get cached MSAL access token if available @@ -250,9 +286,7 @@ def get_msal_token(cmd): token_cache = load_cache(cache_file) app = msal.PublicClientApplication( - config['client_id'], - authority=config['authority'], - token_cache=token_cache + config["client_id"], authority=config["authority"], token_cache=token_cache ) # Find account in cache @@ -265,27 +299,47 @@ def get_msal_token(cmd): account = accounts[0] logger.debug( "Attempting to acquire token for account: %s", - account.get('username', 'unknown')) + account.get("username", "unknown"), + ) # Try silent token acquisition - result = app.acquire_token_silent(config['scopes'], account=account) + result = app.acquire_token_silent(config["scopes"], account=account) if result and "access_token" in result: logger.debug("Successfully acquired MSAL token from cache") save_cache(token_cache, cache_file) # Extract tenant ID from token claims - tenant_id = result.get( - "id_token_claims", {}).get( - "tid", config['tenant_id']) - - # Return in format expected by get_frontend_token() - # (access_token, subscription, tenant_id) - return (result["access_token"], None, tenant_id) + tenant_id = result.get("id_token_claims", {}).get( + "tid", config["tenant_id"] + ) + + # The frontend service validates tokens as JWTs, so we need + # the id_token (starts with "eyJ"), NOT the opaque MS Graph + # access_token (starts with "EwA"). acquire_token_silent() + # does NOT include id_token in its return dict on silent + # refresh, but it IS stored in the MSAL token cache. + token = result.get("id_token") + if not token or not token.startswith("eyJ"): + # Read ID token directly from the MSAL cache. + # Try the in-memory cache object first, then fall back + # to reading the JSON file. + token = _read_id_token_from_cache( + token_cache, config["client_id"], cache_file + ) + + if not token: + logger.warning( + "No JWT id_token found in cache; falling back to " + "access_token (frontend may reject this)" + ) + token = result["access_token"] + + return (token, None, tenant_id) logger.debug( - "Failed to acquire token silently: %s", - result.get('error', 'Unknown error')) + "Failed to acquire token silently: %s", result.get("error", "Unknown error") + ) return None except Exception as ex: diff --git a/src/managedcleanroom/azext_managedcleanroom/_params.py b/src/managedcleanroom/azext_managedcleanroom/_params.py index 9594acfe9c2..9b10dde24ce 100644 --- a/src/managedcleanroom/azext_managedcleanroom/_params.py +++ b/src/managedcleanroom/azext_managedcleanroom/_params.py @@ -75,6 +75,11 @@ def load_arguments(self, _): # pylint: disable=unused-argument with self.argument_context('managedcleanroom frontend analytics') as c: c.argument('collaboration_id', collaboration_id_type) + # Analytics SKR policy context + with self.argument_context('managedcleanroom frontend analytics skr-policy') as c: + c.argument('dataset_id', options_list=['--dataset-id', '-d'], + help='Dataset identifier for which to retrieve SKR policy') + # OIDC context with self.argument_context('managedcleanroom frontend oidc issuerinfo') as c: c.argument('collaboration_id', collaboration_id_type) diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/__init__.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/__init__.py index a2915430dea..e3229dff489 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/__init__.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/__init__.py @@ -3,19 +3,17 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- -# flake8: noqa: F403 # pylint: disable=wrong-import-position from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._patch import * # pylint: disable=unused-wildcard-import + from ._patch import * # pylint: disable=unused-wildcard-import # noqa: F403 from ._client import AnalyticsFrontendAPI # type: ignore try: from ._patch import __all__ as _patch_all - from ._patch import * except ImportError: _patch_all = [] from ._patch import patch_sdk as _patch_sdk diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_client.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_client.py index a3ae5a123bd..1a84e3e8aae 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_client.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_client.py @@ -38,28 +38,39 @@ def __init__( # pylint: disable=missing-client-constructor-parameter-credential _policies = kwargs.pop("policies", None) if _policies is None: _policies = [ - policies.RequestIdPolicy(**kwargs), + policies.RequestIdPolicy( + **kwargs), self._config.headers_policy, self._config.user_agent_policy, self._config.proxy_policy, - policies.ContentDecodePolicy(**kwargs), + policies.ContentDecodePolicy( + **kwargs), self._config.redirect_policy, self._config.retry_policy, self._config.authentication_policy, self._config.custom_hook_policy, self._config.logging_policy, - policies.DistributedTracingPolicy(**kwargs), - policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, + policies.DistributedTracingPolicy( + **kwargs), + policies.SensitiveHeaderCleanupPolicy( + **kwargs) if self._config.redirect_policy else None, self._config.http_logging_policy, ] - self._client: PipelineClient = PipelineClient(base_url=endpoint, policies=_policies, **kwargs) + self._client: PipelineClient = PipelineClient( + base_url=endpoint, policies=_policies, **kwargs) self._serialize = Serializer() self._deserialize = Deserializer() self._serialize.client_side_validation = False - self.collaboration = CollaborationOperations(self._client, self._config, self._serialize, self._deserialize) - - def send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: Any) -> HttpResponse: + self.collaboration = CollaborationOperations( + self._client, self._config, self._serialize, self._deserialize) + + def send_request( + self, + request: HttpRequest, + *, + stream: bool = False, + **kwargs: Any) -> HttpResponse: """Runs the network request through the client's chained policies. >>> from azure.core.rest import HttpRequest @@ -79,7 +90,8 @@ def send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: request_copy = deepcopy(request) request_copy.url = self._client.format_url(request_copy.url) - return self._client.send_request(request_copy, stream=stream, **kwargs) # type: ignore + return self._client.send_request( + request_copy, stream=stream, **kwargs) # type: ignore def close(self) -> None: self._client.close() diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_configuration.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_configuration.py index 23dc01e234d..15b0d18a3e3 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_configuration.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_configuration.py @@ -3,7 +3,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- -# pylint: disable=too-few-public-methods from typing import Any @@ -12,7 +11,7 @@ VERSION = "unknown" -class AnalyticsFrontendAPIConfiguration: # pylint: disable=too-many-instance-attributes +class AnalyticsFrontendAPIConfiguration: # pylint: disable=too-many-instance-attributes,too-few-public-methods """Configuration for AnalyticsFrontendAPI. Note that all parameters used to create this instance are saved as instance @@ -27,17 +26,27 @@ def __init__(self, **kwargs: Any) -> None: api_version: str = kwargs.pop("api_version", "2026-03-01-preview") self.api_version = api_version - kwargs.setdefault("sdk_moniker", "analyticsfrontendapi/{}".format(VERSION)) + kwargs.setdefault( + "sdk_moniker", + "analyticsfrontendapi/{}".format(VERSION)) self.polling_interval = kwargs.get("polling_interval", 30) self._configure(**kwargs) def _configure(self, **kwargs: Any) -> None: - self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) - self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) - self.proxy_policy = kwargs.get("proxy_policy") or policies.ProxyPolicy(**kwargs) - self.logging_policy = kwargs.get("logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) - self.http_logging_policy = kwargs.get("http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) - self.custom_hook_policy = kwargs.get("custom_hook_policy") or policies.CustomHookPolicy(**kwargs) - self.redirect_policy = kwargs.get("redirect_policy") or policies.RedirectPolicy(**kwargs) - self.retry_policy = kwargs.get("retry_policy") or policies.RetryPolicy(**kwargs) + self.user_agent_policy = kwargs.get( + "user_agent_policy") or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get( + "headers_policy") or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get( + "proxy_policy") or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get( + "logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get( + "http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) + self.custom_hook_policy = kwargs.get( + "custom_hook_policy") or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get( + "redirect_policy") or policies.RedirectPolicy(**kwargs) + self.retry_policy = kwargs.get( + "retry_policy") or policies.RetryPolicy(**kwargs) self.authentication_policy = kwargs.get("authentication_policy") diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_patch.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_patch.py index 87676c65a8f..2bd950a309c 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_patch.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_patch.py @@ -9,7 +9,8 @@ """ -__all__: list[str] = [] # Add all objects you want publicly available to users at this package level +# Add all objects you want publicly available to users at this package level +__all__: list[str] = [] def patch_sdk(): diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/__init__.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/__init__.py index bb9094db921..9b3a65674cb 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/__init__.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/__init__.py @@ -1,4 +1,5 @@ # -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.10.8, generator: @autorest/python@6.50.2) # Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------- +# Licensed under the MIT License. See License.txt in the project root for +# license information. diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/serialization.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/serialization.py index 53fbca493e3..4874031c4b8 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/serialization.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/_utils/serialization.py @@ -1,9 +1,8 @@ -# pylint: disable=line-too-long,useless-suppression,too-many-lines -# flake8: noqa: E731 +# pylint: disable=line-too-long,useless-suppression,too-many-lines,protected-access,broad-exception-caught # coding=utf-8 -# -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- # pyright: reportUnnecessaryTypeIgnoreComment=false @@ -57,7 +56,8 @@ class RawDeserializer: CONTEXT_NAME = "deserialized_data" @classmethod - def deserialize_from_text(cls, data: Optional[Union[AnyStr, IO]], content_type: Optional[str] = None) -> Any: + def deserialize_from_text( + cls, data: Optional[Union[AnyStr, IO]], content_type: Optional[str] = None) -> Any: """Decode data according to content-type. Accept a stream of data as well, but will be load at once in memory for now. @@ -90,14 +90,17 @@ def deserialize_from_text(cls, data: Optional[Union[AnyStr, IO]], content_type: try: return json.loads(data_as_str) except ValueError as err: - raise DeserializationError("JSON is invalid: {}".format(err), err) from err + raise DeserializationError( + "JSON is invalid: {}".format(err), err) from err elif "xml" in (content_type or []): try: try: if isinstance(data, unicode): # type: ignore - # If I'm Python 2.7 and unicode XML will scream if I try a "fromstring" on unicode string - data_as_str = data_as_str.encode(encoding="utf-8") # type: ignore + # If I'm Python 2.7 and unicode XML will scream if I + # try a "fromstring" on unicode string + data_as_str = data_as_str.encode( + encoding="utf-8") # type: ignore except NameError: pass @@ -124,10 +127,12 @@ def _json_attemp(data): raise DeserializationError("XML is invalid") from err elif content_type.startswith("text/"): return data_as_str - raise DeserializationError("Cannot deserialize content-type: {}".format(content_type)) + raise DeserializationError( + "Cannot deserialize content-type: {}".format(content_type)) @classmethod - def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]], headers: Mapping) -> Any: + def deserialize_from_http_generics( + cls, body_bytes: Optional[Union[AnyStr, IO]], headers: Mapping) -> Any: """Deserialize from HTTP response. Use bytes and headers to NOT use any requests/aiohttp or whatever @@ -142,7 +147,8 @@ def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]], # Try to use content-type from headers if available content_type = None if "content-type" in headers: - content_type = headers["content-type"].split(";")[0].strip().lower() + content_type = headers["content-type"].split(";")[ + 0].strip().lower() # Ouch, this server did not declare what it sent... # Let's guess it's JSON... # Also, since Autorest was considering that an empty body was a valid JSON, @@ -234,9 +240,15 @@ def __init__(self, **kwargs: Any) -> None: self.additional_properties: Optional[dict[str, Any]] = {} for k in kwargs: # pylint: disable=consider-using-dict-items if k not in self._attribute_map: - _LOGGER.warning("%s is not a known attribute of class %s and will be ignored", k, self.__class__) + _LOGGER.warning( + "%s is not a known attribute of class %s and will be ignored", + k, + self.__class__) elif k in self._validation and self._validation[k].get("readonly", False): - _LOGGER.warning("Readonly attribute %s will be ignored in class %s", k, self.__class__) + _LOGGER.warning( + "Readonly attribute %s will be ignored in class %s", + k, + self.__class__) else: setattr(self, k, kwargs[k]) @@ -265,7 +277,8 @@ def __str__(self) -> str: @classmethod def enable_additional_properties_sending(cls) -> None: - cls._attribute_map["additional_properties"] = {"key": "", "type": "{object}"} + cls._attribute_map["additional_properties"] = { + "key": "", "type": "{object}"} @classmethod def is_xml_model(cls) -> bool: @@ -287,7 +300,11 @@ def _create_xml_node(cls): except AttributeError: xml_map = {} - return _create_xml_node(xml_map.get("name", cls.__name__), xml_map.get("prefix", None), xml_map.get("ns", None)) + return _create_xml_node( + xml_map.get( + "name", cls.__name__), xml_map.get( + "prefix", None), xml_map.get( + "ns", None)) def serialize(self, keep_readonly: bool = False, **kwargs: Any) -> JSON: """Return the JSON that would be sent to server from this model. @@ -352,16 +369,24 @@ def _infer_class_models(cls): try: str_models = cls.__module__.rsplit(".", 1)[0] models = sys.modules[str_models] - client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + client_models = { + k: v for k, + v in models.__dict__.items() if isinstance( + v, + type)} if cls.__name__ not in client_models: raise ValueError("Not Autorest generated code") except Exception: # pylint: disable=broad-exception-caught - # Assume it's not Autorest generated (tests?). Add ourselves as dependencies. + # Assume it's not Autorest generated (tests?). Add ourselves as + # dependencies. client_models = {cls.__name__: cls} return client_models @classmethod - def deserialize(cls, data: Any, content_type: Optional[str] = None) -> Self: + def deserialize( + cls, + data: Any, + content_type: Optional[str] = None) -> Self: """Parse a str using the RestAPI syntax and return a model. :param str data: A str using RestAPI structure. JSON by default. @@ -371,7 +396,10 @@ def deserialize(cls, data: Any, content_type: Optional[str] = None) -> Self: :rtype: Self """ deserializer = Deserializer(cls._infer_class_models()) - return deserializer(cls.__name__, data, content_type=content_type) # type: ignore + return deserializer( + cls.__name__, + data, + content_type=content_type) # type: ignore @classmethod def from_dict( @@ -403,7 +431,10 @@ def from_dict( if key_extractors is None else key_extractors ) - return deserializer(cls.__name__, data, content_type=content_type) # type: ignore + return deserializer( + cls.__name__, + data, + content_type=content_type) # type: ignore @classmethod def _flatten_subtype(cls, key, objects): @@ -411,7 +442,8 @@ def _flatten_subtype(cls, key, objects): return {} result = dict(cls._subtype_map[key]) for valuetype in cls._subtype_map[key].values(): - result |= objects[valuetype]._flatten_subtype(key, objects) # pylint: disable=protected-access + result |= objects[valuetype]._flatten_subtype( + key, objects) # pylint: disable=protected-access return result @classmethod @@ -428,18 +460,24 @@ def _classify(cls, response, objects): subtype_value = None if not isinstance(response, ET.Element): - rest_api_response_key = cls._get_rest_key_parts(subtype_key)[-1] - subtype_value = response.get(rest_api_response_key, None) or response.get(subtype_key, None) + rest_api_response_key = cls._get_rest_key_parts( + subtype_key)[-1] + subtype_value = response.get( + rest_api_response_key, None) or response.get( + subtype_key, None) else: - subtype_value = xml_key_extractor(subtype_key, cls._attribute_map[subtype_key], response) + subtype_value = xml_key_extractor( + subtype_key, cls._attribute_map[subtype_key], response) if subtype_value: # Try to match base class. Can be class name only # (bug to fix in Autorest to support x-ms-discriminator-name) if cls.__name__ == subtype_value: return cls - flatten_mapping_type = cls._flatten_subtype(subtype_key, objects) + flatten_mapping_type = cls._flatten_subtype( + subtype_key, objects) try: - return objects[flatten_mapping_type[subtype_value]] # type: ignore + # type: ignore + return objects[flatten_mapping_type[subtype_value]] except KeyError: _LOGGER.warning( "Subtype value %s has no mapping, use base class %s.", @@ -448,7 +486,10 @@ def _classify(cls, response, objects): ) break else: - _LOGGER.warning("Discriminator %s is absent or null, use base class %s.", subtype_key, cls.__name__) + _LOGGER.warning( + "Discriminator %s is absent or null, use base class %s.", + subtype_key, + cls.__name__) break return cls @@ -460,7 +501,8 @@ def _get_rest_key_parts(cls, attr_key): :rtype: list """ rest_split_key = _FLATTEN.split(cls._attribute_map[attr_key]["key"]) - return [_decode_attribute_map_key(key_part) for key_part in rest_split_key] + return [_decode_attribute_map_key(key_part) + for key_part in rest_split_key] def _decode_attribute_map_key(key): @@ -480,7 +522,14 @@ class Serializer: # pylint: disable=too-many-public-methods basic_types = {str: "str", int: "int", bool: "bool", float: "float"} _xml_basic_types_serializers = {"bool": lambda x: str(x).lower()} - days = {0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat", 6: "Sun"} + days = { + 0: "Mon", + 1: "Tue", + 2: "Wed", + 3: "Thu", + 4: "Fri", + 5: "Sat", + 6: "Sun"} months = { 1: "Jan", 2: "Feb", @@ -560,7 +609,8 @@ def _serialize( # pylint: disable=too-many-nested-blocks, too-many-branches, to try: is_xml_model_serialization = kwargs["is_xml"] except KeyError: - is_xml_model_serialization = kwargs.setdefault("is_xml", target_obj.is_xml_model()) + is_xml_model_serialization = kwargs.setdefault( + "is_xml", target_obj.is_xml_model()) serialized = {} if is_xml_model_serialization: @@ -584,11 +634,13 @@ def _serialize( # pylint: disable=too-many-nested-blocks, too-many-branches, to if is_xml_model_serialization: pass # Don't provide "transformer" for XML for now. Keep "orig_attr" else: # JSON - keys, orig_attr = key_transformer(attr, attr_desc.copy(), orig_attr) + keys, orig_attr = key_transformer( + attr, attr_desc.copy(), orig_attr) keys = keys if isinstance(keys, list) else [keys] kwargs["serialization_ctxt"] = attr_desc - new_attr = self.serialize_data(orig_attr, attr_desc["type"], **kwargs) + new_attr = self.serialize_data( + orig_attr, attr_desc["type"], **kwargs) if is_xml_model_serialization: xml_desc = attr_desc.get("xml", {}) @@ -608,17 +660,21 @@ def _serialize( # pylint: disable=too-many-nested-blocks, too-many-branches, to serialized.extend(new_attr) # type: ignore elif isinstance(new_attr, ET.Element): # If the down XML has no XML/Name, - # we MUST replace the tag with the local tag. But keeping the namespaces. - if "name" not in getattr(orig_attr, "_xml_map", {}): + # we MUST replace the tag with the local tag. But + # keeping the namespaces. + if "name" not in getattr( + orig_attr, "_xml_map", {}): splitted_tag = new_attr.tag.split("}") if len(splitted_tag) == 2: # Namespace - new_attr.tag = "}".join([splitted_tag[0], xml_name]) + new_attr.tag = "}".join( + [splitted_tag[0], xml_name]) else: new_attr.tag = xml_name serialized.append(new_attr) # type: ignore else: # That's a basic type # Integrate namespace if necessary - local_node = _create_xml_node(xml_name, xml_prefix, xml_ns) + local_node = _create_xml_node( + xml_name, xml_prefix, xml_ns) local_node.text = str(new_attr) serialized.append(local_node) # type: ignore else: # JSON @@ -637,7 +693,8 @@ def _serialize( # pylint: disable=too-many-nested-blocks, too-many-branches, to raise except (AttributeError, KeyError, TypeError) as err: - msg = "Attribute {} in object {} cannot be serialized.\n{}".format(attr_name, class_name, str(target_obj)) + msg = "Attribute {} in object {} cannot be serialized.\n{}".format( + attr_name, class_name, str(target_obj)) raise SerializationError(msg) from err return serialized @@ -654,12 +711,14 @@ def body(self, data, data_type, **kwargs): # Just in case this is a dict internal_data_type_str = data_type.strip("[]{}") - internal_data_type = self.dependencies.get(internal_data_type_str, None) + internal_data_type = self.dependencies.get( + internal_data_type_str, None) try: is_xml_model_serialization = kwargs["is_xml"] except KeyError: if internal_data_type and issubclass(internal_data_type, Model): - is_xml_model_serialization = kwargs.setdefault("is_xml", internal_data_type.is_xml_model()) + is_xml_model_serialization = kwargs.setdefault( + "is_xml", internal_data_type.is_xml_model()) else: is_xml_model_serialization = False if internal_data_type and not isinstance(internal_data_type, Enum): @@ -678,9 +737,11 @@ def body(self, data, data_type, **kwargs): attribute_key_case_insensitive_extractor, last_rest_key_case_insensitive_extractor, ] - data = deserializer._deserialize(data_type, data) # pylint: disable=protected-access + data = deserializer._deserialize( + data_type, data) # pylint: disable=protected-access except DeserializationError as err: - raise SerializationError("Unable to build a model: " + str(err)) from err + raise SerializationError( + "Unable to build a model: " + str(err)) from err return self._serialize(data, data_type, **kwargs) @@ -702,11 +763,14 @@ def url(self, name, data, data_type, **kwargs): if kwargs.get("skip_quote") is True: output = str(output) - output = output.replace("{", quote("{")).replace("}", quote("}")) + output = output.replace( + "{", quote("{")).replace("}", quote("}")) else: output = quote(str(output), safe="") except SerializationError as exc: - raise TypeError("{} must be type {}.".format(name, data_type)) from exc + raise TypeError( + "{} must be type {}.".format( + name, data_type)) from exc return output def query(self, name, data, data_type, **kwargs): @@ -721,11 +785,13 @@ def query(self, name, data, data_type, **kwargs): :returns: The serialized query parameter """ try: - # Treat the list aside, since we don't want to encode the div separator + # Treat the list aside, since we don't want to encode the div + # separator if data_type.startswith("["): internal_data_type = data_type[1:-1] do_quote = not kwargs.get("skip_quote", False) - return self.serialize_iter(data, internal_data_type, do_quote=do_quote, **kwargs) + return self.serialize_iter( + data, internal_data_type, do_quote=do_quote, **kwargs) # Not a list, regular serialization output = self.serialize_data(data, data_type, **kwargs) @@ -736,7 +802,9 @@ def query(self, name, data, data_type, **kwargs): else: output = quote(str(output), safe="") except SerializationError as exc: - raise TypeError("{} must be type {}.".format(name, data_type)) from exc + raise TypeError( + "{} must be type {}.".format( + name, data_type)) from exc return str(output) def header(self, name, data, data_type, **kwargs): @@ -758,7 +826,9 @@ def header(self, name, data, data_type, **kwargs): if data_type == "bool": output = json.dumps(output) except SerializationError as exc: - raise TypeError("{} must be type {}.".format(name, data_type)) from exc + raise TypeError( + "{} must be type {}.".format( + name, data_type)) from exc return str(output) def serialize_data(self, data, data_type, **kwargs): @@ -786,13 +856,15 @@ def serialize_data(self, data, data_type, **kwargs): # If dependencies is empty, try with current data class # It has to be a subclass of Enum anyway - enum_type = self.dependencies.get(data_type, cast(type, data.__class__)) + enum_type = self.dependencies.get( + data_type, cast(type, data.__class__)) if issubclass(enum_type, Enum): return Serializer.serialize_enum(data, enum_obj=enum_type) iter_type = data_type[0] + data_type[-1] if iter_type in self.serialize_type: - return self.serialize_type[iter_type](data, data_type[1:-1], **kwargs) + return self.serialize_type[iter_type]( + data, data_type[1:-1], **kwargs) except (ValueError, TypeError) as err: msg = "Unable to serialize value: {!r} as type: {!r}." @@ -801,7 +873,8 @@ def serialize_data(self, data, data_type, **kwargs): @classmethod def _get_custom_serializers(cls, data_type, **kwargs): # pylint: disable=inconsistent-return-statements - custom_serializer = kwargs.get("basic_types_serializers", {}).get(data_type) + custom_serializer = kwargs.get( + "basic_types_serializers", {}).get(data_type) if custom_serializer: return custom_serializer if kwargs.get("is_xml", False): @@ -890,7 +963,9 @@ def serialize_iter(self, data, iter_type, div=None, **kwargs): serialized.append(None) if kwargs.get("do_quote", False): - serialized = ["" if s is None else quote(str(s), safe="") for s in serialized] + serialized = [ + "" if s is None else quote( + str(s), safe="") for s in serialized] if div: serialized = ["" if s is None else str(s) for s in serialized] @@ -903,11 +978,15 @@ def serialize_iter(self, data, iter_type, div=None, **kwargs): if not xml_name: xml_name = serialization_ctxt["key"] - # Create a wrap node if necessary (use the fact that Element and list have "append") + # Create a wrap node if necessary (use the fact that Element and + # list have "append") is_wrapped = xml_desc.get("wrapped", False) node_name = xml_desc.get("itemsName", xml_name) if is_wrapped: - final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) + final_result = _create_xml_node( + xml_name, xml_desc.get( + "prefix", None), xml_desc.get( + "ns", None)) else: final_result = [] # All list elements to "local_node" @@ -915,7 +994,10 @@ def serialize_iter(self, data, iter_type, div=None, **kwargs): if isinstance(el, ET.Element): el_node = el else: - el_node = _create_xml_node(node_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) + el_node = _create_xml_node( + node_name, xml_desc.get( + "prefix", None), xml_desc.get( + "ns", None)) if el is not None: # Otherwise it writes "None" :-p el_node.text = str(el) final_result.append(el_node) @@ -934,7 +1016,8 @@ def serialize_dict(self, attr, dict_type, **kwargs): serialized = {} for key, value in attr.items(): try: - serialized[self.serialize_unicode(key)] = self.serialize_data(value, dict_type, **kwargs) + serialized[self.serialize_unicode(key)] = self.serialize_data( + value, dict_type, **kwargs) except ValueError as err: if isinstance(err, SerializationError): raise @@ -945,7 +1028,10 @@ def serialize_dict(self, attr, dict_type, **kwargs): xml_desc = serialization_ctxt["xml"] xml_name = xml_desc["name"] - final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) + final_result = _create_xml_node( + xml_name, xml_desc.get( + "prefix", None), xml_desc.get( + "ns", None)) for key, value in serialized.items(): ET.SubElement(final_result, key).text = value return final_result @@ -968,7 +1054,8 @@ def serialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-s return attr obj_type = type(attr) if obj_type in self.basic_types: - return self.serialize_basic(attr, self.basic_types[obj_type], **kwargs) + return self.serialize_basic( + attr, self.basic_types[obj_type], **kwargs) if obj_type is _long_type: return self.serialize_long(attr) if obj_type is str: @@ -992,7 +1079,8 @@ def serialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-s serialized = {} for key, value in attr.items(): try: - serialized[self.serialize_unicode(key)] = self.serialize_object(value, **kwargs) + serialized[self.serialize_unicode( + key)] = self.serialize_object(value, **kwargs) except ValueError: serialized[self.serialize_unicode(key)] = None return serialized @@ -1115,10 +1203,12 @@ def serialize_rfc(attr, **kwargs): # pylint: disable=unused-argument """ try: if not attr.tzinfo: - _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") + _LOGGER.warning( + "Datetime with no tzinfo will be considered UTC.") utc = attr.utctimetuple() except AttributeError as exc: - raise TypeError("RFC1123 object must be valid Datetime object.") from exc + raise TypeError( + "RFC1123 object must be valid Datetime object.") from exc return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format( Serializer.days[utc.tm_wday], @@ -1143,17 +1233,22 @@ def serialize_iso(attr, **kwargs): # pylint: disable=unused-argument attr = isodate.parse_datetime(attr) try: if not attr.tzinfo: - _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") + _LOGGER.warning( + "Datetime with no tzinfo will be considered UTC.") utc = attr.utctimetuple() if utc.tm_year > 9999 or utc.tm_year < 1: raise OverflowError("Hit max or min date") - microseconds = str(attr.microsecond).rjust(6, "0").rstrip("0").ljust(3, "0") + microseconds = str( + attr.microsecond).rjust( + 6, + "0").rstrip("0").ljust( + 3, + "0") if microseconds: microseconds = "." + microseconds date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format( - utc.tm_year, utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec - ) + utc.tm_year, utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec) return date + microseconds + "Z" except (ValueError, OverflowError) as err: msg = "Unable to serialize datetime object." @@ -1176,10 +1271,12 @@ def serialize_unix(attr, **kwargs): # pylint: disable=unused-argument return attr try: if not attr.tzinfo: - _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") + _LOGGER.warning( + "Datetime with no tzinfo will be considered UTC.") return int(calendar.timegm(attr.utctimetuple())) except AttributeError as exc: - raise TypeError("Unix time object must be valid Datetime object.") from exc + raise TypeError( + "Unix time object must be valid Datetime object.") from exc def rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument @@ -1187,7 +1284,8 @@ def rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argumen working_data = data while "." in key: - # Need the cast, as for some reasons "split" is typed as list[str | Any] + # Need the cast, as for some reasons "split" is typed as list[str | + # Any] dict_keys = cast(list[str], _FLATTEN.split(key)) if len(dict_keys) == 1: key = _decode_attribute_map_key(dict_keys[0]) @@ -1215,7 +1313,8 @@ def rest_key_case_insensitive_extractor( # pylint: disable=unused-argument, inc key = _decode_attribute_map_key(dict_keys[0]) break working_key = _decode_attribute_map_key(dict_keys[0]) - working_data = attribute_key_case_insensitive_extractor(working_key, None, working_data) + working_data = attribute_key_case_insensitive_extractor( + working_key, None, working_data) if working_data is None: # If at any point while following flatten JSON path see None, it means # that all properties under are None as well @@ -1223,7 +1322,8 @@ def rest_key_case_insensitive_extractor( # pylint: disable=unused-argument, inc key = ".".join(dict_keys[1:]) if working_data: - return attribute_key_case_insensitive_extractor(key, None, working_data) + return attribute_key_case_insensitive_extractor( + key, None, working_data) def last_rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument @@ -1319,7 +1419,11 @@ def xml_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument # - Wrapped node # - Internal type is an enum (considered basic types) # - Internal type has no XML/Name node - if is_wrapped or (internal_type and (issubclass(internal_type, Enum) or "name" not in internal_type_xml_map)): + if is_wrapped or ( + internal_type and ( + issubclass( + internal_type, + Enum) or "name" not in internal_type_xml_map)): children = data.findall(xml_name) # If internal type has a local name and it's not a list, I use that name elif not is_iter_type and internal_type and "name" in internal_type_xml_map: @@ -1355,7 +1459,8 @@ def xml_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument # Here it's not a itertype, we should have found one element only or empty if len(children) > 1: - raise DeserializationError("Find several XML '{}' where it was not expected".format(xml_name)) + raise DeserializationError( + "Find several XML '{}' where it was not expected".format(xml_name)) return children[0] @@ -1368,7 +1473,8 @@ class Deserializer: basic_types = {str: "str", int: "int", bool: "bool", float: "float"} - valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") + valid_date = re.compile( + r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None: self.deserialize_type = { @@ -1426,9 +1532,15 @@ def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return """ # This is already a model, go recursive just in case if hasattr(data, "_attribute_map"): - constants = [name for name, config in getattr(data, "_validation", {}).items() if config.get("constant")] + constants = [ + name for name, + config in getattr( + data, + "_validation", + {}).items() if config.get("constant")] try: - for attr, mapconfig in data._attribute_map.items(): # pylint: disable=protected-access + for attr, mapconfig in data._attribute_map.items( + ): # pylint: disable=protected-access if attr in constants: continue value = getattr(data, attr) @@ -1436,7 +1548,8 @@ def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return continue local_type = mapconfig["type"] internal_data_type = local_type.strip("[]{}") - if internal_data_type not in self.dependencies or isinstance(internal_data_type, Enum): + if internal_data_type not in self.dependencies or isinstance( + internal_data_type, Enum): continue setattr(data, attr, self._deserialize(local_type, value)) return data @@ -1453,10 +1566,12 @@ def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return if data is None or data is CoreNull: return data try: - attributes = response._attribute_map # type: ignore # pylint: disable=protected-access + # type: ignore # pylint: disable=protected-access + attributes = response._attribute_map d_attrs = {} for attr, attr_desc in attributes.items(): - # Check empty string. If it's not empty, someone has a real "additionalProperties"... + # Check empty string. If it's not empty, someone has a real + # "additionalProperties"... if attr == "additional_properties" and attr_desc["key"] == "": continue raw_value = None @@ -1472,9 +1587,9 @@ def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return if raw_value is not None and raw_value != found_value: msg = ( "Ignoring extracted value '%s' from %s for key '%s'" - " (duplicate extraction, follow extractors order)" - ) - _LOGGER.warning(msg, found_value, key_extractor, attr) + " (duplicate extraction, follow extractors order)") + _LOGGER.warning( + msg, found_value, key_extractor, attr) continue raw_value = found_value @@ -1483,14 +1598,18 @@ def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return except (AttributeError, TypeError, KeyError) as err: msg = "Unable to deserialize to object: " + class_name # type: ignore raise DeserializationError(msg) from err - additional_properties = self._build_additional_properties(attributes, data) - return self._instantiate_model(response, d_attrs, additional_properties) + additional_properties = self._build_additional_properties( + attributes, data) + return self._instantiate_model( + response, d_attrs, additional_properties) def _build_additional_properties(self, attribute_map, data): if not self.additional_properties_detection: return None - if "additional_properties" in attribute_map and attribute_map.get("additional_properties", {}).get("key") != "": - # Check empty string. If it's not empty, someone has a real "additionalProperties" + if "additional_properties" in attribute_map and attribute_map.get( + "additional_properties", {}).get("key") != "": + # Check empty string. If it's not empty, someone has a real + # "additionalProperties" return None if isinstance(data, ET.Element): data = {el.tag: el.text for el in data} @@ -1524,7 +1643,8 @@ def _classify_target(self, target, data): return target, target try: - target = target._classify(data, self.dependencies) # type: ignore # pylint: disable=protected-access + # type: ignore # pylint: disable=protected-access + target = target._classify(data, self.dependencies) except AttributeError: pass # Target is not a Model, no classify return target, target.__class__.__name__ # type: ignore @@ -1544,10 +1664,10 @@ def failsafe_deserialize(self, target_obj, data, content_type=None): """ try: return self(target_obj, data, content_type=content_type) - except: # pylint: disable=bare-except + except BaseException: # pylint: disable=bare-except _LOGGER.debug( - "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True - ) + "Ran into a deserialization error. Ignoring since this is failsafe deserialization", + exc_info=True) return None @staticmethod @@ -1569,23 +1689,30 @@ def _unpack_content(raw_data, content_type=None): :rtype: object :return: Unpacked content. """ - # Assume this is enough to detect a Pipeline Response without importing it + # Assume this is enough to detect a Pipeline Response without importing + # it context = getattr(raw_data, "context", {}) if context: if RawDeserializer.CONTEXT_NAME in context: return context[RawDeserializer.CONTEXT_NAME] - raise ValueError("This pipeline didn't have the RawDeserializer policy; can't deserialize") + raise ValueError( + "This pipeline didn't have the RawDeserializer policy; can't deserialize") - # Assume this is enough to recognize universal_http.ClientResponse without importing it + # Assume this is enough to recognize universal_http.ClientResponse + # without importing it if hasattr(raw_data, "body"): - return RawDeserializer.deserialize_from_http_generics(raw_data.text(), raw_data.headers) + return RawDeserializer.deserialize_from_http_generics( + raw_data.text(), raw_data.headers) - # Assume this enough to recognize requests.Response without importing it. + # Assume this enough to recognize requests.Response without importing + # it. if hasattr(raw_data, "_content_consumed"): - return RawDeserializer.deserialize_from_http_generics(raw_data.text, raw_data.headers) + return RawDeserializer.deserialize_from_http_generics( + raw_data.text, raw_data.headers) if isinstance(raw_data, (str, bytes)) or hasattr(raw_data, "read"): - return RawDeserializer.deserialize_from_text(raw_data, content_type) # type: ignore + return RawDeserializer.deserialize_from_text( + raw_data, content_type) # type: ignore return raw_data def _instantiate_model(self, response, attrs, additional_properties=None): @@ -1610,7 +1737,10 @@ def _instantiate_model(self, response, attrs, additional_properties=None): for k, v in response._validation.items() # pylint: disable=protected-access # type: ignore if v.get("constant") ] - kwargs = {k: v for k, v in attrs.items() if k not in subtype and k not in readonly + const} + kwargs = { + k: v for k, + v in attrs.items() if k not in subtype and k not in readonly + + const} response_obj = response(**kwargs) for attr in readonly: setattr(response_obj, attr, attrs.get(attr)) @@ -1618,7 +1748,8 @@ def _instantiate_model(self, response, attrs, additional_properties=None): response_obj.additional_properties = additional_properties # type: ignore return response_obj except TypeError as err: - msg = "Unable to deserialize {} into model {}. ".format(kwargs, response) # type: ignore + msg = "Unable to deserialize {} into model {}. ".format( + kwargs, response) # type: ignore raise DeserializationError(msg + str(err)) from err else: try: @@ -1648,15 +1779,21 @@ def deserialize_data(self, data, data_type): # pylint: disable=too-many-return- if data_type in self.basic_types.values(): return self.deserialize_basic(data, data_type) if data_type in self.deserialize_type: - if isinstance(data, self.deserialize_expected_types.get(data_type, tuple())): + if isinstance( + data, + self.deserialize_expected_types.get( + data_type, + tuple())): return data - is_a_text_parsing_type = lambda x: x not in [ # pylint: disable=unnecessary-lambda-assignment - "object", - "[]", - r"{}", - ] - if isinstance(data, ET.Element) and is_a_text_parsing_type(data_type) and not data.text: + def is_a_text_parsing_type(x): # pylint: disable=unnecessary-lambda-assignment + return x not in [ + "object", + "[]", + r"{}", + ] + if isinstance(data, ET.Element) and is_a_text_parsing_type( + data_type) and not data.text: return None data_val = self.deserialize_type[data_type](data) return data_val @@ -1687,10 +1824,14 @@ def deserialize_iter(self, attr, iter_type): """ if attr is None: return None - if isinstance(attr, ET.Element): # If I receive an element here, get the children + if isinstance( + attr, + ET.Element): # If I receive an element here, get the children attr = list(attr) if not isinstance(attr, (list, set)): - raise DeserializationError("Cannot deserialize as [{}] an object of type {}".format(iter_type, type(attr))) + raise DeserializationError( + "Cannot deserialize as [{}] an object of type {}".format( + iter_type, type(attr))) return [self.deserialize_data(a, iter_type) for a in attr] def deserialize_dict(self, attr, dict_type): @@ -1703,12 +1844,16 @@ def deserialize_dict(self, attr, dict_type): :rtype: dict """ if isinstance(attr, list): - return {x["key"]: self.deserialize_data(x["value"], dict_type) for x in attr} + return { + x["key"]: self.deserialize_data( + x["value"], + dict_type) for x in attr} if isinstance(attr, ET.Element): # Transform value into {"Key": "value"} attr = {el.tag: el.text for el in attr} - return {k: self.deserialize_data(v, dict_type) for k, v in attr.items()} + return {k: self.deserialize_data(v, dict_type) + for k, v in attr.items()} def deserialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-statements """Deserialize a generic object. @@ -1736,7 +1881,8 @@ def deserialize_object(self, attr, **kwargs): # pylint: disable=too-many-return deserialized = {} for key, value in attr.items(): try: - deserialized[key] = self.deserialize_object(value, **kwargs) + deserialized[key] = self.deserialize_object( + value, **kwargs) except ValueError: deserialized[key] = None return deserialized @@ -1840,15 +1986,20 @@ def deserialize_enum(data, enum_obj): return list(enum_obj.__members__.values())[data] except IndexError as exc: error = "{!r} is not a valid index for enum {!r}" - raise DeserializationError(error.format(data, enum_obj)) from exc + raise DeserializationError( + error.format(data, enum_obj)) from exc try: return enum_obj(str(data)) except ValueError: for enum_value in enum_obj: if enum_value.value.lower() == str(data).lower(): return enum_value - # We don't fail anymore for unknown value, we deserialize as a string - _LOGGER.warning("Deserializer is not able to find %s as valid enum in %s", data, enum_obj) + # We don't fail anymore for unknown value, we deserialize as a + # string + _LOGGER.warning( + "Deserializer is not able to find %s as valid enum in %s", + data, + enum_obj) return Deserializer.deserialize_unicode(data) @staticmethod @@ -1940,8 +2091,11 @@ def deserialize_date(attr): if isinstance(attr, ET.Element): attr = attr.text if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore - raise DeserializationError("Date must have only digits and -. Received: %s" % attr) - # This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception. + raise DeserializationError( + "Date must have only digits and -. Received: %s" % + attr) + # This must NOT use defaultmonth/defaultday. Using None ensure this + # raises an exception. return isodate.parse_date(attr, defaultmonth=0, defaultday=0) @staticmethod @@ -1956,7 +2110,9 @@ def deserialize_time(attr): if isinstance(attr, ET.Element): attr = attr.text if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore - raise DeserializationError("Date must have only digits and -. Received: %s" % attr) + raise DeserializationError( + "Date must have only digits and -. Received: %s" % + attr) return isodate.parse_time(attr) @staticmethod @@ -1972,9 +2128,8 @@ def deserialize_rfc(attr): attr = attr.text try: parsed_date = email.utils.parsedate_tz(attr) # type: ignore - date_obj = datetime.datetime( - *parsed_date[:6], tzinfo=datetime.timezone(datetime.timedelta(minutes=(parsed_date[9] or 0) / 60)) - ) + date_obj = datetime.datetime(*parsed_date[:6], tzinfo=datetime.timezone( + datetime.timedelta(minutes=(parsed_date[9] or 0) / 60))) if not date_obj.tzinfo: date_obj = date_obj.astimezone(tz=TZ_UTC) except ValueError as err: diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/__init__.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/__init__.py index a2915430dea..e3229dff489 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/__init__.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/__init__.py @@ -3,19 +3,17 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- -# flake8: noqa: F403 # pylint: disable=wrong-import-position from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._patch import * # pylint: disable=unused-wildcard-import + from ._patch import * # pylint: disable=unused-wildcard-import # noqa: F403 from ._client import AnalyticsFrontendAPI # type: ignore try: from ._patch import __all__ as _patch_all - from ._patch import * except ImportError: _patch_all = [] from ._patch import patch_sdk as _patch_sdk diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_client.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_client.py index efbdf666ab9..eeb74e28e1c 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_client.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_client.py @@ -38,26 +38,32 @@ def __init__( # pylint: disable=missing-client-constructor-parameter-credential _policies = kwargs.pop("policies", None) if _policies is None: _policies = [ - policies.RequestIdPolicy(**kwargs), + policies.RequestIdPolicy( + **kwargs), self._config.headers_policy, self._config.user_agent_policy, self._config.proxy_policy, - policies.ContentDecodePolicy(**kwargs), + policies.ContentDecodePolicy( + **kwargs), self._config.redirect_policy, self._config.retry_policy, self._config.authentication_policy, self._config.custom_hook_policy, self._config.logging_policy, - policies.DistributedTracingPolicy(**kwargs), - policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, + policies.DistributedTracingPolicy( + **kwargs), + policies.SensitiveHeaderCleanupPolicy( + **kwargs) if self._config.redirect_policy else None, self._config.http_logging_policy, ] - self._client: AsyncPipelineClient = AsyncPipelineClient(base_url=endpoint, policies=_policies, **kwargs) + self._client: AsyncPipelineClient = AsyncPipelineClient( + base_url=endpoint, policies=_policies, **kwargs) self._serialize = Serializer() self._deserialize = Deserializer() self._serialize.client_side_validation = False - self.collaboration = CollaborationOperations(self._client, self._config, self._serialize, self._deserialize) + self.collaboration = CollaborationOperations( + self._client, self._config, self._serialize, self._deserialize) def send_request( self, request: HttpRequest, *, stream: bool = False, **kwargs: Any @@ -81,7 +87,8 @@ def send_request( request_copy = deepcopy(request) request_copy.url = self._client.format_url(request_copy.url) - return self._client.send_request(request_copy, stream=stream, **kwargs) # type: ignore + return self._client.send_request( + request_copy, stream=stream, **kwargs) # type: ignore async def close(self) -> None: await self._client.close() diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_configuration.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_configuration.py index 57d5d6b89bb..6ce8e57c748 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_configuration.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_configuration.py @@ -3,7 +3,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- -# pylint: disable=too-few-public-methods from typing import Any @@ -12,7 +11,7 @@ VERSION = "unknown" -class AnalyticsFrontendAPIConfiguration: # pylint: disable=too-many-instance-attributes +class AnalyticsFrontendAPIConfiguration: # pylint: disable=too-many-instance-attributes,too-few-public-methods """Configuration for AnalyticsFrontendAPI. Note that all parameters used to create this instance are saved as instance @@ -27,17 +26,27 @@ def __init__(self, **kwargs: Any) -> None: api_version: str = kwargs.pop("api_version", "2026-03-01-preview") self.api_version = api_version - kwargs.setdefault("sdk_moniker", "analyticsfrontendapi/{}".format(VERSION)) + kwargs.setdefault( + "sdk_moniker", + "analyticsfrontendapi/{}".format(VERSION)) self.polling_interval = kwargs.get("polling_interval", 30) self._configure(**kwargs) def _configure(self, **kwargs: Any) -> None: - self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) - self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) - self.proxy_policy = kwargs.get("proxy_policy") or policies.ProxyPolicy(**kwargs) - self.logging_policy = kwargs.get("logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) - self.http_logging_policy = kwargs.get("http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) - self.custom_hook_policy = kwargs.get("custom_hook_policy") or policies.CustomHookPolicy(**kwargs) - self.redirect_policy = kwargs.get("redirect_policy") or policies.AsyncRedirectPolicy(**kwargs) - self.retry_policy = kwargs.get("retry_policy") or policies.AsyncRetryPolicy(**kwargs) + self.user_agent_policy = kwargs.get( + "user_agent_policy") or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get( + "headers_policy") or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get( + "proxy_policy") or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get( + "logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get( + "http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) + self.custom_hook_policy = kwargs.get( + "custom_hook_policy") or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get( + "redirect_policy") or policies.AsyncRedirectPolicy(**kwargs) + self.retry_policy = kwargs.get( + "retry_policy") or policies.AsyncRetryPolicy(**kwargs) self.authentication_policy = kwargs.get("authentication_policy") diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_patch.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_patch.py index 87676c65a8f..2bd950a309c 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_patch.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/_patch.py @@ -9,7 +9,8 @@ """ -__all__: list[str] = [] # Add all objects you want publicly available to users at this package level +# Add all objects you want publicly available to users at this package level +__all__: list[str] = [] def patch_sdk(): diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/__init__.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/__init__.py index 5a4c57889d9..91ecd54c4e2 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/__init__.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/__init__.py @@ -3,18 +3,16 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- -# flake8: noqa: F403 # pylint: disable=wrong-import-position from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._patch import * # pylint: disable=unused-wildcard-import + from ._patch import * # pylint: disable=unused-wildcard-import # noqa: F403 from ._operations import CollaborationOperations # type: ignore from ._patch import __all__ as _patch_all -from ._patch import * from ._patch import patch_sdk as _patch_sdk __all__ = [ diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_operations.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_operations.py index e3ec7f77050..765ba48bf92 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_operations.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_operations.py @@ -1,8 +1,8 @@ # pylint: disable=too-many-lines # coding=utf-8 -# -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- from collections.abc import MutableMapping from io import IOBase @@ -25,7 +25,6 @@ from ..._utils.serialization import Deserializer, Serializer from ...operations._operations import ( build_collaboration_analytics_auditevents_get_request, - build_collaboration_analytics_cleanroompolicy_get_request, build_collaboration_analytics_datasets_document_id_get_request, build_collaboration_analytics_datasets_document_id_publish_post_request, build_collaboration_analytics_datasets_document_id_queries_get_request, @@ -39,6 +38,7 @@ build_collaboration_analytics_queries_list_get_request, build_collaboration_analytics_runs_job_id_get_request, build_collaboration_analytics_secrets_secret_name_put_request, + build_collaboration_analytics_skr_policy_get_request, build_collaboration_consent_document_id_get_request, build_collaboration_consent_document_id_put_request, build_collaboration_id_get_request, @@ -55,7 +55,8 @@ JSON = MutableMapping[str, Any] T = TypeVar("T") -ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, dict[str, Any]], Any]] +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, + AsyncHttpResponse], T, dict[str, Any]], Any]] class CollaborationOperations: # pylint: disable=too-many-public-methods @@ -70,13 +71,21 @@ class CollaborationOperations: # pylint: disable=too-many-public-methods def __init__(self, *args, **kwargs) -> None: input_args = list(args) - self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AnalyticsFrontendAPIConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + self._client: AsyncPipelineClient = input_args.pop( + 0) if input_args else kwargs.pop("client") + self._config: AnalyticsFrontendAPIConfiguration = input_args.pop( + 0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop( + 0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop( + 0) if input_args else kwargs.pop("deserializer") @distributed_trace_async - async def list_get(self, *, active_only: bool = False, **kwargs: Any) -> list[JSON]: + async def list_get( + self, + *, + active_only: bool = False, + **kwargs: Any) -> list[JSON]: """List all collaborations. List all collaborations. @@ -129,7 +138,10 @@ async def list_get(self, *, active_only: bool = False, **kwargs: Any) -> list[JS response = pipeline_response.http_response if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -138,12 +150,22 @@ async def list_get(self, *, active_only: bool = False, **kwargs: Any) -> list[JS deserialized = None if cls: - return cls(pipeline_response, cast(list[JSON], deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + list[JSON], + deserialized), + {}) # type: ignore return cast(list[JSON], deserialized) # type: ignore @distributed_trace_async - async def id_get(self, collaboration_id: str, *, active_only: bool = False, **kwargs: Any) -> JSON: + async def id_get( + self, + collaboration_id: str, + *, + active_only: bool = False, + **kwargs: Any) -> JSON: """Get collaboration by id. Get collaboration by id. @@ -205,7 +227,10 @@ async def id_get(self, collaboration_id: str, *, active_only: bool = False, **kw response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -214,7 +239,12 @@ async def id_get(self, collaboration_id: str, *, active_only: bool = False, **kw deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -307,7 +337,10 @@ async def report_get(self, collaboration_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response if response.status_code not in [200, 400, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -316,12 +349,20 @@ async def report_get(self, collaboration_id: str, **kwargs: Any) -> JSON: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def analytics_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + async def analytics_get( + self, + collaboration_id: str, + **kwargs: Any) -> JSON: """Get collaboration analytics workload. Get collaboration analytics workload. @@ -380,7 +421,10 @@ async def analytics_get(self, collaboration_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -389,18 +433,29 @@ async def analytics_get(self, collaboration_id: str, **kwargs: Any) -> JSON: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: Any) -> JSON: - """Get collaboration analytics cleanroompolicy. + async def analytics_skr_policy_get( + self, + collaboration_id: str, + dataset_id: str, + **kwargs: Any) -> JSON: + """Get collaboration analytics skr policy for a dataset. - Get collaboration analytics cleanroompolicy. + Get collaboration analytics skr policy for a dataset. :param collaboration_id: Required. :type collaboration_id: str + :param dataset_id: Required. + :type dataset_id: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -410,14 +465,18 @@ async def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: A # response body for status code(s): 200 response == { - "claims": { - "claims": { - "str": {} + "anyOf": [ + { + "allOf": [ + { + "claim": "str", + "equals": "str" + } + ], + "authority": "str" } - }, - "proposalIds": [ - "str" - ] + ], + "version": "str" } # response body for status code(s): 422 response == { @@ -441,8 +500,9 @@ async def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: A cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_collaboration_analytics_cleanroompolicy_get_request( + _request = build_collaboration_analytics_skr_policy_get_request( collaboration_id=collaboration_id, + dataset_id=dataset_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -457,7 +517,10 @@ async def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: A response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -466,12 +529,20 @@ async def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: A deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def oidc_issuer_info_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + async def oidc_issuer_info_get( + self, + collaboration_id: str, + **kwargs: Any) -> JSON: """Get collaboration OIDC issuer info. Get collaboration OIDC issuer info. @@ -532,7 +603,10 @@ async def oidc_issuer_info_get(self, collaboration_id: str, **kwargs: Any) -> JS response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -541,7 +615,12 @@ async def oidc_issuer_info_get(self, collaboration_id: str, **kwargs: Any) -> JS deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -635,9 +714,11 @@ async def oidc_set_issuer_url_post( """ @distributed_trace_async - async def oidc_set_issuer_url_post( - self, collaboration_id: str, body: Optional[Union[JSON, IO[bytes]]] = None, **kwargs: Any - ) -> JSON: + async def oidc_set_issuer_url_post(self, + collaboration_id: str, + body: Optional[Union[JSON, + IO[bytes]]] = None, + **kwargs: Any) -> JSON: """Set collaboration oidc issuer url. Set collaboration oidc issuer url. @@ -683,7 +764,8 @@ async def oidc_set_issuer_url_post( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) content_type = content_type if body else None cls: ClsType[JSON] = kwargs.pop("cls", None) @@ -717,7 +799,10 @@ async def oidc_set_issuer_url_post( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -726,12 +811,20 @@ async def oidc_set_issuer_url_post( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def oidc_keys_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + async def oidc_keys_get( + self, + collaboration_id: str, + **kwargs: Any) -> JSON: """Get collaboration oidc signing keys (JWKS). Get collaboration oidc signing keys (JWKS). @@ -801,7 +894,10 @@ async def oidc_keys_get(self, collaboration_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -810,12 +906,22 @@ async def oidc_keys_get(self, collaboration_id: str, **kwargs: Any) -> JSON: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def invitations_get(self, collaboration_id: str, *, pending_only: bool = False, **kwargs: Any) -> JSON: + async def invitations_get( + self, + collaboration_id: str, + *, + pending_only: bool = False, + **kwargs: Any) -> JSON: """List all invitations. List all invitations. @@ -877,7 +983,10 @@ async def invitations_get(self, collaboration_id: str, *, pending_only: bool = F response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -886,12 +995,21 @@ async def invitations_get(self, collaboration_id: str, *, pending_only: bool = F deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def invitation_id_get(self, collaboration_id: str, invitation_id: str, **kwargs: Any) -> JSON: + async def invitation_id_get( + self, + collaboration_id: str, + invitation_id: str, + **kwargs: Any) -> JSON: """Get invitation by id. Get invitation by id. @@ -958,7 +1076,10 @@ async def invitation_id_get(self, collaboration_id: str, invitation_id: str, **k response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -967,7 +1088,12 @@ async def invitation_id_get(self, collaboration_id: str, invitation_id: str, **k deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -1029,7 +1155,10 @@ async def invitation_id_accept_post( response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -1045,7 +1174,8 @@ async def invitation_id_accept_post( return deserialized # type: ignore @distributed_trace_async - async def analytics_datasets_list_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + async def analytics_datasets_list_get( + self, collaboration_id: str, **kwargs: Any) -> JSON: """List all datasets. List all datasets. @@ -1108,7 +1238,10 @@ async def analytics_datasets_list_get(self, collaboration_id: str, **kwargs: Any response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1117,12 +1250,21 @@ async def analytics_datasets_list_get(self, collaboration_id: str, **kwargs: Any deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def analytics_datasets_document_id_get(self, collaboration_id: str, document_id: str, **kwargs: Any) -> JSON: + async def analytics_datasets_document_id_get( + self, + collaboration_id: str, + document_id: str, + **kwargs: Any) -> JSON: """Get dataset by id. Get dataset by id. @@ -1225,7 +1367,10 @@ async def analytics_datasets_document_id_get(self, collaboration_id: str, docume response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1234,7 +1379,12 @@ async def analytics_datasets_document_id_get(self, collaboration_id: str, docume deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -1445,7 +1595,8 @@ async def analytics_datasets_document_id_publish_post( # pylint: disable=name-t _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -1476,7 +1627,10 @@ async def analytics_datasets_document_id_publish_post( # pylint: disable=name-t response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -1492,7 +1646,11 @@ async def analytics_datasets_document_id_publish_post( # pylint: disable=name-t return deserialized # type: ignore @distributed_trace_async - async def consent_document_id_get(self, collaboration_id: str, document_id: str, **kwargs: Any) -> JSON: + async def consent_document_id_get( + self, + collaboration_id: str, + document_id: str, + **kwargs: Any) -> JSON: """Check execution consent by ID of the Query or the Dataset. Check execution consent by ID of the Query or the Dataset. @@ -1555,7 +1713,10 @@ async def consent_document_id_get(self, collaboration_id: str, document_id: str, response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1564,7 +1725,12 @@ async def consent_document_id_get(self, collaboration_id: str, document_id: str, deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -1654,9 +1820,12 @@ async def consent_document_id_put( """ @distributed_trace_async - async def consent_document_id_put( - self, collaboration_id: str, document_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any - ) -> Optional[JSON]: + async def consent_document_id_put(self, + collaboration_id: str, + document_id: str, + body: Union[JSON, + IO[bytes]], + **kwargs: Any) -> Optional[JSON]: """Set execution consent (enable / disable) by ID of the Query or the Dataset. Set execution consent (enable / disable) by ID of the Query or the Dataset. @@ -1699,7 +1868,8 @@ async def consent_document_id_put( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -1730,7 +1900,10 @@ async def consent_document_id_put( response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -1894,7 +2067,8 @@ async def analytics_queries_document_id_publish_post( # pylint: disable=name-to _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -1925,7 +2099,10 @@ async def analytics_queries_document_id_publish_post( # pylint: disable=name-to response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -1941,7 +2118,10 @@ async def analytics_queries_document_id_publish_post( # pylint: disable=name-to return deserialized # type: ignore @distributed_trace_async - async def analytics_queries_list_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + async def analytics_queries_list_get( + self, + collaboration_id: str, + **kwargs: Any) -> JSON: """List all queries. List all queries. @@ -2004,7 +2184,10 @@ async def analytics_queries_list_get(self, collaboration_id: str, **kwargs: Any) response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2013,12 +2196,21 @@ async def analytics_queries_list_get(self, collaboration_id: str, **kwargs: Any) deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def analytics_queries_document_id_get(self, collaboration_id: str, document_id: str, **kwargs: Any) -> JSON: + async def analytics_queries_document_id_get( + self, + collaboration_id: str, + document_id: str, + **kwargs: Any) -> JSON: """Get query by id. Get query by id. @@ -2099,7 +2291,10 @@ async def analytics_queries_document_id_get(self, collaboration_id: str, documen response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2108,7 +2303,12 @@ async def analytics_queries_document_id_get(self, collaboration_id: str, documen deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -2245,7 +2445,8 @@ async def analytics_queries_document_id_vote_post( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) content_type = content_type if body else None cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) @@ -2280,7 +2481,10 @@ async def analytics_queries_document_id_vote_post( response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -2515,7 +2719,8 @@ async def analytics_queries_document_id_run_post( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[JSON] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -2546,7 +2751,10 @@ async def analytics_queries_document_id_run_post( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2555,12 +2763,21 @@ async def analytics_queries_document_id_run_post( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace_async - async def analytics_runs_job_id_get(self, collaboration_id: str, job_id: str, **kwargs: Any) -> JSON: + async def analytics_runs_job_id_get( + self, + collaboration_id: str, + job_id: str, + **kwargs: Any) -> JSON: """Get query run result by job id. Get query run result by job id. @@ -2636,7 +2853,10 @@ async def analytics_runs_job_id_get(self, collaboration_id: str, job_id: str, ** response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2645,7 +2865,12 @@ async def analytics_runs_job_id_get(self, collaboration_id: str, job_id: str, ** deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -2754,7 +2979,10 @@ async def analytics_queries_document_id_runs_get( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2763,7 +2991,8 @@ async def analytics_queries_document_id_runs_get( deserialized = None if cls: - return cls(pipeline_response, cast(Union[list[JSON], JSON], deserialized), {}) # type: ignore + return cls(pipeline_response, cast( + Union[list[JSON], JSON], deserialized), {}) # type: ignore return cast(Union[list[JSON], JSON], deserialized) # type: ignore @@ -2829,7 +3058,10 @@ async def analytics_datasets_document_id_queries_get( # pylint: disable=name-to response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2838,7 +3070,8 @@ async def analytics_datasets_document_id_queries_get( # pylint: disable=name-to deserialized = None if cls: - return cls(pipeline_response, cast(Union[list[str], JSON], deserialized), {}) # type: ignore + return cls(pipeline_response, cast( + Union[list[str], JSON], deserialized), {}) # type: ignore return cast(Union[list[str], JSON], deserialized) # type: ignore @@ -2936,9 +3169,12 @@ async def analytics_secrets_secret_name_put( """ @distributed_trace_async - async def analytics_secrets_secret_name_put( - self, collaboration_id: str, secret_name: str, body: Optional[Union[JSON, IO[bytes]]] = None, **kwargs: Any - ) -> JSON: + async def analytics_secrets_secret_name_put(self, + collaboration_id: str, + secret_name: str, + body: Optional[Union[JSON, + IO[bytes]]] = None, + **kwargs: Any) -> JSON: """Set secret for analytics workload. Set secret for analytics workload. @@ -2985,7 +3221,8 @@ async def analytics_secrets_secret_name_put( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) content_type = content_type if body else None cls: ClsType[JSON] = kwargs.pop("cls", None) @@ -3020,7 +3257,10 @@ async def analytics_secrets_secret_name_put( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3029,7 +3269,12 @@ async def analytics_secrets_secret_name_put( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -3119,7 +3364,10 @@ async def analytics_auditevents_get( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3128,6 +3376,11 @@ async def analytics_auditevents_get( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_patch.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_patch.py index 87676c65a8f..2bd950a309c 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_patch.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/aio/operations/_patch.py @@ -9,7 +9,8 @@ """ -__all__: list[str] = [] # Add all objects you want publicly available to users at this package level +# Add all objects you want publicly available to users at this package level +__all__: list[str] = [] def patch_sdk(): diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/__init__.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/__init__.py index 5a4c57889d9..91ecd54c4e2 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/__init__.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/__init__.py @@ -3,18 +3,16 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------- -# flake8: noqa: F403 # pylint: disable=wrong-import-position from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._patch import * # pylint: disable=unused-wildcard-import + from ._patch import * # pylint: disable=unused-wildcard-import # noqa: F403 from ._operations import CollaborationOperations # type: ignore from ._patch import __all__ as _patch_all -from ._patch import * from ._patch import patch_sdk as _patch_sdk __all__ = [ diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_operations.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_operations.py index 61807e6a0e7..56e9515acf4 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_operations.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_operations.py @@ -1,8 +1,8 @@ # pylint: disable=too-many-lines # coding=utf-8 -# -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- from collections.abc import MutableMapping from io import IOBase @@ -27,17 +27,23 @@ JSON = MutableMapping[str, Any] T = TypeVar("T") -ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, dict[str, Any]], Any]] +ClsType = Optional[Callable[[ + PipelineResponse[HttpRequest, HttpResponse], T, dict[str, Any]], Any]] _SERIALIZER = Serializer() _SERIALIZER.client_side_validation = False -def build_collaboration_list_get_request(*, active_only: bool = False, **kwargs: Any) -> HttpRequest: +def build_collaboration_list_get_request( + *, + active_only: bool = False, + **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL @@ -45,13 +51,20 @@ def build_collaboration_list_get_request(*, active_only: bool = False, **kwargs: # Construct parameters if active_only is not None: - _params["activeOnly"] = _SERIALIZER.query("active_only", active_only, "bool") - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["activeOnly"] = _SERIALIZER.query( + "active_only", active_only, "bool") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_id_get_request( @@ -60,50 +73,69 @@ def build_collaboration_id_get_request( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters if active_only is not None: - _params["activeOnly"] = _SERIALIZER.query("active_only", active_only, "bool") - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["activeOnly"] = _SERIALIZER.query( + "active_only", active_only, "bool") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) -def build_collaboration_report_get_request(collaboration_id: str, **kwargs: Any) -> HttpRequest: +def build_collaboration_report_get_request( + collaboration_id: str, + **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/report" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_get_request( # pylint: disable=name-too-long @@ -112,50 +144,67 @@ def build_collaboration_analytics_get_request( # pylint: disable=name-too-long _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) -def build_collaboration_analytics_cleanroompolicy_get_request( # pylint: disable=name-too-long - collaboration_id: str, **kwargs: Any +def build_collaboration_analytics_skr_policy_get_request( # pylint: disable=name-too-long + collaboration_id: str, dataset_id: str, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL - _url = "/collaborations/{collaborationId}/analytics/cleanroompolicy" + _url = "/collaborations/{collaborationId}/analytics/datasets/{datasetId}/skrpolicy" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "datasetId": _SERIALIZER.url( + "dataset_id", dataset_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_oidc_issuer_info_get_request( # pylint: disable=name-too-long @@ -164,24 +213,32 @@ def build_collaboration_oidc_issuer_info_get_request( # pylint: disable=name-to _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/oidc/issuerInfo" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_oidc_set_issuer_url_post_request( # pylint: disable=name-too-long @@ -190,27 +247,38 @@ def build_collaboration_oidc_set_issuer_url_post_request( # pylint: disable=nam _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop( + "Content-Type", None)) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/oidc/setIssuerUrl" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Content-Type"] = _SERIALIZER.header( + "content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_oidc_keys_get_request( # pylint: disable=name-too-long @@ -219,24 +287,32 @@ def build_collaboration_oidc_keys_get_request( # pylint: disable=name-too-long _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/oidc/keys" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_invitations_get_request( # pylint: disable=name-too-long @@ -245,26 +321,35 @@ def build_collaboration_invitations_get_request( # pylint: disable=name-too-lon _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/invitations" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters if pending_only is not None: - _params["pendingOnly"] = _SERIALIZER.query("pending_only", pending_only, "bool") - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["pendingOnly"] = _SERIALIZER.query( + "pending_only", pending_only, "bool") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_invitation_id_get_request( # pylint: disable=name-too-long @@ -273,25 +358,39 @@ def build_collaboration_invitation_id_get_request( # pylint: disable=name-too-l _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/invitations/{invitationId}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "invitationId": _SERIALIZER.url("invitation_id", invitation_id, "str"), + "collaborationId": _SERIALIZER.url( + "collaboration_id", + collaboration_id, + "str"), + "invitationId": _SERIALIZER.url( + "invitation_id", + invitation_id, + "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_invitation_id_accept_post_request( # pylint: disable=name-too-long @@ -300,25 +399,39 @@ def build_collaboration_invitation_id_accept_post_request( # pylint: disable=na _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/invitations/{invitationId}/accept" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "invitationId": _SERIALIZER.url("invitation_id", invitation_id, "str"), + "collaborationId": _SERIALIZER.url( + "collaboration_id", + collaboration_id, + "str"), + "invitationId": _SERIALIZER.url( + "invitation_id", + invitation_id, + "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_datasets_list_get_request( # pylint: disable=name-too-long @@ -327,24 +440,32 @@ def build_collaboration_analytics_datasets_list_get_request( # pylint: disable= _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/datasets" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_datasets_document_id_get_request( # pylint: disable=name-too-long @@ -353,25 +474,33 @@ def build_collaboration_analytics_datasets_document_id_get_request( # pylint: d _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/datasets/{documentId}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_datasets_document_id_publish_post_request( # pylint: disable=name-too-long @@ -380,28 +509,39 @@ def build_collaboration_analytics_datasets_document_id_publish_post_request( # _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop( + "Content-Type", None)) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/datasets/{documentId}/publish" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Content-Type"] = _SERIALIZER.header( + "content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_consent_document_id_get_request( # pylint: disable=name-too-long @@ -410,25 +550,33 @@ def build_collaboration_consent_document_id_get_request( # pylint: disable=name _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/consent/{documentId}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_consent_document_id_put_request( # pylint: disable=name-too-long @@ -437,28 +585,39 @@ def build_collaboration_consent_document_id_put_request( # pylint: disable=name _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop( + "Content-Type", None)) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/consent/{documentId}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Content-Type"] = _SERIALIZER.header( + "content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="PUT", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_queries_document_id_publish_post_request( # pylint: disable=name-too-long @@ -467,28 +626,39 @@ def build_collaboration_analytics_queries_document_id_publish_post_request( # p _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop( + "Content-Type", None)) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/queries/{documentId}/publish" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Content-Type"] = _SERIALIZER.header( + "content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_queries_list_get_request( # pylint: disable=name-too-long @@ -497,24 +667,32 @@ def build_collaboration_analytics_queries_list_get_request( # pylint: disable=n _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/queries" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_queries_document_id_get_request( # pylint: disable=name-too-long @@ -523,25 +701,33 @@ def build_collaboration_analytics_queries_document_id_get_request( # pylint: di _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/queries/{documentId}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_queries_document_id_vote_post_request( # pylint: disable=name-too-long @@ -550,28 +736,39 @@ def build_collaboration_analytics_queries_document_id_vote_post_request( # pyli _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop( + "Content-Type", None)) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/queries/{documentId}/vote" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Content-Type"] = _SERIALIZER.header( + "content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_queries_document_id_run_post_request( # pylint: disable=name-too-long @@ -580,28 +777,39 @@ def build_collaboration_analytics_queries_document_id_run_post_request( # pylin _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop( + "Content-Type", None)) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/queries/{documentId}/run" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Content-Type"] = _SERIALIZER.header( + "content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="POST", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_runs_job_id_get_request( # pylint: disable=name-too-long @@ -610,25 +818,33 @@ def build_collaboration_analytics_runs_job_id_get_request( # pylint: disable=na _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/runs/{jobId}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "jobId": _SERIALIZER.url("job_id", job_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "jobId": _SERIALIZER.url( + "job_id", job_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_queries_document_id_runs_get_request( # pylint: disable=name-too-long @@ -637,25 +853,33 @@ def build_collaboration_analytics_queries_document_id_runs_get_request( # pylin _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/queries/{documentId}/runs" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_datasets_document_id_queries_get_request( # pylint: disable=name-too-long @@ -664,25 +888,33 @@ def build_collaboration_analytics_datasets_document_id_queries_get_request( # p _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/datasets/{documentId}/queries" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "documentId": _SERIALIZER.url("document_id", document_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "documentId": _SERIALIZER.url( + "document_id", document_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_secrets_secret_name_put_request( # pylint: disable=name-too-long @@ -691,28 +923,39 @@ def build_collaboration_analytics_secrets_secret_name_put_request( # pylint: di _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop( + "Content-Type", None)) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/secrets/{secretName}" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - "secretName": _SERIALIZER.url("secret_name", secret_name, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), "secretName": _SERIALIZER.url( + "secret_name", secret_name, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore # Construct parameters - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers if content_type is not None: - _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Content-Type"] = _SERIALIZER.header( + "content_type", content_type, "str") _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="PUT", + url=_url, + params=_params, + headers=_headers, + **kwargs) def build_collaboration_analytics_auditevents_get_request( # pylint: disable=name-too-long @@ -726,14 +969,16 @@ def build_collaboration_analytics_auditevents_get_request( # pylint: disable=na _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) - api_version: str = kwargs.pop("api_version", _params.pop("api-version", "2026-03-01-preview")) + api_version: str = kwargs.pop( + "api_version", _params.pop( + "api-version", "2026-03-01-preview")) accept = _headers.pop("Accept", "application/json") # Construct URL _url = "/collaborations/{collaborationId}/analytics/auditevents" path_format_arguments = { - "collaborationId": _SERIALIZER.url("collaboration_id", collaboration_id, "str"), - } + "collaborationId": _SERIALIZER.url( + "collaboration_id", collaboration_id, "str"), } _url: str = _url.format(**path_format_arguments) # type: ignore @@ -741,15 +986,22 @@ def build_collaboration_analytics_auditevents_get_request( # pylint: disable=na if scope is not None: _params["scope"] = _SERIALIZER.query("scope", scope, "str") if from_seqno is not None: - _params["from_seqno"] = _SERIALIZER.query("from_seqno", from_seqno, "str") + _params["from_seqno"] = _SERIALIZER.query( + "from_seqno", from_seqno, "str") if to_seqno is not None: _params["to_seqno"] = _SERIALIZER.query("to_seqno", to_seqno, "str") - _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + _params["api-version"] = _SERIALIZER.query( + "api_version", api_version, "str") # Construct headers _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") - return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + return HttpRequest( + method="GET", + url=_url, + params=_params, + headers=_headers, + **kwargs) class CollaborationOperations: # pylint: disable=too-many-public-methods @@ -764,13 +1016,21 @@ class CollaborationOperations: # pylint: disable=too-many-public-methods def __init__(self, *args, **kwargs) -> None: input_args = list(args) - self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") - self._config: AnalyticsFrontendAPIConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") - self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") - self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + self._client: PipelineClient = input_args.pop( + 0) if input_args else kwargs.pop("client") + self._config: AnalyticsFrontendAPIConfiguration = input_args.pop( + 0) if input_args else kwargs.pop("config") + self._serialize: Serializer = input_args.pop( + 0) if input_args else kwargs.pop("serializer") + self._deserialize: Deserializer = input_args.pop( + 0) if input_args else kwargs.pop("deserializer") @distributed_trace - def list_get(self, *, active_only: bool = False, **kwargs: Any) -> list[JSON]: + def list_get( + self, + *, + active_only: bool = False, + **kwargs: Any) -> list[JSON]: """List all collaborations. List all collaborations. @@ -823,7 +1083,10 @@ def list_get(self, *, active_only: bool = False, **kwargs: Any) -> list[JSON]: response = pipeline_response.http_response if response.status_code not in [200]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -832,12 +1095,22 @@ def list_get(self, *, active_only: bool = False, **kwargs: Any) -> list[JSON]: deserialized = None if cls: - return cls(pipeline_response, cast(list[JSON], deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + list[JSON], + deserialized), + {}) # type: ignore return cast(list[JSON], deserialized) # type: ignore @distributed_trace - def id_get(self, collaboration_id: str, *, active_only: bool = False, **kwargs: Any) -> JSON: + def id_get( + self, + collaboration_id: str, + *, + active_only: bool = False, + **kwargs: Any) -> JSON: """Get collaboration by id. Get collaboration by id. @@ -899,7 +1172,10 @@ def id_get(self, collaboration_id: str, *, active_only: bool = False, **kwargs: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -908,7 +1184,12 @@ def id_get(self, collaboration_id: str, *, active_only: bool = False, **kwargs: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -1001,7 +1282,10 @@ def report_get(self, collaboration_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response if response.status_code not in [200, 400, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1010,7 +1294,12 @@ def report_get(self, collaboration_id: str, **kwargs: Any) -> JSON: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -1074,7 +1363,10 @@ def analytics_get(self, collaboration_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1083,18 +1375,29 @@ def analytics_get(self, collaboration_id: str, **kwargs: Any) -> JSON: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: Any) -> JSON: - """Get collaboration analytics cleanroompolicy. + def analytics_skr_policy_get( + self, + collaboration_id: str, + dataset_id: str, + **kwargs: Any) -> JSON: + """Get collaboration analytics skr policy for a dataset. - Get collaboration analytics cleanroompolicy. + Get collaboration analytics skr policy for a dataset. :param collaboration_id: Required. :type collaboration_id: str + :param dataset_id: Required. + :type dataset_id: str :return: JSON object :rtype: JSON :raises ~azure.core.exceptions.HttpResponseError: @@ -1104,14 +1407,18 @@ def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: Any) -> # response body for status code(s): 200 response == { - "claims": { - "claims": { - "str": {} + "anyOf": [ + { + "allOf": [ + { + "claim": "str", + "equals": "str" + } + ], + "authority": "str" } - }, - "proposalIds": [ - "str" - ] + ], + "version": "str" } # response body for status code(s): 422 response == { @@ -1135,8 +1442,9 @@ def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: Any) -> cls: ClsType[JSON] = kwargs.pop("cls", None) - _request = build_collaboration_analytics_cleanroompolicy_get_request( + _request = build_collaboration_analytics_skr_policy_get_request( collaboration_id=collaboration_id, + dataset_id=dataset_id, api_version=self._config.api_version, headers=_headers, params=_params, @@ -1151,7 +1459,10 @@ def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: Any) -> response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1160,12 +1471,20 @@ def analytics_cleanroompolicy_get(self, collaboration_id: str, **kwargs: Any) -> deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def oidc_issuer_info_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + def oidc_issuer_info_get( + self, + collaboration_id: str, + **kwargs: Any) -> JSON: """Get collaboration OIDC issuer info. Get collaboration OIDC issuer info. @@ -1226,7 +1545,10 @@ def oidc_issuer_info_get(self, collaboration_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1235,7 +1557,12 @@ def oidc_issuer_info_get(self, collaboration_id: str, **kwargs: Any) -> JSON: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -1329,9 +1656,11 @@ def oidc_set_issuer_url_post( """ @distributed_trace - def oidc_set_issuer_url_post( - self, collaboration_id: str, body: Optional[Union[JSON, IO[bytes]]] = None, **kwargs: Any - ) -> JSON: + def oidc_set_issuer_url_post(self, + collaboration_id: str, + body: Optional[Union[JSON, + IO[bytes]]] = None, + **kwargs: Any) -> JSON: """Set collaboration oidc issuer url. Set collaboration oidc issuer url. @@ -1377,7 +1706,8 @@ def oidc_set_issuer_url_post( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) content_type = content_type if body else None cls: ClsType[JSON] = kwargs.pop("cls", None) @@ -1411,7 +1741,10 @@ def oidc_set_issuer_url_post( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1420,7 +1753,12 @@ def oidc_set_issuer_url_post( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -1495,7 +1833,10 @@ def oidc_keys_get(self, collaboration_id: str, **kwargs: Any) -> JSON: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1504,12 +1845,22 @@ def oidc_keys_get(self, collaboration_id: str, **kwargs: Any) -> JSON: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def invitations_get(self, collaboration_id: str, *, pending_only: bool = False, **kwargs: Any) -> JSON: + def invitations_get( + self, + collaboration_id: str, + *, + pending_only: bool = False, + **kwargs: Any) -> JSON: """List all invitations. List all invitations. @@ -1571,7 +1922,10 @@ def invitations_get(self, collaboration_id: str, *, pending_only: bool = False, response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1580,12 +1934,21 @@ def invitations_get(self, collaboration_id: str, *, pending_only: bool = False, deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def invitation_id_get(self, collaboration_id: str, invitation_id: str, **kwargs: Any) -> JSON: + def invitation_id_get( + self, + collaboration_id: str, + invitation_id: str, + **kwargs: Any) -> JSON: """Get invitation by id. Get invitation by id. @@ -1652,7 +2015,10 @@ def invitation_id_get(self, collaboration_id: str, invitation_id: str, **kwargs: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1661,12 +2027,21 @@ def invitation_id_get(self, collaboration_id: str, invitation_id: str, **kwargs: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def invitation_id_accept_post(self, collaboration_id: str, invitation_id: str, **kwargs: Any) -> Optional[JSON]: + def invitation_id_accept_post( + self, + collaboration_id: str, + invitation_id: str, + **kwargs: Any) -> Optional[JSON]: """Accept invitation by id. Accept invitation by id. @@ -1721,7 +2096,10 @@ def invitation_id_accept_post(self, collaboration_id: str, invitation_id: str, * response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -1737,7 +2115,10 @@ def invitation_id_accept_post(self, collaboration_id: str, invitation_id: str, * return deserialized # type: ignore @distributed_trace - def analytics_datasets_list_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + def analytics_datasets_list_get( + self, + collaboration_id: str, + **kwargs: Any) -> JSON: """List all datasets. List all datasets. @@ -1800,7 +2181,10 @@ def analytics_datasets_list_get(self, collaboration_id: str, **kwargs: Any) -> J response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1809,12 +2193,21 @@ def analytics_datasets_list_get(self, collaboration_id: str, **kwargs: Any) -> J deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def analytics_datasets_document_id_get(self, collaboration_id: str, document_id: str, **kwargs: Any) -> JSON: + def analytics_datasets_document_id_get( + self, + collaboration_id: str, + document_id: str, + **kwargs: Any) -> JSON: """Get dataset by id. Get dataset by id. @@ -1917,7 +2310,10 @@ def analytics_datasets_document_id_get(self, collaboration_id: str, document_id: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -1926,7 +2322,12 @@ def analytics_datasets_document_id_get(self, collaboration_id: str, document_id: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -2137,7 +2538,8 @@ def analytics_datasets_document_id_publish_post( # pylint: disable=name-too-lon _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -2168,7 +2570,10 @@ def analytics_datasets_document_id_publish_post( # pylint: disable=name-too-lon response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -2184,7 +2589,11 @@ def analytics_datasets_document_id_publish_post( # pylint: disable=name-too-lon return deserialized # type: ignore @distributed_trace - def consent_document_id_get(self, collaboration_id: str, document_id: str, **kwargs: Any) -> JSON: + def consent_document_id_get( + self, + collaboration_id: str, + document_id: str, + **kwargs: Any) -> JSON: """Check execution consent by ID of the Query or the Dataset. Check execution consent by ID of the Query or the Dataset. @@ -2247,7 +2656,10 @@ def consent_document_id_get(self, collaboration_id: str, document_id: str, **kwa response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2256,7 +2668,12 @@ def consent_document_id_get(self, collaboration_id: str, document_id: str, **kwa deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -2346,9 +2763,12 @@ def consent_document_id_put( """ @distributed_trace - def consent_document_id_put( - self, collaboration_id: str, document_id: str, body: Union[JSON, IO[bytes]], **kwargs: Any - ) -> Optional[JSON]: + def consent_document_id_put(self, + collaboration_id: str, + document_id: str, + body: Union[JSON, + IO[bytes]], + **kwargs: Any) -> Optional[JSON]: """Set execution consent (enable / disable) by ID of the Query or the Dataset. Set execution consent (enable / disable) by ID of the Query or the Dataset. @@ -2391,7 +2811,8 @@ def consent_document_id_put( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -2422,7 +2843,10 @@ def consent_document_id_put( response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -2586,7 +3010,8 @@ def analytics_queries_document_id_publish_post( # pylint: disable=name-too-long _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -2617,7 +3042,10 @@ def analytics_queries_document_id_publish_post( # pylint: disable=name-too-long response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -2633,7 +3061,10 @@ def analytics_queries_document_id_publish_post( # pylint: disable=name-too-long return deserialized # type: ignore @distributed_trace - def analytics_queries_list_get(self, collaboration_id: str, **kwargs: Any) -> JSON: + def analytics_queries_list_get( + self, + collaboration_id: str, + **kwargs: Any) -> JSON: """List all queries. List all queries. @@ -2696,7 +3127,10 @@ def analytics_queries_list_get(self, collaboration_id: str, **kwargs: Any) -> JS response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2705,12 +3139,21 @@ def analytics_queries_list_get(self, collaboration_id: str, **kwargs: Any) -> JS deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def analytics_queries_document_id_get(self, collaboration_id: str, document_id: str, **kwargs: Any) -> JSON: + def analytics_queries_document_id_get( + self, + collaboration_id: str, + document_id: str, + **kwargs: Any) -> JSON: """Get query by id. Get query by id. @@ -2791,7 +3234,10 @@ def analytics_queries_document_id_get(self, collaboration_id: str, document_id: response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -2800,7 +3246,12 @@ def analytics_queries_document_id_get(self, collaboration_id: str, document_id: deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -2891,9 +3342,12 @@ def analytics_queries_document_id_vote_post( """ @distributed_trace - def analytics_queries_document_id_vote_post( - self, collaboration_id: str, document_id: str, body: Optional[Union[JSON, IO[bytes]]] = None, **kwargs: Any - ) -> Optional[JSON]: + def analytics_queries_document_id_vote_post(self, + collaboration_id: str, + document_id: str, + body: Optional[Union[JSON, + IO[bytes]]] = None, + **kwargs: Any) -> Optional[JSON]: """Vote on query by id. Vote on query by id. @@ -2937,7 +3391,8 @@ def analytics_queries_document_id_vote_post( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) content_type = content_type if body else None cls: ClsType[Optional[JSON]] = kwargs.pop("cls", None) @@ -2972,7 +3427,10 @@ def analytics_queries_document_id_vote_post( response = pipeline_response.http_response if response.status_code not in [204, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) deserialized = None @@ -3207,7 +3665,8 @@ def analytics_queries_document_id_run_post( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) cls: ClsType[JSON] = kwargs.pop("cls", None) content_type = content_type or "application/json" @@ -3238,7 +3697,10 @@ def analytics_queries_document_id_run_post( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3247,12 +3709,21 @@ def analytics_queries_document_id_run_post( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @distributed_trace - def analytics_runs_job_id_get(self, collaboration_id: str, job_id: str, **kwargs: Any) -> JSON: + def analytics_runs_job_id_get( + self, + collaboration_id: str, + job_id: str, + **kwargs: Any) -> JSON: """Get query run result by job id. Get query run result by job id. @@ -3328,7 +3799,10 @@ def analytics_runs_job_id_get(self, collaboration_id: str, job_id: str, **kwargs response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3337,7 +3811,12 @@ def analytics_runs_job_id_get(self, collaboration_id: str, job_id: str, **kwargs deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -3446,7 +3925,10 @@ def analytics_queries_document_id_runs_get( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3455,7 +3937,8 @@ def analytics_queries_document_id_runs_get( deserialized = None if cls: - return cls(pipeline_response, cast(Union[list[JSON], JSON], deserialized), {}) # type: ignore + return cls(pipeline_response, cast( + Union[list[JSON], JSON], deserialized), {}) # type: ignore return cast(Union[list[JSON], JSON], deserialized) # type: ignore @@ -3521,7 +4004,10 @@ def analytics_datasets_document_id_queries_get( # pylint: disable=name-too-long response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3530,7 +4016,8 @@ def analytics_datasets_document_id_queries_get( # pylint: disable=name-too-long deserialized = None if cls: - return cls(pipeline_response, cast(Union[list[str], JSON], deserialized), {}) # type: ignore + return cls(pipeline_response, cast( + Union[list[str], JSON], deserialized), {}) # type: ignore return cast(Union[list[str], JSON], deserialized) # type: ignore @@ -3628,9 +4115,12 @@ def analytics_secrets_secret_name_put( """ @distributed_trace - def analytics_secrets_secret_name_put( - self, collaboration_id: str, secret_name: str, body: Optional[Union[JSON, IO[bytes]]] = None, **kwargs: Any - ) -> JSON: + def analytics_secrets_secret_name_put(self, + collaboration_id: str, + secret_name: str, + body: Optional[Union[JSON, + IO[bytes]]] = None, + **kwargs: Any) -> JSON: """Set secret for analytics workload. Set secret for analytics workload. @@ -3677,7 +4167,8 @@ def analytics_secrets_secret_name_put( _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = kwargs.pop("params", {}) or {} - content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + content_type: Optional[str] = kwargs.pop( + "content_type", _headers.pop("Content-Type", None)) content_type = content_type if body else None cls: ClsType[JSON] = kwargs.pop("cls", None) @@ -3712,7 +4203,10 @@ def analytics_secrets_secret_name_put( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3721,7 +4215,12 @@ def analytics_secrets_secret_name_put( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore @@ -3811,7 +4310,10 @@ def analytics_auditevents_get( response = pipeline_response.http_response if response.status_code not in [200, 422]: - map_error(status_code=response.status_code, response=response, error_map=error_map) + map_error( + status_code=response.status_code, + response=response, + error_map=error_map) raise HttpResponseError(response=response) if response.content: @@ -3820,6 +4322,11 @@ def analytics_auditevents_get( deserialized = None if cls: - return cls(pipeline_response, cast(JSON, deserialized), {}) # type: ignore + return cls( + pipeline_response, + cast( + JSON, + deserialized), + {}) # type: ignore return cast(JSON, deserialized) # type: ignore diff --git a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_patch.py b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_patch.py index 87676c65a8f..2bd950a309c 100644 --- a/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_patch.py +++ b/src/managedcleanroom/azext_managedcleanroom/analytics_frontend_api/operations/_patch.py @@ -9,7 +9,8 @@ """ -__all__: list[str] = [] # Add all objects you want publicly available to users at this package level +# Add all objects you want publicly available to users at this package level +__all__: list[str] = [] def patch_sdk(): diff --git a/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_collaboration.py b/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_collaboration.py index b42e5653640..5cbae64dc84 100644 --- a/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_collaboration.py +++ b/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_collaboration.py @@ -14,7 +14,7 @@ frontend_collaboration_list, frontend_collaboration_show, frontend_collaboration_analytics_show, - frontend_collaboration_analytics_cleanroompolicy, + frontend_collaboration_analytics_skr_policy, frontend_collaboration_oidc_issuerinfo_show, frontend_collaboration_oidc_set_issuer_url, frontend_collaboration_oidc_keys_show, @@ -128,28 +128,31 @@ def test_show_analytics(self, mock_get_client): "test-collab-123") @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') - def test_show_analytics_cleanroom_policy(self, mock_get_client): - """Test showing cleanroom policy""" + def test_show_analytics_skr_policy(self, mock_get_client): + """Test showing SKR policy for a dataset""" # Mock policy response mock_policy = { - "policyId": "policy-123", + "policyId": "skr-policy-123", + "datasetId": "dataset-456", "rules": ["rule1", "rule2"] } mock_client = Mock() - mock_client.collaboration.analytics_cleanroompolicy_get.return_value = mock_policy + mock_client.collaboration.analytics_skr_policy_get.return_value = mock_policy mock_get_client.return_value = mock_client # Execute - result = frontend_collaboration_analytics_cleanroompolicy( + result = frontend_collaboration_analytics_skr_policy( cmd=Mock(), - collaboration_id="test-collab-123" + collaboration_id="test-collab-123", + dataset_id="dataset-456" ) # Verify - self.assertEqual(result["policyId"], "policy-123") + self.assertEqual(result["policyId"], "skr-policy-123") + self.assertEqual(result["datasetId"], "dataset-456") self.assertEqual(len(result["rules"]), 2) - mock_client.collaboration.analytics_cleanroompolicy_get.assert_called_once_with( - "test-collab-123") + mock_client.collaboration.analytics_skr_policy_get.assert_called_once_with( + "test-collab-123", "dataset-456") # OIDC Tests diff --git a/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_dataset.py b/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_dataset.py index 818b9593b39..6ce1d38b8d8 100644 --- a/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_dataset.py +++ b/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_dataset.py @@ -156,7 +156,7 @@ def test_publish_dataset_with_sse_parameters(self, mock_get_client): ], "format": "Delta" } - + with patch('builtins.open', unittest.mock.mock_open(read_data=json.dumps(test_schema))): # Execute with SSE parameters result = frontend_collaboration_dataset_publish( @@ -180,18 +180,19 @@ def test_publish_dataset_with_sse_parameters(self, mock_get_client): dek_secret_id=None, kek_keyvault_url=None, kek_secret_id=None, - kek_maa_url=None - ) + kek_maa_url=None) # Verify self.assertEqual(result["datasetId"], "test-dataset-123") self.assertEqual(result["status"], "published") - + # Verify the body was constructed correctly call_args = mock_client.collaboration.analytics_datasets_document_id_publish_post.call_args body = call_args[0][2] self.assertEqual(body["name"], "test-dataset-123") - self.assertEqual(body["store"]["storageAccountUrl"], "https://mystorageaccount.blob.core.windows.net") + self.assertEqual( + body["store"]["storageAccountUrl"], + "https://mystorageaccount.blob.core.windows.net") self.assertEqual(body["store"]["encryptionMode"], "SSE") self.assertEqual(body["identity"]["name"], "northwind-identity") self.assertNotIn("dek", body) @@ -215,7 +216,7 @@ def test_publish_dataset_with_cpk_parameters(self, mock_get_client): "fields": [{"fieldName": "id", "fieldType": "string"}], "format": "Delta" } - + with patch('builtins.open', unittest.mock.mock_open(read_data=json.dumps(test_schema))): # Execute with CPK parameters result = frontend_collaboration_dataset_publish( @@ -239,18 +240,19 @@ def test_publish_dataset_with_cpk_parameters(self, mock_get_client): dek_secret_id="dek-secret-123", kek_keyvault_url="https://mykeyvault.vault.azure.net", kek_secret_id="kek-secret-123", - kek_maa_url="https://sharedeus.eus.attest.azure.net" - ) + kek_maa_url="https://sharedeus.eus.attest.azure.net") # Verify self.assertEqual(result["datasetId"], "test-dataset-cpk") self.assertEqual(result["status"], "published") - + # Verify CPK fields are present in body call_args = mock_client.collaboration.analytics_datasets_document_id_publish_post.call_args body = call_args[0][2] self.assertIn("dek", body) - self.assertEqual(body["dek"]["keyVaultUrl"], "https://mykeyvault.vault.azure.net") + self.assertEqual( + body["dek"]["keyVaultUrl"], + "https://mykeyvault.vault.azure.net") self.assertIn("kek", body) self.assertEqual(body["kek"]["secretId"], "kek-secret-123") @@ -258,7 +260,7 @@ def test_publish_dataset_with_cpk_parameters(self, mock_get_client): def test_publish_dataset_mutual_exclusion(self, mock_get_client): """Test that body and parameters are mutually exclusive""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -273,13 +275,15 @@ def test_publish_dataset_mutual_exclusion(self, mock_get_client): container_name="datasets" ) - self.assertIn("Cannot use --body together with individual parameters", str(context.exception)) + self.assertIn( + "Cannot use --body together with individual parameters", str(context.exception)) @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') - def test_publish_dataset_missing_required_parameters(self, mock_get_client): + def test_publish_dataset_missing_required_parameters( + self, mock_get_client): """Test validation of required parameters""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -300,13 +304,13 @@ def test_publish_dataset_missing_required_parameters(self, mock_get_client): def test_publish_dataset_cpk_missing_keys(self, mock_get_client): """Test CPK mode requires DEK/KEK parameters""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client # Mock file reading test_schema = {"fields": [], "format": "Delta"} - + with patch('builtins.open', unittest.mock.mock_open(read_data=json.dumps(test_schema))): # Execute CPK mode without DEK/KEK params - should raise error with self.assertRaises(CLIError) as context: @@ -328,8 +332,9 @@ def test_publish_dataset_cpk_missing_keys(self, mock_get_client): # Missing DEK/KEK params ) - self.assertIn("CPK encryption mode requires", str(context.exception)) - + self.assertIn( + "CPK encryption mode requires", str( + context.exception)) if __name__ == '__main__': diff --git a/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_query.py b/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_query.py index 8fcb3739935..e7e320b9e4c 100644 --- a/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_query.py +++ b/src/managedcleanroom/azext_managedcleanroom/tests/latest/test_frontend_query.py @@ -202,7 +202,7 @@ def test_list_query_run_history(self, mock_get_client): """Test listing query execution history""" # Mock the client and its method chain mock_client = Mock() - mock_client.collaboration.analytics_queries_document_id_runhistory_get.return_value = [ + mock_client.collaboration.analytics_queries_document_id_runs_get.return_value = [ { "runId": "run-1", "queryId": "test-query-123", @@ -226,7 +226,7 @@ def test_list_query_run_history(self, mock_get_client): self.assertEqual(len(result), 2) self.assertEqual(result[0]["runId"], "run-1") self.assertEqual(result[1]["runId"], "run-2") - mock_client.collaboration.analytics_queries_document_id_runhistory_get.assert_called_once_with( + mock_client.collaboration.analytics_queries_document_id_runs_get.assert_called_once_with( "test-collab-123", "test-query-123") @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') @@ -234,7 +234,7 @@ def test_show_query_run_result(self, mock_get_client): """Test showing specific query run details""" # Mock the client and its method chain mock_client = Mock() - mock_client.collaboration.analytics_queries_jobid_get.return_value = { + mock_client.collaboration.analytics_runs_job_id_get.return_value = { "runId": "test-job-123", "queryId": "test-query-123", "status": "completed", @@ -255,7 +255,7 @@ def test_show_query_run_result(self, mock_get_client): self.assertEqual(result["runId"], "test-job-123") self.assertEqual(result["queryId"], "test-query-123") self.assertEqual(result["status"], "completed") - mock_client.collaboration.analytics_queries_jobid_get.assert_called_once_with( + mock_client.collaboration.analytics_runs_job_id_get.assert_called_once_with( "test-collab-123", "test-job-123") # Query Publish with Parameters Tests @@ -266,9 +266,7 @@ def test_publish_query_with_parameters_from_files(self, mock_get_client): # Mock the client mock_client = Mock() mock_client.collaboration.analytics_queries_document_id_publish_post.return_value = { - "queryId": "test-query-123", - "status": "published" - } + "queryId": "test-query-123", "status": "published"} mock_get_client.return_value = mock_client # Mock file reading for JSON segment files @@ -290,8 +288,8 @@ def test_publish_query_with_parameters_from_files(self, mock_get_client): "preConditions": "", "postFilters": "" } - - def mock_open_handler(filename, mode='r'): + + def mock_open_handler(filename, mode='r', **kwargs): content = { 'segment1.json': json.dumps(segment_1), 'segment2.json': json.dumps(segment_2), @@ -307,20 +305,24 @@ def mock_open_handler(filename, mode='r'): collaboration_id="test-collab-123", document_id="test-query-123", body=None, - query_segment=["@segment1.json", "@segment2.json", "@segment3.json"], + query_segment=[ + "@segment1.json", + "@segment2.json", + "@segment3.json"], execution_sequence=None, input_datasets="dataset1:view1,dataset2:view2", - output_dataset="output-dataset:results" - ) + output_dataset="output-dataset:results") # Verify self.assertEqual(result["queryId"], "test-query-123") self.assertEqual(result["status"], "published") - + # Verify body construction - segments were parsed from JSON call_args = mock_client.collaboration.analytics_queries_document_id_publish_post.call_args body = call_args[0][2] - self.assertEqual(body["inputDatasets"], "dataset1:view1,dataset2:view2") + self.assertEqual( + body["inputDatasets"], + "dataset1:view1,dataset2:view2") self.assertEqual(body["outputDataset"], "output-dataset:results") self.assertEqual(len(body["queryData"]), 3) self.assertEqual(body["queryData"][0]["data"], "SELECT * FROM table1") @@ -333,9 +335,7 @@ def test_publish_query_with_inline_sql(self, mock_get_client): # Mock the client mock_client = Mock() mock_client.collaboration.analytics_queries_document_id_publish_post.return_value = { - "queryId": "test-query-inline", - "status": "published" - } + "queryId": "test-query-inline", "status": "published"} mock_get_client.return_value = mock_client # Execute with inline SQL @@ -352,7 +352,7 @@ def test_publish_query_with_inline_sql(self, mock_get_client): # Verify self.assertEqual(result["status"], "published") - + # Verify body construction call_args = mock_client.collaboration.analytics_queries_document_id_publish_post.call_args body = call_args[0][2] @@ -364,7 +364,7 @@ def test_publish_query_with_inline_sql(self, mock_get_client): def test_publish_query_mutual_exclusion(self, mock_get_client): """Test that body and parameters are mutually exclusive for query publish""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -378,13 +378,14 @@ def test_publish_query_mutual_exclusion(self, mock_get_client): query_segment=["SELECT * FROM table1"] ) - self.assertIn("Cannot use --body together with individual parameters", str(context.exception)) + self.assertIn( + "Cannot use --body together with individual parameters", str(context.exception)) @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') def test_publish_query_segment_count_mismatch(self, mock_get_client): """Test validation when segment count doesn't match execution sequence count""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -401,13 +402,15 @@ def test_publish_query_segment_count_mismatch(self, mock_get_client): output_dataset="output-dataset:results" ) - self.assertIn("must match execution sequence count", str(context.exception)) + self.assertIn( + "must match execution sequence count", str( + context.exception)) @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') def test_publish_query_file_mode_rejects_exec_seq(self, mock_get_client): """Test that FILE mode raises error if --execution-sequence is provided""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -417,7 +420,7 @@ def test_publish_query_file_mode_rejects_exec_seq(self, mock_get_client): "preConditions": "", "postFilters": "" }) - + with patch('builtins.open', unittest.mock.mock_open(read_data=segment_json)): with self.assertRaises(CLIError) as context: frontend_collaboration_query_publish( @@ -431,13 +434,15 @@ def test_publish_query_file_mode_rejects_exec_seq(self, mock_get_client): output_dataset="output-dataset:results" ) - self.assertIn("must not be provided when using @file.json", str(context.exception)) + self.assertIn( + "must not be provided when using @file.json", str(context.exception)) @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') - def test_publish_query_inline_mode_requires_exec_seq(self, mock_get_client): + def test_publish_query_inline_mode_requires_exec_seq( + self, mock_get_client): """Test that INLINE mode requires --execution-sequence""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -459,29 +464,44 @@ def test_publish_query_inline_mode_requires_exec_seq(self, mock_get_client): def test_publish_query_disallows_mixed_segments(self, mock_get_client): """Test that mixing @file.json and inline segments raises error""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client - with self.assertRaises(CLIError) as context: - frontend_collaboration_query_publish( - cmd=Mock(), - collaboration_id="test-collab-123", - document_id="test-query-123", - body=None, - query_segment=["@segment1.json", "SELECT * FROM table2"], # Mixed - execution_sequence="1,2", - input_datasets="dataset1:view1", - output_dataset="output-dataset:results" - ) + # Mock file content for segment1.json + segment_1 = { + "data": "SELECT * FROM table1", + "executionSequence": 1, + "preConditions": "", + "postFilters": "" + } + mock_file_content = json.dumps(segment_1) - self.assertIn("Cannot mix @file.json and inline SQL", str(context.exception)) + with patch('builtins.open', unittest.mock.mock_open(read_data=mock_file_content)): + with self.assertRaises(CLIError) as context: + frontend_collaboration_query_publish( + cmd=Mock(), + collaboration_id="test-collab-123", + document_id="test-query-123", + body=None, + query_segment=[ + "@segment1.json", + "SELECT * FROM table2"], + # Mixed + execution_sequence="1,2", + input_datasets="dataset1:view1", + output_dataset="output-dataset:results" + ) + + self.assertIn("Cannot mix @file.json / JSON-dict and inline SQL", + str(context.exception)) @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') - def test_publish_query_file_missing_execution_sequence(self, mock_get_client): + def test_publish_query_file_missing_execution_sequence( + self, mock_get_client): """Test that segment JSON file must contain executionSequence""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -491,7 +511,7 @@ def test_publish_query_file_missing_execution_sequence(self, mock_get_client): "preConditions": "", "postFilters": "" }) - + with patch('builtins.open', unittest.mock.mock_open(read_data=segment_json)): with self.assertRaises(CLIError) as context: frontend_collaboration_query_publish( @@ -505,13 +525,15 @@ def test_publish_query_file_missing_execution_sequence(self, mock_get_client): output_dataset="output-dataset:results" ) - self.assertIn('must contain "executionSequence"', str(context.exception)) + self.assertIn( + 'must contain "executionSequence"', str( + context.exception)) @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') def test_publish_query_invalid_dataset_format(self, mock_get_client): """Test validation of dataset ID:view format""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -542,9 +564,7 @@ def test_run_query_with_parameters(self, mock_get_client, mock_uuid4): # Mock the client mock_client = Mock() mock_client.collaboration.analytics_queries_document_id_run_post.return_value = { - "runId": "generated-run-id-456", - "status": "running" - } + "runId": "generated-run-id-456", "status": "running"} mock_get_client.return_value = mock_client # Execute with parameters @@ -561,7 +581,7 @@ def test_run_query_with_parameters(self, mock_get_client, mock_uuid4): # Verify self.assertEqual(result["runId"], "generated-run-id-456") - + # Verify body was constructed with parameters call_args = mock_client.collaboration.analytics_queries_document_id_run_post.call_args body = call_args[1]["body"] @@ -575,7 +595,7 @@ def test_run_query_with_parameters(self, mock_get_client, mock_uuid4): def test_run_query_mutual_exclusion(self, mock_get_client): """Test that body and parameters are mutually exclusive for query run""" from azure.cli.core.util import CLIError - + mock_client = Mock() mock_get_client.return_value = mock_client @@ -589,11 +609,13 @@ def test_run_query_mutual_exclusion(self, mock_get_client): dry_run=True ) - self.assertIn("Cannot use --body together with individual parameters", str(context.exception)) + self.assertIn( + "Cannot use --body together with individual parameters", str(context.exception)) @patch('uuid.uuid4') @patch('azext_managedcleanroom._frontend_custom.get_frontend_client') - def test_run_query_with_partial_parameters(self, mock_get_client, mock_uuid4): + def test_run_query_with_partial_parameters( + self, mock_get_client, mock_uuid4): """Test running a query with only some optional parameters""" # Mock UUID generation mock_uuid4.return_value = "generated-run-id-789" @@ -601,9 +623,7 @@ def test_run_query_with_partial_parameters(self, mock_get_client, mock_uuid4): # Mock the client mock_client = Mock() mock_client.collaboration.analytics_queries_document_id_run_post.return_value = { - "runId": "generated-run-id-789", - "status": "running" - } + "runId": "generated-run-id-789", "status": "running"} mock_get_client.return_value = mock_client # Execute with only dry_run parameter @@ -620,7 +640,7 @@ def test_run_query_with_partial_parameters(self, mock_get_client, mock_uuid4): # Verify self.assertEqual(result["status"], "running") - + # Verify only dry_run is in body (not False boolean values) call_args = mock_client.collaboration.analytics_queries_document_id_run_post.call_args body = call_args[1]["body"] diff --git a/src/managedcleanroom/setup.py b/src/managedcleanroom/setup.py index 1c7d7a36b43..da36f59036b 100644 --- a/src/managedcleanroom/setup.py +++ b/src/managedcleanroom/setup.py @@ -10,7 +10,7 @@ # HISTORY.rst entry. -VERSION = '1.0.0b4' +VERSION = '1.0.0b5' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers