From f8cc79f087640c5546ecae6d3d43a5ea3e2a0d30 Mon Sep 17 00:00:00 2001 From: Yishi Wang Date: Mon, 2 Dec 2024 13:15:54 +0800 Subject: [PATCH] {Core} Add dict transformation for typespec generated SDKs (#30339) * Add dict transformation for typespec generated SDKs * camel case * exclude autorest model * fix typo * remove keyvault security domain output transformer * rewrite knack.todict * remove unused func * fix typo --- .../azure/cli/core/commands/__init__.py | 3 +- src/azure-cli-core/azure/cli/core/util.py | 36 ++++++++++++++++++- .../command_modules/keyvault/_transformers.py | 10 ------ .../cli/command_modules/keyvault/commands.py | 10 +++--- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/commands/__init__.py b/src/azure-cli-core/azure/cli/core/commands/__init__.py index 9f366b4bc0b..80dd362115c 100644 --- a/src/azure-cli-core/azure/cli/core/commands/__init__.py +++ b/src/azure-cli-core/azure/cli/core/commands/__init__.py @@ -38,7 +38,7 @@ from knack.preview import ImplicitPreviewItem, PreviewItem, resolve_preview_info from knack.experimental import ImplicitExperimentalItem, ExperimentalItem, resolve_experimental_info from knack.log import get_logger, CLILogging -from knack.util import CLIError, CommandResultItem, todict +from knack.util import CLIError, CommandResultItem from knack.events import EVENT_INVOKER_TRANSFORM_RESULT from knack.validators import DefaultStr @@ -715,6 +715,7 @@ def _run_job(self, expanded_arg, cmd_copy): elif _is_paged(result): result = list(result) + from ..util import todict result = todict(result, AzCliCommandInvoker.remove_additional_prop_layer) event_data = {'result': result} diff --git a/src/azure-cli-core/azure/cli/core/util.py b/src/azure-cli-core/azure/cli/core/util.py index 8abc77884a7..886972fee1e 100644 --- a/src/azure-cli-core/azure/cli/core/util.py +++ b/src/azure-cli-core/azure/cli/core/util.py @@ -18,7 +18,7 @@ from urllib.request import urlopen from knack.log import get_logger -from knack.util import CLIError, to_snake_case +from knack.util import CLIError, to_snake_case, to_camel_case logger = get_logger(__name__) @@ -624,6 +624,40 @@ def b64_to_hex(s): return hex_data +def todict(obj, post_processor=None): + """ + Convert an object to a dictionary. Use 'post_processor(original_obj, dictionary)' to update the + dictionary in the process + """ + from datetime import date, time, datetime, timedelta + from enum import Enum + if isinstance(obj, dict): + result = {k: todict(v, post_processor) for (k, v) in obj.items()} + return post_processor(obj, result) if post_processor else result + if isinstance(obj, list): + return [todict(a, post_processor) for a in obj] + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, (date, time, datetime)): + return obj.isoformat() + if isinstance(obj, timedelta): + return str(obj) + # This is the only difference with knack.util.todict because for typespec generated SDKs + # The base model stores data in obj.__dict__['_data'] instead of in obj.__dict__ + # We need to call obj.as_dict() to extract data for this kind of model + if hasattr(obj, 'as_dict') and not hasattr(obj, '_attribute_map'): + result = {to_camel_case(k): todict(v, post_processor) for k, v in obj.as_dict().items()} + return post_processor(obj, result) if post_processor else result + if hasattr(obj, '_asdict'): + return todict(obj._asdict(), post_processor) + if hasattr(obj, '__dict__'): + result = {to_camel_case(k): todict(v, post_processor) + for k, v in obj.__dict__.items() + if not callable(v) and not k.startswith('_')} + return post_processor(obj, result) if post_processor else result + return obj + + def random_string(length=16, force_lower=False, digits_only=False): from string import ascii_letters, digits, ascii_lowercase from random import choice diff --git a/src/azure-cli/azure/cli/command_modules/keyvault/_transformers.py b/src/azure-cli/azure/cli/command_modules/keyvault/_transformers.py index ecc55ed67df..98647ab3d03 100644 --- a/src/azure-cli/azure/cli/command_modules/keyvault/_transformers.py +++ b/src/azure-cli/azure/cli/command_modules/keyvault/_transformers.py @@ -474,13 +474,3 @@ def transform_certificate_issuer_admin_list(result, **command_args): } for contact in admin_contacts] return ret return result - - -def transform_security_domain_output(result, **command_args): - if not result or isinstance(result, dict): - return result - ret = { - 'status': getattr(result, 'status', None), - 'statusDetails': getattr(result, 'status_details', None) - } - return ret diff --git a/src/azure-cli/azure/cli/command_modules/keyvault/commands.py b/src/azure-cli/azure/cli/command_modules/keyvault/commands.py index 90391133146..ce1747746cf 100644 --- a/src/azure-cli/azure/cli/command_modules/keyvault/commands.py +++ b/src/azure-cli/azure/cli/command_modules/keyvault/commands.py @@ -12,7 +12,7 @@ get_client, get_client_factory, Clients, is_azure_stack_profile) from azure.cli.command_modules.keyvault._transformers import ( - filter_out_managed_resources, transform_security_domain_output, + filter_out_managed_resources, multi_transformers, transform_key_decryption_output, keep_max_results, transform_key_list_output, transform_key_output, transform_key_encryption_output, transform_key_random_output, transform_secret_list, transform_deleted_secret_list, transform_secret_set, @@ -145,11 +145,9 @@ def load_command_table(self, _): with self.command_group('keyvault security-domain', data_security_domain_entity.command_type) as g: g.keyvault_custom('init-recovery', 'security_domain_init_recovery') g.keyvault_custom('restore-blob', 'security_domain_restore_blob') - g.keyvault_custom('upload', 'security_domain_upload', supports_no_wait=True, - transform=transform_security_domain_output) - g.keyvault_custom('download', 'security_domain_download', supports_no_wait=True, - transform=transform_security_domain_output) - g.keyvault_custom('wait', '_wait_security_domain_operation', transform=transform_security_domain_output) + g.keyvault_custom('upload', 'security_domain_upload', supports_no_wait=True) + g.keyvault_custom('download', 'security_domain_download', supports_no_wait=True) + g.keyvault_custom('wait', '_wait_security_domain_operation') with self.command_group('keyvault key', data_key_entity.command_type) as g: g.keyvault_custom('create', 'create_key', transform=transform_key_output, validator=validate_key_create)