-
Notifications
You must be signed in to change notification settings - Fork 211
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
Add support for global error handling #411
Comments
Kiota wouldn't need to process the Overlay directly. That overlay code would be implemented somewhere else. |
how would the HTTP library know which type to deserialize the error to then? |
The Overlay would be processed before Kiota started processing the OpenAPI. |
So kiota would see responses with schemas on different response codes. But how would it map the successful response codes with a specific bad response code schéma? Especially if the schema for 5xx differs from 4xx for some reason? |
For people writing OpenAPI by hand, this feels like an OpenAPI problem, not a Kiota problem. I feel like we should close this issue as this is not where this problem should be solved. |
So if we say that we need an error description for each endpoint, we still have work to do in Kiota/design decisions to make. Having global error handling (things that apply to all endpoints) would have allowed us to pass those type registrations once for the client/request adapter initialization. It's possible but means two things:
Thoughts @darrelmiller? |
Also, should we assume any type described in the error responses can derive from Exception/Error and throw? |
This is a tricky one from my point of view working with strongly typed languages however if a path returns 200 and a model and a possiblte 400 with another model the api client has to return one of those types. The only thing I can think of, off the top of my head is using the Go approach and have a model from the client that is something like:
The types of the Model/ErrorModel would have to match the openapi schema defined models and the developer would have to check if the error model is populated before continuing using the response but it could work |
Thanks for jumping in here, this is an interesting suggestion. Couple of follow up questions:
|
2)You can only every get one response whether thats 400/403/404/500 so assuming the openapi doc has those in it and a defined schema for each possible response status code then you return that model in the generated classes and the developer has to make a decision on what to do if the error model is populated |
extending on your example with this information we'd have public class ResponseModel
{
public SuccessModel Model{get;set;}
public ErrorModel400 ErrorModel400{get;set;}
public ErrorModel403 ErrorModel403{get;set;}
public ErrorModel404 ErrorModel404{get;set;}
public ErrorModel500 ErrorModel500{get;set;}
} Which means the SDK user would have to do something like var myResult = await client.Something.SomethingElse.GetAsync();
if (myresul.SuccessModel != null) {
// continue normally
} else if (myresult.ErrorModel400 != null) {
// error handling for 400
} else if (myresult.ErrorModel403 != null) {
// error handling for 403
} else if (myresult.ErrorModel404 != null) {
// error handling for 404
} else if (myresult.ErrorModel500 != null) {
// error handling for 500
} else {
// I'm not sure what to do but my static analysis tool told me that I'm not handling all cases
} Which I don't believe is a great experience over throwing exceptions try {
var myResult = await client.Something.SomethingElse.GetAsync();
// continue normally
} catch (ErrorModel400 ex) {
// error handling for 400
} catch (ErrorModel403 ex) {
// error handling for 403
} catch (ErrorModel404 ex) {
// error handling for 404
} catch (ErrorModel500 ex) {
// error handling for 500
}
// I can add a finally block if I really need to, but if I don't, the static analysis tool won't complain Which would also allow me to do the following try {
var myResult = await client.Something.SomethingElse.GetAsync();
// continue normally
} catch (Exception ex) {
// global error handling, like logging
} Or even just var myResult = await client.Something.SomethingElse.GetAsync();
// continue normally Assuming I have some cross-cutting concern reusable implementation at a higher-level with things like APM (application insights), global error display (ASP.NET error handlers), logging, or retry-handling policies (Polly etc). Which would work across the different path items for statically typed languages. Thoughts? |
Ah good point, I was having in my head run time deserializing to one model but that's not going to work is it for C# 😂😂 And so you would end up with what you propose which is pretty grim however after more thought I think it's explicit. Even if you used exceptions to control flow you're still going to have to do the same |
One assumption I forgot to communicate in my previous response, each of the types derive from Exception (ErrorModel400, ErrorModel403, ErrorModel403, ErrorModel500). That's an assumption we're making for types referenced in responses >399 < 600. |
Default, as in they've not defined a schema response? I'd assume success Doesn't matter if they reference the same model does it? The SuccessModel will be set to an instance of T and if it's a 400 response the ErrorModel will be an instance of T |
default as sure, but they'll derive from Error/Exception which will include additional behavior depending on the target language/platform. |
@baywet Your example of having different models for different status codes is a good scenario. This example is a bit extreme, but all of the different responses are viable. openapi: 3.0.3
info:
title: Responses with different models
version: 1.0.0
paths:
/:
get:
responses:
'2XX':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/MySpecialEntity'
'202':
description: Status monitor
content:
application/json:
schema:
$ref: '#/components/schemas/StatusMonitor'
'300':
description: Download links
content:
application/json:
schema:
$ref: '#/components/schemas/DownloadLinks'
'4XX':
description: Client Error
content:
application/json:
schema:
$ref: '#/components/schemas/ClientFailureInfo'
'5XX':
description: Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ServerFailureInfo' I guess we either pass a map to core, or core returns a stream and we emit code in the service library to do the deserialization. Whatever happened to the plan of passing a codegen'd response handler to core? |
If your question is "how do we pass the response type and constructor /factory to the request adapter?" the code generator passes those to the send method. And yes we could pass a map of error codes and factories there too. |
I don't have a problem with the error type deriving from exception. We can throw one of multiple exceptions depending on the status code. That still leaves us with the issue of different types for 200, 202 & 300. What I meant was, what if instead of using the default implementation of IResponseHandler that is defined in core, we actually generated a response handler that mapped the status codes to code that deserialized the correct type. I suppose the problem that still remains is the request executor method still can only return one type unless we create a wrapper type. If we have to create a wrapper type anyway, we might as well pass the map to core and avoid doing the codegen. |
Thanks for all the additional information and the open discussion, the next step will be to update the conversion library so we have an accurate description for Microsoft Graph. |
as an additional precision to the "spec", if the request adapter received an error code [400, 599], and if it doesn't find an exact match in the mapping (e.g. 403), or a class match (eg 4XX), it should just throw an exception with an error code and ignore the body. |
Currently the http core implementations do no try to parse error responses. Those could contain useful information to convey to the consumer.
You can already define responses in open API for HTTP status codes that correspond to errors (4XX, 5XX), however defining those for each endpoint in Microsoft Graph would be fastidious.
Instead we should make use of another feature in openAPI called overlays. This feature is not yet implemented in the openAPI.net lib but it'd allow defining global responses that apply to all the endpoints if not already defined locally.
AB#10401
The text was updated successfully, but these errors were encountered: