From 8a49c96697684d3f51ef35c64b6414b5e472ea5f Mon Sep 17 00:00:00 2001 From: Weidong Xu Date: Fri, 14 Jul 2023 15:39:42 +0800 Subject: [PATCH] mgmt, mitigate the issue of ErrorResponse used in model (#2242) --- fluent-tests/prepare-tests.bat | 6 ++- .../java/com/azure/mgmttest/RuntimeTests.java | 19 ++++++++ .../transformer/ErrorTypeNormalization.java | 45 ++++++++++++++++++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/fluent-tests/prepare-tests.bat b/fluent-tests/prepare-tests.bat index 5852bf2e17..76feb66b98 100644 --- a/fluent-tests/prepare-tests.bat +++ b/fluent-tests/prepare-tests.bat @@ -63,7 +63,7 @@ REM flatten the empty model which has non-empty parent model CALL autorest --version=%AUTOREST_CORE_VERSION% %FLUENT_ARGUMENTS% --input-file=https://github.com/Azure/azure-rest-api-specs/blob/main/specification/monitor/resource-manager/Microsoft.Insights/preview/2021-09-01-preview/dataCollectionRules_API.json --namespace=com.azure.mgmttest.monitor if %errorlevel% neq 0 exit /b %errorlevel% -REM swagger customed Resource and ProxyResource +REM swagger customized Resource and ProxyResource CALL autorest --version=%AUTOREST_CORE_VERSION% %FLUENT_ARGUMENTS% --input-file=https://github.com/Azure/azure-rest-api-specs/blob/main/specification/trafficmanager/resource-manager/Microsoft.Network/stable/2018-08-01/trafficmanager.json --namespace=com.azure.mgmttest.trafficmanager if %errorlevel% neq 0 exit /b %errorlevel% @@ -92,6 +92,10 @@ REM multiple inheritance with conflict field CALL autorest --version=%AUTOREST_CORE_VERSION% %FLUENTLITE_ARGUMENTS% --regenerate-pom=false https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/botservice/resource-manager/readme.md --tag=package-preview-2021-05 --java.namespace=com.azure.mgmtlitetest.botservice if %errorlevel% neq 0 exit /b %errorlevel% +REM model inherit ErrorResponse +CALL autorest --version=%AUTOREST_CORE_VERSION% %FLUENTLITE_ARGUMENTS% --regenerate-pom=false --input-file=https://raw.githubusercontent.com/Azure/azure-rest-api-specs/196886564583ff59186bd0ef44d923120aaf3f78/specification/managednetworkfabric/resource-manager/Microsoft.ManagedNetworkFabric/stable/2023-06-15/NetworkFabrics.json --java.namespace=com.azure.mgmtlitetest.managednetworkfabric +if %errorlevel% neq 0 exit /b %errorlevel% + REM schema clean-up CALL autorest --version=%AUTOREST_CORE_VERSION% %FLUENTLITE_ARGUMENTS% --regenerate-pom=false --input-file=./swagger/schema-cleanup.json --java.namespace=com.azure.mgmtlitetest.schemacleanup if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/fluent-tests/src/test/java/com/azure/mgmttest/RuntimeTests.java b/fluent-tests/src/test/java/com/azure/mgmttest/RuntimeTests.java index 54887d5fc6..cb6106ee68 100644 --- a/fluent-tests/src/test/java/com/azure/mgmttest/RuntimeTests.java +++ b/fluent-tests/src/test/java/com/azure/mgmttest/RuntimeTests.java @@ -28,6 +28,8 @@ import com.azure.mgmtlitetest.advisor.models.ResourceRecommendationBase; import com.azure.mgmtlitetest.advisor.models.SuppressionContract; import com.azure.mgmtlitetest.botservice.models.Site; +import com.azure.mgmtlitetest.managednetworkfabric.fluent.models.CommonPostActionResponseForDeviceUpdateInner; +import com.azure.mgmtlitetest.managednetworkfabric.models.ConfigurationState; import com.azure.mgmtlitetest.mediaservices.MediaServicesManager; import com.azure.mgmtlitetest.mediaservices.models.MediaService; import com.azure.mgmtlitetest.mediaservices.models.StorageAccountType; @@ -182,6 +184,23 @@ public void testSchemaCleanUp() { Assertions.assertThrows(ClassNotFoundException.class, () -> Class.forName("com.azure.mgmtlitetest.schemacleanup.models.CloudErrorBody")); } + @Test + public void testModelInheritErrorResponse() throws IOException { + SerializerAdapter serializerAdapter = SerializerFactory.createDefaultManagementSerializerAdapter(); + String json = "{\n" + + " \"configurationState\": \"Succeeded\",\n" + + " \"error\": {\n" + + " \"code\": \"CODE\",\n" + + " \"message\": \"MESSAGE\"\n" + + " }\n" + + "}"; + + CommonPostActionResponseForDeviceUpdateInner model = serializerAdapter.deserialize(json, CommonPostActionResponseForDeviceUpdateInner.class, SerializerEncoding.JSON); + Assertions.assertEquals(ConfigurationState.SUCCEEDED, model.configurationState()); + Assertions.assertEquals("CODE", model.error().getCode()); + Assertions.assertEquals("MESSAGE", model.error().getMessage()); + } + @Test @Disabled("live test") public void testStorage() { diff --git a/fluentnamer/src/main/java/com/azure/autorest/fluent/transformer/ErrorTypeNormalization.java b/fluentnamer/src/main/java/com/azure/autorest/fluent/transformer/ErrorTypeNormalization.java index 54a9d05107..f8f1f53a65 100644 --- a/fluentnamer/src/main/java/com/azure/autorest/fluent/transformer/ErrorTypeNormalization.java +++ b/fluentnamer/src/main/java/com/azure/autorest/fluent/transformer/ErrorTypeNormalization.java @@ -12,11 +12,13 @@ import com.azure.autorest.extension.base.model.codemodel.Relations; import com.azure.autorest.extension.base.model.codemodel.Response; import com.azure.autorest.extension.base.model.codemodel.Schema; +import com.azure.autorest.extension.base.model.codemodel.SchemaContext; import com.azure.autorest.extension.base.model.codemodel.Value; import com.azure.autorest.extension.base.plugin.PluginLogger; import com.azure.autorest.fluent.model.FluentType; import com.azure.autorest.fluent.util.Utils; import com.azure.autorest.fluentnamer.FluentNamer; +import com.azure.core.util.CoreUtils; import org.slf4j.Logger; import java.util.ArrayList; @@ -33,6 +35,8 @@ public class ErrorTypeNormalization { private static final Logger LOGGER = new PluginLogger(FluentNamer.getPluginInstance(), ErrorTypeNormalization.class); + private final Set clonedObjects = new HashSet<>(); + public CodeModel process(CodeModel codeModel) { codeModel.getOperationGroups().stream() .flatMap(og -> og.getOperations().stream()) @@ -42,6 +46,8 @@ public CodeModel process(CodeModel codeModel) { .distinct() .forEach(s -> process((ObjectSchema) s)); + codeModel.getSchemas().getObjects().addAll(clonedObjects); + return codeModel; } @@ -83,20 +89,45 @@ private void process(ObjectSchema error) { private void normalizeErrorType(ObjectSchema error, ObjectSchema errorSchema) { switch (getErrorType(errorSchema)) { case MANAGEMENT_ERROR: - LOGGER.info("Rename error from '{}' to 'ManagementError'", Utils.getJavaName(error)); + final boolean updateChildrenParent = errorSchema != error && existNoneExceptionChildren(error); + final ObjectSchema clonedError = new ObjectSchema(); + if (updateChildrenParent) { + // clone ErrorResponse model, as we need it for input/output + // this only solve the case when its subclass is referenced in operation; it won't solve the case that ErrorResponse itself is used in operation. + LOGGER.info("Clone error '{}'", Utils.getJavaName(error)); + Utils.shallowCopy(error, clonedError, ObjectSchema.class, LOGGER); + clonedError.setLanguage(new Languages()); + Utils.shallowCopy(error.getLanguage(), clonedError.getLanguage(), Languages.class, LOGGER); + clonedError.getLanguage().setJava(new Language()); + Utils.shallowCopy(error.getLanguage().getJava(), clonedError.getLanguage().getJava(), Language.class, LOGGER); + clonedObjects.add(clonedError); + } + LOGGER.info("Rename error from '{}' to 'ManagementError'", Utils.getJavaName(error)); error.getLanguage().getJava().setName(FluentType.ManagementError.getName()); if (errorSchema != error) { + LOGGER.info("Rename error from '{}' to 'ManagementError'", Utils.getJavaName(errorSchema)); errorSchema.getLanguage().getJava().setName(FluentType.ManagementError.getName()); } - if (errorSchema != error) { + if (errorSchema != error && !updateChildrenParent) { error.setChildren(errorSchema.getChildren()); } normalizeSubclass(errorSchema); + if (updateChildrenParent) { + // update its children to point to the cloned ErrorResponse model + clonedError.getChildren().getAll().stream().filter(ErrorTypeNormalization::usedMoreThanException).forEach(o -> { + if (o instanceof ObjectSchema) { + ObjectSchema schema = (ObjectSchema) o; + schema.getParents().getImmediate().replaceAll(p -> p == error ? clonedError : p); + schema.getParents().getAll().replaceAll(p -> p == error ? clonedError : p); + } + }); + } + break; case SUBCLASS_MANAGEMENT_ERROR: @@ -130,6 +161,16 @@ private void normalizeErrorType(ObjectSchema error, ObjectSchema errorSchema) { } } + private static boolean existNoneExceptionChildren(ObjectSchema error) { + return error.getChildren() != null && error.getChildren().getAll().stream() + .anyMatch(ErrorTypeNormalization::usedMoreThanException); + } + + private static boolean usedMoreThanException(Schema schema) { + return !CoreUtils.isNullOrEmpty(schema.getUsage()) + && (schema.getUsage().contains(SchemaContext.INPUT) || schema.getUsage().contains(SchemaContext.OUTPUT)); + } + private void normalizeSubclass(ObjectSchema errorSchema) { if (errorSchema.getChildren() != null && errorSchema.getChildren().getImmediate() != null) { for (Schema schema : errorSchema.getChildren().getImmediate()) {