Skip to content

Commit

Permalink
- adds intermediat error type for typescript
Browse files Browse the repository at this point in the history
- adds error mapping abstractions and implements deserialization of errors for typescript http

Signed-off-by: Vincent Biret <vibiret@microsoft.com>
  • Loading branch information
baywet committed Feb 4, 2022
1 parent ab84fa5 commit 296965f
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 44 deletions.
11 changes: 11 additions & 0 deletions abstractions/typescript/src/apiError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** Parent interface for errors thrown by the client when receiving failed responses to its requests. */
interface ApiError extends Error {
}

interface ApiErrorConstructor extends ErrorConstructor {
new(message?: string): ApiError;
(message?: string): ApiError;
readonly prototype: ApiError;
}

export var ApiError: ApiErrorConstructor;
1 change: 1 addition & 0 deletions abstractions/typescript/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './apiClientBuilder';
export * from './apiError';
export * from './authentication';
export * from './dateOnly';
export * from './duration';
Expand Down
6 changes: 5 additions & 1 deletion abstractions/typescript/src/nativeResponseHandler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
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;
public handleResponseAsync<NativeResponseType, ModelType>(response: NativeResponseType): Promise<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. */
public errorMappings: Record<string, new () => Parsable> | undefined
public handleResponseAsync<NativeResponseType, ModelType>(response: NativeResponseType, errorMappings: Record<string, new () => Parsable> | undefined): Promise<ModelType> {
this.value = response;
this.errorMappings = errorMappings;
return Promise.resolve<ModelType>(undefined as any);
}
}
15 changes: 10 additions & 5 deletions abstractions/typescript/src/requestAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,51 @@ 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.
*/
sendAsync<ModelType extends Parsable>(requestInfo: RequestInformation, type: new() => ModelType, responseHandler: ResponseHandler | undefined): Promise<ModelType>;
sendAsync<ModelType extends Parsable>(requestInfo: RequestInformation, type: new() => ModelType, responseHandler: ResponseHandler | undefined, errorMappings: Record<string, new () => Parsable> | undefined): Promise<ModelType>;
/**
* 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.
*/
sendCollectionAsync<ModelType extends Parsable>(requestInfo: RequestInformation, type: new() => ModelType, responseHandler: ResponseHandler | undefined): Promise<ModelType[]>;
sendCollectionAsync<ModelType extends Parsable>(requestInfo: RequestInformation, type: new() => ModelType, responseHandler: ResponseHandler | undefined, errorMappings: Record<string, new () => Parsable> | undefined): Promise<ModelType[]>;
/**
* 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.
*/
sendCollectionOfPrimitiveAsync<ResponseType>(requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date", responseHandler: ResponseHandler | undefined): Promise<ResponseType[] | undefined>;
sendCollectionOfPrimitiveAsync<ResponseType>(requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date", responseHandler: ResponseHandler | undefined, errorMappings: Record<string, new () => Parsable> | undefined): Promise<ResponseType[] | undefined>;
/**
* 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.
*/
sendPrimitiveAsync<ResponseType>(requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date" | "ArrayBuffer", responseHandler: ResponseHandler | undefined): Promise<ResponseType>;
sendPrimitiveAsync<ResponseType>(requestInfo: RequestInformation, responseType: "string" | "number" | "boolean" | "Date" | "ArrayBuffer", responseHandler: ResponseHandler | undefined, errorMappings: Record<string, new () => Parsable> | undefined): Promise<ResponseType>;
/**
* 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): Promise<void>;
sendNoResponseContentAsync(requestInfo: RequestInformation, responseHandler: ResponseHandler | undefined, errorMappings: Record<string, new () => Parsable> | undefined): Promise<void>;
/**
* Enables the backing store proxies for the SerializationWriters and ParseNodes in use.
* @param backingStoreFactory the backing store factory to use.
Expand Down
5 changes: 4 additions & 1 deletion abstractions/typescript/src/responseHandler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
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<NativeResponseType, ModelType>(response: NativeResponseType): Promise<ModelType>;
handleResponseAsync<NativeResponseType, ModelType>(response: NativeResponseType, errorMappings: Record<string, new () => Parsable> | undefined): Promise<ModelType>;
}
4 changes: 2 additions & 2 deletions http/dotnet/httpclient/src/HttpClientRequestAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,12 @@ private async Task ThrowFailedResponse(HttpResponseMessage response, Dictionary<
!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 HttpRequestException($"The server returned an unexpected status code and no error factory is registered for this code: {statusCodeAsString}");
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 HttpRequestException($"The server returned an unexpected status code and the error registered for this code failed to deserialize: {statusCodeAsString}");
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<IParseNode> GetRootParseNode(HttpResponseMessage response)
Expand Down
Loading

0 comments on commit 296965f

Please sign in to comment.