microsoft/kiota

Take exception message from OpenAPI document

Open

#4,349 opened on Mar 16, 2024

 (21 comments) (0 reactions) (0 assignees)C# (326 forks)auto 404
enhancementgeneratorhelp wanted

Repository metrics

Stars
 (3,783 stars)
PR merge metrics
 (PR metrics pending)

Description

Today, when a non-successful status code is returned (which is described in the OpenAPI document), an exception is thrown with an unhelpful generic message:

Exception of type 'MyProject.Models.ErrorResponseDocument' was thrown.

In contrast, NSwag takes the message from the OpenAPI document.

For comparison, here are my test assertions from Kiota:

var exception = (await action.Should().ThrowExactlyAsync<ErrorResponseDocument>()).Which;
exception.ResponseStatusCode.Should().Be((int)HttpStatusCode.Conflict);
exception.Message.Should().Be($"Exception of type '{typeof(ErrorResponseDocument).FullName}' was thrown.");
exception.Errors.ShouldHaveCount(1);

Compared with those from NSwag:

var exception = (await action.Should().ThrowExactlyAsync<ApiException<ErrorResponseDocument>>()).Which;
exception.StatusCode.Should().Be((int)HttpStatusCode.Conflict);
exception.Message.Should().Be("HTTP 409: The request body contains conflicting information or another resource with the same ID already exists.");
exception.Result.Errors.ShouldHaveCount(1);

Here's the OpenAPI fragment the code was generated against:

"responses": {
  "400": {
    "description": "The query string is invalid or the request body is missing or malformed.",
    "content": {
      "application/vnd.api+json": {
        "schema": {
          "$ref": "#/components/schemas/errorResponseDocument"
        }
      }
    }
  },
  "409": {
    "description": "The request body contains conflicting information or another resource with the same ID already exists.",
    "content": {
      "application/vnd.api+json": {
        "schema": {
          "$ref": "#/components/schemas/errorResponseDocument"
        }
      }
    }
  },
  "422": {
    "description": "Validation of the request body failed.",
    "content": {
      "application/vnd.api+json": {
        "schema": {
          "$ref": "#/components/schemas/errorResponseDocument"
        }
      }
    }
  }
}

And, for reference, our shared ApiException class we point NSwag to, where we format the exception message by prefixing it with the HTTP status code (note: the constructor signatures are prescribed by NSwag):

// We cannot rely on generating ApiException as soon as we are generating multiple clients, see https://github.com/RicoSuter/NSwag/issues/2839#issuecomment-776647377.
// Instead, we configure NSwag to point to the exception below in the generated code.

namespace JsonApiDotNetCore.OpenApi.Client.NSwag;

public class ApiException(string message, int statusCode, string? response, IReadOnlyDictionary<string, IEnumerable<string>> headers, Exception? innerException)
    : Exception($"HTTP {statusCode}: {message}", innerException)
{
    public int StatusCode { get; } = statusCode;
    public virtual string? Response { get; } = string.IsNullOrEmpty(response) ? null : response;
    public IReadOnlyDictionary<string, IEnumerable<string>> Headers { get; } = headers;
}

public sealed class ApiException<TResult>(
    string message, int statusCode, string? response, IReadOnlyDictionary<string, IEnumerable<string>> headers, TResult result, Exception? innerException)
    : ApiException(message, statusCode, response, headers, innerException)
{
    public TResult Result { get; } = result;
    public override string Response => $"The response body is unavailable. Use the {nameof(Result)} property instead.";
}

So, to summarize, would it be possible for Kiota to use the text from the OpenAPI document as the Exception.Message value of the generated exception class? It should be a parameter (like in NSwag), because the same exception type could be used for multiple status codes (each with a different message).

Contributor guide