diff --git a/src/apic-extension/azext_apic_extension/aaz/latest/apic/metadata/_export.py b/src/apic-extension/azext_apic_extension/aaz/latest/apic/metadata/_export.py index 3af9fdda2c3..c28b3b73baa 100644 --- a/src/apic-extension/azext_apic_extension/aaz/latest/apic/metadata/_export.py +++ b/src/apic-extension/azext_apic_extension/aaz/latest/apic/metadata/_export.py @@ -25,6 +25,9 @@ class Export(AAZCommand): :example: Export Metadata Schema assigned to environment az apic metadata export -g api-center-test -n contosoeuap --assignments environment --file-name filename.json + + :example: Export Custom Metadata Schema Only + az apic metadata export -g api-center-test -n contosoeuap --assignments api --file-name filename.json --custom-metadata-only """ _aaz_info = { diff --git a/src/apic-extension/azext_apic_extension/custom.py b/src/apic-extension/azext_apic_extension/custom.py index 584ab597b6b..bbc65d3bd93 100644 --- a/src/apic-extension/azext_apic_extension/custom.py +++ b/src/apic-extension/azext_apic_extension/custom.py @@ -18,7 +18,7 @@ from knack.log import get_logger from knack.util import CLIError import chardet -from azure.cli.core.aaz._arg import AAZStrArg +from azure.cli.core.aaz._arg import AAZStrArg, AAZBoolArg from .command_patches import ImportAPIDefinitionExtension from .command_patches import ExportAPIDefinitionExtension from .command_patches import ExportMetadataExtension @@ -105,6 +105,12 @@ def _build_arguments_schema(cls, *args, **kwargs): required=True, registered=True ) + args_schema.custom_metadata_only = AAZBoolArg( + options=["--custom-metadata-only"], + help='Export only custom metadata.', + required=False, + blank=True + ) return args_schema def _output(self, *args, **kwargs): @@ -118,9 +124,14 @@ def _output(self, *args, **kwargs): if response_format == 'link': getReponse = requests.get(exportedResults, timeout=10) if getReponse.status_code == 200: - exportedResults = getReponse.content.decode() + content = json.loads(getReponse.content.decode()) + # Check if custom metadata only + exportedResults = content.get('properties').get('customProperties', {}) if arguments.custom_metadata_only else content else: logger.error('Error while fetching the results from the link. Status code: %s', getReponse.status_code) + else: + # Check if custom metadata only + exportedResults = json.loads(exportedResults).get('properties').get('customProperties', {}) if arguments.custom_metadata_only else exportedResults if arguments.source_profile: try: diff --git a/src/apic-extension/azext_apic_extension/tests/latest/recordings/test_metadata_export_with_custom_metadata_only.yaml b/src/apic-extension/azext_apic_extension/tests/latest/recordings/test_metadata_export_with_custom_metadata_only.yaml new file mode 100644 index 00000000000..07d4ca65615 --- /dev/null +++ b/src/apic-extension/azext_apic_extension/tests/latest/recordings/test_metadata_export_with_custom_metadata_only.yaml @@ -0,0 +1,71 @@ +interactions: +- request: + body: '{"assignedTo": "api"}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - apic metadata export + Connection: + - keep-alive + Content-Length: + - '21' + Content-Type: + - application/json + ParameterSetName: + - -g -n --assignments --file-name --custom-metadata-only + User-Agent: + - AZURECLI/2.63.0 azsdk-python-core/1.28.0 Python/3.11.9 (Windows-10-10.0.22631-SP0) + method: POST + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clirg000001/providers/Microsoft.ApiCenter/services/clitest000002/exportMetadataSchema?api-version=2024-03-01 + response: + body: + string: '{"format":"json-schema","value":"{\"type\":\"object\",\"properties\":{\"Name\":{\"type\":\"string\"},\"WorkspaceName\":{\"type\":\"string\"},\"Title\":{\"type\":\"string\"},\"Summary\":{\"type\":\"string\"},\"Description\":{\"type\":\"string\"},\"Kind\":{\"type\":\"string\"},\"LifecycleStage\":{\"type\":\"string\",\"enum\":[\"design\",\"development\",\"testing\",\"preview\",\"production\",\"deprecated\",\"retired\"]},\"TermsOfService\":{\"type\":\"object\",\"properties\":{\"url\":{\"description\":\"URL + pointing to the terms of service.\",\"type\":\"string\",\"maxLength\":200,\"format\":\"uri\"}}},\"License\":{\"type\":\"object\",\"properties\":{\"name\":{\"description\":\"Name + of the license.\",\"type\":\"string\",\"maxLength\":50},\"url\":{\"description\":\"URL + pointing to the license details. The URL field is mutually exclusive of the + identifier field.\",\"type\":\"string\",\"maxLength\":200,\"format\":\"uri\"},\"identifier\":{\"description\":\"SPDX + license information for the API. The identifier field is mutually exclusive + of the URL field.\",\"type\":\"string\",\"maxLength\":200,\"format\":\"uri\"}}},\"ExternalDocumentation\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"title\":{\"description\":\"Title + of the documentation.\",\"type\":\"string\",\"maxLength\":50},\"description\":{\"description\":\"Description + of the documentation.\",\"type\":\"string\",\"maxLength\":1000},\"url\":{\"description\":\"URL + pointing to the documentation.\",\"type\":\"string\",\"maxLength\":200,\"format\":\"uri\"}}}},\"Contacts\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"name\":{\"description\":\"Name + of the contact.\",\"type\":\"string\",\"maxLength\":100},\"url\":{\"description\":\"URL + for the contact.\",\"type\":\"string\",\"maxLength\":200,\"format\":\"uri\"},\"email\":{\"description\":\"Email + address for the contact.\",\"type\":\"string\",\"maxLength\":100,\"format\":\"email\"}}}},\"CustomPropertiesData\":{},\"ApiSourceName\":{\"type\":\"string\"},\"CatalogNameTypeSafe\":{\"type\":\"object\",\"additionalProperties\":false,\"properties\":{\"Name\":{\"type\":\"string\"}}},\"CatalogName\":{\"type\":\"string\"},\"SubscriptionId\":{\"type\":\"string\"},\"SubscriptionIdTypeSafe\":{\"type\":\"string\"},\"ResourceGroupName\":{\"type\":\"string\"},\"ResourceGroupNameTypeSafe\":{\"type\":\"object\",\"additionalProperties\":false,\"properties\":{\"Name\":{\"type\":\"string\"}}},\"ETag\":{\"type\":\"string\"},\"Created\":{\"type\":\"string\",\"format\":\"date-time\"},\"Updated\":{\"type\":\"string\",\"format\":\"date-time\"},\"CreatedBy\":{\"type\":\"string\"},\"UpdatedBy\":{\"type\":\"string\"},\"customProperties\":{\"type\":\"object\",\"properties\":{\"clitest000003\":{\"type\":\"boolean\",\"title\":\"Public + Facing\"}},\"unevaluatedProperties\":false,\"required\":[\"clitest000003\"]}}}"}' + headers: + api-supported-versions: + - 2023-07-01-preview, 2024-03-01, 2024-03-15-preview + cache-control: + - no-cache + content-length: + - '2865' + content-type: + - application/json; charset=utf-8 + date: + - Thu, 26 Sep 2024 02:30:22 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-cache: + - CONFIG_NOCACHE + x-content-type-options: + - nosniff + x-ms-ratelimit-remaining-subscription-writes: + - '1199' + x-msedge-ref: + - 'Ref A: BB450A2E8FCB40B6A0514E0031D51D56 Ref B: MAA201060516029 Ref C: 2024-09-26T02:30:22Z' + x-powered-by: + - ASP.NET + status: + code: 200 + message: OK +version: 1 diff --git a/src/apic-extension/azext_apic_extension/tests/latest/test_metadata_commands.py b/src/apic-extension/azext_apic_extension/tests/latest/test_metadata_commands.py index d482af7754a..89c5c05d106 100644 --- a/src/apic-extension/azext_apic_extension/tests/latest/test_metadata_commands.py +++ b/src/apic-extension/azext_apic_extension/tests/latest/test_metadata_commands.py @@ -129,6 +129,27 @@ def test_metadata_export(self): finally: os.remove(self.kwargs['filename']) + @ResourceGroupPreparer(name_prefix="clirg", location=TEST_REGION, random_name_length=32) + @ApicServicePreparer() + @ApicMetadataPreparer() + def test_metadata_export_with_custom_metadata_only(self): + self.kwargs.update({ + 'filename': 'metadata_export.json' + }) + self.cmd('az apic metadata export -g {rg} -n {s} --assignments api --file-name {filename} --custom-metadata-only') + + try: + with open(self.kwargs['filename'], 'r') as f: + data = json.load(f) + + assert 'properties' in data, "properties not found in customProperties" + assert data['type'] == 'object', "Type of the customProperties does not match the expected type" + assert len(data['properties']) == 1, "The number of properties in customProperties does not match the expected number" + assert data['unevaluatedProperties'] == False, "unevaluatedProperties does not match the expected value" + assert len(data['required']) == 1, "The number of required does not match the expected number" + finally: + os.remove(self.kwargs['filename']) + @ResourceGroupPreparer(name_prefix="clirg", location=TEST_REGION, random_name_length=32) @ApicServicePreparer() def test_examples_create_metadata_1(self):