Closed linde12 closed 4 months ago
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!
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
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.
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 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 š
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)
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-inValidationProblem
(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 anAdditionalData
dictionary. This dictionary is not what you would expect though (Dictionary<string, string[]>
of errors) but a dict containing one key ("errors") which is anMicrosoft.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.Expected behavior
I expect Kiota to follow the API Schema (see screenshot in description above) and generate a
MyApi.Models.HttpValidationProblemDetails
exception which contains aErrors
(alternativelyErrors.AdditionalData
if thats some convention Kiota follows?) that is of typeDictionary<string, string[]>
)The OpenAPI Schema clearly states that
errors
is an object where eachadditionalProperty
(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 theProducesValidationProblem
method. Generate a client and look at theHttpValidationProblemDetails.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