-
Notifications
You must be signed in to change notification settings - Fork 371
Support gRPC richer error model #1436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 9 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
21acec2
First pass at supporting richer error model in Dapr .NET SDK
jev-e 4d21f9a
Add ExtendedErrorDetailFactory, move to seperate files / new folder, …
jev-e 8ff9539
Flesh out + rename tests file, tidy more comments.
jev-e 2ffe30e
Add metadata to ErrorInfo details, add tests for each details type, m…
jev-e 0d759c0
Tidy up comments, add copyright to file.
jev-e f97a86b
add and use constants, more docs tidy up.
jev-e e475139
add initial docs pages for error handling in .net sdk.
jev-e 587039a
write daprdocs detailing usage of extendedErrorInfo, rename vars
jev-e 581aedf
Merge branch 'master' into support-extended-error-info
jev-e 6e81dae
Address PR comments
jev-e cf34edb
pr comment; adjust weight
jev-e File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
daprdocs/content/en/dotnet-sdk-docs/dotnet-error-handling/_index.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| type: docs | ||
| title: "Error Handling in the Dapr .NET SDK" | ||
| linkTitle: "Error handling" | ||
| weight: 50000 | ||
| description: Learn about error handling in the Dapr.NET SDK. | ||
| --- | ||
138 changes: 138 additions & 0 deletions
138
...s/content/en/dotnet-sdk-docs/dotnet-error-handling/dotnet-richer-error-model.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| --- | ||
| type: docs | ||
| title: "Richer Error Model in the Dapr .NET SDK" | ||
| linkTitle: "Richer error model" | ||
| weight: 59000 | ||
| description: Learn how to use the richer error model in the .NET SDK. | ||
| --- | ||
|
|
||
| The Dapr .NET SDK supports the richer error model, implemented by the Dapr runtime. This model provides a way for applications to enrich their errors with added context, | ||
| allowing consumers of the application to better understand the issue and resolve faster. You can read more about the richer error model [here](https://google.aip.dev/193), and you | ||
| can find the Dapr proto file implementing these errors [here](https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto"). | ||
|
|
||
| The Dapr .NET SDK implements all details supported by the Dapr runtime, implemented in the `Dapr.Common.Exceptions` namespace, and is accessible through | ||
| the `DaprException` extension method `TryGetExtendedErrorInfo`. Currently this detail extraction is only supported for | ||
| `RpcException`'s where the details are present. | ||
|
|
||
| ```csharp | ||
| // Example usage of ExtendedErrorInfo | ||
|
|
||
| try | ||
| { | ||
| // Perform some action with the Dapr client that throws a DaprException. | ||
| } | ||
| catch (DaprException daprEx) | ||
| { | ||
| if (daprEx.TryGetExtendedErrorInfo(out DaprExtendedErrorInfo errorInfo) | ||
| { | ||
| Console.WriteLine(errorInfo.Code); | ||
| Console.WriteLine(errorInfo.Message); | ||
|
|
||
| foreach (DaprExtendedErrorDetail detail in errorInfo.Details) | ||
| { | ||
| Console.WriteLine(detail.ErrorType); | ||
| switch (detail.ErrorType) | ||
| case ExtendedErrorType.ErrorInfo: | ||
| Console.WriteLine(detail.Reason); | ||
| Console.WriteLine(detail.Domain); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## DaprExtendedErrorInfo | ||
|
|
||
| Contains `Code` (the status code) and `Message` (the error message) associated with the error, parsed from an inner `RpcException`. | ||
| Also contains a collection of `DaprExtendedErrorDetails` parsed from the details in the exception. | ||
|
|
||
| ## DaprExtendedErrorDetail | ||
|
|
||
| All details implement the abstract `DaprExtendedErrorDetail` and have an associated `DaprExtendedErrorType`. | ||
|
|
||
| 1. [RetryInfo](#retryinfo) | ||
|
|
||
| 2. [DebugInfo](#debuginfo) | ||
|
|
||
| 3. [QuotaFailure](#quotafailure) | ||
|
|
||
| 4. [PreconditionFailure](#preconditionfailure) | ||
|
|
||
| 5. [RequestInfo](#requestinfo) | ||
|
|
||
| 6. [LocalizedMessage](#localizedmessage) | ||
|
|
||
| 7. [BadRequest](#badrequest) | ||
|
|
||
| 8. [ErrorInfo](#errorinfo) | ||
|
|
||
| 9. [Help](#help) | ||
|
|
||
| 10. [ResourceInfo](#resourceinfo) | ||
|
|
||
| 11. [Unknown](#unknown) | ||
|
|
||
| ## RetryInfo | ||
|
|
||
| Information telling the client how long to wait before they should retry. Provides a `DaprRetryDelay` with the properties | ||
| `Second` (offset in seconds) and `Nano` (offset in nanoseconds). | ||
|
|
||
| ## DebugInfo | ||
|
|
||
| Debugging information offered by the server. Contains `StackEntries` (a collection of strings containing the stack trace), and | ||
| `Detail` (further debugging information). | ||
|
|
||
| ## QuotaFailure | ||
|
|
||
| Information relating to some quota that may have been reach, such as a daily usage limit on an API. It has one property `Violations`, | ||
|
jev-e marked this conversation as resolved.
Outdated
|
||
| a collection of `DaprQuotaFailureViolation`, which each contain a `Subject` (the subject of the request) and `Description` (further information regarding the failure). | ||
|
|
||
| ## PreconditionFailure | ||
|
|
||
| Information informing the client that some required precondition was not met. Has one property `Violations`, a collection of | ||
| `DaprPreconditionFailureViolation`, which each has `Subject` (subject where the precondition failure occured e.g. "Azure"), `Type` (representation of the precondition type e.g. "TermsOfService"), and `Description` (further description e.g. "ToS must be accepted."). | ||
|
|
||
| ## RequestInfo | ||
|
|
||
| Information returned by the server that can be used by the server to identify the clients request. Contains | ||
| `RequestId` and `ServingData` properties, `RequestId` being some string (such as a UID) the server can interpret, | ||
| and `ServingData` being some arbitrary data that made up part of the request. | ||
|
|
||
| ## LocalizedMessage | ||
|
|
||
| Contains a localized message, along with the locale of the message. Contains `Locale` (the locale e.g. "en-US") and `Message` (the localized message). | ||
|
|
||
| ## BadRequest | ||
|
|
||
| Describes a bad request field. Contains collection of `DaprBadRequestDetailFieldViolation`, which each has `Field` (the offending field in request e.g. 'first_name') and | ||
| `Description` (further information detailing the reason e.g. "first_name cannot contain special characters"). | ||
|
|
||
| ## ErrorInfo | ||
|
|
||
| Details the cause of an error. Contains three properties, `Reason` (the reason for the error, which should take the form of UPPER_SNAKE_CASE e.g. DAPR_INVALID_KEY), | ||
| `Domain` (domain the error belongs to e.g. 'dapr.io'), and `Metadata`, a key value based collection of futher information. | ||
|
|
||
| ## Help | ||
|
|
||
| Provides resources for the client to perform further research into the issue. Contains a collection of `DaprHelpDetailLink`, | ||
| which provides `Url` (a url to help or documentation), and `Description` (a description of what the link provides). | ||
|
|
||
| ## ResourceInfo | ||
|
|
||
| Provides information relating to an accessed resource. Provides three properties `ResourceType` (type of the resource being access e.g. "Azure service bus"), | ||
| `ResourceName` (The name of the resource e.g. "my-configured-service-bus"), `Owner` (the owner of the resource e.g. "subscriptionowner@dapr.io"), | ||
| and `Description` (further information on the resource relating to the error e.g. "missing permissions to use this resource"). | ||
|
|
||
| ## Unknown | ||
|
|
||
| Returned when the detail type url cannot be mapped to the correct `DaprExtendedErrorDetail` implementation. | ||
| Provides one property `TypeUrl` (the type url that could not be parsed e.g. "type.googleapis.com/Google.rpc.UnrecognizedType"). | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| // ------------------------------------------------------------------------ | ||
| // Copyright 2024 The Dapr Authors | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // ------------------------------------------------------------------------ | ||
|
|
||
| using System.Diagnostics.CodeAnalysis; | ||
| using Grpc.Core; | ||
|
|
||
| namespace Dapr.Common.Exceptions | ||
| { | ||
| /// <summary> | ||
| /// Provides extension methods for <see cref="DaprException"/>. | ||
| /// </summary> | ||
| public static class DaprExceptionExtensions | ||
| { | ||
| /// <summary> | ||
| /// Attempt to retrieve <see cref="DaprExtendedErrorInfo"/> from <see cref="DaprException"/>. | ||
| /// </summary> | ||
| /// <param name="exception">A Dapr exception. <see cref="DaprException"/>.</param> | ||
| /// <param name="daprExtendedErrorInfo"><see cref="DaprExtendedErrorInfo"/> out if parsable from inner exception, null otherwise.</param> | ||
| /// <returns>True if extended info is available, false otherwise.</returns> | ||
| public static bool TryGetExtendedErrorInfo(this DaprException exception, [NotNullWhen(true)] out DaprExtendedErrorInfo? daprExtendedErrorInfo) | ||
| { | ||
| daprExtendedErrorInfo = null; | ||
| if (exception.InnerException is not RpcException rpcException) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| var metadata = rpcException.Trailers.Get(DaprExtendedErrorConstants.GrpcDetails); | ||
|
|
||
| if (metadata is null) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| var status = Google.Rpc.Status.Parser.ParseFrom(metadata.ValueBytes); | ||
|
|
||
| daprExtendedErrorInfo = new DaprExtendedErrorInfo(status.Code, status.Message) | ||
| { | ||
| Details = status.Details.Select(detail => ExtendedErrorDetailFactory.CreateErrorDetail(detail)).ToArray(), | ||
| }; | ||
|
|
||
| return true; | ||
| } | ||
| } | ||
| } |
|
jev-e marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| namespace Dapr.Common.Exceptions | ||
| { | ||
| internal class DaprExtendedErrorConstants | ||
| { | ||
| public const string ErrorDetailTypeUrl = "type.googleapis.com/"; | ||
|
|
||
| public const string GrpcDetails = "grpc-status-details-bin"; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| // ------------------------------------------------------------------------ | ||
| // Copyright 2024 The Dapr Authors | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| // ------------------------------------------------------------------------ | ||
|
|
||
| namespace Dapr.Common.Exceptions | ||
| { | ||
| /// <summary> | ||
| /// Abstract base class of the Dapr extended error detail. | ||
| /// </summary> | ||
| public abstract record DaprExtendedErrorDetail(DaprExtendedErrorType ErrorType); | ||
|
|
||
| /// <summary> | ||
| /// Detail when the type url is unrecognized. | ||
| /// </summary> | ||
| /// <param name="TypeUrl">The unrecognized type url.</param> | ||
| public sealed record DaprUnknownDetail(string TypeUrl) : DaprExtendedErrorDetail(DaprExtendedErrorType.Unknown); | ||
|
jev-e marked this conversation as resolved.
|
||
|
|
||
| /// <summary> | ||
| /// Detail proving debugging information. | ||
| /// </summary> | ||
| /// <param name="StackEntries">Stack trace entries relating to error.</param> | ||
| /// <param name="Detail">Further related debugging information.</param> | ||
| public sealed record DaprDebugInfoDetail(string[] StackEntries, string Detail) : DaprExtendedErrorDetail(DaprExtendedErrorType.DebugInfo); | ||
|
jev-e marked this conversation as resolved.
Outdated
|
||
|
|
||
| /// <summary> | ||
| /// A precondtion violation. | ||
| /// </summary> | ||
| /// <param name="Type">The type of the violation.</param> | ||
| /// <param name="Subject">The subject that the violation relates to.</param> | ||
| /// <param name="Description">A description of how the precondition may have failed.</param> | ||
| public sealed record DaprPreconditionFailureViolation(string Type, string Subject, string Description); | ||
|
|
||
| /// <summary> | ||
| /// Detail relating to a failed precondition e.g user has not completed some required check. | ||
| /// </summary> | ||
| public sealed record DaprPreconditionFailureDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.PreconditionFailure) | ||
| { | ||
| /// <summary> | ||
| /// Collection of <see cref="DaprBadRequestDetailFieldViolation"/>. | ||
| /// </summary> | ||
| public DaprPreconditionFailureViolation[] Violations { get; init; } = Array.Empty<DaprPreconditionFailureViolation>(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Provides the time offset the client should use before retrying. | ||
| /// </summary> | ||
| /// <param name="Seconds">Second offset.</param> | ||
| /// <param name="Nanos">Nano offset.</param> | ||
| public sealed record DaprRetryDelay(long Seconds, int Nanos); | ||
|
|
||
| /// <summary> | ||
| /// Detail containing retry information. Provides the minimum amount of the time the client should wait before retrying a request. | ||
| /// </summary> | ||
| public sealed record DaprRetryInfoDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.RetryInfo) | ||
| { | ||
| /// <summary> | ||
| /// A <see cref="DaprRetryDelay"/>. | ||
| /// </summary> | ||
| public DaprRetryDelay Delay = new(Seconds: 1, Nanos: default); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Further details relating to a quota violation. | ||
| /// </summary> | ||
| /// <param name="Subject">The subject where the quota violation occured e.g and ip address or remote resource.</param> | ||
| /// <param name="Description">Further information relating to the quota violation.</param> | ||
| public sealed record DaprQuotaFailureViolation(string Subject, string Description); | ||
|
|
||
| /// <summary> | ||
| /// Detail relating to a quota failure e.g reaching API limit. | ||
| /// </summary> | ||
| public sealed record DaprQuotaFailureDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.QuotaFailure) | ||
| { | ||
| /// <summary> | ||
| /// Collection of <see cref="DaprQuotaFailureViolation"/>. | ||
| /// </summary> | ||
| public DaprQuotaFailureViolation[] Violations { get; init; } = Array.Empty<DaprQuotaFailureViolation>(); | ||
|
jev-e marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Further infomation related to a bad request. | ||
| /// </summary> | ||
| /// <param name="Field">The field that generated the bad request e.g 'NewAccountName||'.</param> | ||
| /// <param name="Description">Further description of the field error e.g 'Account name cannot contain '||'' </param> | ||
| public sealed record DaprBadRequestDetailFieldViolation(string Field, string Description); | ||
|
|
||
| /// <summary> | ||
| /// Detail containing information related to a bad request from the client. | ||
| /// </summary> | ||
| public sealed record DaprBadRequestDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.BadRequest) | ||
| { | ||
| /// <summary> | ||
| /// Collection of <see cref="DaprBadRequestDetailFieldViolation"/>. | ||
| /// </summary> | ||
| public DaprBadRequestDetailFieldViolation[] FieldViolations { get; init; } = Array.Empty<DaprBadRequestDetailFieldViolation>(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Detail containing request info used by the client to provide back to the server in relation to filing bugs, providing feedback, or general debugging by the server. | ||
| /// </summary> | ||
| /// <param name="RequestId">A string understandable by the server e.g an internal UID related a trace.</param> | ||
| /// <param name="ServingData">Any data that furthers server debugging.</param> | ||
| public sealed record DaprRequestInfoDetail(string RequestId, string ServingData) : DaprExtendedErrorDetail(DaprExtendedErrorType.RequestInfo); | ||
|
|
||
| /// <summary> | ||
| /// Detail containing a message that can be localized. | ||
| /// </summary> | ||
| /// <param name="Locale">The locale e.g 'en-US'.</param> | ||
| /// <param name="Message">A message to be localized.</param> | ||
| public sealed record DaprLocalizedMessageDetail(string Locale, string Message) : DaprExtendedErrorDetail(DaprExtendedErrorType.LocalizedMessage); | ||
|
|
||
|
|
||
| /// <summary> | ||
| /// Contains a link to a help resource. | ||
| /// </summary> | ||
| /// <param name="Url">Url to help resources or documentation e.g 'https://v1-15.docs.dapr.io/developing-applications/error-codes/error-codes-reference/'.</param> | ||
| /// <param name="Description">A description of the link.</param> | ||
| public sealed record DaprHelpDetailLink(string Url, string Description); | ||
|
|
||
| /// <summary> | ||
| /// Detail containing links to further help resources. | ||
| /// </summary> | ||
| public sealed record DaprHelpDetail() : DaprExtendedErrorDetail(DaprExtendedErrorType.Help) | ||
| { | ||
| /// <summary> | ||
| /// Collection of <see cref="DaprHelpDetailLink"/>. | ||
| /// </summary> | ||
| public DaprHelpDetailLink[] Links { get; init; } = Array.Empty<DaprHelpDetailLink>(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Detail containg resource information. | ||
| /// </summary> | ||
| /// <param name="ResourceType">The type of the resource e.g 'state'.</param> | ||
| /// <param name="ResourceName">The name of the resource e.g 'statestore'.</param> | ||
| /// <param name="Owner">The owner of the resource.</param> | ||
| /// <param name="Description">Further description of the resource.</param> | ||
| public sealed record DaprResourceInfoDetail(string ResourceType, string ResourceName, string Owner, string Description) : DaprExtendedErrorDetail(DaprExtendedErrorType.ResourceInfo); | ||
|
|
||
| /// <summary> | ||
| /// Detail containing information related to a server error. | ||
| /// </summary> | ||
| /// <param name="Reason">The error reason e.g 'DAPR_STATE_ILLEGAL_KEY'.</param> | ||
| /// <param name="Domain">The error domain e.g 'dapr.io'.</param> | ||
| /// <param name="Metadata">Further key / value based metadata.</param> | ||
| public sealed record DaprErrorInfoDetail(string Reason, string Domain, IDictionary<string, string>? Metadata) : DaprExtendedErrorDetail(DaprExtendedErrorType.ErrorInfo); | ||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| namespace Dapr.Common.Exceptions | ||
| { | ||
| /// <summary> | ||
| /// Dapr implementation of the richer error model. | ||
| /// </summary> | ||
| /// <param name="Code">A status code.</param> | ||
| /// <param name="Message">A message.</param> | ||
| public sealed record DaprExtendedErrorInfo(int Code, string Message) | ||
| { | ||
| /// <summary> | ||
| /// A collection of details that provide more information on the error. | ||
| /// </summary> | ||
| public DaprExtendedErrorDetail[] Details { get; init; } = Array.Empty<DaprExtendedErrorDetail>(); | ||
|
jev-e marked this conversation as resolved.
|
||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.