Skip to content
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

C# .NET HttpValidationProblem not working (or at least extremely unergonomic) #4862

Closed
linde12 opened this issue Jun 19, 2024 · 6 comments
Closed
Labels
Csharp Pull requests that update .net code duplicate This issue or pull request already exists enhancement New feature or request type:enhancement Enhancement request targeting an existing experience
Milestone

Comments

@linde12
Copy link

linde12 commented Jun 19, 2024

What are you generating using Kiota, clients or plugins?

API Client/SDK

In what context or format are you using Kiota?

Linux executable

Client library/SDK language

Csharp

Describe the bug

I'm generating towards an endpoint that returns a .NET HttpProblemResult or anything that produces a problem details response, but when i instead switch to the built-in ValidationProblem (e.g. return TypedResults.ValidationProblem(errors); from my endpoint) and annotate the endpoint with .ProducesValidationProblem(); Kiota produces an extremely unusable client.

The generated OpenAPI Schema (from the .NET application) is fine (see screenshot), and in the generated .NET client there is a ex.Errors which in turn contains an AdditionalData dictionary. This dictionary is not what you would expect though (Dictionary<string, string[]> of errors) but a dict containing one key ("errors") which is an Microsoft.Kiota.Abstractions.Serialization.UntypedArray

As it stands, by following the problem details standard and using the ValidationProblem provided by .NET, Kiota generates a pretty unusable or at least very unergonomic (i have not looked into UntypedArray) exception for non-200 OK situations.

Image

Expected behavior

I expect Kiota to follow the API Schema (see screenshot in description above) and generate a MyApi.Models.HttpValidationProblemDetails exception which contains a Errors (alternatively Errors.AdditionalData if thats some convention Kiota follows?) that is of type Dictionary<string, string[]>)

The OpenAPI Schema clearly states that errors is an object where each additionalProperty (every property in this case) is an array of strings.

How to reproduce

Create e.g. a .NET Minimal API endpoint that produces the schema above and make the endpoint throw return a TypedResults.ValidationProblem and "annotate" the route with the ProducesValidationProblem method. Generate a client and look at the HttpValidationProblemDetails.cs

Open API description file

Cannot provide since it is confidential, but would be willing to produce a more complete minimally reproducible example if this is written off as expected behavior.

Kiota Version

1.14

Latest Kiota version known to work for scenario above?(Not required)

No response

Known Workarounds

No response

Configuration

No response

Debug output

No response

Other information

No response

@linde12 linde12 added status:waiting-for-triage An issue that is yet to be reviewed or assigned type:bug A broken experience labels Jun 19, 2024
@github-project-automation github-project-automation bot moved this to Needs Triage 🔍 in Kiota Jun 19, 2024
@baywet
Copy link
Member

baywet commented Jun 19, 2024

Hi @linde12
Thanks for using kiota and for reaching out.
Can you please share more of your OAS description (in text format) to include the path/request/responses so we have more context here?
Can you also share the C# of what's being generated today, and what you'd expect instead?
Thanks!

@baywet baywet added question status:waiting-for-author-feedback Issue that we've responded but needs author feedback to close type:question An issue that's a question and removed type:bug A broken experience status:waiting-for-triage An issue that is yet to be reviewed or assigned labels Jun 19, 2024
@baywet baywet added this to the Backlog milestone Jun 19, 2024
@baywet baywet moved this from Needs Triage 🔍 to Waits for author 🔁 in Kiota Jun 19, 2024
@msgraph-bot msgraph-bot bot added the Csharp Pull requests that update .net code label Jun 19, 2024
@linde12
Copy link
Author

linde12 commented Jun 24, 2024

Hi @baywet I was celebrating a Swedish holiday so excuse the delay 😄

Here is a full OAS:

{
  "openapi": "3.0.1",
  "info": {
    "title": "ex",
    "version": "1.0"
  },
  "paths": {
    "/weatherforecast": {
      "get": {
        "tags": [
          "ex"
        ],
        "operationId": "GetWeatherForecast",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/HttpValidationProblemDetails"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "DateOnly": {
        "type": "object",
        "properties": {
          "year": {
            "type": "integer",
            "format": "int32"
          },
          "month": {
            "type": "integer",
            "format": "int32"
          },
          "day": {
            "type": "integer",
            "format": "int32"
          },
          "dayOfWeek": {
            "$ref": "#/components/schemas/DayOfWeek"
          },
          "dayOfYear": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          },
          "dayNumber": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          }
        },
        "additionalProperties": false
      },
      "DayOfWeek": {
        "enum": [
          0,
          1,
          2,
          3,
          4,
          5,
          6
        ],
        "type": "integer",
        "format": "int32"
      },
      "HttpValidationProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "instance": {
            "type": "string",
            "nullable": true
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "nullable": true
          }
        },
        "additionalProperties": { }
      },
      "WeatherForecast": {
        "type": "object",
        "properties": {
          "date": {
            "$ref": "#/components/schemas/DateOnly"
          },
          "temperatureC": {
            "type": "integer",
            "format": "int32"
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "temperatureF": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}

This is generated from a standard WebAPI application, and my endpoint looks like so:

static Results<Ok<WeatherForecast[]>, ValidationProblem> GetWeatherForecast()
{
  if (Random.Shared.Next(0, 2) == 0)
  {
    var errors = new Dictionary<string, string[]>
    {
        { "username", new[] { "Required" } }
    };
    return TypedResults.ValidationProblem(errors);
  }

  var forecast = Enumerable.Range(1, 5).Select(index =>
      new WeatherForecast
      (
          DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
          Random.Shared.Next(-20, 55),
          "Freezing"
      ))
      .ToArray();

  return TypedResults.Ok(forecast);
}

app.MapGet("/weatherforecast", GetWeatherForecast)
  .WithName("GetWeatherForecast")
  .WithOpenApi()
  .ProducesValidationProblem();

and 50% of the time it will produce a validation problem response containing an error like so (retrieved with curl):

{"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"One or more validation errors occurred.","status":400,"errors":{"username":["Required"]}}

Then i am using the generated client from Kiota like so:

using ApiSdk;
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Kiota.Http.HttpClientLibrary;

var adapter = new HttpClientRequestAdapter(new AnonymousAuthenticationProvider());
adapter.BaseUrl = "http://localhost:5117";
var client = new ApiClient(adapter);

try
{
  var res = await client.Weatherforecast.GetAsync();
}
catch (ApiSdk.Models.HttpValidationProblemDetails problem)
{
  var errors = problem.Errors; // type is HttpValidationProblemDetails_errors, only contains a "AdditionalData" field
  foreach (var error in errors.AdditionalData)
  {
    Console.WriteLine($"Error: {error.Key} - {error.Value}");
  }
}

This piece of code will print the following when an error occurs:

Error: username - Microsoft.Kiota.Abstractions.Serialization.UntypedArray

I'd expect to be able to do the following (according to OAS):

  var errors = problem.Errors; // type is HttpValidationProblemDetails_errors, only contains a "AdditionalData" field
  foreach (var error in errors.AdditionalData)
  {
    Console.WriteLine($"Error: {error.Key}");
    foreach (var message in error.Value)
    {
      Console.WriteLine($"{message}"); // print every error message for the given key
    }
  }

but this does not work since error.Value is upcasted to object and in reality is UntypedArray

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Attention 👋 and removed status:waiting-for-author-feedback Issue that we've responded but needs author feedback to close Status: No Recent Activity labels Jun 24, 2024
@linde12
Copy link
Author

linde12 commented Jun 24, 2024

The OpenAPI Schema was generated using the standard UseSwagger() you get from dotnet new webapi and the returned ValidationProblem is from Microsoft.AspNetCore

I'm not sure how i should work with validation errors here.

@andrueastman
Copy link
Member

At the moment, the type information for the isn't used by the deserializer for the items in the additionalData. So they are represented using UntypedNodes as documented at https://learn.microsoft.com/en-us/openapi/kiota/serialization?tabs=csharp#untyped-node.

Any chance you are able to print out the information with the code below?

foreach (var error in errors.AdditionalData)
{
    Console.WriteLine($"Error: {error.Key}");
    if (error.Value is UntypedArray arrayValue)
    {
        foreach (var message in arrayValue.GetValue())
        {
            if (message is UntypedString messageString)
                Console.WriteLine($"{messageString.GetValue()}"); // print every error message for the given key
        }
    }
}

@andrueastman andrueastman added status:waiting-for-author-feedback Issue that we've responded but needs author feedback to close and removed Needs: Attention 👋 labels Jun 25, 2024
@linde12
Copy link
Author

linde12 commented Jun 28, 2024

@andrueastman Ah, that explains it. Is this something that is being actively worked or something that's planned? We ended up with a similar workaround to reach the wanted values, it was just a bit cumbersome and we had to do some digging to understand how to approach this.

Thank you for prompt replies 🙏

@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Attention 👋 and removed status:waiting-for-author-feedback Issue that we've responded but needs author feedback to close labels Jun 28, 2024
@baywet
Copy link
Member

baywet commented Jun 28, 2024

Hi everyone,
Thanks for all the additional information here.
I'm going to go ahead and close this as a duplicate of #62 since the resolution requires support of dictionaries in kiota, which requires support of OAS 3.1 with openapi.net so both the description generation on asp.net side, and the code generation can work hand in hand.
Additional, if you read the conversation over there, you'll see that people have been complaining about the same scenario (validation errors from asp.net)

@baywet baywet closed this as not planned Won't fix, can't repro, duplicate, stale Jun 28, 2024
@github-project-automation github-project-automation bot moved this from Waits for author 🔁 to Done ✔️ in Kiota Jun 28, 2024
@baywet baywet added duplicate This issue or pull request already exists enhancement New feature or request type:enhancement Enhancement request targeting an existing experience and removed type:question An issue that's a question labels Jun 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Csharp Pull requests that update .net code duplicate This issue or pull request already exists enhancement New feature or request type:enhancement Enhancement request targeting an existing experience
Projects
Archived in project
Development

No branches or pull requests

3 participants