From 010916f8bad2cb721ce4680828ad74000fbcd4ed Mon Sep 17 00:00:00 2001 From: Jake <45181984+jake-bassett@users.noreply.github.com> Date: Fri, 8 Sep 2023 14:26:21 -0700 Subject: [PATCH] feat: expose error handling in graphql request service (#2386) --- .../assets-library/assets/styles/_font.scss | 4 +- .../src/graphql-execution-error.ts | 5 ++ .../src/graphql-request-error.ts | 10 +++ .../graphql-client/src/graphql-request.api.ts | 22 ++++++ .../src/graphql-request.service.ts | 76 +++++++++---------- projects/graphql-client/src/public-api.ts | 4 +- 6 files changed, 79 insertions(+), 42 deletions(-) create mode 100644 projects/graphql-client/src/graphql-execution-error.ts create mode 100644 projects/graphql-client/src/graphql-request-error.ts create mode 100644 projects/graphql-client/src/graphql-request.api.ts diff --git a/projects/assets-library/assets/styles/_font.scss b/projects/assets-library/assets/styles/_font.scss index b179ecc69..995d2b399 100644 --- a/projects/assets-library/assets/styles/_font.scss +++ b/projects/assets-library/assets/styles/_font.scss @@ -43,8 +43,8 @@ $font-family: 'Work Sans', sans-serif; letter-spacing: -0.02em; } -@mixin font-placeholder { - color: $gray-5; +@mixin font-placeholder($color: $gray-5) { + color: $color; font-size: 15px; line-height: 18px; letter-spacing: -0.01em; diff --git a/projects/graphql-client/src/graphql-execution-error.ts b/projects/graphql-client/src/graphql-execution-error.ts new file mode 100644 index 000000000..948bd12ba --- /dev/null +++ b/projects/graphql-client/src/graphql-execution-error.ts @@ -0,0 +1,5 @@ +export class GraphqlExecutionError extends Error { + public constructor(public readonly message: string, public readonly requestString: string) { + super(message); + } +} diff --git a/projects/graphql-client/src/graphql-request-error.ts b/projects/graphql-client/src/graphql-request-error.ts new file mode 100644 index 000000000..21c38fe88 --- /dev/null +++ b/projects/graphql-client/src/graphql-request-error.ts @@ -0,0 +1,10 @@ +import { GraphqlExecutionError } from './graphql-execution-error'; +import { GraphQlRequest } from './graphql-request.api'; + +export class GraphqlRequestError extends GraphqlExecutionError { + public constructor(public readonly error: GraphqlExecutionError, public readonly request: GraphQlRequest) { + super(error.message, error.requestString); + this.name = error.name; + this.stack = error.stack; + } +} diff --git a/projects/graphql-client/src/graphql-request.api.ts b/projects/graphql-client/src/graphql-request.api.ts new file mode 100644 index 000000000..0bec30fce --- /dev/null +++ b/projects/graphql-client/src/graphql-request.api.ts @@ -0,0 +1,22 @@ +import { GraphQlHandler } from './graphql-config'; + +export const enum GraphQlResultStatus { + Success = 'SUCCESS', + Error = 'ERROR' +} + +export type GraphQlRequest = unknown; + +export type RequestTypeForHandler> = T extends GraphQlHandler< + infer TRequest, + unknown +> + ? TRequest + : never; + +export type ResponseTypeForHandler> = T extends GraphQlHandler< + unknown, + infer TResponse +> + ? TResponse + : never; diff --git a/projects/graphql-client/src/graphql-request.service.ts b/projects/graphql-client/src/graphql-request.service.ts index 7ed9f0d99..01a8aa23e 100644 --- a/projects/graphql-client/src/graphql-request.service.ts +++ b/projects/graphql-client/src/graphql-request.service.ts @@ -13,6 +13,14 @@ import { GraphQlRequestOptions, GRAPHQL_OPTIONS } from './graphql-config'; +import { GraphqlExecutionError } from './graphql-execution-error'; +import { GraphqlRequestError } from './graphql-request-error'; +import { + GraphQlRequest, + GraphQlResultStatus, + RequestTypeForHandler, + ResponseTypeForHandler +} from './graphql-request.api'; import { GraphQlSelection } from './model/graphql-selection'; import { GraphQlRequestBuilder } from './utils/builders/request/graphql-request-builder'; import { GraphQlDataExtractor } from './utils/extractor/graphql-data-extractor'; @@ -131,7 +139,7 @@ export class GraphQlRequestService { ); } - private executeRequest( + private executeRequest>( requestString: string, type: GraphQlHandlerType, options: GraphQlRequestOptions @@ -141,7 +149,7 @@ export class GraphQlRequestService { : this.executeQuery(requestString, options); } - private executeQuery( + private executeQuery>( requestString: string, options: GraphQlRequestOptions ): Observable { @@ -154,15 +162,14 @@ export class GraphQlRequestService { .pipe( tap(response => { if (!isNil(response.errors)) { - // eslint-disable-next-line no-console - console.error(`Query response error(s) for request '${requestString}'`, response.errors); + throw new GraphqlExecutionError(`Query response error(s) for request '${requestString}'`, requestString); } }), map(response => response.data) ); } - private executeMutation(requestString: string): Observable { + private executeMutation>(requestString: string): Observable { return this.apollo .mutate({ mutation: gql(`mutation ${requestString}`) @@ -170,8 +177,7 @@ export class GraphQlRequestService { .pipe( tap(response => { if (!isNil(response.errors)) { - // eslint-disable-next-line no-console - console.error(`Mutation response error(s) for request '${requestString}'`, response.errors); + throw new GraphqlExecutionError(`Mutation response error(s) for request '${requestString}'`, requestString); } }), mergeMap(response => (response.data ? of(response.data) : EMPTY)) @@ -197,11 +203,10 @@ export class GraphQlRequestService { return result.value as T; } - const errorValue = String(result.value); // eslint-disable-next-line no-console - console.error(errorValue); + console.error(`${result.error.message}\n\nExpand output to see request, result.`, request, result); - throw Error(errorValue); + throw result.error; }) ); } @@ -240,7 +245,7 @@ export class GraphQlRequestService { private buildResponseGetter( queryBuilder: GraphQlRequestBuilder, - selectionResponseMap: Map>, + selectionResponseMap: Map>>, selectionMultiMap: Map> ): ResponseGetter { return request => { @@ -277,15 +282,18 @@ export class GraphQlRequestService { return [ request, responseGetter(request).pipe( - map(response => ({ - status: GraphQlResultStatus.Success, - value: response - })), - catchError(err => + map( + response => + ({ + status: GraphQlResultStatus.Success, + value: response + } as GraphQlSuccessResult) + ), + catchError((err: GraphqlExecutionError) => of({ status: GraphQlResultStatus.Error, - value: err - }) + error: new GraphqlRequestError(err, request) + } as GraphQlErrorResult) ) ) ]; @@ -329,35 +337,25 @@ export class GraphQlRequestService { } } -const enum GraphQlResultStatus { - Success = 'SUCCESS', - Error = 'ERROR' +interface Dictionary { + [key: string]: T; } -interface GraphQlResult { - status: GraphQlResultStatus; +type GraphQlResult = GraphQlSuccessResult | GraphQlErrorResult; + +interface GraphQlSuccessResult { + status: GraphQlResultStatus.Success; value: unknown; } +interface GraphQlErrorResult { + status: GraphQlResultStatus.Error; + error: GraphqlRequestError; +} + interface RequestWithOptions { request: GraphQlRequest; options?: GraphQlRequestOptions; } type ResponseGetter = (request: unknown) => Observable; - -type GraphQlRequest = unknown; - -export type RequestTypeForHandler> = T extends GraphQlHandler< - infer TRequest, - unknown -> - ? TRequest - : never; - -export type ResponseTypeForHandler> = T extends GraphQlHandler< - unknown, - infer TResponse -> - ? TResponse - : never; diff --git a/projects/graphql-client/src/public-api.ts b/projects/graphql-client/src/public-api.ts index b1bd3f7c5..8d0d78167 100644 --- a/projects/graphql-client/src/public-api.ts +++ b/projects/graphql-client/src/public-api.ts @@ -2,7 +2,9 @@ * Public API Surface of graphql-client */ -export { GraphQlRequestService, RequestTypeForHandler, ResponseTypeForHandler } from './graphql-request.service'; +export { RequestTypeForHandler, ResponseTypeForHandler } from './graphql-request.api'; +export { GraphqlRequestError } from './graphql-request-error'; +export { GraphQlRequestService } from './graphql-request.service'; export { GraphQlHandler, GraphQlQueryHandler,