|
27 | 27 | import com.azure.core.http.HttpMethod;
|
28 | 28 | import com.azure.core.http.rest.Page;
|
29 | 29 | import com.azure.core.http.rest.RequestOptions;
|
| 30 | +import com.azure.core.http.rest.Response; |
| 31 | +import com.azure.core.http.rest.ResponseBase; |
30 | 32 | import com.azure.core.http.rest.RestProxy;
|
31 | 33 | import com.azure.core.http.rest.StreamResponse;
|
32 | 34 | import com.azure.core.implementation.TypeUtil;
|
33 | 35 | import com.azure.core.implementation.UnixTime;
|
34 | 36 | import com.azure.core.implementation.http.UnexpectedExceptionInformation;
|
35 | 37 | import com.azure.core.implementation.serializer.HttpResponseDecodeData;
|
36 | 38 | import com.azure.core.util.Base64Url;
|
| 39 | +import com.azure.core.util.BinaryData; |
37 | 40 | import com.azure.core.util.Context;
|
38 | 41 | import com.azure.core.util.CoreUtils;
|
39 | 42 | import com.azure.core.util.DateTimeRfc1123;
|
|
44 | 47 | import reactor.core.publisher.Flux;
|
45 | 48 | import reactor.core.publisher.Mono;
|
46 | 49 |
|
| 50 | +import java.io.InputStream; |
47 | 51 | import java.lang.annotation.Annotation;
|
48 | 52 | import java.lang.reflect.Method;
|
49 | 53 | import java.lang.reflect.Type;
|
| 54 | +import java.nio.ByteBuffer; |
50 | 55 | import java.util.ArrayList;
|
51 | 56 | import java.util.Arrays;
|
52 | 57 | import java.util.BitSet;
|
53 | 58 | import java.util.HashMap;
|
54 | 59 | import java.util.List;
|
55 | 60 | import java.util.Map;
|
56 | 61 | import java.util.Objects;
|
| 62 | +import java.util.function.Predicate; |
57 | 63 | import java.util.regex.Pattern;
|
58 | 64 | import java.util.stream.Collectors;
|
59 | 65 |
|
| 66 | +import static com.azure.core.implementation.TypeUtil.getRawClass; |
| 67 | +import static com.azure.core.implementation.TypeUtil.typeImplementsInterface; |
| 68 | + |
60 | 69 | /**
|
61 | 70 | * This class contains the metadata of a {@link Method} contained in a Swagger interface used to make REST API calls in
|
62 | 71 | * {@link RestProxy}.
|
@@ -93,6 +102,8 @@ public class SwaggerMethodParser implements HttpResponseDecodeData {
|
93 | 102 | private final int requestOptionsPosition;
|
94 | 103 | private final boolean isReactive;
|
95 | 104 | private final boolean isStreamResponse;
|
| 105 | + private final boolean returnTypeDecodeable; |
| 106 | + private final boolean responseEagerlyRead; |
96 | 107 |
|
97 | 108 | private Map<Integer, UnexpectedExceptionInformation> exceptionMapping;
|
98 | 109 | private UnexpectedExceptionInformation defaultException;
|
@@ -265,6 +276,10 @@ public SwaggerMethodParser(Method swaggerMethod, String rawHost, SerializerAdapt
|
265 | 276 | }
|
266 | 277 | this.contextPosition = contextPosition;
|
267 | 278 | this.requestOptionsPosition = requestOptionsPosition;
|
| 279 | + |
| 280 | + Type unwrappedReturnType = unwrapReturnType(returnType); |
| 281 | + this.returnTypeDecodeable = isReturnTypeDecodeable(unwrappedReturnType); |
| 282 | + this.responseEagerlyRead = isResponseEagerlyRead(unwrappedReturnType); |
268 | 283 | }
|
269 | 284 |
|
270 | 285 | /**
|
@@ -649,4 +664,90 @@ boolean isStreamResponseType(Type type) {
|
649 | 664 | public boolean isStreamResponse() {
|
650 | 665 | return isStreamResponse;
|
651 | 666 | }
|
| 667 | + |
| 668 | + @Override |
| 669 | + public boolean isReturnTypeDecodeable() { |
| 670 | + return returnTypeDecodeable; |
| 671 | + } |
| 672 | + |
| 673 | + @Override |
| 674 | + public boolean isResponseEagerlyRead() { |
| 675 | + return responseEagerlyRead; |
| 676 | + } |
| 677 | + |
| 678 | + static boolean isReturnTypeDecodeable(Type unwrappedReturnType) { |
| 679 | + if (unwrappedReturnType == null) { |
| 680 | + return false; |
| 681 | + } |
| 682 | + |
| 683 | + return !TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, BinaryData.class) |
| 684 | + && !TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, byte[].class) |
| 685 | + && !TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, ByteBuffer.class) |
| 686 | + && !TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, InputStream.class) |
| 687 | + && !TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, Void.TYPE) |
| 688 | + && !TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, Void.class); |
| 689 | + } |
| 690 | + |
| 691 | + static boolean isResponseEagerlyRead(Type unwrappedReturnType) { |
| 692 | + if (unwrappedReturnType == null) { |
| 693 | + return false; |
| 694 | + } |
| 695 | + |
| 696 | + return isReturnTypeDecodeable(unwrappedReturnType) |
| 697 | + || TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, Void.TYPE) |
| 698 | + || TypeUtil.isTypeOrSubTypeOf(unwrappedReturnType, Void.class); |
| 699 | + } |
| 700 | + |
| 701 | + static Type unwrapReturnType(Type returnType) { |
| 702 | + if (returnType == null) { |
| 703 | + return null; |
| 704 | + } |
| 705 | + |
| 706 | + // First check if the return type is assignable, is a sub-type, to ResponseBase. |
| 707 | + // If it is begin walking up the super type hierarchy until ResponseBase is the raw type. |
| 708 | + // Then unwrap the second generic type (body type). |
| 709 | + if (TypeUtil.isTypeOrSubTypeOf(returnType, ResponseBase.class)) { |
| 710 | + returnType = walkSuperTypesUntil(returnType, type -> getRawClass(type) == ResponseBase.class); |
| 711 | + |
| 712 | + return unwrapReturnType(TypeUtil.getTypeArguments(returnType)[1]); |
| 713 | + } |
| 714 | + |
| 715 | + // Then, like ResponseBase, check if the return type is assignable to Response. |
| 716 | + // If it is begin walking up the super type hierarchy until the raw type implements Response. |
| 717 | + // Then unwrap its only generic type. |
| 718 | + if (TypeUtil.isTypeOrSubTypeOf(returnType, Response.class)) { |
| 719 | + // Handling for Response is slightly different as it is an interface unlike ResponseBase which is a class. |
| 720 | + // The super class hierarchy needs be walked until the super class itself implements Response. |
| 721 | + returnType = walkSuperTypesUntil(returnType, type -> typeImplementsInterface(type, Response.class)); |
| 722 | + |
| 723 | + return unwrapReturnType(TypeUtil.getTypeArgument(returnType)); |
| 724 | + } |
| 725 | + |
| 726 | + // Then check if the return type is a Mono or Flux and unwrap its only generic type. |
| 727 | + if (TypeUtil.isTypeOrSubTypeOf(returnType, Mono.class)) { |
| 728 | + returnType = walkSuperTypesUntil(returnType, type -> getRawClass(type) == Mono.class); |
| 729 | + |
| 730 | + return unwrapReturnType(TypeUtil.getTypeArgument(returnType)); |
| 731 | + } |
| 732 | + |
| 733 | + if (TypeUtil.isTypeOrSubTypeOf(returnType, Flux.class)) { |
| 734 | + returnType = walkSuperTypesUntil(returnType, type -> getRawClass(type) == Flux.class); |
| 735 | + |
| 736 | + return unwrapReturnType(TypeUtil.getTypeArgument(returnType)); |
| 737 | + } |
| 738 | + |
| 739 | + // Finally, there is no more unwrapping to perform and return the type as-is. |
| 740 | + return returnType; |
| 741 | + } |
| 742 | + |
| 743 | + /* |
| 744 | + * Helper method that walks up the super types until the type is an instance of the Class. |
| 745 | + */ |
| 746 | + private static Type walkSuperTypesUntil(Type type, Predicate<Type> untilChecker) { |
| 747 | + while (!untilChecker.test(type)) { |
| 748 | + type = TypeUtil.getSuperType(type); |
| 749 | + } |
| 750 | + |
| 751 | + return type; |
| 752 | + } |
652 | 753 | }
|
0 commit comments