Skip to content

Commit

Permalink
Merge pull request #1100 from microsoft/feature/error-description
Browse files Browse the repository at this point in the history
adds error handling support
  • Loading branch information
baywet authored Feb 10, 2022
2 parents 7aca9e4 + 992d206 commit 93a92d5
Show file tree
Hide file tree
Showing 61 changed files with 4,423 additions and 3,402 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"cSpell.words": [
"npmrc",
"serializers"
]
],
"java.configuration.updateBuildConfiguration": "automatic"
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Modified the TypeScript RequestInformation content data type to ArrayBuffer.
- Updated PHP abstractions to make property keys and values nullable in `SerializationWriter.php`.
- Fixed an issue where enum collections parsing would fail in Go.
- Breaking. Kiota clients generate error types and throw when the target API returns a failed response (dotnet, go, java, typescript). #1100

## [0.0.15] - 2021-12-17

Expand Down
26 changes: 26 additions & 0 deletions abstractions/dotnet/src/ApiException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------
using System;

namespace Microsoft.Kiota.Abstractions;

/// <summary>
/// Parent type for exceptions thrown by the client when receiving failed responses to its requests.
/// </summary>
public class ApiException : Exception
{
/// <inheritdoc/>
public ApiException(): base()
{

}
/// <inheritdoc/>
public ApiException(string message) : base(message)
{
}
/// <inheritdoc/>
public ApiException(string message, Exception innerException) : base(message, innerException)
{
}
}
16 changes: 11 additions & 5 deletions abstractions/dotnet/src/IRequestAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -29,41 +30,46 @@ public interface IRequestAdapter
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="responseHandler">The response handler to use for the HTTP request instead of the default handler.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized response model.</returns>
Task<ModelType> SendAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
Task<ModelType> SendAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary<string, Func<IParsable>> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="responseHandler">The response handler to use for the HTTP request instead of the default handler.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized response model collection.</returns>
Task<IEnumerable<ModelType>> SendCollectionAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
Task<IEnumerable<ModelType>> SendCollectionAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary<string, Func<IParsable>> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="responseHandler">The response handler to use for the HTTP request instead of the default handler.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized primitive response model.</returns>
Task<ModelType> SendPrimitiveAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default);
Task<ModelType> SendPrimitiveAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary<string, Func<IParsable>> errorMapping = default, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model collection.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="responseHandler">The response handler to use for the HTTP request instead of the default handler.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized primitive response model collection.</returns>
Task<IEnumerable<ModelType>> SendPrimitiveCollectionAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default);
Task<IEnumerable<ModelType>> SendPrimitiveCollectionAsync<ModelType>(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary<string, Func<IParsable>> errorMapping = default, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation with no return content.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="responseHandler">The response handler to use for the HTTP request instead of the default handler.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>A Task to await completion.</returns>
Task SendNoContentAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default);
Task SendNoContentAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary<string, Func<IParsable>> errorMapping = default, CancellationToken cancellationToken = default);
/// <summary>
/// The base url for every request.
/// </summary>
Expand Down
6 changes: 5 additions & 1 deletion abstractions/dotnet/src/IResponseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// ------------------------------------------------------------------------------

using System.Threading.Tasks;
using System.Collections.Generic;
using System;
using Microsoft.Kiota.Abstractions.Serialization;

namespace Microsoft.Kiota.Abstractions
{
Expand All @@ -15,9 +18,10 @@ public interface IResponseHandler
/// Callback method that is invoked when a response is received.
/// </summary>
/// <param name="response">The native response object.</param>
/// <param name="errorMappings">The error mappings for the response to use when deserializing failed responses bodies. Where an error code like 401 applies specifically to that status code, a class code like 4XX applies to all status codes within the range if an the specific error code is not present.</param>
/// <typeparam name="NativeResponseType">The type of the native response object.</typeparam>
/// <typeparam name="ModelType">The type of the response model object.</typeparam>
/// <returns>A task that represents the asynchronous operation and contains the deserialized response.</returns>
Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response);
Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response, Dictionary<string, Func<IParsable>> errorMappings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<LangVersion>latest</LangVersion>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryUrl>https://github.com/microsoft/kiota</RepositoryUrl>
<Version>1.0.28</Version>
<Version>1.0.29</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<!-- Enable this line once we go live to prevent breaking changes -->
Expand Down
13 changes: 9 additions & 4 deletions abstractions/dotnet/src/NativeResponseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// ------------------------------------------------------------------------------

using System.Threading.Tasks;
using System.Collections.Generic;
using System;
using Microsoft.Kiota.Abstractions.Serialization;

namespace Microsoft.Kiota.Abstractions
{
Expand All @@ -17,13 +20,15 @@ public class NativeResponseHandler : IResponseHandler
public object Value;

/// <summary>
/// Handles the response of type <typeparam name="NativeResponseType"/>and return an instance of <typeparam name="ModelType"/>
/// The error mappings for the response to use when deserializing failed responses bodies. Where an error code like 401 applies specifically to that status code, a class code like 4XX applies to all status codes within the range if an the specific error code is not present.
/// </summary>
/// <param name="response">The response to be handled</param>
/// <returns></returns>
public Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response)
public Dictionary<string, Func<IParsable>> ErrorMappings { get; set; }

/// <inheritdoc />
public Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response, Dictionary<string, Func<IParsable>> errorMappings)
{
Value = response;
ErrorMappings = errorMappings;
return Task.FromResult(default(ModelType));
}
}
Expand Down
6 changes: 6 additions & 0 deletions abstractions/dotnet/src/serialization/IParseNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ public interface IParseNode
/// <returns>The model object value of the node.</returns>
T GetObjectValue<T>() where T : IParsable;
/// <summary>
/// Gets the resulting error from the node.
/// </summary>
/// <returns>The error object value of the node.</returns>
/// <param name="factory">The error factory.</param>
IParsable GetErrorValue(Func<IParsable> factory);
/// <summary>
/// Callback called before the node is deserialized.
/// </summary>
Action<IParsable> OnBeforeAssignFieldValues { get; set; }
Expand Down
21 changes: 21 additions & 0 deletions abstractions/go/api_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package abstractions

import "fmt"

// ApiError is the parent type for errors thrown by the client when receiving failed responses to its requests
type ApiError struct {
Message string
}

func (e *ApiError) Error() string {
if len(e.Message) > 0 {
return fmt.Sprint(e.Message)
} else {
return "error status code received from the API"
}
}

// NewApiError creates a new ApiError instance
func NewApiError() *ApiError {
return &ApiError{}
}
13 changes: 8 additions & 5 deletions abstractions/go/request_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ import (
s "github.com/microsoft/kiota/abstractions/go/serialization"
)

// ErrorMappings is a mapping of status codes to error types factories.
type ErrorMappings map[string]s.ParsableFactory

// RequestAdapter is the service responsible for translating abstract RequestInformation into native HTTP requests.
type RequestAdapter interface {
// SendAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized response model.
SendAsync(requestInfo RequestInformation, constructor func() s.Parsable, responseHandler ResponseHandler) (s.Parsable, error)
SendAsync(requestInfo RequestInformation, constructor s.ParsableFactory, responseHandler ResponseHandler, errorMappings ErrorMappings) (s.Parsable, error)
// SendCollectionAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
SendCollectionAsync(requestInfo RequestInformation, constructor func() s.Parsable, responseHandler ResponseHandler) ([]s.Parsable, error)
SendCollectionAsync(requestInfo RequestInformation, constructor s.ParsableFactory, responseHandler ResponseHandler, errorMappings ErrorMappings) ([]s.Parsable, error)
// SendPrimitiveAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
SendPrimitiveAsync(requestInfo RequestInformation, typeName string, responseHandler ResponseHandler) (interface{}, error)
SendPrimitiveAsync(requestInfo RequestInformation, typeName string, responseHandler ResponseHandler, errorMappings ErrorMappings) (interface{}, error)
// SendPrimitiveCollectionAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model collection.
SendPrimitiveCollectionAsync(requestInfo RequestInformation, typeName string, responseHandler ResponseHandler) ([]interface{}, error)
SendPrimitiveCollectionAsync(requestInfo RequestInformation, typeName string, responseHandler ResponseHandler, errorMappings ErrorMappings) ([]interface{}, error)
// SendNoContentAsync executes the HTTP request specified by the given RequestInformation with no return content.
SendNoContentAsync(requestInfo RequestInformation, responseHandler ResponseHandler) error
SendNoContentAsync(requestInfo RequestInformation, responseHandler ResponseHandler, errorMappings ErrorMappings) error
// GetSerializationWriterFactory returns the serialization writer factory currently in use for the request adapter service.
GetSerializationWriterFactory() s.SerializationWriterFactory
// EnableBackingStore enables the backing store proxies for the SerializationWriters and ParseNodes in use.
Expand Down
2 changes: 1 addition & 1 deletion abstractions/go/response_handler.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package abstractions

// ResponseHandler handler to implement when a request's response should be handled a specific way.
type ResponseHandler func(response interface{}) (interface{}, error)
type ResponseHandler func(response interface{}, errorMappings ErrorMappings) (interface{}, error)
3 changes: 3 additions & 0 deletions abstractions/go/serialization/parsable.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ type Parsable interface {
// IsNil returns whether the current object is nil or not.
IsNil() bool
}

// ParsableFactory is a factory for creating Parsable.
type ParsableFactory func() Parsable
2 changes: 1 addition & 1 deletion abstractions/java/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-abstractions'
version '1.0.25'
version '1.0.26'
from(components.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.microsoft.kiota;

/** Parent type for exceptions thrown by the client when receiving failed responses to its requests. */
public class ApiException extends Exception {
/** {@inheritdoc} */
public ApiException() {
super();
}
/** {@inheritdoc} */
public ApiException(String message) {
super(message);
}
/** {@inheritdoc} */
public ApiException(String message, Throwable cause) {
super(message, cause);
}
/** {@inheritdoc} */
public ApiException(Throwable cause) {
super(cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
package com.microsoft.kiota;

import java.util.HashMap;
import java.util.concurrent.CompletableFuture;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.microsoft.kiota.serialization.Parsable;

/** Default response handler to access the native response object. */
public class NativeResponseHandler implements ResponseHandler {
/** Native response object as returned by the core service */
@Nullable
public Object value;

/** The error mappings for the response to use when deserializing failed responses bodies. Where an error code like 401 applies specifically to that status code, a class code like 4XX applies to all status codes within the range if an the specific error code is not present. */
@Nullable
public HashMap<String, Class<? extends Parsable>> errorMappings;

/** {@inheritdoc} */
@Nonnull
@Override
public <NativeResponseType, ModelType> CompletableFuture<ModelType> handleResponseAsync(
NativeResponseType response) {
NativeResponseType response,
HashMap<String, Class<? extends Parsable>> errorMappings) {
this.value = response;
this.errorMappings = errorMappings;
return CompletableFuture.completedFuture(null);
}

Expand Down
Loading

0 comments on commit 93a92d5

Please sign in to comment.