diff --git a/.vscode/settings.json b/.vscode/settings.json
index 601a42d3c4..aa309383b2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -18,5 +18,6 @@
"cSpell.words": [
"npmrc",
"serializers"
- ]
+ ],
+ "java.configuration.updateBuildConfiguration": "automatic"
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 871911466e..da44f7d58b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/abstractions/dotnet/src/ApiException.cs b/abstractions/dotnet/src/ApiException.cs
new file mode 100644
index 0000000000..1af85e0e48
--- /dev/null
+++ b/abstractions/dotnet/src/ApiException.cs
@@ -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;
+
+///
+/// Parent type for exceptions thrown by the client when receiving failed responses to its requests.
+///
+public class ApiException : Exception
+{
+ ///
+ public ApiException(): base()
+ {
+
+ }
+ ///
+ public ApiException(string message) : base(message)
+ {
+ }
+ ///
+ public ApiException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+}
diff --git a/abstractions/dotnet/src/IRequestAdapter.cs b/abstractions/dotnet/src/IRequestAdapter.cs
index 61da556f49..b7980f6b10 100644
--- a/abstractions/dotnet/src/IRequestAdapter.cs
+++ b/abstractions/dotnet/src/IRequestAdapter.cs
@@ -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;
@@ -29,41 +30,46 @@ public interface IRequestAdapter
///
/// The RequestInformation object to use for the HTTP request.
/// The response handler to use for the HTTP request instead of the default handler.
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the requests.
/// The deserialized response model.
- Task SendAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
+ Task SendAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
///
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
///
/// The RequestInformation object to use for the HTTP request.
/// The response handler to use for the HTTP request instead of the default handler.
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the requests.
/// The deserialized response model collection.
- Task> SendCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
+ Task> SendCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
///
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
///
/// The RequestInformation object to use for the HTTP request.
/// The response handler to use for the HTTP request instead of the default handler.
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the requests.
/// The deserialized primitive response model.
- Task SendPrimitiveAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default);
+ Task SendPrimitiveAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default);
///
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model collection.
///
/// The RequestInformation object to use for the HTTP request.
/// The response handler to use for the HTTP request instead of the default handler.
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the requests.
/// The deserialized primitive response model collection.
- Task> SendPrimitiveCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default);
+ Task> SendPrimitiveCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default);
///
/// Executes the HTTP request specified by the given RequestInformation with no return content.
///
/// The RequestInformation object to use for the HTTP request.
/// The response handler to use for the HTTP request instead of the default handler.
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the requests.
/// A Task to await completion.
- Task SendNoContentAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default);
+ Task SendNoContentAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default);
///
/// The base url for every request.
///
diff --git a/abstractions/dotnet/src/IResponseHandler.cs b/abstractions/dotnet/src/IResponseHandler.cs
index c2012208b2..e46c3d8c0c 100644
--- a/abstractions/dotnet/src/IResponseHandler.cs
+++ b/abstractions/dotnet/src/IResponseHandler.cs
@@ -3,6 +3,9 @@
// ------------------------------------------------------------------------------
using System.Threading.Tasks;
+using System.Collections.Generic;
+using System;
+using Microsoft.Kiota.Abstractions.Serialization;
namespace Microsoft.Kiota.Abstractions
{
@@ -15,9 +18,10 @@ public interface IResponseHandler
/// Callback method that is invoked when a response is received.
///
/// The native response object.
+ /// 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.
/// The type of the native response object.
/// The type of the response model object.
/// A task that represents the asynchronous operation and contains the deserialized response.
- Task HandleResponseAsync(NativeResponseType response);
+ Task HandleResponseAsync(NativeResponseType response, Dictionary> errorMappings);
}
}
diff --git a/abstractions/dotnet/src/Microsoft.Kiota.Abstractions.csproj b/abstractions/dotnet/src/Microsoft.Kiota.Abstractions.csproj
index 104f8a5660..a93b15b929 100644
--- a/abstractions/dotnet/src/Microsoft.Kiota.Abstractions.csproj
+++ b/abstractions/dotnet/src/Microsoft.Kiota.Abstractions.csproj
@@ -6,7 +6,7 @@
latest
true
https://github.com/microsoft/kiota
- 1.0.28
+ 1.0.29
true
true
diff --git a/abstractions/dotnet/src/NativeResponseHandler.cs b/abstractions/dotnet/src/NativeResponseHandler.cs
index e15cf42191..111e4c2196 100644
--- a/abstractions/dotnet/src/NativeResponseHandler.cs
+++ b/abstractions/dotnet/src/NativeResponseHandler.cs
@@ -3,6 +3,9 @@
// ------------------------------------------------------------------------------
using System.Threading.Tasks;
+using System.Collections.Generic;
+using System;
+using Microsoft.Kiota.Abstractions.Serialization;
namespace Microsoft.Kiota.Abstractions
{
@@ -17,13 +20,15 @@ public class NativeResponseHandler : IResponseHandler
public object Value;
///
- /// Handles the response of type and return an instance of
+ /// 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.
///
- /// The response to be handled
- ///
- public Task HandleResponseAsync(NativeResponseType response)
+ public Dictionary> ErrorMappings { get; set; }
+
+ ///
+ public Task HandleResponseAsync(NativeResponseType response, Dictionary> errorMappings)
{
Value = response;
+ ErrorMappings = errorMappings;
return Task.FromResult(default(ModelType));
}
}
diff --git a/abstractions/dotnet/src/serialization/IParseNode.cs b/abstractions/dotnet/src/serialization/IParseNode.cs
index 314c49f8ae..e96a2db892 100644
--- a/abstractions/dotnet/src/serialization/IParseNode.cs
+++ b/abstractions/dotnet/src/serialization/IParseNode.cs
@@ -99,6 +99,12 @@ public interface IParseNode
/// The model object value of the node.
T GetObjectValue() where T : IParsable;
///
+ /// Gets the resulting error from the node.
+ ///
+ /// The error object value of the node.
+ /// The error factory.
+ IParsable GetErrorValue(Func factory);
+ ///
/// Callback called before the node is deserialized.
///
Action OnBeforeAssignFieldValues { get; set; }
diff --git a/abstractions/go/api_error.go b/abstractions/go/api_error.go
new file mode 100644
index 0000000000..90c3c0fb30
--- /dev/null
+++ b/abstractions/go/api_error.go
@@ -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{}
+}
diff --git a/abstractions/go/request_adapter.go b/abstractions/go/request_adapter.go
index 4891c81033..7d75e5b799 100644
--- a/abstractions/go/request_adapter.go
+++ b/abstractions/go/request_adapter.go
@@ -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.
diff --git a/abstractions/go/response_handler.go b/abstractions/go/response_handler.go
index 7623213ced..7c729219ba 100644
--- a/abstractions/go/response_handler.go
+++ b/abstractions/go/response_handler.go
@@ -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)
diff --git a/abstractions/go/serialization/parsable.go b/abstractions/go/serialization/parsable.go
index 320873c453..3a4ca7696f 100644
--- a/abstractions/go/serialization/parsable.go
+++ b/abstractions/go/serialization/parsable.go
@@ -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
diff --git a/abstractions/java/lib/build.gradle b/abstractions/java/lib/build.gradle
index 305581f03c..458ed41c1a 100644
--- a/abstractions/java/lib/build.gradle
+++ b/abstractions/java/lib/build.gradle
@@ -48,7 +48,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-abstractions'
- version '1.0.25'
+ version '1.0.26'
from(components.java)
}
}
diff --git a/abstractions/java/lib/src/main/java/com/microsoft/kiota/ApiException.java b/abstractions/java/lib/src/main/java/com/microsoft/kiota/ApiException.java
new file mode 100644
index 0000000000..78c0a926b7
--- /dev/null
+++ b/abstractions/java/lib/src/main/java/com/microsoft/kiota/ApiException.java
@@ -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);
+ }
+}
diff --git a/abstractions/java/lib/src/main/java/com/microsoft/kiota/NativeResponseHandler.java b/abstractions/java/lib/src/main/java/com/microsoft/kiota/NativeResponseHandler.java
index 366f19b2c1..b370c6b36a 100644
--- a/abstractions/java/lib/src/main/java/com/microsoft/kiota/NativeResponseHandler.java
+++ b/abstractions/java/lib/src/main/java/com/microsoft/kiota/NativeResponseHandler.java
@@ -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> errorMappings;
+
+ /** {@inheritdoc} */
@Nonnull
@Override
public CompletableFuture handleResponseAsync(
- NativeResponseType response) {
+ NativeResponseType response,
+ HashMap> errorMappings) {
this.value = response;
+ this.errorMappings = errorMappings;
return CompletableFuture.completedFuture(null);
}
diff --git a/abstractions/java/lib/src/main/java/com/microsoft/kiota/RequestAdapter.java b/abstractions/java/lib/src/main/java/com/microsoft/kiota/RequestAdapter.java
index 62121bb6d2..828387fa94 100644
--- a/abstractions/java/lib/src/main/java/com/microsoft/kiota/RequestAdapter.java
+++ b/abstractions/java/lib/src/main/java/com/microsoft/kiota/RequestAdapter.java
@@ -1,6 +1,7 @@
package com.microsoft.kiota;
import java.util.concurrent.CompletableFuture;
+import java.util.HashMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -23,41 +24,45 @@ public interface RequestAdapter {
@Nonnull
SerializationWriterFactory getSerializationWriterFactory();
/**
- * Excutes the HTTP request specified by the given RequestInformation and returns the deserialized response model.
+ * Executes the HTTP request specified by the given RequestInformation and returns the deserialized response model.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
* @param targetClass the class of the response model to deserialize the response into.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param the type of the response model to deserialize the response into.
* @return a {@link CompletableFuture} with the deserialized response model.
*/
- CompletableFuture sendAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler);
+ CompletableFuture sendAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings);
/**
- * Excutes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
+ * Executes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
* @param targetClass the class of the response model to deserialize the response into.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param the type of the response model to deserialize the response into.
* @return a {@link CompletableFuture} with the deserialized response model collection.
*/
- CompletableFuture> sendCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler);
+ CompletableFuture> sendCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings);
/**
- * Excutes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
+ * Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
* @param targetClass the class of the response model to deserialize the response into.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param the type of the response model to deserialize the response into.
* @return a {@link CompletableFuture} with the deserialized primitive response model.
*/
- CompletableFuture sendPrimitiveAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler);
+ CompletableFuture sendPrimitiveAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings);
/**
- * Excutes the HTTP request specified by the given RequestInformation and returns the deserialized primitive collection response model.
+ * Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive collection response model.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
* @param targetClass the class of the response model to deserialize the response into.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param the type of the response model to deserialize the response into.
* @return a {@link CompletableFuture} with the deserialized primitive collection response model.
*/
- CompletableFuture> sendPrimitiveCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler);
+ CompletableFuture> sendPrimitiveCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings);
/**
* Sets The base url for every request.
* @param baseUrl The base url for every request.
diff --git a/abstractions/java/lib/src/main/java/com/microsoft/kiota/ResponseHandler.java b/abstractions/java/lib/src/main/java/com/microsoft/kiota/ResponseHandler.java
index 9505518093..bcb99d3801 100644
--- a/abstractions/java/lib/src/main/java/com/microsoft/kiota/ResponseHandler.java
+++ b/abstractions/java/lib/src/main/java/com/microsoft/kiota/ResponseHandler.java
@@ -1,18 +1,23 @@
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;
/** Defines the contract for a response handler. */
public interface ResponseHandler {
/**
* Callback method that is invoked when a response is received.
* @param response The native response object.
+ * @param 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 The type of the native response object.
* @param The type of the response model object.
* @return A CompletableFuture that represents the asynchronous operation and contains the deserialized response.
*/
@Nonnull
- CompletableFuture handleResponseAsync(@Nonnull final NativeResponseType response);
+ CompletableFuture handleResponseAsync(@Nonnull final NativeResponseType response, @Nullable final HashMap> errorMappings);
}
\ No newline at end of file
diff --git a/abstractions/typescript/package-lock.json b/abstractions/typescript/package-lock.json
index 2081e46ea5..2f1a2633f8 100644
--- a/abstractions/typescript/package-lock.json
+++ b/abstractions/typescript/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@microsoft/kiota-abstractions",
- "version": "1.0.28",
+ "version": "1.0.29",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@microsoft/kiota-abstractions",
- "version": "1.0.28",
+ "version": "1.0.29",
"license": "MIT",
"dependencies": {
"tinyduration": "^3.2.2",
diff --git a/abstractions/typescript/package.json b/abstractions/typescript/package.json
index df0995dadc..7b16aa4878 100644
--- a/abstractions/typescript/package.json
+++ b/abstractions/typescript/package.json
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-abstractions",
- "version": "1.0.28",
+ "version": "1.0.29",
"description": "Core abstractions for kiota generated libraries in TypeScript and JavaScript",
"main": "dist/cjs/index.js",
"module": "dist/es/index.js",
diff --git a/abstractions/typescript/src/apiError.ts b/abstractions/typescript/src/apiError.ts
new file mode 100644
index 0000000000..96ae787ffa
--- /dev/null
+++ b/abstractions/typescript/src/apiError.ts
@@ -0,0 +1,7 @@
+/** Parent interface for errors thrown by the client when receiving failed responses to its requests. */
+export class ApiError extends Error {
+ public constructor(message?: string) {
+ super(message);
+ Object.setPrototypeOf(this, ApiError.prototype);
+ }
+}
\ No newline at end of file
diff --git a/abstractions/typescript/src/index.ts b/abstractions/typescript/src/index.ts
index 65f1372969..33e1f86925 100644
--- a/abstractions/typescript/src/index.ts
+++ b/abstractions/typescript/src/index.ts
@@ -1,4 +1,5 @@
export * from "./apiClientBuilder";
+export * from "./apiError";
export * from "./authentication";
export * from "./dateOnly";
export * from "./duration";
diff --git a/abstractions/typescript/src/nativeResponseHandler.ts b/abstractions/typescript/src/nativeResponseHandler.ts
index dda482fe2f..171a3d1fee 100644
--- a/abstractions/typescript/src/nativeResponseHandler.ts
+++ b/abstractions/typescript/src/nativeResponseHandler.ts
@@ -1,13 +1,18 @@
import { ResponseHandler } from "./responseHandler";
+import { Parsable } from "./serialization";
/** Default response handler to access the native response object. */
export class NativeResponseHandler implements ResponseHandler {
/** Native response object as returned by the core service */
public value?: any;
+ /** 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 a specific error code is not present. */
+ public errorMappings: Record Parsable> | undefined
public handleResponseAsync(
- response: NativeResponseType
+ response: NativeResponseType,
+ errorMappings: Record Parsable> | undefined
): Promise {
this.value = response;
+ this.errorMappings = errorMappings;
return Promise.resolve(undefined as any);
}
}
diff --git a/abstractions/typescript/src/requestAdapter.ts b/abstractions/typescript/src/requestAdapter.ts
index f90a95eb1e..ec1e452c54 100644
--- a/abstractions/typescript/src/requestAdapter.ts
+++ b/abstractions/typescript/src/requestAdapter.ts
@@ -14,6 +14,7 @@ export interface RequestAdapter {
* Excutes the HTTP request specified by the given RequestInformation and returns the deserialized response model.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param type the class of the response model to deserialize the response into.
* @typeParam ModelType the type of the response model to deserialize the response into.
* @return a {@link Promise} with the deserialized response model.
@@ -21,12 +22,14 @@ export interface RequestAdapter {
sendAsync(
requestInfo: RequestInformation,
type: new () => ModelType,
- responseHandler: ResponseHandler | undefined
+ responseHandler: ResponseHandler | undefined,
+ errorMappings: Record Parsable> | undefined
): Promise;
/**
* Excutes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param type the class of the response model to deserialize the response into.
* @typeParam ModelType the type of the response model to deserialize the response into.
* @return a {@link Promise} with the deserialized response model collection.
@@ -34,13 +37,15 @@ export interface RequestAdapter {
sendCollectionAsync(
requestInfo: RequestInformation,
type: new () => ModelType,
- responseHandler: ResponseHandler | undefined
+ responseHandler: ResponseHandler | undefined,
+ errorMappings: Record Parsable> | undefined
): Promise;
/**
* Excutes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
* @param requestInfo the request info to execute.
* @param responseType the class of the response model to deserialize the response into.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param type the class of the response model to deserialize the response into.
* @typeParam ResponseType the type of the response model to deserialize the response into.
* @return a {@link Promise} with the deserialized response model collection.
@@ -48,12 +53,14 @@ export interface RequestAdapter {
sendCollectionOfPrimitiveAsync(
requestInfo: RequestInformation,
responseType: "string" | "number" | "boolean" | "Date",
- responseHandler: ResponseHandler | undefined
+ responseHandler: ResponseHandler | undefined,
+ errorMappings: Record Parsable> | undefined
): Promise;
/**
* Excutes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @param responseType the class of the response model to deserialize the response into.
* @typeParam ResponseType the type of the response model to deserialize the response into.
* @return a {@link Promise} with the deserialized primitive response model.
@@ -61,17 +68,20 @@ export interface RequestAdapter {
sendPrimitiveAsync(
requestInfo: RequestInformation,
responseType: "string" | "number" | "boolean" | "Date" | "ArrayBuffer",
- responseHandler: ResponseHandler | undefined
+ responseHandler: ResponseHandler | undefined,
+ errorMappings: Record Parsable> | undefined
): Promise;
/**
* Excutes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @return a {@link Promise} of void.
*/
sendNoResponseContentAsync(
requestInfo: RequestInformation,
- responseHandler: ResponseHandler | undefined
+ responseHandler: ResponseHandler | undefined,
+ errorMappings: Record Parsable> | undefined
): Promise;
/**
* Enables the backing store proxies for the SerializationWriters and ParseNodes in use.
diff --git a/abstractions/typescript/src/responseHandler.ts b/abstractions/typescript/src/responseHandler.ts
index 84ab703b86..4e39734f79 100644
--- a/abstractions/typescript/src/responseHandler.ts
+++ b/abstractions/typescript/src/responseHandler.ts
@@ -1,13 +1,17 @@
+import { Parsable } from "./serialization";
+
/** Defines the contract for a response handler. */
export interface ResponseHandler {
/**
* Callback method that is invoked when a response is received.
* @param response The native response object.
+ * @param errorMappings the error factories mapping to use in case of a failed request.
* @typeParam NativeResponseType The type of the native response object.
* @typeParam ModelType The type of the response model object.
* @return A {@link Promise} that represents the asynchronous operation and contains the deserialized response.
*/
handleResponseAsync(
- response: NativeResponseType
+ response: NativeResponseType,
+ errorMappings: Record Parsable> | undefined
): Promise;
}
diff --git a/docs/extending/kiotaabstractions.md b/docs/extending/kiotaabstractions.md
index 187afe0bea..ebd694aa49 100644
--- a/docs/extending/kiotaabstractions.md
+++ b/docs/extending/kiotaabstractions.md
@@ -23,19 +23,23 @@ public interface IRequestAdapter
Task SendAsync(
RequestInformation requestInfo,
- IResponseHandler responseHandler = default) where ModelType : IParsable;
+ IResponseHandler responseHandler = default,
+ Dictionary> errorMappings = default) where ModelType : IParsable;
Task> SendCollectionAsync(
RequestInformation requestInfo,
- IResponseHandler responseHandler = default) where ModelType : IParsable;
+ IResponseHandler responseHandler = default,
+ Dictionary> errorMappings = default) where ModelType : IParsable;
Task SendPrimitiveAsync(
RequestInformation requestInfo,
- IResponseHandler responseHandler = default);
+ IResponseHandler responseHandler = default,
+ Dictionary> errorMappings = default);
Task SendNoContentAsync(
RequestInformation requestInfo,
- IResponseHandler responseHandler = default);
+ IResponseHandler responseHandler = default,
+ Dictionary> errorMappings = default);
}
```
@@ -69,10 +73,23 @@ When passed to the execution method from the fluent style API, this allows core
```csharp
public interface IResponseHandler
{
- Task HandleResponseAsync(NativeResponseType response);
+ Task HandleResponseAsync(NativeResponseType response, Dictionary> errorMappings);
}
```
+### Failed responses handling
+
+A Kiota API client will handle failed http responses (status code ∈ [400, 600[) as an exception/error. If error types are described for the operation, Kiota will generate those and attempt to deserialize a failed response to an instance of the corresponding error type with the following sequence:
+
+1. If the response is successful, deserialize it to the expected model, otherwise move to the next step.
+1. If an error factory is registered for the corresponding code (e.g. 403), deserialize to that type and throw, otherwise move to the next step.
+1. If an error factory is registered for the error class (e.g. 4XX or 5XX), deserialize to that type and throw, otherwise move to the next step.
+1. Throw the generic **ApiException** type defined in the abstractions.
+
+Additionally all generated error types inherit from the **ApiException** type defined in the abstractions to enable cross cutting implementations and returning an error when no error types are defined for an operation. This type inherits itself from **Exception** (or the native error type on the platform).
+
+> Note: if a response handler is passed, the error detection logic is bypassed to allow the caller to implement whichever custom handling they desire.
+
## Serialization
This section provides information about the types offered in the abstractions library the generated result depends on which are related to serializing and deserializing payloads.
diff --git a/http/dotnet/httpclient/src/HttpClientRequestAdapter.cs b/http/dotnet/httpclient/src/HttpClientRequestAdapter.cs
index 95a82600cb..f648c90846 100644
--- a/http/dotnet/httpclient/src/HttpClientRequestAdapter.cs
+++ b/http/dotnet/httpclient/src/HttpClientRequestAdapter.cs
@@ -58,67 +58,74 @@ public ISerializationWriterFactory SerializationWriterFactory
///
/// The instance to send
/// The to use with the response
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the request.
- public async Task> SendCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default) where ModelType : IParsable
+ public async Task> SendCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable
{
var response = await GetHttpResponseMessage(requestInfo, cancellationToken);
requestInfo.Content?.Dispose();
if(responseHandler == null)
{
+ await ThrowFailedResponse(response, errorMapping);
var rootNode = await GetRootParseNode(response);
var result = rootNode.GetCollectionOfObjectValues();
return result;
}
else
- return await responseHandler.HandleResponseAsync>(response);
+ return await responseHandler.HandleResponseAsync>(response, errorMapping);
}
///
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model collection.
///
/// The RequestInformation object to use for the HTTP request.
/// The response handler to use for the HTTP request instead of the default handler.
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the request.
/// The deserialized primitive response model collection.
- public async Task> SendPrimitiveCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default) {
+ public async Task> SendPrimitiveCollectionAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default) {
var response = await GetHttpResponseMessage(requestInfo, cancellationToken);
requestInfo.Content?.Dispose();
if(responseHandler == null)
{
+ await ThrowFailedResponse(response, errorMapping);
var rootNode = await GetRootParseNode(response);
var result = rootNode.GetCollectionOfPrimitiveValues();
return result;
}
else
- return await responseHandler.HandleResponseAsync>(response);
+ return await responseHandler.HandleResponseAsync>(response, errorMapping);
}
///
/// Send a instance with an instance of
///
/// The instance to send
/// The to use with the response
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the request.
/// The deserialized response model.
- public async Task SendAsync(RequestInformation requestInfo, IResponseHandler responseHandler = null, CancellationToken cancellationToken = default) where ModelType : IParsable
+ public async Task SendAsync(RequestInformation requestInfo, IResponseHandler responseHandler = null, Dictionary> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable
{
var response = await GetHttpResponseMessage(requestInfo, cancellationToken);
requestInfo.Content?.Dispose();
if(responseHandler == null)
{
+ await ThrowFailedResponse(response, errorMapping);
var rootNode = await GetRootParseNode(response);
var result = rootNode.GetObjectValue();
return result;
}
else
- return await responseHandler.HandleResponseAsync(response);
+ return await responseHandler.HandleResponseAsync(response, errorMapping);
}
///
/// Send a instance with a primitive instance of
///
/// The instance to send
/// The to use with the response
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the request.
/// The deserialized primitive response model.
- public async Task SendPrimitiveAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, CancellationToken cancellationToken = default)
+ public async Task SendPrimitiveAsync(RequestInformation requestInfo, IResponseHandler responseHandler = default, Dictionary> errorMapping = default, CancellationToken cancellationToken = default)
{
var response = await GetHttpResponseMessage(requestInfo, cancellationToken);
requestInfo.Content?.Dispose();
@@ -131,6 +138,7 @@ public async Task SendPrimitiveAsync(RequestInformation re
}
else
{
+ await ThrowFailedResponse(response, errorMapping);
var rootNode = await GetRootParseNode(response);
object result;
if(modelType == typeof(bool))
@@ -166,23 +174,44 @@ public async Task SendPrimitiveAsync(RequestInformation re
}
}
else
- return await responseHandler.HandleResponseAsync(response);
+ return await responseHandler.HandleResponseAsync(response, errorMapping);
}
///
/// Send a instance with an empty request body
///
/// The instance to send
/// The to use with the response
+ /// The error factories mapping to use in case of a failed request.
/// The to use for cancelling the request.
///
- public async Task SendNoContentAsync(RequestInformation requestInfo, IResponseHandler responseHandler = null, CancellationToken cancellationToken = default)
+ public async Task SendNoContentAsync(RequestInformation requestInfo, IResponseHandler responseHandler = null, Dictionary> errorMapping = default, CancellationToken cancellationToken = default)
{
var response = await GetHttpResponseMessage(requestInfo, cancellationToken);
+ await ThrowFailedResponse(response, errorMapping);
requestInfo.Content?.Dispose();
if(responseHandler == null)
response.Dispose();
else
- await responseHandler.HandleResponseAsync(response);
+ await responseHandler.HandleResponseAsync(response, errorMapping);
+ }
+ private async Task ThrowFailedResponse(HttpResponseMessage response, Dictionary> errorMapping)
+ {
+ if(response.IsSuccessStatusCode) return;
+
+ var statusCodeAsInt = (int)response.StatusCode;
+ var statusCodeAsString = statusCodeAsInt.ToString();
+ Func errorFactory;
+ if(errorMapping == null ||
+ !errorMapping.TryGetValue(statusCodeAsString, out errorFactory) &&
+ !(statusCodeAsInt >= 400 && statusCodeAsInt < 500 && errorMapping.TryGetValue("4XX", out errorFactory)) &&
+ !(statusCodeAsInt >= 500 && statusCodeAsInt < 600 && errorMapping.TryGetValue("5XX", out errorFactory)))
+ throw new ApiException($"The server returned an unexpected status code and no error factory is registered for this code: {statusCodeAsString}");
+
+ var rootNode = await GetRootParseNode(response);
+ var result = rootNode.GetErrorValue(errorFactory);
+ if(result is not Exception ex)
+ throw new ApiException($"The server returned an unexpected status code and the error registered for this code failed to deserialize: {statusCodeAsString}");
+ else throw ex;
}
private async Task GetRootParseNode(HttpResponseMessage response)
{
diff --git a/http/dotnet/httpclient/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj b/http/dotnet/httpclient/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj
index d7693d5a5f..1b88fb2621 100644
--- a/http/dotnet/httpclient/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj
+++ b/http/dotnet/httpclient/src/Microsoft.Kiota.Http.HttpClientLibrary.csproj
@@ -6,7 +6,7 @@
latest
true
https://github.com/microsoft/kiota
- 1.0.19
+ 1.0.20
true
@@ -14,7 +14,7 @@
-
+
diff --git a/http/go/nethttp/nethttp_request_adapter.go b/http/go/nethttp/nethttp_request_adapter.go
index 1936e8a7a1..cb6c731258 100644
--- a/http/go/nethttp/nethttp_request_adapter.go
+++ b/http/go/nethttp/nethttp_request_adapter.go
@@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"io/ioutil"
+ "strconv"
"strings"
ctx "context"
@@ -138,23 +139,23 @@ func (a *NetHttpRequestAdapter) getRequestFromRequestInformation(requestInfo abs
}
// SendAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized response model.
-func (a *NetHttpRequestAdapter) SendAsync(requestInfo abs.RequestInformation, constructor func() absser.Parsable, responseHandler abs.ResponseHandler) (absser.Parsable, error) {
+func (a *NetHttpRequestAdapter) SendAsync(requestInfo abs.RequestInformation, constructor absser.ParsableFactory, responseHandler abs.ResponseHandler, errorMappings abs.ErrorMappings) (absser.Parsable, error) {
response, err := a.getHttpResponseMessage(requestInfo)
if err != nil {
return nil, err
}
if responseHandler != nil {
- result, err := responseHandler(response)
+ result, err := responseHandler(response, errorMappings)
if err != nil {
return nil, err
}
return result.(absser.Parsable), nil
} else if response != nil {
- body, err := ioutil.ReadAll(response.Body)
+ err = a.throwFailedResponses(response, errorMappings)
if err != nil {
return nil, err
}
- parseNode, err := a.parseNodeFactory.GetRootParseNode(a.getResponsePrimaryContentType(response), body)
+ parseNode, err := a.getRootParseNode(response)
if err != nil {
return nil, err
}
@@ -166,23 +167,23 @@ func (a *NetHttpRequestAdapter) SendAsync(requestInfo abs.RequestInformation, co
}
// SendCollectionAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
-func (a *NetHttpRequestAdapter) SendCollectionAsync(requestInfo abs.RequestInformation, constructor func() absser.Parsable, responseHandler abs.ResponseHandler) ([]absser.Parsable, error) {
+func (a *NetHttpRequestAdapter) SendCollectionAsync(requestInfo abs.RequestInformation, constructor absser.ParsableFactory, responseHandler abs.ResponseHandler, errorMappings abs.ErrorMappings) ([]absser.Parsable, error) {
response, err := a.getHttpResponseMessage(requestInfo)
if err != nil {
return nil, err
}
if responseHandler != nil {
- result, err := responseHandler(response)
+ result, err := responseHandler(response, errorMappings)
if err != nil {
return nil, err
}
return result.([]absser.Parsable), nil
} else if response != nil {
- body, err := ioutil.ReadAll(response.Body)
+ err = a.throwFailedResponses(response, errorMappings)
if err != nil {
return nil, err
}
- parseNode, err := a.parseNodeFactory.GetRootParseNode(a.getResponsePrimaryContentType(response), body)
+ parseNode, err := a.getRootParseNode(response)
if err != nil {
return nil, err
}
@@ -194,23 +195,26 @@ func (a *NetHttpRequestAdapter) SendCollectionAsync(requestInfo abs.RequestInfor
}
// SendPrimitiveAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
-func (a *NetHttpRequestAdapter) SendPrimitiveAsync(requestInfo abs.RequestInformation, typeName string, responseHandler abs.ResponseHandler) (interface{}, error) {
+func (a *NetHttpRequestAdapter) SendPrimitiveAsync(requestInfo abs.RequestInformation, typeName string, responseHandler abs.ResponseHandler, errorMappings abs.ErrorMappings) (interface{}, error) {
response, err := a.getHttpResponseMessage(requestInfo)
if err != nil {
return nil, err
}
if responseHandler != nil {
- result, err := responseHandler(response)
+ result, err := responseHandler(response, errorMappings)
if err != nil {
return nil, err
}
return result.(absser.Parsable), nil
} else if response != nil {
- body, err := ioutil.ReadAll(response.Body)
+ err = a.throwFailedResponses(response, errorMappings)
if err != nil {
return nil, err
}
- parseNode, err := a.parseNodeFactory.GetRootParseNode(a.getResponsePrimaryContentType(response), body)
+ if typeName == "[]byte" {
+ return ioutil.ReadAll(response.Body)
+ }
+ parseNode, err := a.getRootParseNode(response)
if err != nil {
return nil, err
}
@@ -240,23 +244,23 @@ func (a *NetHttpRequestAdapter) SendPrimitiveAsync(requestInfo abs.RequestInform
}
// SendPrimitiveCollectionAsync executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model collection.
-func (a *NetHttpRequestAdapter) SendPrimitiveCollectionAsync(requestInfo abs.RequestInformation, typeName string, responseHandler abs.ResponseHandler) ([]interface{}, error) {
+func (a *NetHttpRequestAdapter) SendPrimitiveCollectionAsync(requestInfo abs.RequestInformation, typeName string, responseHandler abs.ResponseHandler, errorMappings abs.ErrorMappings) ([]interface{}, error) {
response, err := a.getHttpResponseMessage(requestInfo)
if err != nil {
return nil, err
}
if responseHandler != nil {
- result, err := responseHandler(response)
+ result, err := responseHandler(response, errorMappings)
if err != nil {
return nil, err
}
return result.([]interface{}), nil
} else if response != nil {
- body, err := ioutil.ReadAll(response.Body)
+ err = a.throwFailedResponses(response, errorMappings)
if err != nil {
return nil, err
}
- parseNode, err := a.parseNodeFactory.GetRootParseNode(a.getResponsePrimaryContentType(response), body)
+ parseNode, err := a.getRootParseNode(response)
if err != nil {
return nil, err
}
@@ -267,13 +271,13 @@ func (a *NetHttpRequestAdapter) SendPrimitiveCollectionAsync(requestInfo abs.Req
}
// SendNoContentAsync executes the HTTP request specified by the given RequestInformation with no return content.
-func (a *NetHttpRequestAdapter) SendNoContentAsync(requestInfo abs.RequestInformation, responseHandler abs.ResponseHandler) error {
+func (a *NetHttpRequestAdapter) SendNoContentAsync(requestInfo abs.RequestInformation, responseHandler abs.ResponseHandler, errorMappings abs.ErrorMappings) error {
response, err := a.getHttpResponseMessage(requestInfo)
if err != nil {
return err
}
if responseHandler != nil {
- _, err := responseHandler(response)
+ _, err := responseHandler(response, errorMappings)
return err
} else if response != nil {
return nil
@@ -281,3 +285,47 @@ func (a *NetHttpRequestAdapter) SendNoContentAsync(requestInfo abs.RequestInform
return errors.New("response is nil")
}
}
+
+func (a *NetHttpRequestAdapter) getRootParseNode(response *nethttp.Response) (absser.ParseNode, error) {
+ body, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ return nil, err
+ }
+ return a.parseNodeFactory.GetRootParseNode(a.getResponsePrimaryContentType(response), body)
+}
+
+func (a *NetHttpRequestAdapter) throwFailedResponses(response *nethttp.Response, errorMappings abs.ErrorMappings) error {
+ if response.StatusCode < 400 {
+ return nil
+ }
+
+ statusAsString := strconv.Itoa(response.StatusCode)
+ var errorCtor absser.ParsableFactory = nil
+ if len(errorMappings) != 0 {
+ if errorMappings[statusAsString] != nil {
+ errorCtor = errorMappings[statusAsString]
+ } else if response.StatusCode >= 400 && response.StatusCode < 500 && errorMappings["4XX"] != nil {
+ errorCtor = errorMappings["4XX"]
+ } else if response.StatusCode >= 500 && response.StatusCode < 600 && errorMappings["5XX"] != nil {
+ errorCtor = errorMappings["5XX"]
+ }
+ }
+
+ if errorCtor == nil {
+ return &abs.ApiError{
+ Message: "The server returned an unexpected status code and no error factory is registered for this code: " + statusAsString,
+ }
+ }
+
+ rootNode, err := a.getRootParseNode(response)
+ if err != nil {
+ return err
+ }
+
+ errValue, err := rootNode.GetObjectValue(errorCtor)
+ if err != nil {
+ return err
+ }
+
+ return errValue.(error)
+}
diff --git a/http/java/okhttp/lib/build.gradle b/http/java/okhttp/lib/build.gradle
index 4ce4a9fa50..0a6590d943 100644
--- a/http/java/okhttp/lib/build.gradle
+++ b/http/java/okhttp/lib/build.gradle
@@ -36,7 +36,7 @@ dependencies {
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:31.0.1-jre'
api 'com.squareup.okhttp3:okhttp:4.9.3'
- api 'com.microsoft.kiota:kiota-abstractions:1.0.23'
+ api 'com.microsoft.kiota:kiota-abstractions:1.0.26'
}
publishing {
@@ -53,7 +53,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-http-okhttplibrary'
- version '1.0.13'
+ version '1.0.14'
from(components.java)
}
}
diff --git a/http/java/okhttp/lib/src/main/java/com/microsoft/kiota/http/OkHttpRequestAdapter.java b/http/java/okhttp/lib/src/main/java/com/microsoft/kiota/http/OkHttpRequestAdapter.java
index 0444635ba1..c91373631e 100644
--- a/http/java/okhttp/lib/src/main/java/com/microsoft/kiota/http/OkHttpRequestAdapter.java
+++ b/http/java/okhttp/lib/src/main/java/com/microsoft/kiota/http/OkHttpRequestAdapter.java
@@ -5,6 +5,7 @@
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
+import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
@@ -15,6 +16,7 @@
import javax.annotation.Nullable;
import com.microsoft.kiota.ApiClientBuilder;
+import com.microsoft.kiota.ApiException;
import com.microsoft.kiota.RequestInformation;
import com.microsoft.kiota.RequestOption;
import com.microsoft.kiota.ResponseHandler;
@@ -91,48 +93,50 @@ public void enableBackingStore(@Nullable final BackingStoreFactory backingStoreF
}
}
@Nonnull
- public CompletableFuture> sendCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler) {
+ public CompletableFuture> sendCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings) {
Objects.requireNonNull(requestInfo, "parameter requestInfo cannot be null");
- return this.getHttpResponseMessage(requestInfo).thenCompose(response -> {
+ return this.getHttpResponseMessage(requestInfo)
+ .thenCompose(response -> {
if(responseHandler == null) {
- final ResponseBody body = response.body();
try {
- try (final InputStream rawInputStream = body.byteStream()) {
- final ParseNode rootNode = pNodeFactory.getParseNode(getMediaTypeAndSubType(body.contentType()), rawInputStream);
- final Iterable result = rootNode.getCollectionOfObjectValues(targetClass);
- return CompletableFuture.completedStage(result);
- }
+ this.throwFailedResponse(response, errorMappings);
+ final ParseNode rootNode = getRootParseNode(response);
+ final Iterable result = rootNode.getCollectionOfObjectValues(targetClass);
+ return CompletableFuture.completedStage(result);
+ } catch(ApiException ex) {
+ return CompletableFuture.failedFuture(ex);
} catch(IOException ex) {
return CompletableFuture.failedFuture(new RuntimeException("failed to read the response body", ex));
} finally {
response.close();
}
} else {
- return responseHandler.handleResponseAsync(response);
+ return responseHandler.handleResponseAsync(response, errorMappings);
}
});
}
@Nonnull
- public CompletableFuture sendAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler) {
+ public CompletableFuture sendAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings) {
Objects.requireNonNull(requestInfo, "parameter requestInfo cannot be null");
- return this.getHttpResponseMessage(requestInfo).thenCompose(response -> {
+ return this.getHttpResponseMessage(requestInfo)
+ .thenCompose(response -> {
if(responseHandler == null) {
- final ResponseBody body = response.body();
try {
- try (final InputStream rawInputStream = body.byteStream()) {
- final ParseNode rootNode = pNodeFactory.getParseNode(getMediaTypeAndSubType(body.contentType()), rawInputStream);
- final ModelType result = rootNode.getObjectValue(targetClass);
- return CompletableFuture.completedStage(result);
- }
+ this.throwFailedResponse(response, errorMappings);
+ final ParseNode rootNode = getRootParseNode(response);
+ final ModelType result = rootNode.getObjectValue(targetClass);
+ return CompletableFuture.completedStage(result);
+ } catch(ApiException ex) {
+ return CompletableFuture.failedFuture(ex);
} catch(IOException ex) {
return CompletableFuture.failedFuture(new RuntimeException("failed to read the response body", ex));
} finally {
response.close();
}
} else {
- return responseHandler.handleResponseAsync(response);
+ return responseHandler.handleResponseAsync(response, errorMappings);
}
});
}
@@ -140,20 +144,21 @@ private String getMediaTypeAndSubType(final MediaType mediaType) {
return mediaType.type() + "/" + mediaType.subtype();
}
@Nonnull
- public CompletableFuture sendPrimitiveAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler) {
- return this.getHttpResponseMessage(requestInfo).thenCompose(response -> {
+ public CompletableFuture sendPrimitiveAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings) {
+ return this.getHttpResponseMessage(requestInfo)
+ .thenCompose(response -> {
if(responseHandler == null) {
- final ResponseBody body = response.body();
try {
+ this.throwFailedResponse(response, errorMappings);
if(targetClass == Void.class) {
return CompletableFuture.completedStage(null);
} else {
- final InputStream rawInputStream = body.byteStream();
if(targetClass == InputStream.class) {
+ final ResponseBody body = response.body();
+ final InputStream rawInputStream = body.byteStream();
return CompletableFuture.completedStage((ModelType)rawInputStream);
}
- final ParseNode rootNode = pNodeFactory.getParseNode(getMediaTypeAndSubType(body.contentType()), rawInputStream);
- rawInputStream.close();
+ final ParseNode rootNode = getRootParseNode(response);
Object result;
if(targetClass == Boolean.class) {
result = rootNode.getBooleanValue();
@@ -174,38 +179,76 @@ public CompletableFuture sendPrimitiveAsync(@Nonnull fina
}
return CompletableFuture.completedStage((ModelType)result);
}
+ } catch(ApiException ex) {
+ return CompletableFuture.failedFuture(ex);
} catch(IOException ex) {
return CompletableFuture.failedFuture(new RuntimeException("failed to read the response body", ex));
} finally {
response.close();
}
} else {
- return responseHandler.handleResponseAsync(response);
+ return responseHandler.handleResponseAsync(response, errorMappings);
}
});
}
- public CompletableFuture> sendPrimitiveCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler) {
+ public CompletableFuture> sendPrimitiveCollectionAsync(@Nonnull final RequestInformation requestInfo, @Nonnull final Class targetClass, @Nullable final ResponseHandler responseHandler, @Nullable final HashMap> errorMappings) {
Objects.requireNonNull(requestInfo, "parameter requestInfo cannot be null");
- return this.getHttpResponseMessage(requestInfo).thenCompose(response -> {
+ return this.getHttpResponseMessage(requestInfo)
+ .thenCompose(response -> {
if(responseHandler == null) {
- final ResponseBody body = response.body();
try {
- try (final InputStream rawInputStream = body.byteStream()) {
- final ParseNode rootNode = pNodeFactory.getParseNode(getMediaTypeAndSubType(body.contentType()), rawInputStream);
- final Iterable result = rootNode.getCollectionOfPrimitiveValues(targetClass);
- return CompletableFuture.completedStage(result);
- }
+ this.throwFailedResponse(response, errorMappings);
+ final ParseNode rootNode = getRootParseNode(response);
+ final Iterable result = rootNode.getCollectionOfPrimitiveValues(targetClass);
+ return CompletableFuture.completedStage(result);
+ } catch(ApiException ex) {
+ return CompletableFuture.failedFuture(ex);
} catch(IOException ex) {
return CompletableFuture.failedFuture(new RuntimeException("failed to read the response body", ex));
} finally {
response.close();
}
} else {
- return responseHandler.handleResponseAsync(response);
+ return responseHandler.handleResponseAsync(response, errorMappings);
}
});
}
+ private ParseNode getRootParseNode(final Response response) throws IOException {
+ final ResponseBody body = response.body();
+ try (final InputStream rawInputStream = body.byteStream()) {
+ final ParseNode rootNode = pNodeFactory.getParseNode(getMediaTypeAndSubType(body.contentType()), rawInputStream);
+ return rootNode;
+ }
+ }
+ private Response throwFailedResponse(final Response response, final HashMap> errorMappings) throws IOException, ApiException {
+ if (response.isSuccessful()) return response;
+
+ final String statusCodeAsString = Integer.toString(response.code());
+ final Integer statusCode = response.code();
+ if (errorMappings == null ||
+ !errorMappings.containsKey(statusCodeAsString) &&
+ !(statusCode >= 400 && statusCode < 500 && errorMappings.containsKey("4XX")) &&
+ !(statusCode >= 500 && statusCode < 600 && errorMappings.containsKey("5XX"))) {
+ throw new ApiException("the server returned an unexpected status code and no error class is registered for this code " + statusCode);
+ }
+ final Class extends Parsable> errorClass = errorMappings.containsKey(statusCodeAsString) ?
+ errorMappings.get(statusCodeAsString) :
+ (statusCode >= 400 && statusCode < 500 ?
+ errorMappings.get("4XX") :
+ errorMappings.get("5XX"));
+ try {
+ final ParseNode rootNode = getRootParseNode(response);
+ final Parsable error = rootNode.getObjectValue(errorClass);
+ if (error instanceof ApiException) {
+ throw (ApiException)error;
+ } else {
+ throw new ApiException("unexpected error type " + error.getClass().getName());
+ }
+ } finally {
+ response.close();
+ }
+ }
private CompletableFuture getHttpResponseMessage(@Nonnull final RequestInformation requestInfo) {
Objects.requireNonNull(requestInfo, "parameter requestInfo cannot be null");
this.setBaseUrlForRequestInformation(requestInfo);
diff --git a/http/typescript/fetch/package-lock.json b/http/typescript/fetch/package-lock.json
index 395d78efe2..4da4498345 100644
--- a/http/typescript/fetch/package-lock.json
+++ b/http/typescript/fetch/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@microsoft/kiota-http-fetchlibrary",
- "version": "1.0.15",
+ "version": "1.0.16",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@microsoft/kiota-http-fetchlibrary",
- "version": "1.0.15",
+ "version": "1.0.16",
"license": "MIT",
"dependencies": {
"@microsoft/kiota-abstractions": "^1.0.27",
diff --git a/http/typescript/fetch/package.json b/http/typescript/fetch/package.json
index 10d786bd7d..fac76880bd 100644
--- a/http/typescript/fetch/package.json
+++ b/http/typescript/fetch/package.json
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-http-fetchlibrary",
- "version": "1.0.15",
+ "version": "1.0.16",
"description": "Kiota request adapter implementation with fetch",
"main": "dist/cjs/index.js",
"module": "dist/es/index.js",
@@ -34,7 +34,7 @@
"registry": "https://npm.pkg.github.com"
},
"dependencies": {
- "@microsoft/kiota-abstractions": "^1.0.27",
+ "@microsoft/kiota-abstractions": "^1.0.29",
"cross-fetch": "^3.1.5",
"tslib": "^2.3.1"
},
diff --git a/http/typescript/fetch/src/fetchRequestAdapter.ts b/http/typescript/fetch/src/fetchRequestAdapter.ts
index 1fb8ecec95..9daabcc2fa 100644
--- a/http/typescript/fetch/src/fetchRequestAdapter.ts
+++ b/http/typescript/fetch/src/fetchRequestAdapter.ts
@@ -1,4 +1,4 @@
-import { AuthenticationProvider, BackingStoreFactory, BackingStoreFactorySingleton, RequestAdapter, Parsable, ParseNodeFactory, RequestInformation, ResponseHandler, ParseNodeFactoryRegistry, enableBackingStoreForParseNodeFactory, SerializationWriterFactoryRegistry, enableBackingStoreForSerializationWriterFactory, SerializationWriterFactory } from '@microsoft/kiota-abstractions';
+import { ApiError, AuthenticationProvider, BackingStoreFactory, BackingStoreFactorySingleton, RequestAdapter, Parsable, ParseNodeFactory, RequestInformation, ResponseHandler, ParseNodeFactoryRegistry, enableBackingStoreForParseNodeFactory, SerializationWriterFactoryRegistry, enableBackingStoreForSerializationWriterFactory, SerializationWriterFactory, ParseNode } from '@microsoft/kiota-abstractions';
import { HttpClient } from './httpClient';
export class FetchRequestAdapter implements RequestAdapter {
@@ -35,25 +35,21 @@ export class FetchRequestAdapter implements RequestAdapter {
if (segments.length === 0) return undefined;
else return segments[0];
}
- public sendCollectionOfPrimitiveAsync = async (requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date", responseHandler: ResponseHandler | undefined): Promise => {
+ public sendCollectionOfPrimitiveAsync = async (requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date", responseHandler: ResponseHandler | undefined, errorMappings: Record Parsable> | undefined): Promise => {
if (!requestInfo) {
throw new Error('requestInfo cannot be null');
}
const response = await this.getHttpResponseMessage(requestInfo);
if (responseHandler) {
- return await responseHandler.handleResponseAsync(response);
+ return await responseHandler.handleResponseAsync(response, errorMappings);
} else {
+ await this.throwFailedResponses(response, errorMappings);
switch (responseType) {
case 'string':
case 'number':
case 'boolean':
case 'Date':
- const payload = await response.arrayBuffer();
- const responseContentType = this.getResponseContentType(response);
- if (!responseContentType)
- throw new Error("no response content type found for deserialization");
-
- const rootNode = this.parseNodeFactory.getRootParseNode(responseContentType, payload);
+ const rootNode = await this.getRootParseNode(response);
if (responseType === 'string') {
return rootNode.getCollectionOfPrimitiveValues() as unknown as ResponseType[];
} else if (responseType === 'number') {
@@ -68,50 +64,43 @@ export class FetchRequestAdapter implements RequestAdapter {
}
}
}
- public sendCollectionAsync = async (requestInfo: RequestInformation, type: new () => ModelType, responseHandler: ResponseHandler | undefined): Promise => {
+ public sendCollectionAsync = async (requestInfo: RequestInformation, type: new () => ModelType, responseHandler: ResponseHandler | undefined, errorMappings: Record Parsable> | undefined): Promise => {
if (!requestInfo) {
throw new Error('requestInfo cannot be null');
}
const response = await this.getHttpResponseMessage(requestInfo);
if (responseHandler) {
- return await responseHandler.handleResponseAsync(response);
+ return await responseHandler.handleResponseAsync(response, errorMappings);
} else {
- const payload = await response.arrayBuffer();
- const responseContentType = this.getResponseContentType(response);
- if (!responseContentType)
- throw new Error("no response content type found for deserialization");
-
- const rootNode = this.parseNodeFactory.getRootParseNode(responseContentType, payload);
+ await this.throwFailedResponses(response, errorMappings);
+ const rootNode = await this.getRootParseNode(response);
const result = rootNode.getCollectionOfObjectValues(type);
return result as unknown as ModelType[];
}
}
- public sendAsync = async (requestInfo: RequestInformation, type: new () => ModelType, responseHandler: ResponseHandler | undefined): Promise => {
+ public sendAsync = async (requestInfo: RequestInformation, type: new () => ModelType, responseHandler: ResponseHandler | undefined, errorMappings: Record Parsable> | undefined): Promise => {
if (!requestInfo) {
throw new Error('requestInfo cannot be null');
}
const response = await this.getHttpResponseMessage(requestInfo);
if (responseHandler) {
- return await responseHandler.handleResponseAsync(response);
+ return await responseHandler.handleResponseAsync(response, errorMappings);
} else {
- const payload = await response.arrayBuffer();
- const responseContentType = this.getResponseContentType(response);
- if (!responseContentType)
- throw new Error("no response content type found for deserialization");
-
- const rootNode = this.parseNodeFactory.getRootParseNode(responseContentType, payload);
+ await this.throwFailedResponses(response, errorMappings);
+ const rootNode = await this.getRootParseNode(response);
const result = rootNode.getObjectValue(type);
return result as unknown as ModelType;
}
}
- public sendPrimitiveAsync = async (requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date" | "ArrayBuffer", responseHandler: ResponseHandler | undefined): Promise => {
+ public sendPrimitiveAsync = async (requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date" | "ArrayBuffer", responseHandler: ResponseHandler | undefined, errorMappings: Record Parsable> | undefined): Promise => {
if (!requestInfo) {
throw new Error('requestInfo cannot be null');
}
const response = await this.getHttpResponseMessage(requestInfo);
if (responseHandler) {
- return await responseHandler.handleResponseAsync(response);
+ return await responseHandler.handleResponseAsync(response, errorMappings);
} else {
+ await this.throwFailedResponses(response, errorMappings);
switch (responseType) {
case "ArrayBuffer":
return await response.arrayBuffer() as unknown as ResponseType;
@@ -119,12 +108,7 @@ export class FetchRequestAdapter implements RequestAdapter {
case 'number':
case 'boolean':
case 'Date':
- const payload = await response.arrayBuffer();
- const responseContentType = this.getResponseContentType(response);
- if (!responseContentType)
- throw new Error("no response content type found for deserialization");
-
- const rootNode = this.parseNodeFactory.getRootParseNode(responseContentType, payload);
+ const rootNode = await this.getRootParseNode(response);
if (responseType === 'string') {
return rootNode.getStringValue() as unknown as ResponseType;
} else if (responseType === 'number') {
@@ -139,14 +123,15 @@ export class FetchRequestAdapter implements RequestAdapter {
}
}
}
- public sendNoResponseContentAsync = async (requestInfo: RequestInformation, responseHandler: ResponseHandler | undefined): Promise => {
+ public sendNoResponseContentAsync = async (requestInfo: RequestInformation, responseHandler: ResponseHandler | undefined, errorMappings: Record Parsable> | undefined): Promise => {
if (!requestInfo) {
throw new Error('requestInfo cannot be null');
}
const response = await this.getHttpResponseMessage(requestInfo);
if (responseHandler) {
- return await responseHandler.handleResponseAsync(response);
+ return await responseHandler.handleResponseAsync(response, errorMappings);
}
+ await this.throwFailedResponses(response, errorMappings);
}
public enableBackingStore = (backingStoreFactory?: BackingStoreFactory | undefined): void => {
this.parseNodeFactory = enableBackingStoreForParseNodeFactory(this.parseNodeFactory);
@@ -157,6 +142,35 @@ export class FetchRequestAdapter implements RequestAdapter {
BackingStoreFactorySingleton.instance = backingStoreFactory;
}
}
+ private getRootParseNode = async (response: Response) : Promise => {
+ const payload = await response.arrayBuffer();
+ const responseContentType = this.getResponseContentType(response);
+ if (!responseContentType)
+ throw new Error("no response content type found for deserialization");
+
+ return this.parseNodeFactory.getRootParseNode(responseContentType, payload);
+ }
+ private throwFailedResponses = async (response: Response, errorMappings: Record Parsable> | undefined): Promise => {
+ if(response.ok) return;
+
+ const statusCode = response.status;
+ const statusCodeAsString = statusCode.toString();
+ if(!errorMappings ||
+ !errorMappings[statusCodeAsString] &&
+ !(statusCode >= 400 && statusCode < 500 && errorMappings['4XX']) &&
+ !(statusCode >= 500 && statusCode < 600 && errorMappings['5XX']))
+ throw new ApiError("the server returned an unexpected status code and no error class is registered for this code " + statusCode);
+
+ const factory = errorMappings[statusCodeAsString] ??
+ (statusCode >= 400 && statusCode < 500 ? errorMappings['4XX'] : undefined) ??
+ (statusCode >= 500 && statusCode < 600 ? errorMappings['5XX'] : undefined);
+
+ const rootNode = await this.getRootParseNode(response);
+ const error = rootNode.getObjectValue(factory);
+
+ if(error) throw error;
+ else throw new ApiError("unexpected error type" + typeof(error))
+ }
private getHttpResponseMessage = async (requestInfo: RequestInformation): Promise => {
if (!requestInfo) {
throw new Error('requestInfo cannot be null');
diff --git a/serialization/dotnet/json/src/JsonParseNode.cs b/serialization/dotnet/json/src/JsonParseNode.cs
index ec44a7228c..378196da94 100644
--- a/serialization/dotnet/json/src/JsonParseNode.cs
+++ b/serialization/dotnet/json/src/JsonParseNode.cs
@@ -256,12 +256,30 @@ public IEnumerable GetCollectionOfPrimitiveValues()
public T GetObjectValue() where T : IParsable
{
var item = (T)(typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { }));
+ return GetObjectValueInternal(item);
+ }
+ private T GetObjectValueInternal(T item) where T : IParsable
+ {
var fieldDeserializers = item.GetFieldDeserializers();
OnBeforeAssignFieldValues?.Invoke(item);
AssignFieldValues(item, fieldDeserializers);
OnAfterAssignFieldValues?.Invoke(item);
return item;
}
+ ///
+ /// Gets the resulting error from the node.
+ ///
+ /// The error object value of the node.
+ /// The error factory.
+ public IParsable GetErrorValue(Func factory)
+ {
+ if (factory == null) throw new ArgumentNullException(nameof(factory));
+
+ var instance = factory.Invoke();
+ if (instance == null) throw new InvalidOperationException("factory returned null");
+
+ return GetObjectValueInternal(instance);
+ }
private void AssignFieldValues(T item, IDictionary> fieldDeserializers) where T : IParsable
{
if(_jsonNode.ValueKind != JsonValueKind.Object) return;
diff --git a/serialization/java/json/lib/.classpath b/serialization/java/json/lib/.classpath
index 5933c3f21a..ae30f860ac 100644
--- a/serialization/java/json/lib/.classpath
+++ b/serialization/java/json/lib/.classpath
@@ -13,7 +13,7 @@
-
+
diff --git a/src/Kiota.Builder/CodeDOM/CodeClass.cs b/src/Kiota.Builder/CodeDOM/CodeClass.cs
index 9b9ed2a998..3d553f9581 100644
--- a/src/Kiota.Builder/CodeDOM/CodeClass.cs
+++ b/src/Kiota.Builder/CodeDOM/CodeClass.cs
@@ -2,128 +2,129 @@
using System.Collections.Generic;
using System.Linq;
-namespace Kiota.Builder
+namespace Kiota.Builder;
+
+public enum CodeClassKind {
+ Custom,
+ RequestBuilder,
+ Model,
+ QueryParameters,
+ ///
+ /// A single parameter to be provided by the SDK user which will contain query parameters, request body, options, etc.
+ /// Only used for languages that do not support overloads or optional parameters like go.
+ ///
+ ParameterSet,
+}
+///
+/// CodeClass represents an instance of a Class to be generated in source code
+///
+public class CodeClass : CodeBlock, IDocumentedElement, ITypeDefinition
{
- public enum CodeClassKind {
- Custom,
- RequestBuilder,
- Model,
- QueryParameters,
- ///
- /// A single parameter to be provided by the SDK user which will contain query parameters, request body, options, etc.
- /// Only used for languages that do not support overloads or optional parameters like go.
- ///
- ParameterSet,
+ private string name;
+ public CodeClass():base()
+ {
+ StartBlock = new Declaration() { Parent = this};
+ EndBlock = new End() { Parent = this };
}
+ public CodeClassKind ClassKind { get; set; } = CodeClassKind.Custom;
+
+ public bool IsErrorDefinition { get; set; }
+
+ public string Description {get; set;}
///
- /// CodeClass represents an instance of a Class to be generated in source code
+ /// Name of Class
///
- public class CodeClass : CodeBlock, IDocumentedElement, ITypeDefinition
+ public override string Name
{
- private string name;
- public CodeClass():base()
+ get => name;
+ set
{
- StartBlock = new Declaration() { Parent = this};
- EndBlock = new End() { Parent = this };
- }
- public CodeClassKind ClassKind { get; set; } = CodeClassKind.Custom;
-
- public string Description {get; set;}
- ///
- /// Name of Class
- ///
- public override string Name
- {
- get => name;
- set
- {
- name = value;
- StartBlock.Name = name;
- }
+ name = value;
+ StartBlock.Name = name;
}
+ }
- public void SetIndexer(CodeIndexer indexer)
- {
- if(indexer == null)
- throw new ArgumentNullException(nameof(indexer));
- if(InnerChildElements.Values.OfType().Any())
- throw new InvalidOperationException("this class already has an indexer, remove it first");
- AddRange(indexer);
- }
+ public void SetIndexer(CodeIndexer indexer)
+ {
+ if(indexer == null)
+ throw new ArgumentNullException(nameof(indexer));
+ if(InnerChildElements.Values.OfType().Any())
+ throw new InvalidOperationException("this class already has an indexer, remove it first");
+ AddRange(indexer);
+ }
- public IEnumerable AddProperty(params CodeProperty[] properties)
- {
- if(properties == null || properties.Any(x => x == null))
- throw new ArgumentNullException(nameof(properties));
- if(!properties.Any())
- throw new ArgumentOutOfRangeException(nameof(properties));
- return AddRange(properties);
- }
- public CodeProperty GetPropertyOfKind(CodePropertyKind kind) =>
- Properties.FirstOrDefault(x => x.IsOfKind(kind));
- public IEnumerable Properties => InnerChildElements.Values.OfType();
- public IEnumerable Methods => InnerChildElements.Values.OfType();
+ public IEnumerable AddProperty(params CodeProperty[] properties)
+ {
+ if(properties == null || properties.Any(x => x == null))
+ throw new ArgumentNullException(nameof(properties));
+ if(!properties.Any())
+ throw new ArgumentOutOfRangeException(nameof(properties));
+ return AddRange(properties);
+ }
+ public CodeProperty GetPropertyOfKind(CodePropertyKind kind) =>
+ Properties.FirstOrDefault(x => x.IsOfKind(kind));
+ public IEnumerable Properties => InnerChildElements.Values.OfType();
+ public IEnumerable Methods => InnerChildElements.Values.OfType();
- public bool ContainsMember(string name)
- {
- return InnerChildElements.ContainsKey(name);
- }
+ public bool ContainsMember(string name)
+ {
+ return InnerChildElements.ContainsKey(name);
+ }
- public IEnumerable AddMethod(params CodeMethod[] methods)
- {
- if(methods == null || methods.Any(x => x == null))
- throw new ArgumentNullException(nameof(methods));
- if(!methods.Any())
- throw new ArgumentOutOfRangeException(nameof(methods));
- return AddRange(methods);
- }
+ public IEnumerable AddMethod(params CodeMethod[] methods)
+ {
+ if(methods == null || methods.Any(x => x == null))
+ throw new ArgumentNullException(nameof(methods));
+ if(!methods.Any())
+ throw new ArgumentOutOfRangeException(nameof(methods));
+ return AddRange(methods);
+ }
- public IEnumerable AddInnerClass(params CodeClass[] codeClasses)
- {
- if(codeClasses == null || codeClasses.Any(x => x == null))
- throw new ArgumentNullException(nameof(codeClasses));
- if(!codeClasses.Any())
- throw new ArgumentOutOfRangeException(nameof(codeClasses));
- return AddRange(codeClasses);
- }
- public CodeClass GetParentClass() {
- if(StartBlock is Declaration declaration)
- return declaration.Inherits?.TypeDefinition as CodeClass;
- else return null;
- }
-
- public CodeClass GetGreatestGrandparent(CodeClass startClassToSkip = null) {
- var parentClass = GetParentClass();
- if(parentClass == null)
- return startClassToSkip != null && startClassToSkip == this ? null : this;
- // we don't want to return the current class if this is the start node in the inheritance tree and doesn't have parent
- else
- return parentClass.GetGreatestGrandparent(startClassToSkip);
- }
+ public IEnumerable AddInnerClass(params CodeClass[] codeClasses)
+ {
+ if(codeClasses == null || codeClasses.Any(x => x == null))
+ throw new ArgumentNullException(nameof(codeClasses));
+ if(!codeClasses.Any())
+ throw new ArgumentOutOfRangeException(nameof(codeClasses));
+ return AddRange(codeClasses);
+ }
+ public CodeClass GetParentClass() {
+ if(StartBlock is Declaration declaration)
+ return declaration.Inherits?.TypeDefinition as CodeClass;
+ else return null;
+ }
+
+ public CodeClass GetGreatestGrandparent(CodeClass startClassToSkip = null) {
+ var parentClass = GetParentClass();
+ if(parentClass == null)
+ return startClassToSkip != null && startClassToSkip == this ? null : this;
+ // we don't want to return the current class if this is the start node in the inheritance tree and doesn't have parent
+ else
+ return parentClass.GetGreatestGrandparent(startClassToSkip);
+ }
- public bool IsOfKind(params CodeClassKind[] kinds) {
- return kinds?.Contains(ClassKind) ?? false;
- }
+ public bool IsOfKind(params CodeClassKind[] kinds) {
+ return kinds?.Contains(ClassKind) ?? false;
+ }
public class Declaration : BlockDeclaration
- {
- private CodeType inherits;
- public CodeType Inherits { get => inherits; set {
- EnsureElementsAreChildren(value);
- inherits = value;
- } }
- private readonly List implements = new ();
- public void AddImplements(params CodeType[] types) {
- if(types == null || types.Any(x => x == null))
- throw new ArgumentNullException(nameof(types));
- EnsureElementsAreChildren(types);
- implements.AddRange(types);
- }
- public IEnumerable Implements => implements;
+ {
+ private CodeType inherits;
+ public CodeType Inherits { get => inherits; set {
+ EnsureElementsAreChildren(value);
+ inherits = value;
+ } }
+ private readonly List implements = new ();
+ public void AddImplements(params CodeType[] types) {
+ if(types == null || types.Any(x => x == null))
+ throw new ArgumentNullException(nameof(types));
+ EnsureElementsAreChildren(types);
+ implements.AddRange(types);
}
+ public IEnumerable Implements => implements;
+ }
- public class End : BlockEnd
- {
- }
+ public class End : BlockEnd
+ {
}
}
diff --git a/src/Kiota.Builder/CodeDOM/CodeMethod.cs b/src/Kiota.Builder/CodeDOM/CodeMethod.cs
index a281a7fb5d..048b437b30 100644
--- a/src/Kiota.Builder/CodeDOM/CodeMethod.cs
+++ b/src/Kiota.Builder/CodeDOM/CodeMethod.cs
@@ -2,122 +2,126 @@
using System.Collections.Generic;
using System.Linq;
-namespace Kiota.Builder
+namespace Kiota.Builder;
+
+public enum CodeMethodKind
{
- public enum CodeMethodKind
- {
- Custom,
- IndexerBackwardCompatibility,
- RequestExecutor,
- RequestGenerator,
- Serializer,
- Deserializer,
- Constructor,
- Getter,
- Setter,
- ClientConstructor,
- RequestBuilderBackwardCompatibility,
- RequestBuilderWithParameters,
- RawUrlConstructor,
- NullCheck,
+ Custom,
+ IndexerBackwardCompatibility,
+ RequestExecutor,
+ RequestGenerator,
+ Serializer,
+ Deserializer,
+ Constructor,
+ Getter,
+ Setter,
+ ClientConstructor,
+ RequestBuilderBackwardCompatibility,
+ RequestBuilderWithParameters,
+ RawUrlConstructor,
+ NullCheck,
+}
+public enum HttpMethod {
+ Get,
+ Post,
+ Patch,
+ Put,
+ Delete,
+ Options,
+ Connect,
+ Head,
+ Trace
+}
+
+public class CodeMethod : CodeTerminal, ICloneable, IDocumentedElement
+{
+ public HttpMethod? HttpMethod {get;set;}
+ public CodeMethodKind MethodKind {get;set;} = CodeMethodKind.Custom;
+ public string ContentType { get; set; }
+ public AccessModifier Access {get;set;} = AccessModifier.Public;
+ private CodeTypeBase returnType;
+ public CodeTypeBase ReturnType {get => returnType;set {
+ EnsureElementsAreChildren(value);
+ returnType = value;
+ }}
+ private readonly List parameters = new ();
+ public void RemoveParametersByKind(params CodeParameterKind[] kinds) {
+ parameters.RemoveAll(p => p.IsOfKind(kinds));
}
- public enum HttpMethod {
- Get,
- Post,
- Patch,
- Put,
- Delete,
- Options,
- Connect,
- Head,
- Trace
+ public IEnumerable Parameters { get => parameters; }
+ public bool IsStatic {get;set;} = false;
+ public bool IsAsync {get;set;} = true;
+ public string Description {get; set;}
+ ///
+ /// The property this method accesses to when it's a getter or setter.
+ ///
+ public CodeProperty AccessedProperty { get; set; }
+ public bool IsOfKind(params CodeMethodKind[] kinds) {
+ return kinds?.Contains(MethodKind) ?? false;
}
+ public bool IsAccessor {
+ get => IsOfKind(CodeMethodKind.Getter, CodeMethodKind.Setter);
+ }
+ public bool IsSerializationMethod {
+ get => IsOfKind(CodeMethodKind.Serializer, CodeMethodKind.Deserializer);
+ }
+ public List SerializerModules { get; set; }
+ public List DeserializerModules { get; set; }
+ ///
+ /// Indicates whether this method is an overload for another method.
+ ///
+ public bool IsOverload { get { return OriginalMethod != null; } }
+ ///
+ /// Provides a reference to the original method that this method is an overload of.
+ ///
+ public CodeMethod OriginalMethod { get; set; }
+ ///
+ /// The original indexer codedom element this method replaces when it is of kind IndexerBackwardCompatibility.
+ ///
+ public CodeIndexer OriginalIndexer { get; set; }
+ ///
+ /// The base url for every request read from the servers property on the description.
+ /// Only provided for constructor on Api client
+ ///
+ public string BaseUrl { get; set; }
+ ///
+ /// Mapping of the error code and response types for this method.
+ ///
+ public Dictionary ErrorMappings { get; set; } = new ();
- public class CodeMethod : CodeTerminal, ICloneable, IDocumentedElement
+ public object Clone()
{
- public HttpMethod? HttpMethod {get;set;}
- public CodeMethodKind MethodKind {get;set;} = CodeMethodKind.Custom;
- public string ContentType { get; set; }
- public AccessModifier Access {get;set;} = AccessModifier.Public;
- private CodeTypeBase returnType;
- public CodeTypeBase ReturnType {get => returnType;set {
- EnsureElementsAreChildren(value);
- returnType = value;
- }}
- private readonly List parameters = new ();
- public void RemoveParametersByKind(params CodeParameterKind[] kinds) {
- parameters.RemoveAll(p => p.IsOfKind(kinds));
- }
- public IEnumerable