Skip to content

Commit

Permalink
mgmt, mitigate the issue of ErrorResponse used in model (#2242)
Browse files Browse the repository at this point in the history
  • Loading branch information
weidongxu-microsoft authored Jul 14, 2023
1 parent e286f4a commit 8a49c96
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 3 deletions.
6 changes: 5 additions & 1 deletion fluent-tests/prepare-tests.bat
Original file line number Diff line number Diff line change
Expand Up @@ -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%

Expand Down Expand Up @@ -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%
Expand Down
19 changes: 19 additions & 0 deletions fluent-tests/src/test/java/com/azure/mgmttest/RuntimeTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -33,6 +35,8 @@ public class ErrorTypeNormalization {

private static final Logger LOGGER = new PluginLogger(FluentNamer.getPluginInstance(), ErrorTypeNormalization.class);

private final Set<ObjectSchema> clonedObjects = new HashSet<>();

public CodeModel process(CodeModel codeModel) {
codeModel.getOperationGroups().stream()
.flatMap(og -> og.getOperations().stream())
Expand All @@ -42,6 +46,8 @@ public CodeModel process(CodeModel codeModel) {
.distinct()
.forEach(s -> process((ObjectSchema) s));

codeModel.getSchemas().getObjects().addAll(clonedObjects);

return codeModel;
}

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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()) {
Expand Down

0 comments on commit 8a49c96

Please sign in to comment.