diff --git a/Google.Api.Gax.Grpc.Tests/RpcExceptionExtensionsTest.cs b/Google.Api.Gax.Grpc.Tests/RpcExceptionExtensionsTest.cs index 7f59b337..d9fd772f 100644 --- a/Google.Api.Gax.Grpc.Tests/RpcExceptionExtensionsTest.cs +++ b/Google.Api.Gax.Grpc.Tests/RpcExceptionExtensionsTest.cs @@ -15,6 +15,7 @@ using Google.Protobuf.WellKnownTypes; using Google.Protobuf; using static Google.Rpc.Help.Types; +using System.Linq; namespace Google.Api.Gax.Grpc.Tests { @@ -32,31 +33,35 @@ public void NullArgument_AllMethodsThrow() Assert.Throws(() => ex.GetHelp()); Assert.Throws(() => ex.GetLocalizedMessage()); Assert.Throws(() => ex.GetStatusDetail()); + Assert.Throws(() => ex.GetAllStatusDetails()); } [Fact] - public void NoTrailers_AllMethodsReturnNull() + public void NoTrailers_AllMethodsReturnNullOrEmpty() { RpcException ex = new RpcException(s_status); AssertAllMethodsReturnNull(ex); + Assert.Empty(ex.GetAllStatusDetails()); } [Fact] - public void IrrelevantTrailer_AllMethodsReturnNull() + public void IrrelevantTrailer_AllMethodsReturnNullOrEmpty() { var metadata = new Metadata(); metadata.Add("key", "value"); RpcException ex = new RpcException(s_status, metadata); AssertAllMethodsReturnNull(ex); + Assert.Empty(ex.GetAllStatusDetails()); } [Fact] - public void InvalidProtobufStatusTrailer_AllMethodsReturnNull() + public void InvalidProtobufStatusTrailer_AllMethodsReturnNullOrEmpty() { var metadata = new Metadata(); metadata.Add(RpcExceptionExtensions.StatusDetailsTrailerName, new byte[] { 1, 2, 3, 4 }); RpcException ex = new RpcException(s_status, metadata); AssertAllMethodsReturnNull(ex); + Assert.Empty(ex.GetAllStatusDetails()); } [Fact] @@ -78,6 +83,7 @@ public void ValidTrailer_GetRpcStatus() Assert.Null(ex.GetHelp()); Assert.Null(ex.GetLocalizedMessage()); Assert.Null(ex.GetStatusDetail()); + Assert.Empty(ex.GetAllStatusDetails()); } [Fact] @@ -128,6 +134,7 @@ public void ValidTrailer_ArbitraryMessages() Assert.Equal(badRequest, ex.GetStatusDetail()); Assert.Equal(help, ex.GetStatusDetail()); Assert.Equal(localizedMessage, ex.GetStatusDetail()); + Assert.Equal(ex.GetAllStatusDetails(), new IMessage[] { debugInfo, requestInfo, badRequest, help, localizedMessage }); } [Fact] @@ -152,6 +159,7 @@ public void GetErrorInfo_Present() RpcException ex = new RpcException(s_status, metadata); Assert.Equal(errorInfo, ex.GetErrorInfo()); + Assert.Equal(ex.GetAllStatusDetails(), new IMessage[] { errorInfo }); } [Fact] @@ -168,6 +176,7 @@ public void GetStatusDetail_BadlyPackedMessage() Assert.Equal(status, ex.GetRpcStatus()); Assert.Null(ex.GetStatusDetail()); + Assert.Throws(() => ex.GetAllStatusDetails().Count()); } private void AssertAllMethodsReturnNull(RpcException ex) diff --git a/Google.Api.Gax.Grpc/RpcExceptionExtensions.cs b/Google.Api.Gax.Grpc/RpcExceptionExtensions.cs index 157d02ff..060c61ea 100644 --- a/Google.Api.Gax.Grpc/RpcExceptionExtensions.cs +++ b/Google.Api.Gax.Grpc/RpcExceptionExtensions.cs @@ -8,6 +8,7 @@ using Google.Protobuf; using Google.Rpc; using Grpc.Core; +using System.Collections.Generic; using System.Linq; using Status = Google.Rpc.Status; @@ -73,7 +74,7 @@ public static Rpc.Status GetRpcStatus(this RpcException ex) => /// /// The message type to decode from within the error details. /// The RPC exception to retrieve details from. Must not be null. - /// + /// The requested status detail, or null if the exception does not contain one. public static T GetStatusDetail(this RpcException ex) where T : class, IMessage, new() { var status = GetRpcStatus(ex); @@ -88,6 +89,19 @@ public static Rpc.Status GetRpcStatus(this RpcException ex) => } } + /// + /// Returns all standard status details associated with an RPC exception. + /// + /// + /// Status details are assumed to be in the standard error type registry. + /// If any detail messages are invalid (known, but containing invalid data) then iterating over the returned collection + /// will throw when the iterator reaches the invalid message. + /// + /// The RPC exception to retrieve details from. Must not be null. + /// All standard status details within the exception. + public static IEnumerable GetAllStatusDetails(this RpcException ex) => + GetRpcStatus(ex)?.UnpackDetailMessages() ?? Enumerable.Empty(); + private static Metadata.Entry GetTrailer(RpcException ex, string key) { GaxPreconditions.CheckNotNull(ex, nameof(ex));